2010-11-21 11 views
17

Estoy creando una gema Ruby y me gustaría extender ActiveRecord :: Migration con mis propios ayudantes para crear las columnas necesarias. (Esto es similar a lo que hace Devise al crear migraciones para sus diversas estrategias de autenticación.) Me doy cuenta de que la funcionalidad que agrego es bastante trivial y probablemente haya formas mejores/más eficientes de hacerlo. Estoy intentando esto como una experiencia de aprendizaje más que como algo con aplicación práctica. Solo quiero entender cómo hacer algo tan invasivo como agregar nuevas capacidades de migración en Rails.¿Cómo extender ActiveRecord :: Migration con métodos adicionales?

Lo que tengo hasta ahora se basa en una joya con éxito y se instala, pero cuando intento ejecutar una migración como:

class CreatePosts < ActiveRecord::Migration 
    def self.up 
    create_table :posts do |t| 
     t.string :name 
     t.string :title 
     t.text :content 
     t.hideable 
     t.tracks_hidden_at 
     t.timestamps 
    end 
    end 
end 

... se produce un error diciendo que hideable no está definido.

He visto la forma en que Devise ha hecho esto y tengo que admitir que estoy un poco perdido, pero he tratado de buscarlo. Yo he hecho lo siguiente:

ActiveRecord extendido con mis nuevas adiciones modelo y ha creado un método para aplicar los cambios de esquema basado en mis nuevos métodos de migración

require 'orm_adapter/adapters/active_record' 

module HiddenRecord 
    module Orm 
    # This module contains some helpers and handle schema (migrations): 
    # 
    # create_table :accounts do |t| 
    #  t.hideable 
    #  t.tracks_hidden_timestamp 
    # end 
    # 
    module ActiveRecord 
     module Schema 
     include HiddenRecord::Schema 

     # Tell how to apply schema methods. 
     def apply_hiddenrecord_schema(name, type, options={}) 
      column name, type.to_s.downcase.to_sym, options 
     end 
     end 
    end 
    end 
end 
ActiveRecord::Base.extend HiddenRecord::Models 
ActiveRecord::ConnectionAdapters::Table.send :include, HiddenRecord::Orm::ActiveRecord::Schema 
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HiddenRecord::Orm::ActiveRecord::Schema 

Creado un módulo de esquema similar al Idear de schema.rb que define los métodos que desea utilizar en la migración y llama a un método para aplicar el esquema

module HiddenRecord 
    # Holds schema definition for hiddenrecord model options. 
    module Schema 
    # Sets the model as having hidable rows 
    # 
    # == Options 
    # * :null - When true, allows the hidden row flag to be null 
    # * :default - Used to set default hidden status to true. If not set, default is false (rows are not hidden) 
    def hideable(options={}) 
     null = options[:null] || false 
     default = options[:default] || false 

     apply_hiddenrecord_schema :hiddenrecord_is_row_hidden, Boolean, :null => null, :default => default 
    end 

    # Sets the model to record the timestamp when a row was hidden 
    def tracks_hidden_timestamp() 
     apply_hiddenrecord_schema :hiddenrecord_hidden_at, DateTime 
    end 
    end 
end 

métodos añadido para los modelos de apoyo a los nuevos campos

module HiddenRecord 
    module Models 
    # This module implements the hideable API 
    module Hideable 
     def self.included(base) 
     base.class_eval do 
      extend ClassMethods 
     end 
     end 

     scope :visible, where(:hiddenrecord_is_row_hidden => true) 

     def hidden? 
     return hiddenrecord_is_row_hidden || false 
     end 

     def hide 
     hiddenrecord_is_row_hidden = true 
     end 

     def hide! 
     hiddenrecord_is_row_hidden = true 
     save! 
     end 

     def unhide 
     hiddenrecord_is_row_hidden = false 
     end 

     def unhide! 
     hiddenrecord_is_row_hidden = false 
     save! 
     end 

    end 
    end 
end 

Cargar los archivos de esquema y el modelo y en el módulo principal de la gema

module HiddenRecord 
    autoload :Schema, 'hiddenrecord/schema' 
    autoload :Models, 'hiddenrecord/models' 
    ... 
end 
require 'hiddenrecord/models/hideable' 
require 'hiddenrecord/models/tracks_hidden_timestamp' 

Una vez más, reconociendo que esto es principalmente una experiencia de aprendizaje, espero que alguien pueda señalarme en la dirección correcta sobre cómo hacer esto. Estoy intentando esto en Rails 3.

+0

Dicho sea de paso, si usa este código para SECAR en migraciones, puede indicar un olor a código porque está repitiendo campos en todo su modelo de datos.Puede indicar que debe normalizar su modelo de datos para reducir la duplicación. – BlueFish

Respuesta

5

Así es como agregué campos personalizados de migración con Rails 2 y MySQL para un proyecto anterior. Funciona genial.

No sé cuánto de esto se aplica a su necesidad exacta, así que siéntase libre de hacerme preguntas.

que poner este código en Rails.root/lib/dbd_migration_helper.rb

module Ddb 

    module MigrationHelper 

    def self.included(base) # :nodoc: 
     base.send(:include, InstanceMethods) 
    end 

    module InstanceMethods 
     def active (column_name=:active)  column(column_name, :boolean, :default=>true) end 
     def email  (column_name=:email)  column(column_name, :string)  end 
     def latitude (column_name=:latitude) column(column_name, :float)  end 
     def longitude (column_name=:longitude) column(column_name, :float)  end 
     def position (column_name=:position) column(column_name, :integer) end 
    end 
    end 
end 

require 'activerecord' 
if defined?(ActiveRecord::ConnectionAdapters::TableDefinition) 
    ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Ddb::MigrationHelper) 
end 
2

Nota rápida, estas líneas:

ActiveRecord::ConnectionAdapters::Table.send :include, HiddenRecord::Orm::ActiveRecord::Schema 
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HiddenRecord::Orm::ActiveRecord::Schema 

no parecen ser include'ing el módulo correcto . Creo que deberían ser:

ActiveRecord::ConnectionAdapters::Table.send :include, HiddenRecord::Schema 
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HiddenRecord::Schema 

pero luego, no parecen tener #tracks_hidden_at definido en cualquier lugar, ya sea.

Cuestiones relacionadas