ridgepole 0.4.7 → 0.4.8.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1450957da8f5ba966dfab934b036b4a93d972e99
4
- data.tar.gz: 2d323098fd39e4e86dea90279e40dad90a4c09a8
3
+ metadata.gz: 8bf1189c330df38c7b922e8d49efbe41345bf5ca
4
+ data.tar.gz: 7577f13cff3830ee86f19617b593c68a83d225d3
5
5
  SHA512:
6
- metadata.gz: 07dacc0bce5cba26d9636bb5acbfb70a7db5bc6bcb5de2eaec9e4ad37ed84728891414d6c534560f1b59c3b3db16714141cdf03eb322274f563e3e682fd069b4
7
- data.tar.gz: 3e064705391531a4918bfdf0c88ddcaa96ceab35d85b0e53a91966afd9fb3b0833365f2019098383407f441b51cd9b25d2a78f2a822ed46cfe0aeec0696ca61f
6
+ metadata.gz: 1599cba8ef9a7720d32b8da9d5f3403c4dec8a8a92930bd80072df35c6a190934ca61f990d97da47cf7464cfe9031b68f1cc69195dabf826fb5a09e8a5f3f0ff
7
+ data.tar.gz: 1709a0da42bb15ce7548b38a3b5bcfce395a4608c68201c8d1db16fd93c38172b9023cc5dcb8c47943abb6fb970792458552fabb414ccec36047c6e74effb4f1
data/README.md CHANGED
@@ -128,6 +128,25 @@ create_table "user_comments", force: true, renamed_from: "comments" do |t|
128
128
  end
129
129
  ```
130
130
 
131
+ ## Execute
132
+ ```sh
133
+ create_table "authors", force: true do |t|
134
+ t.string "name", null: false
135
+ end
136
+
137
+ create_table "books", force: true do |t|
138
+ t.string "title", null: false
139
+ t.integer "author_id", unsigned: true, null: false
140
+ end
141
+
142
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
143
+
144
+ execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
145
+ # Execute SQL only if there is no foreign key
146
+ c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = 'bookshelf' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
147
+ end
148
+ ```
149
+
131
150
  ## Diff
132
151
  ```sh
133
152
  $ ridgepole --diff file1.schema file2.schema
data/bin/ridgepole CHANGED
@@ -34,11 +34,23 @@ set_mode = proc do |m|
34
34
  end
35
35
 
36
36
  def noop_migrate(delta)
37
- puts delta.script + "\n\n"
37
+ unless delta.script.empty?
38
+ puts delta.script + "\n\n"
39
+ end
40
+
41
+ migrated, out = delta.migrate(:noop => true)
38
42
 
39
- delta.migrate(:noop => true).each_line do |line|
40
- puts line.strip.gsub(/([^\d])([(),])([^\d])/) { "#{$1}#{$2}\n#{$3}" }.each_line.map {|i| "# #{i.gsub(/^\s+/, '')}"}.join + "\n\n"
43
+ if migrated
44
+ out.each_line do |line|
45
+ if line =~ /\A\s+/
46
+ puts "# #{line}"
47
+ else
48
+ puts line.strip.gsub(/([^\d])([(),])([^\d])/) { "#{$1}#{$2}\n#{$3}" }.each_line.map {|i| "# #{i.gsub(/^\s+/, '')}"}.join + "\n"
49
+ end
50
+ end
41
51
  end
52
+
53
+ return migrated
42
54
  end
43
55
 
44
56
  ARGV.options do |opt|
@@ -161,17 +173,18 @@ begin
161
173
 
162
174
  dsl = File.read(file)
163
175
  delta = client.diff(dsl, :path => file)
176
+ differ = delta.differ?
164
177
 
165
178
  if options[:dry_run]
166
- if delta.differ?
167
- noop_migrate(delta)
179
+ if differ
180
+ differ = noop_migrate(delta)
168
181
  end
169
182
  else
170
183
  logger.verbose_info('# Update schema')
171
- delta.migrate
184
+ differ, out = delta.migrate
172
185
  end
