繁体   English   中英

来自类中Private方法的NoMethodError(未定义方法)

[英]NoMethodError (undefined method) from Private method in class

为什么我不能在课堂上使用私有方法? 如何修复我的代码以防止错误?

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(CarRegistration :: Basics:Class的未定义方法`fields_of_model'):

我认为您在这里遇到了许多问题。

首先,您已经将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

请注意,我使用private :fields_of_model将类方法:fields_of_models私有。

为了演示整个过程,我进行了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是蛮好的,而不是你的问题的根源。 实际上,这很正常。

另外,我会注意到JörgW Mittag希望说:

我是那些喜欢指出Ruby中没有类方法之类的Ruby Purists的人之一。 不过, 只要各方都充分理解这是一种口语用法,那么以口语方式使用术语类方法 就可以了 换句话说,如果你知道有没有这样的事,作为一个类的方法和术语“类方法”只是短期的“单身类的对象,它是实例的实例方法Class ”,再有就是没问题。 但是否则,我只能看到它妨碍理解。

各方应完全理解,术语“ 类方法”是在其通俗意义上使用的。

因为您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.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM