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.
@@ -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