Используйте send () для связанной коллекции - PullRequest
0 голосов
/ 19 октября 2018

У меня есть несколько списков, которые почти равны, просто они вызывают другой метод (метод области действия в связанной модели).Поэтому я подумал, что мог бы использовать метод send ruby ​​для вызова этого метода.Но происходит сбой с ошибкой:

неверное количество аргументов (задано 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.

На консоли рельсов:

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))

1 Ответ

0 голосов
/ 19 октября 2018

Проблема здесь в том, что имя сталкивается с 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...