开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