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
@@ -1,9 +1,12 @@
1
+ require 'data_mapper/property'
1
2
  require 'data_mapper/support/active_record_impersonation'
2
3
  require 'data_mapper/support/serialization'
3
- require 'data_mapper/validations/validation_helper'
4
+ require 'data_mapper/validations'
4
5
  require 'data_mapper/associations'
5
6
  require 'data_mapper/callbacks'
6
7
  require 'data_mapper/embedded_value'
8
+ require 'data_mapper/auto_migrations'
9
+ require 'data_mapper/dependency_queue'
7
10
 
8
11
  begin
9
12
  require 'ferret'
@@ -11,7 +14,7 @@ rescue LoadError
11
14
  end
12
15
 
13
16
  module DataMapper
14
-
17
+
15
18
  class Base
16
19
 
17
20
  # This probably needs to be protected
@@ -20,7 +23,7 @@ module DataMapper
20
23
  include CallbacksHelper
21
24
  include Support::ActiveRecordImpersonation
22
25
  include Support::Serialization
23
- include Validations::ValidationHelper
26
+ include Validations
24
27
  include Associations
25
28
 
26
29
  # Track classes that inherit from DataMapper::Base.
@@ -34,44 +37,52 @@ module DataMapper
34
37
  end
35
38
  end
36
39
 
40
+ def self.dependencies
41
+ @dependency_queue || (@dependency_queue = DependencyQueue.new)
42
+ end
43
+
37
44
  def self.inherited(klass)
45
+ klass.instance_variable_set('@properties', [])
46
+
47
+ klass.send :extend, AutoMigrations
38
48
  DataMapper::Base::subclasses << klass
39
- klass.send(:undef_method, :id)
49
+ klass.send(:undef_method, :id)
40
50
 
41
51
  # When this class is sub-classed, copy the declared columns.
42
52
  klass.class_eval do
43
-
44
- def self.auto_migrate!
45
- if self::subclasses.empty?
46
- database.schema[self].drop!
47
- database.save(self)
48
- else
49
- schema = database.schema
50
- columns = self::subclasses.inject(schema[self].columns) do |span, subclass|
51
- span + schema[subclass].columns
52
- end
53
-
54
- table_name = schema[self].name.to_s
55
- table = schema[table_name]
56
- columns.each do |column|
57
- table.add_column(column.name, column.type, column.options)
58
- end
59
-
60
- table.drop!
61
- table.create!
62
- end
63
- end
64
-
65
53
  def self.subclasses
66
54
  @subclasses || (@subclasses = [])
67
55
  end
68
56
 
69
57
  def self.inherited(subclass)
58
+
59
+ super_table = database.table(self)
60
+
61
+ if super_table.type_column.nil?
62
+ super_table.add_column(:type, :class, {})
63
+ end
64
+
70
65
  self::subclasses << subclass
71
66
  end
72
67
  end
73
68
  end
74
69
 
70
+ def self.logger
71
+ database.logger
72
+ end
73
+
74
+ def logger
75
+ self.class.logger
76
+ end
77
+
78
+ def self.transaction
79
+ yield
80
+ end
81
+
82
+ def self.properties
83
+ @properties
84
+ end
85
+
75
86
  # Allows you to override the table name for a model.
76
87
  # EXAMPLE:
77
88
  # class WorkItem
@@ -90,9 +101,15 @@ module DataMapper
90
101
  end
91
102
  end
92
103
 
104
+ PROPERTY_OPTIONS = [
105
+ :public, :protected, :private, :accessor, :reader, :writer,
106
+ :lazy, :default, :nullable, :key, :serial, :column
107
+ ]
108
+
93
109
  # Adds property accessors for a field that you'd like to be able to modify. The DataMapper doesn't
94
110
  # use the table schema to infer accessors, you must explicity call #property to add field accessors
95
111
  # to your model.
112
+ #
96
113
  # EXAMPLE:
97
114
  # class CellProvider
98
115
  # property :name, :string
@@ -105,10 +122,34 @@ module DataMapper
105
122
  #
