Rails: функции макросов - PullRequest
13 голосов
/ 29 мая 2009

В моделях и контроллерах мы часто используем макросы Rails типа before_validation, skip_before_filter поверх определения класса.

Как это реализовано? Как добавить пользовательские?

Спасибо!

Ответы [ 2 ]

20 голосов
/ 29 мая 2009

Это просто стандартные функции Ruby. Гибкий подход Ruby к синтаксису делает его лучше, чем он есть на самом деле. Вы можете создать свой собственный, просто написав свой метод как обычную функцию Ruby и выполнив одно из следующих действий:

  1. размещение его в таком месте, которое доступно вашим контроллерам, например application.rb

  2. положить его в файл и требовать его.

  3. смешивание кода в классе с помощью ключевого слова Ruby include.


Этот последний вариант отлично подходит для классов моделей, а первый вариант действительно только для контроллеров.


Пример


Пример первого подхода показан ниже. В этом примере мы добавляем код в класс ApplicationController (в application.rb) и используем его в других контроллерах.

class BusinessEntitiesController < ApplicationController

    nested_within :Glossary

    private

        #  Standard controller code here ....

nested_within предоставляет вспомогательные функции и переменные, помогающие идентифицировать идентификатор «родительского» ресурса. По сути, он анализирует 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


Функция nested_within определена в ApplicationController (controllers / application.rb) и, следовательно, включается автоматически.

Обратите внимание, что nested_within выполняется внутри тела класса контроллера. Это добавляет метод find_parent_id к контроллеру.


Резюме

Сочетание гибкого синтаксиса Ruby и соглашения о переконфигурации Rail делает все это более мощным (или более странным), чем на самом деле.

В следующий раз, когда вы найдете классный метод, просто поставьте перед ним точку останова и проследите ее. Ааа с открытым исходным кодом!

Дайте мне знать, могу ли я помочь в дальнейшем или если вы хотите несколько советов о том, как работает этот код nested_within.

Chris

16 голосов
/ 29 мая 2009

Крис ответил правильно. Но вот где вы хотите бросить свой код, чтобы написать свой собственный:

Самый простой способ добавить такие методы контроллера - это определить его в ApplicationController:

class ApplicationController < ActionController::Base
  ...
  def self.acts_as_awesome
    do_awesome_things
  end
end

Затем вы можете получить к нему доступ с отдельных контроллеров, например:

class AwesomeController < ApplicationController
  acts_as_awesome
end

Для моделей вы хотите снова открыть ActiveRecord::Base:

module ActiveRecord
  class Base
    def self.acts_as_super_awesome
      do_more_awesome_stuff
    end
  end
end

Я лично поместил бы это в файл в config/initializers, чтобы он загружался один раз, и чтобы я всегда знал, где его искать.

Тогда вы можете получить к нему доступ в следующих моделях:

class MySuperAwesomeModel < ActiveRecord::Base
  acts_as_super_awesome
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...