17 Stimmen

Wie können ActiveRecord::Migration um zusätzliche Methoden erweitert werden?

Ich erstelle ein Ruby-Gem und möchte ActiveRecord::Migration mit meinen eigenen Hilfsprogrammen erweitern, um die erforderlichen Spalten zu erstellen. (Das ist ähnlich wie bei Devise, wenn sie Migrationen für ihre verschiedenen Authentifizierungsstrategien erstellen.) Mir ist bewusst, dass die Funktionalität, die ich hinzufüge, selbst ziemlich trivial ist und es wahrscheinlich bessere/effizientere Möglichkeiten gibt, dies zu tun - ich versuche dies als Lernerfahrung, nicht als etwas mit praktischem Nutzen. Ich möchte einfach verstehen, wie man etwas so tiefgreifendes wie das Hinzufügen neuer Migrationseigenschaften in Rails macht.

Was ich bisher erstellt habe, baut erfolgreich in ein Gem ein und installiert sich, aber wenn ich versuche, eine Migration wie folgt auszuführen:

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

... schlägt es fehl und sagt, dass hideable nicht definiert ist.

Ich habe mir angesehen, wie Devise das gemacht hat, und ich muss zugeben, dass ich ein wenig verloren bin, aber ich habe versucht, mich durchzuarbeiten. Ich habe Folgendes gemacht:

Erweitert ActiveRecord mit meinen neuen Modell-Ergänzungen und eine Methode erstellt, um die Schema-Änderungen basierend auf meinen neuen Migrationsmethoden anzuwenden

require 'orm_adapter/adapters/active_record'

module HiddenRecord
  module Orm
    # Diese Modul enthält einige Hilfsprogramme und behandelt das Schema (Migrationen):
    #
    #   create_table :accounts do |t|
    #     t.hideable
    #     t.tracks_hidden_timestamp
    #   end
    #
    module ActiveRecord
      module Schema
        include HiddenRecord::Schema

        # Sagt, wie Schema-Methoden angewendet werden.
        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

Ein Schema-Modul ähnlich dem Schema.rb von Devise erstellt, das die Methoden definiert, die ich in der Migration verwenden möchte, und eine Methode aufruft, um das Schema anzuwenden

module HiddenRecord
  # Enthält die Schema-Definition für die Optionen des hiddenrecord-Modells.
  module Schema
    # Legt das Modell als Zeilen mit der Eigenschaft 'versteckbar' fest
    #
    # == Optionen
    # * :null - Wenn true, erlaubt es, dass die versteckte Zeilenflagge null ist
    # * :default - Wird verwendet, um den Standardstatus auf true festzulegen. Wenn nicht gesetzt, ist der Standardwert falsch (Zeilen sind nicht versteckt)
    def hideable(options={})
      null = options[:null] || false
      default = options[:default] || false

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

    # Legt das Modell so fest, dass der Zeitstempel aufgezeichnet wird, wenn eine Zeile versteckt wurde
    def tracks_hidden_timestamp()
      apply_hiddenrecord_schema :hiddenrecord_hidden_at, DateTime
    end
  end
end

Methoden für die Modelle hinzugefügt, um die neuen Felder zu unterstützen

module HiddenRecord
  module Models
    # Dieses Modul implementiert die API für 'versteckbar'
    module Hideable
      def self.included(base)
        base.class_eval do
          extend ClassMethods
        end
      end

      scope :sichtbar, 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

Das Schema- und Modelldateien laden und im Hauptmodul des Gems inkludieren

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

Noch einmal, wissend, dass dies hauptsächlich eine Lernerfahrung ist, hoffe ich, dass mir jemand den richtigen Weg zeigen kann, wie man das macht. Ich versuche dies auf Rails 3.

0 Stimmen

Übrigens, wenn Sie dies verwenden, um Code in Migrationen zu trocknen, kann es auf einen Code-Geruch hinweisen, da Sie Felder in Ihrem Datenmodell wiederholen. Dies könnte darauf hindeuten, dass Sie Ihr Datenmodell normalisieren sollten, um Redundanzen zu reduzieren.

7voto

joelparkerhenderson Punkte 33553

So habe ich benutzerdefinierte Migrationsfelder mit Rails 2 und MySQL für ein früheres Projekt hinzugefügt. Funktioniert großartig.

Ich weiß nicht, wie viel davon auf Ihr genaues Bedürfnis zutrifft, also zögern Sie nicht, mir Fragen zu stellen.

Ich habe diesen Code in Rails.root/lib/dbd_migration_helper.rb platziert

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

2voto

Michael Slade Punkte 13634

Schnelle Notiz, diese Zeilen:

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

scheinen nicht das richtige Modul zu inkludieren. Ich denke, sie sollten sein:

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

aber dann, scheinen Sie auch nirgendwo #tracks_hidden_at definiert zu haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X