1

I have a module that defines a class method to dynamically define a series of instance methods based on values in given columns, roughly as follows:

lib/active_record_extension.rb

module ActiveRecordExtension
  extend ActiveSupport::Concern

  module ClassMethods
    def define_some_methods(*attribute_names)
      # define some methods
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecordExtension)

config/initializers/extensions.rb

require 'active_record_extension.rb'

app/models/my_model.rb

class MyModel < ActiveRecord::Base
  define_some_methods :first_attribute, :second_attribute
end

This setup for adding a class method to ActiveRecord::Base is based on the first answer to this question.

This works beautifully in my Rails app and console, allowing me to define a variety of similar methods without cluttering up my model. However, it doesn't work at all in my rspec tests, which now all fail with NoMethodErrors for calls to the dynamically defined methods.

How can I be sure this module (or just this method) is correctly included in my models while running rspec?

EDIT: Here is my spec/spec_helper.rb:

ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'

#to test with sunspot
require 'sunspot/rails/spec_helper'
RSpec.configure do |config|
  ::Sunspot.session = ::Sunspot::Rails::StubSessionProxy.new(::Sunspot.session)
end

#adds devise and jasmine fixture test helpers
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.use_transactional_fixtures = true
  config.infer_base_class_for_anonymous_controllers = false
  config.order = "random"
end
Community
  • 1
  • 1
eirikir
  • 3,802
  • 3
  • 21
  • 39

1 Answers1

1

I would propose an alternative way to solve the first problem which should also solve the current issue you're asking about. There's nothing ActiveSupport::Concern can do for you here that ruby can't so I would just stick with pure ruby and do:

module ActiveRecordExtension
  def self.included(klass)
    klass.class_eval do 
      extend ClassMethods
    end
  end 
 module ClassMethods
  def define_some_methods(*attribute_names)
   # define some methods
  end
 end
end

ActiveRecord::Base.send(:include, ActiveRecordExtension)

If you have any questions about the included hook I can explain in more detail. You also don't need to require this file in an initializer(although when you make changes you will need to restart your local server)

jvans
  • 2,765
  • 2
  • 22
  • 23
  • The included hook gets around having to extend `ActiveSupport::Concern` nicely. However, I'm not sure how this helps load the needed class method in rspec. Whether I require the file in an initializer, in my model files, or in `spec_helper.rb`, the same `NoMethodError`s keep coming up in rpsec. Am I missing something? – eirikir Sep 02 '13 at 23:30
  • Rspec is supposed to load your environment before running tests so my hunch is that it has something to do with your spec_helper.rb file. Can you edit the question and paste it in? – jvans Sep 03 '13 at 02:03
  • Nothing wrong with your solution but Concern comes to resolve some problems in it such as dependency, and make things simpler. – Billy Chan Sep 03 '13 at 03:30