ardm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +35 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/README.md +70 -0
- data/Rakefile +4 -0
- data/ardm.gemspec +29 -0
- data/db/.gitignore +1 -0
- data/lib/ardm/active_record/associations.rb +80 -0
- data/lib/ardm/active_record/base.rb +49 -0
- data/lib/ardm/active_record/dirty.rb +25 -0
- data/lib/ardm/active_record/hooks.rb +31 -0
- data/lib/ardm/active_record/inheritance.rb +37 -0
- data/lib/ardm/active_record/is/state_machine.rb +21 -0
- data/lib/ardm/active_record/is.rb +22 -0
- data/lib/ardm/active_record/not_found.rb +7 -0
- data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
- data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
- data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
- data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
- data/lib/ardm/active_record/predicate_builder.rb +19 -0
- data/lib/ardm/active_record/property.rb +357 -0
- data/lib/ardm/active_record/query.rb +108 -0
- data/lib/ardm/active_record/record.rb +70 -0
- data/lib/ardm/active_record/relation.rb +83 -0
- data/lib/ardm/active_record/repository.rb +38 -0
- data/lib/ardm/active_record/serialization.rb +164 -0
- data/lib/ardm/active_record/storage_names.rb +28 -0
- data/lib/ardm/active_record/validations.rb +111 -0
- data/lib/ardm/active_record.rb +43 -0
- data/lib/ardm/data_mapper/not_found.rb +5 -0
- data/lib/ardm/data_mapper/record.rb +41 -0
- data/lib/ardm/data_mapper.rb +5 -0
- data/lib/ardm/env.rb +5 -0
- data/lib/ardm/property/api_key.rb +30 -0
- data/lib/ardm/property/bcrypt_hash.rb +31 -0
- data/lib/ardm/property/binary.rb +23 -0
- data/lib/ardm/property/boolean.rb +29 -0
- data/lib/ardm/property/class.rb +19 -0
- data/lib/ardm/property/comma_separated_list.rb +28 -0
- data/lib/ardm/property/csv.rb +35 -0
- data/lib/ardm/property/date.rb +12 -0
- data/lib/ardm/property/datetime.rb +12 -0
- data/lib/ardm/property/decimal.rb +38 -0
- data/lib/ardm/property/discriminator.rb +65 -0
- data/lib/ardm/property/enum.rb +51 -0
- data/lib/ardm/property/epoch_time.rb +38 -0
- data/lib/ardm/property/file_path.rb +25 -0
- data/lib/ardm/property/flag.rb +65 -0
- data/lib/ardm/property/float.rb +18 -0
- data/lib/ardm/property/integer.rb +24 -0
- data/lib/ardm/property/invalid_value_error.rb +22 -0
- data/lib/ardm/property/ip_address.rb +35 -0
- data/lib/ardm/property/json.rb +49 -0
- data/lib/ardm/property/lookup.rb +29 -0
- data/lib/ardm/property/numeric.rb +40 -0
- data/lib/ardm/property/object.rb +36 -0
- data/lib/ardm/property/paranoid_boolean.rb +18 -0
- data/lib/ardm/property/paranoid_datetime.rb +17 -0
- data/lib/ardm/property/regexp.rb +22 -0
- data/lib/ardm/property/serial.rb +16 -0
- data/lib/ardm/property/slug.rb +29 -0
- data/lib/ardm/property/string.rb +40 -0
- data/lib/ardm/property/support/dirty_minder.rb +169 -0
- data/lib/ardm/property/support/flags.rb +41 -0
- data/lib/ardm/property/support/paranoid_base.rb +78 -0
- data/lib/ardm/property/text.rb +11 -0
- data/lib/ardm/property/time.rb +12 -0
- data/lib/ardm/property/uri.rb +27 -0
- data/lib/ardm/property/uuid.rb +65 -0
- data/lib/ardm/property/validation.rb +208 -0
- data/lib/ardm/property/yaml.rb +38 -0
- data/lib/ardm/property.rb +891 -0
- data/lib/ardm/property_set.rb +152 -0
- data/lib/ardm/query/expression.rb +85 -0
- data/lib/ardm/query/ext/symbol.rb +37 -0
- data/lib/ardm/query/operator.rb +64 -0
- data/lib/ardm/record.rb +1 -0
- data/lib/ardm/support/assertions.rb +8 -0
- data/lib/ardm/support/deprecate.rb +12 -0
- data/lib/ardm/support/descendant_set.rb +89 -0
- data/lib/ardm/support/equalizer.rb +48 -0
- data/lib/ardm/support/ext/array.rb +22 -0
- data/lib/ardm/support/ext/blank.rb +25 -0
- data/lib/ardm/support/ext/hash.rb +67 -0
- data/lib/ardm/support/ext/module.rb +47 -0
- data/lib/ardm/support/ext/object.rb +57 -0
- data/lib/ardm/support/ext/string.rb +24 -0
- data/lib/ardm/support/ext/try_dup.rb +12 -0
- data/lib/ardm/support/hook.rb +405 -0
- data/lib/ardm/support/lazy_array.rb +451 -0
- data/lib/ardm/support/local_object_space.rb +13 -0
- data/lib/ardm/support/logger.rb +201 -0
- data/lib/ardm/support/mash.rb +176 -0
- data/lib/ardm/support/naming_conventions.rb +90 -0
- data/lib/ardm/support/ordered_set.rb +380 -0
- data/lib/ardm/support/subject.rb +33 -0
- data/lib/ardm/support/subject_set.rb +250 -0
- data/lib/ardm/version.rb +3 -0
- data/lib/ardm.rb +56 -0
- data/spec/fixtures/api_user.rb +11 -0
- data/spec/fixtures/article.rb +22 -0
- data/spec/fixtures/bookmark.rb +14 -0
- data/spec/fixtures/invention.rb +5 -0
- data/spec/fixtures/network_node.rb +23 -0
- data/spec/fixtures/person.rb +17 -0
- data/spec/fixtures/software_package.rb +22 -0
- data/spec/fixtures/ticket.rb +12 -0
- data/spec/fixtures/tshirt.rb +15 -0
- data/spec/integration/api_key_spec.rb +25 -0
- data/spec/integration/bcrypt_hash_spec.rb +45 -0
- data/spec/integration/comma_separated_list_spec.rb +85 -0
- data/spec/integration/dirty_minder_spec.rb +197 -0
- data/spec/integration/enum_spec.rb +79 -0
- data/spec/integration/epoch_time_spec.rb +59 -0
- data/spec/integration/file_path_spec.rb +158 -0
- data/spec/integration/flag_spec.rb +72 -0
- data/spec/integration/ip_address_spec.rb +151 -0
- data/spec/integration/json_spec.rb +70 -0
- data/spec/integration/slug_spec.rb +65 -0
- data/spec/integration/uri_spec.rb +136 -0
- data/spec/integration/uuid_spec.rb +102 -0
- data/spec/integration/yaml_spec.rb +85 -0
- data/spec/public/property/binary_spec.rb +41 -0
- data/spec/public/property/boolean_spec.rb +30 -0
- data/spec/public/property/class_spec.rb +28 -0
- data/spec/public/property/date_spec.rb +22 -0
- data/spec/public/property/date_time_spec.rb +22 -0
- data/spec/public/property/decimal_spec.rb +23 -0
- data/spec/public/property/discriminator_spec.rb +133 -0
- data/spec/public/property/float_spec.rb +22 -0
- data/spec/public/property/integer_spec.rb +22 -0
- data/spec/public/property/object_spec.rb +103 -0
- data/spec/public/property/serial_spec.rb +22 -0
- data/spec/public/property/string_spec.rb +22 -0
- data/spec/public/property/text_spec.rb +23 -0
- data/spec/public/property/time_spec.rb +22 -0
- data/spec/public/property_spec.rb +316 -0
- data/spec/rcov.opts +6 -0
- data/spec/schema.rb +86 -0
- data/spec/semipublic/property/binary_spec.rb +14 -0
- data/spec/semipublic/property/boolean_spec.rb +48 -0
- data/spec/semipublic/property/class_spec.rb +36 -0
- data/spec/semipublic/property/date_spec.rb +44 -0
- data/spec/semipublic/property/date_time_spec.rb +47 -0
- data/spec/semipublic/property/decimal_spec.rb +83 -0
- data/spec/semipublic/property/discriminator_spec.rb +22 -0
- data/spec/semipublic/property/float_spec.rb +83 -0
- data/spec/semipublic/property/integer_spec.rb +83 -0
- data/spec/semipublic/property/lookup_spec.rb +27 -0
- data/spec/semipublic/property/serial_spec.rb +14 -0
- data/spec/semipublic/property/string_spec.rb +14 -0
- data/spec/semipublic/property/text_spec.rb +30 -0
- data/spec/semipublic/property/time_spec.rb +49 -0
- data/spec/semipublic/property_spec.rb +51 -0
- data/spec/shared/flags_shared_spec.rb +36 -0
- data/spec/shared/identity_function_group.rb +5 -0
- data/spec/shared/public_property_spec.rb +229 -0
- data/spec/shared/semipublic_property_spec.rb +159 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/unit/bcrypt_hash_spec.rb +154 -0
- data/spec/unit/csv_spec.rb +139 -0
- data/spec/unit/dirty_minder_spec.rb +64 -0
- data/spec/unit/enum_spec.rb +125 -0
- data/spec/unit/epoch_time_spec.rb +72 -0
- data/spec/unit/file_path_spec.rb +75 -0
- data/spec/unit/flag_spec.rb +114 -0
- data/spec/unit/ip_address_spec.rb +109 -0
- data/spec/unit/json_spec.rb +127 -0
- data/spec/unit/paranoid_boolean_spec.rb +142 -0
- data/spec/unit/paranoid_datetime_spec.rb +149 -0
- data/spec/unit/regexp_spec.rb +62 -0
- data/spec/unit/uri_spec.rb +64 -0
- data/spec/unit/yaml_spec.rb +111 -0
- data/tasks/spec.rake +40 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +350 -0
@@ -0,0 +1,891 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'time'
|
3
|
+
require 'coercible'
|
4
|
+
|
5
|
+
|
6
|
+
module Ardm
|
7
|
+
class Property
|
8
|
+
autoload :CommaSeparatedList, 'ardm/property/comma_separated_list'
|
9
|
+
autoload :Csv, 'ardm/property/csv'
|
10
|
+
autoload :BCryptHash, 'ardm/property/bcrypt_hash'
|
11
|
+
autoload :Enum, 'ardm/property/enum'
|
12
|
+
autoload :EpochTime, 'ardm/property/epoch_time'
|
13
|
+
autoload :FilePath, 'ardm/property/file_path'
|
14
|
+
autoload :Flag, 'ardm/property/flag'
|
15
|
+
autoload :IPAddress, 'ardm/property/ip_address'
|
16
|
+
autoload :Json, 'ardm/property/json'
|
17
|
+
autoload :Regexp, 'ardm/property/regexp'
|
18
|
+
autoload :ParanoidBoolean, 'ardm/property/paranoid_boolean'
|
19
|
+
autoload :ParanoidDateTime, 'ardm/property/paranoid_datetime'
|
20
|
+
autoload :Slug, 'ardm/property/slug'
|
21
|
+
autoload :UUID, 'ardm/property/uuid'
|
22
|
+
autoload :URI, 'ardm/property/uri'
|
23
|
+
autoload :Yaml, 'ardm/property/yaml'
|
24
|
+
autoload :APIKey, 'ardm/property/api_key'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Ardm
|
29
|
+
# = Properties
|
30
|
+
# Properties for a model are not derived from a database structure, but
|
31
|
+
# instead explicitly declared inside your model class definitions. These
|
32
|
+
# properties then map (or, if using automigrate, generate) fields in your
|
33
|
+
# database.
|
34
|
+
#
|
35
|
+
# If you are coming to Ardm from another ORM framework, such as
|
36
|
+
# ActiveRecord, this may be a fundamental difference in thinking to you.
|
37
|
+
# However, there are several advantages to defining your properties in your
|
38
|
+
# models:
|
39
|
+
#
|
40
|
+
# * information about your model is centralized in one place: rather than
|
41
|
+
# having to dig out migrations, xml or other configuration files.
|
42
|
+
# * use of mixins can be applied to model properties: better code reuse
|
43
|
+
# * having information centralized in your models, encourages you and the
|
44
|
+
# developers on your team to take a model-centric view of development.
|
45
|
+
# * it provides the ability to use Ruby's access control functions.
|
46
|
+
# * and, because Ardm only cares about properties explicitly defined
|
47
|
+
# in your models, Ardm plays well with legacy databases, and shares
|
48
|
+
# databases easily with other applications.
|
49
|
+
#
|
50
|
+
# == Declaring Properties
|
51
|
+
# Inside your class, you call the property method for each property you want
|
52
|
+
# to add. The only two required arguments are the name and type, everything
|
53
|
+
# else is optional.
|
54
|
+
#
|
55
|
+
# class Post < ActiveRecord::Base
|
56
|
+
# property :title, String, :required => true # Cannot be null
|
57
|
+
# property :publish, Boolean, :default => false # Default value for new records is false
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# By default, Ardm supports the following primitive (Ruby) types
|
61
|
+
# also called core properties:
|
62
|
+
#
|
63
|
+
# * Boolean
|
64
|
+
# * Class (datastore primitive is the same as String. Used for Inheritance)
|
65
|
+
# * Date
|
66
|
+
# * DateTime
|
67
|
+
# * Decimal
|
68
|
+
# * Float
|
69
|
+
# * Integer
|
70
|
+
# * Object (marshalled out during serialization)
|
71
|
+
# * String (default length is 50)
|
72
|
+
# * Text (limit of 65k characters by default)
|
73
|
+
# * Time
|
74
|
+
#
|
75
|
+
# == Limiting Access
|
76
|
+
# Property access control is uses the same terminology Ruby does. Properties
|
77
|
+
# are public by default, but can also be declared private or protected as
|
78
|
+
# needed (via the :accessor option).
|
79
|
+
#
|
80
|
+
# class Post < ActiveRecord::Base
|
81
|
+
# property :title, String, :accessor => :private # Both reader and writer are private
|
82
|
+
# property :body, Text, :accessor => :protected # Both reader and writer are protected
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# Access control is also analogous to Ruby attribute readers and writers, and can
|
86
|
+
# be declared using :reader and :writer, in addition to :accessor.
|
87
|
+
#
|
88
|
+
# class Post < ActiveRecord::Base
|
89
|
+
# property :title, String, :writer => :private # Only writer is private
|
90
|
+
# property :tags, String, :reader => :protected # Only reader is protected
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# == Overriding Accessors
|
94
|
+
# The reader/writer for any property can be overridden in the same manner that Ruby
|
95
|
+
# attr readers/writers can be. After the property is defined, just add your custom
|
96
|
+
# reader or writer:
|
97
|
+
#
|
98
|
+
# class Post < ActiveRecord::Base
|
99
|
+
# property :title, String
|
100
|
+
#
|
101
|
+
# def title=(new_title)
|
102
|
+
# raise ArgumentError if new_title != 'Lee is l337'
|
103
|
+
# super(new_title)
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# Calling super ensures that any validators defined for the property are kept active.
|
108
|
+
#
|
109
|
+
# == Lazy Loading
|
110
|
+
# By default, some properties are not loaded when an object is fetched in
|
111
|
+
# Ardm. These lazily loaded properties are fetched on demand when their
|
112
|
+
# accessor is called for the first time (as it is often unnecessary to
|
113
|
+
# instantiate -every- property -every- time an object is loaded). For
|
114
|
+
# instance, Ardm::Property::Text fields are lazy loading by default,
|
115
|
+
# although you can over-ride this behavior if you wish:
|
116
|
+
#
|
117
|
+
# Example:
|
118
|
+
#
|
119
|
+
# class Post < ActiveRecord::Base
|
120
|
+
# property :title, String # Loads normally
|
121
|
+
# property :body, Text # Is lazily loaded by default
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# If you want to over-ride the lazy loading on any field you can set it to a
|
125
|
+
# context or false to disable it with the :lazy option. Contexts allow
|
126
|
+
# multiple lazy properties to be loaded at one time. If you set :lazy to
|
127
|
+
# true, it is placed in the :default context
|
128
|
+
#
|
129
|
+
# class Post < ActiveRecord::Base
|
130
|
+
# property :title, String # Loads normally
|
131
|
+
# property :body, Text, :lazy => false # The default is now over-ridden
|
132
|
+
# property :comment, String, :lazy => [ :detailed ] # Loads in the :detailed context
|
133
|
+
# property :author, String, :lazy => [ :summary, :detailed ] # Loads in :summary & :detailed context
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# Delaying the request for lazy-loaded attributes even applies to objects
|
137
|
+
# accessed through associations. In a sense, Ardm anticipates that
|
138
|
+
# you will likely be iterating over objects in associations and rolls all
|
139
|
+
# of the load commands for lazy-loaded properties into one request from
|
140
|
+
# the database.
|
141
|
+
#
|
142
|
+
# Example:
|
143
|
+
#
|
144
|
+
# Widget.get(1).components
|
145
|
+
# # loads when the post object is pulled from database, by default
|
146
|
+
#
|
147
|
+
# Widget.get(1).components.first.body
|
148
|
+
# # loads the values for the body property on all objects in the
|
149
|
+
# # association, rather than just this one.
|
150
|
+
#
|
151
|
+
# Widget.get(1).components.first.comment
|
152
|
+
# # loads both comment and author for all objects in the association
|
153
|
+
# # since they are both in the :detailed context
|
154
|
+
#
|
155
|
+
# == Keys
|
156
|
+
# Properties can be declared as primary or natural keys on a table.
|
157
|
+
# You should a property as the primary key of the table:
|
158
|
+
#
|
159
|
+
# Examples:
|
160
|
+
#
|
161
|
+
# property :id, Serial # auto-incrementing key
|
162
|
+
# property :legacy_pk, String, :key => true # 'natural' key
|
163
|
+
#
|
164
|
+
# This is roughly equivalent to ActiveRecord's <tt>set_primary_key</tt>,
|
165
|
+
# though non-integer data types may be used, thus Ardm supports natural
|
166
|
+
# keys. When a property is declared as a natural key, accessing the object
|
167
|
+
# using the indexer syntax <tt>Class[key]</tt> remains valid.
|
168
|
+
#
|
169
|
+
# User.get(1)
|
170
|
+
# # when :id is the primary key on the users table
|
171
|
+
# User.get('bill')
|
172
|
+
# # when :name is the primary (natural) key on the users table
|
173
|
+
#
|
174
|
+
# == Indices
|
175
|
+
# You can add indices for your properties by using the <tt>:index</tt>
|
176
|
+
# option. If you use <tt>true</tt> as the option value, the index will be
|
177
|
+
# automatically named. If you want to name the index yourself, use a symbol
|
178
|
+
# as the value.
|
179
|
+
#
|
180
|
+
# property :last_name, String, :index => true
|
181
|
+
# property :first_name, String, :index => :name
|
182
|
+
#
|
183
|
+
# You can create multi-column composite indices by using the same symbol in
|
184
|
+
# all the columns belonging to the index. The columns will appear in the
|
185
|
+
# index in the order they are declared.
|
186
|
+
#
|
187
|
+
# property :last_name, String, :index => :name
|
188
|
+
# property :first_name, String, :index => :name
|
189
|
+
# # => index on (last_name, first_name)
|
190
|
+
#
|
191
|
+
# If you want to make the indices unique, use <tt>:unique_index</tt> instead
|
192
|
+
# of <tt>:index</tt>
|
193
|
+
#
|
194
|
+
# == Inferred Validations
|
195
|
+
# If you require the dm-validations plugin, auto-validations will
|
196
|
+
# automatically be mixed-in in to your model classes: validation rules that
|
197
|
+
# are inferred when properties are declared with specific column restrictions.
|
198
|
+
#
|
199
|
+
# class Post < ActiveRecord::Base
|
200
|
+
# property :title, String, :length => 250, :min => 0, :max => 250
|
201
|
+
# # => infers 'validates_length :title'
|
202
|
+
#
|
203
|
+
# property :title, String, :required => true
|
204
|
+
# # => infers 'validates_present :title'
|
205
|
+
#
|
206
|
+
# property :email, String, :format => :email_address
|
207
|
+
# # => infers 'validates_format :email, :with => :email_address'
|
208
|
+
#
|
209
|
+
# property :title, String, :length => 255, :required => true
|
210
|
+
# # => infers both 'validates_length' as well as 'validates_present'
|
211
|
+
# # better: property :title, String, :length => 1..255
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
# This functionality is available with the dm-validations gem. For more information
|
215
|
+
# about validations, check the documentation for dm-validations.
|
216
|
+
#
|
217
|
+
# == Default Values
|
218
|
+
# To set a default for a property, use the <tt>:default</tt> key. The
|
219
|
+
# property will be set to the value associated with that key the first time
|
220
|
+
# it is accessed, or when the resource is saved if it hasn't been set with
|
221
|
+
# another value already. This value can be a static value, such as 'hello'
|
222
|
+
# but it can also be a proc that will be evaluated when the property is read
|
223
|
+
# before its value has been set. The property is set to the return of the
|
224
|
+
# proc. The proc is passed two values, the resource the property is being set
|
225
|
+
# for and the property itself.
|
226
|
+
#
|
227
|
+
# property :display_name, String, :default => lambda { |resource, property| resource.login }
|
228
|
+
#
|
229
|
+
# Word of warning. Don't try to read the value of the property you're setting
|
230
|
+
# the default for in the proc. An infinite loop will ensue.
|
231
|
+
#
|
232
|
+
# == Embedded Values (not implemented yet)
|
233
|
+
# As an alternative to extraneous has_one relationships, consider using an
|
234
|
+
# EmbeddedValue.
|
235
|
+
#
|
236
|
+
# == Property options reference
|
237
|
+
#
|
238
|
+
# :accessor if false, neither reader nor writer methods are
|
239
|
+
# created for this property
|
240
|
+
#
|
241
|
+
# :reader if false, reader method is not created for this property
|
242
|
+
#
|
243
|
+
# :writer if false, writer method is not created for this property
|
244
|
+
#
|
245
|
+
# :lazy if true, property value is only loaded when on first read
|
246
|
+
# if false, property value is always loaded
|
247
|
+
# if a symbol, property value is loaded with other properties
|
248
|
+
# in the same group
|
249
|
+
#
|
250
|
+
# :default default value of this property
|
251
|
+
#
|
252
|
+
# :allow_nil if true, property may have a nil value on save
|
253
|
+
#
|
254
|
+
# :key name of the key associated with this property.
|
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
|
280
|
+
#
|
281
|
+
# == Overriding default Property options
|
282
|
+
#
|
283
|
+
# There is the ability to reconfigure a Property and it's subclasses by explicitly
|
284
|
+
# setting a value in the Property, eg:
|
285
|
+
#
|
286
|
+
# # set all String properties to have a default length of 255
|
287
|
+
# Ardm::Property::String.length(255)
|
288
|
+
#
|
289
|
+
# # set all Boolean properties to not allow nil (force true or false)
|
290
|
+
# Ardm::Property::Boolean.allow_nil(false)
|
291
|
+
#
|
292
|
+
# # set all properties to be required by default
|
293
|
+
# Ardm::Property.required(true)
|
294
|
+
#
|
295
|
+
# # turn off auto-validation for all properties by default
|
296
|
+
# Ardm::Property.auto_validation(false)
|
297
|
+
#
|
298
|
+
# # set all mutator methods to be private by default
|
299
|
+
# Ardm::Property.writer(:private)
|
300
|
+
#
|
301
|
+
# Please note that this has no effect when a subclass has explicitly
|
302
|
+
# defined it's own option. For example, setting the String length to
|
303
|
+
# 255 will not affect the Text property even though it inherits from
|
304
|
+
# String, because it sets it's own default length to 65535.
|
305
|
+
#
|
306
|
+
# == Misc. Notes
|
307
|
+
# * Properties declared as strings will default to a length of 50, rather than
|
308
|
+
# 255 (typical max varchar column size). To overload the default, pass
|
309
|
+
# <tt>:length => 255</tt> or <tt>:length => 0..255</tt>. Since Ardm
|
310
|
+
# does not introspect for properties, this means that legacy database tables
|
311
|
+
# may need their <tt>String</tt> columns defined with a <tt>:length</tt> so
|
312
|
+
# that DM does not apply an un-needed length validation, or allow overflow.
|
313
|
+
# * You may declare a Property with the data-type of <tt>Class</tt>.
|
314
|
+
# see SingleTableInheritance for more on how to use <tt>Class</tt> columns.
|
315
|
+
class Property
|
316
|
+
include Ardm::Assertions
|
317
|
+
include Subject
|
318
|
+
extend Equalizer
|
319
|
+
|
320
|
+
equalize :model, :name, :options
|
321
|
+
|
322
|
+
module Undefined; end
|
323
|
+
|
324
|
+
PRIMITIVES = [
|
325
|
+
TrueClass,
|
326
|
+
::String,
|
327
|
+
::Float,
|
328
|
+
::Integer,
|
329
|
+
::BigDecimal,
|
330
|
+
::DateTime,
|
331
|
+
::Date,
|
332
|
+
::Time,
|
333
|
+
::Class
|
334
|
+
].to_set.freeze
|
335
|
+
|
336
|
+
OPTIONS = [
|
337
|
+
:load_as, :dump_as, :coercion_method,
|
338
|
+
:accessor, :reader, :writer,
|
339
|
+
:lazy, :default, :key, :field,
|
340
|
+
:index, :unique_index,
|
341
|
+
:unique, :allow_nil, :allow_blank, :required
|
342
|
+
]
|
343
|
+
|
344
|
+
# Possible :visibility option values
|
345
|
+
VISIBILITY_OPTIONS = [ :public, :protected, :private ].to_set.freeze
|
346
|
+
|
347
|
+
# Invalid property names
|
348
|
+
INVALID_NAMES = %w[]#(::ActiveRecord::Base.instance_methods +
|
349
|
+
#::ActiveRecord::Base.private_instance_methods
|
350
|
+
#).map { |name| name.to_s }
|
351
|
+
|
352
|
+
attr_reader :load_as, :dump_as, :coercion_method,
|
353
|
+
:model, :name, :instance_variable_name,
|
354
|
+
:reader_visibility, :writer_visibility, :options,
|
355
|
+
:default, :allow_nil, :allow_blank, :required
|
356
|
+
|
357
|
+
alias_method :load_class, :load_as
|
358
|
+
alias_method :dump_class, :dump_as
|
359
|
+
|
360
|
+
class << self
|
361
|
+
# @api semipublic
|
362
|
+
def determine_class(type)
|
363
|
+
return type if type < Ardm::Property::Object
|
364
|
+
find_class(demodulize(type.name))
|
365
|
+
end
|
366
|
+
|
367
|
+
def demodulize(name)
|
368
|
+
name.to_s.gsub(/^.*::/,'')
|
369
|
+
end
|
370
|
+
|
371
|
+
# @api private
|
372
|
+
def demodulized_names
|
373
|
+
@demodulized_names ||= {}
|
374
|
+
end
|
375
|
+
|
376
|
+
# @api semipublic
|
377
|
+
def find_class(name)
|
378
|
+
klass = demodulized_names[name]
|
379
|
+
klass ||= const_get(name) if const_defined?(name)
|
380
|
+
klass
|
381
|
+
end
|
382
|
+
|
383
|
+
# @api public
|
384
|
+
def descendants
|
385
|
+
@descendants ||= DescendantSet.new
|
386
|
+
end
|
387
|
+
|
388
|
+
# @api private
|
389
|
+
def inherited(descendant)
|
390
|
+
# Descendants is a tree rooted in Ardm::Property that tracks
|
391
|
+
# inheritance. We pre-calculate each comparison value (demodulized
|
392
|
+
# class name) to achieve a Hash[]-time lookup, rather than walk the
|
393
|
+
# entire descendant tree and calculate names on-demand (expensive,
|
394
|
+
# redundant).
|
395
|
+
#
|
396
|
+
# Since the algorithm relegates property class name lookups to a flat
|
397
|
+
# namespace, we need to ensure properties defined outside of DM don't
|
398
|
+
# override built-ins (Serial, String, etc) by merely defining a property
|
399
|
+
# of a same name. We avoid this by only ever adding to the lookup
|
400
|
+
# table. Given that DM loads its own property classes first, we can
|
401
|
+
# assume that their names are "reserved" when added to the table.
|
402
|
+
#
|
403
|
+
# External property authors who want to provide "replacements" for
|
404
|
+
# builtins (e.g. in a non-DM-supported adapter) should follow the
|
405
|
+
# convention of wrapping those properties in a module, and include'ing
|
406
|
+
# the module on the model class directly. This bypasses the DM-hooked
|
407
|
+
# const_missing lookup that would normally check this table.
|
408
|
+
descendants << descendant
|
409
|
+
|
410
|
+
Property.demodulized_names[demodulize(descendant.name)] ||= descendant
|
411
|
+
|
412
|
+
# inherit accepted options
|
413
|
+
descendant.accepted_options.concat(accepted_options)
|
414
|
+
|
415
|
+
# inherit the option values
|
416
|
+
options.each { |key, value| descendant.send(key, value) }
|
417
|
+
|
418
|
+
super
|
419
|
+
end
|
420
|
+
|
421
|
+
# @api public
|
422
|
+
def accepted_options
|
423
|
+
@accepted_options ||= []
|
424
|
+
end
|
425
|
+
|
426
|
+
# @api public
|
427
|
+
def accept_options(*args)
|
428
|
+
accepted_options.concat(args)
|
429
|
+
|
430
|
+
# create methods for each new option
|
431
|
+
args.each do |property_option|
|
432
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
433
|
+
def self.#{property_option}(value = Undefined) # def self.unique(value = Undefined)
|
434
|
+
return @#{property_option} if value.equal?(Undefined) # return @unique if value.equal?(Undefined)
|
435
|
+
descendants.each do |descendant| # descendants.each do |descendant|
|
436
|
+
unless descendant.instance_variable_defined?(:@#{property_option}) # unless descendant.instance_variable_defined?(:@unique)
|
437
|
+
descendant.#{property_option}(value) # descendant.unique(value)
|
438
|
+
end # end
|
439
|
+
end # end
|
440
|
+
@#{property_option} = value # @unique = value
|
441
|
+
end # end
|
442
|
+
RUBY
|
443
|
+
end
|
444
|
+
|
445
|
+
descendants.each { |descendant| descendant.accepted_options.concat(args) }
|
446
|
+
end
|
447
|
+
|
448
|
+
# Gives all the options set on this property
|
449
|
+
#
|
450
|
+
# @return [Hash] with all options and their values set on this property
|
451
|
+
#
|
452
|
+
# @api public
|
453
|
+
def options
|
454
|
+
options = {}
|
455
|
+
accepted_options.each do |name|
|
456
|
+
options[name] = send(name) if instance_variable_defined?("@#{name}")
|
457
|
+
end
|
458
|
+
options
|
459
|
+
end
|
460
|
+
|
461
|
+
# @api deprecated
|
462
|
+
def primitive(*args)
|
463
|
+
warn "Ardm::Property.primitive is deprecated, use .load_as instead (#{caller.first})"
|
464
|
+
load_as(*args)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
accept_options *Property::OPTIONS
|
469
|
+
|
470
|
+
# A hook to allow properties to extend or modify the model it's bound to.
|
471
|
+
# Implementations are not supposed to modify the state of the property
|
472
|
+
# class, and should produce no side-effects on the property instance.
|
473
|
+
def bind
|
474
|
+
# no op
|
475
|
+
end
|
476
|
+
|
477
|
+
# Supplies the field in the data-store which the property corresponds to
|
478
|
+
#
|
479
|
+
# @return [String] name of field in data-store
|
480
|
+
#
|
481
|
+
# @api semipublic
|
482
|
+
def field
|
483
|
+
# defer setting the field with the adapter specific naming
|
484
|
+
# conventions until after the adapter has been setup
|
485
|
+
@field ||= model.field_naming_convention.call(self).freeze
|
486
|
+
end
|
487
|
+
|
488
|
+
# Returns true if property is unique. Serial properties and keys
|
489
|
+
# are unique by default.
|
490
|
+
#
|
491
|
+
# @return [Boolean]
|
492
|
+
# true if property has uniq index defined, false otherwise
|
493
|
+
#
|
494
|
+
# @api public
|
495
|
+
def unique?
|
496
|
+
!!@unique
|
497
|
+
end
|
498
|
+
|
499
|
+
# Returns index name if property has index.
|
500
|
+
#
|
501
|
+
# @return [Boolean, Symbol, Array]
|
502
|
+
# returns true if property is indexed by itself
|
503
|
+
# returns a Symbol if the property is indexed with other properties
|
504
|
+
# returns an Array if the property belongs to multiple indexes
|
505
|
+
# returns false if the property does not belong to any indexes
|
506
|
+
#
|
507
|
+
# @api public
|
508
|
+
attr_reader :index
|
509
|
+
|
510
|
+
# Returns true if property has unique index. Serial properties and
|
511
|
+
# keys are unique by default.
|
512
|
+
#
|
513
|
+
# @return [Boolean, Symbol, Array]
|
514
|
+
# returns true if property is indexed by itself
|
515
|
+
# returns a Symbol if the property is indexed with other properties
|
516
|
+
# returns an Array if the property belongs to multiple indexes
|
517
|
+
# returns false if the property does not belong to any indexes
|
518
|
+
#
|
519
|
+
# @api public
|
520
|
+
attr_reader :unique_index
|
521
|
+
|
522
|
+
# Returns whether or not the property is to be lazy-loaded
|
523
|
+
#
|
524
|
+
# @return [Boolean]
|
525
|
+
# true if the property is to be lazy-loaded
|
526
|
+
#
|
527
|
+
# @api public
|
528
|
+
def lazy?
|
529
|
+
@lazy
|
530
|
+
end
|
531
|
+
|
532
|
+
# Returns whether or not the property is a key or a part of a key
|
533
|
+
#
|
534
|
+
# @return [Boolean]
|
535
|
+
# true if the property is a key or a part of a key
|
536
|
+
#
|
537
|
+
# @api public
|
538
|
+
def key?
|
539
|
+
@key
|
540
|
+
end
|
541
|
+
|
542
|
+
# Returns whether or not the property is "serial" (auto-incrementing)
|
543
|
+
#
|
544
|
+
# @return [Boolean]
|
545
|
+
# whether or not the property is "serial"
|
546
|
+
#
|
547
|
+
# @api public
|
548
|
+
def serial?
|
549
|
+
@serial
|
550
|
+
end
|
551
|
+
|
552
|
+
# Returns whether or not the property must be non-nil and non-blank
|
553
|
+
#
|
554
|
+
# @return [Boolean]
|
555
|
+
# whether or not the property is required
|
556
|
+
#
|
557
|
+
# @api public
|
558
|
+
def required?
|
559
|
+
@required
|
560
|
+
end
|
561
|
+
|
562
|
+
# Returns whether or not the property can accept 'nil' as it's value
|
563
|
+
#
|
564
|
+
# @return [Boolean]
|
565
|
+
# whether or not the property can accept 'nil'
|
566
|
+
#
|
567
|
+
# @api public
|
568
|
+
def allow_nil?
|
569
|
+
@allow_nil
|
570
|
+
end
|
571
|
+
|
572
|
+
# Returns whether or not the property can be a blank value
|
573
|
+
#
|
574
|
+
# @return [Boolean]
|
575
|
+
# whether or not the property can be blank
|
576
|
+
#
|
577
|
+
# @api public
|
578
|
+
def allow_blank?
|
579
|
+
@allow_blank
|
580
|
+
end
|
581
|
+
|
582
|
+
# Standardized reader method for the property
|
583
|
+
#
|
584
|
+
# @param [Resource] resource
|
585
|
+
# model instance for which this property is to be loaded
|
586
|
+
#
|
587
|
+
# @return [Object]
|
588
|
+
# the value of this property for the provided instance
|
589
|
+
#
|
590
|
+
# @raise [ArgumentError] "+resource+ should be a Resource, but was ...."
|
591
|
+
#
|
592
|
+
# @api private
|
593
|
+
def get(resource)
|
594
|
+
get!(resource)
|
595
|
+
end
|
596
|
+
|
597
|
+
# Fetch the ivar value in the resource
|
598
|
+
#
|
599
|
+
# @param [Resource] resource
|
600
|
+
# model instance for which this property is to be unsafely loaded
|
601
|
+
#
|
602
|
+
# @return [Object]
|
603
|
+
# current @ivar value of this property in +resource+
|
604
|
+
#
|
605
|
+
# @api private
|
606
|
+
def get!(resource)
|
607
|
+
#resource.instance_variable_get(instance_variable_name)
|
608
|
+
val = resource.send :read_attribute, field
|
609
|
+
if val.nil?
|
610
|
+
set_default_value(resource)
|
611
|
+
else
|
612
|
+
val
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def set_default_value(resource)
|
617
|
+
return if loaded?(resource) || !default?
|
618
|
+
set(resource, default_for(resource))
|
619
|
+
end
|
620
|
+
|
621
|
+
# Provides a standardized setter method for the property
|
622
|
+
#
|
623
|
+
# @param [Resource] resource
|
624
|
+
# the resource to get the value from
|
625
|
+
# @param [Object] value
|
626
|
+
# the value to set in the resource
|
627
|
+
#
|
628
|
+
# @return [Object]
|
629
|
+
# +value+ after being typecasted according to this property's primitive
|
630
|
+
#
|
631
|
+
# @raise [ArgumentError] "+resource+ should be a Resource, but was ...."
|
632
|
+
#
|
633
|
+
# @api private
|
634
|
+
def set(resource, value)
|
635
|
+
set!(resource, typecast(value))
|
636
|
+
end
|
637
|
+
|
638
|
+
# Set the ivar value in the resource
|
639
|
+
#
|
640
|
+
# @param [Resource] resource
|
641
|
+
# the resource to set
|
642
|
+
# @param [Object] value
|
643
|
+
# the value to set in the resource
|
644
|
+
#
|
645
|
+
# @return [Object]
|
646
|
+
# the value set in the resource
|
647
|
+
#
|
648
|
+
# @api private
|
649
|
+
def set!(resource, value)
|
650
|
+
#resource.instance_variable_set(instance_variable_name, value)
|
651
|
+
resource.send :write_attribute, field, value
|
652
|
+
resource.send :read_attribute, field
|
653
|
+
end
|
654
|
+
|
655
|
+
# Check if the attribute corresponding to the property is loaded
|
656
|
+
#
|
657
|
+
# @param [Resource] resource
|
658
|
+
# model instance for which the attribute is to be tested
|
659
|
+
#
|
660
|
+
# @return [Boolean]
|
661
|
+
# true if the attribute is loaded in the resource
|
662
|
+
#
|
663
|
+
# @api private
|
664
|
+
def loaded?(resource)
|
665
|
+
resource.send(:read_attribute, field) != nil
|
666
|
+
#resource.instance_variable_defined?(instance_variable_name)
|
667
|
+
#true
|
668
|
+
end
|
669
|
+
|
670
|
+
# @api private
|
671
|
+
def properties
|
672
|
+
@properties ||= model.properties
|
673
|
+
end
|
674
|
+
|
675
|
+
# @api semipublic
|
676
|
+
def typecast(value)
|
677
|
+
@coercer ||= Coercible::Coercer.new
|
678
|
+
if Array === value
|
679
|
+
value.map { |v| typecast(v) }
|
680
|
+
else
|
681
|
+
@coercer[value.class].send(coercion_method, value)
|
682
|
+
end
|
683
|
+
rescue Coercible::UnsupportedCoercion
|
684
|
+
value
|
685
|
+
end
|
686
|
+
|
687
|
+
# Test the value to see if it is a valid value for this Property
|
688
|
+
#
|
689
|
+
# @param [Object] loaded_value
|
690
|
+
# the value to be tested
|
691
|
+
#
|
692
|
+
# @return [Boolean]
|
693
|
+
# true if the value is valid
|
694
|
+
#
|
695
|
+
# @api semipulic
|
696
|
+
def valid?(value, negated = false)
|
697
|
+
dumped_value = dump(value)
|
698
|
+
|
699
|
+
if required? && dumped_value.nil?
|
700
|
+
negated || false
|
701
|
+
else
|
702
|
+
value_dumped?(dumped_value) || (dumped_value.nil? && (allow_nil? || negated))
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
# Asserts value is valid
|
707
|
+
#
|
708
|
+
# @param [Object] loaded_value
|
709
|
+
# the value to be tested
|
710
|
+
#
|
711
|
+
# @return [Boolean]
|
712
|
+
# true if the value is valid
|
713
|
+
#
|
714
|
+
# @raise [Property::InvalidValueError]
|
715
|
+
# if value is not valid
|
716
|
+
def assert_valid_value(value)
|
717
|
+
unless valid?(value)
|
718
|
+
raise Property::InvalidValueError.new(self,value)
|
719
|
+
end
|
720
|
+
true
|
721
|
+
end
|
722
|
+
|
723
|
+
# Returns a concise string representation of the property instance.
|
724
|
+
#
|
725
|
+
# @return [String]
|
726
|
+
# Concise string representation of the property instance.
|
727
|
+
#
|
728
|
+
# @api public
|
729
|
+
def inspect
|
730
|
+
"#<#{self.class.name} @model=#{model.inspect} @name=#{name.inspect}>"
|
731
|
+
end
|
732
|
+
|
733
|
+
# Test a value to see if it matches the primitive type
|
734
|
+
#
|
735
|
+
# @param [Object] value
|
736
|
+
# value to test
|
737
|
+
#
|
738
|
+
# @return [Boolean]
|
739
|
+
# true if the value is the correct type
|
740
|
+
#
|
741
|
+
# @api semipublic
|
742
|
+
def primitive?(value)
|
743
|
+
warn "#primitive? is deprecated, use #value_dumped? instead (#{caller.first})"
|
744
|
+
value_dumped?(value)
|
745
|
+
end
|
746
|
+
|
747
|
+
def primitive
|
748
|
+
warn "#primitive is deprecated, use #dump_as instead (#{caller.first})"
|
749
|
+
dump_as
|
750
|
+
end
|
751
|
+
|
752
|
+
# @api semipublic
|
753
|
+
def value_dumped?(value)
|
754
|
+
value.kind_of?(dump_as)
|
755
|
+
end
|
756
|
+
|
757
|
+
# @api semipublic
|
758
|
+
def value_loaded?(value)
|
759
|
+
value.kind_of?(load_as)
|
760
|
+
end
|
761
|
+
|
762
|
+
protected
|
763
|
+
|
764
|
+
# @api semipublic
|
765
|
+
def initialize(model, name, options = {})
|
766
|
+
options = options.to_hash.dup
|
767
|
+
|
768
|
+
if INVALID_NAMES.include?(name.to_s) || (kind_of?(Boolean) && INVALID_NAMES.include?("#{name}?"))
|
769
|
+
raise ArgumentError,
|
770
|
+
"+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method or a query option"
|
771
|
+
end
|
772
|
+
|
773
|
+
assert_valid_options(options)
|
774
|
+
|
775
|
+
predefined_options = self.class.options
|
776
|
+
|
777
|
+
@model = model
|
778
|
+
@name = name.to_s.chomp('?').to_sym
|
779
|
+
@options = predefined_options.merge(options).freeze
|
780
|
+
@instance_variable_name = "@#{@name}".freeze
|
781
|
+
@coercion_method = @options.fetch(:coercion_method)
|
782
|
+
|
783
|
+
@load_as = self.class.load_as
|
784
|
+
@dump_as = self.class.dump_as
|
785
|
+
|
786
|
+
@field = @options[:field].freeze unless @options[:field].nil?
|
787
|
+
@default = @options[:default]
|
788
|
+
|
789
|
+
@serial = @options.fetch(:serial, false)
|
790
|
+
@key = @options.fetch(:key, @serial)
|
791
|
+
@unique = @options.fetch(:unique, @key ? :key : false)
|
792
|
+
@required = @options.fetch(:required, @key)
|
793
|
+
@allow_nil = @options.fetch(:allow_nil, !@required)
|
794
|
+
@allow_blank = @options.fetch(:allow_blank, !@required)
|
795
|
+
@index = @options.fetch(:index, false)
|
796
|
+
@unique_index = @options.fetch(:unique_index, @unique)
|
797
|
+
@lazy = @options.fetch(:lazy, false) && !@key
|
798
|
+
|
799
|
+
determine_visibility
|
800
|
+
|
801
|
+
bind
|
802
|
+
end
|
803
|
+
|
804
|
+
# @api private
|
805
|
+
def assert_valid_options(options)
|
806
|
+
keys = options.keys
|
807
|
+
|
808
|
+
if (unknown_keys = keys - self.class.accepted_options).any?
|
809
|
+
raise ArgumentError, "options #{unknown_keys.map { |key| key.inspect }.join(' and ')} are unknown"
|
810
|
+
end
|
811
|
+
|
812
|
+
options.each do |key, value|
|
813
|
+
boolean_value = value == true || value == false
|
814
|
+
|
815
|
+
case key
|
816
|
+
when :field
|
817
|
+
assert_kind_of "options[:#{key}]", value, ::String
|
818
|
+
|
819
|
+
when :default
|
820
|
+
if value.nil?
|
821
|
+
raise ArgumentError, "options[:#{key}] must not be nil"
|
822
|
+
end
|
823
|
+
|
824
|
+
when :serial, :key, :allow_nil, :allow_blank, :required, :auto_validation
|
825
|
+
unless boolean_value
|
826
|
+
raise ArgumentError, "options[:#{key}] must be either true or false"
|
827
|
+
end
|
828
|
+
|
829
|
+
if key == :required && (keys.include?(:allow_nil) || keys.include?(:allow_blank))
|
830
|
+
raise ArgumentError, 'options[:required] cannot be mixed with :allow_nil or :allow_blank'
|
831
|
+
end
|
832
|
+
|
833
|
+
when :index, :unique_index, :unique, :lazy
|
834
|
+
unless boolean_value || value.kind_of?(Symbol) || (value.kind_of?(Array) && value.any? && value.all? { |val| val.kind_of?(Symbol) })
|
835
|
+
raise ArgumentError, "options[:#{key}] must be either true, false, a Symbol or an Array of Symbols"
|
836
|
+
end
|
837
|
+
|
838
|
+
when :length
|
839
|
+
assert_kind_of "options[:#{key}]", value, Range, ::Integer
|
840
|
+
|
841
|
+
when :size, :precision, :scale
|
842
|
+
assert_kind_of "options[:#{key}]", value, ::Integer
|
843
|
+
|
844
|
+
when :reader, :writer, :accessor
|
845
|
+
assert_kind_of "options[:#{key}]", value, Symbol
|
846
|
+
|
847
|
+
unless VISIBILITY_OPTIONS.include?(value)
|
848
|
+
raise ArgumentError, "options[:#{key}] must be #{VISIBILITY_OPTIONS.join(' or ')}"
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
# Assert given visibility value is supported.
|
855
|
+
#
|
856
|
+
# Will raise ArgumentError if this Property's reader and writer
|
857
|
+
# visibilities are not included in VISIBILITY_OPTIONS.
|
858
|
+
#
|
859
|
+
# @return [undefined]
|
860
|
+
#
|
861
|
+
# @raise [ArgumentError] "property visibility must be :public, :protected, or :private"
|
862
|
+
#
|
863
|
+
# @api private
|
864
|
+
def determine_visibility
|
865
|
+
default_accessor = @options.fetch(:accessor, :public)
|
866
|
+
|
867
|
+
@reader_visibility = @options.fetch(:reader, default_accessor)
|
868
|
+
@writer_visibility = @options.fetch(:writer, default_accessor)
|
869
|
+
end
|
870
|
+
end # class Property
|
871
|
+
end
|
872
|
+
|
873
|
+
require 'ardm/property/invalid_value_error'
|
874
|
+
require 'ardm/property/object'
|
875
|
+
require 'ardm/property/string'
|
876
|
+
require 'ardm/property/binary'
|
877
|
+
require 'ardm/property/text'
|
878
|
+
require 'ardm/property/numeric'
|
879
|
+
require 'ardm/property/float'
|
880
|
+
require 'ardm/property/decimal'
|
881
|
+
require 'ardm/property/boolean'
|
882
|
+
require 'ardm/property/integer'
|
883
|
+
require 'ardm/property/serial'
|
884
|
+
require 'ardm/property/date'
|
885
|
+
require 'ardm/property/datetime'
|
886
|
+
require 'ardm/property/time'
|
887
|
+
require 'ardm/property/class'
|
888
|
+
require 'ardm/property/discriminator'
|
889
|
+
require 'ardm/property/lookup'
|
890
|
+
require 'ardm/property_set'
|
891
|
+
require 'ardm/property/validation'
|