【问题标题】:Ruby on Rails - Creating and Using a Custom MethodRuby on Rails - 创建和使用自定义方法
【发布时间】:2015-10-16 05:18:24
【问题描述】:

我对 Rails 比较陌生,如果能提供任何帮助,我将不胜感激。我创建了以下方法:

  def name_fix
    name = self.split
    mod_name = []
    name.each do |n|
      n.split("")
      if n[0]
        n.upcase
      else
        n.downcase
      end
      mod_name.push(n)
    end
    mod_name.join
  end

我想在我的控制器中使用这种方法:

def create
  @patient = Patient.new(params[:patient])
  @patient.name = params[:params][:name].name_fix
  if @patient.save
    redirect_to patients_path
  else
    render :new
  end
end

我怎样才能做到这一点?这个方法会驻留在我的模型或控制器中吗?以前,我遇到过未定义的方法错误。

注意:我确信有一种方法可以更好地编写我的代码。我也很感激这方面的帮助。

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:
    #app/models/patient.rb
    class Patient < ActiveRecord::Base
    
       protected
    
       def name=(value)
          mod_name = []
          value.split.each do |n|
             n.split("")
             type = n[0] ? "up" : "down"
             n.send("#{type}case")
             mod_name.push(n)
          end
          @name = mod_name.join
       end
    end
    
    #app/controllers/patients_controller.rb
    class PatientsController < ApplicationController
       def create
          @patient = Patient.new patient_params
          @patient.save ? redirect_to(patients_path) : render(:new)
      end
    
      private
    
      def patient_params
         params.require(:patient).permit(:name)
      end
    end
    

    你正在做的是试图覆盖setter 方法,这可以使用上面的代码来完成。更高效且不碍事。


    我创建了以下方法

    既然你是新人,让我解释一下。

    请务必注意在哪里您正在使用此方法。

    您当前已将其放入 模型,这意味着您必须调用它来操作使用该模型创建的任何对象的某些属性/功能。

    --

    模型 - 在 Rails 中 - 构建填充您的应用程序的 objects。 Ruby 是一个object orientated language,这意味着您程序的每个元素都应该在某种程度上围绕数据对象。

    正如您在上面看到的,在系统中构建对象的方法实际上是调用。这些类包含方法,可以在级别(IE通过方法调用类)或实例级别(IE在已调用的对象上调用方法)。

    您可以从以下位置获得"class" methods (Model.method) and "instance" methods (@model.method)

    #app/models/patient.rb
    class Patient < ActiveRecord::Base
       def explode
          #this is an instance method
          puts "Instance Explode"
       end
    
       def self.explode
          #this is a class method
          puts "Exploded"
       end
    end
    

    因此您可以调用以下代码:

    @patient = Patient.find params[:id]
    @patient.explode #-> "Instance explode"
    
    Patient.explode #-> "Exploded"
    

    --

    这很重要,因为它为您提供了一个严格的框架,说明您应该在模型中的哪些位置使用方法。

    它解释了为什么你有 controllershelpers,并允许你制定构建应用程序的最佳方式,以充分利用最少的代码。

    例如...

    您对@patient.name = params[:params][:name].name_fix 的使用不正确

    这是错误的,因为您在与您的模型完全无关的数据上调用 instance 方法 .name_fix。如果你想像这样在一般意义上使用.name_fix,你可能会使用helper

    #app/helpers/patients_helper.rb
    class PatientsHelper
       def name_fix value
          # stuff here
       end
    end
    
    #app/controllers/patients_controller.rb
    class PatientsController < ApplicationController
       def create
          @patient.name = name_fix params[:patient][:name]
       end
    end
    

    由于您使用该方法来填充模型的 .name 属性,因此覆盖 name= 设置器是有意义的。这不仅会提供额外的功能,而且比任何其他方式都更加流畅和高效。

    【讨论】:

      【解决方案2】:

      直接调用的方法最好放在 Controller 中(或者如果您认为多个控制器可能要使用它,则放在 ApplicationController 中)。

      这些方法类似于

      # app/controllers/my_controller.rb
      
      def foo(bar)
        # do something here
      end
      
      def create
        id = params[:id]
        value = foo(id)
      end
      

      如果你想要一个链式方法作为你调用它的任何属性方法。这些是模型工作方式的特征——您拥有主模型,并在该模型的实例上调用属性或方法。

      # app/models/my_model.rb
      def full_name
       first_name + " " + last_name
      end
      
      # app/controller/my_controller.rb
      def create
        id = params[:id]
        model = MyModel.find(id)
        full_name = model.full_name
      end
      

      在您的情况下,您想在params[:params][:name] 返回的任何内容上调用name_fix,即(我猜)String

      你有两个选择

      1. 修改String 类以定义一个名为name_fix 的方法。我强烈建议不要这样做。它被称为“monkeypatching”,没有充分的理由不应该这样做。只是让您知道在某些情况下您可以做到。

      2. 在您的控制器中使用直接方法或ApplicationController,如上面的第一个示例。

        @patient.name = name_fix(params[:params][:name])

      编辑: 至于您对编写代码的更好方法的要求......这很难在一个答案中教授或传达。我想说阅读一些开源项目,看看人们如何编写 Ruby 以及一些用于清理代码的常见习语。为了让您开始,这是我重新编写代码的方式

      def create
        @patient = Patient.new(params[:patient])
      
        # 1. Be descriptive with your method names. `name_fix` is vague
        # 2. Why is `:name` nested under another `[:params]` hash?
        @patient.name = capitalize_name(params[:name])
      
        if @patient.save
          # 1. I think `patient_path` has to be singular
          # 2. It needs a `Patient` object to know how to construct the URL
          #     e.g. `/patients/:id`
          redirect_to patient_path(@patient)
        else
          render :new
        end
      end
      
      
      def capitalize_name(full_name)
        # Example: julio jones
        # 
        # 1. `split` produces an array => ["julio", "jones"]
        # 2. `map` applies a function (`capitalize`) to each element
        #       => ["Julio", "Jones"]
        # 3. `join(" ")` rejoins it => "Julio Jones"
        full_name.split.map(&:capitalize).join(" ")
      end
      

      【讨论】:

      • 感谢您的帮助。我真诚地感谢它。我尝试了您的解决方案并收到以下错误:nil:NilClass 的未定义方法`split'。我该如何解决这个问题?
      • split 仅适用于 String 对象,因此如果传入的 full_namenil 它将不起作用。您可以改为尝试full_name.split.map(&amp;:capitalize).join(" ") if full_name is_a?(String),因此只有在full_nameString 相同时才会尝试它
      • 另一个要问的问题是为什么是 full_name nil ?这意味着 params[:name] 正在返回 nil - 您需要确定这是否是有效输入。如果不是,则无论调用此控制器操作的内容都应确保它永远不会传入 nil 对象。
      • 一切都很好。感谢您的帮助。
      【解决方案3】:

      假设您使用 name_fix 方法的目标只是将每个名称的第一个字母大写,您可以将 name 作为参数传递并将其作为私有方法存储在控制器上:

      # app/controllers/patient_controller.rb
      private
      def name_fix(name)
        name.split.map(&:capitalize).join(" ")
      end
      

      那你就可以了

      @patient.name = name_fix(params[:params][:name])
      

      create 方法中。

      或者,您可以将此方法存储在模型中:

      # app/models/patient.rb
      def self.name_fix(name)
        name.split.map(&:capitalize).join(" ")
      end
      

      然后您可以在控制器中执行此操作:

      @patient.name = Patient.name_fix(params[:params][:name])
      

      我还建议将您的 name_fix 方法重命名为 capitalize_name 之类的名称。

      【讨论】:

        【解决方案4】:

        如下更新您的创建方法

          def create
            @patient = Patient.new(params[:patient])
            @patient.name = params[:params][:name]
            @patient = @patient.name_fix
             if @patient.save
                redirect_to patients_path
             else
               render :new
             end
           end
        

        它应该可以工作。

        【讨论】:

          猜你喜欢
          • 2018-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多