Я думаю, у вас здесь много проблем.
Сначала вы определили fields_of_model
как метод экземпляра, здесь:
def fields_of_model(model)
car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
но вы пытаетесь позвонить из класса, здесь:
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
Итак, вы захотите сделать fields_of_model
метод класса и определить его перед вызовом. Что-то вроде:
module CarRegistration
class Basics < Base
private
car_structure = #array of hashes
class << self
def fields_of_model(model)
car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
end
Я думаю, у вас также будут проблемы с этой car_structure
переменной, потому что она будет выходить за рамки метода класса. Итак, я думаю, вам нужно сделать переменную экземпляра уровня класса. Итак, попробуйте:
module CarRegistration
class Basics < Base
@car_structure = #array of hashes
class << self
def fields_of_model(model)
@car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
private :fields_of_model
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
end
Обратите внимание, что я сделал метод класса :fields_of_models
закрытым, используя private :fields_of_model
.
Чтобы продемонстрировать все это, я создал этот тест RSpec:
require 'rails_helper'
class Car
attr_accessor *%w(
color
make
year
).freeze
end
module CarRegistration
class Basic
@car_structure = [
{model: :car, name: :color},
{model: :car, name: :make},
{model: :car, name: :year}
]
class << self
def fields_of_model(model)
@car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
private :fields_of_model
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
def car
@car ||= Car.new
end
end
end
RSpec.describe CarRegistration::Basic do
it "has :fields_of_model as a private class method" do
expect(CarRegistration::Basic.public_methods).not_to include(:fields_of_model)
expect(CarRegistration::Basic.private_methods).to include(:fields_of_model)
end
it "responds to :color and :color=" do
expect(car_registration).to respond_to(:color)
expect(car_registration).to respond_to(:color=)
end
it "sets and gets attributes on car" do
expect(car_registration.color).to be_nil
expect(car_registration.car.color).to be_nil
car_registration.color = :red
expect(car_registration.car.color).to eq(:red)
expect(car_registration.color).to eq(:red)
expect(car_registration.instance_variable_get(:@color)).to be_nil
end
end
def car_registration
@car_registration ||= described_class.new
end
Что при запуске дает:
CarRegistration::Basic
has :fields_of_model as a private class method
responds to :color and :color=
sets and gets attributes on car
Finished in 0.733 seconds (files took 27.84 seconds to load)
3 examples, 0 failures
Кстати, наличие этого кода в вашем классе за пределами def
- end
просто прекрасно, а не является причиной вашей проблемы. На самом деле это вполне нормально.
Также отмечу, что Йорг Миттаг хочет сказать:
Я один из тех Ruby Purists, которому нравится указывать на то, что в Ruby нет такого понятия, как метод класса. Тем не менее, я в порядке, используя термин метод класса в разговорной речи, , пока все стороны полностью понимают, что это разговорное использование . Другими словами, если вы знаете , что не существует такой вещи, как метод класса, и что термин «метод класса» просто сокращен от «метод экземпляра синглтон-класса объекта, который является экземпляром Class
", тогда проблем нет. Но в остальном я видел, как это мешало пониманию.
Пусть все стороны полностью поймут, что термин метод класса используется выше в разговорном смысле.