173
186
 
174
- unless delta.differ?
187
+ unless differ
175
188
  logger.info('No change')
176
189
  end
177
190
  when :diff
@@ -193,15 +206,18 @@ begin
193
206
 
194
207
  if diff_with_apply
195
208
  logger.verbose_info('# Update schema')
209
+ differ = delta.differ?
196
210
 
197
- if delta.differ?
198
- delta.migrate
199
- else
211
+ if differ
212
+ differ, out = delta.migrate
213
+ end
214
+
215
+ if differ
200
216
  logger.info('No change')
201
217
  end
202
218
  elsif delta.differ?
203
- noop_migrate(delta)
204
- exit_code = 1
219
+ differ = noop_migrate(delta)
220
+ exit_code = 1 if differ
205
221
  end
206
222
  end
207
223
  rescue => e
@@ -22,11 +22,11 @@ class Ridgepole::Client
22
22
  logger = Ridgepole::Logger.instance
23
23
 
24
24
  logger.verbose_info('# Parse DSL')
25
- expected_definition = @parser.parse(dsl, opts)
25
+ expected_definition, expected_execute = @parser.parse(dsl, opts)
26
26
  logger.verbose_info('# Load tables')
27
- current_definition = @parser.parse(@dumper.dump)
27
+ current_definition, current_execute = @parser.parse(@dumper.dump)
28
28
  logger.verbose_info('# Compare definitions')
29
- @diff.diff(current_definition, expected_definition)
29
+ @diff.diff(current_definition, expected_definition, :execute => expected_execute)
30
30
  end
31
31
 
32
32
  class << self
@@ -34,9 +34,9 @@ class Ridgepole::Client
34
34
  logger = Ridgepole::Logger.instance
35
35
 
36
36
  logger.verbose_info('# Parse DSL1')
37
- definition1 = load_definition(dsl_or_config1)
37
+ definition1, execute1 = load_definition(dsl_or_config1)
38
38
  logger.verbose_info('# Parse DSL2')
39
- definition2 = load_definition(dsl_or_config2)
39
+ definition2, execute2 = load_definition(dsl_or_config2)
40
40
 
41
41
  logger.verbose_info('# Compare definitions')
42
42
  diff = Ridgepole::Diff.new(options)
@@ -4,6 +4,7 @@ class Ridgepole::Delta
4
4
  def initialize(delta, options = {})
5
5
  @delta = delta
6
6
  @options = options
7
+ @logger = Ridgepole::Logger.instance
7
8
  end
8
9
 
9
10
  def migrate(options = {})
@@ -41,12 +42,15 @@ class Ridgepole::Delta
41
42
  end
42
43
 
43
44
  def differ?
44
- not script.empty?
45
+ not script.empty? or not delta_execute.empty?
45
46
  end
46
47
 
47
48
  private
48
49
 
49
50
  def migrate0(options = {})
51
+ migrated = false
52
+ out = nil
53
+
50
54
  if options[:noop]
51
55
  disable_logging_orig = ActiveRecord::Migration.disable_logging
52
56
 
@@ -59,26 +63,74 @@ class Ridgepole::Delta
59
63
  end
60
64
 
61
65
  Ridgepole::ExecuteExpander.without_operation(callback) do
62
- eval_script(script, options.merge(:out => buf))
66
+ migrated = eval_script(script, options.merge(:out => buf))
63
67
  end
64
68
 
65
- buf.string.strip
69
+ out = buf.string.strip
66
70
  ensure
67
71
  ActiveRecord::Migration.disable_logging = disable_logging_orig
68
72
  end
69
73
  else
70
- eval_script(script, options)
74
+ migrated = eval_script(script, options)
71
75
  end
76
+
77
+ [migrated, out]
72
78
  end
73
79
 
74
80
  def eval_script(script, options = {})
81
+ execute_count = 0
82
+
75
83
  begin
76
84
  with_pre_post_query(options) do
