我有一些列表,它们几乎相等,只是它们正在调用其他方法(相关模型上的作用域方法)。所以我想我可以使用ruby的send方法来调用该方法。但失败并显示以下错误:
错误的参数数量(给定0,应为1..3)
我的(简化的)班是:
class Zone < ApplicationRecord
has_many :customers
has_many :orders, through: :customers
end
class Order < ApplicationRecord
scope :open, -> { where(status: 'open') }
end
因此,如果我致电@zone.orders.open
,则会收到所有具有预期状态的订单。
但是,如果我做@zone.orders.send(:open)
,它将失败,并出现上述错误。
这个想法是使用.send()
作为局部变量,只是传递一个符号来检索@zone.orders
上的其他作用域。
在Rails控制台上:
2.5.1 :002 > zone = Zone.last => #<Zone id: ...> | > zone.orders.send(:open) | Creating scope :open. Overwriting existing method Order.open. | Creating scope :route. Overwriting existing method Order.route. | Traceback (most recent call last): | 3: from (irb):3 2: from (irb):3:in open' 1: from (irb):3:in initialize' ArgumentError (wrong number of arguments (given 0, expected 1..3))
答案 0 :(得分:4)
这里的问题是名称与用于打开IO流的Kernel#open
冲突。
irb(main):001:0> z = Zone.create
(0.2ms) BEGIN
Zone Create (0.8ms) INSERT INTO "zones" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-10-19 16:29:40.018339"], ["updated_at", "2018-10-19 16:29:40.018339"]]
(0.7ms) COMMIT
=> #<Zone id: 10, created_at: "2018-10-19 16:29:40", updated_at: "2018-10-19 16:29:40">
irb(main):002:0> z.orders.send(:open)
Creating scope :open. Overwriting existing method Order.open.
ArgumentError: wrong number of arguments (given 0, expected 1..3)
from (irb):2:in `initialize'
from (irb):2:in `open'
from (irb):2
irb(main):003:0> z.orders.method(:open)
=> #<Method: Order::ActiveRecord_Associations_CollectionProxy(Kernel)#open>
irb(main):004:0> z.orders.method(:open).call
ArgumentError: wrong number of arguments (given 0, expected 1..3)
from (irb):4:in `initialize'
from (irb):4:in `open'
from (irb):4:in `call'
from (irb):4
irb(main):005:0> z.orders
Order Load (0.8ms) SELECT "orders".* FROM "orders" INNER JOIN "customers" ON "orders"."customer_id" = "customers"."id" WHERE "customers"."zone_id" = $1 LIMIT $2 [["zone_id", 10], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):006:0> z.orders.open
Order Load (0.9ms) SELECT "orders".* FROM "orders" INNER JOIN "customers" ON "orders"."customer_id" = "customers"."id" WHERE "customers"."zone_id" = $1 AND "orders"."status" = $2 LIMIT $3 [["zone_id", 10], ["status", "open"], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation []>
您可以看到,调用在加载集合之后先转到Kernel#open
,然后转到范围定义的方法。我猜这是由于ActiveRecord::Associations::CollectionProxy
对关联目标类进行了某种形式的延迟传播。由于CollectionProxy
已经有一个open
方法(Kernel#open),因此它使用它。
一种简单的解决方案,而不是动态调用,是使用带有参数的范围:
class Order < ApplicationRecord
belongs_to :customer
scope :with_status, ->(status){ where(status: status.to_s) }
end
或者甚至更好地使用ActiveRecord::Enum
,它足够聪明以正确使用收集代理。
class Order < ApplicationRecord
belongs_to :customer
# you need to change the DB column to an integer type
enum status: [:open, :closed]
end