ridgepole 0.0.1 → 0.1.0
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.
- 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
|
[](http://badge.fury.io/rb/ridgepole)
|
9
|
+
[](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
|