What are before_create, validates_presence_of, has_many etc? - ruby-on-rails

I understand what these statements do, but not how to refer to them. They exist within a class, outside of that class's methods and perform a variety of functions.
Collectively, what are they called?

These methods are really just class methods. Try this:
class Test
def self.before_create
puts "before_create"
end
before_create
end
The specific use case you mentioned - Rails DSL methods such as before_create, that are only available inside a class body — are often called class macros. Rubys metaprogramming abilities give you multiple ways to build them. A simple one is to make them private:
module Foo
private
def before_create
puts "before_create"
end
end
class Bar
extend Foo
before_create
end
before_create is now accessible inside the class body, but not from outside:
Bar.before_create
NoMethodError: private method `before_create' called for Bar:Class

In pure Ruby terms, they are all just method calls.
However, they do have a common theme. In the way they are constructed and used, you could consider them part of a Domain-Specific Language (DSL) - the ones you list are part of Active Record's DSL for creating data models.
Ruby lends itself well to creating DSL-like mini languages, using mix-ins or a base class in order to provide a set of class methods, which in turn will store data or create methods on the class and instances of it using meta-programming techniques.

Related

Ruby/Rails - force subclasses to override specific methods?

I'm wondering if there is a way to force a subclass to override a method from its parent method in either Ruby or Rails (in Java you would do this with an abstract class/method).
Let's say I have the following classes:
class Pet
end
class Dog < Pet
def collar_color
"red"
end
end
class Cat < Pet
end
dog = Dog.new
dog.collar_color
==> red
cat = Cat.new
cat.collar_color
==> NoMethodError
In this example I would never instantiate a Pet object, but it exists to serve as a way to collect common methods to common classes. But let's say I want to ensure that all subclasses of Pet override the collar_color method. Is there a way to do that? Could I achieve it through testing in some way? Assume I don't want a default defined in the parent class.
My real-life use case is a collection of classes that all have polymorphic ownership of another class. If I have a display page of the owned class, then one of the owner classes not having a method could leave me with a NoMethodError problem.
No, there is no way to enforce this.
I can guarantee you, whatever idea you can come up with, it will break in some way.
First off: doing this statically is out of the question. Determining whether a method is overridden or not is known to be equivalent to solving the Halting Problem.
So, you have to do it dynamically. But even that is going to be problematic.
For example: you could implement the inherited hook and check whether every class that inherits from Pet implements the method. But, that will prevent someone from inheriting their own abstract class. (Also, there is no guarantee when the inherited hook will run – it could run when the class is opened, i.e. before the methods are defined.)
Also, even if you can check that the method exists at the point where a class inherits Pet, the method can still be removed again later, so you don't get any guarantees. And, of course, they can just provide a dummy method, in order to get around your protection.
You could create default implementations of the methods that just raise an Exception, but there is no need to do that: if you don't create a default implementation, that will already raise a NoMethodError exception anyway. (If you do go down this route, do not use NotImplementedError. Instead, use a custom exception that inherits from RuntimeError.)
There are examples of this in the core library: for example, the Enumerable mixin depends on an abstract method each that must be implemented by subclasses. And the way this is handled is by simply documenting that fact:
Usage
To use module Enumerable in a collection class:
Include it:
include Enumerable
Implement method #each which must yield successive elements of the collection. The method will be called by almost any Enumerable method.
That is actually the way any type-related issues have been dealt with in Ruby since the beginning. Since Ruby does not have types, typing only happens in the programmer's head and type information is only written down in documentation.
There always were informal third-party type annotation languages that were used by various IDEs. More recently, two type annotation languages have been introduced: RBI, a third-party type annotation language used by the Sorbet type checker, and RBS, a type annotation language that is part of Ruby proper.
As far as I know, RBS has no way of expressing abstract methods, but RBI does:
class Pet
extend T::Sig
extend T::Helpers
interface!
sig {abstract.returns(String)}
def collar_color; end
end
This will give you a type error if there is object instantiated from a subclass that does not at some point in the inheritance chain define the method. But, of course, only if the user of the code actually type-checks the code using a type-checker like Sorbet. If the user of the code does not type-check it, they will not get a type error.
Ruby has relatively few keywords but it provides the basic building blocks to implement something that vaguely resembles abstract classes or methods.
In its simplest form you just raise an error in the parent "abstract" method:
class AbstractMethodError < StandardError
def initialize(klass, m)
super("Expected #{klass} to implement #{m}")
end
end
class Pet
def collar_color
raise AbstractMethodError.new(self.class, __method__)
end
end
class Cat < Pet
end
Cat.new.collar_color # Expected Cat to implement collar_color (AbstractMethodError)
__method__ is a magic variable that contains the name of the current method.
You can make this a bit more elegant by creating a class method that defines the "abstract method":
module Abstractions
def abstract_method(name)
define_method(name) do
raise AbstractMethodError.new(self.class, __method__)
end
end
end
class Pet
extend Abstractions
abstract_method :collar_color
end
However Ruby is a dynamic langauge and you don't have a compile time check so this will only give a slightly more obvious error message when the method is called. It doesn't actually give any guarentees that subclasses implement the method.
That is down to testing or using type checkers like Sorbet or RBS. In general it might be helpful when learning to forget everything you think you know about Object Oriented Programming and learn the Ruby way. It has a very different design philophy compared to Java - instead of abstract methods and interfaces you use duck typing to see if the object responds to that method.
Just define the default method implementation in the abstract class by raising Not implemented error or something. By doing that you also clarifies in your class design that when others / you want to inherit the Pet class they need to override collar_color method. Clarity is a good think and there is no benefit in not defining a default method in the abstract class.
Or if you want to achieve that by testing you can create a test case for Pet class that check if its descendants is defining their own collar_color method or not. I think Rails / Ruby 3.1 have .descendants methods defined or you can just google them.
# Pet_spec.rb
describe "descendants must implement collar_color" do
it "should not throw error" do
descendants = Pet.descendants
descendants.each do |descendant|
expect { descendant.new.collar_color }.to.not raise_error
end
end
end

What's the difference between sending :include to class and directly defining method in second class definition?

Recently I had to add a method to Redmine's core class. I was unable to use inheritance, so I've done something like this:
require_dependency 'time_entry_query'
class TimeEntryQuery < Query
def my_new_method(foo, bar)
end
end
and it works perfectly - my method is added to all new objects. However, I've seen someone declaring the new method in their own module instead and then sending :include to class, so it become a mixin. Here's an example:
module Patches
module SomeClassPatch
def my_new_method
end
end
and somewhere in app's initialization:
SomeClass.send(:include, Patches::SomeClassPatch) unless SomeClass.include? (Patches::SomeClassPatch)
What's difference between these two methods and which one should I use?
There are two differences:
When you use a mixin, there is a clear place where your "patch" methods can live. If I wonder "Hmm, where's this my_new_method" coming from, and I look at, say, TimeEntryQuery.ancestors or TimeEntryQuery.instance_method(:my_new_method).owner, that will return Patches::SomeClassPatch. So I know I have to look for a file named lib/patches/some_class_patch.rb somewhere to find where it is probably defined. (I could try source_location as well, but that is not always reliable.)
Mixing in a module into a class makes the module the superclass of the class it is being mixed into. So, if there already is a my_new_method defined in TimeEntryQuery, your first option will overwrite it, whereas in your second option, your method will become the super method of that method. IOW: with your second option, your new method won't be called unless the already existing method calls super.

How to Implement Mongoid callbacks in a Module

I'm trying to implement a model auditor that looks for changes to the Mongo. Originally, I tried to make a base class that my models inherit from, but I found out that's not possible.
I'm adding a module to a model that relies on Mongoid. The module contains after_create, after_update, and after_destroy callbacks. And that's the problem... In order to get the callbacks to work as if they are class-level methods, I have to do something this.
module Auditor
def self.after_create
#after create code
end
end
However, I this will override any after create calls inside my model.
Is there a way I can modify my Auditor module's after_create method to accept what the model wants to run callbacks on?

How do you stub ActiveRecord::Base methods without making assumptions about how it's used?

ActiveRecord::Base has a big ol' API with multiple methods for both finding and saving objects. For example, your AR::B objects might have been instantiated from a number of methods:
Foo.new(…)
Foo.create(…)
Foo.find(…)
Foo.find_by_sql(…)
Foo.find_[all_]by_*(…)
bar.foos (associations)
…and finder methods on associations, of course
Similarly, the object in question might get persisted by a few different methods:
foo.create or foo.create!
foo.save or foo.save!
foo.update_attributes or foo.update_attributes!
Now, when writing unit tests, it's good practice to stub external method calls so that your test can focus on the business logic of the method in question. However, when it comes to working with AR::B objects – for example in controller unit tests – it seems like you have to commit to one of the above methods, when actually as far as the business logic of the method is concerned it shouldn't be important which you choose.
Do you have to couple the behaviour of your method this tightly with its implementation or am I missing something simple?
One approach is to build your classes in such a way that you wrap up any ActiveRecord::Base method calls in your own methods.
So instead of calling Foo.new(…) directly...
class Foo < ActiveRecord::Base
def self.create_object(…)
new(…)
end
end
That way in your tests you can stub out your own methods instead of ActiveRecord's.
This approach (including its' benefits) is outlined in detail by Avdi Grimm in the book 'Objects On Rails'... http://objectsonrails.com

Factoring out belongs_to into a common module

I'm factoring out the meat of an ActiveRecord class A into a module M, so that I'm able to duplicate certain data pipelines. The original A had AR macro methods such as belongs_to :X. Although I'm able to separate out non-AR things fine into the module and mix it back into A, the module does not know anything about belongs_to or B out of the box. How do I make those available to the module, and then mix it back into the new shallow A, which only includes M now, and B, which is a clone of A with its own underlying AR table? Or should I write something like an acts_as plugin (right?) instead of M? Retaining belongs_to in A and duplicating it in B works, but defeats the DRY...
When to create a module and when to use inheritance?
A question came up today that got me thinking about how much Ruby on Rails developers really understand about the tools they use.
The question related to refactoring two models that shared functionality. A common enough requirement and a very sensible thing to want to do but the comments and solutions raised my eyebrows some what.
This was the question, somewhat edited and reformatted to make it clearer
I'm factoring out the meat of an ActiveRecord class A into a module M,
so that I'm able to duplicate certain data pipelines.
The original A model had ActiveRecord macro methods such as belongs_to
:X.
Although I'm able to separate out non-AR things fine into the module
and mix it back into A, the module does not know anything about
belongs_to or anything about AR model B out of the box.
How do I make those available to the module, and then mix it back into
the new shallow A, which only includes M now, and B, which is a clone
of A with its own underlying AR table? Or should I write something
like an acts_as plugin (right?) instead of M? Retaining belongs_to in
A and duplicating it in B works, but defeats the DRY principle
What I didn't understand was why the person asking the question was putting this code into a module instead of into a class that the models could descend from.
In Rails (almost) every class descends from another class right?
You see code like
class MyModel < ActiveRecord::Base
all over the place. Fine that ::Base might seem a little mysterious and I can see how that kind of hides what is going on here so lets look at the controller example
All controllers descend from ApplicationController when first generated right?
So you get
class MyController < ApplicationController
How many of you put code into the application controller like before filters and current)user methods and end up using that code in your controllers and views?
Once you take time to think about it a bit then you can see that if you put code in ApplicationController that is public or protected then all controllers that descend from ApplicationController get that functionality right?
ApplicationController is just a class that descends from ActionController::Base the definition looks like this
class ApplicationController < ActionController::Base
Now that looks so familiar the above usage is so common that I start to think that a lot of Rails developers struggle to see the wood for the trees.
This is all about inheritance.
Rails puts a bunch of methods into the ActionController::Base and ActiveRecord::Base classes (That's all they are, classes inside a module) so you can descend your own classes from these classes thereby inheriting the methods and functionality provided by these base classes.
So why not create an abstract ActiveRecord::Base class to solve the problem. This seemed to me the most totally obvious and natural approach to take.
I came up with this solution
In a_base_class.rb
class ABaseClass < ActiveRecord::Base
abstract true
has_many :whatevers
belongs_to :whatevers
def common_methods
#some code here
end
end
Then class a
class A < ABaseClass
# Whatever
end
This could be placed inside a module for namespacing purposes
If you want to put that in a module then descend from WhateverModule::ABaseClass then that's a cool way of name spacing the new base class so that class names don't conflict and that is one of the main purposes of using a module. To name space classes.
Obviously use whatever real class names that make sense to you.
#Rishav Rastogi provided a great answer for using modules and this is what got mne really wondering why thwe solution was not so clear to others and why the question had even been asked in the first place and I started to get the impression that people don't really know what code like this really does
class SomeController < ApplicationController
and
class MyModel < ActiveRecord::Base
When this is stuff that Rails developers use every day?
It's all about inheritance.
Abstract and non abstract classes all inherit from a single class right? The class being inherited from may well inherit from a number of other classes forming a single inheritance chain but it's still single inheritance. each class only descends from one other class.
So what can modules do to help?
Modules are somewhat confusingly used for 2 purposes.
1) To name space things as already mentioned
2) To provide a multiple inheritance scenario. Multiple inheritance is a dirty word in the development world. Things can end up in a right mess but modules provide quite a neat solution.
An example of why you would want multiple inheritance
ActiveRecord::Base provides methods like find_by_something and find.all that return an array of ActiveRecord::Base objects (classes are the code objects are actual things)
Knowing this it would make sense to have the Base class inherit from the array class, but if it did that then it wouldn't be able to inherit from any other more appropriate class. The solution is to mix in a module. If the module contains the array class you get all the juice of array functionality such as .each and .empty? plus all the juice of the other classes that ActiveRecord::Base uses.
So when to use a module and when to inheritance?
Use a module for name spacing (classes can live inside a module)
Use a class for inheritance
So use them both together at the same time unless you want multiple inheritance in which case just use modules
Basically belongs_to is a class method. so you can always. You can write a acts_as plugin as well
module ActiveRecord
module Acts
module M
def self.included(base)
base.extend M::ClassMethods
end
module ClassMethods
def acts_as_m
class_eval <<-CLASS_METHODS
belongs_to :X
CLASS_METHODS
end
end
end
end
ActiveRecord::Base.send(:include, ActiveRecord::Acts::M)
class A
acts_as_m
end
Its generally just about running class_eval

Resources