datamapper-dm-core 0.9.11 → 0.10.0
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/.autotest +17 -14
- data/.gitignore +3 -1
- data/FAQ +6 -5
- data/History.txt +5 -39
- data/Manifest.txt +67 -76
- data/QUICKLINKS +1 -1
- data/README.txt +21 -15
- data/Rakefile +16 -15
- data/SPECS +2 -29
- data/TODO +1 -1
- data/dm-core.gemspec +11 -15
- data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
- data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
- data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
- data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
- data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
- data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
- data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
- data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
- data/lib/dm-core/adapters.rb +135 -16
- data/lib/dm-core/associations/many_to_many.rb +372 -90
- data/lib/dm-core/associations/many_to_one.rb +220 -73
- data/lib/dm-core/associations/one_to_many.rb +319 -255
- data/lib/dm-core/associations/one_to_one.rb +66 -53
- data/lib/dm-core/associations/relationship.rb +560 -158
- data/lib/dm-core/collection.rb +1104 -381
- data/lib/dm-core/core_ext/kernel.rb +12 -0
- data/lib/dm-core/core_ext/symbol.rb +10 -0
- data/lib/dm-core/identity_map.rb +4 -34
- data/lib/dm-core/migrations.rb +1283 -0
- data/lib/dm-core/model/descendant_set.rb +81 -0
- data/lib/dm-core/model/hook.rb +45 -0
- data/lib/dm-core/model/is.rb +32 -0
- data/lib/dm-core/model/property.rb +248 -0
- data/lib/dm-core/model/relationship.rb +335 -0
- data/lib/dm-core/model/scope.rb +90 -0
- data/lib/dm-core/model.rb +570 -369
- data/lib/dm-core/property.rb +753 -280
- data/lib/dm-core/property_set.rb +141 -98
- data/lib/dm-core/query/conditions/comparison.rb +814 -0
- data/lib/dm-core/query/conditions/operation.rb +247 -0
- data/lib/dm-core/query/direction.rb +43 -0
- data/lib/dm-core/query/operator.rb +42 -0
- data/lib/dm-core/query/path.rb +102 -0
- data/lib/dm-core/query/sort.rb +45 -0
- data/lib/dm-core/query.rb +974 -492
- data/lib/dm-core/repository.rb +147 -107
- data/lib/dm-core/resource.rb +644 -429
- data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
- data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
- data/lib/dm-core/support/chainable.rb +20 -0
- data/lib/dm-core/support/deprecate.rb +12 -0
- data/lib/dm-core/support/equalizer.rb +23 -0
- data/lib/dm-core/support/logger.rb +13 -0
- data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
- data/lib/dm-core/transaction.rb +333 -92
- data/lib/dm-core/type.rb +98 -60
- data/lib/dm-core/types/boolean.rb +1 -1
- data/lib/dm-core/types/discriminator.rb +34 -20
- data/lib/dm-core/types/object.rb +7 -4
- data/lib/dm-core/types/paranoid_boolean.rb +11 -9
- data/lib/dm-core/types/paranoid_datetime.rb +11 -9
- data/lib/dm-core/types/serial.rb +3 -3
- data/lib/dm-core/types/text.rb +3 -4
- data/lib/dm-core/version.rb +1 -1
- data/lib/dm-core.rb +106 -110
- data/script/performance.rb +102 -109
- data/script/profile.rb +169 -38
- data/spec/lib/adapter_helpers.rb +105 -0
- data/spec/lib/collection_helpers.rb +18 -0
- data/spec/lib/counter_adapter.rb +34 -0
- data/spec/lib/pending_helpers.rb +27 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
- data/spec/public/associations/many_to_many_spec.rb +193 -0
- data/spec/public/associations/many_to_one_spec.rb +73 -0
- data/spec/public/associations/one_to_many_spec.rb +77 -0
- data/spec/public/associations/one_to_one_spec.rb +156 -0
- data/spec/public/collection_spec.rb +65 -0
- data/spec/public/model/relationship_spec.rb +924 -0
- data/spec/public/model_spec.rb +159 -0
- data/spec/public/property_spec.rb +829 -0
- data/spec/public/resource_spec.rb +71 -0
- data/spec/public/sel_spec.rb +44 -0
- data/spec/public/setup_spec.rb +145 -0
- data/spec/public/shared/association_collection_shared_spec.rb +317 -0
- data/spec/public/shared/collection_shared_spec.rb +1723 -0
- data/spec/public/shared/finder_shared_spec.rb +1619 -0
- data/spec/public/shared/resource_shared_spec.rb +924 -0
- data/spec/public/shared/sel_shared_spec.rb +112 -0
- data/spec/public/transaction_spec.rb +129 -0
- data/spec/public/types/discriminator_spec.rb +130 -0
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
- data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
- data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
- data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
- data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
- data/spec/semipublic/associations/relationship_spec.rb +194 -0
- data/spec/semipublic/associations_spec.rb +177 -0
- data/spec/semipublic/collection_spec.rb +142 -0
- data/spec/semipublic/property_spec.rb +61 -0
- data/spec/semipublic/query/conditions_spec.rb +528 -0
- data/spec/semipublic/query/path_spec.rb +443 -0
- data/spec/semipublic/query_spec.rb +2626 -0
- data/spec/semipublic/resource_spec.rb +47 -0
- data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
- data/spec/spec.opts +3 -1
- data/spec/spec_helper.rb +80 -57
- data/tasks/ci.rb +19 -31
- data/tasks/dm.rb +43 -48
- data/tasks/doc.rb +8 -11
- data/tasks/gemspec.rb +5 -5
- data/tasks/hoe.rb +15 -16
- data/tasks/install.rb +8 -10
- metadata +72 -93
- data/lib/dm-core/associations/relationship_chain.rb +0 -81
- data/lib/dm-core/associations.rb +0 -207
- data/lib/dm-core/auto_migrations.rb +0 -105
- data/lib/dm-core/dependency_queue.rb +0 -32
- data/lib/dm-core/hook.rb +0 -11
- data/lib/dm-core/is.rb +0 -16
- data/lib/dm-core/logger.rb +0 -232
- data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
- data/lib/dm-core/migrator.rb +0 -29
- data/lib/dm-core/scope.rb +0 -58
- data/lib/dm-core/support/array.rb +0 -13
- data/lib/dm-core/support/assertions.rb +0 -8
- data/lib/dm-core/support/errors.rb +0 -23
- data/lib/dm-core/support/kernel.rb +0 -11
- data/lib/dm-core/support/symbol.rb +0 -41
- data/lib/dm-core/support.rb +0 -7
- data/lib/dm-core/type_map.rb +0 -80
- data/lib/dm-core/types.rb +0 -19
- data/script/all +0 -4
- data/spec/integration/association_spec.rb +0 -1382
- data/spec/integration/association_through_spec.rb +0 -203
- data/spec/integration/associations/many_to_many_spec.rb +0 -449
- data/spec/integration/associations/many_to_one_spec.rb +0 -163
- data/spec/integration/associations/one_to_many_spec.rb +0 -188
- data/spec/integration/auto_migrations_spec.rb +0 -413
- data/spec/integration/collection_spec.rb +0 -1073
- data/spec/integration/data_objects_adapter_spec.rb +0 -32
- data/spec/integration/dependency_queue_spec.rb +0 -46
- data/spec/integration/model_spec.rb +0 -197
- data/spec/integration/mysql_adapter_spec.rb +0 -85
- data/spec/integration/postgres_adapter_spec.rb +0 -731
- data/spec/integration/property_spec.rb +0 -253
- data/spec/integration/query_spec.rb +0 -514
- data/spec/integration/repository_spec.rb +0 -61
- data/spec/integration/resource_spec.rb +0 -513
- data/spec/integration/sqlite3_adapter_spec.rb +0 -352
- data/spec/integration/sti_spec.rb +0 -273
- data/spec/integration/strategic_eager_loading_spec.rb +0 -156
- data/spec/integration/transaction_spec.rb +0 -75
- data/spec/integration/type_spec.rb +0 -275
- data/spec/lib/logging_helper.rb +0 -18
- data/spec/lib/mock_adapter.rb +0 -27
- data/spec/lib/model_loader.rb +0 -100
- data/spec/lib/publicize_methods.rb +0 -28
- data/spec/models/content.rb +0 -16
- data/spec/models/vehicles.rb +0 -34
- data/spec/models/zoo.rb +0 -48
- data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
- data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
- data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
- data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
- data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
- data/spec/unit/associations/many_to_many_spec.rb +0 -32
- data/spec/unit/associations/many_to_one_spec.rb +0 -159
- data/spec/unit/associations/one_to_many_spec.rb +0 -393
- data/spec/unit/associations/one_to_one_spec.rb +0 -7
- data/spec/unit/associations/relationship_spec.rb +0 -71
- data/spec/unit/associations_spec.rb +0 -242
- data/spec/unit/auto_migrations_spec.rb +0 -111
- data/spec/unit/collection_spec.rb +0 -182
- data/spec/unit/data_mapper_spec.rb +0 -35
- data/spec/unit/identity_map_spec.rb +0 -126
- data/spec/unit/is_spec.rb +0 -80
- data/spec/unit/migrator_spec.rb +0 -33
- data/spec/unit/model_spec.rb +0 -321
- data/spec/unit/naming_conventions_spec.rb +0 -36
- data/spec/unit/property_set_spec.rb +0 -90
- data/spec/unit/property_spec.rb +0 -753
- data/spec/unit/query_spec.rb +0 -571
- data/spec/unit/repository_spec.rb +0 -93
- data/spec/unit/resource_spec.rb +0 -649
- data/spec/unit/scope_spec.rb +0 -142
- data/spec/unit/transaction_spec.rb +0 -493
- data/spec/unit/type_map_spec.rb +0 -114
- data/spec/unit/type_spec.rb +0 -119
data/lib/dm-core/property.rb
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
require 'date'
|
|
2
|
-
require 'time'
|
|
3
|
-
require 'bigdecimal'
|
|
4
|
-
|
|
5
1
|
module DataMapper
|
|
6
2
|
|
|
7
3
|
# :include:QUICKLINKS
|
|
@@ -13,15 +9,18 @@ module DataMapper
|
|
|
13
9
|
# repository/database.
|
|
14
10
|
#
|
|
15
11
|
# If you are coming to DataMapper from another ORM framework, such as
|
|
16
|
-
# ActiveRecord, this
|
|
17
|
-
# are several advantages to defining your properties in your
|
|
12
|
+
# ActiveRecord, this may be a fundamental difference in thinking to you.
|
|
13
|
+
# However, there are several advantages to defining your properties in your
|
|
14
|
+
# models:
|
|
18
15
|
#
|
|
19
16
|
# * information about your model is centralized in one place: rather than
|
|
20
17
|
# having to dig out migrations, xml or other configuration files.
|
|
18
|
+
# * use of mixins can be applied to model properties: better code reuse
|
|
21
19
|
# * having information centralized in your models, encourages you and the
|
|
22
20
|
# developers on your team to take a model-centric view of development.
|
|
23
21
|
# * it provides the ability to use Ruby's access control functions.
|
|
24
|
-
# * and, because DataMapper only cares about properties explicitly defined
|
|
22
|
+
# * and, because DataMapper only cares about properties explicitly defined
|
|
23
|
+
# in
|
|
25
24
|
# your models, DataMapper plays well with legacy databases, and shares
|
|
26
25
|
# databases easily with other applications.
|
|
27
26
|
#
|
|
@@ -32,16 +31,16 @@ module DataMapper
|
|
|
32
31
|
#
|
|
33
32
|
# class Post
|
|
34
33
|
# include DataMapper::Resource
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# property :publish,
|
|
38
|
-
# # Default value for new records is false
|
|
34
|
+
#
|
|
35
|
+
# property :title, String, :nullable => false # Cannot be null
|
|
36
|
+
# property :publish, Boolean, :default => false # Default value for new records is false
|
|
39
37
|
# end
|
|
40
38
|
#
|
|
41
|
-
# By default, DataMapper supports the following primitive types
|
|
39
|
+
# By default, DataMapper supports the following primitive (Ruby) types
|
|
40
|
+
# also called core types:
|
|
42
41
|
#
|
|
43
|
-
# *
|
|
44
|
-
# * String
|
|
42
|
+
# * Boolean
|
|
43
|
+
# * String (default length is 50)
|
|
45
44
|
# * Text (limit of 65k characters by default)
|
|
46
45
|
# * Float
|
|
47
46
|
# * Integer
|
|
@@ -52,6 +51,8 @@ module DataMapper
|
|
|
52
51
|
# * Object (marshalled out during serialization)
|
|
53
52
|
# * Class (datastore primitive is the same as String. Used for Inheritance)
|
|
54
53
|
#
|
|
54
|
+
# Other types are known as custom types.
|
|
55
|
+
#
|
|
55
56
|
# For more information about available Types, see DataMapper::Type
|
|
56
57
|
#
|
|
57
58
|
# == Limiting Access
|
|
@@ -61,33 +62,30 @@ module DataMapper
|
|
|
61
62
|
#
|
|
62
63
|
# class Post
|
|
63
64
|
# include DataMapper::Resource
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
# property :body,
|
|
67
|
-
# # Both reader and writer are protected
|
|
65
|
+
#
|
|
66
|
+
# property :title, String, :accessor => :private # Both reader and writer are private
|
|
67
|
+
# property :body, Text, :accessor => :protected # Both reader and writer are protected
|
|
68
68
|
# end
|
|
69
69
|
#
|
|
70
|
-
# Access control is also analogous to Ruby
|
|
70
|
+
# Access control is also analogous to Ruby attribute readers and writers, and can
|
|
71
71
|
# be declared using :reader and :writer, in addition to :accessor.
|
|
72
72
|
#
|
|
73
73
|
# class Post
|
|
74
74
|
# include DataMapper::Resource
|
|
75
75
|
#
|
|
76
|
-
# property :title, String, :writer => :private
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
# property :tags, String, :reader => :protected
|
|
80
|
-
# # Only reader is protected
|
|
76
|
+
# property :title, String, :writer => :private # Only writer is private
|
|
77
|
+
# property :tags, String, :reader => :protected # Only reader is protected
|
|
81
78
|
# end
|
|
82
79
|
#
|
|
83
80
|
# == Overriding Accessors
|
|
84
|
-
# The
|
|
85
|
-
#
|
|
86
|
-
#
|
|
81
|
+
# The reader/writer for any property can be overridden in the same manner that Ruby
|
|
82
|
+
# attr readers/writers can be. After the property is defined, just add your custom
|
|
83
|
+
# reader or writer:
|
|
87
84
|
#
|
|
88
85
|
# class Post
|
|
89
86
|
# include DataMapper::Resource
|
|
90
|
-
#
|
|
87
|
+
#
|
|
88
|
+
# property :title, String
|
|
91
89
|
#
|
|
92
90
|
# def title=(new_title)
|
|
93
91
|
# raise ArgumentError if new_title != 'Luke is Awesome'
|
|
@@ -107,8 +105,9 @@ module DataMapper
|
|
|
107
105
|
#
|
|
108
106
|
# class Post
|
|
109
107
|
# include DataMapper::Resource
|
|
110
|
-
#
|
|
111
|
-
# property :
|
|
108
|
+
#
|
|
109
|
+
# property :title, String # Loads normally
|
|
110
|
+
# property :body, Text # Is lazily loaded by default
|
|
112
111
|
# end
|
|
113
112
|
#
|
|
114
113
|
# If you want to over-ride the lazy loading on any field you can set it to a
|
|
@@ -119,17 +118,10 @@ module DataMapper
|
|
|
119
118
|
# class Post
|
|
120
119
|
# include DataMapper::Resource
|
|
121
120
|
#
|
|
122
|
-
# property :title,
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
# property :
|
|
126
|
-
# # The default is now over-ridden
|
|
127
|
-
#
|
|
128
|
-
# property :comment, String, lazy => [:detailed]
|
|
129
|
-
# # Loads in the :detailed context
|
|
130
|
-
#
|
|
131
|
-
# property :author, String, lazy => [:summary,:detailed]
|
|
132
|
-
# # Loads in :summary & :detailed context
|
|
121
|
+
# property :title, String # Loads normally
|
|
122
|
+
# property :body, Text, :lazy => false # The default is now over-ridden
|
|
123
|
+
# property :comment, String, :lazy => [ :detailed ] # Loads in the :detailed context
|
|
124
|
+
# property :author, String, :lazy => [ :summary, :detailed ] # Loads in :summary & :detailed context
|
|
133
125
|
# end
|
|
134
126
|
#
|
|
135
127
|
# Delaying the request for lazy-loaded attributes even applies to objects
|
|
@@ -140,14 +132,14 @@ module DataMapper
|
|
|
140
132
|
#
|
|
141
133
|
# Example:
|
|
142
134
|
#
|
|
143
|
-
# Widget
|
|
135
|
+
# Widget.get(1).components
|
|
144
136
|
# # loads when the post object is pulled from database, by default
|
|
145
137
|
#
|
|
146
|
-
# Widget
|
|
138
|
+
# Widget.get(1).components.first.body
|
|
147
139
|
# # loads the values for the body property on all objects in the
|
|
148
140
|
# # association, rather than just this one.
|
|
149
141
|
#
|
|
150
|
-
# Widget
|
|
142
|
+
# Widget.get(1).components.first.comment
|
|
151
143
|
# # loads both comment and author for all objects in the association
|
|
152
144
|
# # since they are both in the :detailed context
|
|
153
145
|
#
|
|
@@ -157,21 +149,21 @@ module DataMapper
|
|
|
157
149
|
#
|
|
158
150
|
# Examples:
|
|
159
151
|
#
|
|
160
|
-
# property :id, Serial
|
|
161
|
-
# property :legacy_pk, String, :key => true
|
|
152
|
+
# property :id, Serial # auto-incrementing key
|
|
153
|
+
# property :legacy_pk, String, :key => true # 'natural' key
|
|
162
154
|
#
|
|
163
155
|
# This is roughly equivalent to ActiveRecord's <tt>set_primary_key</tt>,
|
|
164
156
|
# though non-integer data types may be used, thus DataMapper supports natural
|
|
165
157
|
# keys. When a property is declared as a natural key, accessing the object
|
|
166
158
|
# using the indexer syntax <tt>Class[key]</tt> remains valid.
|
|
167
159
|
#
|
|
168
|
-
# User
|
|
160
|
+
# User.get(1)
|
|
169
161
|
# # when :id is the primary key on the users table
|
|
170
|
-
# User
|
|
162
|
+
# User.get('bill')
|
|
171
163
|
# # when :name is the primary (natural) key on the users table
|
|
172
164
|
#
|
|
173
|
-
# ==
|
|
174
|
-
# You can add
|
|
165
|
+
# == Indices
|
|
166
|
+
# You can add indices for your properties by using the <tt>:index</tt>
|
|
175
167
|
# option. If you use <tt>true</tt> as the option value, the index will be
|
|
176
168
|
# automatically named. If you want to name the index yourself, use a symbol
|
|
177
169
|
# as the value.
|
|
@@ -179,7 +171,7 @@ module DataMapper
|
|
|
179
171
|
# property :last_name, String, :index => true
|
|
180
172
|
# property :first_name, String, :index => :name
|
|
181
173
|
#
|
|
182
|
-
# You can create multi-column composite
|
|
174
|
+
# You can create multi-column composite indices by using the same symbol in
|
|
183
175
|
# all the columns belonging to the index. The columns will appear in the
|
|
184
176
|
# index in the order they are declared.
|
|
185
177
|
#
|
|
@@ -187,7 +179,7 @@ module DataMapper
|
|
|
187
179
|
# property :first_name, String, :index => :name
|
|
188
180
|
# # => index on (last_name, first_name)
|
|
189
181
|
#
|
|
190
|
-
# If you want to make the
|
|
182
|
+
# If you want to make the indices unique, use <tt>:unique_index</tt> instead
|
|
191
183
|
# of <tt>:index</tt>
|
|
192
184
|
#
|
|
193
185
|
# == Inferred Validations
|
|
@@ -230,7 +222,7 @@ module DataMapper
|
|
|
230
222
|
# proc. The proc is passed two values, the resource the property is being set
|
|
231
223
|
# for and the property itself.
|
|
232
224
|
#
|
|
233
|
-
# property :display_name, String, :default => { |
|
|
225
|
+
# property :display_name, String, :default => { |resource, property| resource.login }
|
|
234
226
|
#
|
|
235
227
|
# Word of warning. Don't try to read the value of the property you're setting
|
|
236
228
|
# the default for in the proc. An infinite loop will ensue.
|
|
@@ -239,6 +231,56 @@ module DataMapper
|
|
|
239
231
|
# As an alternative to extraneous has_one relationships, consider using an
|
|
240
232
|
# EmbeddedValue.
|
|
241
233
|
#
|
|
234
|
+
# == Property options reference
|
|
235
|
+
#
|
|
236
|
+
# :accessor if false, neither reader nor writer methods are
|
|
237
|
+
# created for this property
|
|
238
|
+
#
|
|
239
|
+
# :reader if false, reader method is not created for this property
|
|
240
|
+
#
|
|
241
|
+
# :writer if false, writer method is not created for this property
|
|
242
|
+
#
|
|
243
|
+
# :lazy if true, property value is only loaded when on first read
|
|
244
|
+
# if false, property value is always loaded
|
|
245
|
+
# if a symbol, property value is loaded with other properties
|
|
246
|
+
# in the same group
|
|
247
|
+
#
|
|
248
|
+
# :default default value of this property
|
|
249
|
+
#
|
|
250
|
+
# :nullable if true, property may have a nil value on save
|
|
251
|
+
#
|
|
252
|
+
# :key name of the key associated with this property.
|
|
253
|
+
#
|
|
254
|
+
# :serial if true, field value is auto incrementing
|
|
255
|
+
#
|
|
256
|
+
# :field field in the data-store which the property corresponds to
|
|
257
|
+
#
|
|
258
|
+
# :length string field length
|
|
259
|
+
#
|
|
260
|
+
# :format format for autovalidation. Use with dm-validations plugin.
|
|
261
|
+
#
|
|
262
|
+
# :index if true, index is created for the property. If a Symbol, index
|
|
263
|
+
# is named after Symbol value instead of being based on property name.
|
|
264
|
+
#
|
|
265
|
+
# :unique_index true specifies that index on this property should be unique
|
|
266
|
+
#
|
|
267
|
+
# :auto_validation if true, automatic validation is performed on the property
|
|
268
|
+
#
|
|
269
|
+
# :validates validation context. Use together with dm-validations.
|
|
270
|
+
#
|
|
271
|
+
# :unique if true, property column is unique. Properties of type Serial
|
|
272
|
+
# are unique by default.
|
|
273
|
+
#
|
|
274
|
+
# :precision Indicates the number of significant digits. Usually only makes sense
|
|
275
|
+
# for float type properties. Must be >= scale option value. Default is 10.
|
|
276
|
+
#
|
|
277
|
+
# :scale The number of significant digits to the right of the decimal point.
|
|
278
|
+
# Only makes sense for float type properties. Must be > 0.
|
|
279
|
+
# Default is nil for Float type and 10 for BigDecimal type.
|
|
280
|
+
#
|
|
281
|
+
# All other keys you pass to +property+ method are stored and available
|
|
282
|
+
# as options[:extra_keys].
|
|
283
|
+
#
|
|
242
284
|
# == Misc. Notes
|
|
243
285
|
# * Properties declared as strings will default to a length of 50, rather than
|
|
244
286
|
# 255 (typical max varchar column size). To overload the default, pass
|
|
@@ -249,28 +291,27 @@ module DataMapper
|
|
|
249
291
|
# * You may declare a Property with the data-type of <tt>Class</tt>.
|
|
250
292
|
# see SingleTableInheritance for more on how to use <tt>Class</tt> columns.
|
|
251
293
|
class Property
|
|
252
|
-
include Assertions
|
|
294
|
+
include Extlib::Assertions
|
|
295
|
+
extend Deprecate
|
|
296
|
+
extend Equalizer
|
|
253
297
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
298
|
+
deprecate :unique, :unique?
|
|
299
|
+
deprecate :size, :length
|
|
300
|
+
|
|
301
|
+
equalize :model, :name
|
|
302
|
+
|
|
303
|
+
# NOTE: PLEASE update OPTIONS in DataMapper::Type when updating
|
|
259
304
|
# them here
|
|
260
|
-
|
|
305
|
+
OPTIONS = [
|
|
261
306
|
:accessor, :reader, :writer,
|
|
262
307
|
:lazy, :default, :nullable, :key, :serial, :field, :size, :length,
|
|
263
|
-
:format, :index, :unique_index, :
|
|
264
|
-
:validates, :unique, :
|
|
308
|
+
:format, :index, :unique_index, :auto_validation,
|
|
309
|
+
:validates, :unique, :precision, :scale, :min, :max
|
|
265
310
|
]
|
|
266
311
|
|
|
267
|
-
|
|
268
|
-
# DataMapper::Adapters::DataObjectsAdapter::TYPES
|
|
269
|
-
# for this?
|
|
270
|
-
TYPES = [
|
|
312
|
+
PRIMITIVES = [
|
|
271
313
|
TrueClass,
|
|
272
314
|
String,
|
|
273
|
-
DataMapper::Types::Text,
|
|
274
315
|
Float,
|
|
275
316
|
Integer,
|
|
276
317
|
BigDecimal,
|
|
@@ -279,71 +320,113 @@ module DataMapper
|
|
|
279
320
|
Time,
|
|
280
321
|
Object,
|
|
281
322
|
Class,
|
|
282
|
-
|
|
283
|
-
DataMapper::Types::Serial
|
|
284
|
-
]
|
|
285
|
-
|
|
286
|
-
IMMUTABLE_TYPES = [ TrueClass, Float, Integer, BigDecimal]
|
|
323
|
+
].to_set.freeze
|
|
287
324
|
|
|
288
|
-
|
|
325
|
+
# Possible :visibility option values
|
|
326
|
+
VISIBILITY_OPTIONS = [ :public, :protected, :private ].to_set.freeze
|
|
289
327
|
|
|
290
|
-
DEFAULT_LENGTH
|
|
291
|
-
DEFAULT_PRECISION
|
|
292
|
-
DEFAULT_SCALE_BIGDECIMAL = 0
|
|
293
|
-
DEFAULT_SCALE_FLOAT
|
|
328
|
+
DEFAULT_LENGTH = 50
|
|
329
|
+
DEFAULT_PRECISION = 10
|
|
330
|
+
DEFAULT_SCALE_BIGDECIMAL = 0 # Default scale for BigDecimal type
|
|
331
|
+
DEFAULT_SCALE_FLOAT = nil # Default scale for Float type
|
|
332
|
+
DEFAULT_NUMERIC_MIN = 0
|
|
333
|
+
DEFAULT_NUMERIC_MAX = 2**31-1
|
|
294
334
|
|
|
295
335
|
attr_reader :primitive, :model, :name, :instance_variable_name,
|
|
296
|
-
:type, :reader_visibility, :writer_visibility, :
|
|
297
|
-
:default, :precision, :scale, :
|
|
336
|
+
:type, :reader_visibility, :writer_visibility, :options,
|
|
337
|
+
:default, :precision, :scale, :min, :max, :repository_name
|
|
298
338
|
|
|
299
339
|
# Supplies the field in the data-store which the property corresponds to
|
|
300
340
|
#
|
|
301
|
-
# @return
|
|
302
|
-
#
|
|
303
|
-
# @api
|
|
341
|
+
# @return [String] name of field in data-store
|
|
342
|
+
#
|
|
343
|
+
# @api semipublic
|
|
304
344
|
def field(repository_name = nil)
|
|
305
|
-
|
|
345
|
+
if repository_name
|
|
346
|
+
warn "Passing in +repository_name+ to #{self.class}#field is deprecated (#{caller[0]})"
|
|
347
|
+
|
|
348
|
+
if repository_name != self.repository_name
|
|
349
|
+
raise ArgumentError, "Mismatching +repository_name+ with #{self.class}#repository_name (#{repository_name.inspect} != #{self.repository_name.inspect})"
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# defer setting the field with the adapter specific naming
|
|
354
|
+
# conventions until after the adapter has been setup
|
|
355
|
+
@field ||= model.field_naming_convention(self.repository_name).call(self).freeze
|
|
306
356
|
end
|
|
307
357
|
|
|
308
|
-
|
|
309
|
-
|
|
358
|
+
# Returns true if property is unique. Serial properties and keys
|
|
359
|
+
# are unique by default.
|
|
360
|
+
#
|
|
361
|
+
# @return [Boolean]
|
|
362
|
+
# true if property has uniq index defined, false otherwise
|
|
363
|
+
#
|
|
364
|
+
# @api public
|
|
365
|
+
def unique?
|
|
366
|
+
@unique
|
|
310
367
|
end
|
|
311
368
|
|
|
369
|
+
# Returns the hash of the property name
|
|
370
|
+
#
|
|
371
|
+
# This is necessary to allow comparisons between different properties
|
|
372
|
+
# in different models, having the same base model
|
|
373
|
+
#
|
|
374
|
+
# @return [Integer]
|
|
375
|
+
# the property name hash
|
|
376
|
+
#
|
|
377
|
+
# @api semipublic
|
|
312
378
|
def hash
|
|
313
|
-
|
|
314
|
-
@type.bind(self)
|
|
315
|
-
@bound = true
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
return @model.hash + @name.hash
|
|
379
|
+
name.hash
|
|
319
380
|
end
|
|
320
381
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
382
|
+
# Returns maximum property length (if applicable).
|
|
383
|
+
# This usually only makes sense when property is of
|
|
384
|
+
# type Range or custom type.
|
|
385
|
+
#
|
|
386
|
+
# @return [Integer, NilClass]
|
|
387
|
+
# the maximum length of this property
|
|
388
|
+
#
|
|
389
|
+
# @api semipublic
|
|
390
|
+
def length
|
|
391
|
+
if @length.kind_of?(Range)
|
|
392
|
+
@length.max
|
|
324
393
|
else
|
|
325
|
-
|
|
394
|
+
@length
|
|
326
395
|
end
|
|
327
396
|
end
|
|
328
397
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
398
|
+
# Returns index name if property has index.
|
|
399
|
+
#
|
|
400
|
+
# @return [true, Symbol, Array, nil]
|
|
401
|
+
# returns true if property is indexed by itself
|
|
402
|
+
# returns a Symbol if the property is indexed with other properties
|
|
403
|
+
# returns an Array if the property belongs to multiple indexes
|
|
404
|
+
# returns nil if the property does not belong to any indexes
|
|
405
|
+
#
|
|
406
|
+
# @api public
|
|
334
407
|
def index
|
|
335
408
|
@index
|
|
336
409
|
end
|
|
337
410
|
|
|
411
|
+
# Returns true if property has unique index. Serial properties and
|
|
412
|
+
# keys are unique by default.
|
|
413
|
+
#
|
|
414
|
+
# @return [true, Symbol, Array, nil]
|
|
415
|
+
# returns true if property is indexed by itself
|
|
416
|
+
# returns a Symbol if the property is indexed with other properties
|
|
417
|
+
# returns an Array if the property belongs to multiple indexes
|
|
418
|
+
# returns nil if the property does not belong to any indexes
|
|
419
|
+
#
|
|
420
|
+
# @api public
|
|
338
421
|
def unique_index
|
|
339
422
|
@unique_index
|
|
340
423
|
end
|
|
341
424
|
|
|
342
425
|
# Returns whether or not the property is to be lazy-loaded
|
|
343
426
|
#
|
|
344
|
-
# @return
|
|
345
|
-
# lazy-loaded
|
|
346
|
-
#
|
|
427
|
+
# @return [Boolean]
|
|
428
|
+
# true if the property is to be lazy-loaded
|
|
429
|
+
#
|
|
347
430
|
# @api public
|
|
348
431
|
def lazy?
|
|
349
432
|
@lazy
|
|
@@ -351,9 +434,9 @@ module DataMapper
|
|
|
351
434
|
|
|
352
435
|
# Returns whether or not the property is a key or a part of a key
|
|
353
436
|
#
|
|
354
|
-
# @return
|
|
355
|
-
# a key
|
|
356
|
-
|
|
437
|
+
# @return [Boolean]
|
|
438
|
+
# true if the property is a key or a part of a key
|
|
439
|
+
#
|
|
357
440
|
# @api public
|
|
358
441
|
def key?
|
|
359
442
|
@key
|
|
@@ -361,8 +444,9 @@ module DataMapper
|
|
|
361
444
|
|
|
362
445
|
# Returns whether or not the property is "serial" (auto-incrementing)
|
|
363
446
|
#
|
|
364
|
-
# @return
|
|
365
|
-
|
|
447
|
+
# @return [Boolean]
|
|
448
|
+
# whether or not the property is "serial"
|
|
449
|
+
#
|
|
366
450
|
# @api public
|
|
367
451
|
def serial?
|
|
368
452
|
@serial
|
|
@@ -370,232 +454,368 @@ module DataMapper
|
|
|
370
454
|
|
|
371
455
|
# Returns whether or not the property can accept 'nil' as it's value
|
|
372
456
|
#
|
|
373
|
-
# @return
|
|
374
|
-
|
|
457
|
+
# @return [Boolean]
|
|
458
|
+
# whether or not the property can accept 'nil'
|
|
459
|
+
#
|
|
375
460
|
# @api public
|
|
376
461
|
def nullable?
|
|
377
462
|
@nullable
|
|
378
463
|
end
|
|
379
464
|
|
|
465
|
+
# Returns whether or not the property is custom (not provided by dm-core)
|
|
466
|
+
#
|
|
467
|
+
# @return [Boolean]
|
|
468
|
+
# whether or not the property is custom
|
|
469
|
+
#
|
|
470
|
+
# @api public
|
|
380
471
|
def custom?
|
|
381
472
|
@custom
|
|
382
473
|
end
|
|
383
474
|
|
|
384
|
-
#
|
|
475
|
+
# Standardized reader method for the property
|
|
476
|
+
#
|
|
477
|
+
# @param [Resource] resource
|
|
478
|
+
# model instance for which this property is to be loaded
|
|
479
|
+
#
|
|
480
|
+
# @return [Object]
|
|
481
|
+
# the value of this property for the provided instance
|
|
482
|
+
#
|
|
483
|
+
# @raise [ArgumentError] "+resource+ should be a Resource, but was ...."
|
|
385
484
|
#
|
|
386
|
-
# @raise <ArgumentError> "+resource+ should be a DataMapper::Resource, but was ...."
|
|
387
|
-
#-
|
|
388
485
|
# @api private
|
|
389
486
|
def get(resource)
|
|
390
|
-
lazy_load(resource)
|
|
391
|
-
|
|
392
|
-
value = get!(resource)
|
|
393
|
-
|
|
394
|
-
set_original_value(resource, value)
|
|
487
|
+
lazy_load(resource) unless loaded?(resource) || resource.new?
|
|
395
488
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
set(resource, value)
|
|
489
|
+
if loaded?(resource)
|
|
490
|
+
get!(resource)
|
|
491
|
+
else
|
|
492
|
+
set(resource, default? ? default_for(resource) : nil)
|
|
401
493
|
end
|
|
402
|
-
|
|
403
|
-
value
|
|
404
494
|
end
|
|
405
495
|
|
|
496
|
+
# Fetch the ivar value in the resource
|
|
497
|
+
#
|
|
498
|
+
# @param [Resource] resource
|
|
499
|
+
# model instance for which this property is to be unsafely loaded
|
|
500
|
+
#
|
|
501
|
+
# @return [Object]
|
|
502
|
+
# current @ivar value of this property in +resource+
|
|
503
|
+
#
|
|
504
|
+
# @api private
|
|
406
505
|
def get!(resource)
|
|
407
506
|
resource.instance_variable_get(instance_variable_name)
|
|
408
507
|
end
|
|
409
508
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
509
|
+
# Sets original value of the property on given resource.
|
|
510
|
+
# When property is set on DataMapper resource instance,
|
|
511
|
+
# original value is preserved. This makes possible to
|
|
512
|
+
# track dirty attributes and save only those really changed,
|
|
513
|
+
# and avoid extra queries to the data source in certain
|
|
514
|
+
# situations.
|
|
515
|
+
#
|
|
516
|
+
# @param [Resource] resource
|
|
517
|
+
# model instance for which to set the original value
|
|
518
|
+
# @param [Object] original
|
|
519
|
+
# value to set as original value for this property in +resource+
|
|
520
|
+
#
|
|
521
|
+
# @api private
|
|
522
|
+
def set_original_value(resource, original)
|
|
523
|
+
original_attributes = resource.original_attributes
|
|
524
|
+
original = self.value(original)
|
|
525
|
+
|
|
526
|
+
if original_attributes.key?(self)
|
|
527
|
+
# stop tracking the value if it has not changed
|
|
528
|
+
original_attributes.delete(self) if original == original_attributes[self] && resource.saved?
|
|
529
|
+
else
|
|
530
|
+
original_attributes[self] = original
|
|
415
531
|
end
|
|
416
532
|
end
|
|
417
533
|
|
|
418
534
|
# Provides a standardized setter method for the property
|
|
419
535
|
#
|
|
420
|
-
# @
|
|
421
|
-
|
|
536
|
+
# @param [Resource] resource
|
|
537
|
+
# the resource to get the value from
|
|
538
|
+
# @param [Object] value
|
|
539
|
+
# the value to set in the resource
|
|
540
|
+
#
|
|
541
|
+
# @return [Object]
|
|
542
|
+
# +value+ after being typecasted according to this property's primitive
|
|
543
|
+
#
|
|
544
|
+
# @raise [ArgumentError] "+resource+ should be a Resource, but was ...."
|
|
545
|
+
#
|
|
422
546
|
# @api private
|
|
423
547
|
def set(resource, value)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
# optimizing, but let's (a) benchmark it first, and (b) do
|
|
428
|
-
# whatever refactoring is necessary, which will benefit from the
|
|
429
|
-
# centralize checking
|
|
430
|
-
lazy_load(resource)
|
|
548
|
+
loaded = loaded?(resource)
|
|
549
|
+
original = get!(resource) if loaded
|
|
550
|
+
value = typecast(value)
|
|
431
551
|
|
|
432
|
-
|
|
433
|
-
|
|
552
|
+
if loaded && value == original
|
|
553
|
+
return original
|
|
554
|
+
end
|
|
434
555
|
|
|
435
|
-
set_original_value(resource,
|
|
556
|
+
set_original_value(resource, original)
|
|
436
557
|
|
|
437
|
-
set!(resource,
|
|
558
|
+
set!(resource, value)
|
|
438
559
|
end
|
|
439
560
|
|
|
561
|
+
# Set the ivar value in the resource
|
|
562
|
+
#
|
|
563
|
+
# @param [Resource] resource
|
|
564
|
+
# the resource to set
|
|
565
|
+
# @param [Object] value
|
|
566
|
+
# the value to set in the resource
|
|
567
|
+
#
|
|
568
|
+
# @return [Object]
|
|
569
|
+
# the value set in the resource
|
|
570
|
+
#
|
|
571
|
+
# @api private
|
|
440
572
|
def set!(resource, value)
|
|
441
573
|
resource.instance_variable_set(instance_variable_name, value)
|
|
442
574
|
end
|
|
443
575
|
|
|
576
|
+
# Check if the attribute corresponding to the property is loaded
|
|
577
|
+
#
|
|
578
|
+
# @param [Resource] resource
|
|
579
|
+
# model instance for which the attribute is to be tested
|
|
580
|
+
#
|
|
581
|
+
# @return [Boolean]
|
|
582
|
+
# true if the attribute is loaded in the resource
|
|
583
|
+
#
|
|
584
|
+
# @api private
|
|
585
|
+
def loaded?(resource)
|
|
586
|
+
resource.instance_variable_defined?(instance_variable_name)
|
|
587
|
+
end
|
|
588
|
+
|
|
444
589
|
# Loads lazy columns when get or set is called.
|
|
445
|
-
|
|
590
|
+
#
|
|
591
|
+
# @param [Resource] resource
|
|
592
|
+
# model instance for which lazy loaded attribute are loaded
|
|
593
|
+
#
|
|
446
594
|
# @api private
|
|
447
595
|
def lazy_load(resource)
|
|
448
|
-
|
|
449
|
-
# which properties would be loaded and then not load them.
|
|
450
|
-
return if resource.new_record? || resource.attribute_loaded?(name)
|
|
451
|
-
# If we're trying to load a lazy property, load it. Otherwise, lazy-load
|
|
452
|
-
# any properties that should be eager-loaded but were not included
|
|
453
|
-
# in the original :fields list
|
|
454
|
-
contexts = lazy? ? name : model.eager_properties(resource.repository.name)
|
|
455
|
-
resource.send(:lazy_load, contexts)
|
|
596
|
+
resource.send(:lazy_load, lazy_load_properties)
|
|
456
597
|
end
|
|
457
598
|
|
|
458
|
-
#
|
|
599
|
+
# TODO: document
|
|
600
|
+
# @api private
|
|
601
|
+
def lazy_load_properties
|
|
602
|
+
@lazy_load_properties ||= properties.in_context(lazy? ? [ self ] : properties.defaults)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# TODO: document
|
|
606
|
+
# @api private
|
|
607
|
+
def properties
|
|
608
|
+
@properties ||= model.properties(repository_name)
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# typecasts values into a primitive (Ruby class that backs DataMapper
|
|
612
|
+
# property type). If property type can handle typecasting, it is delegated.
|
|
613
|
+
# How typecasting is perfomed, depends on the primitive of the type.
|
|
614
|
+
#
|
|
615
|
+
# If type's primitive is a TrueClass, values of 1, t and true are casted to true.
|
|
616
|
+
#
|
|
617
|
+
# For String primitive, +to_s+ is called on value.
|
|
618
|
+
#
|
|
619
|
+
# For Float primitive, +to_f+ is called on value but only if value is a number
|
|
620
|
+
# otherwise value is returned.
|
|
621
|
+
#
|
|
622
|
+
# For Integer primitive, +to_i+ is called on value but only if value is a
|
|
623
|
+
# number, otherwise value is returned.
|
|
624
|
+
#
|
|
625
|
+
# For BigDecimal primitive, +to_d+ is called on value but only if value is a
|
|
626
|
+
# number, otherwise value is returned.
|
|
627
|
+
#
|
|
628
|
+
# Casting to DateTime, Time and Date can handle both hashes with keys like :day or
|
|
629
|
+
# :hour and strings in format methods like Time.parse can handle.
|
|
630
|
+
#
|
|
631
|
+
# @param [#to_s, #to_f, #to_i, #to_d, Hash] value
|
|
632
|
+
# the value to typecast
|
|
633
|
+
#
|
|
634
|
+
# @return [rue, String, Float, Integer, BigDecimal, DateTime, Date, Time, Class]
|
|
635
|
+
# The typecasted +value+
|
|
459
636
|
#
|
|
460
|
-
# @return <TrueClass, String, Float, Integer, BigDecimal, DateTime, Date, Time
|
|
461
|
-
# Class> the primitive data-type, defaults to TrueClass
|
|
462
|
-
#-
|
|
463
637
|
# @api private
|
|
464
638
|
def typecast(value)
|
|
465
639
|
return type.typecast(value, self) if type.respond_to?(:typecast)
|
|
466
|
-
return value if
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
# * Integer(value) rescue nil
|
|
479
|
-
# * Integer(value_to_s =~ /(\d+)/ ? $1 : value_to_s) rescue nil
|
|
480
|
-
#
|
|
481
|
-
# [YK] The previous implementation used a rescue. Why use a rescue
|
|
482
|
-
# when the list of cases where a valid string other than "0" could
|
|
483
|
-
# produce 0 is known?
|
|
484
|
-
value_to_i = value.to_i
|
|
485
|
-
if value_to_i == 0
|
|
486
|
-
value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
|
|
487
|
-
else
|
|
488
|
-
value_to_i
|
|
489
|
-
end
|
|
490
|
-
elsif primitive == BigDecimal then BigDecimal(value.to_s)
|
|
491
|
-
elsif primitive == DateTime then typecast_to_datetime(value)
|
|
492
|
-
elsif primitive == Date then typecast_to_date(value)
|
|
493
|
-
elsif primitive == Time then typecast_to_time(value)
|
|
494
|
-
elsif primitive == Class then self.class.find_const(value)
|
|
495
|
-
else
|
|
496
|
-
value
|
|
497
|
-
end
|
|
498
|
-
rescue
|
|
640
|
+
return value if primitive?(value) || value.nil?
|
|
641
|
+
|
|
642
|
+
if primitive == Integer then typecast_to_integer(value)
|
|
643
|
+
elsif primitive == String then typecast_to_string(value)
|
|
644
|
+
elsif primitive == TrueClass then typecast_to_boolean(value)
|
|
645
|
+
elsif primitive == BigDecimal then typecast_to_bigdecimal(value)
|
|
646
|
+
elsif primitive == Float then typecast_to_float(value)
|
|
647
|
+
elsif primitive == DateTime then typecast_to_datetime(value)
|
|
648
|
+
elsif primitive == Time then typecast_to_time(value)
|
|
649
|
+
elsif primitive == Date then typecast_to_date(value)
|
|
650
|
+
elsif primitive == Class then typecast_to_class(value)
|
|
651
|
+
else
|
|
499
652
|
value
|
|
500
653
|
end
|
|
501
654
|
end
|
|
502
655
|
|
|
656
|
+
# Returns a default value of the
|
|
657
|
+
# property for given resource.
|
|
658
|
+
#
|
|
659
|
+
# When default value is a callable object,
|
|
660
|
+
# it is called with resource and property passed
|
|
661
|
+
# as arguments.
|
|
662
|
+
#
|
|
663
|
+
# @param [Resource] resource
|
|
664
|
+
# the model instance for which the default is to be set
|
|
665
|
+
#
|
|
666
|
+
# @return [Object]
|
|
667
|
+
# the default value of this property for +resource+
|
|
668
|
+
#
|
|
669
|
+
# @api semipublic
|
|
503
670
|
def default_for(resource)
|
|
504
|
-
@default.respond_to?(:call)
|
|
671
|
+
if @default.respond_to?(:call)
|
|
672
|
+
@default.call(resource, self)
|
|
673
|
+
else
|
|
674
|
+
@default.try_dup
|
|
675
|
+
end
|
|
505
676
|
end
|
|
506
677
|
|
|
507
|
-
|
|
508
|
-
|
|
678
|
+
# Returns true if the property has a default value
|
|
679
|
+
#
|
|
680
|
+
# @return [Boolean]
|
|
681
|
+
# true if the property has a default value
|
|
682
|
+
#
|
|
683
|
+
# @api semipublic
|
|
684
|
+
def default?
|
|
685
|
+
@options.key?(:default)
|
|
509
686
|
end
|
|
510
687
|
|
|
511
|
-
|
|
512
|
-
|
|
688
|
+
# Returns given value unchanged for core types and
|
|
689
|
+
# uses +dump+ method of the property type for custom types.
|
|
690
|
+
#
|
|
691
|
+
# @param [Object] value
|
|
692
|
+
# the value to be converted into a storeable (ie., primitive) value
|
|
693
|
+
#
|
|
694
|
+
# @return [Object]
|
|
695
|
+
# the primitive value to be stored in the repository for +val+
|
|
696
|
+
#
|
|
697
|
+
# @api semipublic
|
|
698
|
+
def value(value)
|
|
699
|
+
if custom?
|
|
700
|
+
type.dump(value, self)
|
|
701
|
+
else
|
|
702
|
+
value
|
|
703
|
+
end
|
|
513
704
|
end
|
|
514
705
|
|
|
515
|
-
#
|
|
516
|
-
#
|
|
517
|
-
|
|
518
|
-
|
|
706
|
+
# Test the value to see if it is a valid value for this Property
|
|
707
|
+
#
|
|
708
|
+
# @param [Object] value
|
|
709
|
+
# the value to be tested
|
|
710
|
+
#
|
|
711
|
+
# @return [Boolean]
|
|
712
|
+
# true if the value is valid
|
|
713
|
+
#
|
|
714
|
+
# @api semipulic
|
|
715
|
+
def valid?(value)
|
|
716
|
+
value = self.value(value)
|
|
717
|
+
primitive?(value) || (value.nil? && nullable?)
|
|
519
718
|
end
|
|
520
719
|
|
|
521
|
-
#
|
|
522
|
-
#
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
720
|
+
# Returns a concise string representation of the property instance.
|
|
721
|
+
#
|
|
722
|
+
# @return [String]
|
|
723
|
+
# Concise string representation of the property instance.
|
|
724
|
+
#
|
|
725
|
+
# @api public
|
|
726
|
+
def inspect
|
|
727
|
+
"#<#{self.class.name} @model=#{model.inspect} @name=#{name.inspect}>"
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# Test a value to see if it matches the primitive type
|
|
731
|
+
#
|
|
732
|
+
# @param [Object] value
|
|
733
|
+
# value to test
|
|
734
|
+
#
|
|
735
|
+
# @return [Boolean]
|
|
736
|
+
# true if the value is the correct type
|
|
737
|
+
#
|
|
738
|
+
# @api semipublic
|
|
739
|
+
def primitive?(value)
|
|
740
|
+
if primitive == TrueClass
|
|
741
|
+
value == true || value == false
|
|
742
|
+
else
|
|
743
|
+
value.kind_of?(primitive)
|
|
744
|
+
end
|
|
526
745
|
end
|
|
527
746
|
|
|
528
747
|
private
|
|
529
748
|
|
|
749
|
+
# TODO: document
|
|
750
|
+
# @api semipublic
|
|
530
751
|
def initialize(model, name, type, options = {})
|
|
531
|
-
assert_kind_of 'model',
|
|
532
|
-
assert_kind_of 'name',
|
|
533
|
-
assert_kind_of 'type',
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
#
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
type
|
|
752
|
+
assert_kind_of 'model', model, Model
|
|
753
|
+
assert_kind_of 'name', name, Symbol
|
|
754
|
+
assert_kind_of 'type', type, Class, Module
|
|
755
|
+
assert_kind_of 'options', options, Hash
|
|
756
|
+
|
|
757
|
+
options = options.dup
|
|
758
|
+
|
|
759
|
+
if TrueClass == type
|
|
760
|
+
warn "#{type} is deprecated, use Boolean instead at #{caller[2]}"
|
|
761
|
+
type = Types::Boolean
|
|
762
|
+
elsif Integer == type && options.delete(:serial)
|
|
763
|
+
warn "#{type} with explicit :serial option is deprecated, use Serial instead (#{caller[2]})"
|
|
764
|
+
type = Types::Serial
|
|
765
|
+
elsif options.key?(:size)
|
|
766
|
+
if String == type
|
|
767
|
+
warn ":size option is deprecated, use #{type} with :length instead (#{caller[2]})"
|
|
768
|
+
length = options.delete(:size)
|
|
769
|
+
options[:length] = length unless options.key?(:length)
|
|
770
|
+
elsif Numeric > type
|
|
771
|
+
warn ":size option is deprecated, specify :min and :max instead (#{caller[2]})"
|
|
772
|
+
end
|
|
543
773
|
end
|
|
544
774
|
|
|
545
|
-
|
|
546
|
-
|
|
775
|
+
assert_valid_options(options)
|
|
776
|
+
|
|
777
|
+
# if the type can be found within Types then
|
|
778
|
+
# use that class rather than the primitive
|
|
779
|
+
unless type.name.blank?
|
|
780
|
+
type = Types.find_const(type.name)
|
|
547
781
|
end
|
|
548
782
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
@extra_options[key] = options.delete(key)
|
|
783
|
+
unless PRIMITIVES.include?(type) || (Type > type && PRIMITIVES.include?(type.primitive))
|
|
784
|
+
raise ArgumentError, "+type+ was #{type.inspect}, which is not a supported type"
|
|
552
785
|
end
|
|
553
786
|
|
|
787
|
+
@repository_name = model.repository_name
|
|
554
788
|
@model = model
|
|
555
789
|
@name = name.to_s.sub(/\?$/, '').to_sym
|
|
556
790
|
@type = type
|
|
557
|
-
@custom =
|
|
558
|
-
@options = @custom ? @type.options.merge(options) : options
|
|
559
|
-
@instance_variable_name = "@#{@name}"
|
|
791
|
+
@custom = Type > @type
|
|
792
|
+
@options = (@custom ? @type.options.merge(options) : options.dup).freeze
|
|
793
|
+
@instance_variable_name = "@#{@name}".freeze
|
|
560
794
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
@
|
|
795
|
+
@primitive = @type.respond_to?(:primitive) ? @type.primitive : @type
|
|
796
|
+
@field = @options[:field].freeze
|
|
797
|
+
@default = @options[:default]
|
|
564
798
|
|
|
565
|
-
@getter = TrueClass == @primitive ? "#{@name}?".to_sym : @name
|
|
566
|
-
@field = @options.fetch(:field, nil)
|
|
567
799
|
@serial = @options.fetch(:serial, false)
|
|
568
800
|
@key = @options.fetch(:key, @serial || false)
|
|
569
|
-
@default = @options.fetch(:default, nil)
|
|
570
801
|
@nullable = @options.fetch(:nullable, @key == false)
|
|
571
|
-
@index = @options.fetch(:index,
|
|
572
|
-
@unique_index = @options.fetch(:unique_index,
|
|
802
|
+
@index = @options.fetch(:index, nil)
|
|
803
|
+
@unique_index = @options.fetch(:unique_index, nil)
|
|
804
|
+
@unique = @options.fetch(:unique, @serial || @key || false)
|
|
573
805
|
@lazy = @options.fetch(:lazy, @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
|
|
574
|
-
@fields = {}
|
|
575
|
-
|
|
576
|
-
@track = @options.fetch(:track) do
|
|
577
|
-
if @custom && @type.respond_to?(:track) && @type.track
|
|
578
|
-
@type.track
|
|
579
|
-
else
|
|
580
|
-
IMMUTABLE_TYPES.include?(@primitive) ? :set : :get
|
|
581
|
-
end
|
|
582
|
-
end
|
|
583
806
|
|
|
584
807
|
# assign attributes per-type
|
|
585
808
|
if String == @primitive || Class == @primitive
|
|
586
|
-
@length = @options.fetch(:length,
|
|
809
|
+
@length = @options.fetch(:length, DEFAULT_LENGTH)
|
|
587
810
|
elsif BigDecimal == @primitive || Float == @primitive
|
|
588
811
|
@precision = @options.fetch(:precision, DEFAULT_PRECISION)
|
|
589
|
-
|
|
590
|
-
default_scale = (Float == @primitive) ? DEFAULT_SCALE_FLOAT : DEFAULT_SCALE_BIGDECIMAL
|
|
591
|
-
@scale = @options.fetch(:scale, default_scale)
|
|
592
|
-
# @scale = @options.fetch(:scale, DEFAULT_SCALE_BIGDECIMAL)
|
|
812
|
+
@scale = @options.fetch(:scale, Float == @primitive ? DEFAULT_SCALE_FLOAT : DEFAULT_SCALE_BIGDECIMAL)
|
|
593
813
|
|
|
594
814
|
unless @precision > 0
|
|
595
815
|
raise ArgumentError, "precision must be greater than 0, but was #{@precision.inspect}"
|
|
596
816
|
end
|
|
597
817
|
|
|
598
|
-
|
|
818
|
+
unless Float == @primitive && @scale.nil?
|
|
599
819
|
unless @scale >= 0
|
|
600
820
|
raise ArgumentError, "scale must be equal to or greater than 0, but was #{@scale.inspect}"
|
|
601
821
|
end
|
|
@@ -606,71 +826,324 @@ module DataMapper
|
|
|
606
826
|
end
|
|
607
827
|
end
|
|
608
828
|
|
|
829
|
+
if Numeric > @primitive && (@options.keys & [ :min, :max ]).any?
|
|
830
|
+
@min = @options.fetch(:min, DEFAULT_NUMERIC_MIN)
|
|
831
|
+
@max = @options.fetch(:max, DEFAULT_NUMERIC_MAX)
|
|
832
|
+
|
|
833
|
+
if @max < DEFAULT_NUMERIC_MIN && !@options.key?(:min)
|
|
834
|
+
raise ArgumentError, "min should be specified when the max is less than #{DEFAULT_NUMERIC_MIN}"
|
|
835
|
+
elsif @max < @min
|
|
836
|
+
raise ArgumentError, "max must be less than the min, but was #{@max} while the min was #{@min}"
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
609
840
|
determine_visibility
|
|
610
841
|
|
|
611
|
-
|
|
612
|
-
|
|
842
|
+
if custom?
|
|
843
|
+
type.bind(self)
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
# comes from dm-validations
|
|
847
|
+
@model.auto_generate_validations(self) if @model.respond_to?(:auto_generate_validations)
|
|
613
848
|
end
|
|
614
849
|
|
|
615
|
-
|
|
850
|
+
# TODO: document
|
|
851
|
+
# @api private
|
|
852
|
+
def assert_valid_options(options)
|
|
853
|
+
if (unknown_keys = options.keys - OPTIONS).any?
|
|
854
|
+
raise ArgumentError, "options #{unknown_keys.map { |key| key.inspect }.join(' and ')} are unknown"
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
options.each do |key, value|
|
|
858
|
+
case key
|
|
859
|
+
when :field
|
|
860
|
+
assert_kind_of "options[#{key.inspect}]", value, String
|
|
861
|
+
|
|
862
|
+
when :default
|
|
863
|
+
if value.nil?
|
|
864
|
+
raise ArgumentError, "options[#{key.inspect}] must not be nil"
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
when :serial, :key, :nullable, :unique, :auto_validation
|
|
868
|
+
unless value == true || value == false
|
|
869
|
+
raise ArgumentError, "options[#{key.inspect}] must be either true or false"
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
when :lazy
|
|
873
|
+
unless value == true || value == false || value.kind_of?(Symbol) || (value.kind_of?(Array) && value.all? { |val| val.kind_of?(Symbol) })
|
|
874
|
+
raise ArgumentError, "options[#{key.inspect}] must be either true, false, a Symbol or an Array of Symbols"
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
when :index, :unique_index
|
|
878
|
+
assert_kind_of "options[#{key.inspect}]", value, Symbol, Array, TrueClass
|
|
879
|
+
|
|
880
|
+
when :length
|
|
881
|
+
assert_kind_of "options[#{key.inspect}]", value, Range, Integer
|
|
882
|
+
|
|
883
|
+
when :size, :precision, :scale
|
|
884
|
+
assert_kind_of "options[#{key.inspect}]", value, Integer
|
|
885
|
+
|
|
886
|
+
when :reader, :writer, :accessor
|
|
887
|
+
assert_kind_of "options[#{key.inspect}]", value, Symbol
|
|
888
|
+
|
|
889
|
+
unless VISIBILITY_OPTIONS.include?(value)
|
|
890
|
+
raise ArgumentError, "options[#{key.inspect}] must be #{VISIBILITY_OPTIONS.join(' or ')}"
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
# Assert given visibility value is supported.
|
|
897
|
+
#
|
|
898
|
+
# Will raise ArgumentError if this Property's reader and writer
|
|
899
|
+
# visibilities are not included in VISIBILITY_OPTIONS.
|
|
900
|
+
# @return [NilClass]
|
|
901
|
+
#
|
|
902
|
+
# @raise [ArgumentError] "property visibility must be :public, :protected, or :private"
|
|
903
|
+
#
|
|
904
|
+
# @api private
|
|
905
|
+
def determine_visibility
|
|
616
906
|
@reader_visibility = @options[:reader] || @options[:accessor] || :public
|
|
617
907
|
@writer_visibility = @options[:writer] || @options[:accessor] || :public
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
# Typecast a value to an Integer
|
|
912
|
+
#
|
|
913
|
+
# @param [#to_str, #to_i] value
|
|
914
|
+
# value to typecast
|
|
915
|
+
#
|
|
916
|
+
# @return [Integer]
|
|
917
|
+
# Integer constructed from value
|
|
918
|
+
#
|
|
919
|
+
# @api private
|
|
920
|
+
def typecast_to_integer(value)
|
|
921
|
+
typecast_to_numeric(value, :to_i)
|
|
922
|
+
end
|
|
618
923
|
|
|
619
|
-
|
|
620
|
-
|
|
924
|
+
# Typecast a value to a String
|
|
925
|
+
#
|
|
926
|
+
# @param [#to_s] value
|
|
927
|
+
# value to typecast
|
|
928
|
+
#
|
|
929
|
+
# @return [String]
|
|
930
|
+
# String constructed from value
|
|
931
|
+
#
|
|
932
|
+
# @api private
|
|
933
|
+
def typecast_to_string(value)
|
|
934
|
+
value.to_s
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
# Typecast a value to a true or false
|
|
938
|
+
#
|
|
939
|
+
# @param [Integer, #to_str] value
|
|
940
|
+
# value to typecast
|
|
941
|
+
#
|
|
942
|
+
# @return [Boolean]
|
|
943
|
+
# true or false constructed from value
|
|
944
|
+
#
|
|
945
|
+
# @api private
|
|
946
|
+
def typecast_to_boolean(value)
|
|
947
|
+
if value.kind_of?(Integer)
|
|
948
|
+
return true if value == 1
|
|
949
|
+
return false if value == 0
|
|
950
|
+
elsif value.respond_to?(:to_str)
|
|
951
|
+
return true if %w[ true 1 t ].include?(value.to_str.downcase)
|
|
952
|
+
return false if %w[ false 0 f ].include?(value.to_str.downcase)
|
|
621
953
|
end
|
|
954
|
+
|
|
955
|
+
value
|
|
622
956
|
end
|
|
623
957
|
|
|
624
|
-
#
|
|
958
|
+
# Typecast a value to a BigDecimal
|
|
959
|
+
#
|
|
960
|
+
# @param [#to_str, #to_d, Integer] value
|
|
961
|
+
# value to typecast
|
|
962
|
+
#
|
|
963
|
+
# @return [BigDecimal]
|
|
964
|
+
# BigDecimal constructed from value
|
|
965
|
+
#
|
|
966
|
+
# @api private
|
|
967
|
+
def typecast_to_bigdecimal(value)
|
|
968
|
+
if value.kind_of?(Integer)
|
|
969
|
+
# TODO: remove this case when Integer#to_d added by extlib
|
|
970
|
+
value.to_s.to_d
|
|
971
|
+
else
|
|
972
|
+
typecast_to_numeric(value, :to_d)
|
|
973
|
+
end
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
# Typecast a value to a Float
|
|
977
|
+
#
|
|
978
|
+
# @param [#to_str, #to_f] value
|
|
979
|
+
# value to typecast
|
|
980
|
+
#
|
|
981
|
+
# @return [Float]
|
|
982
|
+
# Float constructed from value
|
|
983
|
+
#
|
|
984
|
+
# @api private
|
|
985
|
+
def typecast_to_float(value)
|
|
986
|
+
typecast_to_numeric(value, :to_f)
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
# Match numeric string
|
|
990
|
+
#
|
|
991
|
+
# @param [#to_str, Numeric] value
|
|
992
|
+
# value to typecast
|
|
993
|
+
# @param [Symbol] method
|
|
994
|
+
# method to typecast with
|
|
995
|
+
#
|
|
996
|
+
# @return [Numeric]
|
|
997
|
+
# number if matched, value if no match
|
|
998
|
+
#
|
|
999
|
+
# @api private
|
|
1000
|
+
def typecast_to_numeric(value, method)
|
|
1001
|
+
if value.respond_to?(:to_str)
|
|
1002
|
+
if value.to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
|
1003
|
+
$1.send(method)
|
|
1004
|
+
else
|
|
1005
|
+
value
|
|
1006
|
+
end
|
|
1007
|
+
elsif value.respond_to?(method)
|
|
1008
|
+
value.send(method)
|
|
1009
|
+
else
|
|
1010
|
+
value
|
|
1011
|
+
end
|
|
1012
|
+
end
|
|
1013
|
+
|
|
1014
|
+
# Typecasts an arbitrary value to a DateTime.
|
|
1015
|
+
# Handles both Hashes and DateTime instances.
|
|
1016
|
+
#
|
|
1017
|
+
# @param [#to_mash, #to_s] value
|
|
1018
|
+
# value to be typecast
|
|
1019
|
+
#
|
|
1020
|
+
# @return [DateTime]
|
|
1021
|
+
# DateTime constructed from value
|
|
1022
|
+
#
|
|
1023
|
+
# @api private
|
|
625
1024
|
def typecast_to_datetime(value)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
else
|
|
1025
|
+
if value.respond_to?(:to_mash)
|
|
1026
|
+
typecast_hash_to_datetime(value)
|
|
1027
|
+
else
|
|
1028
|
+
DateTime.parse(value.to_s)
|
|
629
1029
|
end
|
|
1030
|
+
rescue ArgumentError
|
|
1031
|
+
value
|
|
630
1032
|
end
|
|
631
1033
|
|
|
632
1034
|
# Typecasts an arbitrary value to a Date
|
|
1035
|
+
# Handles both Hashes and Date instances.
|
|
1036
|
+
#
|
|
1037
|
+
# @param [#to_mash, #to_s] value
|
|
1038
|
+
# value to be typecast
|
|
1039
|
+
#
|
|
1040
|
+
# @return [Date]
|
|
1041
|
+
# Date constructed from value
|
|
1042
|
+
#
|
|
1043
|
+
# @api private
|
|
633
1044
|
def typecast_to_date(value)
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
else
|
|
1045
|
+
if value.respond_to?(:to_mash)
|
|
1046
|
+
typecast_hash_to_date(value)
|
|
1047
|
+
else
|
|
1048
|
+
Date.parse(value.to_s)
|
|
637
1049
|
end
|
|
1050
|
+
rescue ArgumentError
|
|
1051
|
+
value
|
|
638
1052
|
end
|
|
639
1053
|
|
|
640
1054
|
# Typecasts an arbitrary value to a Time
|
|
1055
|
+
# Handles both Hashes and Time instances.
|
|
1056
|
+
#
|
|
1057
|
+
# @param [#to_mash, #to_s] value
|
|
1058
|
+
# value to be typecast
|
|
1059
|
+
#
|
|
1060
|
+
# @return [Time]
|
|
1061
|
+
# Time constructed from value
|
|
1062
|
+
#
|
|
1063
|
+
# @api private
|
|
641
1064
|
def typecast_to_time(value)
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
else
|
|
1065
|
+
if value.respond_to?(:to_mash)
|
|
1066
|
+
typecast_hash_to_time(value)
|
|
1067
|
+
else
|
|
1068
|
+
Time.parse(value.to_s)
|
|
645
1069
|
end
|
|
1070
|
+
rescue ArgumentError
|
|
1071
|
+
value
|
|
646
1072
|
end
|
|
647
1073
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
1074
|
+
# Creates a DateTime instance from a Hash with keys :year, :month, :day,
|
|
1075
|
+
# :hour, :min, :sec
|
|
1076
|
+
#
|
|
1077
|
+
# @param [#to_mash] value
|
|
1078
|
+
# value to be typecast
|
|
1079
|
+
#
|
|
1080
|
+
# @return [DateTime]
|
|
1081
|
+
# DateTime constructed from hash
|
|
1082
|
+
#
|
|
1083
|
+
# @api private
|
|
1084
|
+
def typecast_hash_to_datetime(value)
|
|
1085
|
+
DateTime.new(*extract_time(value))
|
|
654
1086
|
end
|
|
655
1087
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1088
|
+
# Creates a Date instance from a Hash with keys :year, :month, :day
|
|
1089
|
+
#
|
|
1090
|
+
# @param [#to_mash] value
|
|
1091
|
+
# value to be typecast
|
|
1092
|
+
#
|
|
1093
|
+
# @return [Date]
|
|
1094
|
+
# Date constructed from hash
|
|
1095
|
+
#
|
|
1096
|
+
# @api private
|
|
1097
|
+
def typecast_hash_to_date(value)
|
|
1098
|
+
Date.new(*extract_time(value)[0, 3])
|
|
662
1099
|
end
|
|
663
1100
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1101
|
+
# Creates a Time instance from a Hash with keys :year, :month, :day,
|
|
1102
|
+
# :hour, :min, :sec
|
|
1103
|
+
#
|
|
1104
|
+
# @param [#to_mash] value
|
|
1105
|
+
# value to be typecast
|
|
1106
|
+
#
|
|
1107
|
+
# @return [Time]
|
|
1108
|
+
# Time constructed from hash
|
|
1109
|
+
#
|
|
1110
|
+
# @api private
|
|
1111
|
+
def typecast_hash_to_time(value)
|
|
1112
|
+
Time.local(*extract_time(value))
|
|
667
1113
|
end
|
|
668
1114
|
|
|
669
1115
|
# Extracts the given args from the hash. If a value does not exist, it
|
|
670
|
-
# uses the value of Time.now
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
1116
|
+
# uses the value of Time.now.
|
|
1117
|
+
#
|
|
1118
|
+
# @param [#to_mash] value
|
|
1119
|
+
# value to extract time args from
|
|
1120
|
+
#
|
|
1121
|
+
# @return [Array]
|
|
1122
|
+
# Extracted values
|
|
1123
|
+
#
|
|
1124
|
+
# @api private
|
|
1125
|
+
def extract_time(value)
|
|
1126
|
+
mash = value.to_mash
|
|
1127
|
+
now = Time.now
|
|
1128
|
+
|
|
1129
|
+
[ :year, :month, :day, :hour, :min, :sec ].map do |segment|
|
|
1130
|
+
typecast_to_numeric(mash.fetch(segment, now.send(segment)), :to_i)
|
|
1131
|
+
end
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
# Typecast a value to a Class
|
|
1135
|
+
#
|
|
1136
|
+
# @param [#to_s] value
|
|
1137
|
+
# value to typecast
|
|
1138
|
+
#
|
|
1139
|
+
# @return [Class]
|
|
1140
|
+
# Class constructed from value
|
|
1141
|
+
#
|
|
1142
|
+
# @api private
|
|
1143
|
+
def typecast_to_class(value)
|
|
1144
|
+
model.find_const(value.to_s)
|
|
1145
|
+
rescue NameError
|
|
1146
|
+
value
|
|
674
1147
|
end
|
|
675
1148
|
end # class Property
|
|
676
1149
|
end # module DataMapper
|