I am reading Rails source code of ActiverRecord::QueryMethods to understand how SQL of eager_load created.
I have gotten a question. Where is spawn object comes from? Moreover, I would like to ask how do eager_load, includes, preload work.
ActiveRecord::QueryMethods
rails/activerecord/lib/active_record/relation/query_methods.rb
module ActiveRecord
module QueryMethods
extend ActiveSupport::Concern
class WhereChain
#leave out some codes
def eager_load(*args)
check_if_method_has_arguments!("eager_load", args)
spawn.eager_load!(*args)
#-----------------
Where spawn object comes from?
#-----------------
end
end
end
It seems spawn_methods file does something but I can not find require file
My guess 1 : ActiveRecord::SpawnMethods
rails/activerecord/lib/active_record/relation/spawn_methods.rb
module ActiveRecord
module SpawnMethods
def spawn #:nodoc:
clone
end
end
end
My guess 2 : ActiveRecord::Associations
rails/activerecord/lib/active_record/associations/collection_proxy.rb
module ActiveRecord
module Associations
class CollectionProxy < Relation
def scope
#association.scope
end
alias spawn scope
end
end
end
**If you have any advise to read code effectively, please give me some. I will appreciate it.
The following modules are all included in ActiveRecord::Relation: (rails/activerecord/lib/active_record/relation.rb)
FinderMethods
Calculations
SpawnMethods
QueryMethods
Batches
Explain
Delegation
This is what allows you you to continually chain these methods together as they will always return an ActiveRecord::Relation object.
So both your guesses are correct. When a ActiveRecord::Relation is returned it will use the SpawnMethods definition and when a ActiveRecord::Associations::CollectionProxy is returned it will use the scope definition aliased as spawn.
See the ActiveRecord::Relation Code for more details.
Related
I have the following standard Rails ActiveRecord Foo defined:
# app/models/foo.rb
class Foo < ApplicationRecord
end
And I'm trying to call Foo.find(..) from within a hierarchy that contains a module also named Foo..
# lib/commands/bar.rb
module Commands
module Bar
module Create
class Command
def initialize(params)
...
Foo.find(params[:foo_id]
...
end
end
end
end
end
# lib/commands/foo.rb
module Commands
module Foo
module Create
class Command
...
end
end
end
end
Ruby/Rails is finding Commands::Foo instead of my Foo Model and throwing undefined method 'find' for Commands::Foo:Module.. how can I point at the correct ActiveModel implementation?
The obvious answer is to rename Commands::Foo.. to Commands::Foos.. but I'm curious to know if there's another way :o)
If you want to avoid the clash then you should rename the modules. The existing structure is unwieldy and will present similar problems to all future maintainers.
The best solution that I find in your code is to ensure you call the appropriate module and method via its full path:
2.3.3 :007 > ::Commands::Foo::Create::Command.new
"Commands::Foo::Command reached"
=> #<Commands::Foo::Create::Command:0x007ffa1b05e2f0>
2.3.3 :008 > ::Commands::Bar::Create::Command.new
"Commands::Bar::Command reached"
=> #<Commands::Bar::Create::Command:0x007ffa1b04f110>
You shouldn't try to override or modify internal Rails calls, because then you've modified the framework to fit code, which leads to unpredictable side effects.
You can try to call::Foo in Commands::Foo, it should go with your Foo model
I want to create a module for query objects. I created a file:
app/queries/invoices/edit.rb
with this class:
module Queries
module Invoices
class Edit
end
end
end
However, I can't initialize it:
2.3.3 :001 > Queries::Invoices::Edit.new
NameError: uninitialized constant Queries
When I omit the Queries module, everything works:
module Invoices
class Edit
end
end
2.3.3 :005 > Invoices::Edit.new
=> #<Invoices::Edit:0x007fc729e15558>
Why is that?
The first level under app isn't considered part of the namespace. It's why you don't say, for instance:
module Models
class Foo < ActiveRecord::Base
end
end
for a model like app/models/foo.rb.
If you want Queries in your namespace, you could do something like:
app/queries/queries/invoices/edit
But, that looks icky to me. I think I'd do something more like:
app/queries/invoice_queries/edit
and then:
module InvoiceQueries
class Edit
end
end
#jvillian's answer is correct. However, I don't like both choices :)
What I do in my projects is put all those custom object types into app/lib
app/lib/queries/invoices/edit.rb
app/lib/services/invoices/sync.rb
This way lib serves as that padding, which pushes queries to be part of namespace. Also, all your "non-standard" code is nicely contained in one directory.
In Python, you can write a decorator for memoizing a function's response.
Is there something similar for Ruby on Rails? I have a model's method that makes a query, which I would like to cache.
I know I can do something inside the method, like:
def foo(param)
if self.cache[param].nil?
self.cache[param] = self.get_query_result(param)
else
self.cache[param]
end
end
However, given that I would do this often, I'd prefer a decorator syntax. It is clearer and better IMO.
Is there something like this for Ruby on Rails?
I usually do this using custom accessors, instance variables, and the ||= operator:
def foo
#foo ||= something_or_other
end
something_or_other could be a private method on the same class that returns the object that foo should be.
EDIT
Here's a slightly more complicated solution that lets you cache any method based on the arguments used to call them.
class MyClass
attr_reader :cache
def initialize
#cache = {}
end
class << self
def cacheable(symbol)
alias_method :"_#{symbol}_uncached", symbol
define_method(symbol) do |*args|
cache[[symbol, *args]] ||= (send :"_#{symbol}_uncached", *args)
end
end
end
end
How this works:
class MyClass
def foo(a, b)
a + b
end
cacheable :foo
end
First, the method is defined normally. Then the class method cacheable is called, which aliases the original method to a new name, then redefines it under the original name to be executed only if it's not already cached. It first checks the cache for anything using the same method and arguments, returns the value if present, and executes the original method if not.
http://martinfowler.com/bliki/TwoHardThings.html:
There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
Rails has a lot of built in caching(including query caching). You might not need to do anything:
http://guides.rubyonrails.org/caching_with_rails.html
Here is a recent blog post about problems with roll your own caching:
http://cmme.org/tdumitrescu/blog/2014/01/careful-what-you-memoize/
so i've got one pretty simple method in a model here:
def log
self.statistics.build()
self.save
return
end
now i wanted to exclude this method into a module to use it in different models.
module Statistic
def log
self.statistics.build()
self.save
return
end
end
i added the file to the autoload paths and included it into my model (the inclusion works fine).
class Foo < ActiveRecord::Base
include Statistic
end
trying to call the .log method results in an error:
undefined methodnew' for Statistic:Modulethe raised line number is theself.statistics.build()` line.
any ideas why this is not working?
thanks for all hints! please leave a comment if something is unclear.
I think this is a naming clash.
It seems you have a has_many :statistics, by default this will look for a class called Statistic.
But this is the same name as the module you have created.
I suggest renaming your module to StatisticsExtensions or something of that sort.
The reason you're seeing a missing method for new is because build is an alias for new in the ActiveRecord source. It's assuming statistics is an active record relation, and it's not.
https://github.com/rails/rails/blob/d22592a05b299c21e30ec8b38890a178dca863b4/activerecord/lib/active_record/relation.rb#L83
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