XML Handler Bypassing ParseError Handling Rack Middleware on Rails 6

In a project using actionpack-xml_parser, and implementing a modified version of Catching Invalid JSON Parse Errors with Rack Middleware to also handle XML errors, an upgrade to Rails 6 broke the handling of the ParseError. Interestingly enough, JSON was being handled as expected.

TL;DR: The short answer to this is that config/initializers/wrap_parameters.rb only had json mentioned as a format. This can be remedied in the initializer or on a per controller basis. The entire app fix was just adding :xml to the initializer:

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json, :xml]
end

In looking through the stack traces/call stack for where the parse error occurs (ActionDispatch::Http::Parameters#parse_formatted_parameters) using the caller array in the gem source and backtrace in the rescue, I was able to catch that ActionController::Metal::ParamsWrapper#_wrapper_enabled? was called for JSON, but not for XML. Ultimately, I was able to track down the difference to ActionController::Metal::ParamsWrapper#_wrapper_formats?, which was only returning [:json], which led to the wrap_parameters functionality in Rails.


rspec-rails tricks: Hacking Your Routes for a Spec

I don’t know that this is so much a reminder of “What can be done with rspec-rails” as a note that, “If you do this will rspec-rails, you will also need to undo it.”

Today’s note: If you hack the application routes for a controller test, you have to reload routes.

The original problem

This all came down to a spec that was attempting to test a method in ApplicationController. In our Rails 3 setup (rspec-core and rspec-rails 3.7.0), the tests only needed a monkey patched ApplicationController to happen prior to the test:

before do
  class ApplicationController
    def dummy_action
      dummy_method_actually_under_test
    end
  end
end

it 'example do test' do
  get :dummy_action #etc...
  # validate dummy_method_actually_under_test did the thing
end

The fix

In Rails 4 with the same gem versions, we’d end up with the “No route matches…” which could be remedied by redefining the routes.

before do
  class ApplicationController
    def dummy_action
      dummy_method_actually_under_test
    end
  end
  Rails.application.routes.draw do
    get 'application/dummy_action/:id', to: 'application#dummy_action'
  end
end

Great! The test passes now! (Insert philosophical argument about whether the person writing the test should have tested a method in this way.)

Or not…

..until you run the rest of the suite, of course. Now *every* subsequent controller test has a “No route matches” issue. (Insert philosophical argument about whether you should be writing controller tests.)

This should serve as a periodic reminder to clean up after your tests, which in this case is:

after do
  Rails.application.reload_routes!
end

Multiple Postgres Schemas and Rails db:test:prepare

In our Rails databases, we use multiple Postgres schemas. This is partly for partitioning archive data from fresh data. On a new Rails 5 project, I started having tests fail once the models had the “archivable” concern added, which depended on an archive schema existing.

Ultimately, the problem is that the archive schema wasn’t being created in the test database, because rake db:test:prepare performs a db:test:load which was loading the db/schema.rb, which is completely ignorant of the Postgres-specific schema concept.

In order to fix this without attempting ugly hacks, the simplest solution was just to switch to using structure.sql for the Rails schema dump.

In config/application.rb, insert the following in your Application class definition:


config.active_record.schema_format = :sql

If you haven’t already added the additional schema(s) to your schema_search_path in your config/database.yml.


database: whatever_db
schema_search_path: 'main,main_archive,public'

You’ll need to run rake db:migrate to force structure.sql generation before running tests again. Also, be sure to switch from schema.rb to structure.sql if you’re committing this file.


RailsSettings vs parallel_tests

RailsSetting stubbing problem

I have an area of functionality that is controlled by a Settings class that is a subclass of RailsSettings::CachedSettings (rails-settings-cached gem) and changing that setting for tests was interfering with the parallel_tests gem when I ran my rspec tests.

I tried namespacing the cache, making TTL 0, invalidating the cache, monkeypatching the lookup… Every convoluted solution possible… to try and get tests to play nicely together.

The humbling thing about programming is how simple actual solutions are compared the things the brain comes up with.

The solution? Wrap the access to the Setting in a layer of abstraction (which I had partially done already.)

class ThatFunctionality
  def self.enabled?
    Setting['that_functionality.enabled']
  end
  def self.enabled=(value)
    Setting['that_functionality.enabled']=value
  end

And then in tests:

before do
  allow(ThatFunctionality).to receive(:enabled?).and_return(true) # or false
end

Trying again to stub RailsSettings

The class method approach is explicit, but once a large set of settings is needed, it can get unwieldy. If you haven’t found a better strategy for wrapping settings values, you can go back to stubbing the :[] method. The caveat is that you need to save the original Settings behavior for any settings that you want to leave unchanged. By stubbing the :[] first to passthrough to .and_call_original, you can add additional stubs on top.

# a module included
module StubSettings
def stub_setting(setting_name, setting_value)
allow(Setting).to receive(:[]).with(setting_name).and_return(setting_value)
end
def stub_setting_assignment
allow(Setting).to receive(:[]=) { |setting, value| stub_setting(setting, setting) }
end
end
# in spec_helper.rb
config.before(:each) do
# detect if Settings being set without you knowing about it
allow(Setting).to receive(:[]=)
.and_raise("stub_setting(setting_name, setting_value) to set the value of a Setting instead")
# passthrough read-only settings calls (necessary if you don't want .with to block other settings
allow(Setting).to receive(:[]).and_call_original
end
# in rspec example
describe 'settings will be set in code invoked from here' do
# automatically stubs assigned value to new stub
before { stub_setting_assignment }
end
# stubbing a setting value
describe 'setting to be forced to a value' do
before { stub_setting('some_toggle_setting', true) }
end

view raw
stub_settings.rb
hosted with ❤ by GitHub


Rails 4, phantom ArgumentError: wrong number of arguments (0 for 1) on UserSession.find for AuthLogic

This was a total pain to locate, as the exceptions being raised were pointed to UserSession.find in Authlogic::Session::Persistence.

gems in question:

  activerecord-session_store 0.1.1
  authlogic 3.4.6
  activerecord 4.2.3
  rails 4.2.3

I noticed that the only meaningful difference between the two environments was the following instance variable in Rails.application.config:

irb> pp Rails.application.config
.
.
.
 @logger=
  #,
   @level=1>
.
.
.

Commenting out this initialization in the environment allowed the application to work again.

   config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new('name of log'))