106
123
  # => AT&T
107
124
  # => 3
125
+ #
126
+ # OPTIONS:
127
+ # * <tt>lazy</tt>: Lazy load the specified property (:lazy => true). False by default.
128
+ # * <tt>accessor</tt>: Set method visibility for the property accessors. Affects both
129
+ # reader and writer. Allowable values are :public, :protected, :private. Defaults to
130
+ # :public
131
+ # * <tt>reader</tt>: Like the accessor option but affects only the property reader.
132
+ # * <tt>writer</tt>: Like the accessor option but affects only the property writer.
133
+ # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
134
+ # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
108
135
  def self.property(name, type, options = {})
109
- mapping = database.schema[self].add_column(name, type, options)
110
- property_getter(name, mapping)
111
- property_setter(name, mapping)
136
+
137
+ options.each_pair do |k,v|
138
+ raise ArgumentError.new("#{k.inspect} is not a supported option in DataMapper::Base::PROPERTY_OPTIONS") unless PROPERTY_OPTIONS.include?(k)
139
+ end
140
+
141
+ visibility_options = [:public, :protected, :private]
142
+ reader_visibility = options[:reader] || options[:accessor] || :public
143
+ writer_visibility = options[:writer] || options[:accessor] || :public
144
+ writer_visibility = :protected if options[:protected]
145
+ writer_visibility = :private if options[:private]
146
+
147
+ raise(ArgumentError.new, "property visibility must be :public, :protected, or :private") unless visibility_options.include?(reader_visibility) && visibility_options.include?(writer_visibility)
148
+
149
+ mapping = database.schema[self].add_column(name.to_s.sub(/\?$/, '').to_sym, type, options)
150
+
151
+ property_getter(mapping, reader_visibility)
152
+ property_setter(mapping, writer_visibility)
112
153
 
113
154
  if MAGIC_PROPERTIES.has_key?(name)
114
155
  class_eval(&MAGIC_PROPERTIES[name])
@@ -124,35 +165,78 @@ module DataMapper
124
165
  :created_on => lambda { before_create { |x| x.created_on = Date::today } }
125
166
  }
126
167
 
127
- def self.embed(class_or_name, &block)
128
- EmbeddedValue::define(self, class_or_name, &block)
168
+ # An embedded value maps the values of an object to fields in the record of the object's owner.
169
+ # #embed takes a symbol to define the embedded class, options, and an optional block. See
170
+ # examples for use cases.
171
+ #
172
+ # EXAMPLE:
173
+ # class CellPhone < DataMapper::Base
174
+ # property :number, :string
175
+ #
176
+ # embed :owner, :prefix => true do
177
+ # property :name, :string
178
+ # property :address, :string
179
+ # end
180
+ # end
181
+ #
182
+ # my_phone = CellPhone.new
183
+ # my_phone.owner.name = "Nick"
184
+ # puts my_phone.owner.name
185
+ #
186
+ # => Nick
187
+ #
188
+ # OPTIONS:
189
+ # * <tt>prefix</tt>: define a column prefix, so instead of mapping :address to an 'address'
190
+ # column, it would map to 'owner_address' in the example above. If :prefix => true is
191
+ # specified, the prefix will be the name of the symbol given as the first parameter. If the
192
+ # prefix is a string the specified string will be used for the prefix.
193
+ # * <tt>lazy</tt>: lazy-load all embedded values at the same time. :lazy => true to enable.
194
+ # Disabled (false) by default.
195
+ # * <tt>accessor</tt>: Set method visibility for all embedded properties. Affects both
196
+ # reader and writer. Allowable values are :public, :protected, :private. Defaults to :public
197
+ # * <tt>reader</tt>: Like the accessor option but affects only embedded property readers.
198
+ # * <tt>writer</tt>: Like the accessor option but affects only embedded property writers.
199
+ # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
200
+ # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
201
+ #
202
+ def self.embed(name, options = {}, &block)
203
+ EmbeddedValue::define(self, name, options, &block)
129
204
  end
