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.
- 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
|