Uniвсячина

понемножку о Linux и программировании

Эмуляция модели в Ruby on Rails

Иногда нужно использовать валидацию для введенных через форму данных, но при этом эти данные никакого отношения к базе данных не имеют. Например, данные формы обратной связи.

Можно сэмулировать стандартную модель использую такой базовый класс-заглушку:

module ActiveRecord
  class Model
    def id; nil; end
    def new_record?; true; end

    def save; nil; end
    def save!; nil; end

    class << self
      def human_name(options = {})
        defaults = self_and_descendants_from_active_record.map do |klass|
          :"#{klass.name.underscore}"
        end
        defaults << self.name.humanize
        I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
      end

      def human_attribute_name(attribute_key_name, options = {})
        defaults = self_and_descendants_from_active_record.map do |klass|
          :"#{klass.name.underscore}.#{attribute_key_name}"
        end
        defaults << options[:default] if options[:default]
        defaults.flatten!
        defaults << attribute_key_name.humanize
        options[:count] ||= 1
        I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
      end

      def self_and_descendants_from_active_record
        klass = self
        classes = [klass]
        while klass != klass.base_class
          classes << klass = klass.superclass
        end
        classes
      rescue
        [self]
      end
    end

    def initialize(params = nil)
      unless params.nil?
        params.each do |k,v|
          self.send("#{k}=", v)
        end
      end
    end

    include ActiveRecord::Validations
  end
end

Класс псевдомодели для формы обратной связи будет выглядить так:

class Message < ActiveRecord::Model
  attr_accessor :name
  attr_accessor :email
  attr_accessor :subject
  attr_accessor :body

  validates_presence_of :name
  validates_length_of :name, :within => 2..16, :allow_blank => true

  validates_presence_of :email
  validates_length_of :email, :within => 6..32, :allow_blank => true
  validates_format_of :email, :with => Authlogic::Regex.email, :allow_blank => true

  validates_presence_of :subject
  validates_length_of :subject, :within => 2..32, :allow_blank => true

  validates_presence_of :body
  validates_length_of :body, :within => 5..4096, :allow_blank => true

  def send_to(recipient)
    # send message to recipient
  end
end

Такую псевдомодель можно использовать в шаблонах с хелпером form_for как обычную. Интернационализация сообщений об ошибках работает так же, как если бы это была обычная модель ActiveRecord::Base.

Пример использования:

class ContactsController < ApplicationController
  def new
    @message = Message.new
  end

  def create
    @message = Message.new(params[:message])
    if @message.valid?
      @message.send_to(configatron.contacts.email)
      flash[:notice] = "Сообщение успешно отправлено"
      redirect_to new_contact_path
    else
      render :action => "new"
    end
  end
end

Comments