sequel_migration_builder 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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +55 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/sequel/migration_builder.rb +140 -0
- data/lib/sequel/schema/alter_table_operations.rb +85 -0
- data/lib/sequel/schema/db_column.rb +96 -0
- data/lib/sequel/schema/db_schema_parser.rb +94 -0
- data/sequel_migration_builder.gemspec +67 -0
- data/spec/alter_table_operations_spec.rb +152 -0
- data/spec/db_column_spec.rb +47 -0
- data/spec/db_schema_parser_spec.rb +99 -0
- data/spec/migration_builder_spec.rb +143 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +110 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Roland Swingler
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= Sequel Migration Builder
|
2
|
+
|
3
|
+
Builds sequel migrations based on the differences between an abstract
|
4
|
+
representation of the desired schema and a database instance.
|
5
|
+
|
6
|
+
== Example
|
7
|
+
|
8
|
+
require 'sequel/migration_builder'
|
9
|
+
|
10
|
+
desired_schema = { ... } # see below
|
11
|
+
builder = Sequel::MigrationBuilder.new(DB)
|
12
|
+
migration_code = builder.generate_migration(desired_schema)
|
13
|
+
File.write("001_some_migration.rb", migration_code)
|
14
|
+
|
15
|
+
== Schema format
|
16
|
+
|
17
|
+
The schema is an abstract representation of the tables in your database,
|
18
|
+
as a hash. A sample YAML version might look like:
|
19
|
+
|
20
|
+
example_table:
|
21
|
+
primary_key: id
|
22
|
+
columns:
|
23
|
+
- name: id
|
24
|
+
column_type: integer
|
25
|
+
- name: foo
|
26
|
+
column_type: varchar
|
27
|
+
default: "bar"
|
28
|
+
null: true
|
29
|
+
size: 30
|
30
|
+
another_table:
|
31
|
+
...
|
32
|
+
|
33
|
+
== Requirements
|
34
|
+
|
35
|
+
* Sequel 3.12.0 or higher
|
36
|
+
|
37
|
+
== TODO
|
38
|
+
|
39
|
+
* Dropping tables when they are removed from the schema
|
40
|
+
* Automigrate functionality
|
41
|
+
* Dealing with renames in some way (even if just logging that they would be possible).
|
42
|
+
|
43
|
+
== Note on Patches/Pull Requests
|
44
|
+
|
45
|
+
* Fork the project.
|
46
|
+
* Make your feature addition or bug fix.
|
47
|
+
* Add tests for it. This is important so I don't break it in a
|
48
|
+
future version unintentionally.
|
49
|
+
* Commit, do not mess with rakefile, version, or history.
|
50
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
51
|
+
* Send me a pull request. Bonus points for topic branches.
|
52
|
+
|
53
|
+
== Copyright
|
54
|
+
|
55
|
+
Copyright (c) 2010 Roland Swingler. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "sequel_migration_builder"
|
8
|
+
gem.summary = "Build Sequel Migrations based on the differences between two schemas"
|
9
|
+
gem.description = "Build Sequel Migrations based on the differences between two schemas"
|
10
|
+
gem.email = "roland.swingler@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/knaveofdiamonds/sequel_migration_builder"
|
12
|
+
gem.authors = ["Roland Swingler"]
|
13
|
+
gem.add_dependency "sequel", ">= 3.12.0"
|
14
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "sequel_migration_builder #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'sequel/schema/db_column'
|
2
|
+
require 'sequel/schema/db_schema_parser'
|
3
|
+
require 'sequel/schema/alter_table_operations'
|
4
|
+
|
5
|
+
module Sequel
|
6
|
+
# Generates a Sequel migration to bring a database inline with an
|
7
|
+
# abstract schema.
|
8
|
+
#
|
9
|
+
class MigrationBuilder
|
10
|
+
INDENT_SPACES = ' '
|
11
|
+
|
12
|
+
# Creates a migration builder for the given database.
|
13
|
+
#
|
14
|
+
def initialize(db)
|
15
|
+
@db = db
|
16
|
+
@db_tables = Schema::DbSchemaParser.for_db(db).parse_db_schema
|
17
|
+
@db_table_names = @db.tables
|
18
|
+
@indent = 0
|
19
|
+
@result = []
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generates a string of ruby code to define a sequel
|
23
|
+
# migration, based on the differences between the database schema
|
24
|
+
# of this MigrationBuilder and the tables passed.
|
25
|
+
#
|
26
|
+
def generate_migration(tables)
|
27
|
+
return if tables.empty? && @db_tables.empty?
|
28
|
+
result.clear
|
29
|
+
|
30
|
+
add_line "Sequel.migration do"
|
31
|
+
indent do
|
32
|
+
generate_up(tables)
|
33
|
+
generate_down(tables)
|
34
|
+
end
|
35
|
+
add_line "end\n"
|
36
|
+
|
37
|
+
result.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generates the 'up' part of the migration.
|
41
|
+
#
|
42
|
+
def generate_up(tables)
|
43
|
+
current_tables, new_tables = table_names(tables).partition do |table_name|
|
44
|
+
@db_table_names.include?(table_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
add_line "up do"
|
48
|
+
create_new_tables(new_tables, tables)
|
49
|
+
alter_tables(current_tables, tables, :up)
|
50
|
+
add_line "end"
|
51
|
+
add_blank_line
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generates the down part of the migration.
|
55
|
+
#
|
56
|
+
def generate_down(tables)
|
57
|
+
current_tables, new_tables = table_names(tables).partition do |table_name|
|
58
|
+
@db_table_names.include?(table_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
add_line "down do"
|
62
|
+
alter_tables(current_tables, tables, :down)
|
63
|
+
indent do
|
64
|
+
new_tables.reverse.each {|table_name| add_line "drop_table #{table_name.inspect}" }
|
65
|
+
end
|
66
|
+
add_line "end"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Generates any create table statements for new tables.
|
70
|
+
#
|
71
|
+
def create_new_tables(new_tables, tables)
|
72
|
+
i = 0
|
73
|
+
new_tables.each do |table_name|
|
74
|
+
i += 1
|
75
|
+
indent { create_table_statement table_name, tables[table_name] }
|
76
|
+
add_blank_line unless i == tables.size
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Generates any alter table statements for current tables.
|
81
|
+
#
|
82
|
+
def alter_tables(current_tables, tables, direction)
|
83
|
+
i = 0
|
84
|
+
indent do
|
85
|
+
current_tables.each do |table_name|
|
86
|
+
i += 1
|
87
|
+
hsh = tables[table_name].dup
|
88
|
+
hsh[:columns] = hsh[:columns].map {|c| Schema::DbColumn.build_from_hash(c) }
|
89
|
+
operations = Schema::AlterTableOperations.build(@db_tables[table_name], hsh)
|
90
|
+
unless operations.empty?
|
91
|
+
add_line "alter_table #{table_name.inspect} do"
|
92
|
+
indent do
|
93
|
+
operations.each {|op| add_line op.__send__(direction) }
|
94
|
+
end
|
95
|
+
add_line "end"
|
96
|
+
add_blank_line unless i == tables.size
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Generates an individual create_table statement.
|
103
|
+
#
|
104
|
+
def create_table_statement(table_name, table)
|
105
|
+
add_line "create_table #{table_name.inspect} do"
|
106
|
+
indent do
|
107
|
+
table[:columns].map {|c| Schema::DbColumn.build_from_hash(c) }.each do |column|
|
108
|
+
add_line column.define_statement
|
109
|
+
end
|
110
|
+
if table[:primary_key]
|
111
|
+
add_blank_line
|
112
|
+
add_line "primary_key #{table[:primary_key].inspect}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
add_line "end"
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
attr_reader :result
|
121
|
+
|
122
|
+
def table_names(tables)
|
123
|
+
tables.keys.map {|n| n.to_s }.sort.map {|n| n.to_sym }
|
124
|
+
end
|
125
|
+
|
126
|
+
def indent
|
127
|
+
@indent += 1
|
128
|
+
yield
|
129
|
+
@indent -= 1
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_line(line)
|
133
|
+
@result << (INDENT_SPACES * @indent + line)
|
134
|
+
end
|
135
|
+
|
136
|
+
def add_blank_line
|
137
|
+
@result << ''
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Schema
|
3
|
+
module AlterTableOperations
|
4
|
+
|
5
|
+
# Returns an array of operations to change the current database
|
6
|
+
# table to be like the defined table.
|
7
|
+
#
|
8
|
+
def self.build(db_table, new_table)
|
9
|
+
db_columns = db_table[:columns].inject({}) {|hsh, column| hsh[column.name] = column; hsh }
|
10
|
+
|
11
|
+
operations = new_table[:columns].map do |column|
|
12
|
+
if db_columns[column.name]
|
13
|
+
build_column_operations db_columns[column.name], column
|
14
|
+
else
|
15
|
+
AddColumn.new(column)
|
16
|
+
end
|
17
|
+
end.flatten
|
18
|
+
|
19
|
+
new_column_names = new_table[:columns].map {|c| c.name }
|
20
|
+
operations + (db_columns.keys - new_column_names).map {|column| DropColumn.new(column) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an array of operations to change the current database
|
24
|
+
# column to be like the defined column.
|
25
|
+
#
|
26
|
+
def self.build_column_operations(db_column, new_column)
|
27
|
+
result = []
|
28
|
+
|
29
|
+
diffs = db_column.diff(new_column)
|
30
|
+
result << :change_type_statement if [:column_type, :size, :unsigned].any? {|sym| diffs.include?(sym) }
|
31
|
+
# only need to explicitly set the default if we're not changing the column type.
|
32
|
+
result << :change_default_statement if diffs.include?(:default) && result.empty?
|
33
|
+
result << :change_null_statement if diffs.include?(:null)
|
34
|
+
|
35
|
+
result.map {|k| ChangeColumn.new(db_column, new_column, k) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Changes a column.
|
39
|
+
class ChangeColumn
|
40
|
+
def initialize(old_column, new_column, statement)
|
41
|
+
@old_column, @new_column = old_column, new_column
|
42
|
+
@statement_method = statement
|
43
|
+
end
|
44
|
+
|
45
|
+
def up
|
46
|
+
@new_column.__send__(@statement_method)
|
47
|
+
end
|
48
|
+
|
49
|
+
def down
|
50
|
+
@old_column.__send__(@statement_method)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Adds a column.
|
55
|
+
class AddColumn
|
56
|
+
def initialize(column)
|
57
|
+
@column = column
|
58
|
+
end
|
59
|
+
|
60
|
+
def up
|
61
|
+
@column.add_statement
|
62
|
+
end
|
63
|
+
|
64
|
+
def down
|
65
|
+
@column.drop_statement
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Drops a column.
|
70
|
+
class DropColumn
|
71
|
+
def initialize(column)
|
72
|
+
@column = column
|
73
|
+
end
|
74
|
+
|
75
|
+
def up
|
76
|
+
@column.drop_statement
|
77
|
+
end
|
78
|
+
|
79
|
+
def down
|
80
|
+
@column.add_statement
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Schema
|
3
|
+
DbColumn = Struct.new(:name, :column_type, :null, :default, :unsigned, :size, :elements)
|
4
|
+
|
5
|
+
# A column in a database table.
|
6
|
+
#
|
7
|
+
# Responsible for generating all migration method calls used by
|
8
|
+
# migration operations.
|
9
|
+
#
|
10
|
+
class DbColumn
|
11
|
+
# Builds a DbColumn from a Hash of attribute values. Keys
|
12
|
+
# can be strings or symbols.
|
13
|
+
#
|
14
|
+
def self.build_from_hash(attrs={})
|
15
|
+
new *members.map {|key| attrs[key] || attrs[key.to_sym] }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a Sequel migration statement to define a column in a
|
19
|
+
# create_table block.
|
20
|
+
#
|
21
|
+
def define_statement
|
22
|
+
["#{column_type} #{name.inspect}", options].compact.join(", ")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a Sequel migration statement to remove the column.
|
26
|
+
#
|
27
|
+
def drop_statement
|
28
|
+
"drop_column #{name.inspect}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a Sequel migration statement to add the column to a
|
32
|
+
# table in an alter_table block.
|
33
|
+
#
|
34
|
+
def add_statement
|
35
|
+
["add_column #{name.inspect}", column_type.inspect, options].compact.join(", ")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a Sequel migration statement to change whether a column
|
39
|
+
# allows null values.
|
40
|
+
#
|
41
|
+
def change_null_statement
|
42
|
+
"set_column_allow_null #{name.inspect}, #{(!!null).inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a Sequel migration statement to change a column's default
|
46
|
+
# value.
|
47
|
+
#
|
48
|
+
def change_default_statement
|
49
|
+
"set_column_default #{name.inspect}, #{default.inspect}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a Sequel migration statement to change the type of an
|
53
|
+
# existing column. Null changes must be handled separately.
|
54
|
+
#
|
55
|
+
def change_type_statement
|
56
|
+
["set_column_type #{name.inspect}", column_type.inspect, change_options].compact.join(", ")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns an Array of attributes that are different between this
|
60
|
+
# and another column.
|
61
|
+
#
|
62
|
+
def diff(other)
|
63
|
+
result = []
|
64
|
+
each_pair {|key, value| result << key if other[key] != value }
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def change_options
|
71
|
+
opts = []
|
72
|
+
|
73
|
+
opts << ":default => #{default.inspect}"
|
74
|
+
# seems odd, but we only want to output if unsigned is a true
|
75
|
+
# boolean, not if it is nil.
|
76
|
+
opts << ":unsigned => #{unsigned.inspect}" if unsigned == true || unsigned == false
|
77
|
+
opts << ":size => #{size.inspect}" if size
|
78
|
+
opts << ":elements => #{elements.inspect}" if elements
|
79
|
+
|
80
|
+
opts.join(", ") unless opts.empty?
|
81
|
+
end
|
82
|
+
|
83
|
+
def options
|
84
|
+
opts = []
|
85
|
+
|
86
|
+
opts << ":null => false" unless null == true
|
87
|
+
opts << ":default => #{default.inspect}" if default
|
88
|
+
opts << ":unsigned => true" if unsigned
|
89
|
+
opts << ":size => #{size.inspect}" if size
|
90
|
+
opts << ":elements => #{elements.inspect}" if elements
|
91
|
+
|
92
|
+
opts.join(", ") unless opts.empty?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Schema
|
3
|
+
# Builds an abstract representation of a database schema.
|
4
|
+
#
|
5
|
+
# Sample usage:
|
6
|
+
#
|
7
|
+
# parser = DbSchemaParser.for_db( sequel_db_connection )
|
8
|
+
# parser.parse_db_schema
|
9
|
+
# # => Returns an array of table definitions
|
10
|
+
#
|
11
|
+
class DbSchemaParser
|
12
|
+
# Returns an appropriate schema parser for the database
|
13
|
+
# connection.
|
14
|
+
#
|
15
|
+
def self.for_db(db)
|
16
|
+
self.new(db)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parses the schema from a Sequel Database connection.
|
20
|
+
#
|
21
|
+
# Returns a hash of table representations.
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# builder.parse_db_schema(db)
|
26
|
+
# # => {:table1 => { :columns => [ DbColumns ... ] }
|
27
|
+
# :table2 => { ... } }
|
28
|
+
#
|
29
|
+
def parse_db_schema
|
30
|
+
result = {}
|
31
|
+
@db.tables.each do |table_name|
|
32
|
+
result[table_name] = {:columns => parse_table_schema(@db.schema(table_name))}
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
# Extracts an array of hashes representing the columns in the
|
38
|
+
# table, given an Array of Arrays returned by DB.schema(:table).
|
39
|
+
#
|
40
|
+
def parse_table_schema(db_schema)
|
41
|
+
db_schema.map do |column|
|
42
|
+
attrs = {
|
43
|
+
:name => column.first,
|
44
|
+
:default => column.last[:ruby_default],
|
45
|
+
:null => column.last[:allow_null],
|
46
|
+
:column_type => parse_type(column.last[:db_type]),
|
47
|
+
:unsigned => column.last[:db_type].include?(" unsigned")
|
48
|
+
}
|
49
|
+
attrs[:size] = extract_size(column) if column.last[:type] == :string
|
50
|
+
attrs[:elements] = extract_enum_elements(column) if attrs[:column_type] == :enum
|
51
|
+
|
52
|
+
DbColumn.build_from_hash(attrs)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
# Creates a new schema parser for the given database
|
59
|
+
# connection. Use for_db instead.
|
60
|
+
#
|
61
|
+
def initialize(db)
|
62
|
+
@db = db
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a type symbol for a given db_type string, suitable for
|
66
|
+
# use in a Sequel migration.
|
67
|
+
#
|
68
|
+
# Examples:
|
69
|
+
#
|
70
|
+
# parse_type("int(11)") # => :integer
|
71
|
+
# parse_type("varchar(20)") # => :varchar
|
72
|
+
#
|
73
|
+
def parse_type(type)
|
74
|
+
case type
|
75
|
+
when /^int/ then :integer
|
76
|
+
when /^tinyint\(1\)/ then :boolean
|
77
|
+
when /^([^(]+)/ then $1.to_sym
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def extract_size(column)
|
84
|
+
match = column.last[:db_type].match(/\((\d+)\)/)
|
85
|
+
match[1].to_i if match[1]
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_enum_elements(column)
|
89
|
+
match = column.last[:db_type].match(/\(([^)]+)\)/)
|
90
|
+
eval('[' + match[1] + ']') if match[1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{sequel_migration_builder}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roland Swingler"]
|
12
|
+
s.date = %q{2010-06-13}
|
13
|
+
s.description = %q{Build Sequel Migrations based on the differences between two schemas}
|
14
|
+
s.email = %q{roland.swingler@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/sequel/migration_builder.rb",
|
27
|
+
"lib/sequel/schema/alter_table_operations.rb",
|
28
|
+
"lib/sequel/schema/db_column.rb",
|
29
|
+
"lib/sequel/schema/db_schema_parser.rb",
|
30
|
+
"sequel_migration_builder.gemspec",
|
31
|
+
"spec/alter_table_operations_spec.rb",
|
32
|
+
"spec/db_column_spec.rb",
|
33
|
+
"spec/db_schema_parser_spec.rb",
|
34
|
+
"spec/migration_builder_spec.rb",
|
35
|
+
"spec/spec.opts",
|
36
|
+
"spec/spec_helper.rb"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/knaveofdiamonds/sequel_migration_builder}
|
39
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = %q{1.3.6}
|
42
|
+
s.summary = %q{Build Sequel Migrations based on the differences between two schemas}
|
43
|
+
s.test_files = [
|
44
|
+
"spec/alter_table_operations_spec.rb",
|
45
|
+
"spec/db_column_spec.rb",
|
46
|
+
"spec/db_schema_parser_spec.rb",
|
47
|
+
"spec/migration_builder_spec.rb",
|
48
|
+
"spec/spec_helper.rb"
|
49
|
+
]
|
50
|
+
|
51
|
+
if s.respond_to? :specification_version then
|
52
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<sequel>, [">= 3.12.0"])
|
57
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<sequel>, [">= 3.12.0"])
|
60
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<sequel>, [">= 3.12.0"])
|
64
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
def build_column(hash)
|
4
|
+
Sequel::Schema::DbColumn.build_from_hash(hash)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "Sequel::Schema::AlterTableOperations.build_column_operations" do
|
8
|
+
it "should return an empty Array if there are no differences between column definitions" do
|
9
|
+
a = build_column(:name => :foo, :column_type => :integer)
|
10
|
+
b = build_column(:name => :foo, :column_type => :integer)
|
11
|
+
|
12
|
+
Sequel::Schema::AlterTableOperations.build_column_operations(a,b).should == []
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return a ChangeColumn operation if the types are different" do
|
16
|
+
a = build_column(:name => :foo, :column_type => :integer)
|
17
|
+
b = build_column(:name => :foo, :column_type => :smallint)
|
18
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
19
|
+
|
20
|
+
ops.first.up.should == "set_column_type :foo, :smallint, :default => nil"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return a ChangeColumn operation if the sizes are different" do
|
24
|
+
a = build_column(:name => :foo, :column_type => :char, :size => 20)
|
25
|
+
b = build_column(:name => :foo, :column_type => :char, :size => 10)
|
26
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
27
|
+
|
28
|
+
ops.first.up.should == "set_column_type :foo, :char, :default => nil, :size => 10"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return a ChangeColumn operation if the unsigned value is different" do
|
32
|
+
a = build_column(:name => :foo, :column_type => :integer, :unsigned => true)
|
33
|
+
b = build_column(:name => :foo, :column_type => :integer, :unsigned => false)
|
34
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
35
|
+
|
36
|
+
ops.first.up.should == "set_column_type :foo, :integer, :default => nil, :unsigned => false"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return a ChangeColumn operation to set the null value if the null value is different" do
|
40
|
+
a = build_column(:name => :foo, :column_type => :integer, :null => true)
|
41
|
+
b = build_column(:name => :foo, :column_type => :integer, :null => false)
|
42
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
43
|
+
|
44
|
+
ops.first.up.should == "set_column_allow_null :foo, false"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return a ChangeColumn operation to set the default if the default value is different" do
|
48
|
+
a = build_column(:name => :foo, :column_type => :integer, :default => 1)
|
49
|
+
b = build_column(:name => :foo, :column_type => :integer, :default => 2)
|
50
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
51
|
+
|
52
|
+
ops.first.up.should == "set_column_default :foo, 2"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should only return 1 operation if the default and other values are different" do
|
56
|
+
a = build_column(:name => :foo, :column_type => :integer, :default => 1)
|
57
|
+
b = build_column(:name => :foo, :column_type => :smallint, :default => 2)
|
58
|
+
ops = Sequel::Schema::AlterTableOperations.build_column_operations(a,b)
|
59
|
+
|
60
|
+
ops.size.should == 1
|
61
|
+
ops.first.up.should == "set_column_type :foo, :smallint, :default => 2"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "Sequel::Schema::AlterTableOperations.build" do
|
66
|
+
it "should return an empty array if nothing is different" do
|
67
|
+
table_a = {:name => :example_table,
|
68
|
+
:columns => [build_column(:name => :foo, :column_type => :integer)]}
|
69
|
+
table_b = {:name => :example_table,
|
70
|
+
:columns => [build_column(:name => :foo, :column_type => :integer)]}
|
71
|
+
ops = Sequel::Schema::AlterTableOperations.build(table_a,table_b)
|
72
|
+
|
73
|
+
ops.should == []
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return an add column operation if the column is new" do
|
77
|
+
table_a = {:name => :example_table,
|
78
|
+
:columns => []}
|
79
|
+
table_b = {:name => :example_table,
|
80
|
+
:columns => [build_column(:name => :foo, :column_type => :integer)]}
|
81
|
+
ops = Sequel::Schema::AlterTableOperations.build(table_a,table_b)
|
82
|
+
|
83
|
+
ops.size.should == 1
|
84
|
+
ops.first.should be_kind_of(Sequel::Schema::AlterTableOperations::AddColumn)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should return a drop column operation if the column has been removed" do
|
88
|
+
table_a = {:name => :example_table,
|
89
|
+
:columns => [build_column(:name => :foo, :column_type => :integer)]}
|
90
|
+
table_b = {:name => :example_table,
|
91
|
+
:columns => []}
|
92
|
+
ops = Sequel::Schema::AlterTableOperations.build(table_a,table_b)
|
93
|
+
|
94
|
+
ops.size.should == 1
|
95
|
+
ops.first.should be_kind_of(Sequel::Schema::AlterTableOperations::DropColumn)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should return a change column operation if columns are different" do
|
99
|
+
table_a = {:name => :example_table,
|
100
|
+
:columns => [build_column(:name => :foo, :column_type => :integer)]}
|
101
|
+
table_b = {:name => :example_table,
|
102
|
+
:columns => [build_column(:name => :foo, :column_type => :smallint)]}
|
103
|
+
ops = Sequel::Schema::AlterTableOperations.build(table_a,table_b)
|
104
|
+
|
105
|
+
ops.size.should == 1
|
106
|
+
ops.first.should be_kind_of(Sequel::Schema::AlterTableOperations::ChangeColumn)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe Sequel::Schema::AlterTableOperations::AddColumn do
|
111
|
+
before(:each) { @mock_column = mock() }
|
112
|
+
|
113
|
+
it "should ask the column for its add column statement on #up" do
|
114
|
+
@mock_column.should_receive(:add_statement)
|
115
|
+
Sequel::Schema::AlterTableOperations::AddColumn.new(@mock_column).up
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should ask the column for its drop column statement on #down" do
|
119
|
+
@mock_column.should_receive(:drop_statement)
|
120
|
+
Sequel::Schema::AlterTableOperations::AddColumn.new(@mock_column).down
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe Sequel::Schema::AlterTableOperations::DropColumn do
|
125
|
+
before(:each) { @mock_column = mock() }
|
126
|
+
|
127
|
+
it "should ask the column for its drop column statement on #up" do
|
128
|
+
@mock_column.should_receive(:drop_statement)
|
129
|
+
Sequel::Schema::AlterTableOperations::DropColumn.new(@mock_column).up
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should ask the column for its add column statement on #down" do
|
133
|
+
@mock_column.should_receive(:add_statement)
|
134
|
+
Sequel::Schema::AlterTableOperations::DropColumn.new(@mock_column).down
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe Sequel::Schema::AlterTableOperations::ChangeColumn do
|
139
|
+
it "should ask the new column for statement on #up" do
|
140
|
+
new = mock(:new)
|
141
|
+
old = mock(:old)
|
142
|
+
new.should_receive(:change_type_statement)
|
143
|
+
Sequel::Schema::AlterTableOperations::ChangeColumn.new(old, new, :change_type_statement).up
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should ask the new column for statement on #down" do
|
147
|
+
new = mock(:new)
|
148
|
+
old = mock(:old)
|
149
|
+
old.should_receive(:change_type_statement)
|
150
|
+
Sequel::Schema::AlterTableOperations::ChangeColumn.new(old, new, :change_type_statement).down
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Sequel::Schema::DbColumn do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@column = Sequel::Schema::DbColumn.new(:foo, :integer, false, 10, true, 10, nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return a #define_statement" do
|
10
|
+
@column.define_statement.should == "integer :foo, :null => false, :default => 10, :unsigned => true, :size => 10"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return a #drop_statement" do
|
14
|
+
@column.drop_statement.should == "drop_column :foo"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return an #add_statement" do
|
18
|
+
@column.add_statement.should == "add_column :foo, :integer, :null => false, :default => 10, :unsigned => true, :size => 10"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return a #change_null statement" do
|
22
|
+
@column.change_null_statement.should == "set_column_allow_null :foo, false"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return a #change_default statement" do
|
26
|
+
@column.change_default_statement.should == "set_column_default :foo, 10"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return a #change_type statement" do
|
30
|
+
@column.change_type_statement.should == "set_column_type :foo, :integer, :default => 10, :unsigned => true, :size => 10"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should be diffable with another DbColumn" do
|
34
|
+
other = Sequel::Schema::DbColumn.new(:foo, :smallint, false, 10, true, 10, nil)
|
35
|
+
@column.diff(other).should == [:column_type]
|
36
|
+
|
37
|
+
other = Sequel::Schema::DbColumn.new(:foo, :integer, true, 11, true, 10, nil)
|
38
|
+
@column.diff(other).should == [:null, :default]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be buildable from a Hash" do
|
42
|
+
Sequel::Schema::DbColumn.build_from_hash(:name => "foo",
|
43
|
+
:column_type => "integer").column_type.should == "integer"
|
44
|
+
Sequel::Schema::DbColumn.build_from_hash('name' => "foo",
|
45
|
+
'column_type' => "integer").name.should == "foo"
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe "Sequel::Schema::DbSchemaParser.for_db" do
|
4
|
+
it "should return a DbSchemaParser" do
|
5
|
+
Sequel::Schema::DbSchemaParser.for_db(stub(:database)).should \
|
6
|
+
be_kind_of(Sequel::Schema::DbSchemaParser)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "A hash in the array returned by Sequel::Schema::DbSchemaParser#parse_table_schema" do
|
11
|
+
before :each do
|
12
|
+
@parser = Sequel::Schema::DbSchemaParser.for_db(stub(:database))
|
13
|
+
@schema = [[:example_column,
|
14
|
+
{ :type => :integer,
|
15
|
+
:default => "1",
|
16
|
+
:ruby_default => 1,
|
17
|
+
:primary_key => false,
|
18
|
+
:db_type => "int(11)",
|
19
|
+
:allow_null => true }]]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should contain the :name of the column" do
|
23
|
+
@parser.parse_table_schema(@schema).first.name.should == :example_column
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should contain the ruby_default as the :default" do
|
27
|
+
@parser.parse_table_schema(@schema).first.default.should == 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should contain whether the column can be :null" do
|
31
|
+
@parser.parse_table_schema(@schema).first.null.should == true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should contain a type of :integer given a int column" do
|
35
|
+
set_db_type "int(11)"
|
36
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :integer
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should contain a type of :boolean given a tinyint(1) column" do
|
40
|
+
set_db_type "tinyint(1)"
|
41
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :boolean
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should contain a type of :tinyint given a tinyint column" do
|
45
|
+
set_db_type "tinyint(4)"
|
46
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :tinyint
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should contain a type of :smallint given a smallint column" do
|
50
|
+
set_db_type "smallint(5)"
|
51
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :smallint
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should contain a type of :mediumint given a mediumint column" do
|
55
|
+
set_db_type "mediumint(5)"
|
56
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :mediumint
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should contain a type of :bigint given a bigint column" do
|
60
|
+
set_db_type "bigint(10)"
|
61
|
+
@parser.parse_table_schema(@schema).first.column_type.should == :bigint
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should contain a :size attribute for text-like columns" do
|
65
|
+
set_db_type "varchar(20)", :string
|
66
|
+
@parser.parse_table_schema(@schema).first.size.should == 20
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should contain :unsigned false if a numeric column is not unsigned" do
|
70
|
+
set_db_type "int(10)"
|
71
|
+
@parser.parse_table_schema(@schema).first.unsigned.should == false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should contain :unsigned true if a numeric column is unsigned" do
|
75
|
+
set_db_type "int(10) unsigned"
|
76
|
+
@parser.parse_table_schema(@schema).first.unsigned.should == true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should contain the elements of an enum column" do
|
80
|
+
set_db_type "enum('foo','bar')"
|
81
|
+
@parser.parse_table_schema(@schema).first.elements.should == ['foo', 'bar']
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_db_type(type, ruby_type=nil)
|
85
|
+
@schema.first.last.merge!(:db_type => type)
|
86
|
+
@schema.first.last.merge!(:type => ruby_type) if ruby_type
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "Sequel::Schema::DbSchemaParser#parse_db_schema" do
|
91
|
+
it "should extract a list of table definitions from a database" do
|
92
|
+
mock_db = mock(:db)
|
93
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([:table1])
|
94
|
+
mock_db.should_receive(:schema).with(:table1).and_return([])
|
95
|
+
|
96
|
+
@parser = Sequel::Schema::DbSchemaParser.for_db(mock_db)
|
97
|
+
@parser.parse_db_schema.keys.should == [:table1]
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Sequel::MigrationBuilder do
|
4
|
+
|
5
|
+
it "should return nil if the table hash is empty and the database has no tables" do
|
6
|
+
mock_db = mock(:database)
|
7
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([])
|
8
|
+
Sequel::MigrationBuilder.new(mock_db).generate_migration({}).should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should produce a simple migration string given a database connection and a hash of tables" do
|
12
|
+
tables = {}
|
13
|
+
tables[:example_table] = {
|
14
|
+
:columns => [{:name => :foo, :column_type => :integer}]
|
15
|
+
}
|
16
|
+
|
17
|
+
expected = <<-END
|
18
|
+
Sequel.migration do
|
19
|
+
up do
|
20
|
+
create_table :example_table do
|
21
|
+
integer :foo, :null => false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
down do
|
26
|
+
drop_table :example_table
|
27
|
+
end
|
28
|
+
end
|
29
|
+
END
|
30
|
+
|
31
|
+
mock_db = mock(:database)
|
32
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([])
|
33
|
+
Sequel::MigrationBuilder.new(mock_db).generate_migration(tables).should == expected
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should produce statements for multiple new tables" do
|
37
|
+
tables = {}
|
38
|
+
tables[:example_table] = {
|
39
|
+
:columns => [{:name => :foo, :column_type => :integer}, {:name => :bar, :column_type => :varchar}]
|
40
|
+
}
|
41
|
+
|
42
|
+
tables[:example_table_2] = {
|
43
|
+
:columns => [{:name => :foo, :column_type => :integer, :null => true}]
|
44
|
+
}
|
45
|
+
|
46
|
+
expected = <<-END
|
47
|
+
Sequel.migration do
|
48
|
+
up do
|
49
|
+
create_table :example_table do
|
50
|
+
integer :foo, :null => false
|
51
|
+
varchar :bar, :null => false
|
52
|
+
end
|
53
|
+
|
54
|
+
create_table :example_table_2 do
|
55
|
+
integer :foo
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
down do
|
60
|
+
drop_table :example_table_2
|
61
|
+
drop_table :example_table
|
62
|
+
end
|
63
|
+
end
|
64
|
+
END
|
65
|
+
|
66
|
+
mock_db = mock(:database)
|
67
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([])
|
68
|
+
Sequel::MigrationBuilder.new(mock_db).generate_migration(tables).should == expected
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should add the primary key of the table" do
|
72
|
+
mock_db = mock(:database)
|
73
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([])
|
74
|
+
table = {
|
75
|
+
:primary_key => :foo,
|
76
|
+
:columns => [{:name => :foo, :column_type => :integer}, {:name => :bar, :column_type => :varchar}]
|
77
|
+
}
|
78
|
+
|
79
|
+
expected = <<-END
|
80
|
+
create_table :example_table do
|
81
|
+
integer :foo, :null => false
|
82
|
+
varchar :bar, :null => false
|
83
|
+
|
84
|
+
primary_key :foo
|
85
|
+
end
|
86
|
+
END
|
87
|
+
|
88
|
+
Sequel::MigrationBuilder.new(mock_db).create_table_statement(:example_table, table).join("\n").
|
89
|
+
should == expected.strip
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
context "when a table needs to be altered" do
|
94
|
+
before :each do
|
95
|
+
@tables = { :example_table =>
|
96
|
+
{:columns => [{:name => :foo, :column_type => :integer}, {:name => :bar, :column_type => :varchar}]}
|
97
|
+
}
|
98
|
+
@mock_db = mock(:database)
|
99
|
+
@mock_db.should_receive(:tables).at_least(:once).and_return([:example_table])
|
100
|
+
@mock_db.should_receive(:schema).with(:example_table).and_return([[:foo, {:type => :integer, :db_type => "smallint(5) unsigned", :allow_null => true, :ruby_default => 10}]])
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return an alter table statement with column changes for #generate_up" do
|
105
|
+
expected = <<-END
|
106
|
+
up do
|
107
|
+
alter_table :example_table do
|
108
|
+
set_column_type :foo, :integer, :default => nil
|
109
|
+
set_column_allow_null :foo, false
|
110
|
+
add_column :bar, :varchar, :null => false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
END
|
114
|
+
Sequel::MigrationBuilder.new(@mock_db).generate_up(@tables).join("\n").should == expected
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should return an alter table statement with column changes for #generate_down" do
|
118
|
+
expected = <<-END
|
119
|
+
down do
|
120
|
+
alter_table :example_table do
|
121
|
+
set_column_type :foo, :smallint, :default => 10, :unsigned => true
|
122
|
+
set_column_allow_null :foo, true
|
123
|
+
drop_column :bar
|
124
|
+
end
|
125
|
+
end
|
126
|
+
END
|
127
|
+
Sequel::MigrationBuilder.new(@mock_db).generate_down(@tables).join("\n").should == expected.strip
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should drop the table if the table exists in the database but not the table hash" do
|
132
|
+
pending # Deal with in a later version.
|
133
|
+
mock_db = mock(:database)
|
134
|
+
mock_db.should_receive(:tables).at_least(:once).and_return([:example_table])
|
135
|
+
|
136
|
+
expected = <<-END
|
137
|
+
up do
|
138
|
+
drop_table :example_table
|
139
|
+
end
|
140
|
+
END
|
141
|
+
Sequel::MigrationBuilder.new(mock_db).generate_up({}).join("\n").should == expected
|
142
|
+
end
|
143
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sequel_migration_builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Roland Swingler
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-13 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: sequel
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 3
|
29
|
+
- 12
|
30
|
+
- 0
|
31
|
+
version: 3.12.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 2
|
44
|
+
- 9
|
45
|
+
version: 1.2.9
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Build Sequel Migrations based on the differences between two schemas
|
49
|
+
email: roland.swingler@gmail.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- .document
|
59
|
+
- .gitignore
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- VERSION
|
64
|
+
- lib/sequel/migration_builder.rb
|
65
|
+
- lib/sequel/schema/alter_table_operations.rb
|
66
|
+
- lib/sequel/schema/db_column.rb
|
67
|
+
- lib/sequel/schema/db_schema_parser.rb
|
68
|
+
- sequel_migration_builder.gemspec
|
69
|
+
- spec/alter_table_operations_spec.rb
|
70
|
+
- spec/db_column_spec.rb
|
71
|
+
- spec/db_schema_parser_spec.rb
|
72
|
+
- spec/migration_builder_spec.rb
|
73
|
+
- spec/spec.opts
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/knaveofdiamonds/sequel_migration_builder
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- --charset=UTF-8
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.6
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: Build Sequel Migrations based on the differences between two schemas
|
105
|
+
test_files:
|
106
|
+
- spec/alter_table_operations_spec.rb
|
107
|
+
- spec/db_column_spec.rb
|
108
|
+
- spec/db_schema_parser_spec.rb
|
109
|
+
- spec/migration_builder_spec.rb
|
110
|
+
- spec/spec_helper.rb
|