簡體   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