datamapper 0.2.3 → 0.2.4

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 (121) hide show
  1. data/example.rb +5 -5
  2. data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
  3. data/lib/data_mapper/adapters/data_object_adapter.rb +141 -147
  4. data/lib/data_mapper/adapters/mysql_adapter.rb +14 -1
  5. data/lib/data_mapper/adapters/postgresql_adapter.rb +123 -18
  6. data/lib/data_mapper/adapters/sql/coersion.rb +21 -9
  7. data/lib/data_mapper/adapters/sql/commands/load_command.rb +36 -19
  8. data/lib/data_mapper/adapters/sql/mappings/column.rb +111 -17
  9. data/lib/data_mapper/adapters/sql/mappings/schema.rb +27 -0
  10. data/lib/data_mapper/adapters/sql/mappings/table.rb +256 -29
  11. data/lib/data_mapper/adapters/sqlite3_adapter.rb +93 -8
  12. data/lib/data_mapper/associations/belongs_to_association.rb +53 -54
  13. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +157 -25
  14. data/lib/data_mapper/associations/has_many_association.rb +45 -15
  15. data/lib/data_mapper/associations/has_n_association.rb +79 -20
  16. data/lib/data_mapper/associations/has_one_association.rb +2 -2
  17. data/lib/data_mapper/associations/reference.rb +1 -1
  18. data/lib/data_mapper/auto_migrations.rb +40 -0
  19. data/lib/data_mapper/base.rb +201 -98
  20. data/lib/data_mapper/context.rb +16 -10
  21. data/lib/data_mapper/database.rb +22 -11
  22. data/lib/data_mapper/dependency_queue.rb +28 -0
  23. data/lib/data_mapper/embedded_value.rb +61 -17
  24. data/lib/data_mapper/property.rb +4 -0
  25. data/lib/data_mapper/support/active_record_impersonation.rb +13 -5
  26. data/lib/data_mapper/support/errors.rb +5 -0
  27. data/lib/data_mapper/support/serialization.rb +8 -4
  28. data/lib/data_mapper/validatable_extensions/errors.rb +12 -0
  29. data/lib/data_mapper/validatable_extensions/macros.rb +7 -0
  30. data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +62 -0
  31. data/lib/data_mapper/validatable_extensions/validation_base.rb +18 -0
  32. data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +43 -0
  33. data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +7 -0
  34. data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +7 -0
  35. data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +7 -0
  36. data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +28 -0
  37. data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +15 -0
  38. data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +7 -0
  39. data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +7 -0
  40. data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +7 -0
  41. data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +33 -0
  42. data/lib/data_mapper/validations.rb +20 -0
  43. data/lib/data_mapper.rb +39 -34
  44. data/performance.rb +24 -18
  45. data/plugins/dataobjects/do_rb +0 -0
  46. data/rakefile.rb +12 -2
  47. data/spec/active_record_impersonation_spec.rb +133 -0
  48. data/spec/acts_as_tree_spec.rb +25 -9
  49. data/spec/associations_spec.rb +124 -4
  50. data/spec/attributes_spec.rb +13 -0
  51. data/spec/auto_migrations_spec.rb +44 -0
  52. data/spec/base_spec.rb +189 -1
  53. data/spec/column_spec.rb +85 -7
  54. data/spec/conditions_spec.rb +2 -2
  55. data/spec/dependency_spec.rb +25 -0
  56. data/spec/embedded_value_spec.rb +123 -3
  57. data/spec/fixtures/animals.yaml +1 -0
  58. data/spec/fixtures/careers.yaml +5 -0
  59. data/spec/fixtures/comments.yaml +1 -0
  60. data/spec/fixtures/people.yaml +14 -9
  61. data/spec/fixtures/projects.yaml +4 -0
  62. data/spec/fixtures/sections.yaml +5 -0
  63. data/spec/fixtures/serializers.yaml +6 -0
  64. data/spec/fixtures/users.yaml +1 -0
  65. data/spec/load_command_spec.rb +5 -4
  66. data/spec/mock_adapter.rb +2 -2
  67. data/spec/models/animal.rb +2 -1
  68. data/spec/models/animals_exhibit.rb +2 -2
  69. data/spec/models/career.rb +6 -0
  70. data/spec/models/comment.rb +4 -0
  71. data/spec/models/exhibit.rb +4 -0
  72. data/spec/models/person.rb +3 -13
  73. data/spec/models/project.rb +1 -1
  74. data/spec/models/serializer.rb +3 -0
  75. data/spec/models/user.rb +4 -0
  76. data/spec/models/zoo.rb +8 -1
  77. data/spec/natural_key_spec.rb +36 -0
  78. data/spec/paranoia_spec.rb +36 -0
  79. data/spec/property_spec.rb +70 -0
  80. data/spec/schema_spec.rb +10 -2
  81. data/spec/serialization_spec.rb +6 -3
  82. data/spec/serialize_spec.rb +19 -0
  83. data/spec/single_table_inheritance_spec.rb +7 -1
  84. data/spec/spec_helper.rb +26 -8
  85. data/spec/table_spec.rb +33 -0
  86. data/spec/validates_confirmation_of_spec.rb +20 -4
  87. data/spec/validates_format_of_spec.rb +22 -8
  88. data/spec/validates_length_of_spec.rb +26 -13
  89. data/spec/validates_uniqueness_of_spec.rb +18 -5
  90. data/spec/validations_spec.rb +55 -10
  91. data/tasks/fixtures.rb +13 -7
  92. metadata +189 -153
  93. data/lib/data_mapper/validations/confirmation_validator.rb +0 -53
  94. data/lib/data_mapper/validations/contextual_validations.rb +0 -50
  95. data/lib/data_mapper/validations/format_validator.rb +0 -85
  96. data/lib/data_mapper/validations/formats/email.rb +0 -78
  97. data/lib/data_mapper/validations/generic_validator.rb +0 -22
  98. data/lib/data_mapper/validations/length_validator.rb +0 -76
  99. data/lib/data_mapper/validations/required_field_validator.rb +0 -41
  100. data/lib/data_mapper/validations/unique_validator.rb +0 -56
  101. data/lib/data_mapper/validations/validation_errors.rb +0 -37
  102. data/lib/data_mapper/validations/validation_helper.rb +0 -77
  103. data/plugins/dataobjects/REVISION +0 -1
  104. data/plugins/dataobjects/Rakefile +0 -9
  105. data/plugins/dataobjects/do.rb +0 -348
  106. data/plugins/dataobjects/do_mysql.rb +0 -212
  107. data/plugins/dataobjects/do_postgres.rb +0 -196
  108. data/plugins/dataobjects/do_sqlite3.rb +0 -157
  109. data/plugins/dataobjects/spec/do_spec.rb +0 -150
  110. data/plugins/dataobjects/spec/spec_helper.rb +0 -81
  111. data/plugins/dataobjects/swig_mysql/extconf.rb +0 -45
  112. data/plugins/dataobjects/swig_mysql/mysql_c.c +0 -16602
  113. data/plugins/dataobjects/swig_mysql/mysql_c.i +0 -67
  114. data/plugins/dataobjects/swig_mysql/mysql_supp.i +0 -46
  115. data/plugins/dataobjects/swig_postgres/extconf.rb +0 -29
  116. data/plugins/dataobjects/swig_postgres/postgres_c.c +0 -8185
  117. data/plugins/dataobjects/swig_postgres/postgres_c.i +0 -73
  118. data/plugins/dataobjects/swig_sqlite/extconf.rb +0 -9
  119. data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +0 -4725
  120. data/plugins/dataobjects/swig_sqlite/sqlite_c.i +0 -168
  121. data/tasks/drivers.rb +0 -20
