scheman 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7c96927177dfbde154defcf9c902c953c60a580a
|
4
|
+
data.tar.gz: 51e98f13cdf9ea43eec4a515be4b883ab855b880
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 09fb8220340760be61f9a6ad057313604657dc4425fb97bc0ac10bf41c05295e7800da6a2ae0bfb8857dc407393a3edd5c6892b5af81ffb783a8c69acdf14ffe
|
7
|
+
data.tar.gz: a1a90f9957eec8d37e83a367d5d3417016a1032143702c3e6c26834cbf3b678797f289b855c757faf05d0fc14923a986ff6606ea7c5b262454e7e686faee5d3b
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ryo Nakamura
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Scheman
|
2
|
+
Manage database schema based on schema definition file.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
Creates Diff with 2 schema files and logs out their diff.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
before = <<-SQL
|
9
|
+
CREATE TABLE `table1` (
|
10
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
11
|
+
PRIMARY KEY (`column1`)
|
12
|
+
);
|
13
|
+
|
14
|
+
CREATE TABLE `table2` (
|
15
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
16
|
+
PRIMARY KEY (`column1`)
|
17
|
+
);
|
18
|
+
SQL
|
19
|
+
|
20
|
+
after = <<-SQL
|
21
|
+
CREATE TABLE `table1` (
|
22
|
+
`column2` VARCHAR(255) NOT NULL,
|
23
|
+
PRIMARY KEY (`column1`)
|
24
|
+
);
|
25
|
+
|
26
|
+
CREATE TABLE `table3` (
|
27
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
28
|
+
PRIMARY KEY (`column1`)
|
29
|
+
);
|
30
|
+
SQL
|
31
|
+
|
32
|
+
puts Scheman::Diff.new(before: before, after: after, type: "mysql")
|
33
|
+
```
|
34
|
+
|
35
|
+
The result would be the following:
|
36
|
+
|
37
|
+
```sql
|
38
|
+
BEGIN;
|
39
|
+
|
40
|
+
SET foreign_key_checks=0;
|
41
|
+
|
42
|
+
CREATE TABLE `table3` (
|
43
|
+
`column1` INTEGER NOT NULL AUTO INCREMENT,
|
44
|
+
PRIMARY KEY (`column1`)
|
45
|
+
);
|
46
|
+
|
47
|
+
ALTER TABLE `table1` ADD COLUMN `column2` VARCHAR(255) NOT NULL;
|
48
|
+
|
49
|
+
ALTER TABLE `table1` DROP COLUMN `column1`;
|
50
|
+
|
51
|
+
DROP TABLE `table2`;
|
52
|
+
|
53
|
+
SET foreign_key_checks=1;
|
54
|
+
|
55
|
+
COMMIT;
|
56
|
+
```
|
data/Rakefile
ADDED
data/lib/scheman/diff.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Scheman
|
2
|
+
class Diff
|
3
|
+
# @param before [String] The previous schema
|
4
|
+
# @param after [String] The next schema
|
5
|
+
# @param type [String] The type of schema syntax, default types of the both schemata (e.g. "mysql")
|
6
|
+
# @param before_type [String] Specify the type of the previous schema if needed
|
7
|
+
# @param after_type [String] Specify the type of the next schema if needed
|
8
|
+
# @param output_type [String] Specify the type of the output schema if needed
|
9
|
+
# @raise [Scheman::Errors::ParserNotFound]
|
10
|
+
def initialize(before: nil, after: nil, type: nil, before_type: nil, after_type: nil, output_type: nil)
|
11
|
+
@before = before
|
12
|
+
@after = after
|
13
|
+
@type = type
|
14
|
+
@before_type = before_type
|
15
|
+
@after_type = after_type
|
16
|
+
@output_type = output_type
|
17
|
+
validate!
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] A string representation of this diff
|
21
|
+
def to_s
|
22
|
+
view_class.new(to_hash).to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
# @note Passed to Parslet::Transform to convert into SQL string
|
26
|
+
# @return [Hash] A hash representation of this diff
|
27
|
+
def to_hash
|
28
|
+
{
|
29
|
+
alter_tables: alter_tables,
|
30
|
+
create_tables: create_tables,
|
31
|
+
drop_tables: drop_tables,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# TODO
|
38
|
+
# @return [Array<Hash>] ALTER TABLE statements we need to apply
|
39
|
+
def alter_tables
|
40
|
+
after_schema.tables.inject([]) do |result, after_table|
|
41
|
+
if before_table = before_schema.tables_indexed_by_name[after_table.name]
|
42
|
+
after_table.fields.each do |after_field|
|
43
|
+
unless before_table.fields_indexed_by_name[after_field.name]
|
44
|
+
result << {
|
45
|
+
add_field: after_field.to_hash.merge(table_name: after_table.name),
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
before_table.fields.each do |before_field|
|
51
|
+
unless after_table.fields_indexed_by_name[before_field.name]
|
52
|
+
result << {
|
53
|
+
drop_field: before_field.to_hash.merge(table_name: after_table.name),
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Array<Hash>] DROP TABLE statements we need to apply
|
63
|
+
def drop_tables
|
64
|
+
table_names_to_drop.map do |name|
|
65
|
+
{
|
66
|
+
drop_table: {
|
67
|
+
name: name,
|
68
|
+
},
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Array<Hash>] CREATE TABLE statements we need to apply
|
74
|
+
def create_tables
|
75
|
+
after_schema.create_tables.select do |statement|
|
76
|
+
table_names_to_create.include?(statement[:create_table][:name])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [Array<String>]
|
81
|
+
def table_names_to_create
|
82
|
+
@table_names_to_create ||= after_schema.table_names - before_schema.table_names
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Array<String>]
|
86
|
+
def table_names_to_drop
|
87
|
+
@table_names_to_drop ||= before_schema.table_names - after_schema.table_names
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Scheman::Schema]
|
91
|
+
def before_schema
|
92
|
+
@before_schema ||= before_parser.parse(@before)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Scheman::Schema]
|
96
|
+
def after_schema
|
97
|
+
@after_schema ||= after_parser.parse(@after)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [Scheman::Parsers::Base]
|
101
|
+
# @raise [Scheman::Errors::ParserNotFound]
|
102
|
+
def before_parser
|
103
|
+
@before_parser ||= ParserBuilder.build(before_type)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [Scheman::Parsers::Base]
|
107
|
+
# @raise [Scheman::Errors::ParserNotFound]
|
108
|
+
def after_parser
|
109
|
+
@after_parser ||= ParserBuilder.build(after_type)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @raise [Scheman::Errors::ParserNotFound]
|
113
|
+
def validate!
|
114
|
+
before_parser
|
115
|
+
after_parser
|
116
|
+
end
|
117
|
+
|
118
|
+
# @return [String]
|
119
|
+
def before_type
|
120
|
+
@before_type || @type
|
121
|
+
end
|
122
|
+
|
123
|
+
# @return [String]
|
124
|
+
def after_type
|
125
|
+
@after_type || @type
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String]
|
129
|
+
def output_type
|
130
|
+
@output_type || @type
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [Class]
|
134
|
+
def view_class
|
135
|
+
case output_type
|
136
|
+
when "mysql"
|
137
|
+
Views::Mysql
|
138
|
+
else
|
139
|
+
raise Errors::ViewNotFound
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Scheman
|
2
|
+
class ParserBuilder
|
3
|
+
# @param type [String] A type of parser (e.g. "mysql")
|
4
|
+
# @return [Scheman::Parsers::Base]
|
5
|
+
def self.build(type)
|
6
|
+
new(type).build
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param type [String]
|
10
|
+
def initialize(type)
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
def build
|
15
|
+
parser_class.new
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parser_class
|
21
|
+
case @type
|
22
|
+
when "mysql"
|
23
|
+
Parsers::Mysql
|
24
|
+
else
|
25
|
+
raise Errors::ParserNotFound
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|