Если вы просто пытаетесь получить рабочую Expando
для использования, используйте вместо нее OpenStruct
.Но если вы делаете это для образовательной ценности, давайте исправим ошибки.
Аргументы method_missing
Когда вы вызываете person.name = "Michael"
, это переводится в вызов person.method_missing(:name=, "Michael")
, так что вам не нужно извлекать параметр с помощью регулярного выражения.Назначаемое вами значение является отдельным параметром.Следовательно,
if method_id.to_s[-1,1] == "=" #the last character, as a string
name=method_id.to_s[0...-1] #everything except the last character
#as a string
#We'll come back to that class_eval line in a minute
#We'll come back to the instance_variable_set line in a minute as well.
else
super.method_missing(method_id, *arguments)
end
instance_variable_set
Имена переменных экземпляра начинаются с символа @
.Это не просто синтаксический сахар, это на самом деле часть названия.Поэтому вам нужно использовать следующую строку для установки переменной экземпляра:
instance_variable_set("@#{name}", arguments[0])
(обратите внимание также на то, как мы извлекли значение, которое мы назначаем, из массива arguments
)
class_eval
self.class
относится к классу Expando
в целом.Если вы определите attr_accessor
для него, то каждые expando будет иметь аксессор для этого атрибута.Я не думаю, что это то, что вы хотите.
Скорее, вам нужно сделать это внутри блока class << self
(это единственный класс или собственный класс self
).Это работает внутри собственного класса для self
.
Так что мы выполним
class << self; attr_accessor name.to_sym ; end
Однако переменная name
на самом деле не доступна внутри, поэтому мы собираемсясначала нужно выделить одноэлементный класс, а затем запустить class_eval
.Обычный способ сделать это - использовать собственный метод eigenclass
Итак, мы определяем
def eigenclass
class << self; self; end
end
и затем вместо этого вызываем self.eigenclass.class_eval { attr_accessor name.to_sym }
Решение
Объедините все это, и окончательное решение получится до
class Expando
def eigenclass
class << self; self; end
end
def method_missing(method_id, *arguments)
if method_id.to_s[-1,1] == "="
name=method_id.to_s[0...-1]
eigenclass.class_eval{ attr_accessor name.to_sym }
instance_variable_set("@#{name}", arguments[0])
else
super.method_missing(method_id, *arguments)
end
end
end