77
- ActiveRecord::Schema.new.instance_eval(script, SCRIPT_NAME, 1)
85
+ unless script.empty?
86
+ ActiveRecord::Schema.new.instance_eval(script, SCRIPT_NAME, 1)
87
+ end
88
+
89
+ execute_count = execute_sqls(options)
78
90
  end
79
91
  rescue => e
80
92
  raise_exception(script, e)
81
93
  end
94
+
95
+ not script.empty? or execute_count.nonzero?
96
+ end
97
+
98
+ def execute_sqls(options = {})
99
+ es = @delta[:execute] || []
100
+ out = options[:out] || $stdout
101
+ execute_count = 0
102
+
103
+ es.each do |exec|
104
+ sql, cond = exec.values_at(:sql, :condition)
105
+ executable = false
106
+
107
+ begin
108
+ executable = cond.nil? || cond.call(ActiveRecord::Base.connection)
109
+ rescue => e
110
+ errmsg = "[WARN] `#{sql}` is not executed: #{e.message}"
111
+
112
+ if @options[:debug]
113
+ errmsg = ([errmsg] + e.backtrace).join("\n\tfrom ")
114
+ end
115
+
116
+ Ridgepole::Logger.instance.warn(errmsg)
117
+
118
+ executable = false
119
+ end
120
+
121
+ next unless executable
122
+
123
+ if options[:noop]
124
+ out.puts(sql.strip_heredoc)
125
+ else
126
+ @logger.info(sql.strip_heredoc)
127
+ ActiveRecord::Base.connection.execute(sql)
128
+ end
129
+
130
+ execute_count += 1
131
+ end
132
+
133
+ return execute_count
82
134
  end
83
135
 
84
136
  def with_pre_post_query(options = {})
@@ -324,4 +376,8 @@ remove_index(#{table_name.inspect}, #{target.inspect})
324
376
  EOS
325
377
  end
326
378
  end
379
+
380
+ def delta_execute
381
+ @delta[:execute] || []
382
+ end
327
383
  end
@@ -3,9 +3,9 @@ class Ridgepole::Diff
3
3
  @options = options
4
4
  end
5
5
 
6
- def diff(from, to)
7
- from = (from || {}).dup
8
- to = (to || {}).dup
6
+ def diff(from, to, options = {})
7
+ from = (from || {}).deep_dup
8
+ to = (to || {}).deep_dup
9
9
 
10
10
  if @options[:reverse]
11
11
  from, to = to, from
@@ -36,6 +36,8 @@ class Ridgepole::Diff
36
36
  end
37
37
  end
38
38
 
39
+ delta[:execute] = options[:execute]
40
+
39
41
  Ridgepole::Delta.new(delta, @options)
40
42
  end
41
43
 
@@ -40,26 +40,28 @@ class Ridgepole::DSLParser
40
40
 
41
41
  def timestamps(*args)
42
42
  options = {:null => false}.merge(args.extract_options!)
43
- column(:created_at, :datetime, options.dup)
44
- column(:updated_at, :datetime, options.dup)
43
+ column(:created_at, :datetime, options)
44
+ column(:updated_at, :datetime, options)
45
45
  end
46
46
 
47
47
  def references(*args)
48
48
  options = args.extract_options!
49
49
  polymorphic = options.delete(:polymorphic)
50
50
  args.each do |col|
51
- column("#{col}_id", :integer, options.dup)
52
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options.dup) unless polymorphic.nil?
51
+ column("#{col}_id", :integer, options)
52
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
53
53
  end
54
54
  end
55
55
  alias :belongs_to :references
56
56
  end
57
57
 
58
58
  attr_reader :__definition
59
+ attr_reader :__execute
59
60
 
60
61
  def initialize(opts = {})
61
62
  @__working_dir = File.expand_path(opts[:path] ? File.dirname(opts[:path]) : Dir.pwd)
62
63
  @__definition = {}
64
+ @__execute = []
63
65
  end
64
66
 
65
67
  def self.eval(dsl, opts = {})
@@ -71,7 +73,7 @@ class Ridgepole::DSLParser
71
73
  ctx.instance_eval(dsl)
