ruby on rails类,klass,数组和方法在哪里

时间:2013-03-27 11:15:48

标签: ruby-on-rails arrays class where rails-activerecord

我完全误解了rails中的一种机制......想象一下具有许多属性的产品模型:

class Product < ActiveRecord::Base
    has_many :properties
end

然后,在控制台中,我输入:

p=Product.last #recover the last product created
arr=p.properties #return the properties in an Array
arr.class #return "Array", so it's effectively an Array object.

在Hirb,它给了我:

1.9.3-p385 :161 > arr=p.properties
| id        | name        | presentation  | created_at              | updated_at              | value_type |
+-----------+-------------+---------------+-------------------------+-------------------------+------------+
| 905834907 | internet    | internet      | 2012-09-17 13:37:57 UTC | 2012-10-02 15:46:37 UTC | boolean    |
| 905834906 | three_d     | 3D            | 2012-09-17 13:37:47 UTC | 2012-10-10 13:10:07 UTC | boolean    |
| 161337574 | brand       | Marque        | 2012-05-22 14:13:04 UTC | 2013-03-26 16:12:12 UTC | string     |

等...

然后,如果我这样做:

1.9.3-p385 :162 > arr.where(:value_type => "boolean")
  Spree::Property Load (0.8ms)  SELECT "spree_properties".* FROM "spree_properties" INNER JOIN "spree_product_properties" ON "spree_properties"."id" = "spree_product_properties"."property_id" WHERE "spree_product_properties"."product_id" = 1060500665 AND "spree_properties"."value_type" = 'boolean'
+-----------+----------+--------------+-------------------------+-------------------------+------------+
| id        | name     | presentation | created_at              | updated_at              | value_type |
+-----------+----------+--------------+-------------------------+-------------------------+------------+
| 905834907 | internet | internet     | 2012-09-17 13:37:57 UTC | 2012-10-02 15:46:37 UTC | boolean    |
| 905834906 | three_d  | 3D           | 2012-09-17 13:37:47 UTC | 2012-10-10 13:10:07 UTC | boolean    |
| 905834914 | wifi     | wifi         | 2013-03-26 16:13:35 UTC | 2013-03-26 16:13:35 UTC | boolean    |

所以我在数组上运行where方法......但是:

tab.method(:where) #returns:
NameError: undefined method `where' for class `Array'

如何在无法识别它的对象上进行操作?我有一个想法:

1.9.3-p385 :164 > arr.klass
 => Spree::Property(id: integer, name: string, presentation: string, created_at: datetime, updated_at: datetime, value_type: string) 

但是我真的不明白这个机制......对于我来说,这是一种面向对象语言的新东西。

感谢您的解释。

PH

2 个答案:

答案 0 :(得分:0)

好问题!

控制台的工作方式有点误导:

p=Product.last #recover the last product created
arr=p.properties #return the properties in an Array
arr.class #return "Array", so it's effectively an Array object.

arr.class返回“Array”,因为控制台和activerecord希望作为程序员帮助您。因此,他们会使用arr.all.class代替arr.class

执行arr.to_sql,您会看到这将导致SQL。 Array对象没有方法to_sql

由于延迟加载,您可以使用where(:value_type => "boolean")作为arr的方法。

请查看http://xyzpub.com/en/ruby-on-rails/3.2/queries.html#lazy_loading以了解有关延迟加载的更多信息。

答案 1 :(得分:0)

问题在于,您实际上并未获得Array的实例,而是ActiveRecord::Relation的实例。这是为了让您将所有这些指令链接在一起,如果您尝试访问结果,则执行底层关系并将其转换为数组。 Pry会自动尝试显示结果,以便触发这些转换。

你可以通过这样的方式看到:

[1] pry(main)> Customer.first.contracts ; nil
  Customer Load (0.6ms)  SELECT `customers`.* FROM `customers` LIMIT 1
=> nil

[2] pry(main)> Customer.first.contracts
  Customer Load (0.5ms)  SELECT `customers`.* FROM `customers` LIMIT 1
  Contract Load (0.7ms)  SELECT `contracts`.* FROM `contracts` WHERE `contracts`.`customer_id` = 1
=> [...results...]

[3] pry(main)> class ActiveRecord::Relation
[3] pry(main)*   def to_a
[3] pry(main)*     puts "I converted me (#{(class << self ; self ; end).superclass}) to an Array"
[3] pry(main)*     logging_query_plan do
[3] pry(main)*       exec_queries
[3] pry(main)*     end
[3] pry(main)*   end
[3] pry(main)* end
=> nil

[4] pry(main)> Customer.first.contracts ; nil
I converted me (ActiveRecord::Relation) to an Array
  Customer Load (0.5ms)  SELECT `customers`.* FROM `customers` LIMIT 1
=> nil

[5] pry(main)> Customer.first.contracts
I converted me (ActiveRecord::Relation) to an Array
  Customer Load (0.6ms)  SELECT `customers`.* FROM `customers` LIMIT 1
I converted me (ActiveRecord::Relation) to an Array
  Contract Load (0.6ms)  SELECT `contracts`.* FROM `contracts` WHERE `contracts`.`customer_id` = 1
=> [...results...]

步骤[1]:仅获取Customercontracts - 关系仍未执行,因为Pry没有尝试访问它(由于; nil

步骤[2]:与1.相同但Pry尝试访问该关系,因此我们也会获取这些关系。

步骤[3]:我们覆盖ActiveRecord :: Relation.to_a方法。我只添加了puts,其余的是原始方法(使用super会导致循环!)。

步骤[4]:重复1.和2.现在,每次转换都会触发其他消息。

Ruby中的元编程非常复杂并且引起很多麻烦,但我希望这能使事情变得清晰。