datamapper 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/example.rb +5 -5
- data/lib/data_mapper/adapters/abstract_adapter.rb +2 -2
- data/lib/data_mapper/adapters/data_object_adapter.rb +141 -147
- data/lib/data_mapper/adapters/mysql_adapter.rb +14 -1
- data/lib/data_mapper/adapters/postgresql_adapter.rb +123 -18
- data/lib/data_mapper/adapters/sql/coersion.rb +21 -9
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +36 -19
- data/lib/data_mapper/adapters/sql/mappings/column.rb +111 -17
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +27 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +256 -29
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +93 -8
- data/lib/data_mapper/associations/belongs_to_association.rb +53 -54
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +157 -25
- data/lib/data_mapper/associations/has_many_association.rb +45 -15
- data/lib/data_mapper/associations/has_n_association.rb +79 -20
- data/lib/data_mapper/associations/has_one_association.rb +2 -2
- data/lib/data_mapper/associations/reference.rb +1 -1
- data/lib/data_mapper/auto_migrations.rb +40 -0
- data/lib/data_mapper/base.rb +201 -98
- data/lib/data_mapper/context.rb +16 -10
- data/lib/data_mapper/database.rb +22 -11
- data/lib/data_mapper/dependency_queue.rb +28 -0
- data/lib/data_mapper/embedded_value.rb +61 -17
- data/lib/data_mapper/property.rb +4 -0
- data/lib/data_mapper/support/active_record_impersonation.rb +13 -5
- data/lib/data_mapper/support/errors.rb +5 -0
- data/lib/data_mapper/support/serialization.rb +8 -4
- data/lib/data_mapper/validatable_extensions/errors.rb +12 -0
- data/lib/data_mapper/validatable_extensions/macros.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +62 -0
- data/lib/data_mapper/validatable_extensions/validation_base.rb +18 -0
- data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +43 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +28 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +15 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +7 -0
- data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +33 -0
- data/lib/data_mapper/validations.rb +20 -0
- data/lib/data_mapper.rb +39 -34
- data/performance.rb +24 -18
- data/plugins/dataobjects/do_rb +0 -0
- data/rakefile.rb +12 -2
- data/spec/active_record_impersonation_spec.rb +133 -0
- data/spec/acts_as_tree_spec.rb +25 -9
- data/spec/associations_spec.rb +124 -4
- data/spec/attributes_spec.rb +13 -0
- data/spec/auto_migrations_spec.rb +44 -0
- data/spec/base_spec.rb +189 -1
- data/spec/column_spec.rb +85 -7
- data/spec/conditions_spec.rb +2 -2
- data/spec/dependency_spec.rb +25 -0
- data/spec/embedded_value_spec.rb +123 -3
- data/spec/fixtures/animals.yaml +1 -0
- data/spec/fixtures/careers.yaml +5 -0
- data/spec/fixtures/comments.yaml +1 -0
- data/spec/fixtures/people.yaml +14 -9
- data/spec/fixtures/projects.yaml +4 -0
- data/spec/fixtures/sections.yaml +5 -0
- data/spec/fixtures/serializers.yaml +6 -0
- data/spec/fixtures/users.yaml +1 -0
- data/spec/load_command_spec.rb +5 -4
- data/spec/mock_adapter.rb +2 -2
- data/spec/models/animal.rb +2 -1
- data/spec/models/animals_exhibit.rb +2 -2
- data/spec/models/career.rb +6 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/exhibit.rb +4 -0
- data/spec/models/person.rb +3 -13
- data/spec/models/project.rb +1 -1
- data/spec/models/serializer.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/zoo.rb +8 -1
- data/spec/natural_key_spec.rb +36 -0
- data/spec/paranoia_spec.rb +36 -0
- data/spec/property_spec.rb +70 -0
- data/spec/schema_spec.rb +10 -2
- data/spec/serialization_spec.rb +6 -3
- data/spec/serialize_spec.rb +19 -0
- data/spec/single_table_inheritance_spec.rb +7 -1
- data/spec/spec_helper.rb +26 -8
- data/spec/table_spec.rb +33 -0
- data/spec/validates_confirmation_of_spec.rb +20 -4
- data/spec/validates_format_of_spec.rb +22 -8
- data/spec/validates_length_of_spec.rb +26 -13
- data/spec/validates_uniqueness_of_spec.rb +18 -5
- data/spec/validations_spec.rb +55 -10
- data/tasks/fixtures.rb +13 -7
- metadata +189 -153
- data/lib/data_mapper/validations/confirmation_validator.rb +0 -53
- data/lib/data_mapper/validations/contextual_validations.rb +0 -50
- data/lib/data_mapper/validations/format_validator.rb +0 -85
- data/lib/data_mapper/validations/formats/email.rb +0 -78
- data/lib/data_mapper/validations/generic_validator.rb +0 -22
- data/lib/data_mapper/validations/length_validator.rb +0 -76
- data/lib/data_mapper/validations/required_field_validator.rb +0 -41
- data/lib/data_mapper/validations/unique_validator.rb +0 -56
- data/lib/data_mapper/validations/validation_errors.rb +0 -37
- data/lib/data_mapper/validations/validation_helper.rb +0 -77
- data/plugins/dataobjects/REVISION +0 -1
- data/plugins/dataobjects/Rakefile +0 -9
- data/plugins/dataobjects/do.rb +0 -348
- data/plugins/dataobjects/do_mysql.rb +0 -212
- data/plugins/dataobjects/do_postgres.rb +0 -196
- data/plugins/dataobjects/do_sqlite3.rb +0 -157
- data/plugins/dataobjects/spec/do_spec.rb +0 -150
- data/plugins/dataobjects/spec/spec_helper.rb +0 -81
- data/plugins/dataobjects/swig_mysql/extconf.rb +0 -45
- data/plugins/dataobjects/swig_mysql/mysql_c.c +0 -16602
- data/plugins/dataobjects/swig_mysql/mysql_c.i +0 -67
- data/plugins/dataobjects/swig_mysql/mysql_supp.i +0 -46
- data/plugins/dataobjects/swig_postgres/extconf.rb +0 -29
- data/plugins/dataobjects/swig_postgres/postgres_c.c +0 -8185
- data/plugins/dataobjects/swig_postgres/postgres_c.i +0 -73
- data/plugins/dataobjects/swig_sqlite/extconf.rb +0 -9
- data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +0 -4725
- data/plugins/dataobjects/swig_sqlite/sqlite_c.i +0 -168
- data/tasks/drivers.rb +0 -20
data/lib/data_mapper/base.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
128
|
-
|
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(
|
206
|
+
def self.property_getter(mapping, visibility = :public)
|
132
207
|
if mapping.lazy?
|
133
208
|
class_eval <<-EOS
|
134
|
-
|
135
|
-
|
136
|
-
|
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(
|
227
|
+
def self.property_setter(mapping, visibility = :public)
|
145
228
|
if mapping.lazy?
|
146
229
|
class_eval <<-EOS
|
147
|
-
|
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
|
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
|
-
|
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 => ([
|
266
|
+
:select => ([key] + names),
|
174
267
|
:reload => true,
|
175
|
-
|
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
|
-
|
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
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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 =
|
335
|
+
table = database_context.table(self.class)
|
232
336
|
|
233
|
-
values_hash.
|
234
|
-
|
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
|
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?
|
245
|
-
|
246
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
339
|
-
@
|
426
|
+
def database_context=(value)
|
427
|
+
@database_context = value
|
340
428
|
end
|
341
429
|
|
342
|
-
def
|
343
|
-
@
|
430
|
+
def database_context
|
431
|
+
@database_context || ( @database_context = database )
|
344
432
|
end
|
345
433
|
|
346
434
|
def key=(value)
|
347
|
-
key_column =
|
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 =
|
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
|
data/lib/data_mapper/context.rb
CHANGED
@@ -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(
|
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 = {
|
34
|
+
options = { key => args.first } # no options, set id
|
33
35
|
end
|
34
36
|
else
|
35
|
-
raise ArgumentError.new('
|
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?(
|
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,
|
50
|
-
|
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
|
-
|
68
|
+
table(klass).truncate!
|
67
69
|
end
|
68
70
|
|
69
71
|
def create_table(klass)
|
70
|
-
|
72
|
+
table(klass).create!
|
71
73
|
end
|
72
74
|
|
73
75
|
def drop_table(klass)
|
74
|
-
|
76
|
+
table(klass).drop!
|
75
77
|
end
|
76
78
|
|
77
79
|
def table_exists?(klass)
|
78
|
-
|
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)
|
data/lib/data_mapper/database.rb
CHANGED
@@ -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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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 =
|
210
|
-
|
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
|