@@ -3,23 +3,27 @@ module DataMapper
3
3
 
4
4
  class HasAndBelongsToManyAssociation
5
5
 
6
- attr_reader :adapter, :table
6
+ attr_reader :adapter
7
7
 
8
8
  def initialize(klass, association_name, options)
9
9
  @adapter = database.adapter
10
- @table = adapter.table(klass)
10
+ @key_table = adapter.table(klass)
11
11
  @association_name = association_name.to_sym
12
12
  @options = options
13
13
 
14
14
  define_accessor(klass)
15
15
  end
16
16
 
17
+ # def key_table
18
+ # @key_table
19
+ # end
20
+
17
21
  def name
18
22
  @association_name
19
23
  end
20
24
 
21
25
  def foreign_name
22
- @foreign_name || (@foreign_name = (@options[:foreign_name] || @table.name).to_sym)
26
+ @foreign_name || (@foreign_name = (@options[:foreign_name] || @key_table.name).to_sym)
23
27
  end
24
28
 
25
29
  def constant
@@ -38,23 +42,27 @@ module DataMapper
38
42
 
39
43
  end
40
44
  end
45
+
46
+ def activate!
47
+ join_table.create!
48
+ end
41
49
 
42
- def association_columns
43
- association_table.columns.reject { |column| column.lazy? } + join_columns
50
+ def associated_columns
51
+ associated_table.columns.reject { |column| column.lazy? } + join_columns
44
52
  end
