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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7a10d9b0607e25c59ff80c62aad7a6994d3b84d
4
- data.tar.gz: 56933fbf7941579d0e83d56a10a8703a555baff6
3
+ metadata.gz: 540a3e7012616c467934600e8bf44d720cf33f77
4
+ data.tar.gz: eaebf6d806fb23a6d6cd82b6af8415d64286d0d1
5
5
  SHA512:
6
- metadata.gz: f353e4f59e6188b7851eb14a90ee7ece7484f994f6a80258c1453e1ef0aeb74edf11d5257a71d4330aa8cebc98c5cf946f5238f3b803010deedefcdf44f76ffd
7
- data.tar.gz: 70a90fa4e5a3acb0b23746871f6bec0a4adcffbe4b7d50891264f9aaf9657c0eccc23dc0868753e506a44805559037c4f7db560bd3be46ef14abaca336e8039a
6
+ metadata.gz: b3f0ffa78c0fb82ff822e02230c9d2997823bc5090dd2e21e41f918b1436793f1f731852237843dcae2bb13ea93a48f8693127207446bc32962f90b0a069814f
7
+ data.tar.gz: 7e4cc137e218c8028a03b8b4c222b477dc037cc4cd99dea39e98721778354ddafa959010354ee8fc14116939b8aaa7306abde4667c4f056ee60170ab05854072
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ tmp
18
18
  .DS_Store
19
19
  test.rb
20
20
  Schemafile
21
+ *.schema
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --require spec_helper
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
@@ -1 +1,5 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
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') {|v| config = v }
24
- opt.on('-a', '--apply') { mode = :apply }
25
- opt.on('-f', '--file FILE') {|v| file = v }
26
- opt.on('', '--dry-run') { options[:dry_run] = true }
27
- opt.on('-e', '--export') { mode = :export }
28
- opt.on('-o', '--output FILE') {|v| output_file = v }
29
- opt.on('' , '--debug') { options[:debug] = true }
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 output_file == '-'
53
- logger.info('# Export Schema')
54
- puts client.dump
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
- logger.info("Export Schema to `#{output_file}`")
57
- open(output_file, 'wb') {|f| f.puts client.dump }
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 = "Apply `#{file}`"
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?
@@ -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
@@ -9,7 +9,9 @@ class Ridgepole::Diff
9
9
  delta = {}
10
10
 
11
11
  to.dup.each do |table_name, to_attrs|
12
- if (from_table_name = (to_attrs[:options] || {}).delete(:from))
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
- from.each do |table_name, from_attrs|
31
- delta[:delete] ||= {}
32
- delta[:delete][table_name] = from_attrs
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(:from))
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
- from.each do |column_name, from_attrs|
99
- definition_delta[:delete] ||= {}
100
- definition_delta[:delete][column_name] = from_attrs
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
- indices_delta[:delete][index_name] = from_attrs
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
- from.each do |index_name, from_attrs|
128
- indices_delta[:delete] ||= {}
129
- indices_delta[:delete][index_name] = from_attrs
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
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Ridgepole
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
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', '>= 2.14.1'
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