How to extend ActiveRecord::Migration with additional methods?
I'm creating a Ruby gem and would like to extend ActiveRecord::Migration with my own helpers for creating the necessary columns. (This is similar to what Devise does when creating migrations for their various authentication strategies.) I realize the functionality I'm adding is pretty trivial itself and there are probably better/more efficient ways of doing this - I'm attempting this as a learning experience rather than as something with practical application. I just want to understand how to do something as invasive as adding new migration capabilities in Rails.
What I have so far builds into a gem successfully and installs, but when I attempt to run a migration like:
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
... it fails saying that hideable isn't defined.
I've looked into the way that Devise has done this and I have to admit I'm a little lost, but I've tried to fumble through it. I've done the following:
Extended ActiveRecord with my new model additions and created a method to apply the schema changes based on my new migration methods
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.
开发者_C百科 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
Created a Schema module similar to Devise's schema.rb that defines the methods I want to use in the migration and calls a method to apply the schema
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
Added methods for the models to support the new fields
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
Load the schema and model files and in the main module of the gem
module HiddenRecord
autoload :Schema, 'hiddenrecord/schema'
autoload :Models, 'hiddenrecord/models'
...
end
require 'hiddenrecord/models/hideable'
require 'hiddenrecord/models/tracks_hidden_timestamp'
Again, recognizing that this is primarily a learning experience, I'm hoping someone can point me in the right direction on how to do this. I'm attempting this on Rails 3.
Here's how I added custom migration fields with Rails 2 and MySQL for a previous project. Works great.
I don't know how much of this applies to your exact need, so feel free to ask me questions.
I put this code in 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
Quick note, these lines:
ActiveRecord::ConnectionAdapters::Table.send :include, HiddenRecord::Orm::ActiveRecord::Schema
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HiddenRecord::Orm::ActiveRecord::Schema
don't appear to be include'ing the correct module. I think they should be:
ActiveRecord::ConnectionAdapters::Table.send :include, HiddenRecord::Schema
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HiddenRecord::Schema
but then, you don't appear to have #tracks_hidden_at
defined anywhere either.
精彩评论