mack-data_mapper 0.8.1 → 0.8.2
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/lib/gems/addressable-2.0.0/lib/addressable/idna.rb +4867 -0
- data/lib/gems/addressable-2.0.0/lib/addressable/uri.rb +2469 -0
- data/lib/gems/addressable-2.0.0/lib/addressable/version.rb +35 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/adapters/data_objects_adapter.rb +85 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/aggregate_functions.rb +201 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/collection.rb +11 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/model.rb +11 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/repository.rb +7 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/support/symbol.rb +21 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/version.rb +7 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates.rb +15 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/abstract_adapter.rb +209 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/data_objects_adapter.rb +709 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/mysql_adapter.rb +136 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/postgres_adapter.rb +188 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters.rb +22 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_many.rb +147 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_one.rb +107 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb +318 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_one.rb +61 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship.rb +223 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship_chain.rb +81 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations.rb +200 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/auto_migrations.rb +105 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/collection.rb +642 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/dependency_queue.rb +32 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/hook.rb +11 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/identity_map.rb +42 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/is.rb +16 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/logger.rb +232 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/migrations/destructive_migrations.rb +17 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/migrator.rb +29 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/model.rb +488 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/naming_conventions.rb +84 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/property.rb +663 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/property_set.rb +169 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/query.rb +628 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/repository.rb +159 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/resource.rb +637 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/scope.rb +58 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/array.rb +13 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/assertions.rb +8 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/errors.rb +23 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/kernel.rb +11 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/symbol.rb +41 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support.rb +7 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/transaction.rb +267 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/type.rb +160 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/type_map.rb +80 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/boolean.rb +7 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/discriminator.rb +34 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/object.rb +24 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_boolean.rb +34 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_datetime.rb +33 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/serial.rb +9 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/text.rb +10 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types.rb +19 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/version.rb +3 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core.rb +217 -0
- data/lib/gems/dm-core-0.9.7/script/all +5 -0
- data/lib/gems/dm-core-0.9.7/script/performance.rb +284 -0
- data/lib/gems/dm-core-0.9.7/script/profile.rb +87 -0
- data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations/version.rb +5 -0
- data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations.rb +1 -0
- data/lib/gems/dm-migrations-0.9.7/lib/migration.rb +215 -0
- data/lib/gems/dm-migrations-0.9.7/lib/migration_runner.rb +88 -0
- data/lib/gems/dm-migrations-0.9.7/lib/spec/example/migration_example_group.rb +73 -0
- data/lib/gems/dm-migrations-0.9.7/lib/spec/matchers/migration_matchers.rb +107 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/column.rb +9 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/mysql.rb +52 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/postgresql.rb +78 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/sqlite3.rb +43 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table.rb +19 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table_creator.rb +81 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table_modifier.rb +53 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql.rb +10 -0
- data/lib/gems/dm-observer-0.9.7/lib/dm-observer/version.rb +5 -0
- data/lib/gems/dm-observer-0.9.7/lib/dm-observer.rb +91 -0
- data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer/version.rb +5 -0
- data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer.rb +183 -0
- data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps/version.rb +5 -0
- data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps.rb +57 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/bcrypt_hash.rb +31 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/csv.rb +28 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/enum.rb +70 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/epoch_time.rb +27 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/file_path.rb +27 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/flag.rb +61 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/ip_address.rb +30 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/json.rb +40 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/regexp.rb +20 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/serial.rb +8 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/slug.rb +37 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/uri.rb +29 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/uuid.rb +64 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/version.rb +5 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/yaml.rb +36 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types.rb +28 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/absent_field_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/acceptance_validator.rb +76 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/auto_validate.rb +153 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/block_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/confirmation_validator.rb +80 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/contextual_validators.rb +56 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/custom_validator.rb +72 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/format_validator.rb +97 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/email.rb +40 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/url.rb +20 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/generic_validator.rb +100 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/length_validator.rb +113 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/method_validator.rb +68 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/numeric_validator.rb +83 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/primitive_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/required_field_validator.rb +88 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/support/object.rb +5 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/uniqueness_validator.rb +64 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/validation_errors.rb +63 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/version.rb +5 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/within_validator.rb +53 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations.rb +234 -0
- data/lib/gems/json_pure-1.1.3/GPL +340 -0
- data/lib/gems/json_pure-1.1.3/VERSION +1 -0
- data/lib/gems/json_pure-1.1.3/bin/edit_json.rb +10 -0
- data/lib/gems/json_pure-1.1.3/bin/prettify_json.rb +76 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Array.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/FalseClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Hash.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Key.xpm +73 -0
- data/lib/gems/json_pure-1.1.3/lib/json/NilClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Numeric.xpm +28 -0
- data/lib/gems/json_pure-1.1.3/lib/json/String.xpm +96 -0
- data/lib/gems/json_pure-1.1.3/lib/json/TrueClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/add/core.rb +135 -0
- data/lib/gems/json_pure-1.1.3/lib/json/add/rails.rb +58 -0
- data/lib/gems/json_pure-1.1.3/lib/json/common.rb +354 -0
- data/lib/gems/json_pure-1.1.3/lib/json/editor.rb +1362 -0
- data/lib/gems/json_pure-1.1.3/lib/json/ext.rb +13 -0
- data/lib/gems/json_pure-1.1.3/lib/json/json.xpm +1499 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure/generator.rb +394 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure/parser.rb +259 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure.rb +75 -0
- data/lib/gems/json_pure-1.1.3/lib/json/version.rb +9 -0
- data/lib/gems/json_pure-1.1.3/lib/json.rb +235 -0
- data/lib/gems/launchy-0.3.2/bin/launchy +12 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/application.rb +163 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/browser.rb +85 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/command_line.rb +48 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/gemspec.rb +53 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/specification.rb +133 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/version.rb +18 -0
- data/lib/gems/launchy-0.3.2/lib/launchy.rb +58 -0
- data/lib/gems/uuidtools-1.0.3/lib/uuidtools/version.rb +32 -0
- data/lib/gems/uuidtools-1.0.3/lib/uuidtools.rb +648 -0
- data/lib/gems.rb +13 -0
- data/lib/mack-data_mapper/migration_generator/migration_generator.rb +5 -0
- data/lib/mack-data_mapper/migration_generator/templates/db/migrations/%=@migration_name%.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/manifest.yml +3 -3
- data/lib/mack-data_mapper/model_generator/model_generator.rb +8 -1
- data/lib/mack-data_mapper/model_generator/templates/model.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/templates/rspec.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/templates/test_case.rb.template +1 -1
- data/lib/mack-data_mapper.rb +3 -2
- data/lib/mack-data_mapper_tasks.rb +7 -0
- metadata +235 -86
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
gem 'dm-core', '~>0.9.7'
|
|
3
|
+
require 'dm-core'
|
|
4
|
+
require 'benchmark'
|
|
5
|
+
require File.dirname(__FILE__) + '/sql'
|
|
6
|
+
|
|
7
|
+
module DataMapper
|
|
8
|
+
class DuplicateMigrationNameError < StandardError
|
|
9
|
+
def initialize(migration)
|
|
10
|
+
super("Duplicate Migration Name: '#{migration.name}', version: #{migration.position}")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Migration
|
|
15
|
+
include SQL
|
|
16
|
+
|
|
17
|
+
attr_accessor :position, :name, :database, :adapter
|
|
18
|
+
|
|
19
|
+
def initialize( position, name, opts = {}, &block )
|
|
20
|
+
@position, @name = position, name
|
|
21
|
+
@options = opts
|
|
22
|
+
|
|
23
|
+
@database = DataMapper.repository(@options[:database] || :default)
|
|
24
|
+
@adapter = @database.adapter
|
|
25
|
+
|
|
26
|
+
case @adapter.class.to_s
|
|
27
|
+
when /Sqlite3/ then @adapter.extend(SQL::Sqlite3)
|
|
28
|
+
when /Mysql/ then @adapter.extend(SQL::Mysql)
|
|
29
|
+
when /Postgres/ then @adapter.extend(SQL::Postgresql)
|
|
30
|
+
else
|
|
31
|
+
raise "Unsupported Migration Adapter #{@adapter.class}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@verbose = @options.has_key?(:verbose) ? @options[:verbose] : true
|
|
35
|
+
|
|
36
|
+
@up_action = lambda {}
|
|
37
|
+
@down_action = lambda {}
|
|
38
|
+
|
|
39
|
+
instance_eval &block
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# define the actions that should be performed on an up migration
|
|
43
|
+
def up(&block)
|
|
44
|
+
@up_action = block
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# define the actions that should be performed on a down migration
|
|
48
|
+
def down(&block)
|
|
49
|
+
@down_action = block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# perform the migration by running the code in the #up block
|
|
53
|
+
def perform_up
|
|
54
|
+
result = nil
|
|
55
|
+
if needs_up?
|
|
56
|
+
# TODO: fix this so it only does transactions for databases that support create/drop
|
|
57
|
+
# database.transaction.commit do
|
|
58
|
+
say_with_time "== Performing Up Migration ##{position}: #{name}", 0 do
|
|
59
|
+
result = @up_action.call
|
|
60
|
+
end
|
|
61
|
+
update_migration_info(:up)
|
|
62
|
+
# end
|
|
63
|
+
end
|
|
64
|
+
result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# un-do the migration by running the code in the #down block
|
|
68
|
+
def perform_down
|
|
69
|
+
result = nil
|
|
70
|
+
if needs_down?
|
|
71
|
+
# TODO: fix this so it only does transactions for databases that support create/drop
|
|
72
|
+
# database.transaction.commit do
|
|
73
|
+
say_with_time "== Performing Down Migration ##{position}: #{name}", 0 do
|
|
74
|
+
result = @down_action.call
|
|
75
|
+
end
|
|
76
|
+
update_migration_info(:down)
|
|
77
|
+
# end
|
|
78
|
+
end
|
|
79
|
+
result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# execute raw SQL
|
|
83
|
+
def execute(sql, *bind_values)
|
|
84
|
+
say_with_time(sql) do
|
|
85
|
+
@adapter.execute(sql, *bind_values)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def create_table(table_name, opts = {}, &block)
|
|
90
|
+
execute TableCreator.new(@adapter, table_name, opts, &block).to_sql
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def drop_table(table_name, opts = {})
|
|
94
|
+
execute "DROP TABLE #{@adapter.send(:quote_table_name, table_name.to_s)}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def modify_table(table_name, opts = {}, &block)
|
|
98
|
+
TableModifier.new(@adapter, table_name, opts, &block).statements.each do |sql|
|
|
99
|
+
execute(sql)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def create_index(table_name, *columns_and_options)
|
|
104
|
+
if columns_and_options.last.is_a?(Hash)
|
|
105
|
+
opts = columns_and_options.pop
|
|
106
|
+
else
|
|
107
|
+
opts = {}
|
|
108
|
+
end
|
|
109
|
+
columns = columns_and_options.flatten
|
|
110
|
+
|
|
111
|
+
opts[:name] ||= "#{opts[:unique] ? 'unique_' : ''}index_#{table_name}_#{columns.join('_')}"
|
|
112
|
+
|
|
113
|
+
execute <<-SQL.compress_lines
|
|
114
|
+
CREATE #{opts[:unique] ? 'UNIQUE ' : '' }INDEX #{quote_column_name(opts[:name])} ON
|
|
115
|
+
#{quote_table_name(table_name)} (#{columns.map { |c| quote_column_name(c) }.join(', ') })
|
|
116
|
+
SQL
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Orders migrations by position, so we know what order to run them in.
|
|
120
|
+
# First order by postition, then by name, so at least the order is predictable.
|
|
121
|
+
def <=> other
|
|
122
|
+
if self.position == other.position
|
|
123
|
+
self.name.to_s <=> other.name.to_s
|
|
124
|
+
else
|
|
125
|
+
self.position <=> other.position
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Output some text. Optional indent level
|
|
130
|
+
def say(message, indent = 4)
|
|
131
|
+
write "#{" " * indent} #{message}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Time how long the block takes to run, and output it with the message.
|
|
135
|
+
def say_with_time(message, indent = 2)
|
|
136
|
+
say(message, indent)
|
|
137
|
+
result = nil
|
|
138
|
+
time = Benchmark.measure { result = yield }
|
|
139
|
+
say("-> %.4fs" % time.real, indent)
|
|
140
|
+
result
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# output the given text, but only if verbose mode is on
|
|
144
|
+
def write(text="")
|
|
145
|
+
puts text if @verbose
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Inserts or removes a row into the `migration_info` table, so we can mark this migration as run, or un-done
|
|
149
|
+
def update_migration_info(direction)
|
|
150
|
+
save, @verbose = @verbose, false
|
|
151
|
+
|
|
152
|
+
create_migration_info_table_if_needed
|
|
153
|
+
|
|
154
|
+
if direction.to_sym == :up
|
|
155
|
+
execute("INSERT INTO #{migration_info_table} (#{migration_name_column}) VALUES (#{quoted_name})")
|
|
156
|
+
elsif direction.to_sym == :down
|
|
157
|
+
execute("DELETE FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
|
|
158
|
+
end
|
|
159
|
+
@verbose = save
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def create_migration_info_table_if_needed
|
|
163
|
+
save, @verbose = @verbose, false
|
|
164
|
+
unless migration_info_table_exists?
|
|
165
|
+
execute("CREATE TABLE #{migration_info_table} (#{migration_name_column} VARCHAR(255) UNIQUE)")
|
|
166
|
+
end
|
|
167
|
+
@verbose = save
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Quote the name of the migration for use in SQL
|
|
171
|
+
def quoted_name
|
|
172
|
+
"'#{name}'"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def migration_info_table_exists?
|
|
176
|
+
adapter.storage_exists?('migration_info')
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Fetch the record for this migration out of the migration_info table
|
|
180
|
+
def migration_record
|
|
181
|
+
return [] unless migration_info_table_exists?
|
|
182
|
+
@adapter.query("SELECT #{migration_name_column} FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# True if the migration needs to be run
|
|
186
|
+
def needs_up?
|
|
187
|
+
return true unless migration_info_table_exists?
|
|
188
|
+
migration_record.empty?
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# True if the migration has already been run
|
|
192
|
+
def needs_down?
|
|
193
|
+
return false unless migration_info_table_exists?
|
|
194
|
+
! migration_record.empty?
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Quoted table name, for the adapter
|
|
198
|
+
def migration_info_table
|
|
199
|
+
@migration_info_table ||= @adapter.send(:quote_table_name, 'migration_info')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Quoted `migration_name` column, for the adapter
|
|
203
|
+
def migration_name_column
|
|
204
|
+
@migration_name_column ||= @adapter.send(:quote_column_name, 'migration_name')
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def quote_table_name(table_name)
|
|
208
|
+
@adapter.send(:quote_table_name, table_name.to_s)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def quote_column_name(column_name)
|
|
212
|
+
@adapter.send(:quote_column_name, column_name.to_s)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/migration'
|
|
2
|
+
|
|
3
|
+
module DataMapper
|
|
4
|
+
module MigrationRunner
|
|
5
|
+
@@migrations ||= []
|
|
6
|
+
|
|
7
|
+
# Creates a new migration, and adds it to the list of migrations to be run.
|
|
8
|
+
# Migrations can be defined in any order, they will be sorted and run in the
|
|
9
|
+
# correct order.
|
|
10
|
+
#
|
|
11
|
+
# The order that migrations are run in is set by the first argument. It is not
|
|
12
|
+
# neccessary that this be unique; migrations with the same version number are
|
|
13
|
+
# expected to be able to be run in any order.
|
|
14
|
+
#
|
|
15
|
+
# The second argument is the name of the migration. This name is used internally
|
|
16
|
+
# to track if the migration has been run. It is required that this name be unique
|
|
17
|
+
# across all migrations.
|
|
18
|
+
#
|
|
19
|
+
# Addtionally, it accepts a number of options:
|
|
20
|
+
# * <tt>:database</tt> If you defined several DataMapper::database instances use this
|
|
21
|
+
# to choose which one to run the migration gagainst. Defaults to <tt>:default</tt>.
|
|
22
|
+
# Migrations are tracked individually per database.
|
|
23
|
+
# * <tt>:verbose</tt> true/false, defaults to true. Determines if the migration should
|
|
24
|
+
# output its status messages when it runs.
|
|
25
|
+
#
|
|
26
|
+
# Example of a simple migration:
|
|
27
|
+
#
|
|
28
|
+
# migration( 1, :create_people_table ) do
|
|
29
|
+
# up do
|
|
30
|
+
# create_table :people do
|
|
31
|
+
# column :id, Integer, :serial => true
|
|
32
|
+
# column :name, String, :size => 50
|
|
33
|
+
# column :age, Integer
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
# down do
|
|
37
|
+
# drop_table :people
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# Its recommended that you stick with raw SQL for migrations that manipulate data. If
|
|
42
|
+
# you write a migration using a model, then later change the model, there's a
|
|
43
|
+
# possibility the migration will no longer work. Using SQL will always work.
|
|
44
|
+
def migration( number, name, opts = {}, &block )
|
|
45
|
+
@@migrations ||= []
|
|
46
|
+
raise "Migration name conflict: '#{name}'" if @@migrations.map { |m| m.name }.include?(name.to_s)
|
|
47
|
+
|
|
48
|
+
@@migrations << DataMapper::Migration.new( number, name.to_s, opts, &block )
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Run all migrations that need to be run. In most cases, this would be called by a
|
|
52
|
+
# rake task as part of a larger project, but this provides the ability to run them
|
|
53
|
+
# in a script or test.
|
|
54
|
+
#
|
|
55
|
+
# has an optional argument 'level' which if supplied, only performs the migrations
|
|
56
|
+
# with a position less than or equal to the level.
|
|
57
|
+
def migrate_up!(level = nil)
|
|
58
|
+
@@migrations.sort.each do |migration|
|
|
59
|
+
if level.nil?
|
|
60
|
+
migration.perform_up()
|
|
61
|
+
else
|
|
62
|
+
migration.perform_up() if migration.position <= level.to_i
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Run all the down steps for the migrations that have already been run.
|
|
68
|
+
#
|
|
69
|
+
# has an optional argument 'level' which, if supplied, only performs the
|
|
70
|
+
# down migrations with a postion greater than the level.
|
|
71
|
+
def migrate_down!(level = nil)
|
|
72
|
+
@@migrations.sort.reverse.each do |migration|
|
|
73
|
+
if level.nil?
|
|
74
|
+
migration.perform_down()
|
|
75
|
+
else
|
|
76
|
+
migration.perform_down() if migration.position > level.to_i
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def migrations
|
|
82
|
+
@@migrations
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
include DataMapper::MigrationRunner
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
|
|
3
|
+
require File.dirname(__FILE__) + '/../matchers/migration_matchers'
|
|
4
|
+
|
|
5
|
+
module Spec
|
|
6
|
+
module Example
|
|
7
|
+
class MigrationExampleGroup < Spec::Example::ExampleGroup
|
|
8
|
+
include Spec::Matchers::Migration
|
|
9
|
+
|
|
10
|
+
before(:all) do
|
|
11
|
+
if this_migration.adapter.supports_schema_transactions?
|
|
12
|
+
run_prereq_migrations
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
before(:each) do
|
|
17
|
+
if ! this_migration.adapter.supports_schema_transactions?
|
|
18
|
+
run_prereq_migrations
|
|
19
|
+
else
|
|
20
|
+
this_migration.adapter.begin_transaction
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
after(:each) do
|
|
25
|
+
if this_migration.adapter.supports_schema_transactions?
|
|
26
|
+
this_migration.adapter.rollback_transaction
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
after(:all) do
|
|
31
|
+
this_migration.adapter.recreate_database
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run_prereq_migrations
|
|
35
|
+
"running n-1 migrations"
|
|
36
|
+
all_databases.each do |db|
|
|
37
|
+
db.adapter.recreate_database
|
|
38
|
+
end
|
|
39
|
+
@@migrations.sort.each do |migration|
|
|
40
|
+
break if migration.name.to_s == migration_name.to_s
|
|
41
|
+
migration.perform_up
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def run_migration
|
|
46
|
+
this_migration.perform_up
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def migration_name
|
|
50
|
+
@migration_name ||= self.class.instance_variable_get("@description_text").to_s
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def all_databases
|
|
54
|
+
@@migrations.map { |m| m.database }.uniq
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def this_migration
|
|
58
|
+
@@migrations.select { |m| m.name.to_s == migration_name }.first
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def query(sql)
|
|
62
|
+
this_migration.adapter.query(sql)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def table(table_name)
|
|
66
|
+
this_migration.adapter.table(table_name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
Spec::Example::ExampleGroupFactory.register(:migration, self)
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
|
|
2
|
+
module Spec
|
|
3
|
+
module Matchers
|
|
4
|
+
module Migration
|
|
5
|
+
|
|
6
|
+
def have_table(table_name)
|
|
7
|
+
HaveTableMatcher.new(table_name)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def have_column(column_name)
|
|
11
|
+
HaveColumnMatcher.new(column_name)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def permit_null
|
|
15
|
+
NullableColumnMatcher.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def be_primary_key
|
|
19
|
+
PrimaryKeyMatcher.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class HaveTableMatcher
|
|
23
|
+
|
|
24
|
+
attr_accessor :table_name, :repository
|
|
25
|
+
|
|
26
|
+
def initialize(table_name)
|
|
27
|
+
@table_name = table_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def matches?(repository)
|
|
31
|
+
repository.adapter.storage_exists?(table_name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def failure_message
|
|
35
|
+
%(expected #{repository} to have table '#{table_name}')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def negative_failure_message
|
|
39
|
+
%(expected #{repository} to not have table '#{table_name}')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class HaveColumnMatcher
|
|
45
|
+
|
|
46
|
+
attr_accessor :table, :column_name
|
|
47
|
+
|
|
48
|
+
def initialize(column_name)
|
|
49
|
+
@column_name = column_name
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def matches?(table)
|
|
53
|
+
@table = table
|
|
54
|
+
table.columns.map { |c| c.name }.include?(column_name.to_s)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def failure_message
|
|
58
|
+
%(expected #{table} to have column '#{column_name}')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def negative_failure_message
|
|
62
|
+
%(expected #{table} to not have column '#{column_name}')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class NullableColumnMatcher
|
|
68
|
+
|
|
69
|
+
attr_accessor :column
|
|
70
|
+
|
|
71
|
+
def matches?(column)
|
|
72
|
+
@column = column
|
|
73
|
+
! column.not_null
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def failure_message
|
|
77
|
+
%(expected #{column.name} to permit NULL)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def negative_failure_message
|
|
81
|
+
%(expected #{column.name} to be NOT NULL)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class PrimaryKeyMatcher
|
|
87
|
+
|
|
88
|
+
attr_accessor :column
|
|
89
|
+
|
|
90
|
+
def matches?(column)
|
|
91
|
+
@column = column
|
|
92
|
+
column.primary_key
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def failure_message
|
|
96
|
+
%(expected #{column.name} to be PRIMARY KEY)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def negative_failure_message
|
|
100
|
+
%(expected #{column.name} to not be PRIMARY KEY)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/table'
|
|
2
|
+
|
|
3
|
+
module SQL
|
|
4
|
+
module Mysql
|
|
5
|
+
|
|
6
|
+
def supports_schema_transactions?
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def table(table_name)
|
|
11
|
+
SQL::Mysql::Table.new(self, table_name)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def recreate_database
|
|
15
|
+
execute "DROP DATABASE #{db_name}"
|
|
16
|
+
execute "CREATE DATABASE #{db_name}"
|
|
17
|
+
execute "USE #{db_name}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def supports_serial?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# TODO: move to dm-more/dm-migrations
|
|
25
|
+
def property_schema_statement(schema)
|
|
26
|
+
if supports_serial? && schema[:serial]
|
|
27
|
+
statement = "#{schema[:quote_column_name]} serial PRIMARY KEY"
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Table
|
|
34
|
+
def initialize(adapter, table_name)
|
|
35
|
+
@columns = []
|
|
36
|
+
adapter.query_table(table_name).each do |col_struct|
|
|
37
|
+
@columns << SQL::Mysql::Column.new(col_struct)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Column
|
|
43
|
+
def initialize(col_struct)
|
|
44
|
+
@name, @type, @default_value, @primary_key = col_struct.name, col_struct.type, col_struct.dflt_value, col_struct.pk
|
|
45
|
+
|
|
46
|
+
@not_null = col_struct.notnull == 0
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module SQL
|
|
2
|
+
module Postgresql
|
|
3
|
+
|
|
4
|
+
def supports_schema_transactions?
|
|
5
|
+
true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def table(table_name)
|
|
9
|
+
SQL::Postgresql::Table.new(self, table_name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def recreate_database
|
|
13
|
+
execute "DROP SCHEMA IF EXISTS test CASCADE"
|
|
14
|
+
execute "CREATE SCHEMA test"
|
|
15
|
+
execute "SET search_path TO test"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def supports_serial?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def property_schema_statement(schema)
|
|
23
|
+
if supports_serial? && schema[:serial]
|
|
24
|
+
statement = "#{schema[:quote_column_name]} serial PRIMARY KEY"
|
|
25
|
+
else
|
|
26
|
+
statement = super
|
|
27
|
+
if schema.has_key?(:sequence_name)
|
|
28
|
+
statement << " DEFAULT nextval('#{schema[:sequence_name]}') NOT NULL"
|
|
29
|
+
end
|
|
30
|
+
statement
|
|
31
|
+
end
|
|
32
|
+
statement
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Table < SQL::Table
|
|
36
|
+
def initialize(adapter, table_name)
|
|
37
|
+
@adapter, @name = adapter, table_name
|
|
38
|
+
@columns = []
|
|
39
|
+
adapter.query_table(table_name).each do |col_struct|
|
|
40
|
+
@columns << SQL::Postgresql::Column.new(col_struct)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
query_column_constraints
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def query_column_constraints
|
|
47
|
+
@adapter.query(
|
|
48
|
+
"SELECT * FROM information_schema.table_constraints WHERE table_name='#{@name}' AND table_schema=current_schema()"
|
|
49
|
+
).each do |table_constraint|
|
|
50
|
+
@adapter.query(
|
|
51
|
+
"SELECT * FROM information_schema.constraint_column_usage WHERE constraint_name='#{table_constraint.constraint_name}' AND table_schema=current_schema()"
|
|
52
|
+
).each do |constrained_column|
|
|
53
|
+
@columns.each do |column|
|
|
54
|
+
if column.name == constrained_column.column_name
|
|
55
|
+
case table_constraint.constraint_type
|
|
56
|
+
when "UNIQUE" then column.unique = true
|
|
57
|
+
when "PRIMARY KEY" then column.primary_key = true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class Column < SQL::Column
|
|
69
|
+
def initialize(col_struct)
|
|
70
|
+
@name, @type, @default_value = col_struct.column_name, col_struct.data_type, col_struct.column_default
|
|
71
|
+
|
|
72
|
+
@not_null = col_struct.is_nullable != "YES"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/table'
|
|
2
|
+
|
|
3
|
+
module SQL
|
|
4
|
+
module Sqlite3
|
|
5
|
+
|
|
6
|
+
def supports_schema_transactions?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def table(table_name)
|
|
11
|
+
SQL::Sqlite3::Table.new(self, table_name)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def recreate_database
|
|
15
|
+
DataMapper.logger.info "Dropping #{@uri.path}"
|
|
16
|
+
system "rm #{@uri.path}"
|
|
17
|
+
# do nothing, sqlite will automatically create the database file
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def supports_serial?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Table < SQL::Table
|
|
25
|
+
def initialize(adapter, table_name)
|
|
26
|
+
@columns = []
|
|
27
|
+
adapter.query_table(table_name).each do |col_struct|
|
|
28
|
+
@columns << SQL::Sqlite3::Column.new(col_struct)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Column < SQL::Column
|
|
34
|
+
def initialize(col_struct)
|
|
35
|
+
@name, @type, @default_value, @primary_key = col_struct.name, col_struct.type, col_struct.dflt_value, col_struct.pk
|
|
36
|
+
|
|
37
|
+
@not_null = col_struct.notnull == 0
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/column'
|
|
2
|
+
|
|
3
|
+
module SQL
|
|
4
|
+
|
|
5
|
+
class Table
|
|
6
|
+
|
|
7
|
+
attr_accessor :name, :columns
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def column(column_name)
|
|
14
|
+
@columns.select { |c| c.name == column_name.to_s }.first
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|