Есть ли способ сделать атрибуты Rails ActiveRecord приватными?

По умолчанию ActiveRecord берет все поля из соответствующей таблицы базы данных и создает для них общедоступные атрибуты.

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

Итак, есть ли способ сделать некоторые атрибуты буквально private?

Или, может, мне стоит перейти на какой-нибудь другой ORM?


person Leonid Shevtsov    schedule 21.09.2010    source источник


Ответы (7)


Джордини большую часть пути был там

Большая часть active_record происходит в method_missing. Если вы определите метод заранее, он не ударит method_missing для этого метода и вместо этого будет использовать ваш (эффективно перезаписывая, но не совсем)

class YourModel < ActiveRecord::Base

  private

  def my_private_attribute
    self[:my_private_attribute]
  end

  def my_private_attribute=(val)
    write_attribute :my_private_attribute, val
  end

end
person Matt Briggs    schedule 22.09.2010
comment
Есть ли причина, по которой я не мог бы / не мог использовать следующее для этого? частный attr_accessor my_private_attribute - person T C; 26.06.2012
comment
@TC: потому что они на самом деле не будут правильно отслеживать атрибуты. методы write_attribute / read_attribute - это способ взаимодействия с active_record - person Matt Briggs; 27.06.2012
comment
на самом деле write_attribute не сохраняет значение в БД. После этого нужно было бы вызвать self.save, чтобы действительно обновить запись в БД. - person Magne; 11.01.2013
comment
Со стороны модели я могу сделать your_model_instance[:my_private_attribute] = "Foo" и изменить значение атрибута. Очень страшный! - person Cristhian Boujon; 06.08.2013
comment
@ Overflow012 вы также можете сделать your_model_instance.send(:my_private_attribute=, "foo"). Ruby придерживается (разумного) подхода, согласно которому цель конфиденциальности является индикатором намерений других программистов, а не блокировкой, которую невозможно сломать. - person Matt Briggs; 07.08.2013
comment
def my_private_attribute; super end подойдет - не нужно переопределять его, затем, конечно, сделайте его частным, как указано выше, или другим способом private :my_private_attribute - person ryan2johnson9; 28.10.2016

ну, вы всегда можете переопределить методы ...

class YourModel < ActiveRecord::Base

  private

  def my_private_attribute
    self[:my_private_attribute]
  end

  def my_private_attribute=(val)
    self[:my_private_attribute] = val
  end

end
person jordinl    schedule 21.09.2010
comment
Похоже, это дает ошибку при использовании обновления из класса модели. - person NicolasWebDev; 24.08.2016

Наткнулся на это недавно. Если вам нужно личное письмо и чтение, а также публичное чтение чего-то вроде этого

class YourModel < ActiveRecord::Base

  attr_reader :attribute

  private

  attr_accessor :attribute


end

кажется, у меня все работает нормально. Вы можете поиграть с attr_reader, attr_writer и attr_accessor, чтобы решить, что должно быть раскрыто, а что должно быть приватным.

person Otto    schedule 17.08.2011
comment
Я почти уверен, что это не будет работать с обычными атрибутами AR, поскольку они не хранятся в переменных экземпляра (с которыми работают методы attr_ *). - person Leonid Shevtsov; 17.08.2011
comment
Я везде искал этот ответ! Похоже, это сработало и было ТОЧНО тем, что я искал. Спасибо! - person ootoovak; 12.05.2012
comment
Со стороны модели я могу сделать your_model_instance[:my_private_attribute] = "Foo" и изменить значение атрибута. Очень страшный! - person Cristhian Boujon; 06.08.2013

Для меня методы как Otto, так и jordinl работают нормально и делают rspec для передачи объекта Class:

object.should_not respond_to :attribute

Но когда я использую метод jordinl, у меня появляется сообщение об устаревании, чтобы не писать в базу данных напрямую, а вместо этого использовать attr_writer.

ОБНОВИТЬ:

Но по-настоящему «правильный» метод, как это сделать, оказывается простым. Спасибо Младену Яблановичу и Кристоферу Кройцигу, отсюда. Чтобы сделать предопределенный метод закрытым или защищенным ... просто переопределите его:

Class Class_name

  private :method_name
  protected :method_name_1
end

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

person Andrew Kozin    schedule 14.01.2013

Вы можете сделать существующий метод закрытым:

YourClass.send(:private, :your_method)
person Mark Thomas    schedule 22.09.2010
comment
на самом деле это не работает с ActiveRecord, к сожалению, поскольку методы доступа фактически не определены, они созданы с помощью method_missing и небольшого метапрограммирования. - person brad; 13.05.2011

Если сделать этот параметр частным, возникает ошибка ActiveRecord.

Я помещаю код управления доступом в перезаписанный метод общедоступного установщика, проверяя вызывающего:

def my_private_attribute=(val)
  if (caller.first.include?('active_record/base.rb') || caller.first.include?('app/models/myclass.rb'))
    self[:my_private_attribute] = val
  else
     raise Exception.new("Cannot set read-only attribute.")
  end
end
person Edmond Chui    schedule 26.11.2012

Я не думаю, что есть 100% надежный способ сделать это. Также стоит проверить самые популярные способы доступа к атрибутам:

http://www.davidverhasselt.com/set-attributes-in-activerecord/ < / а>

person Artur Beljajev    schedule 11.11.2017