72
74
  end
73
75
 
74
- ctx.__definition
76
+ [ctx.__definition, ctx.__execute]
75
77
  end
76
78
 
77
79
  def create_table(table_name, options = {})
@@ -123,6 +125,13 @@ class Ridgepole::DSLParser
123
125
  Kernel.require(file)
124
126
  end
125
127
  end
128
+
129
+ def execute(sql, name = nil, &cond)
130
+ @__execute << {
131
+ :sql => sql,
132
+ :condition => cond,
133
+ }
134
+ end
126
135
  end
127
136
 
128
137
  def initialize(options = {})
@@ -130,15 +139,15 @@ class Ridgepole::DSLParser
130
139
  end
131
140
 
132
141
  def parse(dsl, opts = {})
133
- parsed = Context.eval(dsl, opts)
134
- check_orphan_index(parsed)
135
- parsed
142
+ definition, execute = Context.eval(dsl, opts)
143
+ check_orphan_index(definition)
144
+ [definition, execute]
136
145
  end
137
146
 
138
147
  private
139
148
 
140
- def check_orphan_index(parsed)
141
- parsed.each do |table_name, attrs|
149
+ def check_orphan_index(definition)
150
+ definition.each do |table_name, attrs|
142
151
  if attrs.length == 1 and attrs[:indices]
143
152
  raise "Table `#{table_name}` to create the index is not defined: #{attrs[:indices].keys.join(',')}"
144
153
  end
@@ -1,3 +1,3 @@
1
1
  module Ridgepole
2
- VERSION = '0.4.7'
2
+ VERSION = '0.4.8.rc1'
3
3
  end
@@ -110,8 +110,8 @@ describe 'ridgepole' do
110
110
  Ridgepole::Client#initialize([{"adapter"=>"mysql2", "database"=>"ridgepole_test"}, {:dry_run=>false, :debug=>false}])
111
111
  Apply `Schemafile`
112
112
  Ridgepole::Client#diff
113
- Ridgepole::Delta#migrate
114
113
  Ridgepole::Delta#differ?
114
+ Ridgepole::Delta#migrate
115
115
  No change
116
116
  EOS
117
117
  end
@@ -131,8 +131,8 @@ describe 'ridgepole' do
131
131
  Ridgepole::Client#initialize([{"adapter"=>"mysql2", "database"=>"ridgepole_test_for_conf_file"}, {:dry_run=>false, :debug=>true}])
132
132
  Apply `Schemafile`
133
133
  Ridgepole::Client#diff
134
- Ridgepole::Delta#migrate
135
134
  Ridgepole::Delta#differ?
135
+ Ridgepole::Delta#migrate
136
136
  No change
137
137
  EOS
138
138
  end
@@ -157,8 +157,8 @@ describe 'ridgepole' do
157
157
  Ridgepole::Client#initialize([{"adapter"=>"mysql2", "database"=>"ridgepole_development"}, {:dry_run=>false, :debug=>true}])
158
158
  Apply `Schemafile`
159
159
  Ridgepole::Client#diff
160
- Ridgepole::Delta#migrate
161
160
  Ridgepole::Delta#differ?
161
+ Ridgepole::Delta#migrate
162
162
  No change
163
163
  EOS
164
164
  end
@@ -173,7 +173,6 @@ describe 'ridgepole' do
173
173
  Apply `Schemafile` (dry-run)
174
174
  Ridgepole::Client#diff
175
175
  Ridgepole::Delta#differ?
176
- Ridgepole::Delta#differ?
177
176
  No change
178
177
  EOS
179
178
  end
@@ -189,8 +188,8 @@ describe 'ridgepole' do
189
188
  Ridgepole::Client#initialize([{"adapter"=>"mysql2", "database"=>"ridgepole_test"}, {:dry_run=>false, :debug=>false}])
190
189
  Apply `Schemafile`
191
190
  Ridgepole::Client#diff
192
- Ridgepole::Delta#migrate
193
191
  Ridgepole::Delta#differ?
192
+ Ridgepole::Delta#migrate
194
193
  EOS
