开发者

Hash for ActiveRecord's select

开发者 https://www.devze.com 2023-03-15 07:06 出处:网络
Is it possible to pass a Hash as a parameter for the ActiveRecord\'s select? I mean like this (here I have simple model: LineItem has id\'s of Product and Order):

Is it possible to pass a Hash as a parameter for the ActiveRecord's select? I mean like this (here I have simple model: LineItem has id's of Product and Order):

Product.select(
    :orders => [{:name => :buyer}, :email], # orders.name as buyer
    :products => {:title => :product}).     # products.title as product
  joins(:line_items => :order)

To get the following SQL:

SELECT "orders"."name" as "buyer", "orders"."email", "products"."title" as "product" 
FROM "products"
INNER JOIN "line_items" ON "line_items"."product_id" = "products"."id" 
INNER JOIN "orders" ON "orders"."id" = "line_items"."order_id"

If it isn't possible, then I suggest some backward-compatible (can be used in old way, with 1 simple string as a peremeter) extension for the select method, but I can't figure out how to make it as transparent (i.e. w/o _h at the end as I did below) application-wide replacement:

class ActiveRecord::Base
  def self.select_h(*fields) # TODO: rid of this ugly _h
    hash_of_fields = fields.last.is_a?(Hash) ? fields.pop : {}
    fields_in_hash = hash_of_fields.map do |table, field_or_fields|
      (field_or_fields.is_a?(Array) ? field_or_fields : [field_开发者_运维技巧or_fields]).map do |field|
        field = "#{field.first[0]}\" as \"#{field.first[1]}" if field.is_a? Hash
        "\"#{table}\".\"#{field}\""
      end
    end
    # calling original select
    select (fields+fields_in_hash).join(', ')
  end
end

I'll appreciate pointing me at some details about internals of ActiveRecord's implementation regarding it's pattern that was used to make this very obscure-to-exam gem :)


The source of the select method--

# File activerecord/lib/active_record/relation/query_methods.rb, line 34
def select(value = Proc.new)
  if block_given?
    to_a.select {|*block_args| value.call(*block_args) }
  else
    relation = clone
    relation.select_values += Array.wrap(value)
    relation
  end
end

-- suggests that it won't do what you're asking it to. However, as it can optionally take a block you might be able to coerce it into the behavior you're looking for...

Whenever I get confused about what I can and can't do with the Arel stuff, I just peruse the source at:

Arel API

I'm not sure this helps but at the very least it might send you in the right direction to explore...


For your final question of how to avoid the _h, see When monkey patching a method, can you call the overridden method from the new implementation?

class Foo
  def bar
    'Hello'
  end
end 

class Foo
  old_bar = instance_method(:bar)

  define_method(:bar) do
    old_bar.bind(self).() + ' World'
  end
end

Foo.new.bar # => 'Hello World'
0

精彩评论

暂无评论...
验证码 换一张
取 消