ridgepole 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/README.md +1 -0
- data/Rakefile +5 -1
- data/bin/ridgepole +49 -14
- data/lib/ridgepole/client.rb +8 -4
- data/lib/ridgepole/diff.rb +32 -13
- data/lib/ridgepole/dsl_parser.rb +18 -5
- data/lib/ridgepole/dumper.rb +43 -1
- data/lib/ridgepole/version.rb +1 -1
- data/ridgepole.gemspec +2 -1
- data/spec/dump/dump_spec.rb +75 -0
- data/spec/migrate/migrate_add_column_spec.rb +155 -0
- data/spec/migrate/migrate_change_column_spec.rb +152 -0
- data/spec/migrate/migrate_create_index_spec.rb +91 -0
- data/spec/migrate/migrate_create_table_spec.rb +91 -0
- data/spec/migrate/migrate_drop_column_spec.rb +145 -0
- data/spec/migrate/migrate_drop_index.rb +91 -0
- data/spec/migrate/migrate_drop_table.rb +91 -0
- data/spec/migrate/migrate_empty_spec.rb +83 -0
- data/spec/migrate/migrate_merge_mode_spec.rb +157 -0
- data/spec/migrate/migrate_rename_column_spec.rb +152 -0
- data/spec/migrate/migrate_rename_table_spec.rb +152 -0
- data/spec/migrate/migrate_same_spec.rb +83 -0
- data/spec/ridgepole_test_database.sql +2 -0
- data/spec/ridgepole_test_tables.sql +79 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/string_ext.rb +61 -0
- metadata +54 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 540a3e7012616c467934600e8bf44d720cf33f77
|
4
|
+
data.tar.gz: eaebf6d806fb23a6d6cd82b6af8415d64286d0d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3f0ffa78c0fb82ff822e02230c9d2997823bc5090dd2e21e41f918b1436793f1f731852237843dcae2bb13ea93a48f8693127207446bc32962f90b0a069814f
|
7
|
+
data.tar.gz: 7e4cc137e218c8028a03b8b4c222b477dc037cc4cd99dea39e98721778354ddafa959010354ee8fc14116939b8aaa7306abde4667c4f056ee60170ab05854072
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/README.md
CHANGED
@@ -6,6 +6,7 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
|
|
6
6
|
(like Chef/Puppet)
|
7
7
|
|
8
8
|
[![Gem Version](https://badge.fury.io/rb/ridgepole.png)](http://badge.fury.io/rb/ridgepole)
|
9
|
+
[![Build Status](https://drone.io/bitbucket.org/winebarrel/ridgepole/status.png)](https://drone.io/bitbucket.org/winebarrel/ridgepole/latest)
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
data/Rakefile
CHANGED
data/bin/ridgepole
CHANGED
@@ -12,21 +12,31 @@ config = nil
|
|
12
12
|
mode = nil
|
13
13
|
file = DEFAULT_FILENAME
|
14
14
|
output_file = '-'
|
15
|
+
split = false
|
15
16
|
|
16
17
|
options = {
|
17
18
|
:dry_run => false,
|
18
19
|
:debug => false,
|
19
20
|
}
|
20
21
|
|
22
|
+
set_mode = proc do |m|
|
23
|
+
raise 'More than one mode is specified' if mode
|
24
|
+
mode = m
|
25
|
+
end
|
26
|
+
|
21
27
|
ARGV.options do |opt|
|
22
28
|
begin
|
23
|
-
opt.on('-c', '--config CONF_OR_FILE')
|
24
|
-
opt.on('-a', '--apply')
|
25
|
-
opt.on('-
|
26
|
-
opt.on('',
|
27
|
-
opt.on('
|
28
|
-
opt.on('-
|
29
|
-
opt.on(''
|
29
|
+
opt.on('-c', '--config CONF_OR_FILE') {|v| config = v }
|
30
|
+
opt.on('-a', '--apply') { set_mode[:apply] }
|
31
|
+
opt.on('-m', '--merge') { set_mode[:apply]; options[:merge] = true }
|
32
|
+
opt.on('-f', '--file FILE') {|v| file = v }
|
33
|
+
opt.on('', '--dry-run') { options[:dry_run] = true }
|
34
|
+
opt.on('-e', '--export') { set_mode[:export] }
|
35
|
+
opt.on('', '--split') {|v| split = true }
|
36
|
+
opt.on('-o', '--output FILE') {|v| output_file = v }
|
37
|
+
opt.on('-t', '--tables TABLES', Array) {|v| options[:tables] = v }
|
38
|
+
opt.on('', '--disable-mysql-unsigned') { options[:disable_mysql_unsigned] = true }
|
39
|
+
opt.on('' , '--debug') { options[:debug] = true }
|
30
40
|
opt.parse!
|
31
41
|
|
32
42
|
unless config and mode
|
@@ -49,24 +59,49 @@ begin
|
|
49
59
|
|
50
60
|
case mode
|
51
61
|
when :export
|
52
|
-
if
|
53
|
-
logger.info('
|
54
|
-
|
62
|
+
if split
|
63
|
+
logger.info('Export Schema')
|
64
|
+
|
65
|
+
output_file = DEFAULT_FILENAME if output_file == '-'
|
66
|
+
requires = []
|
67
|
+
|
68
|
+
client.dump do |name, definition|
|
69
|
+
schema_file = File.join(File.dirname(output_file), "#{name}.schema")
|
70
|
+
requires << schema_file
|
71
|
+
logger.info(" write `#{schema_file}`")
|
72
|
+
|
73
|
+
open(schema_file, 'wb') do |f|
|
74
|
+
f.puts definition
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
logger.info(" write `#{output_file}`")
|
79
|
+
|
80
|
+
open(output_file, 'wb') do |f|
|
81
|
+
requires.each do |schema_file|
|
82
|
+
f.puts "require '#{File.basename schema_file}'"
|
83
|
+
end
|
84
|
+
end
|
55
85
|
else
|
56
|
-
|
57
|
-
|
86
|
+
if output_file == '-'
|
87
|
+
logger.info('# Export Schema')
|
88
|
+
puts client.dump
|
89
|
+
else
|
90
|
+
logger.info("Export Schema to `#{output_file}`")
|
91
|
+
open(output_file, 'wb') {|f| f.puts client.dump }
|
92
|
+
end
|
58
93
|
end
|
59
94
|
when :apply
|
60
95
|
unless File.exist?(file)
|
61
96
|
raise "No Schemafile found (looking for: #{file})"
|
62
97
|
end
|
63
98
|
|
64
|
-
msg =
|
99
|
+
msg = (options[:merge] ? 'Merge' : 'Apply') + " `#{file}`"
|
65
100
|
msg << ' (dry-run)' if options[:dry_run]
|
66
101
|
logger.info(msg)
|
67
102
|
|
68
103
|
dsl = open(file) {|f| f.read }
|
69
|
-
delta = client.diff(dsl)
|
104
|
+
delta = client.diff(dsl, :path => file)
|
70
105
|
|
71
106
|
if options[:dry_run]
|
72
107
|
puts delta.script if delta.differ?
|
data/lib/ridgepole/client.rb
CHANGED
@@ -6,14 +6,18 @@ class Ridgepole::Client
|
|
6
6
|
@dumper = Ridgepole::Dumper.new(@options)
|
7
7
|
@parser = Ridgepole::DSLParser.new(@options)
|
8
8
|
@diff = Ridgepole::Diff.new(@options)
|
9
|
+
|
10
|
+
unless @options[:disable_mysql_unsigned]
|
11
|
+
require 'activerecord-mysql-unsigned'
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
|
-
def dump
|
12
|
-
@dumper.dump
|
15
|
+
def dump(&block)
|
16
|
+
@dumper.dump(&block)
|
13
17
|
end
|
14
18
|
|
15
|
-
def diff(dsl)
|
16
|
-
expected_definition = @parser.parse(dsl)
|
19
|
+
def diff(dsl, opts = {})
|
20
|
+
expected_definition = @parser.parse(dsl, opts)
|
17
21
|
current_definition = @parser.parse(@dumper.dump)
|
18
22
|
@diff.diff(current_definition, expected_definition)
|
19
23
|
end
|
data/lib/ridgepole/diff.rb
CHANGED
@@ -9,7 +9,9 @@ class Ridgepole::Diff
|
|
9
9
|
delta = {}
|
10
10
|
|
11
11
|
to.dup.each do |table_name, to_attrs|
|
12
|
-
|
12
|
+
next unless target?(table_name)
|
13
|
+
|
14
|
+
if (from_table_name = (to_attrs[:options] || {}).delete(:rename_from))
|
13
15
|
next unless from.has_key?(from_table_name)
|
14
16
|
delta[:rename] ||= {}
|
15
17
|
delta[:rename][table_name] = from_table_name
|
@@ -19,6 +21,8 @@ class Ridgepole::Diff
|
|
19
21
|
end
|
20
22
|
|
21
23
|
to.each do |table_name, to_attrs|
|
24
|
+
next unless target?(table_name)
|
25
|
+
|
22
26
|
if (from_attrs = from.delete(table_name))
|
23
27
|
scan_change(table_name, from_attrs, to_attrs, delta)
|
24
28
|
else
|
@@ -27,9 +31,13 @@ class Ridgepole::Diff
|
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
unless @options[:merge]
|
35
|
+
from.each do |table_name, from_attrs|
|
36
|
+
next unless target?(table_name)
|
37
|
+
|
38
|
+
delta[:delete] ||= {}
|
39
|
+
delta[:delete][table_name] = from_attrs
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
43
|
Ridgepole::Delta.new(delta, @options)
|
@@ -62,7 +70,7 @@ class Ridgepole::Diff
|
|
62
70
|
definition_delta = {}
|
63
71
|
|
64
72
|
to.dup.each do |column_name, to_attrs|
|
65
|
-
if (from_column_name = (to_attrs[:options] || {}).delete(:
|
73
|
+
if (from_column_name = (to_attrs[:options] || {}).delete(:rename_from))
|
66
74
|
next unless from.has_key?(from_column_name)
|
67
75
|
definition_delta[:rename] ||= {}
|
68
76
|
definition_delta[:rename][column_name] = from_column_name
|
@@ -95,9 +103,11 @@ class Ridgepole::Diff
|
|
95
103
|
priv_column_name = column_name
|
96
104
|
end
|
97
105
|
|
98
|
-
|
99
|
-
|
100
|
-
|
106
|
+
unless @options[:merge]
|
107
|
+
from.each do |column_name, from_attrs|
|
108
|
+
definition_delta[:delete] ||= {}
|
109
|
+
definition_delta[:delete][column_name] = from_attrs
|
110
|
+
end
|
101
111
|
end
|
102
112
|
|
103
113
|
unless definition_delta.empty?
|
@@ -114,9 +124,12 @@ class Ridgepole::Diff
|
|
114
124
|
if (from_attrs = from.delete(index_name))
|
115
125
|
if from_attrs != to_attrs
|
116
126
|
indices_delta[:add] ||= {}
|
117
|
-
indices_delta[:delete] ||= {}
|
118
127
|
indices_delta[:add][index_name] = to_attrs
|
119
|
-
|
128
|
+
|
129
|
+
unless @options[:merge]
|
130
|
+
indices_delta[:delete] ||= {}
|
131
|
+
indices_delta[:delete][index_name] = from_attrs
|
132
|
+
end
|
120
133
|
end
|
121
134
|
else
|
122
135
|
indices_delta[:add] ||= {}
|
@@ -124,13 +137,19 @@ class Ridgepole::Diff
|
|
124
137
|
end
|
125
138
|
end
|
126
139
|
|
127
|
-
|
128
|
-
|
129
|
-
|
140
|
+
unless @options[:merge]
|
141
|
+
from.each do |index_name, from_attrs|
|
142
|
+
indices_delta[:delete] ||= {}
|
143
|
+
indices_delta[:delete][index_name] = from_attrs
|
144
|
+
end
|
130
145
|
end
|
131
146
|
|
132
147
|
unless indices_delta.empty?
|
133
148
|
table_delta[:indices] = indices_delta
|
134
149
|
end
|
135
150
|
end
|
151
|
+
|
152
|
+
def target?(table_name)
|
153
|
+
not @options[:tables] or @options[:tables].include?(table_name)
|
154
|
+
end
|
136
155
|
end
|
data/lib/ridgepole/dsl_parser.rb
CHANGED
@@ -39,12 +39,13 @@ class Ridgepole::DSLParser
|
|
39
39
|
|
40
40
|
attr_reader :__definition
|
41
41
|
|
42
|
-
def initialize
|
42
|
+
def initialize(opts = {})
|
43
|
+
@__working_dir = File.expand_path(opts[:path] ? File.dirname(opts[:path]) : Dir.pwd)
|
43
44
|
@__definition = {}
|
44
45
|
end
|
45
46
|
|
46
|
-
def self.eval(dsl)
|
47
|
-
ctx = self.new
|
47
|
+
def self.eval(dsl, opts = {})
|
48
|
+
ctx = self.new(opts)
|
48
49
|
ctx.instance_eval(dsl)
|
49
50
|
ctx.__definition
|
50
51
|
end
|
@@ -69,13 +70,25 @@ class Ridgepole::DSLParser
|
|
69
70
|
:options => options,
|
70
71
|
}
|
71
72
|
end
|
73
|
+
|
74
|
+
def require(file)
|
75
|
+
schemafile = File.join(@__working_dir, file)
|
76
|
+
|
77
|
+
if File.exist?(schemafile)
|
78
|
+
instance_eval(File.read(schemafile))
|
79
|
+
elsif File.exist?(schemafile + '.rb')
|
80
|
+
instance_eval(File.read(schemafile + '.rb'))
|
81
|
+
else
|
82
|
+
Kernel.require(file)
|
83
|
+
end
|
84
|
+
end
|
72
85
|
end
|
73
86
|
|
74
87
|
def initialize(options = {})
|
75
88
|
@options = options
|
76
89
|
end
|
77
90
|
|
78
|
-
def parse(dsl)
|
79
|
-
Context.eval(dsl)
|
91
|
+
def parse(dsl, opts = {})
|
92
|
+
Context.eval(dsl, opts)
|
80
93
|
end
|
81
94
|
end
|
data/lib/ridgepole/dumper.rb
CHANGED
@@ -7,10 +7,52 @@ class Ridgepole::Dumper
|
|
7
7
|
stream = StringIO.new
|
8
8
|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
9
9
|
|
10
|
-
stream.string.lines.select {|line|
|
10
|
+
dsl = stream.string.lines.select {|line|
|
11
11
|
line !~ /\A#/ &&
|
12
12
|
line !~ /\AActiveRecord::Schema\.define/ &&
|
13
13
|
line !~ /\Aend/
|
14
14
|
}.join.undent.strip
|
15
|
+
|
16
|
+
definitions = []
|
17
|
+
|
18
|
+
each_table(dsl) do |name, definition|
|
19
|
+
if target?(name)
|
20
|
+
definitions << definition
|
21
|
+
yield(name, definition) if block_given?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
definitions.join("\n\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def each_table(dsl)
|
31
|
+
name = nil
|
32
|
+
definition = []
|
33
|
+
|
34
|
+
pass = proc do
|
35
|
+
if name
|
36
|
+
yield(name, definition.join.strip)
|
37
|
+
name = nil
|
38
|
+
definition = []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
dsl.lines.each do |line|
|
43
|
+
if line =~ /\Acreate_table/
|
44
|
+
pass.call
|
45
|
+
name = line.split(/[\s,'"]+/)[1]
|
46
|
+
definition << line
|
47
|
+
elsif name
|
48
|
+
definition << line
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
pass.call
|
53
|
+
end
|
54
|
+
|
55
|
+
def target?(table_name)
|
56
|
+
not @options[:tables] or @options[:tables].include?(table_name)
|
15
57
|
end
|
16
58
|
end
|
data/lib/ridgepole/version.rb
CHANGED
data/ridgepole.gemspec
CHANGED
@@ -19,8 +19,9 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_dependency 'activerecord'
|
22
|
+
spec.add_dependency 'activerecord-mysql-unsigned'
|
22
23
|
spec.add_development_dependency 'bundler'
|
23
24
|
spec.add_development_dependency 'rake'
|
24
|
-
spec.add_development_dependency 'rspec', '
|
25
|
+
spec.add_development_dependency 'rspec', '~> 2.14.1'
|
25
26
|
spec.add_development_dependency 'mysql2'
|
26
27
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
describe 'Ridgepole::Client#dump' do
|
2
|
+
context 'when there is a tables' do
|
3
|
+
before { restore_tables }
|
4
|
+
subject { client }
|
5
|
+
|
6
|
+
it {
|
7
|
+
expect(subject.dump).to eq (<<-RUBY).undent.strip
|
8
|
+
create_table "clubs", force: true do |t|
|
9
|
+
t.string "name", default: "", null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index "clubs", ["name"], name: "idx_name", unique: true, using: :btree
|
13
|
+
|
14
|
+
create_table "departments", primary_key: "dept_no", force: true do |t|
|
15
|
+
t.string "dept_name", limit: 40, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index "departments", ["dept_name"], name: "dept_name", unique: true, using: :btree
|
19
|
+
|
20
|
+
create_table "dept_emp", id: false, force: true do |t|
|
21
|
+
t.integer "emp_no", null: false
|
22
|
+
t.string "dept_no", limit: 4, null: false
|
23
|
+
t.date "from_date", null: false
|
24
|
+
t.date "to_date", null: false
|
25
|
+
end
|
26
|
+
|
27
|
+
add_index "dept_emp", ["dept_no"], name: "dept_no", using: :btree
|
28
|
+
add_index "dept_emp", ["emp_no"], name: "emp_no", using: :btree
|
29
|
+
|
30
|
+
create_table "dept_manager", id: false, force: true do |t|
|
31
|
+
t.string "dept_no", limit: 4, null: false
|
32
|
+
t.integer "emp_no", null: false
|
33
|
+
t.date "from_date", null: false
|
34
|
+
t.date "to_date", null: false
|
35
|
+
end
|
36
|
+
|
37
|
+
add_index "dept_manager", ["dept_no"], name: "dept_no", using: :btree
|
38
|
+
add_index "dept_manager", ["emp_no"], name: "emp_no", using: :btree
|
39
|
+
|
40
|
+
create_table "employee_clubs", force: true do |t|
|
41
|
+
t.integer "emp_no", unsigned: true, null: false
|
42
|
+
t.integer "club_id", unsigned: true, null: false
|
43
|
+
end
|
44
|
+
|
45
|
+
add_index "employee_clubs", ["emp_no", "club_id"], name: "idx_emp_no_club_id", using: :btree
|
46
|
+
|
47
|
+
create_table "employees", primary_key: "emp_no", force: true do |t|
|
48
|
+
t.date "birth_date", null: false
|
49
|
+
t.string "first_name", limit: 14, null: false
|
50
|
+
t.string "last_name", limit: 16, null: false
|
51
|
+
t.string "gender", limit: 1, null: false
|
52
|
+
t.date "hire_date", null: false
|
53
|
+
end
|
54
|
+
|
55
|
+
create_table "salaries", id: false, force: true do |t|
|
56
|
+
t.integer "emp_no", null: false
|
57
|
+
t.integer "salary", null: false
|
58
|
+
t.date "from_date", null: false
|
59
|
+
t.date "to_date", null: false
|
60
|
+
end
|
61
|
+
|
62
|
+
add_index "salaries", ["emp_no"], name: "emp_no", using: :btree
|
63
|
+
|
64
|
+
create_table "titles", id: false, force: true do |t|
|
65
|
+
t.integer "emp_no", null: false
|
66
|
+
t.string "title", limit: 50, null: false
|
67
|
+
t.date "from_date", null: false
|
68
|
+
t.date "to_date"
|
69
|
+
end
|
70
|
+
|
71
|
+
add_index "titles", ["emp_no"], name: "emp_no", using: :btree
|
72
|
+
RUBY
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|