scheman 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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +2 -0
- data/lib/scheman/diff.rb +143 -0
- data/lib/scheman/errors.rb +12 -0
- data/lib/scheman/parser_builder.rb +29 -0
- data/lib/scheman/parsers/base.rb +9 -0
- data/lib/scheman/parsers/mysql.rb +599 -0
- data/lib/scheman/schema.rb +75 -0
- data/lib/scheman/version.rb +3 -0
- data/lib/scheman/views/base.rb +6 -0
- data/lib/scheman/views/mysql.rb +267 -0
- data/lib/scheman.rb +17 -0
- data/scheman.gemspec +25 -0
- data/spec/scheman/diff_spec.rb +76 -0
- data/spec/scheman/parsers/mysql_spec.rb +390 -0
- data/spec/spec_helper.rb +11 -0
- metadata +151 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
module Scheman
|
2
|
+
module Views
|
3
|
+
class Mysql < Base
|
4
|
+
def self.transform
|
5
|
+
@transform ||= Transform.new
|
6
|
+
end
|
7
|
+
|
8
|
+
# @param diff [Hash]
|
9
|
+
def initialize(diff)
|
10
|
+
@diff = diff
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def to_s
|
15
|
+
self.class.transform.apply(
|
16
|
+
root: @diff
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
class Transform < Parslet::Transform
|
21
|
+
rule(root: subtree(:root)) do
|
22
|
+
[
|
23
|
+
"BEGIN;",
|
24
|
+
"SET foreign_key_checks=0;",
|
25
|
+
root,
|
26
|
+
"SET foreign_key_checks=1;",
|
27
|
+
"COMMIT;",
|
28
|
+
].join("\n\n") + "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
rule(
|
32
|
+
alter_tables: sequence(:alter_tables),
|
33
|
+
create_tables: sequence(:create_tables),
|
34
|
+
drop_tables: sequence(:drop_tables),
|
35
|
+
) do
|
36
|
+
[
|
37
|
+
CreateTables.new(create_tables).to_s.presence,
|
38
|
+
AlterTables.new(alter_tables).to_s.presence,
|
39
|
+
DropTables.new(drop_tables).to_s.presence,
|
40
|
+
].compact.join("\n\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
rule(drop_table: subtree(:drop_table)) do
|
44
|
+
DropTable.new(drop_table)
|
45
|
+
end
|
46
|
+
|
47
|
+
rule(create_table: subtree(:create_table)) do
|
48
|
+
CreateTable.new(create_table)
|
49
|
+
end
|
50
|
+
|
51
|
+
rule(field: subtree(:field)) do
|
52
|
+
Field.new(field)
|
53
|
+
end
|
54
|
+
|
55
|
+
rule(qualifier: subtree(:qualifier)) do
|
56
|
+
Qualifier.new(qualifier)
|
57
|
+
end
|
58
|
+
|
59
|
+
rule(index: subtree(:index)) do
|
60
|
+
Index.new(index)
|
61
|
+
end
|
62
|
+
|
63
|
+
rule(add_field: subtree(:add_field)) do
|
64
|
+
AddField.new(add_field)
|
65
|
+
end
|
66
|
+
|
67
|
+
rule(drop_field: subtree(:drop_field)) do
|
68
|
+
DropField.new(drop_field)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Node
|
73
|
+
def initialize(element)
|
74
|
+
@element = element
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Statements < Node
|
79
|
+
def to_s
|
80
|
+
@element.join("\n\n")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class AlterTables < Statements
|
85
|
+
end
|
86
|
+
|
87
|
+
class DropTables < Statements
|
88
|
+
end
|
89
|
+
|
90
|
+
class CreateTables < Statements
|
91
|
+
end
|
92
|
+
|
93
|
+
class DropTable < Node
|
94
|
+
def to_s
|
95
|
+
"DROP TABLE `#{table_name}`;"
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def table_name
|
101
|
+
@element[:name]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class AlterTable < Node
|
106
|
+
def to_s
|
107
|
+
"TODO"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class AlterField < Node
|
112
|
+
private
|
113
|
+
|
114
|
+
def table_name
|
115
|
+
@element[:table_name]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class AddField < AlterField
|
120
|
+
def to_s
|
121
|
+
"ALTER TABLE `#{table_name}` ADD COLUMN #{field};"
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def field
|
127
|
+
Field.new(@element)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class DropField < AlterField
|
132
|
+
def to_s
|
133
|
+
"ALTER TABLE `#{table_name}` DROP COLUMN `#{field_name}`;"
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def field_name
|
139
|
+
@element[:name]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class CreateTable < Node
|
144
|
+
def to_s
|
145
|
+
str = ""
|
146
|
+
str << "CREATE TABLE `#{table_name}` (\n"
|
147
|
+
str << definitions.join(",\n").indent(2) + "\n"
|
148
|
+
str << ");"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def table_name
|
154
|
+
@element[:name]
|
155
|
+
end
|
156
|
+
|
157
|
+
def definitions
|
158
|
+
@element[:fields] + @element[:indices]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Field < Node
|
163
|
+
def to_s
|
164
|
+
str = "`#{name}` #{type}"
|
165
|
+
str << "(#{values})" if has_values?
|
166
|
+
str << " #{qualifiers}" if has_qualifiers?
|
167
|
+
str
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
# @example
|
173
|
+
# "id"
|
174
|
+
def name
|
175
|
+
@element[:name]
|
176
|
+
end
|
177
|
+
|
178
|
+
# @example
|
179
|
+
# "INTEGER"
|
180
|
+
def type
|
181
|
+
@element[:type].upcase
|
182
|
+
end
|
183
|
+
|
184
|
+
def qualifiers
|
185
|
+
@element[:qualifiers].map(&:to_s).join(" ")
|
186
|
+
end
|
187
|
+
|
188
|
+
def values
|
189
|
+
@element[:values].join(", ")
|
190
|
+
end
|
191
|
+
|
192
|
+
def has_qualifiers?
|
193
|
+
!@element[:qualifiers].empty?
|
194
|
+
end
|
195
|
+
|
196
|
+
def has_values?
|
197
|
+
!@element[:values].empty?
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Qualifier < Node
|
202
|
+
def to_s
|
203
|
+
str = type
|
204
|
+
str << " #{value}" if has_value?
|
205
|
+
str
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
# @example
|
211
|
+
# "NOT NULL"
|
212
|
+
def type
|
213
|
+
@element[:type].upcase.gsub("_", " ")
|
214
|
+
end
|
215
|
+
|
216
|
+
# @example
|
217
|
+
# "utf8"
|
218
|
+
def value
|
219
|
+
@element[:value]
|
220
|
+
end
|
221
|
+
|
222
|
+
def has_value?
|
223
|
+
@element[:value]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class Index < Node
|
228
|
+
def to_s
|
229
|
+
"#{prefix} (`#{column}`)"
|
230
|
+
end
|
231
|
+
|
232
|
+
def primary_key?
|
233
|
+
!!@element[:primary]
|
234
|
+
end
|
235
|
+
|
236
|
+
def fulltext?
|
237
|
+
@element[:type] == "fulltext"
|
238
|
+
end
|
239
|
+
|
240
|
+
def spatial?
|
241
|
+
@element[:type] == "spatial"
|
242
|
+
end
|
243
|
+
|
244
|
+
# @example
|
245
|
+
# "id"
|
246
|
+
def column
|
247
|
+
@element[:column]
|
248
|
+
end
|
249
|
+
|
250
|
+
# @example
|
251
|
+
# "PRIMARY KEY"
|
252
|
+
def prefix
|
253
|
+
case
|
254
|
+
when primary_key?
|
255
|
+
"PRIMARY KEY"
|
256
|
+
when fulltext?
|
257
|
+
"FULLTEXT"
|
258
|
+
when spatial?
|
259
|
+
"SPATIAL"
|
260
|
+
else
|
261
|
+
"KEY"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
data/lib/scheman.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "active_support/core_ext/array/wrap"
|
2
|
+
require "active_support/core_ext/enumerable"
|
3
|
+
require "active_support/core_ext/hash/slice"
|
4
|
+
require "active_support/core_ext/object/blank"
|
5
|
+
require "active_support/core_ext/object/try"
|
6
|
+
require "active_support/core_ext/string/indent"
|
7
|
+
require "parslet"
|
8
|
+
|
9
|
+
require "scheman/diff"
|
10
|
+
require "scheman/errors"
|
11
|
+
require "scheman/parser_builder"
|
12
|
+
require "scheman/parsers/base"
|
13
|
+
require "scheman/parsers/mysql"
|
14
|
+
require "scheman/schema"
|
15
|
+
require "scheman/version"
|
16
|
+
require "scheman/views/base"
|
17
|
+
require "scheman/views/mysql"
|
data/scheman.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "scheman/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "scheman"
|
7
|
+
spec.version = Scheman::VERSION
|
8
|
+
spec.authors = ["Ryo Nakamura"]
|
9
|
+
spec.email = ["r7kamura@gmail.com"]
|
10
|
+
spec.summary = "Manage database schema based on schema definition file."
|
11
|
+
spec.homepage = "https://github.com/r7kamura/scheman"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_dependency "activesupport"
|
20
|
+
spec.add_dependency "parslet"
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "pry"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "rspec", "2.14.1"
|
25
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Scheman::Diff do
|
4
|
+
let(:instance) do
|
5
|
+
described_class.new(args)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:args) do
|
9
|
+
{
|
10
|
+
before: before_schema,
|
11
|
+
after: after_schema,
|
12
|
+
type: type,
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:type) do
|
17
|
+
"mysql"
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:before_schema) do
|
21
|
+
<<-EOS.strip_heredoc
|
22
|
+
CREATE TABLE `table1` (
|
23
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
24
|
+
PRIMARY KEY (`column1`)
|
25
|
+
);
|
26
|
+
|
27
|
+
CREATE TABLE `table2` (
|
28
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
29
|
+
PRIMARY KEY (`column1`)
|
30
|
+
);
|
31
|
+
EOS
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:after_schema) do
|
35
|
+
<<-EOS.strip_heredoc
|
36
|
+
CREATE TABLE `table1` (
|
37
|
+
`column2` VARCHAR(255) NOT NULL,
|
38
|
+
PRIMARY KEY (`column1`)
|
39
|
+
);
|
40
|
+
|
41
|
+
CREATE TABLE `table3` (
|
42
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
43
|
+
PRIMARY KEY (`column1`)
|
44
|
+
);
|
45
|
+
EOS
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#to_s" do
|
49
|
+
subject do
|
50
|
+
instance.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns a diff in SQL" do
|
54
|
+
should == <<-EOS.strip_heredoc
|
55
|
+
BEGIN;
|
56
|
+
|
57
|
+
SET foreign_key_checks=0;
|
58
|
+
|
59
|
+
CREATE TABLE `table3` (
|
60
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
61
|
+
PRIMARY KEY (`column1`)
|
62
|
+
);
|
63
|
+
|
64
|
+
ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL;
|
65
|
+
|
66
|
+
ALTER TABLE `table1` DROP COLUMN `column1`;
|
67
|
+
|
68
|
+
DROP TABLE `table2`;
|
69
|
+
|
70
|
+
SET foreign_key_checks=1;
|
71
|
+
|
72
|
+
COMMIT;
|
73
|
+
EOS
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|