Ruby on Rails自定义迁移生成器

6

我正在创建一个与Active Record密切集成的Rails宝石(gem)。该宝石(gem)需要定义一系列的字段。例如:

class User < ActiveRecord::Base
  # requires 'avatar_identifier', 'avatar_extension', 'avatar_size'
  has_attached :avatar
end

有没有可能实现类似以下的功能:

rails g model user name:string avatar:attached

导致:
create_table :users do |t|
  t.string :name
  t.string :avatar_identifier
  t.string :avatar_extension
  t.integer :avatar_size
end

如果这不可行,有没有任何方法可以实现以下目的:
create_table :users do |t|
  t.string :name
  t.attached :avatar
end

生成多个字段?谢谢!

3个回答

5

虽然Pravin指出了正确的方向,但我发现实现起来并不直接。我采取了以下措施,在config/initializers中添加了一个文件(名称不重要),其内容如下:

require 'active_support'
require 'active_record'

class YourApplication
  module SchemaDefinitions

    module ExtraMethod
      def attachment(*args)
        options = args.extract_options!
        args.each do |col|
          column("#{col}_identifier", :string, options)
          column("#{col}_extension", :string, options)
          column("#{col}_size", :integer, options)
        end
      end
    end

    def self.load!
      ::ActiveRecord::ConnectionAdapters::TableDefinition.class_eval { include YourApplication::SchemaDefinitions::ExtraMethod }
    end

  end
end


ActiveSupport.on_load :active_record do
  YourApplication::SchemaDefinitions.load!
end

然后你可以这样做:
rails g model Person name:string title:string avatar:attachment

这将创建以下迁移:

def self.up
  create_table :people do |t|
    t.string :name
    t.string :title
    t.attachment :avatar

    t.timestamps
  end
end

如果您运行迁移,rake db:migrate,它将创建以下Person模型:
ruby-1.9.2-p0 > Person
 => Person(id: integer, name: string, title: string, avatar_identifier: string, avatar_extension: string, avatar_size: integer, created_at: datetime, updated_at: datetime) 

希望这有所帮助!

4
实际上,如果您调用
rails g model profile name:string next:attached

Rails已经为您生成了一个带有迁移功能的模板:

def self.up
  create_table :profiles do |t|
    t.string :name
    t.attached :next

    t.timestamps
  end
end

但是,您可以通过将其放置在/lib/templates/active_record/model/migration.rb中来覆盖默认的迁移模板。

您应该编写一个 rake my_gem:setup 任务将文件放置在那里。我没有尝试过,但我猜测Rails不会在非引擎宝石中查找这些模板。

您的迁移模板内容应如下:

class <%= migration_class_name %> < ActiveRecord::Migration
  def self.up
    create_table :<%= table_name %> do |t|
<% for attribute in attributes -%>
  <% if attribute.type.to_s == "attached" %>
      t.string :<%= attribute.name %>_identifier
      t.string :<%= attribute.name %>_extension
      t.integer :<%= attribute.name %>_size
  <% else %>
      t.<%= attribute.type %> :<%= attribute.name %>
  <% end %>
<% end -%>
<% if options[:timestamps] %>
      t.timestamps
<% end -%>
    end
  end

  def self.down
    drop_table :<%= table_name %>
  end
end

这是一个不错的替代方法,但它只适用于使用生成器的情况。 - nathanvda
好的,问题是“是否可能有像rails g model user name:string avatar:attached这样的东西”。因此我想这是可行的,但我理解你的观点。 - Valdis

2

我认为t.attached和多态关联中的t.references类似。

关于references方法,您可以像下面这样使用:

def attached(*args)
  options = args.extract_options!
  column(:avatar_identifier, :string, options)
  column(:avatar_extension, :string, options)
  column(:avatar_size, :integer, options)
end

您可能想要扩展 ActiveRecord::ConnectionAdapters::TableDefinition
请查看这个链接:http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-references


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接