dm-core 0.10.2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -1
- data/Gemfile +143 -0
- data/Rakefile +9 -5
- data/VERSION +1 -1
- data/dm-core.gemspec +160 -57
- data/lib/dm-core.rb +131 -56
- data/lib/dm-core/adapters.rb +98 -14
- data/lib/dm-core/adapters/abstract_adapter.rb +24 -4
- data/lib/dm-core/adapters/in_memory_adapter.rb +7 -2
- data/lib/dm-core/associations/many_to_many.rb +19 -30
- data/lib/dm-core/associations/many_to_one.rb +58 -42
- data/lib/dm-core/associations/one_to_many.rb +33 -23
- data/lib/dm-core/associations/one_to_one.rb +27 -11
- data/lib/dm-core/associations/relationship.rb +4 -4
- data/lib/dm-core/collection.rb +23 -16
- data/lib/dm-core/core_ext/array.rb +36 -0
- data/lib/dm-core/core_ext/hash.rb +30 -0
- data/lib/dm-core/core_ext/module.rb +46 -0
- data/lib/dm-core/core_ext/object.rb +31 -0
- data/lib/dm-core/core_ext/pathname.rb +20 -0
- data/lib/dm-core/core_ext/string.rb +22 -0
- data/lib/dm-core/core_ext/try_dup.rb +44 -0
- data/lib/dm-core/model.rb +88 -27
- data/lib/dm-core/model/hook.rb +75 -18
- data/lib/dm-core/model/property.rb +50 -9
- data/lib/dm-core/model/relationship.rb +31 -31
- data/lib/dm-core/model/scope.rb +3 -3
- data/lib/dm-core/property.rb +196 -516
- data/lib/dm-core/property/binary.rb +7 -0
- data/lib/dm-core/property/boolean.rb +35 -0
- data/lib/dm-core/property/class.rb +24 -0
- data/lib/dm-core/property/date.rb +47 -0
- data/lib/dm-core/property/date_time.rb +48 -0
- data/lib/dm-core/property/decimal.rb +43 -0
- data/lib/dm-core/property/discriminator.rb +48 -0
- data/lib/dm-core/property/float.rb +24 -0
- data/lib/dm-core/property/integer.rb +32 -0
- data/lib/dm-core/property/numeric.rb +43 -0
- data/lib/dm-core/property/object.rb +32 -0
- data/lib/dm-core/property/serial.rb +8 -0
- data/lib/dm-core/property/string.rb +49 -0
- data/lib/dm-core/property/text.rb +12 -0
- data/lib/dm-core/property/time.rb +48 -0
- data/lib/dm-core/property/typecast/numeric.rb +32 -0
- data/lib/dm-core/property/typecast/time.rb +28 -0
- data/lib/dm-core/property_set.rb +10 -4
- data/lib/dm-core/query.rb +14 -37
- data/lib/dm-core/query/conditions/comparison.rb +8 -6
- data/lib/dm-core/query/conditions/operation.rb +33 -2
- data/lib/dm-core/query/operator.rb +2 -5
- data/lib/dm-core/query/path.rb +4 -6
- data/lib/dm-core/repository.rb +21 -6
- data/lib/dm-core/resource.rb +316 -133
- data/lib/dm-core/resource/state.rb +79 -0
- data/lib/dm-core/resource/state/clean.rb +40 -0
- data/lib/dm-core/resource/state/deleted.rb +30 -0
- data/lib/dm-core/resource/state/dirty.rb +86 -0
- data/lib/dm-core/resource/state/immutable.rb +34 -0
- data/lib/dm-core/resource/state/persisted.rb +29 -0
- data/lib/dm-core/resource/state/transient.rb +70 -0
- data/lib/dm-core/spec/lib/adapter_helpers.rb +52 -0
- data/lib/dm-core/spec/lib/collection_helpers.rb +20 -0
- data/{spec → lib/dm-core/spec}/lib/counter_adapter.rb +5 -1
- data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
- data/lib/dm-core/spec/lib/spec_helper.rb +68 -0
- data/lib/dm-core/spec/setup.rb +165 -0
- data/lib/dm-core/spec/{adapter_shared_spec.rb → shared/adapter_spec.rb} +21 -7
- data/{spec/public/shared/resource_shared_spec.rb → lib/dm-core/spec/shared/resource_spec.rb} +120 -83
- data/{spec/public/shared/sel_shared_spec.rb → lib/dm-core/spec/shared/sel_spec.rb} +5 -6
- data/lib/dm-core/support/assertions.rb +8 -0
- data/lib/dm-core/support/equalizer.rb +1 -0
- data/lib/dm-core/support/hook.rb +420 -0
- data/lib/dm-core/support/lazy_array.rb +453 -0
- data/lib/dm-core/support/local_object_space.rb +12 -0
- data/lib/dm-core/support/logger.rb +193 -6
- data/lib/dm-core/support/naming_conventions.rb +8 -8
- data/lib/dm-core/support/subject.rb +33 -0
- data/lib/dm-core/type.rb +4 -0
- data/lib/dm-core/types/boolean.rb +2 -0
- data/lib/dm-core/types/decimal.rb +9 -0
- data/lib/dm-core/types/discriminator.rb +2 -0
- data/lib/dm-core/types/object.rb +3 -0
- data/lib/dm-core/types/serial.rb +2 -0
- data/lib/dm-core/types/text.rb +2 -0
- data/lib/dm-core/version.rb +1 -1
- data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +67 -0
- data/spec/public/model/hook_spec.rb +209 -0
- data/spec/public/model/property_spec.rb +35 -0
- data/spec/public/model/relationship_spec.rb +33 -20
- data/spec/public/model_spec.rb +142 -10
- data/spec/public/property/binary_spec.rb +14 -0
- data/spec/public/property/boolean_spec.rb +14 -0
- data/spec/public/property/class_spec.rb +20 -0
- data/spec/public/property/date_spec.rb +14 -0
- data/spec/public/property/date_time_spec.rb +14 -0
- data/spec/public/property/decimal_spec.rb +14 -0
- data/spec/public/{types → property}/discriminator_spec.rb +2 -12
- data/spec/public/property/float_spec.rb +14 -0
- data/spec/public/property/integer_spec.rb +14 -0
- data/spec/public/property/object_spec.rb +9 -17
- data/spec/public/property/serial_spec.rb +14 -0
- data/spec/public/property/string_spec.rb +14 -0
- data/spec/public/property/text_spec.rb +52 -0
- data/spec/public/property/time_spec.rb +14 -0
- data/spec/public/property_spec.rb +28 -87
- data/spec/public/resource_spec.rb +101 -0
- data/spec/public/sel_spec.rb +5 -15
- data/spec/public/shared/collection_shared_spec.rb +16 -30
- data/spec/public/shared/finder_shared_spec.rb +2 -4
- data/spec/public/shared/property_shared_spec.rb +176 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +2 -2
- data/spec/semipublic/associations/many_to_many_spec.rb +89 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +24 -1
- data/spec/semipublic/associations/one_to_many_spec.rb +51 -0
- data/spec/semipublic/associations/one_to_one_spec.rb +49 -0
- data/spec/semipublic/associations/relationship_spec.rb +3 -3
- data/spec/semipublic/associations_spec.rb +1 -1
- data/spec/semipublic/property/binary_spec.rb +13 -0
- data/spec/semipublic/property/boolean_spec.rb +65 -0
- data/spec/semipublic/property/class_spec.rb +33 -0
- data/spec/semipublic/property/date_spec.rb +43 -0
- data/spec/semipublic/property/date_time_spec.rb +46 -0
- data/spec/semipublic/property/decimal_spec.rb +82 -0
- data/spec/semipublic/property/discriminator_spec.rb +19 -0
- data/spec/semipublic/property/float_spec.rb +82 -0
- data/spec/semipublic/property/integer_spec.rb +82 -0
- data/spec/semipublic/property/serial_spec.rb +13 -0
- data/spec/semipublic/property/string_spec.rb +13 -0
- data/spec/semipublic/property/text_spec.rb +31 -0
- data/spec/semipublic/property/time_spec.rb +50 -0
- data/spec/semipublic/property_spec.rb +2 -532
- data/spec/semipublic/query/conditions/comparison_spec.rb +171 -169
- data/spec/semipublic/query/conditions/operation_spec.rb +53 -51
- data/spec/semipublic/query/path_spec.rb +17 -17
- data/spec/semipublic/query_spec.rb +47 -78
- data/spec/semipublic/resource/state/clean_spec.rb +88 -0
- data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
- data/spec/semipublic/resource/state/dirty_spec.rb +133 -0
- data/spec/semipublic/resource/state/immutable_spec.rb +99 -0
- data/spec/semipublic/resource/state/transient_spec.rb +128 -0
- data/spec/semipublic/resource/state_spec.rb +226 -0
- data/spec/semipublic/shared/property_shared_spec.rb +143 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +16 -15
- data/spec/semipublic/shared/resource_state_shared_spec.rb +78 -0
- data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
- data/spec/spec_helper.rb +21 -97
- data/spec/support/types/huge_integer.rb +17 -0
- data/spec/unit/array_spec.rb +48 -0
- data/spec/unit/hash_spec.rb +35 -0
- data/spec/unit/hook_spec.rb +1234 -0
- data/spec/unit/lazy_array_spec.rb +1959 -0
- data/spec/unit/module_spec.rb +70 -0
- data/spec/unit/object_spec.rb +37 -0
- data/spec/unit/try_dup_spec.rb +45 -0
- data/tasks/local_gemfile.rake +18 -0
- data/tasks/spec.rake +0 -3
- metadata +197 -71
- data/deps.rip +0 -2
- data/lib/dm-core/adapters/data_objects_adapter.rb +0 -712
- data/lib/dm-core/adapters/mysql_adapter.rb +0 -42
- data/lib/dm-core/adapters/oracle_adapter.rb +0 -229
- data/lib/dm-core/adapters/postgres_adapter.rb +0 -22
- data/lib/dm-core/adapters/sqlite3_adapter.rb +0 -17
- data/lib/dm-core/adapters/sqlserver_adapter.rb +0 -114
- data/lib/dm-core/adapters/yaml_adapter.rb +0 -111
- data/lib/dm-core/core_ext/enumerable.rb +0 -28
- data/lib/dm-core/migrations.rb +0 -1427
- data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +0 -366
- data/lib/dm-core/transaction.rb +0 -508
- data/lib/dm-core/types/paranoid_boolean.rb +0 -42
- data/lib/dm-core/types/paranoid_datetime.rb +0 -41
- data/spec/lib/adapter_helpers.rb +0 -105
- data/spec/lib/collection_helpers.rb +0 -18
- data/spec/lib/pending_helpers.rb +0 -46
- data/spec/public/migrations_spec.rb +0 -503
- data/spec/public/transaction_spec.rb +0 -153
- data/spec/semipublic/adapters/mysql_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/oracle_adapter_spec.rb +0 -194
- data/spec/semipublic/adapters/postgres_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +0 -17
- data/spec/semipublic/adapters/yaml_adapter_spec.rb +0 -12
@@ -6,9 +6,10 @@ module DataMapper
|
|
6
6
|
# (1 to 1, 1 to n, n to m) implements a subclass of this class
|
7
7
|
# with methods like get and set overridden.
|
8
8
|
class Relationship
|
9
|
-
include
|
9
|
+
include DataMapper::Assertions
|
10
|
+
include Subject
|
10
11
|
|
11
|
-
OPTIONS = [ :child_repository_name, :parent_repository_name, :child_key, :parent_key, :min, :max, :inverse, :reader_visibility, :writer_visibility ].to_set
|
12
|
+
OPTIONS = [ :child_repository_name, :parent_repository_name, :child_key, :parent_key, :min, :max, :inverse, :reader_visibility, :writer_visibility, :default ].to_set
|
12
13
|
|
13
14
|
# Relationship name
|
14
15
|
#
|
@@ -319,8 +320,6 @@ module DataMapper
|
|
319
320
|
#
|
320
321
|
# @api semipublic
|
321
322
|
def loaded?(resource)
|
322
|
-
assert_kind_of 'resource', resource, source_model
|
323
|
-
|
324
323
|
resource.instance_variable_defined?(instance_variable_name)
|
325
324
|
end
|
326
325
|
|
@@ -449,6 +448,7 @@ module DataMapper
|
|
449
448
|
@max = @options[:max]
|
450
449
|
@reader_visibility = @options.fetch(:reader_visibility, :public)
|
451
450
|
@writer_visibility = @options.fetch(:writer_visibility, :public)
|
451
|
+
@default = @options.fetch(:default, nil)
|
452
452
|
|
453
453
|
# TODO: normalize the @query to become :conditions => AndOperation
|
454
454
|
# - Property/Relationship/Path should be left alone
|
data/lib/dm-core/collection.rb
CHANGED
@@ -352,8 +352,12 @@ module DataMapper
|
|
352
352
|
def at(offset)
|
353
353
|
if loaded? || partially_loaded?(offset)
|
354
354
|
super
|
355
|
-
elsif offset
|
355
|
+
elsif offset == 0
|
356
|
+
first
|
357
|
+
elsif offset > 0
|
356
358
|
first(:offset => offset)
|
359
|
+
elsif offset == -1
|
360
|
+
last
|
357
361
|
else
|
358
362
|
last(:offset => offset.abs - 1)
|
359
363
|
end
|
@@ -787,7 +791,7 @@ module DataMapper
|
|
787
791
|
#
|
788
792
|
# @api public
|
789
793
|
def create(attributes = {})
|
790
|
-
_create(
|
794
|
+
_create(attributes)
|
791
795
|
end
|
792
796
|
|
793
797
|
# Create a Resource in the Collection, bypassing hooks
|
@@ -800,7 +804,7 @@ module DataMapper
|
|
800
804
|
#
|
801
805
|
# @api public
|
802
806
|
def create!(attributes = {})
|
803
|
-
_create(
|
807
|
+
_create(attributes, false)
|
804
808
|
end
|
805
809
|
|
806
810
|
# Update every Resource in the Collection
|
@@ -814,7 +818,7 @@ module DataMapper
|
|
814
818
|
# true if the resources were successfully updated
|
815
819
|
#
|
816
820
|
# @api public
|
817
|
-
def update(attributes
|
821
|
+
def update(attributes)
|
818
822
|
assert_update_clean_only(:update)
|
819
823
|
|
820
824
|
dirty_attributes = model.new(attributes).dirty_attributes
|
@@ -832,7 +836,7 @@ module DataMapper
|
|
832
836
|
# true if the resources were successfully updated
|
833
837
|
#
|
834
838
|
# @api public
|
835
|
-
def update!(attributes
|
839
|
+
def update!(attributes)
|
836
840
|
assert_update_clean_only(:update!)
|
837
841
|
|
838
842
|
model = self.model
|
@@ -866,7 +870,7 @@ module DataMapper
|
|
866
870
|
#
|
867
871
|
# @api public
|
868
872
|
def save
|
869
|
-
_save
|
873
|
+
_save
|
870
874
|
end
|
871
875
|
|
872
876
|
# Save every Resource in the Collection bypassing validation
|
@@ -915,7 +919,10 @@ module DataMapper
|
|
915
919
|
return false
|
916
920
|
end
|
917
921
|
|
918
|
-
each
|
922
|
+
each do |resource|
|
923
|
+
resource.persisted_state = Resource::State::Immutable.new(resource)
|
924
|
+
end
|
925
|
+
|
919
926
|
clear
|
920
927
|
else
|
921
928
|
mark_loaded
|
@@ -1192,8 +1199,8 @@ module DataMapper
|
|
1192
1199
|
|
1193
1200
|
# Creates a resource in the collection
|
1194
1201
|
#
|
1195
|
-
# @param [Boolean]
|
1196
|
-
# Whether to
|
1202
|
+
# @param [Boolean] execute_hooks
|
1203
|
+
# Whether to execute hooks or not
|
1197
1204
|
# @param [Hash] attributes
|
1198
1205
|
# Attributes with which to create the new resource
|
1199
1206
|
#
|
@@ -1201,8 +1208,8 @@ module DataMapper
|
|
1201
1208
|
# a saved Resource
|
1202
1209
|
#
|
1203
1210
|
# @api private
|
1204
|
-
def _create(
|
1205
|
-
resource = repository.scope { model.send(
|
1211
|
+
def _create(attributes, execute_hooks = true)
|
1212
|
+
resource = repository.scope { model.send(execute_hooks ? :create : :create!, default_attributes.merge(attributes)) }
|
1206
1213
|
self << resource if resource.saved?
|
1207
1214
|
resource
|
1208
1215
|
end
|
@@ -1220,18 +1227,18 @@ module DataMapper
|
|
1220
1227
|
|
1221
1228
|
# Saves a collection
|
1222
1229
|
#
|
1223
|
-
# @param [
|
1224
|
-
#
|
1230
|
+
# @param [Boolean] execute_hooks
|
1231
|
+
# Whether to execute hooks or not
|
1225
1232
|
#
|
1226
1233
|
# @return [Boolean]
|
1227
1234
|
# Returns true if collection was updated
|
1228
1235
|
#
|
1229
1236
|
# @api private
|
1230
|
-
def _save(
|
1237
|
+
def _save(execute_hooks = true)
|
1231
1238
|
loaded_entries = self.loaded_entries
|
1232
1239
|
loaded_entries.each { |resource| set_default_attributes(resource) }
|
1233
1240
|
@removed.clear
|
1234
|
-
loaded_entries.all? { |resource| resource.__send__(
|
1241
|
+
loaded_entries.all? { |resource| resource.__send__(execute_hooks ? :save : :save!) }
|
1235
1242
|
end
|
1236
1243
|
|
1237
1244
|
# Returns default values to initialize new Resources in the Collection
|
@@ -1436,7 +1443,7 @@ module DataMapper
|
|
1436
1443
|
|
1437
1444
|
if model.model_method_defined?(method)
|
1438
1445
|
delegate_to_model(method, *args, &block)
|
1439
|
-
elsif relationship = relationships[method] || relationships[method.to_s.
|
1446
|
+
elsif relationship = relationships[method] || relationships[method.to_s.singularize.to_sym]
|
1440
1447
|
delegate_to_relationship(relationship, *args)
|
1441
1448
|
else
|
1442
1449
|
super
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
##
|
4
|
+
# Transforms an Array of key/value pairs into a Hash
|
5
|
+
#
|
6
|
+
# This is a better idiom than using Hash[*array.flatten] in Ruby 1.8.6
|
7
|
+
# because it is not possible to limit the flattening to a single
|
8
|
+
# level.
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
# A Hash where each entry in the Array is turned into a key/value
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def to_hash
|
15
|
+
h = {}
|
16
|
+
each { |k,v| h[k] = v }
|
17
|
+
h
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Transforms an Array of key/value pairs into a Mash
|
22
|
+
#
|
23
|
+
# This is a better idiom than using Mash[*array.flatten] in Ruby 1.8.6
|
24
|
+
# because it is not possible to limit the flattening to a single
|
25
|
+
# level.
|
26
|
+
#
|
27
|
+
# @return [Mash]
|
28
|
+
# A Hash where each entry in the Array is turned into a key/value
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def to_mash
|
32
|
+
m = Mash.new
|
33
|
+
each { |k,v| m[k] = v }
|
34
|
+
m
|
35
|
+
end
|
36
|
+
end # class Array
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
##
|
4
|
+
# Create a hash with *only* key/value pairs in receiver and +allowed+
|
5
|
+
#
|
6
|
+
# { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 }
|
7
|
+
#
|
8
|
+
# @param [Array[String, Symbol]] *allowed The hash keys to include.
|
9
|
+
#
|
10
|
+
# @return [Hash] A new hash with only the selected keys.
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def only(*allowed)
|
14
|
+
hash = {}
|
15
|
+
allowed.each {|k| hash[k] = self[k] if self.has_key?(k) }
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# Convert to Mash. This class has semantics of ActiveSupport's
|
20
|
+
# HashWithIndifferentAccess and we only have it so that people can write
|
21
|
+
# params[:key] instead of params['key'].
|
22
|
+
#
|
23
|
+
# @return [Mash] This hash as a Mash for string or symbol key access.
|
24
|
+
def to_mash
|
25
|
+
hash = Mash.new(self)
|
26
|
+
hash.default = default
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'dm-core/core_ext/object'
|
2
|
+
|
3
|
+
class Module
|
4
|
+
|
5
|
+
def find_const(const_name)
|
6
|
+
if const_name[0..1] == '::'
|
7
|
+
Object.full_const_get(const_name[2..-1])
|
8
|
+
else
|
9
|
+
nested_const_lookup(const_name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Doesn't do any caching since constants can change with remove_const
|
16
|
+
def nested_const_lookup(const_name)
|
17
|
+
unless equal?(Object)
|
18
|
+
constants = []
|
19
|
+
|
20
|
+
name.split('::').each do |part|
|
21
|
+
const = constants.last || Object
|
22
|
+
constants << const.const_get(part)
|
23
|
+
end
|
24
|
+
|
25
|
+
parts = const_name.split('::')
|
26
|
+
|
27
|
+
# from most to least specific constant, use each as a base and try
|
28
|
+
# to find a constant with the name const_name within them
|
29
|
+
constants.reverse_each do |const|
|
30
|
+
# return the nested constant if available
|
31
|
+
return const if parts.all? do |part|
|
32
|
+
const = if RUBY_VERSION >= '1.9.0'
|
33
|
+
const.const_defined?(part, false) ? const.const_get(part, false) : nil
|
34
|
+
else
|
35
|
+
const.const_defined?(part) ? const.const_get(part) : nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# no relative constant found, fallback to an absolute lookup and
|
42
|
+
# use const_missing if not found
|
43
|
+
Object.full_const_get(const_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
# @param name<String> The name of the constant to get, e.g. "Merb::Router".
|
4
|
+
#
|
5
|
+
# @return [Object] The constant corresponding to the name.
|
6
|
+
def full_const_get(name)
|
7
|
+
list = name.split("::")
|
8
|
+
list.shift if list.first.blank?
|
9
|
+
obj = self
|
10
|
+
list.each do |x|
|
11
|
+
# This is required because const_get tries to look for constants in the
|
12
|
+
# ancestor chain, but we only want constants that are HERE
|
13
|
+
obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
|
14
|
+
end
|
15
|
+
obj
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param name<String> The name of the constant to get, e.g. "Merb::Router".
|
19
|
+
# @param value<Object> The value to assign to the constant.
|
20
|
+
#
|
21
|
+
# @return [Object] The constant corresponding to the name.
|
22
|
+
def full_const_set(name, value)
|
23
|
+
list = name.split("::")
|
24
|
+
toplevel = list.first.blank?
|
25
|
+
list.shift if toplevel
|
26
|
+
last = list.pop
|
27
|
+
obj = list.empty? ? Object : Object.full_const_get(list.join("::"))
|
28
|
+
obj.const_set(last, value) if obj && !obj.const_defined?(last)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Pathname
|
2
|
+
# Append path segments and expand to absolute path
|
3
|
+
#
|
4
|
+
# file = Pathname(Dir.pwd) / "subdir1" / :subdir2 / "filename.ext"
|
5
|
+
#
|
6
|
+
# @param [Pathname, String, #to_s] path path segment to concatenate with receiver
|
7
|
+
#
|
8
|
+
# @return [Pathname]
|
9
|
+
# receiver with _path_ appended and expanded to an absolute path
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
def /(path)
|
13
|
+
(self + path).expand_path
|
14
|
+
end
|
15
|
+
|
16
|
+
# alias to_s to to_str when to_str not defined
|
17
|
+
unless public_instance_methods(false).any? { |m| m.to_sym == :to_str }
|
18
|
+
alias to_str to_s
|
19
|
+
end
|
20
|
+
end # class Pathname
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
##
|
4
|
+
# Replace sequences of whitespace (including newlines) with either
|
5
|
+
# a single space or remove them entirely (according to param _spaced_)
|
6
|
+
#
|
7
|
+
# <<QUERY.compress_lines
|
8
|
+
# SELECT name
|
9
|
+
# FROM users
|
10
|
+
# QUERY => "SELECT name FROM users"
|
11
|
+
#
|
12
|
+
# @param [TrueClass, FalseClass] spaced (default=true)
|
13
|
+
# Determines whether returned string has whitespace collapsed or removed
|
14
|
+
#
|
15
|
+
# @return [String] Receiver with whitespace (including newlines) replaced
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def compress_lines(spaced = true)
|
19
|
+
split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Object
|
2
|
+
# Override this in a child if it cannot be dup'ed
|
3
|
+
#
|
4
|
+
# @return [Object]
|
5
|
+
def try_dup
|
6
|
+
self.dup
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TrueClass
|
11
|
+
def try_dup
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FalseClass
|
17
|
+
def try_dup
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Module
|
23
|
+
def try_dup
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class NilClass
|
29
|
+
def try_dup
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Numeric
|
35
|
+
def try_dup
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Symbol
|
41
|
+
def try_dup
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
data/lib/dm-core/model.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# TODO: add Model#create!, Model#update, Model#update!, Model#destroy and Model#destroy!
|
2
2
|
|
3
|
+
# TODO: DRY up raise_on_save_failure with attr_accessor_with_default
|
4
|
+
# once AS branch is merged in
|
5
|
+
|
3
6
|
module DataMapper
|
4
7
|
module Model
|
5
8
|
extend Chainable
|
@@ -30,7 +33,7 @@ module DataMapper
|
|
30
33
|
warn "Passing in +storage_name+ to #{name}.new is deprecated (#{caller[0]})"
|
31
34
|
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
32
35
|
def self.default_storage_name
|
33
|
-
#{
|
36
|
+
#{ActiveSupport::Inflector.classify(storage_name).inspect}.freeze
|
34
37
|
end
|
35
38
|
RUBY
|
36
39
|
end
|
@@ -72,6 +75,68 @@ module DataMapper
|
|
72
75
|
# @api semipublic
|
73
76
|
attr_reader :descendants
|
74
77
|
|
78
|
+
# Return if Resource#save should raise an exception on save failures (globally)
|
79
|
+
#
|
80
|
+
# This is false by default.
|
81
|
+
#
|
82
|
+
# DataMapper::Model.raise_on_save_failure # => false
|
83
|
+
#
|
84
|
+
# @return [Boolean]
|
85
|
+
# true if a failure in Resource#save should raise an exception
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
def self.raise_on_save_failure
|
89
|
+
if defined?(@raise_on_save_failure)
|
90
|
+
@raise_on_save_failure
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Specify if Resource#save should raise an exception on save failures (globally)
|
97
|
+
#
|
98
|
+
# @param [Boolean]
|
99
|
+
# a boolean that if true will cause Resource#save to raise an exception
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
# true if a failure in Resource#save should raise an exception
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def self.raise_on_save_failure=(raise_on_save_failure)
|
106
|
+
@raise_on_save_failure = raise_on_save_failure
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return if Resource#save should raise an exception on save failures (per-model)
|
110
|
+
#
|
111
|
+
# This delegates to DataMapper::Model.raise_on_save_failure by default.
|
112
|
+
#
|
113
|
+
# User.raise_on_save_failure # => false
|
114
|
+
#
|
115
|
+
# @return [Boolean]
|
116
|
+
# true if a failure in Resource#save should raise an exception
|
117
|
+
#
|
118
|
+
# @api public
|
119
|
+
def raise_on_save_failure
|
120
|
+
if defined?(@raise_on_save_failure)
|
121
|
+
@raise_on_save_failure
|
122
|
+
else
|
123
|
+
DataMapper::Model.raise_on_save_failure
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Specify if Resource#save should raise an exception on save failures (per-model)
|
128
|
+
#
|
129
|
+
# @param [Boolean]
|
130
|
+
# a boolean that if true will cause Resource#save to raise an exception
|
131
|
+
#
|
132
|
+
# @return [Boolean]
|
133
|
+
# true if a failure in Resource#save should raise an exception
|
134
|
+
#
|
135
|
+
# @api public
|
136
|
+
def raise_on_save_failure=(raise_on_save_failure)
|
137
|
+
@raise_on_save_failure = raise_on_save_failure
|
138
|
+
end
|
139
|
+
|
75
140
|
# Appends a module for inclusion into the model class after Resource.
|
76
141
|
#
|
77
142
|
# This is a useful way to extend Resource while still retaining a
|
@@ -147,6 +212,8 @@ module DataMapper
|
|
147
212
|
model.instance_variable_set(:@default_order, {})
|
148
213
|
model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
|
149
214
|
|
215
|
+
model.extend(Chainable)
|
216
|
+
|
150
217
|
extra_extensions.each { |mod| model.extend(mod) }
|
151
218
|
extra_inclusions.each { |mod| model.send(:include, mod) }
|
152
219
|
end
|
@@ -163,13 +230,6 @@ module DataMapper
|
|
163
230
|
model.instance_variable_set(:@storage_names, @storage_names.dup)
|
164
231
|
model.instance_variable_set(:@default_order, @default_order.dup)
|
165
232
|
model.instance_variable_set(:@descendants, descendants.class.new(model, descendants))
|
166
|
-
|
167
|
-
# TODO: move this into dm-validations
|
168
|
-
if respond_to?(:validators)
|
169
|
-
validators.contexts.each do |context, validators|
|
170
|
-
model.validators.context(context).concat(validators)
|
171
|
-
end
|
172
|
-
end
|
173
233
|
end
|
174
234
|
end
|
175
235
|
|
@@ -410,7 +470,7 @@ module DataMapper
|
|
410
470
|
#
|
411
471
|
# @api public
|
412
472
|
def create(attributes = {})
|
413
|
-
_create(
|
473
|
+
_create(attributes)
|
414
474
|
end
|
415
475
|
|
416
476
|
# Create a Resource, bypassing hooks
|
@@ -423,7 +483,7 @@ module DataMapper
|
|
423
483
|
#
|
424
484
|
# @api public
|
425
485
|
def create!(attributes = {})
|
426
|
-
_create(
|
486
|
+
_create(attributes, false)
|
427
487
|
end
|
428
488
|
|
429
489
|
# Copy a set of records from one repository to another.
|
@@ -452,7 +512,7 @@ module DataMapper
|
|
452
512
|
|
453
513
|
all(query.merge(:repository => source_repository_name)).each do |resource|
|
454
514
|
new_resource = new
|
455
|
-
query[:fields].each { |property| property.
|
515
|
+
query[:fields].each { |property| new_resource.__send__("#{property.name}=", property.get(resource)) }
|
456
516
|
resources << new_resource if new_resource.save
|
457
517
|
end
|
458
518
|
|
@@ -490,7 +550,7 @@ module DataMapper
|
|
490
550
|
record = record.dup
|
491
551
|
field_map.each { |property, field| record[property] = record.delete(field) if record.key?(field) }
|
492
552
|
|
493
|
-
model = discriminator && record[discriminator] || self
|
553
|
+
model = discriminator && discriminator.load(record[discriminator]) || self
|
494
554
|
model_key = model.key(repository_name)
|
495
555
|
|
496
556
|
resource = if model_key.valid?(key_values = record.values_at(*model_key))
|
@@ -507,9 +567,7 @@ module DataMapper
|
|
507
567
|
|
508
568
|
# TODO: typecasting should happen inside the Adapter
|
509
569
|
# and all values should come back as expected objects
|
510
|
-
|
511
|
-
value = property.type.load(value, property)
|
512
|
-
end
|
570
|
+
value = property.load(value)
|
513
571
|
|
514
572
|
property.set!(resource, value)
|
515
573
|
end
|
@@ -533,14 +591,15 @@ module DataMapper
|
|
533
591
|
end
|
534
592
|
|
535
593
|
resource.instance_variable_set(:@_repository, repository)
|
536
|
-
resource.instance_variable_set(:@_saved, true)
|
537
594
|
|
538
595
|
if identity_map
|
596
|
+
resource.persisted_state = Resource::State::Clean.new(resource) unless resource.persisted_state?
|
597
|
+
|
539
598
|
# defer setting the IdentityMap so second level caches can
|
540
599
|
# record the state of the resource after loaded
|
541
600
|
identity_map[key_values] = resource
|
542
601
|
else
|
543
|
-
resource.
|
602
|
+
resource.persisted_state = Resource::State::Immutable.new(resource)
|
544
603
|
end
|
545
604
|
|
546
605
|
resource
|
@@ -622,9 +681,9 @@ module DataMapper
|
|
622
681
|
private
|
623
682
|
|
624
683
|
# @api private
|
625
|
-
def _create(
|
684
|
+
def _create(attributes, execute_hooks = true)
|
626
685
|
resource = new(attributes)
|
627
|
-
resource.__send__(
|
686
|
+
resource.__send__(execute_hooks ? :save : :save!)
|
628
687
|
resource
|
629
688
|
end
|
630
689
|
|
@@ -635,8 +694,10 @@ module DataMapper
|
|
635
694
|
self
|
636
695
|
elsif name == :Resource
|
637
696
|
Resource
|
638
|
-
elsif
|
639
|
-
|
697
|
+
elsif DataMapper::Property.const_defined?(name)
|
698
|
+
DataMapper::Property.const_get(name)
|
699
|
+
elsif DataMapper::Types.const_defined?(name)
|
700
|
+
DataMapper::Types.const_get(name)
|
640
701
|
else
|
641
702
|
super
|
642
703
|
end
|
@@ -681,14 +742,14 @@ module DataMapper
|
|
681
742
|
if self.query.repository == repository
|
682
743
|
query
|
683
744
|
else
|
684
|
-
|
745
|
+
repository.new_query(self, query.options)
|
685
746
|
end
|
686
747
|
end
|
687
748
|
end
|
688
749
|
|
689
750
|
# @api private
|
690
|
-
def assert_valid # :nodoc:
|
691
|
-
return if @valid
|
751
|
+
def assert_valid(force = false) # :nodoc:
|
752
|
+
return if @valid && !force
|
692
753
|
@valid = true
|
693
754
|
|
694
755
|
name = self.name
|
@@ -704,8 +765,8 @@ module DataMapper
|
|
704
765
|
end
|
705
766
|
|
706
767
|
# initialize join models and target keys
|
707
|
-
@relationships.
|
708
|
-
relationships.
|
768
|
+
@relationships.values.each do |relationships|
|
769
|
+
relationships.values.each do |relationship|
|
709
770
|
relationship.child_key
|
710
771
|
relationship.through if relationship.respond_to?(:through)
|
711
772
|
relationship.via if relationship.respond_to?(:via)
|
@@ -715,7 +776,7 @@ module DataMapper
|
|
715
776
|
|
716
777
|
# @api private
|
717
778
|
def model_methods
|
718
|
-
@model_methods ||= ancestor_instance_methods { |mod| mod.
|
779
|
+
@model_methods ||= ancestor_instance_methods { |mod| mod.singleton_class }
|
719
780
|
end
|
720
781
|
|
721
782
|
# @api private
|