how to define/attach rails validations in multiple generations of modules - ruby-on-rails

I've found a way to make this work, but am curious about a better way / the Rails 3 way. (I'm using 2.3.5 still, but hope to migrate around New Year's.)
The situation: I've got two layers of module inheritance, the second layer gets mixed into a Rails model. Both modules define validation methods and I'd like both of them to attach the validations to the base class, but because of the two levels of inheritance, the following doesn't work:
def self.included(base)
base.validate :yadda_yadda
end
When that module is included by another module, the interpreter grinds to a screeching halt because Modules don't know about ActiveRecord::Validations. Including the validations module begs the question of "where is save?" thanks to alias_method.
The following works, as long as you remember to call super whenever you override validate(). I don't trust myself or future maintainers to remember that, so I'd like to use the validate :yadda_yadda idiom instead, if possible.
module Grandpa
def validate
must_be_ok
end
def must_be_ok
errors.add_to_base("#{self} wasn't ok")
end
end
module Dad
include Grandpa
def validate
super
must_be_ok_too
end
def must_be_ok_too
errors.add_to_base("#{self} wasn't ok either")
end
end
class Kid < ActiveRecord::Base
include Dad
validate :must_be_ok_three
def must_be_ok_three
errors.add_to_base("#{self} wasn't ok furthermore")
end
end
Suggestions? Tips for Rails 3 approach? I don't think the validations API has changed that much.

I solved it (when I ran into the same problem, but with something other than validation).
Short answer: you can call send(:included, base) on the module you want to bring in. Within the higher-up included() definition, you need to check whether the base is a Class or a Module.
Why would you ever want to do this? Well, I've got some modules that extract some common functionality out of my models. For instance, the module HasAllocable sets up a polymorphic belongs_to relationship, and a getter/setter pair for a virtual attribute. Now I have another module that needs to pull in HasAllocable, to spare the base classes from having to remember it.
I'd be interested to know whether this smells funny to anyone. I haven't seen anything like it on the web, so I wonder if multiple layers of model inheritance is more of an antipattern.
module Grandpa
def self.included(base)
if base.kind_of?(Class)
base.validate :must_be_ok
end
end
end
module Dad
include Grandpa
def self.included(base)
if base.kind_of?(Class)
# you can do this
#base.send(:include, Grandpa)
# you can also do this
Grandpa.send(:included, base)
# this does not invoke Grandpa.included(Kid)
#super(base)
base.validate :must_be_ok_too
end
end
end
class Kid < ActiveRecord::Base
include Dad
validate :must_be_ok_three
end

Related

Modularizing class-level method calls by evaluating them in `included` methods

I'm working with a Rails project that uses the flip gem for feature flags. We have a Feature class in which you can declare the various feature flags you want to use,
# app/models/feature.rb
class Feature < ActiveRecord::Base
extend Flip::Declarable
strategy Flip::DeclarationStrategy
feature :ivans_feature_A
feature :ivans_feature_B
feature :ivans_feature_C
feature :kramers_feature_X
feature :kramers_feature_X
end
As the project grows, so does the number of feature-flags we've got declared in this file. A coworker suggested we break related feature-declarations into separate modules to organize things.
I've found a way to do this, but it's not a pattern I've seen used before so I wonder if there's a more standard way. I'm defining namespaced modules for each bunch of features I want to group together:
app/models/features/ivans_features.rb
module Features::IvansFeatures
def self.included(base)
base.feature :ivans_feature_A
base.feature :ivans_feature_B
base.feature :ivans_feature_C
end
end
app/models/features/kramers_features.rb
module Features::KramersFeatures
def self.included(base)
base.feature :kramers_feature_X
base.feature :kramers_feature_Y
end
end
...and including them in the feature model:
# app/models/feature.rb
class Feature < ActiveRecord::Base
extend Flip::Declarable
strategy Flip::DeclarationStrategy
include Features::IvansFeatures
include Features::KramersFeatures
end
Is it strange to mix a module into a class for the sole purpose of usingthe included callback to run some method calls on the class?
In your Feature class you can extend ActiveSupport::Concern and then do something like
included do
feature :kramers_feature_X
end

Rails - Best practice for abstract class definition and file naming

