Add join table migration generator

For instance, running

    rails g migration CreateMediaJoinTable artists musics:uniq

will create a migration with

    create_join_table :artists, :musics do |t|
      # t.index [:artist_id, :music_id]
      t.index [:music_id, :artist_id], unique: true
    end
This commit is contained in:
Aleksey Magusev 2012-07-11 01:38:21 +04:00
parent d481170251
commit 211d88b71b
7 changed files with 66 additions and 16 deletions

@ -206,9 +206,12 @@ def create_join_table(table_1, table_2, options = {})
column_options = options.delete(:column_options) || {} column_options = options.delete(:column_options) || {}
column_options.reverse_merge!({:null => false}) column_options.reverse_merge!({:null => false})
t1_column, t2_column = [table_1, table_2].map{ |t| "#{t.to_s.singularize}_id" }
create_table(join_table_name, options.merge!(:id => false)) do |td| create_table(join_table_name, options.merge!(:id => false)) do |td|
td.integer :"#{table_1.to_s.singularize}_id", column_options td.integer t1_column, column_options
td.integer :"#{table_2.to_s.singularize}_id", column_options td.integer t2_column, column_options
yield td if block_given?
end end
end end

@ -4,13 +4,11 @@ module JoinTable #:nodoc:
private private
def find_join_table_name(table_1, table_2, options = {}) def find_join_table_name(table_1, table_2, options = {})
options.delete(:table_name) { join_table_name(table_1, table_2) } options.delete(:table_name) || join_table_name(table_1, table_2)
end end
def join_table_name(table_1, table_2) def join_table_name(table_1, table_2)
tables_names = [table_1, table_2].map(&:to_s).sort [table_1, table_2].sort.join("_").to_sym
tables_names.join("_").to_sym
end end
end end
end end

@ -11,15 +11,28 @@ def create_migration_file
end end
protected protected
attr_reader :migration_action attr_reader :migration_action, :join_tables
def set_local_assigns! def set_local_assigns!
if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/ case file_name
@migration_action = $1 when /^(add|remove)_.*_(?:to|from)_(.*)/
@table_name = $2.pluralize @migration_action = $1
@table_name = $2.pluralize
when /join_table/
if attributes.length == 2
@migration_action = 'join'
@join_tables = attributes.map(&:name)
set_index_names
end end
end end
end
def set_index_names
attributes.each_with_index do |attr, i|
attr.index_name = [attr, attributes[i - 1]].map{ |a| :"#{a.name.singularize}_id"}
end
end
end end
end end
end end

@ -12,6 +12,14 @@ def change
<%- end -%> <%- end -%>
<%- end -%> <%- end -%>
end end
<%- elsif migration_action == 'join' -%>
def change
create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
<%- attributes.each do |attribute| -%>
<%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
<%- end -%>
end
end
<%- else -%> <%- else -%>
def up def up
<% attributes.each do |attribute| -%> <% attributes.each do |attribute| -%>

@ -42,22 +42,36 @@ def test_create_join_table_with_the_proper_order
end end
def test_create_join_table_with_the_table_name def test_create_join_table_with_the_table_name
connection.create_join_table :artists, :musics, :table_name => :catalog connection.create_join_table :artists, :musics, table_name: :catalog
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end end
def test_create_join_table_with_the_table_name_as_string def test_create_join_table_with_the_table_name_as_string
connection.create_join_table :artists, :musics, :table_name => 'catalog' connection.create_join_table :artists, :musics, table_name: 'catalog'
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end end
def test_create_join_table_with_column_options def test_create_join_table_with_column_options
connection.create_join_table :artists, :musics, :column_options => {:null => true} connection.create_join_table :artists, :musics, column_options: {null: true}
assert_equal [true, true], connection.columns(:artists_musics).map(&:null) assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
end end
def test_create_join_table_without_indexes
connection.create_join_table :artists, :musics
assert connection.indexes(:artists_musics).blank?
end
def test_create_join_table_with_index
connection.create_join_table :artists, :musics do |t|
t.index [:artist_id, :music_id]
end
assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns)
end
end end
end end
end end

@ -8,6 +8,7 @@ class GeneratedAttribute
attr_accessor :name, :type attr_accessor :name, :type
attr_reader :attr_options attr_reader :attr_options
attr_writer :index_name
class << self class << self
def parse(column_definition) def parse(column_definition)
@ -94,7 +95,7 @@ def human_name
end end
def index_name def index_name
if reference? @index_name ||= if reference?
polymorphic? ? %w(id type).map { |t| "#{name}_#{t}" } : "#{name}_id" polymorphic? ? %w(id type).map { |t| "#{name}_#{t}" } : "#{name}_id"
else else
name name

@ -167,6 +167,19 @@ def test_add_migration_with_references_options
end end
end end
def test_create_join_table_migration
migration = "add_media_join_table"
run_generator [migration, "artists", "musics:uniq"]
assert_migration "db/migrate/#{migration}.rb" do |content|
assert_method :change, content do |up|
assert_match(/create_join_table :artists, :musics/, up)
assert_match(/# t.index \[:artist_id, :music_id\]/, up)
assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, up)
end
end
end
def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove def test_should_create_empty_migrations_if_name_not_start_with_add_or_remove
migration = "create_books" migration = "create_books"
run_generator [migration, "title:string", "content:text"] run_generator [migration, "title:string", "content:text"]