migration-fu 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/README.rdoc +51 -0
- data/Rakefile +45 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/migration_fu.rb +58 -0
- data/lib/version.rb +5 -0
- data/migration-fu.gemspec +21 -0
- data/test/migration_fu_test.rb +85 -0
- metadata +76 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
= MigrationFu
|
2
|
+
|
3
|
+
Rails gem / plugin for generating mysql foreign key constraints.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
=== as gem
|
8
|
+
|
9
|
+
sudo gem install sleistner-migration_fu --source http://gems.github.com
|
10
|
+
|
11
|
+
=== or plugin
|
12
|
+
|
13
|
+
script/plugin install git://github.com/sleistner/migration_fu.git
|
14
|
+
|
15
|
+
== Usage
|
16
|
+
|
17
|
+
----------------- -----------------
|
18
|
+
| users | | addresses |
|
19
|
+
----------------- -----------------
|
20
|
+
| id | | id |
|
21
|
+
| username | <---- | user_id |
|
22
|
+
| password | | street |
|
23
|
+
----------------- -----------------
|
24
|
+
|
25
|
+
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
|
26
|
+
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
|
27
|
+
|
28
|
+
see http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
|
29
|
+
|
30
|
+
arguments: from_table, to_table, options => { :name, :on_delete, :on_update }
|
31
|
+
|
32
|
+
add_foreign_key(:addresses, :users, :name => 'fk_add_user')
|
33
|
+
add_foreign_key(:addresses, :users, :on_delete => :cascade)
|
34
|
+
add_foreign_key(:addresses, :users, :on_delete => :cascade, :on_update => :cascade)
|
35
|
+
|
36
|
+
class CreateUsers < ActiveRecord::Migration
|
37
|
+
|
38
|
+
def self.up
|
39
|
+
create_table :users, :force => true do |t|
|
40
|
+
t.string :username, :null => false
|
41
|
+
t.string :password, :null => false
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table :addresses, :force => true do |t|
|
45
|
+
t.references :user
|
46
|
+
t.string :street, :null => false
|
47
|
+
end
|
48
|
+
|
49
|
+
add_foreign_key(:addresses, :users, :on_delete => :cascade)
|
50
|
+
end
|
51
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'echoe'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test the migration_fu plugin.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.pattern = 'test/**/*_test.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate documentation for the migration_fu plugin.'
|
18
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'MigrationFu'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace :test do
|
27
|
+
desc 'Measure test coverage'
|
28
|
+
task :coverage do
|
29
|
+
system("rcov --rails --text-summary -Ilib --xrefs --html test/unit/*_test.rb")
|
30
|
+
#test/functional/*_test.rb test/views/*_test.rb test/integration/*_test.rb")
|
31
|
+
system("open coverage/index.html") if PLATFORM['darwin']
|
32
|
+
system("firefox coverage/index.html") if PLATFORM['linux']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Echoe.new('migration_fu', '0.0.2') do |p|
|
37
|
+
p.description = 'Rails gem / plugin for generating mysql foreign key constraints.'
|
38
|
+
p.url = 'http://github.com/sleistner/migration_fu'
|
39
|
+
p.author = 'Steffen Leistner'
|
40
|
+
p.email = 'sleistner@gmail.com'
|
41
|
+
p.ignore_pattern = ['tmp/*', 'script/*.rake']
|
42
|
+
p.development_dependencies = []
|
43
|
+
end
|
44
|
+
|
45
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'migration_fu'
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
|
data/lib/migration_fu.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
|
3
|
+
class Migration
|
4
|
+
|
5
|
+
MAX_KEY_LENGTH = 64
|
6
|
+
OPTION_KEYS = [:restrict, :set_null, :cascade, :no_action]
|
7
|
+
OPTION_VALUES = [:on_update, :on_delete]
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def add_foreign_key(from_table, to_table, options = {})
|
12
|
+
process(from_table, to_table, options) do |ft, tt, id, fk|
|
13
|
+
execute "ALTER TABLE #{ft} ADD CONSTRAINT #{id} FOREIGN KEY(#{fk}) REFERENCES #{tt}(id)" << conditions(options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_foreign_key(from_table, to_table, options = {})
|
18
|
+
process(from_table, to_table, options) do |ft, tt, id|
|
19
|
+
execute "ALTER TABLE #{ft} DROP FOREIGN KEY #{id}, DROP KEY #{id}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def entirely_reset_column_information
|
24
|
+
ActiveRecord::Base.send(:subclasses).each(&:reset_column_information)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def conditions(options)
|
30
|
+
conditions = ''
|
31
|
+
options.each_pair do |key, value|
|
32
|
+
conditions << " #{key.to_s.gsub(/_/, ' ')} #{value.to_s.gsub(/_/, ' ')}".upcase if condition_valid?(key, value)
|
33
|
+
end
|
34
|
+
conditions
|
35
|
+
end
|
36
|
+
|
37
|
+
def condition_valid?(key, value)
|
38
|
+
OPTION_VALUES.include?(key.to_sym) && OPTION_KEYS.include?(value.to_sym)
|
39
|
+
end
|
40
|
+
|
41
|
+
def process(from_table, to_table, options)
|
42
|
+
id = options[:name] || "fk_#{from_table}_#{to_table}"
|
43
|
+
if options[:fk_field]
|
44
|
+
fk = options[:fk_field]
|
45
|
+
id = "#{id}_#{options[:fk_field]}"
|
46
|
+
else
|
47
|
+
fk = "#{to_table.to_s.singularize}_id"
|
48
|
+
end
|
49
|
+
|
50
|
+
if id.size > MAX_KEY_LENGTH
|
51
|
+
puts "*** foreign key id has more than #{MAX_KEY_LENGTH} characters - sliced to '#{id}'"
|
52
|
+
end
|
53
|
+
yield(from_table.to_s, to_table, id[0...MAX_KEY_LENGTH], fk)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "migration-fu"
|
7
|
+
s.version = Migration::Fu::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["me also"]
|
10
|
+
s.email = ["sumskyi@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{fks}
|
13
|
+
s.description = %q{foreign keys support for MySQL}
|
14
|
+
|
15
|
+
s.rubyforge_project = "sumskyi-migration-fu"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
begin require 'redgreen'; rescue LoadError; end
|
4
|
+
require File.dirname(__FILE__) + '/../lib/migration_fu'
|
5
|
+
|
6
|
+
class String
|
7
|
+
def singularize; self[0...-1] end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ActiveRecord::Migration
|
11
|
+
def self.execute command; command end
|
12
|
+
end
|
13
|
+
|
14
|
+
class MigrationFuTest < Test::Unit::TestCase
|
15
|
+
|
16
|
+
ID = 'fk_users_files'
|
17
|
+
CUSTOM_ID = 'fk_my_name'
|
18
|
+
CUSTOM_FK = 'person_id'
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@foo = ActiveRecord::Migration
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_add_foreign_key_without_options
|
25
|
+
assert_equal add_command, add
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_add_foreign_key_with_invalid_options_but_ignore_them
|
29
|
+
assert_equal add_command, add(:on_del => :ca)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_add_foreign_key_with_valid_options
|
33
|
+
assert_equal "#{add_command} ON DELETE CASCADE", add(:on_delete => :cascade)
|
34
|
+
assert_match(/ON DELETE CASCADE/, add(:on_update => :set_null, :on_delete => :cascade))
|
35
|
+
assert_match(/ON UPDATE SET NULL/, add(:on_update => :set_null, :on_delete => :cascade))
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_add_foreign_key_with_optional_name
|
39
|
+
assert_equal add_command(CUSTOM_ID), add(:name => CUSTOM_ID)
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def test_should_add_foreign_key_with_optional_fk_field
|
44
|
+
assert_equal add_command(CUSTOM_ID, :fk_field => CUSTOM_FK), add(:name => CUSTOM_ID, :fk_field => CUSTOM_FK)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_add_foreign_key_and_truncate_id
|
48
|
+
to = 'x' * 70
|
49
|
+
assert_equal add_command('fk_users_' << 'x' * 55, :to => to), @foo.add_foreign_key(:users, to.to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_remove_foreign_key
|
53
|
+
assert_equal remove_command, remove
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_remove_foreign_key_with_optional_name
|
57
|
+
assert_equal remove_command(CUSTOM_ID), remove(:name => CUSTOM_ID)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def add(options = {})
|
63
|
+
@foo.add_foreign_key :users, :files, options
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove(options = {})
|
67
|
+
@foo.remove_foreign_key :users, :files, options
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_command(id = ID, opts = {})
|
71
|
+
opts = {:to => 'files'}.merge!(opts)
|
72
|
+
if opts[:fk_field]
|
73
|
+
fk = opts[:fk_field]
|
74
|
+
id = "#{id}_#{opts[:fk_field]}"
|
75
|
+
else
|
76
|
+
fk = "#{opts[:to].singularize}_id"
|
77
|
+
end
|
78
|
+
"ALTER TABLE users ADD CONSTRAINT #{id} FOREIGN KEY(#{fk}) REFERENCES #{opts[:to]}(id)"
|
79
|
+
end
|
80
|
+
|
81
|
+
def remove_command(id = ID)
|
82
|
+
"ALTER TABLE users DROP FOREIGN KEY #{id}, DROP KEY #{id}"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: migration-fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- me also
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-25 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: foreign keys support for MySQL
|
23
|
+
email:
|
24
|
+
- sumskyi@gmail.com
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- README.rdoc
|
34
|
+
- Rakefile
|
35
|
+
- init.rb
|
36
|
+
- install.rb
|
37
|
+
- lib/migration_fu.rb
|
38
|
+
- lib/version.rb
|
39
|
+
- migration-fu.gemspec
|
40
|
+
- test/migration_fu_test.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: ""
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: sumskyi-migration-fu
|
71
|
+
rubygems_version: 1.4.2
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: fks
|
75
|
+
test_files:
|
76
|
+
- test/migration_fu_test.rb
|