I want to define 3 classes:
a MotherClass (abstract, can not be inferred)
a SubClassA (inherits from MotherClass)
a SubClassB (inherits from MotherClass)
What is the best solution to declare it in Rails ?
1. Put everything in app/models/
MotherClass < AR::Base in app/models/mother_class.rb
SubClassA < MotherClass in app_models/sub_class_a.rb
SubClassB < MotherClass in app/models/sub_class_b.rb
Advantage: not very complicated to implement
Inconvenient: a big mess in models folder
2. Create a module for the two subclasses
MotherClass < AR::Base in app/models/mother_class.rb
MotherModule::SubClassA < MotherClass in app/models/mother_module/sub_class_a.rb
MotherModule::SubClassB < MotherClass in app/models/mother_module/sub_class_b.rb
Advantage: same than Solution 1
Inconvenient: naming MotherModule and MotherClass with different names, but they mean almost the same thing
3. Create a module for the 3 classes
MotherModule::Base < AR::Base in app/models/mother_module/base.rb
MotherModule::SubClassA < MotherModule::Base in app/models/mother_module/sub_class_a.rb
MotherModule::SubClassB < MotherModule::Base in app/models/mother_module/sub_class_b.rb
Advantage: very clean
Inconvenient: need some functions in Base to override (table_name for example)
So my question is: What is the best practice in Rails and
- how to name my classes?
- what are their directories?
First of all, I think you must already realize that ruby does not have true abstract classes. But we can approximate the behavior. And while doing so, it sounds like you have a preference toward organizational structure which I will attempt to address.
I must start by saying, however, that I'm surprised that you're coming at the problem so strongly from the organizational angle. First on my mind would be whether I really wanted to implement single table inheritance or not and then let that drive the organizational problem. Usually the answer here is that Single Table Inheritance is not what you actually want. But... let's dive in!
Using Single Table Inheritance
Here's the standard way to utilize and organize models using Single Table Inheritance:
# app/models/mother_class.rb
class MotherClass < ActiveRecord::Base
# An "abstract" method
def method1
raise NotImplementedError, "Subclasses must define `method1`."
end
def method2
puts method1 # raises NotImplementedError if `method1` is not redefined by a subclass
end
end
# app/models/sub_class_a.rb
class SubClassA < MotherClass
def method1
# do something
end
end
# app/models/sub_class_b.rb
class SubClassB < MotherClass
def method1
# do something
end
end
Given the above, we would get an exception when calling MotherClass.new.method2 but not when calling SubClassA.new.method2 or SubClassB.new.method2. So we've satisfied the "abstract" requirements. Organizationally, you called this a big mess in the models folder... which I can understand if you've got tons of these subclasses or something. But, remember that in single table inheritance even then parent class is a model and is / should be usable as such! So, that said, if you'd really like to organize your models file system better then you are free to do so. For example, you could do:
app/models/<some_organizational_name>/mother_class.rb
app/models/<some_organizational_name>/sub_class_a.rb
app/models/<some_organizational_name>/sub_class_b.rb
In this, we are keeping all other things (i.e. the Code for each of these models) the same. We're not namespacing these models in any way, we're just organizing them. To make this work it's just a matter of helping Rails to find the models now that we've placed them in a subfolder of the models folder without any other clues (i.e. without namespacing them). Please refer to this other Stack Overflow post for this. But, in short, you simply need to add the following to your config/application.rb file:
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}')]
Using Mixins
If you decide that Single Table Inheritance is not what you want (and they often aren't, really) then mixins can give you the same quasi-abstract functionality. And you can, again, be flexible on file organization. The common, organizational pattern for mixins is this:
# app/models/concerns/mother_module.rb
module MotherModule
extend ActiveSupport::Concern
# An "abstract" method
def method1
raise NotImplementedError, "Subclasses must define `method1`."
end
def method2
puts method1 # raises NotImplementedError if `method1` is not redefined
end
end
# app/models/sub_class_a.rb
class SubClassA
include MotherModule
def method1
# do something
end
end
# app/models/sub_class_b.rb
class SubClassB
include MotherModule
def method1
# do something
end
end
With this approach, we continue to not get an exception when calling SubClassA.new.method2 or SubClassB.new.method2 because we've overridden these methods in the "subclasses". And since we can't really call MotherModule#method1 directly it is certainly an abstract method.
In the above organization, we've tucked MotherModule away into the models/concerns folder. This is the common location for mixins in Rails these days. You didn't mention what rails version you're on, so if you don't already have a models/concerns folder you'll want to make one and then make rails autoload models from there. This would, again, be done in config/application.rb with the following line:
config.autoload_paths += Dir[Rails.root.join('app', 'concerns', '{**/}')]
The organization with the mixins approach is, in my opinion, simple and clear in that SubclassA and SubClassB are (obviously) models and, since they include the MotherModule concern they get the behaviors of MotherModule. If you wanted to group the subclass models, organizationally, into a folder then you could still do this of course. Just use the same approach outlined at the end of the Single Table Inheritance section, above. But I'd probably keep MotherModule located in the models/concerns folder still.
Even though ruby doesn't really have abstract classes, it's powerful enough to let you implement it yourself by implementing self.included on a mixin module. Hopefully this generic example gives you enough to go on for your particular implementation.
module MotherInterface
def self.included base
required_class_methods = [:method1, :method2]
required_instance_methods = [:fizzle, :fazzle]
required_associations = [:traits, :whatevers]
required_class_methods.each do |cm|
raise "MotherInterface: please define .#{cm} class method on host class #{base.name}" unless base.respond_to?(cm)
end
required_instance_methods.each do |im|
raise "MotherInterface: please define ##{im} instance method on host class #{base.name}" unless base.instance_methods.include?(im)
end
required_associations.each do |ass|
raise "MotherInterface: please be sure #{base.name} has a :#{ass} association" unless base.reflections.key?(ass)
end
base.send :include, InstanceMethods
base.extend ClassMethods
end
# inherited instance methods
module InstanceMethods
def foo
end
def bar
end
end
# inherited class methods
module ClassMethods
def baz
end
def bat
end
end
end
class SubClassA < ActiveRecord::Base
include MotherInterface
# ... define required methods here ...
end
class SubClassB < ActiveRecord::Base
include MotherInterface
end
Some advantages to this approach are:
Yes, you can still technically instantiate the mixin, but it's not actually tied to active record, so it tastes more like an abstract class.
The sub classes get to define their own connection information. You have two databases? Differing columns? Cool, no problem. Just implement your instance methods and stuff appropriately.
The dividing line between parent and child is very obvious.
But, there are disadvantages too:
All the meta programming is a bit more complex. You'll have to think a little abstractly (HA!) about how to organize your code.
There are probably other advantages and disadvantages I haven't considered, kind of in a hurry here.
Now, as far as file locations, I would suggest that the mixin itself, presumably mother_interface.rb, go someplace other than your models folder.
In config/application.rb, throw in a line like this:
config.autoload_paths << File.join(Rails.root, 'app', 'lib')
...and then you can create (rails)/app/lib/mother_interface.rb. Really, you should do it however makes sense to you. I dislike the word "concerns" for this, and other people dislike the word "lib." So, use whatever word you like, or make up your own.
Using Single Table Inheritance with little bit meta programming
# app/models/mother_class.rb
class MotherClass < ActiveRecord::Base
def self.inherited(subclass)
subclass.include(OnlyChildMethods)
end
module OnlyChildMethods
extend ActiveSupport::Concern
included do
def child_method_one
puts 'hi one'
end
def child_method_two
puts 'hi two'
end
end
end
end
# app/models/sub_class_a.rb
class SubClassA < MotherClass
def some_specific_method
#some code
end
end
# app/models/sub_class_b.rb
class SubClassB < MotherClass
def some_specific_method
#some code
end
end
mother_class_instance.child_method_one
=> NoMethodError: undefined method 'child_method_one'
sub_class_a_instance.child_method_one
hi one
=> nil

How to refactor "shared" methods?

I am using Ruby on Rails 3.2.2 and I would like to "extract" some methods from my models / classes. That is, in more than one class / model I have some methods (note: methods are related to user authorizations and are named the "CRUD way") that are and work practically the same; so I thought that a DRY approach is to put those methods in a "shared" module or something like that.
What is a common and right way to accomplish that? For example, where (in which directories and files) should I put the "shared" code? how can I include mentioned methods in my classes / models? what do you advice about?
Note: I am looking for a "Ruby on Rails Way to make things".
One popular approach is to use ActiveSupport concerns. You would then place the common logic typically under app/concerns/ or app/models/concerns/ directory (based on your preference). An illustrative example:
# app/concerns/mooable.rb
module Mooable
extend ActiveSupport::Concern
included do
before_create :say_moo
self.mooables
where(can_moo: true)
end
end
private
def say_moo
puts "Moo!"
end
end
And in the model:
# app/models/cow.rb
class Cow < ActiveRecord::Base
include Mooable
end
In order to make it work this way you have to add the following line to config/application.rb
config.autoload_paths += %W(#{config.root}/app/concerns)
More information:
http://chris-schmitz.com/extending-activemodel-via-activesupportconcern/
http://blog.waxman.me/extending-your-models-in-rails-3
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
My answer has nothing to do with RoR directly but more with Ruby.
Shraing common code may be done in various ways in Ruby. In my opinion the most obvious way is to create Ruby Modules that contain the code and then include them inside your class/model. Those shared modules are frequently under the lib directory of your app root. For example:
# lib/authorizable.rb
module Authorizable
def method1
#some logic here
end
def method2
#some more logic here
end
end
# app/models/user.rb
class User < ActiveRecord::Base
include Authorizable
end
The User class may now invoke method1 and method2 which belong to the Authorizable module. You can include this module in any other Ruby class you'd like, this way you DRY your code.

Creating a Function that works for 2 models in Rails

I have two models which need an identical function. I'd like to learn how to make this DRY the right rails way...
For both models I have:
before_save :assign_uuid
Which in each model has:
def assign_uuid
if self.uuid.nil?
self.uuid = ActiveSupport::SecureRandom.hex(32)
end
end
Since, assign_uuid lives in both models, Where is the one place I should place this func? Also, in the models, where it say's before_save. How do I call the assign_uuid in the location it is located?
Thanks
I'm no Ruby expert, so I'm not sure if this is frowned upon or not, but if I were you, I'd chuck it in a file in lib/ and whack it straight on ActiveRecord::Base with class_eval.
ActiveRecord::Base.class_eval do
def assign_uuid
if self.uuid.nil?
self.uuid = ActiveSupport::SecureRandom.hex(32)
end
end
end
That way, it's available for all your models. Either that, or create a miscellaneous model helpers file and include the module into the models you'd like.
# lib/misc_model_helpers.rb
module MiscModelHelpers
def assign_uuid
if self.uuid.nil?
self.uuid = ActiveSupport::SecureRandom.hex(32)
end
end
end
# models/person.rb
class Person < ActiveRecord::Base
include MiscModelHelpers
before_save :assign_uuid
end
Again, I'm really not 100% on the most rubyish way of doing this. It's probably something completely different. These are just two ways that work for me!
In your lib folder, add a file uuid_lib.rb, and write
module UuidLib
def assign_uuid
if self.uuid.nil?
self.uuid = ActiveSupport::SecureRandom.hex(32)
end
end
end
and inside your model write
include UuidLib
before_save :assign_uuid
An example article explaining modules and mixins in more detail can be found here.
You should add this as a module and mix it into your models, that is the Ruby way to do what you are after.

How does Rails method call like "has_one" work?

I am PHP dev and at the moment I am learning Rails (3) and of course - Ruby. I don't want to believe in magic and so I try to understand as much as I can about things that happen "behind" Rails. What I found interesting are the method calls like has_one or belongs_to in ActiveRecord models.
I tried to reproduce that, and came with naive example:
# has_one_test_1.rb
module Foo
class Base
def self.has_one
puts 'Will it work?'
end
end
end
class Model2 < Foo::Base
has_one
end
Just running this file will output "Will it work?", as I expected.
While searching through rails source I found responsible function: def has_one(association_id, options = {}).
How could this be, because it is obviously an instance (?) and not a class method, it should not work.
After some researching I found an example that could be an answer:
# has_one_test_2.rb
module Foo
module Bar
module Baz
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
def self.included mod
mod.extend(Baz)
end
end
class Base
include Bar
end
end
class Model < Foo::Base
has_one 'CHEEZBURGER'
end
Now running has_one_test_2.rb file will output I CAN HAS CHEEZBURGER. If I understood this well - first thing that happens is that Base class tries to include Bar module. On the time of this inclusion the self.included method is invoked, which extends Bar module with Baz module (and its instance has_one method). So in the essence has_one method is included (mixed?) into Base class. But still, I don't fully get it. Object#extend adds the method from module but still, I am not sure how to reproduce this behaviour using extend. So my questions are:
What exactly happened here. I mean, still don't know how has_one method become class method? Which part exactly caused it?
This possibility to make this method calls (which looks like configuration) is really cool. Is there an alternative or simpler way to achieve this?
You can extend and include a module.
extend adds the methods from the module as class methods
A simpler implementation of your example:
module Bar
def has_one stuff
puts "I CAN HAS #{stuff}?"
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
include adds the methods from the module as instance methods
class Model
include Bar
end
Model.new.has_one 'CHEEZBURGER'
Rails uses this to dynamically add methods to your class.
For example you could use define_method:
module Bar
def has_one stuff
define_method(stuff) do
puts "I CAN HAS #{stuff}?"
end
end
end
class Model
extend Bar
has_one 'CHEEZBURGER'
end
Model.new.CHEEZBURGER # => I CAN HAS CHEEZBURGER?
I commend you for refusing to believe in the magic. I highly recommend you get the Metaprogramming Ruby book. I just recently got it and it was triggering epiphanies left and right in mah brainz. It goes over many of these things that people commonly refer to as 'magic'. Once it covers them all, it goes over Active Record as an example to show you that you now understand the topics. Best of all, the book reads very easily: it's very digestible and short.
Yehuda went through some alternatives on way to Rails3: http://yehudakatz.com/2009/11/12/better-ruby-idioms/
Moreover, you can use a (usually heavily abused, but sometimes quite useful) method_missing concept:
class Foo
def method_missing(method, *arg)
# Here you are if was some method that wasn't defined before called to an object
puts "method = #{method.inspect}"
puts "args = #{arg.inspect}"
return nil
end
end
Foo.new.abracadabra(1, 2, 'a')
yields
method = :abracadabra
args = [1, 2, "a"]
Generally, this mechanism is quite often used as
def method_missing(method, *arg)
case method
when :has_one
# implement has_one method
when :has_many
# ...
else
raise NoMethodError.new
end
end

Resources