Игорь Олемской — практические заметки по системному администрированию Linux CentOS

Архив тега ‘activerecord’

Foreign keys для ActiveRecord (перепечатка)

Комментариев нет

Делаю очередной проект на Ruby on Rails. Как обычно, в миграциях понадобились foreign keys на уровне БД. Окинул взглядом все плагины, которые смог найти в google и на github.com. Ни один из них не умеет делать FK, используя ActiveRecord::ConnectionAdapters::Table#references и ActiveRecord::ConnectionAdapters::TableDefinition#references.

Поэтому сел и написал свой плагин: active_record_foreign_keys.

Установка:

Rails::Initializer.run do |config|
  ...
  config.gem "active_record_foreign_keys", :source => "http://gemcutter.org"
  ...
end
$ rake gems:install

Использование:

def self.up
  # create reference table
  create_table :users do |t|
  end
  # create referencing table
  create_table :a_examples do |t|
    t.references :user, :foreign_key => true
  end
  # or
  create_table :b_examples do |t|
    t.references :user, :foreign_key => { :o n_update => :cascade, :o n_delete => :restrict }
  end
  # or
  create_table :c_examples do |t|
  end
  add_foreign_key :c_examples, :user_id, :users, :id, :o n_update => :no_action, :o n_delete => :set_null
  # or change existing table
  change_table :d_examples do |t|
    t.references :user, :foreign_key => true
  end
end
def self.down
  # remove constraint
  remove_foreign_key :examples, :user_id, :users, :id
end

Плагин тестировался только под PostgreSQL, но по идее должен работать и под MySQL, и под Sqlite.

Эмуляция модели в 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