Ruby on Rails ActiveRecord scopes vs class methods - ruby-on-rails

Rails internally converts scopes to class methods then why can't we use class methods itself instead of going with scopes.

From the fine guide:
14 Scopes
[...]
To define a simple scope, we use the scope method inside the class, passing the query that we'd like to run when this scope is called:
class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
class Article < ActiveRecord::Base
def self.published
where(published: true)
end
end
Note in particular:
This is exactly the same as defining a class method, and which you use is a matter of personal preference
And a little further (the Rails3 guide says the same thing here BTW):
14.1 Passing in arguments
[...]
Using a class method is the preferred way to accept arguments for scopes.
So which you use is a matter of preference and it is even recommended that you use class methods for scopes that take arguments.
Using scope is mostly a notational issue. If you say scope :whatever then you're explicitly saying that whatever is meant to be a query builder; if you say def self.whatever then you're not implying anything about the intent of the whatever method, you're just defining some class method that may or may not behave like a scope.
Of course, 14.1 makes a mess of this notational distinction by recommending that you not use scope when your scope takes arguments. Also keep in mind that in Rails3 you could say:
scope :published, where(published: true)
so an argumentless scope was visually "clean" and terse but adding a lambda to handle arguments would make it look messier:
scope :pancakes, ->(x) { where(things: x) }
But Rails4 wants lambdas even for argumentless scopes the distinction makes even less sense now.
I suspect that the difference is historical at this point. Scopes were probably something special back in the before times but became plain old class methods in the Rails3 era to cut down on duplication and to better mesh with the new query interface that came with Rails3.
So you can skip scope and go straight to class methods if you wish. You're even encouraged to do so when your scope takes arguments.

Scopes are just class methods.
Internally Active Record converts a scope into a class method.
"There is no difference between them" or “it is a matter of taste”. I tend to agree with both sentences, but I’d like to show some slight differences that exist between both.
This blogs explains the difference very well.

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 are before_create, validates_presence_of, has_many etc?

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.

Update a relation in a scope?

How can I have something like an ActiveRecord scope that modifies the relation it's acting on?
Specifically, I want to be able to do this:
Model.some_scope.fake_destroy_all
Where fake_destroy_all is the functionality I'm trying to create. It'll be pretty much equivalent to .update_all(deleted: true).
A weak alternative would be:
def fake_destroy_all(relation = Model)
relation.update_all(deleted: true)
end
#...
Model.fake_destroy_all(Model.some_scope)
But that's not ideal. What I'd like to do is something like:
scope :fake_destroy_all, update_all(deleted: true)
But that doesn't work.
Is it possible to do something like what I'm describing?
Try this:
def self.fake_destroy_all
self.update_all(deleted: true)
end
It turns out that relations retain all the class methods. Scopes are class methods as well, and it's the exact mechanism that allows scopes to be "chained".
Therefore, relations with this model will also have this method with self being an instance of a relation.
The scope syntax can probably be fixed like so (cannot test right now, but note the lambda instead of simple method call):
scope :fake_destroy_all, -> { update_all(deleted: true) }
I suppose, without updating inside a lambda it effectively calls this method on invoking scope, on class definition. It does work with simple where-type scopes because where has no side-effect, it only returns an applicable filter. It's evaluated once and saved in a method for use in the future as-is.
With a lambda, however, the execution is held until the scope is actually applied, not until defined. It's necessary, because update_all does have a side-effect.
The same trick is useful if you are specifying a scope with a constantly changing condition, the simplest case being dependency on Time.now, that changes over time and needs to be re-evaluated each time.
However, I suggest you use a class method: a scope is more like a filter, whereas this is more like a "filter enforcement". While this might work too, scopes are semantically for different use cases.

Where to place common logic with RoR

I have a model Project that appears in multiple controllers in an application I'm building as it appears on multiple pages. The where clause for this isn't complicated, per se, but I feel like it is too large to be repeated on every method requiring projects with these constraints.
My question is, where, if possible, does this common call for Projects go? In .NET, I'd have a ProjectService class with a method that would return all projects, and another that returned all projects that satisfied my conditions. I'm new to Rails so I'm struggling to see where this fits in?
You can either use a class method or Scopes.
class Project < ActiveRecord::Base
# example for a scope
scope :awkward_projects,where(awkward: true)
# example for a class method.
def self.awkward_projects
where(awkward: true)
end
end
Its very safe to do what was once given in a SO answer. Read below and choose carefully.
Quoting an answer
"Generally, I use scope entries for simple one-liners to filter down my result set. However, if I'm doing anything complicated in a "scope" which may require detailed logic, lambdas, multiple lines, etc., I prefer to use a class method. And as you caught, if I need to return counts or anything like that, I use a class method."

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

Resources