Ruby - constantize in certain directory - ruby-on-rails

In an application, I have a method that gets the name of a policy for a certain ActiveRecord as follows:
def policy_for(record:)
"#{record.class}Policy".constantize.new(record)
end
This works perfectly, except in one case where I have a model and a policy having the same name. This line then returns the model, and not the policy.
In other words, Problem: Multiple classes have the same name.
Goal: Need a constantize alternative that gets the class but in a certain directory, or has a certain superclass for example (BasePolicy instead of ActiveRecord).
How can I do this in Ruby?

1 - Don't have classes with similar names under the same module.
2 - In rails, models will be the outermost context. So consider moving your policies under a Policies namespace. That will solve your issue right and similar other issues down the road.

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.

Why does Active Record use module_eval internally for certain features?

I've noticed a few places in the Rails source code where module_eval is used. One place is in ActiveRecord::Enum and another is in ActiveRecord::Store. I'm familiar with class_eval and instance_eval and have used them for extending the functionality of existing classes or objects, but in the case of module_eval, it seems like it's serving a different purpose.
In both cases they are using a similar pattern to define the module:
def _store_accessors_module
#_store_accessors_module ||= begin
mod = Module.new
include mod
mod
end
end
If the module is being included in the class it's defined in, what benefit is there to defining related methods in a nested module like this? Is it better isolation of code? The reason I'm asking is because I have a gem that adds functionality to Active Record, and am wondering if this approach is more of a "best practices" way of doing the same thing. Here's the relevant source code of my gem for reference.
The reason the methods are being defined in a nested module is so that users may override the methods and still have access to super to get at the original functionality. Recall that when you include modules in Ruby they are inserted into the ancestors list for the current class and super works by simply iterating through the ancestors array, looking for the first object that responds to the current method. To this end, the name of the module is not important as it is simply an inheritance-chain-like delivery mechanism. So that's why they define just an anonymous new module and include it on the fly.
If you look at the blame view for the 2 examples you listed, you can see the reasoning behind the changes. The commit message in the ActiveRecord::Store example makes the case pretty well. As you can see, they're adding the ability to override the accessor definition color and tack on to the results of the original method via super || 'red'. Whereas, in the original implementation, one would have to both override the color accessor method and then do the same work as the original accessor method, i.e. call read_store_attribute(:settings, :color) || 'red'. So it's all about not being forced to reproduce the internals or using alias method chains to augment the functionality of the dynamically defined methods.
I'm not sure whether this is a useful feature in your gem, but I'm guessing maybe not as your accessors seem to be returning well-defined object enum-related objects. But, of course, that's up to you and the gem's users :).

Monkeypatch across Engines in Rails

First of all: Yes, monkey patching is bad, I'm feeling like a sinner already. But in this case I haven't found another solution.
Situation:
Engine 1 (Alchemy-CMS): Provides a page model that contains several elements.
Engine 2 (my Alchemy-Contentable): Should provide a way to bind these elements to any resource (=model) you like.
Purpose is to give the resource cms-capabilities like Alchemy has, that means adding elements like headings, paragraphs, pictures, videos...
On the "page-layer" everything is fine. I created a mixin for a model that identifies itself as contentable towards the element.
I want to stay as close as possible to alchemy and therefore inheriting from element is not an option (new database-table, new associated models...).
So monkey patching seems adequate here (still hacky, but I'm fine with it this time I guess).
Problem:
How do I successfully monkey-patch one engine's classes from another engine?
When I try to reopen classes as usual it complains not to find the constant. Probably this is due to lazy loading of classes.
What works is:
config.after_initialize do
Alchemy::Admin::ElementsController.send(:include, AlchemyContentable::ElementsControllerMixin)
Alchemy::Admin::ElementsController.send(:before_filter, :load_contentable_to_page, :only => [:index, :new, :create])
end
but this seems to be error prone, especially when accessing a newly defined message from a mixin (like Alchemy::Element.my_new_method in model_mixin, which is included in your app's model)
Has someone had a situation like this before? Any ways out? At least a 'reliable' way to create and overwrite some methods in the engine's classes and to call them inside a mixin?
A solution is to monkey-patch the Alchemy::Page model in the following fashion:
require File.expand_path('../../app/models/alchemy/page', Alchemy::Engine.called_from)
module Alchemy
class Page < BaseRecord
# Your code goes here...
end
end
Note the require statement which will ensure that the original class is extended and not overwritten by your file.
See this blog post for more context.

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.

Rails naming conventions for models with forbidden names

I'm writing a rails application, and I need to name one of my model Test, and inside that model I need a class attribute - when I tried the first one, I couldn't run any UT since Test ceased to be a module - so I ranamed to Tst. I haven't even tried naming a column class - I went with clss. What names would you use? Are there any conventions known amongs RoR developers for such situations?
I haven't seen anything for Test but class is typically changed to klass.
Ruby and Rails have several different objects part of a standard library. You cannot use an already-defined class name as name for your models.
For instance, you cannot call a model Thread or Object because Ruby already defines a Thread and Object class.
Likewise, Ruby has a library called Test::Unit so you can't use the Test namespace as model name.
There isn't a real full list of reserve objects because it really depends on your current environment. However, in order to successfully use Rails, you should at least have a basic Ruby knowledge so that you know the names of the most common standard library classes.
I've run up against this a few times (writing educational software--where you regularly want to model 'tests' :-). Depends exactly what you're modeling I suppose, but I usually opt for 'quiz' to avoid conflicts. I'd love to hear a better solution, since I find 'quizzes' an awkward plural.
Like dj2 said, class is usually done as 'klass'.
Please check the following page for Rails reserved words: http://reservedwords.herokuapp.com/ None of these words should be used as class or attribute name.

Resources