mini_record 0.2.1 → 0.3.0.a

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .rvmrc
6
+ *.swp
7
+ *.swo
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx-2.0
6
+ - ruby-head
7
+ - ree
8
+ notifications:
9
+ recipients:
10
+ - d.dagostino@lipsiasoft.com
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in mini_record.gemspec
4
+ gem "rake"
4
5
  gem "minitest"
5
6
  gem "sqlite3"
6
7
  gemspec
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ [![Build Status](https://secure.travis-ci.org/DAddYE/mini_record.png)](http://travis-ci.org/DAddYE/mini_record)
2
+
3
+
1
4
  MiniRecord is a micro extension for our `ActiveRecord` gem.
2
5
  With MiniRecord you can add the ability to create columns outside the default `schema.rb`, directly
3
6
  in your **model** in a similar way that should know in others projects
@@ -9,7 +12,7 @@ My inspiration come from this handy [project](https://github.com/pjhyett/auto_mi
9
12
 
10
13
  * Define columns/properties inside your model
11
14
  * Perform migrations automatically
12
- * Auto upgrade your schema, so if you know what you are doing you don't lost your existing data!
15
+ * Auto upgrade your schema, so if you know what you are doing you don't lose your existing data!
13
16
  * Add, Remove, Change Columns; Add, Remove, Change indexes
14
17
 
15
18
  ## Instructions
@@ -48,8 +51,8 @@ Instead of `:as => :my_type` you can use `:type => :my_type`
48
51
  Option `:as` or `:type` if not provided is `:string` by default, you can use all ActiveRecord types:
49
52
 
50
53
  ``` rb
51
- :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean
52
- :references, :belongs_to, :primary_key, :timestamp
54
+ :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time,
55
+ :date, :binary, :boolean, :references, :belongs_to, :timestamp
53
56
  ```
54
57
 
55
58
  You can provide others ActiveRecord options like:
@@ -64,7 +67,7 @@ class Foo < ActiveRecord::Base
64
67
  end
65
68
  ```
66
69
 
67
- See [ActiveRecord::TableDefinition](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html)
70
+ See [ActiveRecord::TableDefinition](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html)
68
71
  for more details.
69
72
 
70
73
  Finally, when you execute `MyModel.auto_upgrade!`, missing columns, indexes and tables will be created on the fly.
@@ -74,6 +77,34 @@ Indexes and columns present in the db but **not** in your model schema will be *
74
77
 
75
78
  MiniRecord as ActiveRecord support STI plus some goodness, see our specs for more details.
76
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 (polymorphic)
93
+ ```ruby
94
+ class Address < ActiveRecord::Base
95
+ belongs_to :addressable, :polymorphic => true
96
+ end
97
+ ```
98
+ Will result in addressable id and type columns with composite indexes `add_index(:addresses), [:addressable_id, :addressable_type]`
99
+
100
+ #### habtm
101
+ ```ruby
102
+ class Address < ActiveRecord::Base
103
+ has_and_belongs_to_many :people
104
+ end
105
+ ```
106
+ Will generate a "addresses_people" join table and index the id columns
107
+
77
108
  ### Adding a new column
78
109
 
79
110
  Super easy, open your model and just add it:
@@ -136,8 +167,8 @@ end
136
167
 
137
168
  ## Warnings
138
169
 
139
- This software is not yet tested in a production project, now is only heavy development and if you can
140
- pleas fork it, find bug add a spec and then come back with a pull request. Thanks!
170
+ This software is not super well tested in a production project.
171
+ Im stated to using it in two customer's projects without any problem.
141
172
 
142
173
  ## Author
143
174
 
@@ -157,4 +188,4 @@ The above copyright notice and this permission notice shall be included in all c
157
188
  THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
158
189
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
159
190
  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
160
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
191
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -6,6 +6,10 @@ module MiniRecord
6
6
 
7
7
  module ClassMethods
8
8
 
9
+ def schema_tables
10
+ @@_schema_tables ||= []
11
+ end
12
+
9
13
  def table_definition
10
14
  return superclass.table_definition unless superclass == ActiveRecord::Base
11
15
 
@@ -31,12 +35,12 @@ module MiniRecord
31
35
  table_definition.send(type, column_name, options)
32
36
  column_name = table_definition.columns[-1].name
33
37
  case index_name = options.delete(:index)
34
- when Hash
35
- add_index(options.delete(:column) || column_name, index_name)
36
- when TrueClass
37
- add_index(column_name)
38
- when String, Symbol, Array
39
- add_index(index_name)
38
+ when Hash
39
+ add_index(options.delete(:column) || column_name, index_name)
40
+ when TrueClass
41
+ add_index(column_name)
42
+ when String, Symbol, Array
43
+ add_index(index_name)
40
44
  end
41
45
  end
42
46
  end
@@ -45,6 +49,10 @@ module MiniRecord
45
49
  alias :field :col
46
50
  alias :attribute :col
47
51
 
52
+ def timestamps
53
+ col :created_at, :updated_at, :as => :datetime
54
+ end
55
+
48
56
  def reset_table_definition!
49
57
  @_table_definition = nil
50
58
  end
@@ -74,99 +82,149 @@ module MiniRecord
74
82
  false
75
83
  end
76
84
 
85
+ def clear_tables!
86
+ # Drop unsued tables
87
+ (connection.tables - schema_tables).each do |name|
88
+ connection.drop_table(name)
89
+ schema_tables.delete(name)
90
+ end
91
+ end
92
+
77
93
  def auto_upgrade!
78
94
  return unless connection?
79
95
 
80
- # Table doesn't exist, create it
81
- unless connection.tables.include?(table_name)
82
- # TODO: Add to create_table options
83
- class << connection; attr_accessor :table_definition; end unless connection.respond_to?(:table_definition=)
84
- connection.table_definition = table_definition
85
- connection.create_table(table_name)
86
- connection.table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
87
- end
96
+ if self == ActiveRecord::Base
97
+ descendants.each(&:auto_upgrade!)
98
+ clear_tables!
99
+ else
100
+ # Table doesn't exist, create it
101
+ unless connection.tables.include?(table_name)
102
+ # TODO: create_table options
103
+ class << connection; attr_accessor :table_definition; end unless connection.respond_to?(:table_definition=)
104
+ connection.table_definition = table_definition
105
+ connection.create_table(table_name)
106
+ connection.table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
107
+ end
88
108
 
89
- # Grab database columns
90
- fields_in_db = connection.columns(table_name).inject({}) do |hash, column|
91
- hash[column.name] = column
92
- hash
93
- end
109
+ # Add this to our schema tables
110
+ schema_tables << table_name unless schema_tables.include?(table_name)
94
111
 
95
- # Grab new schema
96
- fields_in_schema = table_definition.columns.inject({}) do |hash, column|
97
- hash[column.name.to_s] = column
98
- hash
99
- end
112
+ # Grab database columns
113
+ fields_in_db = connection.columns(table_name).inject({}) do |hash, column|
114
+ hash[column.name] = column
115
+ hash
116
+ end
100
117
 
101
- # Add to schema inheritance column if necessary
102
- if descendants.present? && !fields_in_schema.include?(inheritance_column.to_s)
103
- table_definition.column inheritance_column, :string
104
- end
118
+ # Generate fields from associations
119
+ if reflect_on_all_associations.any?
120
+ reflect_on_all_associations.each do |association|
121
+ id_key = if association.options[:foreign_key]
122
+ association.options[:foreign_key]
123
+ else
124
+ "#{association.name.to_s}_id".to_sym
125
+ end
126
+ type_key = "#{association.name.to_s}_type".to_sym
127
+ case association.macro
128
+ when :belongs_to
129
+ table_definition.send(:integer, id_key)
130
+ if association.options[:polymorphic]
131
+ table_definition.send(:string, type_key)
132
+ add_index [id_key, type_key]
133
+ else
134
+ add_index id_key
135
+ end
136
+ when :has_and_belongs_to_many
137
+ table = [table_name, association.name.to_s].sort.join("_")
138
+ index = ""
139
+ unless connection.tables.include?(table)
140
+ connection.create_table(table)
141
+ connection.add_column table, "#{table.singularize}_id", :integer
142
+ connection.add_column table, "#{association.name.to_s.singularize}_id", :integer
143
+ connection.add_index table.to_sym, ["#{table.singularize}_id", "#{association.name.to_s.singularize}_id"].sort.map(&:to_sym), association.options
144
+ end
145
+ # Add join table to our schema tables
146
+ schema_tables << table unless schema_tables.include?(table)
147
+ end
148
+ end
149
+ end
105
150
 
151
+ # Grab new schema
152
+ fields_in_schema = table_definition.columns.inject({}) do |hash, column|
153
+ hash[column.name.to_s] = column
154
+ hash
155
+ end
106
156
 
107
- # Remove fields from db no longer in schema
108
- (fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
109
- column = fields_in_db[field]
110
- connection.remove_column table_name, column.name
111
- end
157
+ # Add to schema inheritance column if necessary
158
+ if descendants.present? && !fields_in_schema.include?(inheritance_column.to_s)
159
+ table_definition.column inheritance_column, :string
160
+ end
112
161
 
113
- # Add fields to db new to schema
114
- (fields_in_schema.keys - fields_in_db.keys).each do |field|
115
- column = fields_in_schema[field]
116
- options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
117
- options[:default] = column.default if !column.default.nil?
118
- options[:null] = column.null if !column.null.nil?
119
- connection.add_column table_name, column.name, column.type.to_sym, options
120
- end
162
+ # Remove fields from db no longer in schema
163
+ (fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
164
+ column = fields_in_db[field]
165
+ connection.remove_column table_name, column.name
166
+ end
121
167
 
122
- # Change attributes of existent columns
123
- (fields_in_schema.keys & fields_in_db.keys).each do |field|
124
- if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
125
- changed = false # flag
126
- new_type = fields_in_schema[field].type.to_sym
127
- new_attr = {}
168
+ # Add fields to db new to schema
169
+ (fields_in_schema.keys - fields_in_db.keys).each do |field|
170
+ column = fields_in_schema[field]
171
+ options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
172
+ options[:default] = column.default if !column.default.nil?
173
+ options[:null] = column.null if !column.null.nil?
174
+ connection.add_column table_name, column.name, column.type.to_sym, options
175
+ end
128
176
 
129
- # First, check if the field type changed
130
- if fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym
131
- changed = true
132
- end
177
+ # Change attributes of existent columns
178
+ (fields_in_schema.keys & fields_in_db.keys).each do |field|
179
+ if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
180
+ changed = false # flag
181
+ new_type = fields_in_schema[field].type.to_sym
182
+ new_attr = {}
133
183
 
134
- # Special catch for precision/scale, since *both* must be specified together
135
- # Always include them in the attr struct, but they'll only get applied if changed = true
136
- new_attr[:precision] = fields_in_schema[field][:precision]
137
- new_attr[:scale] = fields_in_schema[field][:scale]
138
-
139
- # Next, iterate through our extended attributes, looking for any differences
140
- # This catches stuff like :null, :precision, etc
141
- fields_in_schema[field].each_pair do |att,value|
142
- next if att == :type or att == :base or att == :name # special cases
143
- if !value.nil? && value != fields_in_db[field].send(att)
144
- new_attr[att] = value
184
+ # First, check if the field type changed
185
+ if fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym
145
186
  changed = true
146
187
  end
147
- end
148
188
 
149
- # Change the column if applicable
150
- connection.change_column table_name, field, new_type, new_attr if changed
189
+ # Special catch for precision/scale, since *both* must be specified together
190
+ # Always include them in the attr struct, but they'll only get applied if changed = true
191
+ new_attr[:precision] = fields_in_schema[field][:precision]
192
+ new_attr[:scale] = fields_in_schema[field][:scale]
193
+
194
+ # Next, iterate through our extended attributes, looking for any differences
195
+ # This catches stuff like :null, :precision, etc
196
+ fields_in_schema[field].each_pair do |att,value|
197
+ next if att == :type or att == :base or att == :name # special cases
198
+ if !value.nil? && value != fields_in_db[field].send(att)
199
+ new_attr[att] = value
200
+ changed = true
201
+ end
202
+ end
203
+
204
+ # Change the column if applicable
205
+ connection.change_column table_name, field, new_type, new_attr if changed
206
+ end
151
207
  end
152
- end
153
208
 
154
- # Remove old index
155
- indexes_in_db = connection.indexes(table_name).map(&:name)
156
- (indexes_in_db - indexes.keys).each do |name|
157
- connection.remove_index(table_name, :name => name)
158
- end
209
+ # Remove old index
210
+ # TODO: remove index from habtm t
211
+ indexes_in_db = connection.indexes(table_name).map(&:name)
212
+ (indexes_in_db - indexes.keys).each do |name|
213
+ connection.remove_index(table_name, :name => name)
214
+ end
159
215
 
160
- # Add indexes
161
- indexes.each do |name, options|
162
- options = options.dup
163
- unless connection.indexes(table_name).detect { |i| i.name == name }
164
- connection.add_index(table_name, options.delete(:column), options)
216
+ # Add indexes
217
+ indexes.each do |name, options|
218
+ options = options.dup
219
+ unless connection.indexes(table_name).detect { |i| i.name == name }
220
+ connection.add_index(table_name, options.delete(:column), options)
221
+ end
165
222
  end
223
+
224
+ # Reload column information
225
+ reset_column_information
166
226
  end
167
227
 
168
- # Reload column information
169
- reset_column_information
170
228
  end
171
229
  end # ClassMethods
172
230
  end # AutoSchema
@@ -1,3 +1,3 @@
1
1
  module MiniRecord
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0.a"
3
3
  end
data/mini_record.gemspec CHANGED
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
24
24
 
25
25
  # specify any dependencies here; for example:
26
26
  # s.add_development_dependency "rspec"
27
- s.add_dependency "activerecord", "~>3.1.0"
27
+ s.add_dependency "activerecord", "~>3.2.0"
28
28
  end
@@ -1,26 +1,34 @@
1
1
  require File.expand_path('../spec_helper.rb', __FILE__)
2
- require File.expand_path('../models.rb', __FILE__)
3
2
 
4
3
  describe MiniRecord do
5
4
 
5
+ before do
6
+ ActiveRecord::Base.descendants.each { |klass| Object.send(:remove_const, klass.to_s) }
7
+ ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base).clear
8
+ load File.expand_path('../models.rb', __FILE__)
9
+ ActiveRecord::Base.auto_upgrade!
10
+ end
11
+
6
12
  it 'has #schema inside model' do
7
- # For unknown reason separate specs doesn't works
8
- ActiveRecord::Base.connection.table_exists?(Person.table_name).must_equal false
9
- Person.auto_upgrade!
10
13
  Person.table_name.must_equal 'people'
11
- Person.db_columns.sort.must_equal %w[id name]
12
- Person.column_names.must_equal Person.db_columns
13
- Person.column_names.must_equal Person.schema_columns
14
+ Person.db_columns.sort.must_equal %w[created_at id name updated_at]
15
+ Person.column_names.sort.must_equal Person.db_columns.sort
16
+ Person.column_names.sort.must_equal Person.schema_columns.sort
14
17
  person = Person.create(:name => 'foo')
15
18
  person.name.must_equal 'foo'
16
19
  proc { person.surname }.must_raise NoMethodError
17
20
 
21
+ # Test the timestamp columns exist
22
+ person.must_respond_to :created_at
23
+ person.must_respond_to :updated_at
24
+
18
25
  # Add a column without lost data
19
26
  Person.class_eval do
20
27
  schema do |p|
21
28
  p.string :name
22
29
  p.string :surname
23
30
  end
31
+ timestamps
24
32
  end
25
33
  Person.auto_upgrade!
26
34
  Person.count.must_equal 1
@@ -28,22 +36,23 @@ describe MiniRecord do
28
36
  person.name.must_equal 'foo'
29
37
  person.surname.must_be_nil
30
38
  person.update_attribute(:surname, 'bar')
31
- Person.db_columns.sort.must_equal %w[id name surname]
32
- Person.column_names.must_equal Person.db_columns
39
+ Person.db_columns.sort.must_equal %w[created_at id name surname updated_at]
40
+ # Person.column_names.must_equal Person.db_columns
33
41
 
34
42
  # Remove a column without lost data
35
43
  Person.class_eval do
36
44
  schema do |p|
37
45
  p.string :name
38
46
  end
47
+ timestamps
39
48
  end
40
49
  Person.auto_upgrade!
41
50
  person = Person.last
42
51
  person.name.must_equal 'foo'
43
52
  proc { person.surname }.must_raise NoMethodError
44
- Person.db_columns.sort.must_equal %w[id name]
45
- Person.column_names.must_equal Person.db_columns
46
- Person.column_names.must_equal Person.schema_columns
53
+ Person.db_columns.sort.must_equal %w[created_at id name updated_at]
54
+ Person.column_names.sort.must_equal Person.db_columns.sort
55
+ Person.column_names.sort.must_equal Person.schema_columns.sort
47
56
 
48
57
  # Change column without lost data
49
58
  Person.class_eval do
@@ -56,9 +65,6 @@ describe MiniRecord do
56
65
  end
57
66
 
58
67
  it 'has #key,col,property,attribute inside model' do
59
- ActiveRecord::Base.connection.table_exists?(Post.table_name).must_equal false
60
- ActiveRecord::Base.connection.table_exists?(Category.table_name).must_equal false
61
- Post.auto_upgrade!; Category.auto_upgrade!
62
68
  Post.column_names.sort.must_equal Post.db_columns
63
69
  Category.column_names.sort.must_equal Category.schema_columns
64
70
 
@@ -81,12 +87,11 @@ describe MiniRecord do
81
87
  post = Post.first
82
88
  post.name.must_be_nil
83
89
  post.category.must_equal category
84
- post.wont_respond_to :title
90
+ proc { post.title }.must_raise ActiveModel::MissingAttributeError
85
91
  end
86
92
 
87
93
  it 'has indexes inside model' do
88
94
  # Check indexes
89
- Animal.auto_upgrade!
90
95
  Animal.db_indexes.size.must_be :>, 0
91
96
  Animal.db_indexes.must_equal Animal.indexes.keys.sort
92
97
 
@@ -110,11 +115,9 @@ describe MiniRecord do
110
115
  it 'works with STI' do
111
116
  class Dog < Pet; end
112
117
  class Cat < Pet; end
113
- Pet.auto_upgrade!
118
+ ActiveRecord::Base.auto_upgrade!
114
119
 
115
120
  # Check inheritance column
116
- Pet.db_columns.wont_include "type"
117
- Dog.auto_upgrade!
118
121
  Pet.db_columns.must_include "type"
119
122
 
120
123
  # Now, let's we know if STI is working
@@ -126,7 +129,6 @@ describe MiniRecord do
126
129
  Pet.all.map(&:name).must_equal ["foo", "bar"]
127
130
 
128
131
  # Check that this doesn't break things
129
- Cat.auto_upgrade!
130
132
  Dog.first.name.must_equal "bar"
131
133
 
132
134
  # What's happen if we change schema?
@@ -135,10 +137,11 @@ describe MiniRecord do
135
137
  Dog.class_eval do
136
138
  col :bau
137
139
  end
138
- Dog.auto_upgrade!
140
+ ActiveRecord::Base.auto_upgrade!
141
+ Dog.schema_columns.must_include "bau"
139
142
  Pet.db_columns.must_include "bau"
140
- Dog.new.must_respond_to :bau
141
- Cat.new.must_respond_to :bau
143
+ # Dog.new.must_respond_to :bau
144
+ # Cat.new.must_respond_to :bau
142
145
  end
143
146
 
144
147
  it 'works with custom inheritance column' do
@@ -146,13 +149,15 @@ describe MiniRecord do
146
149
  col :name
147
150
  col :surname
148
151
  col :role
149
- set_inheritance_column :role
152
+ def self.inheritance_column; 'role'; end
150
153
  end
154
+
151
155
  class Administrator < User; end
152
156
  class Customer < User; end
153
157
 
154
158
  User.auto_upgrade!
155
- Administrator.create(:name => "Davide", :surname => "D'Agostino")
159
+ User.inheritance_column.must_equal 'role'
160
+ Administrator.create(:name => "Davide", :surname => 'DAddYE')
156
161
  Customer.create(:name => "Foo", :surname => "Bar")
157
162
  Administrator.count.must_equal 1
158
163
  Administrator.first.name.must_equal "Davide"
@@ -176,4 +181,103 @@ describe MiniRecord do
176
181
  fake.category_id.must_equal 1
177
182
  fake.group_id.must_equal 2
178
183
  end
184
+
185
+ it 'creates a column and index based on belongs_to relation' do
186
+ Article.create(:title => 'Hello', :publisher_id => 1)
187
+ Article.first.tap do |a|
188
+ a.title.must_equal 'Hello'
189
+ a.publisher_id.must_equal 1
190
+ end
191
+ Article.db_indexes.must_include 'index_articles_on_publisher_id'
192
+ # Ensure that associated field/index is not deleted on upgrade
193
+ Article.auto_upgrade!
194
+ Article.first.publisher_id.must_equal 1
195
+ Article.db_indexes.must_include 'index_articles_on_publisher_id'
196
+ end
197
+
198
+ it 'removes a column and index when belongs_to relation is removed' do
199
+ class Foo < ActiveRecord::Base
200
+ key :name
201
+ belongs_to :image, :polymorphic => true
202
+ end
203
+ Foo.auto_upgrade!
204
+ Foo.db_columns.must_include 'name'
205
+ Foo.db_columns.must_include 'image_type'
206
+ Foo.db_columns.must_include 'image_id'
207
+ Foo.db_indexes.must_include 'index_foos_on_image_id_and_image_type'
208
+ Foo.class_eval do
209
+ reset_table_definition!
210
+ reflections.clear
211
+ indexes.clear
212
+ key :name
213
+ end
214
+ Foo.auto_upgrade!
215
+ Foo.db_columns.must_include 'name'
216
+ Foo.db_columns.wont_include 'image_type'
217
+ Foo.db_columns.wont_include 'image_id'
218
+ Foo.db_indexes.must_be_empty
219
+ end
220
+
221
+ it 'creates columns and index based on belongs_to polymorphic relation' do
222
+ Attachment.create(:name => 'Avatar', :attachable_id => 1, :attachable_type => 'Post')
223
+ Attachment.first.tap do |attachment|
224
+ attachment.name.must_equal 'Avatar'
225
+ attachment.attachable_id.must_equal 1
226
+ attachment.attachable_type.must_equal 'Post'
227
+ end
228
+ index = 'index_attachments_on_attachable_id_and_attachable_type'
229
+ Attachment.db_indexes.must_include index
230
+ # Ensure that associated fields/indexes are not deleted on subsequent upgrade
231
+ Attachment.auto_upgrade!
232
+ Attachment.first.attachable_id.must_equal 1
233
+ Attachment.first.attachable_type.must_equal 'Post'
234
+ Attachment.db_indexes.must_include index
235
+ end
236
+
237
+ it 'creates a join table with indexes for has_and_belongs_to_many relations' do
238
+ tables = Tool.connection.tables
239
+ tables.must_include('purposes_tools')
240
+ index = 'index_purposes_tools_on_purpose_id_and_purposes_tool_id'
241
+ Tool.connection.indexes('purposes_tools').map(&:name).must_include index
242
+ # Ensure that join table is not deleted on subsequent upgrade
243
+ Tool.auto_upgrade!
244
+ tables.must_include('purposes_tools')
245
+ Tool.connection.indexes('purposes_tools').map(&:name).must_include index
246
+ end
247
+
248
+ it 'drops join table if has_and_belongs_to_many relation is deleted' do
249
+ Tool.schema_tables.delete('purposes_tools')
250
+ ActiveRecord::Base.schema_tables.wont_include('purposes_tools')
251
+ ActiveRecord::Base.clear_tables!
252
+ Tool.connection.tables.wont_include('purposes_tools')
253
+ end
254
+
255
+ it 'should support #belongs_to with :class_name' do
256
+ Task.schema_columns.must_include 'author_id'
257
+ Task.db_columns.must_include 'author_id'
258
+ end
259
+
260
+ it 'should support #belongs_to with :foreign_key' do
261
+ Activity.schema_columns.must_include 'custom_id'
262
+ Activity.db_columns.must_include 'custom_id'
263
+ end
264
+
265
+ it 'should memonize in schema relationships' do
266
+ conn = ActiveRecord::Base.connection
267
+ conn.create_table('foos')
268
+ conn.add_column :foos, :name, :string
269
+ conn.add_column :foos, :bar_id, :integer
270
+ conn.add_index :foos, :bar_id
271
+ class Foo < ActiveRecord::Base
272
+ col :name
273
+ belongs_to :bar
274
+ end
275
+ Foo.db_columns.must_include 'name'
276
+ Foo.db_columns.must_include 'bar_id'
277
+ Foo.db_indexes.must_include 'index_foos_on_bar_id'
278
+ Foo.auto_upgrade!
279
+ Foo.schema_columns.must_include 'name'
280
+ Foo.schema_columns.must_include 'bar_id'
281
+ Foo.indexes.must_include 'index_foos_on_bar_id'
282
+ end
179
283
  end
data/spec/models.rb CHANGED
@@ -1,38 +1,14 @@
1
- require 'logger'
2
-
3
1
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
2
  # ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new($stdout)
5
3
 
6
- module SpecHelper
7
- def self.included(base)
8
- base.extend(ClassMethods)
9
- end
10
-
11
- module ClassMethods
12
- def db_columns
13
- connection.columns(table_name).map(&:name).sort
14
- end
15
-
16
- def db_indexes
17
- connection.indexes(table_name).map(&:name).sort
18
- end
19
-
20
- def schema_columns
21
- table_definition.columns.map { |c| c.name.to_s }.sort
22
- end
23
- end
24
- end
25
-
26
4
  class Person < ActiveRecord::Base
27
- include SpecHelper
28
5
  schema do |s|
29
6
  s.string :name
30
7
  end
8
+ timestamps
31
9
  end
32
10
 
33
11
  class Post < ActiveRecord::Base
34
- include SpecHelper
35
-
36
12
  key :title
37
13
  key :body
38
14
  key :category, :as => :references
@@ -40,21 +16,52 @@ class Post < ActiveRecord::Base
40
16
  end
41
17
 
42
18
  class Category < ActiveRecord::Base
43
- include SpecHelper
44
-
45
19
  key :title
20
+ has_many :articles
46
21
  has_many :posts
22
+ has_many :items
47
23
  end
48
24
 
49
25
  class Animal < ActiveRecord::Base
50
- include SpecHelper
51
-
52
26
  key :name, :index => true
53
27
  index :id
54
28
  end
55
29
 
56
30
  class Pet < ActiveRecord::Base
57
- include SpecHelper
58
-
59
31
  key :name, :index => true
60
32
  end
33
+
34
+ class Tool < ActiveRecord::Base
35
+ has_and_belongs_to_many :purposes
36
+ end
37
+
38
+ class Purpose < ActiveRecord::Base
39
+ has_and_belongs_to_many :tools
40
+ end
41
+
42
+ class Publisher < ActiveRecord::Base
43
+ has_many :articles
44
+ col :name
45
+ end
46
+
47
+ class Article < ActiveRecord::Base
48
+ key :title
49
+ belongs_to :publisher
50
+ end
51
+
52
+ class Attachment < ActiveRecord::Base
53
+ key :name
54
+ belongs_to :attachable, :polymorphic => true
55
+ end
56
+
57
+ class Account < ActiveRecord::Base
58
+ key :name
59
+ end
60
+
61
+ class Task < ActiveRecord::Base
62
+ belongs_to :author, :class_name => 'Account'
63
+ end
64
+
65
+ class Activity < ActiveRecord::Base
66
+ belongs_to :author, :class_name => 'Account', :foreign_key => 'custom_id'
67
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,3 +2,25 @@ require 'rubygems' unless defined?(Gem)
2
2
  require 'bundler/setup'
3
3
  require 'mini_record'
4
4
  require 'minitest/autorun'
5
+
6
+ module SpecHelper
7
+ module ClassMethods
8
+ def db_columns
9
+ connection.columns(table_name).map(&:name).sort
10
+ end
11
+
12
+ def db_indexes
13
+ connection.indexes(table_name).map(&:name).sort
14
+ end
15
+
16
+ def schema_columns
17
+ table_definition.columns.map { |c| c.name.to_s }.sort
18
+ end
19
+
20
+ def reset!
21
+ reset
22
+ end
23
+ end
24
+ end
25
+
26
+ ActiveRecord::Base.extend(SpecHelper::ClassMethods)
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_record
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
5
- prerelease:
4
+ hash: 50
5
+ prerelease: 6
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 3
9
+ - 0
10
+ - a
11
+ version: 0.3.0.a
11
12
  platform: ruby
12
13
  authors:
13
14
  - Davide D'Agostino
@@ -15,8 +16,7 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-09-11 00:00:00 +02:00
19
- default_executable:
19
+ date: 2012-01-21 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: activerecord
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 3
29
+ hash: 15
30
30
  segments:
31
31
  - 3
32
- - 1
32
+ - 2
33
33
  - 0
34
- version: 3.1.0
34
+ version: 3.2.0
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  description: "\n\
@@ -48,6 +48,7 @@ extra_rdoc_files: []
48
48
 
49
49
  files:
50
50
  - .gitignore
51
+ - .travis.yml
51
52
  - Gemfile
52
53
  - README.md
53
54
  - Rakefile
@@ -58,7 +59,6 @@ files:
58
59
  - spec/mini_record_spec.rb
59
60
  - spec/models.rb
60
61
  - spec/spec_helper.rb
61
- has_rdoc: true
62
62
  homepage: https://github.com/DAddYE/mini_record
63
63
  licenses: []
64
64
 
@@ -79,16 +79,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  none: false
81
81
  requirements:
82
- - - ">="
82
+ - - ">"
83
83
  - !ruby/object:Gem::Version
84
- hash: 3
84
+ hash: 25
85
85
  segments:
86
- - 0
87
- version: "0"
86
+ - 1
87
+ - 3
88
+ - 1
89
+ version: 1.3.1
88
90
  requirements: []
89
91
 
90
92
  rubyforge_project: mini_record
91
- rubygems_version: 1.6.2
93
+ rubygems_version: 1.8.15
92
94
  signing_key:
93
95
  specification_version: 3
94
96
  summary: MiniRecord is a micro gem that allow you to write schema inside your model as you can do in DataMapper.
@@ -96,3 +98,4 @@ test_files:
96
98
  - spec/mini_record_spec.rb
97
99
  - spec/models.rb
98
100
  - spec/spec_helper.rb
101
+ has_rdoc: