Rails:宏样式函数

Grn*_*gle 13 ruby-on-rails

在模型和控制器,我们经常使用Rails的宏一样before_validation,skip_before_filter在类定义的顶部.

这是如何实现的?如何添加自定义的?

谢谢!

Chr*_*ley 20

它们只是标准的Ruby函数.Ruby灵活的语法方法使它看起来比它更好.您可以通过将方法编写为普通的Ruby函数并执行以下操作之一来创建自己的:

  1. 把它放在你的控制器可以访问的地方,比如application.rb

  2. 把它放在一个文件中并要求它.

  3. 通过Ruby include关键字将代码混合到一个类中.


最后一个选项对于模型类很有用,第一个选项实际上只适用于控制器.


一个例子


第一种方法的一个例子如下所示.在这个例子中,我们将代码添加到ApplicationController类(在application.rb中)并在其他控制器中使用它.

class BusinessEntitiesController < ApplicationController

    nested_within :Glossary

    private

        #  Standard controller code here ....
Run Code Online (Sandbox Code Playgroud)

nested_within提供帮助函数和变量,以帮助识别"父"资源的ID.实际上它会动态解析URL,并且每个控制器都可以访问它.例如,当一个请求进入控制器时,它会被自动解析,并且类属性@parent_resource被设置为Rails查找的结果.副作用是如果父资源不存在则发回"未找到"响应.这使我们无需在每个嵌套资源中键入样板代码.

这听起来很聪明,但它只是一个标准的Ruby功能......


    def self.nested_within(resource)
        #
        #   Add a filter to the about-to-be-created method find_parent_ud
        #
        before_filter :find_parent_id

        #
        #   Work out what the names of things
        #
        resource_name = "#{resource.to_s.tableize.singularize}"
        resource_id = "#{resource_name}_id"
        resource_path = "#{resource.to_s.tableize}_path"

        #
        #   Get a reference to the find method in the model layer
        #
        finder = instance_eval("#{resource}.method :find_#{resource_name}")


        #
        #   Create a new method which gets executed by the before_filter above
        #
        define_method(:find_parent_id) do
            @parent_resource = finder.call(params[resource_id])

            head :status => :not_found, :location => resource_path 
                    unless @parent_resource
        end
    end
Run Code Online (Sandbox Code Playgroud)


nested_within函数在ApplicationController(controllers/application.rb)中定义,因此会自动引入.

请注意,nested_within在控制器类的主体内执行.这将方法find_parent_id添加到控制器.


摘要

结合使用Ruby的灵活语法和Rail的约定配置使得这一切看起来比实际更强大(或更加笨拙).

下次找到一个很酷的方法时,只需在它前面粘一个断点并跟踪它.啊开源!

让我知道我是否可以进一步提供帮助,或者如果你想了解嵌套代码如何工作的一些指示.

克里斯


Ian*_*ell 16

克里斯的回答是正确的.但是这里是你想要编写自己的代码的地方:

添加类似Controller方法的最简单方法是在以下位置定义它ApplicationController:

class ApplicationController < ActionController::Base
  ...
  def self.acts_as_awesome
    do_awesome_things
  end
end
Run Code Online (Sandbox Code Playgroud)

然后您可以从各个控制器访问它,如下所示:

class AwesomeController < ApplicationController
  acts_as_awesome
end
Run Code Online (Sandbox Code Playgroud)

对于模型,您想要重新打开ActiveRecord::Base:

module ActiveRecord
  class Base
    def self.acts_as_super_awesome
      do_more_awesome_stuff
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我个人会把它放在一个文件中,config/initializers以便它被加载一次,所以我知道在哪里寻找它总是.

然后你可以在这样的模型中访问它:

class MySuperAwesomeModel < ActiveRecord::Base
  acts_as_super_awesome
end
Run Code Online (Sandbox Code Playgroud)