Understand all the terms in How Ruby method lookup works, in particular:
include
extend
prepend
Do you understand why object.extend(SomeModule)
is the same as object.singleton_class.include(SomeModule)
?
How does include
and extend
work together with inheritance?
You may also read more about the Ruby Object Model, if all of this is quite confusing to you.
ActiveSupport::Concern
works.(...)
can be quite handy in some cases.Because we have metaprogramming, it might not be apparent where a method is defined. There are several ways to find a source:
Method#source_location
Show archive.org snapshot
.save!
in the
Rails API docs
Show archive.org snapshot
. The result should have a link to the source location on GitHub.Note
Given that Ruby is a dynamic language, RubyMine can only guess. RubyMine rarely knows the type of an object.
Write a monkey patch that patches the #attributes=
methods of all your models.
The monkey patch should print the number of attributes that will be set, then call the original method:
movie = Movie.find(1)
movie.attributes = { title: 'Sunshine', year: '2007' }
# Console prints "Setting 2 attributes"
# Check that the original #attributes= method is still called:
movie.title # => 'Sunshine'
movie.year # => 2007
Write two versions of that patch:
Module#prepend
.Hint
- The base class for all your models is
ApplicationRecord
. Prepend your patch withApplicationRecord#prepend(YourPatch)
.- A monkey patch is usually an initializer Show archive.org snapshot .
- We have some tips for organizing monkey patches.
- When you add or change an initializer you need to restart your Rails server or console for the changes to be picked up. Only changes in
app
are picked up automatically.
Look at the following "mini Active Record" interface:
class Login
include Attributes
attribute :email
attribute :password
attribute :remember_me, default: true
end
login = Login.new
login.email = "me@example.org"
login.password = "mypassword"
login.email # => "me@example.org"
login.password # => "mypassword"
login.remember_me # => true
login.attributes # => { email: "me@example.org", password: "mypassword", remember_me: true }
login.remember_me = false
login.remember_me # => false
Implement the Attributes
module in plain ruby, so it supports the API above.
ActiveSupport::Concern
Add a second implementation AttributesConcern
that uses ActiveSupport::Concern
.
It should work identical to the first implementation.
Modularity
Add a third implementation DoesAttributes
that uses Modularity
.
For modularity we often use an alternative interface:
class Login
include DoesAttribute[:email]
include DoesAttribute[:password]
include DoesAttribute[:remember_me, default: true]
end
Implement this new interface. For ruby 3 make sure that you use at least modularity 3.1.0 Show archive.org snapshot .
Now compare your implementations. Which one do you like best?
Does modularity
offer features the other approaches don't?