Как изменить OpenStruct в созданном экземпляре класса

#ruby #instantiation #openstruct

#ruby #создание экземпляра #openstruct

Вопрос:

У меня есть следующий код, в котором я создаю экземпляр объекта, который возвращает результат OpenStruct.

 require 'ostruct'

class TestModel
  attr_accessor :result

  def initializer
  end

  def result
    OpenStruct.new(successful?: false)
  end

  def successful!
    result.send('successful?', true)
  end
end

 

Я хочу, чтобы класс работал так, чтобы я мог изменять любые свои атрибуты result «на лету».

 test = TestModel.new

test.result
=> #<OpenStruct successful?=false>

test.result.successful!
=> #<OpenStruct successful?=true>
 

Этот синтаксис взят с официальной страницы OpenStruct, и он работает сам по себе, но не внутри созданного класса — https://ruby-doc.org/stdlib-2.5.3/libdoc/ostruct/rdoc/OpenStruct.html

result.send('successful?', true)

Я также пытался использовать lambda , но безрезультатно

   def result
    OpenStruct.new(successful?: false, :successful! => lamdba {self.uccessful? = true})
  end
 

Есть идеи? Я действительно хочу знать, как это сделать.

Ответ №1:

OpenStruct требует, чтобы вы использовали Object#send или хэш-подобные ключи для использования символов предикатов. В документации говорится:

Хэш-ключи с пробелами или символами, которые обычно не могут использоваться для вызовов методов (например ()[]* ), не будут немедленно доступны в объекте OpenStruct в качестве метода для извлечения или назначения, но все равно могут быть доступны через метод Object#send или с помощью [] .

Кроме того, неясно, почему вы хотите определить @result как доступный для записи, или почему вы переопределяете метод getter, чтобы TestModel#result всегда возвращал false . Вероятно, это вызывает хотя бы часть вашей проблемы.

Вместо этого я бы переписал код следующим образом:

 require 'ostruct'

class TestModel 
  attr_reader :result

  def initialize
    @result = OpenStruct.new :successful? => nil
  end

  def unsuccessful
    @result.send "successful?=", false
  end
    
  def successful!
    @result.send "successful?=", true
  end
end
 
 test_model = TestModel.new
#=> #<TestModel:0x00007faf5c1b9528 @result=#<OpenStruct successful?=nil>>

test_model.result
#=> nil

test_model.successful!
#=> true

test_model.result
#=> #<OpenStruct successful?=true>

test_model.unsuccessful
#=> false

test_model.result
#=> #<OpenStruct successful?=false>
 

Вы, конечно, могли бы инициализировать элемент struct как false , а не nil как, если хотите, но я думаю, что это семантически понятнее. Ваш опыт в этом отношении может отличаться.