sequel_migration_builder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|