Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Require dependency to get Rails subclasses

I have following setup:

app/models/my_module/service.rb

module MyModule
  class Service < ActiveRecord::Base
    def self.types
      self.subclasses
    end

    def self.raw_types
      self.types.map { |c| c.name.split("::").last }
    end
  end
end

require_dependency "my_module/service/rack"
require_dependency "my_module/service/rails"
require_dependency "my_module/service/sinatra"

app/models/my_module/service/rack.rb:

module MyModule
  class Service::Rack < Service
  end
end

app/models/my_module/service/rails.rb:

module MyModule
  class Service::Rails < Service
  end
end

app/models/my_module/service/sinatra.rb:

module MyModule
  class Service::Sinatra < Service
  end
end

That works so far, but now my question:

Why I have to add these three lines:

require_dependency "my_module/service/rack"
require_dependency "my_module/service/rails"
require_dependency "my_module/service/sinatra"

to my service.rb file?

If I don't add the three lines:

MyModule::Service.raw_types
=> []

If I add the three lines:

MyModule::Service.raw_types
=> ["Rack", "Rails", "Sinatra"]

Anybody an idea?

Btw: I use Ruby 2.0.0-preview1, Rails 4.0.0.rc1 and create a new Rails engine with

rails plugin new MyModule
like image 425
Matthias Avatar asked May 15 '13 16:05

Matthias


1 Answers

By default, in the development environment, Rails will autoload constants in the usual subdirectories of app, by looking in the conventional place (e.g., /app/models/my_module/service/rack.rb for MyModule::Service::Rack). This autoloading happens when the constant is referenced for the first time, not on app initialization.

But this means that before the constant is referenced, the file that defines it is not loaded unless it is explicitly required.

Thus, when you call MyModule::Service.raw_types, MyModule::Service is loaded from app/models/my_module/service.rb if it isn't already defined. However, if no reference has yet been made to its subclasses, those constants won't be defined unless the files that define them are explicitly required. Thus, requiring those files in the file that is autoloaded on that method call makes them available.

Moral: if you want to ensure that the subclasses of MyModule::Service are always defined whenever MyModule::Service is, you need to require them in /app/models/my_module/service.rb

like image 64
gregates Avatar answered Nov 02 '22 06:11

gregates