Ruby on Rails - 创建和使用自定义方法

5

我对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

我该如何完成这个任务?这个方法应该放在我的模型(Model)还是控制器(Controller)中?之前,我遇到了一个未定义的方法错误。

注意:我相信有更好的编写代码的方法。我也很感谢您提供帮助。

4个回答

2
#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中 - 构建填充您的应用程序的对象。Ruby是一种面向对象语言,这意味着您程序的每个元素都应该在某种程度上围绕数据对象展开。

enter image description here

如上所示,构建系统中的对象的方法实际上是调用。这些类包含可以被调用的方法,可以在级别(即通过方法调用类)或实例级别(即在已调用对象上调用方法)调用。
这就是您获得"类方法 (Model.method)和实例方法 (@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"

--

这很重要,因为它为您提供了一个严格的框架,告诉您应该在哪里使用方法,不应该在模型中使用哪些方法。
它解释了为什么您需要控制器和帮助程序,并允许您制定最佳的应用程序结构,以便在最少的代码下获得最大的收益。
例如...
您使用的@patient.name = params[:params][:name].name_fix不正确的。
这是错误的,因为您正在对与您的模型完全无关的数据调用实例方法.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= setter 是有意义的。这不仅提供了额外的功能,而且比任何其他方式更加流畅和高效。

0

直接调用的方法最好放在控制器中(如果您认为超过一个控制器可能需要使用它,则可以将其放在 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

在您的情况下,您想要调用name_fixparams[:params][:name]返回的任何内容上,这是(我猜)一个String

您有两个选择:

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

  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

感谢您的帮助。我非常感激。我尝试了您提供的解决方案,但是出现了以下错误:undefined method `split' for nil:NilClass。我该如何修复这个问题? - M.M.
split 只能用于 String 对象,所以如果传入的 full_namenil,它将无法工作。如果您只想在 full_nameString 时尝试使用它,可以尝试使用 full_name.split.map(&:capitalize).join(" ") if full_name is_a?(String) - user2490003
另一个需要问的问题是为什么full_namenil?这意味着params[:name]返回了nil - 你需要决定是否这是有效的输入。如果不是,那么调用此控制器操作的任何内容都应确保它永远不会传递nil对象。 - user2490003
1
一切都好。我非常感激你的帮助。 - M.M.

0
假设你的目标是使用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

0
请将您的create方法更新为以下内容。
  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

它应该可以工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接