195
194
  end
196
195
 
@@ -204,15 +203,13 @@ describe 'ridgepole' do
204
203
  Ridgepole::Client#diff
205
204
  Ridgepole::Delta#differ?
206
205
  Ridgepole::Delta#script
206
+ Ridgepole::Delta#script
207
207
  create_table :table do
208
208
  end
209
209
 
210
210
  Ridgepole::Delta#migrate
211
211
  # create_table :table do
212
-
213
212
  # end
214
-
215
- Ridgepole::Delta#differ?
216
213
  EOS
217
214
  end
218
215
  end
@@ -244,12 +241,12 @@ describe 'ridgepole' do
244
241
  Ridgepole::Client.diff([{"adapter"=>"mysql2", "database"=>"ridgepole_test"}, {"adapter"=>"mysql2", "database"=>"ridgepole_test"}, {:dry_run=>false, :debug=>false}])
245
242
  Ridgepole::Delta#differ?
246
243
  Ridgepole::Delta#script
244
+ Ridgepole::Delta#script
247
245
  create_table :table do
248
246
  end
249
247
 
250
248
  Ridgepole::Delta#migrate
251
249
  # create_table :table do
252
-
253
250
  # end
254
251
  EOS
255
252
  end
@@ -30,7 +30,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
30
30
  delta = subject.diff(expected_dsl)
31
31
  expect(delta.differ?).to be_truthy
32
32
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
33
- sql = delta.migrate(:noop => true)
33
+ migrated, sql = delta.migrate(:noop => true)
34
+ expect(migrated).to be_truthy
34
35
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
35
36
 
36
37
  sql = sql.each_line.map {|i| i.strip }.join("\n")
@@ -69,7 +70,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
69
70
  delta = subject.diff(expected_dsl)
70
71
  expect(delta.differ?).to be_truthy
71
72
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
72
- sql = delta.migrate(:noop => true)
73
+ migrated, sql = delta.migrate(:noop => true)
74
+ expect(migrated).to be_truthy
73
75
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
74
76
 
75
77
  sql = sql.each_line.map {|i| i.strip }.join("\n")
@@ -108,7 +110,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
108
110
  delta = subject.diff(expected_dsl)
109
111
  expect(delta.differ?).to be_truthy
110
112
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
111
- sql = delta.migrate(:noop => true)
113
+ migrated, sql = delta.migrate(:noop => true)
114
+ expect(migrated).to be_truthy
112
115
  expect(subject.dump).to eq actual_dsl.strip_heredoc.strip
113
116
 
114
117
  sql = sql.each_line.map {|i| i.strip }.join("\n")
