ПОРО от AR Object - PullRequest
       2

ПОРО от AR Object

0 голосов
/ 10 января 2020

Итак, я познакомился с использованием PORO вместо объекта AR для абстракции и уменьшения размера.

Но у меня так много таблиц AR, что не имело смысла тратить столько времени на создание класса PORO для каждого и каждый. Прошло бы час или два !! Поэтому вместо этого я потратил много часов на размышления о том, как мне сделать это проще. И вот что я в итоге сделал:

class BasePORO     
  def initialize(obj, immutable = true)  
    self.class::ATTRIBUTES.each do |attr|  
      instance_variable_set("@#{attr}".to_sym, obj.attributes[attr.to_s])  
      instance_eval("undef #{attr}=") if immutable
    end
  end
end

class UserPORO < BasePORO 
  # or plug your own attributes
  ATTRIBUTES = User.new.attributes.keys.map(&:to_sym).freeze  
  attr_accessor(*ATTRIBUTES)
end

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

Могу ли я как-то переместить attr_accessor и ATTRIBUTES по умолчанию в основной класс BasePORO?

Любые указатели или отзывы приветствуются.

Ответы [ 2 ]

1 голос
/ 12 января 2020

Как предлагается в комментариях, OpenStruct может сделать большую часть тяжелой работы за вас. Стоит отметить, что если вы не freeze это сделаете, то после его инициализации вы сможете добавить к нему больше атрибутов в течение всего времени его жизни, например:

struct = OpenStruct.new(name: "Joe", age: 20)
struct.email = "joe@example.com" # this works
p struct.email # => "joe@example.com"

(так что по сути это работает как Hash с объектоподобным интерфейсом)

Такое поведение может быть нежелательным. И если вы сделаете freeze структуру, она не позволит больше определения атрибутов, но тогда вы также потеряете возможность переопределять существующие значения (что, я думаю, вы хотите сделать в тех случаях, когда кто-то устанавливает immutable в false).

Чтобы флаг immutable работал, как я понимаю, вы ожидаете этого, я бы создал класс, который использует OpenStruct под капотом, например, так:

class BasePORO
  def initialize(obj, immutable = true)
    @immutable = immutable
    @data = OpenStruct.new(obj.attributes)

    obj.attributes.keys.each do |attr|
      self.class.define_method(attr.to_sym) do
        @data.send(attr.to_sym)
      end

      self.class.define_method("#{attr}=".to_sym) do |new_value|
        if @immutable
          raise StandardError.new("#{self} is immutable")
        else
          @data.send("#{attr}=".to_sym, new_value)
        end
      end
    end
  end
end

class UserPORO < BasePORO
end

Кстати, если вы настаивали на том, чтобы иметь решение, подобное тому, которое показано в вопросе, то вы могли бы достичь этого с помощью чего-то такого:

class BasePORO
  def initialize(obj, immutable = true)
    @immutable = immutable

    attributes.each do |attr|
      instance_variable_set("@#{attr}".to_sym, obj.attributes[attr.to_s])

      self.class.define_method(attr.to_sym) do
        instance_variable_get("@#{attr}".to_sym)
      end

      self.class.define_method("#{attr}=".to_sym) do |new_value|
        if @immutable
          raise StandardError.new("#{self} is immutable")
        else
          instance_variable_set("@#{attr}".to_sym, new_value)
        end
      end
    end
  end

  private

  # default attributes
  def attributes
    [:id]
  end
end

class UserPORO < BasePORO

  private

  # overriding default attributes from BasePORO
  def attributes
    User.new.attributes.keys.map(&:to_sym).freeze
  end
end
0 голосов
/ 12 января 2020

Итак, вот что на самом деле закончилось:

class BaseStruct < OpenStruct
  def initialize(model, immutable: true, only: [], includes: [])
    if only.empty?
      hash = model.attributes
    else
      hash = model.attributes.slice(*only.map!(&:to_s))
    end
    includes.each do |i|
      relation = model.public_send(i)
      if relation.respond_to?(:each)
        hash[i.to_s] = relation.map{|r| OpenStruct.new(r.attributes).freeze}
      else
        hash[i.to_s] = OpenStruct.new(relation.attributes).freeze
      end
    end
    super(hash)
    self.freeze if immutable
  end
end

Не стесняйтесь критиковать или предлагать улучшения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...