mini_record-cj 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 27896303273b7579110fcaca700f5ad8573606f6
4
+ data.tar.gz: 411456a93af4970469f7f7d7d4f5d9012518016a
5
+ SHA512:
6
+ metadata.gz: 6d6ae3f5c75e2041ad1787ca7ff2a4b0d69b46133c46c3a12b7fee9608f05a9c6e6f6bffb298adc29ce85abd4e8f5b7a16a395d21f7c42d77e7ee52e7c29034b
7
+ data.tar.gz: 4ee3159eee1475baa6764fc7e4f9781208222b94ca1f05a384255f40735a9e3d0542b690876d062f1346281217590a0051c46fc4b0a02c3814e3d1bb679ab9e8
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
6
+ *.swp
7
+ *.swo
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ before_install: 'gem update --system'
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - rbx-18mode
8
+ - rbx
9
+ - ree
10
+ notifications:
11
+ recipients:
12
+ - info@daddye.it
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mini_record.gemspec
4
+ gem 'rake'
5
+ gem 'minitest'
6
+ gem 'sqlite3'
7
+ gem 'mysql2'
8
+ gem 'mysql'
9
+ gem 'pg'
10
+ gem 'activerecord', '<= 3.2'
11
+
12
+ group :test do
13
+ gem 'foreigner', '>= 1.4.2'
14
+ end
15
+
16
+ gemspec
@@ -0,0 +1,198 @@
1
+ [![Build Status](https://secure.travis-ci.org/DAddYE/mini_record.png)](http://travis-ci.org/DAddYE/mini_record)
2
+
3
+
4
+ MiniRecord is a micro extension for our `ActiveRecord` gem.
5
+ With MiniRecord you can add the ability to create columns outside the default `schema.rb`, directly
6
+ in your **model** in a similar way that should know in others projects
7
+ like DataMapper, MongoMapper or MongoID.
8
+
9
+ My inspiration come from this handy [project](https://github.com/pjhyett/auto_migrations).
10
+
11
+ ## Features
12
+
13
+ * Define columns/properties inside your model
14
+ * Perform migrations automatically
15
+ * Auto upgrade your schema, so if you know what you are doing you don't lose your existing data!
16
+ * Add, Remove, Change Columns; Add, Remove, Change indexes
17
+
18
+ ## Instructions
19
+
20
+ What you need is to move/remove your `db/schema.rb`.
21
+ This avoid conflicts.
22
+
23
+ Add to your `Gemfile`:
24
+
25
+ ``` rb
26
+ gem 'mini_record'
27
+ ```
28
+
29
+ That's all!
30
+
31
+ ## Examples
32
+
33
+ Remember that inside properties you can use all migrations methods,
34
+ see [documentation](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html)
35
+
36
+ ``` rb
37
+ class Post < ActiveRecord::Base
38
+ col :title_en, :title_jp
39
+ col :description_en, :description_jp, :as => :text
40
+ col :permalink, :index => true, :limit => 50
41
+ col :comments_count, :as => :integer
42
+ col :category, :as => :references, :index => true
43
+ end
44
+ Post.auto_upgrade!
45
+ ```
46
+
47
+ If you don't like `col` there are also few aliases: `key, field, property, attribute`
48
+
49
+ Instead of `:as => :my_type` you can use `:type => :my_type`
50
+
51
+ Option `:as` or `:type` if not provided is `:string` by default, you can use all ActiveRecord types:
52
+
53
+ ``` rb
54
+ :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time,
55
+ :date, :binary, :boolean, :references, :belongs_to, :timestamp
56
+ ```
57
+
58
+ You can provide others ActiveRecord options like:
59
+
60
+ ``` rb
61
+ :limit, :default, :null, :precision, :scale
62
+
63
+ # example
64
+ class Foo < ActiveRecord::Base
65
+ col :title, :default => "MyTitle" # :as => :string is by default
66
+ col :price, :as => :decimal, :scale => 8, :precision => 2
67
+ end
68
+ ```
69
+
70
+ See [ActiveRecord::TableDefinition](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html)
71
+ for more details.
72
+
73
+ Finally, when you execute `MyModel.auto_upgrade!`, missing columns, indexes and tables will be created on the fly.
74
+ Indexes and columns present in the db but **not** in your model schema will be **deleted*** also in your db.
75
+
76
+ ### Single Table Inheritance
77
+
78
+ MiniRecord as ActiveRecord support STI plus some goodness, see our specs for more details.
79
+
80
+ ### ActiveRecord Relations
81
+
82
+ MiniRecord has built-in support of belongs_to, belongs_to polymorphic and habtm relations. Just declaring these in your model will generate the necessary id columns, indexes and join tables
83
+
84
+ #### belongs_to
85
+ ```ruby
86
+ class Address < ActiveRecord::Base
87
+ belongs_to :person
88
+ end
89
+ ```
90
+ Will result in a person_id column (you can override with the `foreign_key` option) which is indexed
91
+
92
+ #### belongs_to with foreign key in database
93
+ ```ruby
94
+ class Address < ActiveRecord::Base
95
+ belongs_to :person
96
+ index :person_id, :foreign => true
97
+ end
98
+ ```
99
+ The same as in the previous case, but foreign key will be added to the database with help of [foreigner](https://github.com/matthuhiggins/foreigner) gem.
100
+
101
+ To remove the key please use :foreign => false
102
+ If you simple remove the index, the foreign key will not be removed.
103
+
104
+ #### belongs_to (polymorphic)
105
+ ```ruby
106
+ class Address < ActiveRecord::Base
107
+ belongs_to :addressable, :polymorphic => true
108
+ end
109
+ ```
110
+ Will result in addressable id and type columns with composite indexes `add_index(:addresses), [:addressable_id, :addressable_type]`
111
+
112
+ #### habtm
113
+ ```ruby
114
+ class Address < ActiveRecord::Base
115
+ has_and_belongs_to_many :people
116
+ end
117
+ ```
118
+ Will generate a "addresses_people" join table and index the id columns
119
+
120
+ ### Adding a new column
121
+
122
+ Super easy, open your model and just add it:
123
+
124
+ ``` rb
125
+ class Post < ActiveRecord::Base
126
+ col :title
127
+ col :body, :as => :text # <<- this
128
+ col :permalink, :index => true
129
+ col :comments_count, :as => :integer
130
+ col :category, :as => :references, :index => true
131
+ end
132
+ Post.auto_upgrade!
133
+ ```
134
+
135
+ So now when you invoke `MyModel.auto_upgrade!` you should see a SQL query like `ALTER TABLE` that mean that your existing
136
+ records are happy and safe.
137
+
138
+ ### Removing a column
139
+
140
+ It's exactly the same, but the column will be _really_ deleted without affect other columns.
141
+
142
+ ### Change columns
143
+
144
+ It's not possible for us know when/what column you have renamed, but we can know if you changed the `type` so
145
+ if you change `t.string :name` to `t.text :name` we are be able to perform an `ALTER TABLE`
146
+
147
+ ### Add/Remove indexes
148
+
149
+ In the same ways we manage columns MiniRecord will detect new indexes and indexes that needs to be removed.
150
+ So when you perform `MyModel.auto_upgrade!` a SQL command like:
151
+
152
+ ``` SQL
153
+ PRAGMA index_info('index_people_on_name')
154
+ CREATE INDEX "index_people_on_surname" ON "people" ("surname")
155
+ ```
156
+
157
+ Note that writing it in DSL way you have same options as `add_index` so you are be able to write:
158
+
159
+ ``` rb
160
+ class Fox < ActiveRecord::Base
161
+ col :foo, :index => true
162
+ col :foo, :index => :custom_name
163
+ col :foo, :index => [:foo, :bar]
164
+ col :foo, :index => { :column => [:branch_id, :party_id], :unique => true, :name => 'by_branch_party' }
165
+ end
166
+ ```
167
+
168
+ That is the same of:
169
+
170
+ ``` rb
171
+ class Fox < ActiveRecord::Base
172
+ col :foo
173
+ add_index :foo
174
+ add_index :custom_name
175
+ add_index [:foo, :bar]
176
+ add_index [:branch_id, :party_id], :unique => true, :name => 'by_branch_party'
177
+ end
178
+ ```
179
+
180
+ ## Author
181
+
182
+ DAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye) or take a look at my site [daddye.it](http://www.daddye.it)
183
+
184
+ ## Copyright
185
+
186
+ Copyright (C) 2011 Davide D'Agostino - [@daddye](http://twitter.com/daddye)
187
+
188
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
189
+ associated documentation files (the “Software”), to deal in the Software without restriction, including without
190
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
191
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
192
+
193
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
194
+
195
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
196
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
197
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
198
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'bundler/gem_tasks'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ %w(install release).each do |task|
7
+ Rake::Task[task].enhance do
8
+ sh "rm -rf pkg"
9
+ end
10
+ end
11
+
12
+ desc "Bump version on github"
13
+ task :bump do
14
+ if `git status -s`.strip == ""
15
+ puts "\e[31mNothing to commit (working directory clean)\e[0m"
16
+ else
17
+ version = Bundler.load_gemspec(Dir[File.expand_path('../*.gemspec', __FILE__)].first).version
18
+ sh "git add .; git commit -a -m \"Bump to version #{version}\""
19
+ end
20
+ end
21
+
22
+ task :release => :bump
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'test'
25
+ test.test_files = Dir['test/**/test_*.rb']
26
+ test.verbose = true
27
+ end
28
+
29
+ task :default => :test
30
+ task :spec => :test
@@ -0,0 +1,5 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'active_record'
3
+ require 'mini_record/auto_schema'
4
+
5
+ ActiveRecord::Base.send(:include, MiniRecord::AutoSchema)
@@ -0,0 +1,364 @@
1
+ module MiniRecord
2
+ module AutoSchema
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ def init_table_definition(connection)
10
+ #connection.create_table(table_name) unless connection.table_exists?(table_name)
11
+
12
+ case ActiveRecord::ConnectionAdapters::TableDefinition.instance_method(:initialize).arity
13
+ when 1
14
+ # Rails 3.2 and earlier
15
+ ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
16
+ when 4, -5
17
+ # Rails 4
18
+ ActiveRecord::ConnectionAdapters::TableDefinition.new(connection.native_database_types, table_name, false, {})
19
+ else
20
+ raise ArgumentError,
21
+ "Unsupported number of args for ActiveRecord::ConnectionAdapters::TableDefinition.new()"
22
+ end
23
+
24
+ end
25
+
26
+ def schema_tables
27
+ @@_schema_tables ||= []
28
+ end
29
+
30
+ def table_definition
31
+ return superclass.table_definition unless (superclass == ActiveRecord::Base) || (superclass.respond_to?(:abstract_class?) && superclass.abstract_class?)
32
+
33
+ @_table_definition ||= begin
34
+ tb = init_table_definition(connection)
35
+ tb.primary_key(primary_key)
36
+ tb
37
+ end
38
+ end
39
+
40
+ def indexes
41
+ return superclass.indexes unless (superclass == ActiveRecord::Base) || (superclass.respond_to?(:abstract_class?) && superclass.abstract_class?)
42
+
43
+ @_indexes ||= {}
44
+ end
45
+
46
+ def indexes_in_db
47
+ connection.indexes(table_name).inject({}) do |hash, index|
48
+ hash[index.name] = index
49
+ hash
50
+ end
51
+ end
52
+
53
+ def get_sql_field_type(field)
54
+ if field.respond_to?(:sql_type)
55
+ # Rails 3.2 and earlier
56
+ field.sql_type.to_s.downcase
57
+ else
58
+ # Rails 4
59
+ connection.type_to_sql(field.type.to_sym, field.limit, field.precision, field.scale)
60
+ end
61
+ end
62
+
63
+ def fields
64
+ table_definition.columns.inject({}) do |hash, column|
65
+ hash[column.name] = column
66
+ hash
67
+ end
68
+ end
69
+
70
+ def fields_in_db
71
+ connection.columns(table_name).inject({}) do |hash, column|
72
+ hash[column.name] = column
73
+ hash
74
+ end
75
+ end
76
+
77
+ def columns_hash
78
+ if mini_record_fake_columns
79
+ super.merge mini_record_fake_columns
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def mini_record_columns
86
+ @@_mr_columns ||= {}
87
+ @@_mr_columns[table_name] ||= {}
88
+ end
89
+
90
+ def mini_record_fake_columns
91
+ @@_mr_fake_columns ||= {}
92
+ @@_mr_fake_columns[table_name] ||= {}
93
+ end
94
+
95
+ def field(*args)
96
+ return unless connection?
97
+
98
+ options = args.extract_options!
99
+ type = options.delete(:as) || options.delete(:type) || :string
100
+ index = options.delete(:index)
101
+ fake = options.delete(:fake) || false
102
+
103
+ args.each do |column_name|
104
+ # add it it the mini record columns for this table so we can access
105
+ # special fields like input_as, used by form builders
106
+ mini_record_columns[column_name] = options
107
+
108
+ if fake
109
+ # allow you to access the field on the instance object
110
+ attr_accessor column_name
111
+ # create a column that column_hashes will understand (a fake row)
112
+ fake_column = ActiveRecord::ConnectionAdapters::Column.new(
113
+ column_name.to_s, nil, type, true
114
+ )
115
+ # add it to the list of fake columns for this table
116
+ mini_record_fake_columns[column_name.to_s] = fake_column
117
+ # skip everything else as it's a fake column and don't want it in the db
118
+ next
119
+ end
120
+
121
+ # Allow custom types like:
122
+ # t.column :type, "ENUM('EMPLOYEE','CLIENT','SUPERUSER','DEVELOPER')"
123
+ if type.is_a?(String)
124
+ # will be converted in: t.column :type, "ENUM('EMPLOYEE','CLIENT')"
125
+ table_definition.column(column_name, type, options.reverse_merge(:limit => 0))
126
+ else
127
+ # wil be converted in: t.string :name
128
+ table_definition.send(type, column_name, options)
129
+ end
130
+
131
+ # Get the correct column_name i.e. in field :category, :as => :references
132
+ column_name = table_definition.columns[-1].name
133
+
134
+ # Parse indexes
135
+ case index
136
+ when Hash
137
+ add_index(options.delete(:column) || column_name, index)
138
+ when TrueClass
139
+ add_index(column_name)
140
+ when String, Symbol, Array
141
+ add_index(index)
142
+ end
143
+ end
144
+ end
145
+ alias :key :field
146
+ alias :property :field
147
+ alias :col :field
148
+
149
+ def timestamps
150
+ field :created_at, :updated_at, :as => :datetime, :null => false
151
+ end
152
+
153
+ def reset_table_definition!
154
+ @_table_definition = nil
155
+ end
156
+ alias :reset_schema! :reset_table_definition!
157
+
158
+ def schema
159
+ reset_table_definition!
160
+ yield table_definition
161
+ table_definition
162
+ end
163
+
164
+ def add_index(column_name, options={})
165
+ index_name = connection.index_name(table_name, :column => column_name)
166
+ indexes[index_name] = options.merge(:column => column_name) unless indexes.key?(index_name)
167
+ index_name
168
+ end
169
+ alias :index :add_index
170
+
171
+ def connection?
172
+ !!connection
173
+ rescue Exception => e
174
+ puts "\e[31m%s\e[0m" % e.message.strip
175
+ false
176
+ end
177
+
178
+ def clear_tables!
179
+ (connection.tables - schema_tables).each do |name|
180
+ connection.drop_table(name)
181
+ schema_tables.delete(name)
182
+ end
183
+ end
184
+
185
+ def foreign_keys
186
+ # fk cache to minimize quantity of sql queries
187
+ @foreign_keys ||= {}
188
+ @foreign_keys[:table_name] ||= connection.foreign_keys(table_name)
189
+ end
190
+
191
+ # Remove foreign keys for indexes with :foreign=>false option
192
+ def remove_foreign_keys
193
+ indexes.each do |name, options|
194
+ if options[:foreign]==false
195
+ foreign_key = foreign_keys.detect { |fk| fk.options[:column] == options[:column].to_s }
196
+ if foreign_key
197
+ connection.remove_foreign_key(table_name, :name => foreign_key.options[:name])
198
+ foreign_keys.delete(foreign_key)
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Add foreign keys for indexes with :foreign=>true option, if the key doesn't exists
205
+ def add_foreign_keys
206
+ indexes.each do |name, options|
207
+ if options[:foreign]
208
+ column = options[:column].to_s
209
+ unless foreign_keys.detect { |fk| fk[:options][:column] == column }
210
+ to_table = reflect_on_all_associations.detect { |a| a.foreign_key.to_s==column }.table_name
211
+ connection.add_foreign_key(table_name, to_table, options)
212
+ foreign_keys << { :options=> { :column=>column } }
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ def auto_upgrade!
219
+ return unless connection?
220
+ return if respond_to?(:abstract_class?) && abstract_class?
221
+
222
+ if self == ActiveRecord::Base
223
+ descendants.each(&:auto_upgrade!)
224
+ clear_tables!
225
+ else
226
+ # If table doesn't exist, create it
227
+ unless connection.tables.include?(table_name)
228
+ # TODO: create_table options
229
+ class << connection; attr_accessor :table_definition; end unless connection.respond_to?(:table_definition=)
230
+ connection.table_definition = table_definition
231
+ connection.create_table(table_name)
232
+ connection.table_definition = init_table_definition(connection)
233
+ end
234
+
235
+ # Add this to our schema tables
236
+ schema_tables << table_name unless schema_tables.include?(table_name)
237
+
238
+ # Generate fields from associations
239
+ if reflect_on_all_associations.any?
240
+ reflect_on_all_associations.each do |association|
241
+ foreign_key = association.options[:foreign_key] || "#{association.name}_id"
242
+ type_key = "#{association.name.to_s}_type"
243
+ case association.macro
244
+ when :belongs_to
245
+ field foreign_key, :as => :integer unless fields.key?(foreign_key.to_s)
246
+ if association.options[:polymorphic]
247
+ field type_key, :as => :string unless fields.key?(type_key.to_s)
248
+ index [foreign_key, type_key]
249
+ else
250
+ index foreign_key
251
+ end
252
+ when :has_and_belongs_to_many
253
+ table = if name = association.options[:join_table]
254
+ name.to_s
255
+ else
256
+ [table_name, association.name.to_s].sort.join("_")
257
+ end
258
+ unless connection.tables.include?(table.to_s)
259
+ foreign_key = association.options[:foreign_key] || association.foreign_key
260
+ association_foreign_key = association.options[:association_foreign_key] || association.association_foreign_key
261
+ connection.create_table(table, :id => false) do |t|
262
+ t.integer foreign_key
263
+ t.integer association_foreign_key
264
+ end
265
+ index_name = connection.index_name(table, :column => [foreign_key, association_foreign_key])
266
+ index_name = index_name[0...connection.index_name_length] if index_name.length > connection.index_name_length
267
+ connection.add_index table, [foreign_key, association_foreign_key], :name => index_name, :unique => true
268
+ end
269
+ # Add join table to our schema tables
270
+ schema_tables << table unless schema_tables.include?(table)
271
+ end
272
+ end
273
+ end
274
+
275
+ # Add to schema inheritance column if necessary
276
+ if descendants.present?
277
+ field inheritance_column, :as => :string unless fields.key?(inheritance_column.to_s)
278
+ index inheritance_column
279
+ end
280
+
281
+ # Remove fields from db no longer in schema
282
+ (fields_in_db.keys - fields.keys & fields_in_db.keys).each do |field|
283
+ column = fields_in_db[field]
284
+ connection.remove_column table_name, column.name
285
+ end
286
+
287
+ # Add fields to db new to schema
288
+ (fields.keys - fields_in_db.keys).each do |field|
289
+ column = fields[field]
290
+ options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
291
+ options[:default] = column.default unless column.default.nil?
292
+ options[:null] = column.null unless column.null.nil?
293
+ connection.add_column table_name, column.name, column.type.to_sym, options
294
+ end
295
+
296
+ # Change attributes of existent columns
297
+ (fields.keys & fields_in_db.keys).each do |field|
298
+ if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
299
+ changed = false # flag
300
+ new_type = fields[field].type.to_sym
301
+ new_attr = {}
302
+
303
+ # First, check if the field type changed
304
+ old_sql_type = get_sql_field_type(fields_in_db[field])
305
+ new_sql_type = get_sql_field_type(fields[field])
306
+
307
+ if old_sql_type != new_sql_type
308
+ logger.debug "[MiniRecord] Detected schema change for #{table_name}.#{field}#type " +
309
+ " from #{old_sql_type.inspect} to #{new_sql_type.inspect}" if logger
310
+ changed = true
311
+ end
312
+
313
+ # Special catch for precision/scale, since *both* must be specified together
314
+ # Always include them in the attr struct, but they'll only get applied if changed = true
315
+ new_attr[:precision] = fields[field][:precision]
316
+ new_attr[:scale] = fields[field][:scale]
317
+
318
+ # If we have precision this is also the limit
319
+ fields[field][:limit] ||= fields[field][:precision]
320
+
321
+ # Next, iterate through our extended attributes, looking for any differences
322
+ # This catches stuff like :null, :precision, etc
323
+ # Ignore junk attributes that different versions of Rails include
324
+ fields[field].each_pair do |att,value|
325
+ next unless [:name, :limit, :precision, :scale, :default, :null].include?(att)
326
+ value = true if att == :null && value.nil?
327
+ old_value = fields_in_db[field].send(att)
328
+ if value != old_value
329
+ logger.debug "[MiniRecord] Detected schema change for #{table_name}.#{field}##{att} " +
330
+ "from #{old_value.inspect} to #{value.inspect}" if logger
331
+ new_attr[att] = value
332
+ changed = true
333
+ end
334
+ end
335
+
336
+ # Change the column if applicable
337
+ connection.change_column table_name, field, new_type, new_attr if changed
338
+ end
339
+ end
340
+
341
+ remove_foreign_keys if connection.respond_to?(:foreign_keys)
342
+
343
+ # Remove old index
344
+ (indexes_in_db.keys - indexes.keys).each do |name|
345
+ connection.remove_index(table_name, :name => name)
346
+ end
347
+
348
+ # Add indexes
349
+ indexes.each do |name, options|
350
+ options = options.dup
351
+ unless connection.indexes(table_name).detect { |i| i.name == name }
352
+ connection.add_index(table_name, options.delete(:column), options)
353
+ end
354
+ end
355
+
356
+ add_foreign_keys if connection.respond_to?(:foreign_keys)
357
+
358
+ # Reload column information
359
+ reset_column_information
360
+ end
361
+ end
362
+ end # ClassMethods
363
+ end # AutoSchema
364
+ end # MiniRecord