45
53
 
46
54
  def join_columns
47
55
  [ left_foreign_key, right_foreign_key ]
48
56
  end
49
57
 
50
- def association_table
51
- @association_table || (@association_table = adapter.table(constant))
58
+ def associated_table
59
+ @associated_table || (@associated_table = adapter.table(constant))
52
60
  end
53
61
 
54
62
  def join_table
55
63
  @join_table || @join_table = begin
56
64
  join_table_name = @options[:join_table] ||
57
- [ table.name.to_s, database.schema[constant].name.to_s ].sort.join('_')
65
+ [ @key_table.name.to_s, database.schema[constant].name.to_s ].sort.join('_')
58
66
 
59
67
  adapter.table(join_table_name)
60
68
  end
@@ -63,32 +71,55 @@ module DataMapper
63
71
  def left_foreign_key
64
72
  @left_foreign_key || @left_foreign_key = begin
65
73
  join_table.add_column(
66
- (@options[:left_foreign_key] || table.default_foreign_key),
67
- :integer, {})
74
+ (@options[:left_foreign_key] || @key_table.default_foreign_key),
75
+ :integer, :key => true)
68
76
  end
69
77
  end
70
78
 
71
79
  def right_foreign_key
72
80
  @right_foreign_key || @right_foreign_key = begin
73
81
  join_table.add_column(
74
- (@options[:right_foreign_key] || association_table.default_foreign_key),
75
- :integer, {})
82
+ (@options[:right_foreign_key] || associated_table.default_foreign_key),
83
+ :integer, :key => true)
76
84
  end
77
85
  end
78
86
 
79
87
  def to_sql
80
88
  <<-EOS.compress_lines
81
89
  JOIN #{join_table.to_sql} ON
82
- #{left_foreign_key.to_sql(true)} = #{table.key.to_sql(true)}
83
- JOIN #{association_table.to_sql} ON
84
- #{association_table.key.to_sql(true)} = #{right_foreign_key.to_sql(true)}
90
+ #{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
91
+ JOIN #{associated_table.to_sql} ON
92
+ #{associated_table.key.to_sql(true)} = #{right_foreign_key.to_sql(true)}
85
93
  EOS
86
94
  end
87
95
 
88
96
  def to_shallow_sql
89
97
  <<-EOS.compress_lines
90
98
  JOIN #{join_table.to_sql} ON