130
205
 
131
- def self.property_getter(name, mapping)
206
+ def self.property_getter(mapping, visibility = :public)
132
207
  if mapping.lazy?
133
208
  class_eval <<-EOS
134
- def #{name}
135
- lazy_load!(#{name.inspect})
136
- @#{name}
209
+ #{visibility.to_s}
210
+ def #{mapping.name}
211
+ lazy_load!(#{mapping.name.inspect})
212
+ class << self;
213
+ attr_accessor #{mapping.name.inspect}
214
+ end
215
+ @#{mapping.name}
137
216
  end
138
217
  EOS
139
218
  else
140
- class_eval("def #{name}; #{mapping.instance_variable_name} end")
219
+ class_eval("#{visibility.to_s}; def #{mapping.name}; #{mapping.instance_variable_name} end") unless [ :public, :private, :protected ].include?(mapping.name)
220
+ end
221
+
222
+ if mapping.type == :boolean
223
+ class_eval("#{visibility.to_s}; def #{mapping.name}?; #{mapping.instance_variable_name} end")
141
224
  end
142
225
  end
143
226
 
144
- def self.property_setter(name, mapping)
227
+ def self.property_setter(mapping, visibility = :public)
145
228
  if mapping.lazy?
146
229
  class_eval <<-EOS
147
- def #{name.to_s.sub(/\?$/, '')}=(value)
230
+ #{visibility.to_s}
231
+ def #{mapping.name}=(value)
148
232
  class << self;
149
- attr_accessor #{name.inspect}
233
+ attr_accessor #{mapping.name.inspect}
150
234
  end
151
- @#{name} = value
235
+ @#{mapping.name} = value
152
236
  end
153
237
  EOS
154
238
  else
155
- class_eval("def #{name.to_s.sub(/\?$/, '')}=(value); #{mapping.instance_variable_name} = value end")
239
+ class_eval("#{visibility.to_s}; def #{mapping.name}=(value); #{mapping.instance_variable_name} = value end")
156
240
  end
157
241
  end
158
242
 
@@ -160,19 +244,28 @@ module DataMapper
160
244
  # for the named methods so that the lazy_loading is skipped the second time.
161
245
  def lazy_load!(*names)
162
246
 
247
+ names = names.map { |name| name.to_sym }.reject { |name| lazy_loaded_attributes.include?(name) }
248
+
163
249
  reset_attribute = lambda do |instance|
164
250
  singleton_class = (class << instance; self end)
165
251
  names.each do |name|
252
+ instance.lazy_loaded_attributes << name
166
253
  singleton_class.send(:attr_accessor, name)
167
254
  end
168
255
  end
169
256
 
170
- unless new_record? || loaded_set.nil?
171
- session.all(
257
+ unless names.empty? || new_record? || loaded_set.nil?
258
+
259
+ key = database_context.table(self.class).key.to_sym
260
+ keys_to_select = loaded_set.map do |instance|
261
+ instance.send(key)
262
+ end
263
+
264
+ database_context.all(
172
265
  self.class,
173
- :select => ([:id] + names),
266
+ :select => ([key] + names),
174
267
  :reload => true,
175
- :id => loaded_set.map(&:id)
268
+ key => keys_to_select
176
269
  ).each(&reset_attribute)
177
270
  else
178
271
  reset_attribute[self]
@@ -183,7 +276,12 @@ module DataMapper
183
276
  def new_record?
184
277
  @new_record.nil? || @new_record
185
278
  end
279
+
280
+ def ==(other)
281
+ private_attributes == other.send("private_attributes")
282
+ end
186
283
 
284
+ # Returns the difference between two objects, in terms of their attributes.
187
285
  def ^(other)
188
286
  results = {}
189
287
 
@@ -199,10 +297,14 @@ module DataMapper
199
297
  results
200
298
  end
201
299
 
300
+ def lazy_loaded_attributes
301
+ @lazy_loaded_attributes || @lazy_loaded_attributes = Set.new
302
+ end
303
+
202
304
  def loaded_attributes
203
305
  pairs = {}
204
306
 
205
- session.table(self).columns.each do |column|
307
+ database_context.table(self).columns.each do |column|
206
308
  pairs[column.name] = instance_variable_get(column.instance_variable_name)
207
309
  end
208
310
 
@@ -217,10 +319,12 @@ module DataMapper
217
319
  def attributes
218
320
  pairs = {}
219
321
 
220
- session.table(self).columns.each do |column|
221
- lazy_load!(column.name) if column.lazy?
222
- value = instance_variable_get(column.instance_variable_name)
223
- pairs[column.name] = column.type == :class ? value.to_s : value
322
+ database_context.table(self).columns.each do |column|
323
+ if self.class.public_method_defined?(column.name)
324
+ lazy_load!(column.name) if column.lazy?
325
+ value = instance_variable_get(column.instance_variable_name)
326
+ pairs[column.name] = column.type == :class ? value.to_s : value
327
+ end
224
328
  end
225
329
 
226
330
  pairs
@@ -228,50 +332,42 @@ module DataMapper
228
332
 
229
333
  # Mass-assign mapped fields.
230
334
  def attributes=(values_hash)
231
- table = session.schema[self.class]
335
+ table = database_context.table(self.class)
232
336
 
233
- values_hash.reject do |key, value|
234
- protected_attribute? key
337
+ values_hash.delete_if do |key, value|
338
+ !self.class.public_method_defined?("#{key}=")
235
339
  end.each_pair do |key, value|
236
- if column = table[key]
237
- instance_variable_set(column.instance_variable_name, value)
238
- else
340
+ if respond_to?(key)
239
341
  send("#{key}=", value)
342
+ elsif column = table[key]
343
+ instance_variable_set(column.instance_variable_name, value)
240
344
  end
241
345
  end
242
346
  end
243
347
 
244
- def dirty?(name = nil)
245
- if name.nil?
246
- session.table(self).columns.any? do |column|
348
+ def dirty?
349
+ result = database_context.table(self).columns.any? do |column|
350
+ if column.type == :object
351
+ Marshal.dump(self.instance_variable_get(column.instance_variable_name)) != original_values[column.name]
352
+ else
247
353
  self.instance_variable_get(column.instance_variable_name) != original_values[column.name]
248
- end || loaded_associations.any? do |loaded_association|
249
- if loaded_association.respond_to?(:dirty?)
250
- loaded_association.dirty?
251
- else
252
- false
253
- end
254
354
  end
255
- else
256
- key = name.kind_of?(Symbol) ? name : name.to_sym
257
- self.instance_variable_get("@#{name}") != original_values[key]
355
+ end
356
+
357
+ return true if result
358
+
359
+ loaded_associations.any? do |loaded_association|
360
+ loaded_association.dirty?
258
361
  end
259
362
  end
260
363
 
261
364
  def dirty_attributes
262
365
  pairs = {}
263
366
 
264
- if new_record?
265
- session.table(self).columns.each do |column|
266
- unless (value = instance_variable_get(column.instance_variable_name)).nil?
267
- pairs[column.name] = value
268
- end
269
- end
270
- else
271
- session.table(self).columns.each do |column|
272
- if (value = instance_variable_get(column.instance_variable_name)) != original_values[column.name]
273
- pairs[column.name] = value
274
- end
367
+ database_context.table(self).columns.each do |column|
368
+ value = instance_variable_get(column.instance_variable_name)
369
+ if value != original_values[column.name] && (!new_record? || !column.serial?)
370
+ pairs[column.name] = column.type != :object ? value : YAML.dump(value)
275
371
  end
276
372
  end
277
373
 
@@ -282,14 +378,6 @@ module DataMapper
282
378
  @original_values || (@original_values = {})
283
379
  end
284
380
 
285
- def protected_attribute?(key)
286
- self.class.protected_attributes.include?(key.kind_of?(Symbol) ? key : key.to_sym)
287
- end
288
-
289
- def self.protected_attributes
290
- @protected_attributes ||= []
291
- end
292
-
293
381
  def self.index
294
382
  @index || @index = Ferret::Index::Index.new(:path => "#{database.adapter.index_path}/#{name}")
295
383
  end
@@ -311,14 +399,14 @@ module DataMapper
311
399
  return all(:id => ids)
312
400
  end
313
401
 
314
- def self.protect(*keys)
315
- keys.each { |key| protected_attributes << key.to_sym }
316
- end
317
-
318
402
  def self.foreign_key
319
403
  Inflector.underscore(self.name) + "_id"
320
404
  end
321
405
 
406
+ def self.table
407
+ database.schema[self]
408
+ end
409
+
322
410
  def inspect
323
411
  inspected_attributes = attributes.map { |k,v| "@#{k}=#{v.inspect}" }
324
412
 
@@ -335,27 +423,42 @@ module DataMapper
335
423
  @loaded_associations || @loaded_associations = []
336
424
  end
337
425
 
338
- def session=(value)
339
- @session = value
426
+ def database_context=(value)
427
+ @database_context = value
340
428
  end
341
429
 
342
- def session
343
- @session || ( @session = database )
430
+ def database_context
431
+ @database_context || ( @database_context = database )
344
432
  end
345
433
 
346
434
  def key=(value)
347
- key_column = session.schema[self.class].key
435
+ key_column = database_context.table(self.class).key
348
436
  @__key = key_column.type_cast_value(value)
349
437
  instance_variable_set(key_column.instance_variable_name, @__key)
350
438
  end
351
439
 
352
440
  def key
353
441
  @__key || @__key = begin
354
- key_column = session.schema[self.class].key
442
+ key_column = database_context.table(self.class).key
355
443
  key_column.type_cast_value(instance_variable_get(key_column.instance_variable_name))
356
444
  end
357
445
  end
446
+
447
+ private
448
+
449
+ # return all attributes, regardless of their visibility
450
+ def private_attributes
451
+ pairs = {}
452
+
453
+ database_context.table(self).columns.each do |column|
454
+ lazy_load!(column.name) if column.lazy?
455
+ value = instance_variable_get(column.instance_variable_name)
456
+ pairs[column.name] = column.type == :class ? value.to_s : value
457
+ end
458
+
459
+ pairs
460
+ end
358
461
 
359
462
  end
360
463
 
361
- end
464
+ end
@@ -20,24 +20,26 @@ module DataMapper
20
20
  def first(klass, *args)
21
21
  id = nil
22
22
  options = nil
23
+ table = self.table(klass)
24
+ key = table.key
23
25
 
24
26
  if args.empty? # No id, no options
25
27
  options = { :limit => 1 }
26
28
  elsif args.size == 2 && args.last.kind_of?(Hash) # id AND options
27
- options = args.last.merge(:id => args.first)
29
+ options = args.last.merge(key => args.first)
28
30
  elsif args.size == 1 # id OR options
29
31
  if args.first.kind_of?(Hash)
30
32
  options = args.first.merge(:limit => 1) # no id, add limit
31
33
  else
32
- options = { :id => args.first } # no options, set id
34
+ options = { key => args.first } # no options, set id
33
35
  end
34
36
  else
35
- raise ArgumentError.new('Session#first takes a class, and optional type_or_id and/or options arguments')
37
+ raise ArgumentError.new('Context#first takes a class, and optional type_or_id and/or options arguments')
36
38
  end
37
39
 
38
40
  # Account for undesired behaviour in MySQL that returns the
39
41
  # last inserted row when the WHERE clause contains a "#{primary_key} IS NULL".
40
- return nil if options.has_key?(:id) && options[:id] == nil
42
+ return nil if options.has_key?(key.name) && options[key.name] == nil
41
43
 
42
44
  @adapter.load(self, klass, options).first
43
45
  end
@@ -46,8 +48,8 @@ module DataMapper
46
48
  @adapter.load(self, klass, options)
47
49
  end
48
50
 
49
- def count(klass, options = {})
50
- @adapter.count(klass, options)
51
+ def count(klass, *args)
52
+ table(klass).count(*args)
51
53
  end
52
54
 
53
55
  def save(instance)
@@ -63,19 +65,23 @@ module DataMapper
63
65
  end
64
66
 
65
67
  def truncate(klass)
66
- @adapter.truncate(self, klass)
68
+ table(klass).truncate!
67
69
  end
68
70
 
69
71
  def create_table(klass)
70
- @adapter.create_table(klass)
72
+ table(klass).create!
71
73
  end
72
74
 
73
75
  def drop_table(klass)
74
- @adapter.drop(self, klass)
76
+ table(klass).drop!
75
77
  end
76
78
 
77
79
  def table_exists?(klass)
78
- @adapter.table_exists?(klass)
80
+ table(klass).exists?
81
+ end
82
+
83
+ def column_exists_for_table?(klass, column_name)
84
+ @adapter.column_exists_for_table?(klass, column_name)
79
85
  end
80
86
 
81
87
  def execute(*args)
@@ -27,10 +27,12 @@ module DataMapper
27
27
  unless block_given?
28
28
  Database.context.last || Context.new(Database[name].adapter)
29
29
  else
30
- Database.context.push(Context.new(Database[name].adapter))
31
- result = yield(Database.context.last)
32
- Database.context.pop
33
- result
30
+ begin
31
+ Database.context.push(Context.new(Database[name].adapter))
32
+ return yield(Database.context.last)
33
+ ensure
34
+ Database.context.pop
35
+ end
34
36
  end
35
37
  end
36
38
 
@@ -172,8 +174,13 @@ module DataMapper
172
174
  @log_stream = nil
173
175
  end
174
176
 
175
- attr_reader :name, :adapter
176
- attr_accessor :host, :database, :schema_search_path, :username, :password, :log_stream, :log_level, :index_path, :socket
177
+ attr_reader :name, :adapter, :log_stream
178
+
179
+ attr_accessor :host, :database, :schema_search_path, :username, :password, :log_level, :index_path, :socket
180
+
181
+ def log_stream=(val)
182
+ @log_stream = (val.is_a?(String) && val =~ /STDOUT/ ? STDOUT : val)
183
+ end
177
184
 
178
185
  # Allows us to set the adapter for this database object. It can only be set once, and expects two types of values.
179
186
  #
@@ -206,17 +213,21 @@ module DataMapper
206
213
 
207
214
  # Default Logger from Ruby's logger.rb
208
215
  def logger
209
- @logger = Logger.new(@log_stream, File::WRONLY | File::APPEND | File::CREAT)
210
- @logger.level = @log_level
211
- at_exit { @logger.close }
212
-
216
+ @logger = create_logger
217
+
213
218
  class << self
214
219
  attr_reader :logger
215
220
  end
216
-
221
+
217
222
  return @logger
218
223
  end
219
224
 
225
+ def create_logger
226
+ x = Logger.new(@log_stream, File::WRONLY | File::APPEND | File::CREAT)
227
+ x.level = @log_level
228
+ at_exit { x.close }
229
+ return x
230
+ end
220
231
  end
221
232
 
222
233
  end
@@ -0,0 +1,28 @@
1
+ module DataMapper
2
+ class DependencyQueue
3
+
4
+ def initialize
5
+ @dependencies = Hash.new { |h,k| h[k] = [] }
6
+ end
7
+
8
+ def add(class_name, &b)
9
+ @dependencies[class_name] << b
10
+ resolve!
11
+ end
12
+
13
+ def resolve!
14
+ @dependencies.each_pair do |class_name, callbacks|
15
+ if Object.const_defined?(class_name)
16
+ klass = Object.const_get(class_name)
17
+
18
+ callbacks.each do |b|
19
+ b.call(klass)
20
+ end
21
+
22
+ callbacks.clear
23
+ end
24
+ end
25
+ end
26
+
27
+ end # class DependencyQueue
28
+ end # module DataMapper