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.
Files changed (166) hide show
  1. data/lib/gems/addressable-2.0.0/lib/addressable/idna.rb +4867 -0
  2. data/lib/gems/addressable-2.0.0/lib/addressable/uri.rb +2469 -0
  3. data/lib/gems/addressable-2.0.0/lib/addressable/version.rb +35 -0
  4. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/adapters/data_objects_adapter.rb +85 -0
  5. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/aggregate_functions.rb +201 -0
  6. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/collection.rb +11 -0
  7. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/model.rb +11 -0
  8. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/repository.rb +7 -0
  9. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/support/symbol.rb +21 -0
  10. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/version.rb +7 -0
  11. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates.rb +15 -0
  12. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  13. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/data_objects_adapter.rb +709 -0
  14. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  15. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  16. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  17. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  18. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters.rb +22 -0
  19. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_many.rb +147 -0
  20. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_one.rb +107 -0
  21. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb +318 -0
  22. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_one.rb +61 -0
  23. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship.rb +223 -0
  24. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship_chain.rb +81 -0
  25. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations.rb +200 -0
  26. data/lib/gems/dm-core-0.9.7/lib/dm-core/auto_migrations.rb +105 -0
  27. data/lib/gems/dm-core-0.9.7/lib/dm-core/collection.rb +642 -0
  28. data/lib/gems/dm-core-0.9.7/lib/dm-core/dependency_queue.rb +32 -0
  29. data/lib/gems/dm-core-0.9.7/lib/dm-core/hook.rb +11 -0
  30. data/lib/gems/dm-core-0.9.7/lib/dm-core/identity_map.rb +42 -0
  31. data/lib/gems/dm-core-0.9.7/lib/dm-core/is.rb +16 -0
  32. data/lib/gems/dm-core-0.9.7/lib/dm-core/logger.rb +232 -0
  33. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrator.rb +29 -0
  35. data/lib/gems/dm-core-0.9.7/lib/dm-core/model.rb +488 -0
  36. data/lib/gems/dm-core-0.9.7/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/gems/dm-core-0.9.7/lib/dm-core/property.rb +663 -0
  38. data/lib/gems/dm-core-0.9.7/lib/dm-core/property_set.rb +169 -0
  39. data/lib/gems/dm-core-0.9.7/lib/dm-core/query.rb +628 -0
  40. data/lib/gems/dm-core-0.9.7/lib/dm-core/repository.rb +159 -0
  41. data/lib/gems/dm-core-0.9.7/lib/dm-core/resource.rb +637 -0
  42. data/lib/gems/dm-core-0.9.7/lib/dm-core/scope.rb +58 -0
  43. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/array.rb +13 -0
  44. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/assertions.rb +8 -0
  45. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/errors.rb +23 -0
  46. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/kernel.rb +11 -0
  47. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/symbol.rb +41 -0
  48. data/lib/gems/dm-core-0.9.7/lib/dm-core/support.rb +7 -0
  49. data/lib/gems/dm-core-0.9.7/lib/dm-core/transaction.rb +267 -0
  50. data/lib/gems/dm-core-0.9.7/lib/dm-core/type.rb +160 -0
  51. data/lib/gems/dm-core-0.9.7/lib/dm-core/type_map.rb +80 -0
  52. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/boolean.rb +7 -0
  53. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/discriminator.rb +34 -0
  54. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/object.rb +24 -0
  55. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_boolean.rb +34 -0
  56. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_datetime.rb +33 -0
  57. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/serial.rb +9 -0
  58. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/text.rb +10 -0
  59. data/lib/gems/dm-core-0.9.7/lib/dm-core/types.rb +19 -0
  60. data/lib/gems/dm-core-0.9.7/lib/dm-core/version.rb +3 -0
  61. data/lib/gems/dm-core-0.9.7/lib/dm-core.rb +217 -0
  62. data/lib/gems/dm-core-0.9.7/script/all +5 -0
  63. data/lib/gems/dm-core-0.9.7/script/performance.rb +284 -0
  64. data/lib/gems/dm-core-0.9.7/script/profile.rb +87 -0
  65. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations/version.rb +5 -0
  66. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations.rb +1 -0
  67. data/lib/gems/dm-migrations-0.9.7/lib/migration.rb +215 -0
  68. data/lib/gems/dm-migrations-0.9.7/lib/migration_runner.rb +88 -0
  69. data/lib/gems/dm-migrations-0.9.7/lib/spec/example/migration_example_group.rb +73 -0
  70. data/lib/gems/dm-migrations-0.9.7/lib/spec/matchers/migration_matchers.rb +107 -0
  71. data/lib/gems/dm-migrations-0.9.7/lib/sql/column.rb +9 -0
  72. data/lib/gems/dm-migrations-0.9.7/lib/sql/mysql.rb +52 -0
  73. data/lib/gems/dm-migrations-0.9.7/lib/sql/postgresql.rb +78 -0
  74. data/lib/gems/dm-migrations-0.9.7/lib/sql/sqlite3.rb +43 -0
  75. data/lib/gems/dm-migrations-0.9.7/lib/sql/table.rb +19 -0
  76. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_creator.rb +81 -0
  77. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_modifier.rb +53 -0
  78. data/lib/gems/dm-migrations-0.9.7/lib/sql.rb +10 -0
  79. data/lib/gems/dm-observer-0.9.7/lib/dm-observer/version.rb +5 -0
  80. data/lib/gems/dm-observer-0.9.7/lib/dm-observer.rb +91 -0
  81. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer/version.rb +5 -0
  82. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer.rb +183 -0
  83. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps/version.rb +5 -0
  84. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps.rb +57 -0
  85. data/lib/gems/dm-types-0.9.7/lib/dm-types/bcrypt_hash.rb +31 -0
  86. data/lib/gems/dm-types-0.9.7/lib/dm-types/csv.rb +28 -0
  87. data/lib/gems/dm-types-0.9.7/lib/dm-types/enum.rb +70 -0
  88. data/lib/gems/dm-types-0.9.7/lib/dm-types/epoch_time.rb +27 -0
  89. data/lib/gems/dm-types-0.9.7/lib/dm-types/file_path.rb +27 -0
  90. data/lib/gems/dm-types-0.9.7/lib/dm-types/flag.rb +61 -0
  91. data/lib/gems/dm-types-0.9.7/lib/dm-types/ip_address.rb +30 -0
  92. data/lib/gems/dm-types-0.9.7/lib/dm-types/json.rb +40 -0
  93. data/lib/gems/dm-types-0.9.7/lib/dm-types/regexp.rb +20 -0
  94. data/lib/gems/dm-types-0.9.7/lib/dm-types/serial.rb +8 -0
  95. data/lib/gems/dm-types-0.9.7/lib/dm-types/slug.rb +37 -0
  96. data/lib/gems/dm-types-0.9.7/lib/dm-types/uri.rb +29 -0
  97. data/lib/gems/dm-types-0.9.7/lib/dm-types/uuid.rb +64 -0
  98. data/lib/gems/dm-types-0.9.7/lib/dm-types/version.rb +5 -0
  99. data/lib/gems/dm-types-0.9.7/lib/dm-types/yaml.rb +36 -0
  100. data/lib/gems/dm-types-0.9.7/lib/dm-types.rb +28 -0
  101. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/absent_field_validator.rb +60 -0
  102. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/acceptance_validator.rb +76 -0
  103. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/auto_validate.rb +153 -0
  104. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/block_validator.rb +60 -0
  105. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/confirmation_validator.rb +80 -0
  106. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/contextual_validators.rb +56 -0
  107. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/custom_validator.rb +72 -0
  108. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/format_validator.rb +97 -0
  109. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/email.rb +40 -0
  110. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/url.rb +20 -0
  111. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/generic_validator.rb +100 -0
  112. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/length_validator.rb +113 -0
  113. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/method_validator.rb +68 -0
  114. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/numeric_validator.rb +83 -0
  115. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/primitive_validator.rb +60 -0
  116. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/required_field_validator.rb +88 -0
  117. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/support/object.rb +5 -0
  118. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/uniqueness_validator.rb +64 -0
  119. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/validation_errors.rb +63 -0
  120. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/version.rb +5 -0
  121. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/within_validator.rb +53 -0
  122. data/lib/gems/dm-validations-0.9.7/lib/dm-validations.rb +234 -0
  123. data/lib/gems/json_pure-1.1.3/GPL +340 -0
  124. data/lib/gems/json_pure-1.1.3/VERSION +1 -0
  125. data/lib/gems/json_pure-1.1.3/bin/edit_json.rb +10 -0
  126. data/lib/gems/json_pure-1.1.3/bin/prettify_json.rb +76 -0
  127. data/lib/gems/json_pure-1.1.3/lib/json/Array.xpm +21 -0
  128. data/lib/gems/json_pure-1.1.3/lib/json/FalseClass.xpm +21 -0
  129. data/lib/gems/json_pure-1.1.3/lib/json/Hash.xpm +21 -0
  130. data/lib/gems/json_pure-1.1.3/lib/json/Key.xpm +73 -0
  131. data/lib/gems/json_pure-1.1.3/lib/json/NilClass.xpm +21 -0
  132. data/lib/gems/json_pure-1.1.3/lib/json/Numeric.xpm +28 -0
  133. data/lib/gems/json_pure-1.1.3/lib/json/String.xpm +96 -0
  134. data/lib/gems/json_pure-1.1.3/lib/json/TrueClass.xpm +21 -0
  135. data/lib/gems/json_pure-1.1.3/lib/json/add/core.rb +135 -0
  136. data/lib/gems/json_pure-1.1.3/lib/json/add/rails.rb +58 -0
  137. data/lib/gems/json_pure-1.1.3/lib/json/common.rb +354 -0
  138. data/lib/gems/json_pure-1.1.3/lib/json/editor.rb +1362 -0
  139. data/lib/gems/json_pure-1.1.3/lib/json/ext.rb +13 -0
  140. data/lib/gems/json_pure-1.1.3/lib/json/json.xpm +1499 -0
  141. data/lib/gems/json_pure-1.1.3/lib/json/pure/generator.rb +394 -0
  142. data/lib/gems/json_pure-1.1.3/lib/json/pure/parser.rb +259 -0
  143. data/lib/gems/json_pure-1.1.3/lib/json/pure.rb +75 -0
  144. data/lib/gems/json_pure-1.1.3/lib/json/version.rb +9 -0
  145. data/lib/gems/json_pure-1.1.3/lib/json.rb +235 -0
  146. data/lib/gems/launchy-0.3.2/bin/launchy +12 -0
  147. data/lib/gems/launchy-0.3.2/lib/launchy/application.rb +163 -0
  148. data/lib/gems/launchy-0.3.2/lib/launchy/browser.rb +85 -0
  149. data/lib/gems/launchy-0.3.2/lib/launchy/command_line.rb +48 -0
  150. data/lib/gems/launchy-0.3.2/lib/launchy/gemspec.rb +53 -0
  151. data/lib/gems/launchy-0.3.2/lib/launchy/specification.rb +133 -0
  152. data/lib/gems/launchy-0.3.2/lib/launchy/version.rb +18 -0
  153. data/lib/gems/launchy-0.3.2/lib/launchy.rb +58 -0
  154. data/lib/gems/uuidtools-1.0.3/lib/uuidtools/version.rb +32 -0
  155. data/lib/gems/uuidtools-1.0.3/lib/uuidtools.rb +648 -0
  156. data/lib/gems.rb +13 -0
  157. data/lib/mack-data_mapper/migration_generator/migration_generator.rb +5 -0
  158. data/lib/mack-data_mapper/migration_generator/templates/db/migrations/%=@migration_name%.rb.template +1 -1
  159. data/lib/mack-data_mapper/model_generator/manifest.yml +3 -3
  160. data/lib/mack-data_mapper/model_generator/model_generator.rb +8 -1
  161. data/lib/mack-data_mapper/model_generator/templates/model.rb.template +1 -1
  162. data/lib/mack-data_mapper/model_generator/templates/rspec.rb.template +1 -1
  163. data/lib/mack-data_mapper/model_generator/templates/test_case.rb.template +1 -1
  164. data/lib/mack-data_mapper.rb +3 -2
  165. data/lib/mack-data_mapper_tasks.rb +7 -0
  166. 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,9 @@
1
+ module SQL
2
+
3
+ class Column
4
+
5
+ attr_accessor :name, :type, :not_null, :default_value, :primary_key, :unique
6
+
7
+ end
8
+
9
+ 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