91
- #{left_foreign_key.to_sql(true)} = #{table.key.to_sql(true)}
99
+ #{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
100
+ EOS
101
+ end
102
+
103
+ def to_insert_sql
104
+ <<-EOS.compress_lines
105
+ INSERT INTO #{join_table.to_sql}
106
+ (#{left_foreign_key.to_sql}, #{right_foreign_key.to_sql})
107
+ VALUES
108
+ EOS
109
+ end
110
+
111
+ def to_delete_sql
112
+ <<-EOS
113
+ DELETE FROM #{join_table.to_sql}
114
+ WHERE #{left_foreign_key.to_sql} = ?
115
+ EOS
116
+ end
117
+
118
+ def to_delete_member_sql
119
+ <<-EOS
120
+ DELETE FROM #{join_table.to_sql}
121
+ WHERE #{left_foreign_key.to_sql} = ?
122
+ AND #{right_foreign_key.to_sql} = ?
92
123
  EOS
93
124
  end
94
125
 
@@ -105,16 +136,13 @@ module DataMapper
105
136
  EOS
106
137
  end
107
138
 
108
- class Set
139
+ class Set < Associations::Reference
109
140
 
110
141
  include Enumerable
111
142
 
112
- def initialize(instance, association_name)
113
- @instance, @association_name = instance, association_name
114
- end
115
-
116
- def association
117
- @association || (@association = @instance.session.schema[@instance.class].associations[@association_name])
143
+ def initialize(*args)
144
+ super
145
+ @new_members = false
118
146
  end
119
147
 
120
148
  def each
@@ -125,7 +153,11 @@ module DataMapper
125
153
  entries.size
126
154
  end
127
155
  alias length size
128
-
156
+
157
+ def count
158
+ entries.size
159
+ end
160
+
129
161
  def [](key)
130
162
  entries[key]
131
163
  end
@@ -134,6 +166,106 @@ module DataMapper
134
166
  entries.empty?
135
167
  end
136
168
 
169
+ def dirty?
170
+ @new_members || (@entries && @entries.any? { |item| item != @instance && item.dirty? })
171
+ end
172
+
173
+ def validate_recursively(event, cleared)
174
+ @entries.blank? || @entries.all? { |item| cleared.include?(item) || item.validate_recursively(event, cleared) }
175
+ end
176
+
177
+ def save_without_validation(database_context)
178
+ unless @entries.nil?
179
+
180
+ if @new_members || dirty?
181
+ adapter = @instance.database_context.adapter
182
+
183
+ adapter.connection do |db|
184
+ command = db.create_command(association.to_delete_sql)
185
+ command.execute_non_query(@instance.key)
186
+ end
187
+
188
+ unless @entries.empty?
189
+ if adapter.batch_insertable?
190
+ sql = association.to_insert_sql
191
+ values = []
192
+ keys = []
193
+
194
+ @entries.each do |member|
195
+ adapter.save_without_validation(database_context, member)
196
+ values << "(?, ?)"
197
+ keys << @instance.key << member.key
198
+ end
199
+
200
+ adapter.connection do |db|
201
+ command = db.create_command(sql << ' ' << values.join(', '))
202
+ command.execute_non_query(*keys)
203
+ end
204
+
205
+ else # adapter doesn't support batch inserts...
206
+ @entries.each do |member|
207
+ adapter.save_without_validation(database_context, member)
208
+ end
209
+
210
+ # Just to keep the same flow as the batch-insert mode.
211
+ @entries.each do |member|
212
+ adapter.connection do |db|
213
+ command = db.create_command("#{association.to_insert_sql} (?, ?)")
214
+ command.execute_non_query(@instance.key, member.key)
215
+ end
216
+ end
217
+ end # if adapter.batch_insertable?
218
+ end # unless @entries.empty?
219
+
220
+ @new_members = false
221
+ end # if @new_members || dirty?
222
+ end
223
+ end
224
+
225
+ def <<(member)
226
+ @new_members = true
227
+ entries << member
228
+ end
229
+
230
+ def clear
231
+ @new_members = true
232
+ @entries = []
233
+ end
234
+
235
+ def reload!
236
+ @new_members = false
237
+ @entries = nil
238
+ end
239
+
240
+ def delete(member)
241
+ @new_members = true
242
+ if entries.delete(member)
243
+ @instance.database_context.adapter.connection do |db|
244
+ command = db.create_command(association.to_delete_member_sql)
245
+ command.execute_non_query(@instance.key, member.key)
246
+ end
247
+ member
248
+ else
249
+ nil
250
+ end
251
+ end
252
+
253
+ def method_missing(symbol, *args, &block)
254
+ if entries.respond_to?(symbol)
255
+ entries.send(symbol, *args, &block)
256
+ elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
257
+ results = []
258
+ each do |item|
259
+ unless (val = item.send(symbol)).blank?
260
+ results << (val.is_a?(Enumerable) ? val.entries : val)
261
+ end
262
+ end
263
+ results.flatten
264
+ else
265
+ super
266
+ end
267
+ end
268
+
137
269
  def entries
138
270
  @entries || @entries = begin
139
271
 
@@ -163,7 +295,7 @@ module DataMapper
163
295
  end
164
296
  end
165
297
 
166
- @instance.session.all(association.constant,
298
+ @instance.database_context.all(association.constant,
167
299
  left_foreign_key => @instance.loaded_set.map(&:key),
168
300
  :shallow_include => association.foreign_name,
169
301
  :intercept_load => matcher
@@ -18,25 +18,39 @@ module DataMapper
18
18
  EOS
19
19
  end
20
20
 
21
+ def to_disassociate_sql
22
+ "UPDATE #{associated_table.to_sql} SET #{foreign_key_column.to_sql} = NULL WHERE #{foreign_key_column.to_sql} = ?"
23
+ end
24
+
21
25
  class Set < Associations::Reference
22
26
 
23
27
  include Enumerable
24
28
 
25
29
  def dirty?
26
- @items && @items.any? { |item| item != @instance && item.dirty? }
30
+ @items && @items.any? { |item| item.dirty? }
27
31
  end
28
32
 
29
- def validate_excluding_association(associated, context)
30
- @items.blank? || @items.all? { |item| item.validate_excluding_association(associated, context) }
33
+ def validate_recursively(event, cleared)
34
+ @items.blank? || @items.all? { |item| cleared.include?(item) || item.validate_recursively(event, cleared) }
31
35
  end
32
36
 
33
- def save
37
+ def save_without_validation(database_context)
38
+
39
+ adapter = @instance.database_context.adapter
40
+
41
+ adapter.connection do |db|
42
+ command = db.create_command(association.to_disassociate_sql)
43
+ command.execute_non_query(@instance.key)
44
+ end
45
+
34
46
  unless @items.nil? || @items.empty?
47
+
48
+
35
49
  setter_method = "#{@association_name}=".to_sym
36
- ivar_name = association.foreign_key.instance_variable_name
50
+ ivar_name = association.foreign_key_column.instance_variable_name
37
51
  @items.each do |item|
38
52
  item.instance_variable_set(ivar_name, @instance.key)
39
- item.save
53
+ @instance.database_context.adapter.save_without_validation(database_context, item)
40
54
  end
41
55
  end
42
56
  end
@@ -49,15 +63,27 @@ module DataMapper
49
63
  items << associated_item
50
64
 
51
65
  # TODO: Optimize!
52
- fk = association.foreign_key
53
- foreign_association = association.association_table.associations.find do |mapping|
54
- mapping.is_a?(BelongsToAssociation) && mapping.foreign_key == fk
66
+ fk = association.foreign_key_column
67
+ foreign_association = association.associated_table.associations.find do |mapping|
68
+ mapping.is_a?(BelongsToAssociation) && mapping.foreign_key_column == fk
55
69
  end
56
70
 
57
71
  associated_item.send("#{foreign_association.name}=", @instance) if foreign_association
58
72
 
59
73
  return @items
60
74
  end
75
+
76
+ def build(options)
77
+ item = association.associated_constant.new(options)
78
+ self << item
79
+ item
80
+ end
81
+
82
+ def create(options)
83
+ item = build(options)
84
+ item.save
85
+ item
86
+ end
61
87
 
62
88
  def set(items)
63
89
  @items = items
@@ -66,7 +92,7 @@ module DataMapper
66
92
  def method_missing(symbol, *args, &block)
67
93
  if items.respond_to?(symbol)
68
94
  items.send(symbol, *args, &block)
69
- elsif association.association_table.associations.any? { |assoc| assoc.name == symbol }
95
+ elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
70
96
  results = []
71
97
  each do |item|
72
98
  unless (val = item.send(symbol)).blank?
@@ -82,19 +108,23 @@ module DataMapper
82
108
  def respond_to?(symbol)
83
109
  items.respond_to?(symbol) || super
84
110
  end
111
+
112
+ def reload!
113
+ @items = nil
114
+ end
85
115
 
86
116
  def items
87
117
  @items || begin
88
118
  if @instance.loaded_set.nil?
89
119
  @items = []
90
120
  else
91
- fk = association.foreign_key.to_sym
121
+ fk = association.foreign_key_column.to_sym
92
122
 
93
- finder_options = { association.foreign_key.to_sym => @instance.loaded_set.map { |item| item.key } }
123
+ finder_options = { association.foreign_key_column.to_sym => @instance.loaded_set.map { |item| item.key } }
94
124
  finder_options.merge!(association.finder_options)
95
125
 
96
- associated_items = @instance.session.all(
97
- association.constant,
126
+ associated_items = @instance.database_context.all(
127
+ association.associated_constant,
98
128
  finder_options
99
129
  ).group_by { |entry| entry.send(fk) }
100
130
 
@@ -116,4 +146,4 @@ module DataMapper
116
146
  end
117
147
 
118
148
  end
119
- end
149
+ end
@@ -1,9 +1,12 @@
1
- module DataMapper
1
+ module DataMapper
2
+
3
+ class ForeignKeyNotFoundError < StandardError; end
4
+
2
5
  module Associations
3
6
 
4
7
  class HasNAssociation
5
8
 
6
- attr_reader :adapter, :table, :options
9
+ attr_reader :options
7
10
 
8
11
  OPTIONS = [
9
12
  :class,
@@ -12,12 +15,35 @@ module DataMapper
12
15
  ]
13
16
 
14
17
  def initialize(klass, association_name, options)
18
+ @constant = klass
15
19
  @adapter = database.adapter
16
- @table = adapter.table(klass)
20
+ @table = @adapter.table(klass)
17
21
  @association_name = association_name.to_sym
18
22
  @options = options || Hash.new
19
23
 
20
24
  define_accessor(klass)
25
+
26
+ Base::dependencies.add(associated_constant_name) do |klass|
27
+ @foreign_key_column = associated_table[foreign_key_name]
28
+
29
+ unless @foreign_key_column
30
+ associated_constant.property(foreign_key_name, foreign_key_type)
31
+
32
+ @foreign_key_column = associated_table[foreign_key_name]
33
+
34
+ if @foreign_key_column.nil?
35
+ raise ForeignKeyNotFoundError.new(<<-EOS.compress_lines)
36
+ key_table => #{key_table.inspect},
37
+ association_table => #{associated_table.inspect},
38
+ association_name => #{name},
39
+ foreign_key_name => #{foreign_key_name.inspect},
40
+ foreign_key_type => #{foreign_key_type.inspect},
41
+ constant => #{constant.inspect},
42
+ associated_constant => #{associated_constant.inspect}
43
+ EOS
44
+ end
45
+ end
46
+ end
21
47
  end
22
48
 
23
49
  def name
@@ -25,39 +51,72 @@ module DataMapper
25
51
  end
26
52
 
27
53
  def constant
28
- @associated_class || @associated_class = if @options.has_key?(:class) || @options.has_key?(:class_name)
29
- associated_class_name = (@options[:class] || @options[:class_name])
30
- if associated_class_name.kind_of?(String)
31
- Kernel.const_get(Inflector.classify(associated_class_name))
54
+ @constant
55
+ end
56
+
57
+ def associated_constant
58
+ @associated_constant || @associated_constant = Kernel.const_get(associated_constant_name)
59
+ end
60
+
61
+ def associated_constant_name
62
+ @associated_constant_name || begin
63
+
64
+ if @options.has_key?(:class) || @options.has_key?(:class_name)
65
+ @associated_constant_name = (@options[:class] || @options[:class_name])
66
+
67
+ if @associated_constant_name.kind_of?(String)
68
+ @associated_constant_name = Inflector.classify(@associated_constant_name)
69
+ elsif @associated_constant_name.kind_of?(Class)
70
+ @associated_constant_name = @associated_constant_name.name
71
+ end
32
72
  else
33
- associated_class_name
73
+ @associated_constant_name = Inflector.classify(@association_name)
34
74
  end
35
- else
36
- Kernel.const_get(Inflector.classify(@association_name))
75
+
76
+ @associated_constant_name
37
77
  end
78
+
38
79
  end
39
80
 
40
- def foreign_key
41
- @foreign_key || @foreign_key = begin
42
- association_table[@options[:foreign_key] || table.default_foreign_key]
43
- end
81
+ def primary_key_column
82
+ @primary_key_column || @primary_key_column = key_table.key
44
83
  end
45
84
 
46
- def association_table
47
- @association_table || (@association_table = adapter.table(constant))
85
+ def foreign_key_column
86
+ @foreign_key_column
48
87
  end
49
88
 
50
- def to_sql
51
- "JOIN #{association_table.to_sql} ON #{foreign_key.to_sql(true)} = #{table.key.to_sql(true)}"
89
+ def foreign_key_name
90
+ @foreign_key_name || @foreign_key_name = (@options[:foreign_key] || key_table.default_foreign_key)
52
91
  end
53
92
 
54
- def association_columns
55
- association_table.columns.reject { |column| column.lazy? }
93
+ def foreign_key_type
94
+ @foreign_key_type || @foreign_key_type = key_table.key.type
95
+ end
96
+
97
+ def key_table
98
+ @key_table || @key_table = @adapter.table(constant)
99
+ end
100
+
101
+ def associated_table
102
+ @association_table || @association_table = @adapter.table(associated_constant)
103
+ end
104
+
105
+ def associated_columns
106
+ associated_table.columns.reject { |column| column.lazy? }
56
107
  end
57
108
 
58
109
  def finder_options
59
110
  @finder_options || @finder_options = @options.reject { |k,v| self.class::OPTIONS.include?(k) }
60
111
  end
112
+
113
+ def to_sql
114
+ "JOIN #{associated_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}"
115
+ end
116
+
117
+ def activate!
118
+ foreign_key_column
119
+ end
61
120
  end
62
121
 
63
122
  end
@@ -40,12 +40,12 @@ module DataMapper
40
40
  else
41
41
  # Temp variable for the instance variable name.
42
42
  setter_method = "#{@association_name}=".to_sym
43
- instance_variable_name = "@#{association.foreign_key}".to_sym
43
+ instance_variable_name = "@#{association.foreign_key_column}".to_sym
44
44
 
45
45
  set = @instance.loaded_set.group_by { |instance| instance.key }
46
46
 
47
47
  # Fetch the foreign objects for all instances in the current object's loaded-set.
48
- @instance.session.all(association.constant, association.foreign_key => set.keys).each do |assoc|
48
+ @instance.database_context.all(association.associated_constant, association.foreign_key_column => set.keys).each do |assoc|
49
49
  set[assoc.instance_variable_get(instance_variable_name)].first.send(setter_method, assoc)
50
50
  end
51
51
 
@@ -38,7 +38,7 @@ module DataMapper
38
38
  # #association provides lazily initialized access to the declared
39
39
  # Association.
40
40
  def association
41
- @association || (@association = @instance.session.table(@instance.class).associations[@association_name])
41
+ @association || (@association = @instance.database_context.table(@instance.class).associations[@association_name])
42
42
  end
43
43
 
44
44
  end
@@ -0,0 +1,40 @@
1
+ module DataMapper
2
+ module AutoMigrations
3
+ def auto_migrate!
4
+ if self::subclasses.empty?
5
+ table = database.table(self)
6
+ table.associations.each do |association|
7
+ association.activate!
8
+ end
9
+
10
+ table.create!(true)
11
+ else
12
+ schema = database.schema
13
+ columns = self::subclasses.inject(schema[self].columns) do |span, subclass|
14
+ span + schema[subclass].columns
15
+ end
16
+
17
+ table_name = schema[self].name.to_s
18
+ table = schema[table_name]
19
+ columns.each do |column|
20
+ table.add_column(column.name, column.type, column.options)
21
+ end
22
+
23
+ table.associations.each do |association|
24
+ association.activate!
25
+ end
26
+
27
+ return table.create!(true)
28
+ end
29
+ end
30
+
31
+ private
32
+ def create_table(table)
33
+ raise NotImplementedError.new
34
+ end
35
+
36
+ def modify_table(table, columns)
37
+ raise NotImplementedError.new
38
+ end
39
+ end
40
+ end