Проблема с поддержкой двухстороннего синтаксиса в ruby

#ruby-on-rails #ruby #rubygems

#ruby-on-rails #ruby #rubygems

Вопрос:

У меня ситуация, когда мне нужно вызвать что-то вроде этого :

 class Office

  attr_accessor :workers, :id

  def initialize
    @workers = []
  end

  def workers worker
    type = worker.type
    resp = Worker.post("/office/#{@id}/workers.json", :worker => {:type => type})
    worker = Worker.new()
    resp.to_hash.each_pair do |k,v|
      worker.send("#{k}=",v) if worker.respond_to?(k)
    end
    self.workers << worker
  end

end
  

Рабочий класс

 class Worker
  attr_accessor :office_id, :type, :id

  def initialize(options={})
    @office_id = options[:office].nil? ? nil : options[:office].id
    @type = options[:type].nil? ? nil : options[:type].camelize
    if !@office_id.nil?
       resp = self.class.post("/office/#{@office_id}/workers.json", :worker => {:type => @type})
       @id = resp.id
       office = options[:office]
       office.workers = self
    end
  end

  def <<(worker)
    if worker
      type = worker.type
      resp = Worker.post("/office/#{office_id}/workers.json", :worker => {:type => type})
      debugger
      @id = resp.id
      resp.to_hash.each_pair do |k,v|
        self.send("#{k}=",v) if self.respond_to?(k)
      end
      debugger
      return self
    end
  end
  

Я могу сделать что-то подобное очень хорошо

 office = Office.new()
new_worker = Worker.new()
office.workers new_worker
  

Но мне нужно сделать то же самое, что я сделал выше, например, следующим образом. Перед этим мне нужно изменить метод инициализации Office, чтобы запустить def <<(worker) метод экземпляра worker.

 class Office
  ...
  def initialize
    @workers = Worker.new
    @workers.office_id = self.id
  end


office = Office.new()
new_worker = Worker.new()
office.workers << new_worker
  

Теперь проблема в том, что более поздняя реализация создает 2 экземпляра worker??

Комментарии:

1. @zabba, он добавляет новый объект worker в атрибуты worker объекта office, а атрибутом worker является массив.

2. Вы изменили больше кода, чем просто это? Как есть, office. workers << new_worker должен быть аргументом ошибки начиная с office. workers — это метод, принимающий 1 параметр.

3. @Джеймс, нет, эта часть в порядке.

4. кстати, «if !worker.nil?» лучше выразить как «если рабочий»

5. @джеймс, да, но проблема не в этом

Ответ №1:

Я не совсем уверен, но я полагаю, вы хотели бы иметь это:

 class Office

  attr_accessor :workers, :id

  def initialize
    @workers = []
  end

  alias_method :workers, :return_worker_array

  def workers worker
    unless worker
      return_worker_array
    else
      type = worker.type
      resp = Worker.post("/office/#{@id}/workers.json", :worker => {:type => type})
      worker = Worker.new()
      resp.to_hash.each_pair do |k,v|
      worker.send("#{k}=",v) if worker.respond_to?(k)
      return_worker_array << worker
  end
end
  

завершение

Таким образом, вы можете полностью избавиться от Worker#<< , и вам также следует удалить строку

 office.workers = self
  

предполагается, что in Worker#initialize since office.workers является массивом. Менять тип атрибута взад и вперед — плохая идея (можно вводить утку), потому что, скорее всего, вы потеряете отслеживание текущего состояния и рано или поздно столкнетесь с ошибками.

Чтобы следовать «разделению проблем», я бы рекомендовал выполнять все управление workers исключительно в Office , иначе это слишком быстро запутывается и будет намного сложнее поддерживать в долгосрочной перспективе.

Ответ №2:

Я не уверен на 100%, почему вы здесь не получаете ошибку, но поскольку последней строкой Office#workers является self.workers << worker, вы добавляете нового worker, созданного в Office #workers (созданного в 3-й строке метода), а затем возвращаете объект workers, который затем снова вызывается #<< для него с новым worker, созданным вне метода

Комментарии:

1. да, вот почему его перепутали, чтобы реализовать это …. так что есть идеи, как я могу реализовать #<< метод при вызове office.workers << new_worker , как я сделал в office.workers new_worker

2. я бы выбрал другое имя для атрибута, чтобы вы могли выполнять office.workers new_worker и office.new_worker_name << new_worker . В идеале я бы выбрал office.add_worker new_worker и office.workers << new_worker , но, похоже, у вас есть устаревший код для поддержки, поэтому вы не хотите изменять предыдущие вызовы. (Я знаю, это не то, о чем вы просили, но именно так я бы это сделал. Если бы вы лучше объяснили сценарий, я мог бы дать другой ответ)

3. Я просто хочу, чтобы более поздний метод функционировал так же, как я делал в предыдущем, и с тем же именем. когда я вызываю office.workers << new_worker , он просто вызывает атрибут получения / установки workers по умолчанию.