datamapper 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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