Как я могу установить «глобальные» переменные, к которым можно получить доступ в контроллерах и моделях в Rails

У меня есть таблица, в которой есть записи. Я хотел бы получить доступ к этим записям как к переменным как в моих моделях, так и в контроллерах, не запрашивая базу данных каждый раз, чтобы установить эти переменные.

Я могу заставить его работать, создавая дубликаты «проблем» для моих моделей и контроллеров. Я также мог бы установить глобальные переменные в моем ApplicationController. Или я мог бы инициализировать их в каждом месте, где они мне нужны. Каким будет правильный способ рельсов для установки и доступа к глобальным переменным, к которым можно получить доступ как в контроллерах, так и в моделях?

class ItemType
  has_many :items
end

class Item
  belongs_to :item_type
  belongs_to :foo
end

class Foo 
  has_many :items  

  def build_item
    bar_item_type = ItemType.find_by(:name => "bar")

    self.items.build(
      :foo_id => self.id,
      :item_type_id => bar_item_type.id
    )
  end
end

class ItemsController
  def update
    bar_item_type = ItemType.find_by(:name => "bar")

    @item.update(:item_type_id => bar_item_type.id)
  end

end

В примере вы можете видеть, что я объявляю переменную bar_item_type как в моей модели Foo, так и в моем ItemsController. Я хотел бы высушить свою кодовую базу, имея возможность создать и получить доступ к этой переменной один раз для моего проекта rails вместо того, чтобы везде делать один и тот же вызов базы данных.

Я не уверен, что ваш пример ясно показывает, ПОЧЕМУ вы хотите это сделать. Вы действительно будете использовать ItemType.find_by(:name => "bar") в своем приложении? Использование глобальных переменных для вашего варианта использования нарушает MVC и основные соглашения Rails. Сначала вам нужно спросить себя, ПОЧЕМУ вы думаете, что вам нужно это сделать. Есть, вероятно, лучшие более традиционные способы сделать это.

lacostenycoder 17.07.2019 22:22

Я думаю, это мой вопрос. Каков обычный/лучший способ сделать это, используя приведенный выше пример? Я хотел бы, чтобы переменная была доступна в обоих этих случаях использования.

Danny Trujillo 17.07.2019 22:30

Проблема в том, как вы можете быть уверены, что ItemType.find_by(:name => "bar") вообще что-то вернет? Ваш код делает предположения о базе данных.

lacostenycoder 17.07.2019 22:33

База данных заполнена определенными типами item_types. База данных предполагается и предназначена для их наличия.

Danny Trujillo 17.07.2019 22:36
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
3
4
1 264
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я бы выступал против такого жестко запрограммированного кода или кода, зависящего от состояния БД. Если вы должны это сделать, вот как это можно сделать одним из известных мне способов:

# models
class ItemType < ActiveRecord::Base
  has_many :items

  # caches the value after first call
  def self.with_bar
    @@with_bar ||= transaction { find_or_create_by(name: "bar") }
  end

  def self.with_bar_id
    with_bar.id
  end
end

class Item < ActiveRecord::Base
  belongs_to :item_type
  belongs_to :foo

  scope :with_bar_types, -> { where(item_type_id: ItemType.with_bar_id) }
end

class Foo < ActiveRecord::Base
  has_many :items  

  # automatically sets the foo_id, no need to mention explicitly
  # the chained with_bar_types automatically sets the item_type_id to ItemType.with_bar_id
  def build_item
    self.items.with_bar_types.new
  end
end

# Controller
class ItemsController
  def update
    @item.update(item_type_id: ItemType.with_bar_id)
  end
end

OP может также захотеть проверить уникальность имени для типа элемента.

lacostenycoder 17.07.2019 22:46

@lacostenycoder: Тогда ОП должен упомянуть об этом в примере кода в сообщении. В любом случае, это довольно легко обойти.

Surya 17.07.2019 22:49

Я согласен, а также думаю, что вы дали прекрасный ответ.

lacostenycoder 18.07.2019 06:22
Ответ принят как подходящий

Если вы ДОЛЖНЫ использовать константу, есть несколько способов сделать это. Но вы должны учитывать, что вы создаете экземпляр объекта модели ActiveRecord, который зависит от данных, присутствующих в базе данных. Это не рекомендуется, потому что теперь у вас есть логика модели и контроллера, основанная на данных, присутствующих в базе данных. Это может быть нормально, если вы заполнили свою базу данных и она не изменится.

class ItemType
  BAR_TYPE ||= where(:name => "bar").limit(1).first 

  has_many :items
end

Теперь, когда вам понадобится этот объект, вы можете вызвать его так:

bar_item_type  = ItemType::BAR_TYPE

Спасибо. Я считаю, что это то, что я ищу. Я не знал, что вы можете объявить BAR_TYPE в модели ItemType вот так.

Danny Trujillo 17.07.2019 23:04

Это допустимый синтаксис ruby, но не совсем обычный стиль Rails. Ответ Сурьи ниже гораздо более традиционен для Rails и имеет некоторые дополнительные преимущества, тогда как мой пример больше похож на взлом.

lacostenycoder 18.07.2019 06:26

Другие вопросы по теме