Fred Willmore Fred Willmore - 1 year ago 70
Ruby Question

reopening class in initializer breaks attribute accessors

I'm trying to use an initializer in Rails 4.2 to add a method to an ActiveRecord model. However, simply reopening the class in the initializer breaks the existing attribute accessors.

model app/models/thing.rb:

class Thing < ActiveRecord::Base

migration db/migrate/20160914144414_create_things.rb:

class CreateThings < ActiveRecord::Migration
def change
create_table :things do |t|
t.integer :test_field
t.timestamps null: false

initializer config/initializers/thing.rb:

class Thing
def self.new_method

test file test/models/thing_test.rb:

require File.expand_path("../../test_helper", __FILE__)

class ThingTest < ActiveSupport::TestCase
test "the truth" do
thing =
thing.test_field = 1
puts thing.new_method

When I run my test here's what I get:

❯❯❯ rtest test/models/thing_test.rb
1) Error:
NoMethodError: undefined method `test_field=' for #<Thing:0x00000101464658>
test/models/thing_test.rb:6:in `block in <class:ThingTest>'

If I use this alternate syntax to add the method to the class, it works:

Thing.class_eval do
def self.new_method

I guess I'm happy to do it this way, but I would like to know why it doesn't work just reopening the class.

Answer Source

The key thing is that you're not reopening the class: you're defining a new one. In development and in tests your classes are loaded when they are first used. By defining Thing in your initializer you prevent your thing.rb file from ever being loaded.

When you use Thing.class_eval you're not defining a new class so rails loads Thing from thing.rb: at this point your are adding to Thing rather than replacing it.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download