Skip to content

Instantly share code, notes, and snippets.

@timriley
Created October 19, 2010 00:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timriley/633357 to your computer and use it in GitHub Desktop.
Save timriley/633357 to your computer and use it in GitHub Desktop.
Association Extensions & Named Scopes
# This is how I would like the models
#
# Order has an extension on its order_transactions association to return the total amount of all the transactions
#
# I want to call this extension method while modifying the association via a named scope, but it doesn't work (see below).
class OrderTransaction < ActiveRecord::Base
scope :purchases, where(:action => 'purchase')
scope :refunds, where(:action => 'credit')
end
class Order < ActiveRecord::Base
has_many :order_transactions, :autosave => true do
def total_amount
sum(:amount) / 100.0
end
end
end
# But it doesn't work
ree-1.8.7-2010.02 > o = Order.first
ree-1.8.7-2010.02 > o.order_transactions.total_amount
=> 18200
ree-1.8.7-2010.02 > o.order_transactions.purchases.total_amount
NoMethodError: undefined method `total_amount' for #<ActiveRecord::Relation:0x1036ff230>
from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/relation.rb:366:in `method_missing'
from (irb):3
# To have it work, I need to create an identical extension on both the association and named scopes. Not DRY!
class OrderTransaction < ActiveRecord::Base
scope :purchases, where(:action => 'purchase') do
def total_amount
sum(:amount) / 100.0
end
end
scope :refunds, where(:action => 'credit') do
def total_amount
sum(:amount) / 100.0
end
end
end
class Order < ActiveRecord::Base
has_many :order_transactions, :autosave => true do
def total_amount
sum(:amount) / 100.0
end
end
end
# And now I can do what I want
ree-1.8.7-2010.02 > o = Order.first
ree-1.8.7-2010.02 > o.order_transactions.total_amount
=> 18200
ree-1.8.7-2010.02 > o.order_transactions.purchases.total_amount
=> 18200
# Here's one solution:
module SummingExtension
def total_amount
sum(:amount) / 100.0
end
end
class OrderTransaction < ActiveRecord::Base
scope :purchases, where(:action => 'purchase') do
include SummingExtension
end
scope :refunds, where(:action => 'credit') do
include SummingExtension
end
end
class Order < ActiveRecord::Base
has_many :order_transactions, :autosave => true, :extend => SummingExtension
end
@modsognir
Copy link

What about:
o.order_transactions.sum(:amount)
o.order_transactions.purchases.sum(:amount)

then you could scope that too,
scope :total_amount, sum(:amount)

@timriley
Copy link
Author

The first two lines work as expected, but I can't seem to have it as a scope:

ree-1.8.7-2010.02 > Order.first.order_transactions.total_amount
NoMethodError: undefined method `includes_values' for 53102823:Fixnum
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/relation/spawn_methods.rb:10:in `send'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/relation/spawn_methods.rb:10:in `merge'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/relation/spawn_methods.rb:9:in `each'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/relation/spawn_methods.rb:9:in `merge'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/named_scope.rb:100:in `cool_amount'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/associations/association_collection.rb:437:in `send'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/associations/association_collection.rb:437:in `method_missing'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/base.rb:1076:in `with_scope'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/associations/association_proxy.rb:203:in `send'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/associations/association_proxy.rb:203:in `with_scope'
  from /Users/tim/.rvm/gems/ree-1.8.7-2010.02@corner/ruby/1.8/gems/activerecord-3.0.0.rc/lib/active_record/associations/association_collection.rb:437:in `method_missing'
  from (irb):5

@modsognir
Copy link

Yeah, you're right, that doesn't work at all. my bad.

Hmm.. not sure if theres a dry-er way to alias it without going to craziness such as
class ActiveRecord::Relation
def total_amount
self.sum(:amount)
end
end

@timriley
Copy link
Author

@modsognir, Check out the last file in the gist. Seems like the DRYest way to do it right now. Works for me.

@modsognir
Copy link

hah, of course. yeah that works well. I might go through ARel later and see if theres an easy way to do something like I originally mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment