NoMethodError (неопределенный метод) из метода Private в классе - PullRequest
0 голосов
/ 08 марта 2019

Почему я не могу использовать приватный метод из моего класса?Как исправить мой код, чтобы предотвратить ошибку?

module CarRegistration
  class Basics < Base

    fields_of_model(:car).each do |attr|
      delegate attr.to_sym, "#{attr}=".to_sym, to: :car
    end

    private

    car_structure = #array of hashes

    def fields_of_model(model)
      car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
    end
end

ошибка

NoMethodError (неопределенный метод `fields_of_model 'для CarRegistration :: Основы: Класс):

Ответы [ 2 ]

3 голосов
/ 08 марта 2019

Я думаю, у вас здесь много проблем.

Сначала вы определили 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 ", тогда проблем нет. Но в остальном я видел, как это мешало пониманию.

Пусть все стороны полностью поймут, что термин метод класса используется выше в разговорном смысле.

0 голосов
/ 08 марта 2019

Поскольку вы написали метод , а не в def - end предложении;Вы должны написать это как

def my_method
  fields_of_model(:car).each do |attr|
    delegate attr.to_sym, "#{attr}=".to_sym, to: :car
  end
end

Именно поэтому в сообщении об ошибке указано CarRegistration::Basics:Class вместо CarRegistration::Basics

Вот пример кода, который работает.Обычно нет необходимости помещать класс в Module, но если вам нужно по какой-то причине, это способ.

module CarRegistration
  class Basics < Object
    def run(model)
      fields_of_model(model)
    end

    private

    def fields_of_model(model)
      puts model
    end
  end
end

a = CarRegistration::Basics.new
a.run('xyz')  # => 'xyz' is printed.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...