Searching on “usersession syslog rails 4” results in the following issue as the third search result: Version 0.1.1 breaks Syslog::Logger setups There is a fix in master, but it doesn’t seem to have been published. I had promoted activerecord-session_store to 0.1.1 because DEPRECATION WARNING: `#quietly` is deprecated in rails-4.2.0.beta4, but it looks like locking the gem at 0.1 will work otherwise except for noisy tests.


Pundit: NoMethodError (undefined method `verify_authorized’ for [WhateversController])

Sometimes, having done things several times before can make you miss the OBVIOUS.

After adding

  after_action :verify_authorized

to my ApplicationController to verify that Pundit was being used for authorization, I got the following error in rspec:

  2) Jobs GET /jobs works! (now write some real specs)
     Failure/Error: get jobs_path
     NoMethodError:
       undefined method `verify_authorized' for #
     # ./spec/requests/jobs_spec.rb:6:in `block (3 levels) in '

Maybe there’s a problem with my RSpec? Nope:

Started GET "/" for 127.0.0.1 at 2015-07-31 07:13:17 -0500
Processing by JobsController#index as HTML
  Job Load (0.1ms)  SELECT "jobs".* FROM "jobs"
  Rendered jobs/index.html.slim within layouts/application (0.5ms)
Completed 500 Internal Server Error in 8ms

NoMethodError (undefined method `verify_authorized' for #):

Okay, does ApplicationController have the method?!

irb(main):008:0> ApplicationController.new.methods.include? :verify_authorized
=> false

Ok, am I doing that wrong?

irb(main):010:0> ApplicationController.new.methods.include? :authenticate_user!
=> true

Sigh. *Rereads documentation.*

Screenshot 2015-07-31 07.25.24

What’s missing? Oh yeah, a simple `include Pundit’ in the ApplicationController
8729962194_b4cc81b2ee_z


devise_security_extension undefined method authenticate for nil:NilClass on Rspec Tests for Controller That Skips Authentication

After installing the Devise Security Extension to add password expiration to my Devise models, I started getting the following error on an RSpec test for a controller that does not perform authentication of the user:

     Failure/Error: get :index
     NoMethodError:
       undefined method `authenticate?' for nil:NilClass

After a bit of digging, I found that the helpers.rb in the gem includes an additional before_filter that needs to be skipped:

module DeviseSecurityExtension
  module Controllers
    module Helpers
      extend ActiveSupport::Concern

      included do
        before_filter :handle_password_change
      end

So while I’m skipping authenticate_user! in my controller, I still needed an additional:

  skip_before_filter :handle_password_change

Interestingly enough, the controller itself doesn’t break, just the tests. The downside is that I’m referencing two different Devise filters/actions just to not use them.


Rails AntiPatterns Review/Summary

I’ve had the Rails AntiPatterns: Best Practice Ruby on Rails Refactoring (Addison-Wesley Professional Ruby Series) book on my Safari Books Online bookshelf for almost a year now, and finally started reading through it. It was “updated for Rails 3”, and there doesn’t appear to be a newer version, so I was a bit skeptical about how much I’d get out of the book.

This book has some overlap with Ruby Science, but it is more Rails-centric in its approach. Going through the book I found quite a few topics that I’ve not really read in-depth on in any other book or come across in a couple years of Rails development experience.

  • Use of ActiveSupport::Concern included for specifying validations as part of a module.
  • Discussion of Rails plugins (which later were mentioned as falling out of favor)
  • serialize :value, Hash vs checkbox/boolean model
  • How Rails helps in the building of Semantic HTML
  • Gems Clearance and AuthLogic as simple authentication gems.
  • MVP pattern and ActivePresenter (looks like Rails 3 was the end?)
  • Emphasis on controller as a representation of a resource
  • Rails responders for changing respond_to to respond_with(object)
  • rescue *EXPECTED_EXCEPTIONS (EXPECTED_EXPECTIONS as a constant array) to whitelist exceptions that are anticipated.
  • Mechanize and RestClient as automated interface gems with other sites.
  • render :status => :unprocessable_entity (or :created and other symbols that represent HTTP status codes)
  • Strategies for testing rake tasks, including FileUtils::NoWrite
  • sqlite3 “:memory:” database for gem testing
  • Thoughts on when to add DB indexes
  • AR association methods count vs. length vs. size and their behavior and resource usage.
  • .flatten() as a code smell for ruby code that should be represented as SQL.
  • Run rake db:migrate && rake db:migrate:redo for all migrations created.
  • Don’t go against AR bias against DB constraints except for NULL & default values.
  • Never fail quietly, whether ignoring return values or overzealously catching all exceptions.
  • rescue_from on controller to redirect specific exceptions.

I think the general concepts covered provided valuable background and food for thought even if not all the solutions are still the same. I don’t regret the help examining current practices in my daily Rails development.


Circular dependency detected while autoloading constant

I ended up with this error because the class name I was referencing was ClassNameXML but I was trying to call it as ClassNameXml.

Not following naming conventions combined with not calling a class by its properly capitalized name will trigger this. I burned an hour figuring this out. I hope you don’t have to.


rails (4) generate migration behaviors I forget to use

When I’m generating a migration, I usually don’t go any deeper than specifying column_name:data_type on the command line, and often I only use the command line to generate the file itself.

I don’t know that the other options really save all that much typing, but stopping to think about the other options for a few seconds might just help avoid having to go back later and add things.

Elementary things I don’t make enough use of:

  • rails g migration AddXXXtoYYY automatically creates an add_column for table YYY for every column name specified.
  • rails g migration RemoveXXXtoYYY automatically creates a remove_column for table YYY for every column name specified.
  • Specifying column_name:data_type:index¬†also adds an index to the new column.
  • rails g migration CreateJoinTableXXXYYY will produce a join table.