@@ -0,0 +1,285 @@
1
+ describe 'Ridgepole::Client#diff -> migrate' do
2
+ context 'when execute' do
3
+ let(:dsl) {
4
+ <<-RUBY
5
+ create_table "authors", force: true do |t|
6
+ t.string "name", null: false
7
+ end
8
+
9
+ create_table "books", force: true do |t|
10
+ t.string "title", null: false
11
+ t.integer "author_id", unsigned: true, null: false
12
+ end
13
+
14
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
15
+ RUBY
16
+ }
17
+
18
+ let(:dsl_with_execute) {
19
+ <<-RUBY
20
+ create_table "authors", force: true do |t|
21
+ t.string "name", null: false
22
+ end
23
+
24
+ create_table "books", force: true do |t|
25
+ t.string "title", null: false
26
+ t.integer "author_id", unsigned: true, null: false
27
+ end
28
+
29
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
30
+
31
+ execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
32
+ c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = '#{TEST_SCHEMA}' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
33
+ end
34
+ RUBY
35
+ }
36
+
37
+ before { subject.diff(dsl).migrate }
38
+ subject { client }
39
+
40
+ it {
41
+ delta = subject.diff(dsl_with_execute)
42
+ expect(delta.differ?).to be_truthy
43
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
44
+
45
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
46
+ CREATE TABLE `books` (
47
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
48
+ `title` varchar(255) NOT NULL,
49
+ `author_id` int(10) unsigned NOT NULL,
50
+ PRIMARY KEY (`id`),
51
+ KEY `idx_author_id` (`author_id`) USING BTREE
52
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
53
+ SQL
54
+
55
+ delta.migrate
56
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
57
+
58
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
59
+ CREATE TABLE `books` (
60
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
61
+ `title` varchar(255) NOT NULL,
62
+ `author_id` int(10) unsigned NOT NULL,
63
+ PRIMARY KEY (`id`),
64
+ KEY `idx_author_id` (`author_id`) USING BTREE,
65
+ CONSTRAINT `fk_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)
66
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
67
+ SQL
68
+ }
69
+ end
70
+
71
+ context 'when not execute' do
72
+ let(:dsl) {
73
+ <<-RUBY
74
+ create_table "authors", force: true do |t|
75
+ t.string "name", null: false
76
+ end
77
+
78
+ create_table "books", force: true do |t|
79
+ t.string "title", null: false
80
+ t.integer "author_id", unsigned: true, null: false
81
+ end
82
+
83
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
84
+ RUBY
85
+ }
86
+
87
+ let(:dsl_with_execute) {
88
+ <<-RUBY
89
+ create_table "authors", force: true do |t|
90
+ t.string "name", null: false
91
+ end
92
+
93
+ create_table "books", force: true do |t|
94
+ t.string "title", null: false
95
+ t.integer "author_id", unsigned: true, null: false
96
+ end
97
+
98
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
99
+
100
+ execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
101
+ c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = '#{TEST_SCHEMA}' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
102
+ end
103
+ RUBY
104
+ }
105
+
106
+ before { subject.diff(dsl_with_execute).migrate }
107
+ subject { client }
108
+
109
+ it {
110
+ delta = subject.diff(dsl_with_execute)
111
+ expect(delta.differ?).to be_truthy
112
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
113
+
114
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
115
+ CREATE TABLE `books` (
116
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
117
+ `title` varchar(255) NOT NULL,
118
+ `author_id` int(10) unsigned NOT NULL,
119
+ PRIMARY KEY (`id`),
120
+ KEY `idx_author_id` (`author_id`) USING BTREE,
121
+ CONSTRAINT `fk_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)
122
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
123
+ SQL
124
+
125
+ migrated, out = delta.migrate
126
+ expect(migrated).to be_falsey
127
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
128
+
129
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
130
+ CREATE TABLE `books` (
131
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
132
+ `title` varchar(255) NOT NULL,
133
+ `author_id` int(10) unsigned NOT NULL,
134
+ PRIMARY KEY (`id`),
135
+ KEY `idx_author_id` (`author_id`) USING BTREE,
136
+ CONSTRAINT `fk_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)
137
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
138
+ SQL
139
+ }
140
+ end
141
+
142
+ context 'when execute (noop)' do
143
+ let(:dsl) {
144
+ <<-RUBY
145
+ create_table "authors", force: true do |t|
146
+ t.string "name", null: false
147
+ end
148
+
149
+ create_table "books", force: true do |t|
150
+ t.string "title", null: false
151
+ t.integer "author_id", unsigned: true, null: false
152
+ end
153
+
154
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
155
+ RUBY
156
+ }
157
+
158
+ let(:dsl_with_execute) {
159
+ <<-RUBY
160
+ create_table "authors", force: true do |t|
161
+ t.string "name", null: false
162
+ end
163
+
164
+ create_table "books", force: true do |t|
165
+ t.string "title", null: false
166
+ t.integer "author_id", unsigned: true, null: false
167
+ end
168
+
169
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
170
+
171
+ execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
172
+ c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = '#{TEST_SCHEMA}' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
173
+ end
174
+ RUBY
175
+ }
176
+
177
+ before { subject.diff(dsl).migrate }
178
+ subject { client }
179
+
180
+ it {
181
+ delta = subject.diff(dsl_with_execute)
182
+ expect(delta.differ?).to be_truthy
183
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
184
+
185
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
186
+ CREATE TABLE `books` (
187
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
188
+ `title` varchar(255) NOT NULL,
189
+ `author_id` int(10) unsigned NOT NULL,
190
+ PRIMARY KEY (`id`),
191
+ KEY `idx_author_id` (`author_id`) USING BTREE
192
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
193
+ SQL
194
+
195
+ migrated, sql = delta.migrate(:noop => true)
196
+ expect(migrated).to be_truthy
197
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
198
+
199
+ expect(sql.strip).to eq "ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)"
200
+
201
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
202
+ CREATE TABLE `books` (
203
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
204
+ `title` varchar(255) NOT NULL,
205
+ `author_id` int(10) unsigned NOT NULL,
206
+ PRIMARY KEY (`id`),
207
+ KEY `idx_author_id` (`author_id`) USING BTREE
208
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
209
+ SQL
210
+ }
211
+ end
212
+
213
+ context 'when not execute (noop)' do
214
+ let(:dsl) {
215
+ <<-RUBY
216
+ create_table "authors", force: true do |t|
217
+ t.string "name", null: false
218
+ end
219
+
220
+ create_table "books", force: true do |t|
221
+ t.string "title", null: false
222
+ t.integer "author_id", unsigned: true, null: false
223
+ end
224
+
225
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
226
+ RUBY
227
+ }
228
+
229
+ let(:dsl_with_execute) {
230
+ <<-RUBY
231
+ create_table "authors", force: true do |t|
232
+ t.string "name", null: false
233
+ end
234
+
235
+ create_table "books", force: true do |t|
236
+ t.string "title", null: false
237
+ t.integer "author_id", unsigned: true, null: false
238
+ end
239
+
240
+ add_index "books", ["author_id"], name: "idx_author_id", using: :btree
241
+
242
+ execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
243
+ c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = '#{TEST_SCHEMA}' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
244
+ end
245
+ RUBY
246
+ }
247
+
248
+ before { subject.diff(dsl_with_execute).migrate }
249
+ subject { client }
250
+
251
+ it {
252
+ delta = subject.diff(dsl_with_execute)
253
+ expect(delta.differ?).to be_truthy
254
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
255
+
256
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
257
+ CREATE TABLE `books` (
258
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
259
+ `title` varchar(255) NOT NULL,
260
+ `author_id` int(10) unsigned NOT NULL,
261
+ PRIMARY KEY (`id`),
262
+ KEY `idx_author_id` (`author_id`) USING BTREE,
263
+ CONSTRAINT `fk_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)
264
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
265
+ SQL
266
+
267
+ migrated, sql = delta.migrate(:noop => true)
268
+ expect(migrated).to be_falsey
269
+ expect(subject.dump.delete_empty_lines).to eq dsl.strip_heredoc.strip.delete_empty_lines
270
+
271
+ expect(sql.strip).to eq ""
272
+
273
+ expect(show_create_table(:books).strip).to eq <<-SQL.strip_heredoc.strip
274
+ CREATE TABLE `books` (
275
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
276
+ `title` varchar(255) NOT NULL,
277
+ `author_id` int(10) unsigned NOT NULL,
278
+ PRIMARY KEY (`id`),
279
+ KEY `idx_author_id` (`author_id`) USING BTREE,
280
+ CONSTRAINT `fk_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)
281
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
282
+ SQL
283
+ }
284
+ end
285
+ end
@@ -75,7 +75,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
75
75
  it {
76
76
  delta = subject.diff(expected_dsl)
77
77
  expect(delta.differ?).to be_truthy
78
- sql = delta.migrate(:noop => true)
78
+ migrated, sql = delta.migrate(:noop => true)
79
+ expect(migrated).to be_truthy
79
80
  expect(subject.dump).to eq actual_dsl
80
81
 
81
82
  sql = sql.each_line.map {|i| i.strip }.join("\n")
@@ -104,7 +105,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
104
105
  it {
105
106
  delta = client(:bulk_change => true).diff(expected_dsl)
106
107
  expect(delta.differ?).to be_truthy
107
- sql = delta.migrate(:noop => true)
108
+ migrated, sql = delta.migrate(:noop => true)
109
+ expect(migrated).to be_truthy
108
110
  expect(subject.dump).to eq actual_dsl
109
111
 
110
112
  sql = sql.each_line.map {|i| i.strip }.join("\n")
@@ -205,7 +207,8 @@ describe 'Ridgepole::Client#diff -> migrate' do
205
207
  it {
206
208
  delta = subject.diff(expected_dsl)
207
209
  expect(delta.differ?).to be_truthy
208
- sql = delta.migrate(:noop => true)
210
+ migrated, sql = delta.migrate(:noop => true)
211
+ expect(migrated).to be_truthy
209
212
  expect(subject.dump).to eq actual_dsl
210
213
 
211
214
  sql = sql.each_line.map {|i| i.strip }.join("\n")
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,8 @@ require 'open3'
7
7
  require 'tempfile'
8
8
  require 'json'
9
9
 
10
+ TEST_SCHEMA = 'ridgepole_test'
11
+
10
12
  ActiveRecord::Migration.verbose = false
11
13
  Ridgepole::Logger.instance.level = ::Logger::ERROR
12
14
 
@@ -38,10 +40,15 @@ end
38
40
  def conn_spec(config = {})
39
41
  {
40
42
  adapter: 'mysql2',
41
- database: 'ridgepole_test',
43
+ database: TEST_SCHEMA,
42
44
  }.merge(config)
43
45
  end
44
46
 
47
+ def show_create_table(table_name)
48
+ raw_conn = ActiveRecord::Base.connection.raw_connection
49
+ raw_conn.query("SHOW CREATE TABLE `#{table_name}`").first[1]
50
+ end
51
+
45
52
  def default_cli_hook
46
53
  <<-RUBY.strip_heredoc
47
54
  require 'ridgepole'
@@ -51,7 +58,7 @@ def default_cli_hook
51
58
  end
52
59
  def migrate(*args)
53
60
  puts "Ridgepole::Delta#migrate"
54
- "create_table :table do\\nend"
61
+ [#{differ}, "create_table :table do\\nend"]
55
62
  end
56
63
  def script
57
64
  puts "Ridgepole::Delta#script"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ridgepole
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.8.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Genki Sugawara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-14 00:00:00.000000000 Z
11
+ date: 2014-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -153,6 +153,7 @@ files:
153
153
  - spec/migrate/migrate_duplicate_index_spec.rb
154
154
  - spec/migrate/migrate_duplicate_table_spec.rb
155
155
  - spec/migrate/migrate_empty_spec.rb
156
+ - spec/migrate/migrate_execute_spec.rb
156
157
  - spec/migrate/migrate_merge_mode_spec.rb
157
158
  - spec/migrate/migrate_noop_spec.rb
158
159
  - spec/migrate/migrate_rename_column_spec.rb
@@ -185,12 +186,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
185
186
  version: '0'
186
187
  required_rubygems_version: !ruby/object:Gem::Requirement
187
188
  requirements:
188
- - - '>='
189
+ - - '>'
189
190
  - !ruby/object:Gem::Version
190
- version: '0'
191
+ version: 1.3.1
191
192
  requirements: []
192
193
  rubyforge_project:
193
- rubygems_version: 2.4.1
194
+ rubygems_version: 2.0.14
194
195
  signing_key:
195
196
  specification_version: 4
196
197
  summary: Ridgepole is a tool to manage DB schema.
@@ -224,6 +225,7 @@ test_files:
224
225
  - spec/migrate/migrate_duplicate_index_spec.rb
225
226
  - spec/migrate/migrate_duplicate_table_spec.rb
226
227
  - spec/migrate/migrate_empty_spec.rb
228
+ - spec/migrate/migrate_execute_spec.rb
227
229
  - spec/migrate/migrate_merge_mode_spec.rb
228
230
  - spec/migrate/migrate_noop_spec.rb
229
231
  - spec/migrate/migrate_rename_column_spec.rb