How to manually load all classes inside rails application? - ruby-on-rails

How to load & get collection of all active-record models used inside rails app.
It should give classes from gems, plugins as well as subclasses having active-record base in parent hierarchy.
ActiveRecord::Base.descendants.collect(&:name)
gives me list but its only after all classes gets loaded.
Is there any way to load all classes inside rails app manually ?

The problem with Ruby is that "all classes" is a somewhat difficult thing to ascertain. Some of them may be generated dynamically and conditionally.
Sometimes you can just load what's present in app/models:
Dir.glob(File.expand_path("app/models/*.rb", Rails.root)).each do |model_file|
require model_file
end
If there's other locations that may contain models you'll need to include those, too.
You might have dependencies, though, and that can preclude model A from loading before model B. This is why the autoloader is used by default and things just aren't loaded in.
The only reliable way to get them all loaded is to somehow exercise them all at least once.

Getting model list based upon db tables -
ActiveRecord::Base.connection.tables.collect{|t| t.singularize.camelize.constantize rescue nil}.compact

Related

Rails Single Table Inheritance with dynamically defined classes

In using Rails STI (Single Table Inheritance), I have defined a model named Poi (point of interest).
Our app's requirement dictate that subclasses of Poi (like Restaurant, Club, etc) must be created in an Admin::Categories view (where it has a class_name string input field), so that an Admin should be able to create a new subclass at anytime without needing a programmer to open a new ruby file with an empty (useless) subclass and re-deploy the app.
At the same time, IF in the future we want to specify different behavior (either instance/class methods) for a subclass of Poi, we can just create that ruby file, but that should be an option and not mandatory. The same goes for a different form with different fields for that subclass: we just need to setup a partial_name_for_form instance method in that subclass that returns a string with the partial name and the views will render that accordingly. If none is found, the default Poi views are rendered.
Since Rails raises an error if you try to instanciate a new Poi object with 'type' attribute that doesn't match a subclass of Poi (so the subclass MUST be previously defined), we came up with the following solution for dynamically creating the Poi subclasses based on class_name:
An after_create hook in model Category that defines the new class immediately using this code: Object.const_set(category.class_name, Class.new(Poi))
A require_dependency call in model Poi file (because it's in the autoload path) to require the ruby files for the subclasses we have eventually created hardcoded subclasses (only if the file exists):
Category.all.each do |category|
require_dependency category.class_name.underscore if File.exist (File.join("app","models","pois","#{category.class_name.underscore}.rb"))
end
An initializer that defines all remaining classes (by 'remaining' I mean other subclasses that don't still have their own ruby file defining them) using the same code in #1, but checking if Object.const_defined? category.class_name first (because the ones defined by require_dependency don't need to be re-defined).
Even tough all this complexity made we almost regret using STI in the first place, it was working fine - in development.
But in production environment, after the creation of a new category providing class_name, the Class doesn't get defined, because when trying to create a new Poi with that subclass is raising an error uninitialized constant.
I confirmed in Rails console in Production environment that the after_create hook IS working, because the class is being defined there. My wild guess is that because we use Unicorn, this bug could be related to the forking of the application code, but I have no clue how to proceed.
10.5 require_dependency and Initializers
One could think about doing some require_dependency calls in an initializer to make sure certain constants are loaded upfront, for example as an attempt to address the gotcha with STIs.
Problem is, in development mode autoloaded constants are wiped if there is any relevant change in the file system. If that happens then we are in the very same situation the initializer wanted to avoid!
From http://guides.rubyonrails.org/autoloading_and_reloading_constants.html. From what I read here, it seems require_dependency in initializer works differently between environments. I found a similar question here - see if that helps.
I would avoid creating dynamic constants because of a broken model.

Caching bunch of simple queries in rails

In my app there're objects, and they belong to countries, regions, cities, types, groups, companies and other sets. Every set is rather simple - it has id, name and sometimes some pointers to other sets, and it never changes. Some sets are small and I load them in before_filter like that:
#countries = Country.all
#regions = Region.all
But then I call, for example,
offer.country.name
or
region.country.name
and my app performs a separate db query-by-id, although I've already loaded them all. After that I perform query through :include, and this case ids, generated by eager loading, do not depend on either I've already loaded this data with another query-by-id or not.
So I want some cache. For example, I may generate hashes with keys as records-ids in my before_filter and then call #countries[offer.country_id].name. This case it seems I don't need eager loading and it's easy turn on Rails.cache here. But maybe there's some smart built-in rails solution that does not require to rewrite everything?
Caching lists of models like that won't cache individual instances of that exist in other model's associations.
The Rails team has worked on implementing Identity Maps in Rails 3.1 to solve this exact problem, but it is disabled by default for now. You can enable it and see if it works for your problem.

Rails: Tableless model that calls other models

I have a Rails app with a few model classes (e.g. Category, Subcategory, User, etc.). In order to implement a not-too-trivial filter functionality, I built a hierarchy of filter classes: FilterCategory, FilterSubcategory, etc., that derive from FilterBase. Each of them uses the appropriate "sister" model class (e.g. Category.find :all).
I quickly realized that I can't simply call the "sister" model class without using "require" first. However, I now suspect that using "require" is the main reason for two other problems I posted here and here, which probably mess up the class caching when config.cache_classes=false.
Is there another way for me to call these other models without requiring them?
I tried using the BaseWithoutTable plugin, but when I call the "sister model", I end up getting "Not a valid constant descriptor: nil", which occurs since Rails looks for "FilterCategory::Category" rather than "Category".
Any thoughts of the best way to do that?
I'm using Rails 2.3.8, Ruby 1.8.7.
Thanks,
Amit
I wonder if you want ::Category - getting Category from the top-level namespace rather than scoping it to FilterCategory?
If your models are in the app/models directory, you shouldn't need to explicitly require them - Rails already takes care of that.

How can I get model names from inside my app?

I have a bunch of models in a sub-directory that inherit from a model in the models root directory. I want to be able to set a class variable in another model that lists each inherited model class.
i.e
##groups = sub_models.map do { |model| model.class.to_s }
Not sure how to get at this info though...
There is a full answer here. Basically, you can either troll through the files or monkey-patch Class (or ActiveRecord). Those are two ways to go, at least.
Edit: On second thought, Rails won't load your Models until necessary, so while the "files" way seems weak, it's probably your only option (unless you want to do something in your models themselves).
In Rails, in production env this should work
Object.subclasses_of ClassInModelDir
You have to load the files manually If Rails.env.development?
You will not be able to do this unless you preload your descendant models. This is the reason why it is prudent to reengineer your code to use Class#ancestors instead of descendants (also descendants are not a core Ruby feature).

constant values in Rails

I have some data that I want to store somewhere in my Rails app because I use it for generating form fields, checking a submitted form to ensure its values are valid, etc. Basically, I want the data in one location because I make use of it in several places.
Previously, I was defining an initialize method in my controller and initializing instance variables within that method, e.g. #graph_types = ['bar', 'line']. This seemed a bad idea because that's really all initialize was being used for (initializing those values) and the instance variables could be changed later, which I don't want.
Now, I define constants outside of any method in my controller, right up at the top after my filters, and I freeze them, e.g. GraphTypes = ['bar', 'line'].freeze.
I didn't want to store such data in a config file because then I would have to keep track of an extra file, read in the file and parse it, etc. I didn't want to store this data in the database because that seems like overkill; I don't need to do any crazy LEFT OUTER JOIN-type queries combining available graph types with another of my constants, say Themes = ['Keynote', 'Odeo', '37 Signals', 'Rails Keynote'].freeze. I didn't want to store the data in environment.rb because this data only pertains to a particular controller.
Considering all this, am I going about this 'the Ruby way'?
For constants that don't really belong anywhere else I have a StaticData class.
class StaticData
GRAPH_TYPES = ['bar', 'line']
SOMETHING_ELSE = ['A', 'B']
end
Then I get at it with
StaticData::GRAPH_TYPES
The same answer I wrote previously to a similar question applies and posting as this answer still comes up in search results.
Putting a constant in the controller makes some sense as the constant pertains directly to it. Constants should otherwise be put in the dedicated initializer file: Rails.root/config/initializers/constants.rb.
As per the comment listed in application.rb:
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded
This is still valid as of Rails 3.
I believe what you are currently doing is fine; you said the data only pertains to one controller, and therefore that's where it belongs. If it was needed for multiple controllers, or if they were more complex than constant values, other approaches may make sense.
Yes, what you are doing is fine. It's more idiomatic Ruby to call your constant GRAPH_TYPES though.
Incidentally, I would avoid defining initialize in your controllers. Seems like it could lead to trouble.
I would agree some what with IDBD and paradisepete. Using constants in the model would be the best way to go so that the controller is skinny and the model fat. see Rails view tips
For example if you had a metrics controller linked to a metric model. In the metric model
class Metric < ActiveRecord::Base
GRAPHTYPES = ['bar', 'line']
Then in the view you could do something like
f.select :graph_type, Metric::GRAPHTYPES
If you are generating forms that are related to some resource then it will be good variant to store it in the models. You don't need to store it in DB because it can be simple class or instance variables/methods.
The same idea is for validation. If you are validating resources/model instances then it will be reasonable choice to store validation parameters inside model class.
Anyways, it will be much closer to the 'thick model and thin controller' pattern then any of the variants you mentioned.

Resources