mack-data_mapper 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/lib/gems/addressable-2.0.0/lib/addressable/idna.rb +4867 -0
  2. data/lib/gems/addressable-2.0.0/lib/addressable/uri.rb +2469 -0
  3. data/lib/gems/addressable-2.0.0/lib/addressable/version.rb +35 -0
  4. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/adapters/data_objects_adapter.rb +85 -0
  5. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/aggregate_functions.rb +201 -0
  6. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/collection.rb +11 -0
  7. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/model.rb +11 -0
  8. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/repository.rb +7 -0
  9. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/support/symbol.rb +21 -0
  10. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/version.rb +7 -0
  11. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates.rb +15 -0
  12. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  13. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/data_objects_adapter.rb +709 -0
  14. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  15. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  16. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  17. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  18. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters.rb +22 -0
  19. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_many.rb +147 -0
  20. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_one.rb +107 -0
  21. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb +318 -0
  22. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_one.rb +61 -0
  23. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship.rb +223 -0
  24. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship_chain.rb +81 -0
  25. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations.rb +200 -0
  26. data/lib/gems/dm-core-0.9.7/lib/dm-core/auto_migrations.rb +105 -0
  27. data/lib/gems/dm-core-0.9.7/lib/dm-core/collection.rb +642 -0
  28. data/lib/gems/dm-core-0.9.7/lib/dm-core/dependency_queue.rb +32 -0
  29. data/lib/gems/dm-core-0.9.7/lib/dm-core/hook.rb +11 -0
  30. data/lib/gems/dm-core-0.9.7/lib/dm-core/identity_map.rb +42 -0
  31. data/lib/gems/dm-core-0.9.7/lib/dm-core/is.rb +16 -0
  32. data/lib/gems/dm-core-0.9.7/lib/dm-core/logger.rb +232 -0
  33. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrator.rb +29 -0
  35. data/lib/gems/dm-core-0.9.7/lib/dm-core/model.rb +488 -0
  36. data/lib/gems/dm-core-0.9.7/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/gems/dm-core-0.9.7/lib/dm-core/property.rb +663 -0
  38. data/lib/gems/dm-core-0.9.7/lib/dm-core/property_set.rb +169 -0
  39. data/lib/gems/dm-core-0.9.7/lib/dm-core/query.rb +628 -0
  40. data/lib/gems/dm-core-0.9.7/lib/dm-core/repository.rb +159 -0
  41. data/lib/gems/dm-core-0.9.7/lib/dm-core/resource.rb +637 -0
  42. data/lib/gems/dm-core-0.9.7/lib/dm-core/scope.rb +58 -0
  43. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/array.rb +13 -0
  44. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/assertions.rb +8 -0
  45. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/errors.rb +23 -0
  46. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/kernel.rb +11 -0
  47. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/symbol.rb +41 -0
  48. data/lib/gems/dm-core-0.9.7/lib/dm-core/support.rb +7 -0
  49. data/lib/gems/dm-core-0.9.7/lib/dm-core/transaction.rb +267 -0
  50. data/lib/gems/dm-core-0.9.7/lib/dm-core/type.rb +160 -0
  51. data/lib/gems/dm-core-0.9.7/lib/dm-core/type_map.rb +80 -0
  52. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/boolean.rb +7 -0
  53. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/discriminator.rb +34 -0
  54. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/object.rb +24 -0
  55. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_boolean.rb +34 -0
  56. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_datetime.rb +33 -0
  57. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/serial.rb +9 -0
  58. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/text.rb +10 -0
  59. data/lib/gems/dm-core-0.9.7/lib/dm-core/types.rb +19 -0
  60. data/lib/gems/dm-core-0.9.7/lib/dm-core/version.rb +3 -0
  61. data/lib/gems/dm-core-0.9.7/lib/dm-core.rb +217 -0
  62. data/lib/gems/dm-core-0.9.7/script/all +5 -0
  63. data/lib/gems/dm-core-0.9.7/script/performance.rb +284 -0
  64. data/lib/gems/dm-core-0.9.7/script/profile.rb +87 -0
  65. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations/version.rb +5 -0
  66. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations.rb +1 -0
  67. data/lib/gems/dm-migrations-0.9.7/lib/migration.rb +215 -0
  68. data/lib/gems/dm-migrations-0.9.7/lib/migration_runner.rb +88 -0
  69. data/lib/gems/dm-migrations-0.9.7/lib/spec/example/migration_example_group.rb +73 -0
  70. data/lib/gems/dm-migrations-0.9.7/lib/spec/matchers/migration_matchers.rb +107 -0
  71. data/lib/gems/dm-migrations-0.9.7/lib/sql/column.rb +9 -0
  72. data/lib/gems/dm-migrations-0.9.7/lib/sql/mysql.rb +52 -0
  73. data/lib/gems/dm-migrations-0.9.7/lib/sql/postgresql.rb +78 -0
  74. data/lib/gems/dm-migrations-0.9.7/lib/sql/sqlite3.rb +43 -0
  75. data/lib/gems/dm-migrations-0.9.7/lib/sql/table.rb +19 -0
  76. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_creator.rb +81 -0
  77. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_modifier.rb +53 -0
  78. data/lib/gems/dm-migrations-0.9.7/lib/sql.rb +10 -0
  79. data/lib/gems/dm-observer-0.9.7/lib/dm-observer/version.rb +5 -0
  80. data/lib/gems/dm-observer-0.9.7/lib/dm-observer.rb +91 -0
  81. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer/version.rb +5 -0
  82. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer.rb +183 -0
  83. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps/version.rb +5 -0
  84. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps.rb +57 -0
  85. data/lib/gems/dm-types-0.9.7/lib/dm-types/bcrypt_hash.rb +31 -0
  86. data/lib/gems/dm-types-0.9.7/lib/dm-types/csv.rb +28 -0
  87. data/lib/gems/dm-types-0.9.7/lib/dm-types/enum.rb +70 -0
  88. data/lib/gems/dm-types-0.9.7/lib/dm-types/epoch_time.rb +27 -0
  89. data/lib/gems/dm-types-0.9.7/lib/dm-types/file_path.rb +27 -0
  90. data/lib/gems/dm-types-0.9.7/lib/dm-types/flag.rb +61 -0
  91. data/lib/gems/dm-types-0.9.7/lib/dm-types/ip_address.rb +30 -0
  92. data/lib/gems/dm-types-0.9.7/lib/dm-types/json.rb +40 -0
  93. data/lib/gems/dm-types-0.9.7/lib/dm-types/regexp.rb +20 -0
  94. data/lib/gems/dm-types-0.9.7/lib/dm-types/serial.rb +8 -0
  95. data/lib/gems/dm-types-0.9.7/lib/dm-types/slug.rb +37 -0
  96. data/lib/gems/dm-types-0.9.7/lib/dm-types/uri.rb +29 -0
  97. data/lib/gems/dm-types-0.9.7/lib/dm-types/uuid.rb +64 -0
  98. data/lib/gems/dm-types-0.9.7/lib/dm-types/version.rb +5 -0
  99. data/lib/gems/dm-types-0.9.7/lib/dm-types/yaml.rb +36 -0
  100. data/lib/gems/dm-types-0.9.7/lib/dm-types.rb +28 -0
  101. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/absent_field_validator.rb +60 -0
  102. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/acceptance_validator.rb +76 -0
  103. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/auto_validate.rb +153 -0
  104. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/block_validator.rb +60 -0
  105. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/confirmation_validator.rb +80 -0
  106. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/contextual_validators.rb +56 -0
  107. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/custom_validator.rb +72 -0
  108. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/format_validator.rb +97 -0
  109. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/email.rb +40 -0
  110. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/url.rb +20 -0
  111. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/generic_validator.rb +100 -0
  112. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/length_validator.rb +113 -0
  113. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/method_validator.rb +68 -0
  114. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/numeric_validator.rb +83 -0
  115. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/primitive_validator.rb +60 -0
  116. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/required_field_validator.rb +88 -0
  117. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/support/object.rb +5 -0
  118. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/uniqueness_validator.rb +64 -0
  119. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/validation_errors.rb +63 -0
  120. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/version.rb +5 -0
  121. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/within_validator.rb +53 -0
  122. data/lib/gems/dm-validations-0.9.7/lib/dm-validations.rb +234 -0
  123. data/lib/gems/json_pure-1.1.3/GPL +340 -0
  124. data/lib/gems/json_pure-1.1.3/VERSION +1 -0
  125. data/lib/gems/json_pure-1.1.3/bin/edit_json.rb +10 -0
  126. data/lib/gems/json_pure-1.1.3/bin/prettify_json.rb +76 -0
  127. data/lib/gems/json_pure-1.1.3/lib/json/Array.xpm +21 -0
  128. data/lib/gems/json_pure-1.1.3/lib/json/FalseClass.xpm +21 -0
  129. data/lib/gems/json_pure-1.1.3/lib/json/Hash.xpm +21 -0
  130. data/lib/gems/json_pure-1.1.3/lib/json/Key.xpm +73 -0
  131. data/lib/gems/json_pure-1.1.3/lib/json/NilClass.xpm +21 -0
  132. data/lib/gems/json_pure-1.1.3/lib/json/Numeric.xpm +28 -0
  133. data/lib/gems/json_pure-1.1.3/lib/json/String.xpm +96 -0
  134. data/lib/gems/json_pure-1.1.3/lib/json/TrueClass.xpm +21 -0
  135. data/lib/gems/json_pure-1.1.3/lib/json/add/core.rb +135 -0
  136. data/lib/gems/json_pure-1.1.3/lib/json/add/rails.rb +58 -0
  137. data/lib/gems/json_pure-1.1.3/lib/json/common.rb +354 -0
  138. data/lib/gems/json_pure-1.1.3/lib/json/editor.rb +1362 -0
  139. data/lib/gems/json_pure-1.1.3/lib/json/ext.rb +13 -0
  140. data/lib/gems/json_pure-1.1.3/lib/json/json.xpm +1499 -0
  141. data/lib/gems/json_pure-1.1.3/lib/json/pure/generator.rb +394 -0
  142. data/lib/gems/json_pure-1.1.3/lib/json/pure/parser.rb +259 -0
  143. data/lib/gems/json_pure-1.1.3/lib/json/pure.rb +75 -0
  144. data/lib/gems/json_pure-1.1.3/lib/json/version.rb +9 -0
  145. data/lib/gems/json_pure-1.1.3/lib/json.rb +235 -0
  146. data/lib/gems/launchy-0.3.2/bin/launchy +12 -0
  147. data/lib/gems/launchy-0.3.2/lib/launchy/application.rb +163 -0
  148. data/lib/gems/launchy-0.3.2/lib/launchy/browser.rb +85 -0
  149. data/lib/gems/launchy-0.3.2/lib/launchy/command_line.rb +48 -0
  150. data/lib/gems/launchy-0.3.2/lib/launchy/gemspec.rb +53 -0
  151. data/lib/gems/launchy-0.3.2/lib/launchy/specification.rb +133 -0
  152. data/lib/gems/launchy-0.3.2/lib/launchy/version.rb +18 -0
  153. data/lib/gems/launchy-0.3.2/lib/launchy.rb +58 -0
  154. data/lib/gems/uuidtools-1.0.3/lib/uuidtools/version.rb +32 -0
  155. data/lib/gems/uuidtools-1.0.3/lib/uuidtools.rb +648 -0
  156. data/lib/gems.rb +13 -0
  157. data/lib/mack-data_mapper/migration_generator/migration_generator.rb +5 -0
  158. data/lib/mack-data_mapper/migration_generator/templates/db/migrations/%=@migration_name%.rb.template +1 -1
  159. data/lib/mack-data_mapper/model_generator/manifest.yml +3 -3
  160. data/lib/mack-data_mapper/model_generator/model_generator.rb +8 -1
  161. data/lib/mack-data_mapper/model_generator/templates/model.rb.template +1 -1
  162. data/lib/mack-data_mapper/model_generator/templates/rspec.rb.template +1 -1
  163. data/lib/mack-data_mapper/model_generator/templates/test_case.rb.template +1 -1
  164. data/lib/mack-data_mapper.rb +3 -2
  165. data/lib/mack-data_mapper_tasks.rb +7 -0
  166. metadata +235 -86
@@ -0,0 +1,663 @@
1
+ require 'date'
2
+ require 'time'
3
+ require 'bigdecimal'
4
+
5
+ module DataMapper
6
+
7
+ # :include:QUICKLINKS
8
+ #
9
+ # = Properties
10
+ # Properties for a model are not derived from a database structure, but
11
+ # instead explicitly declared inside your model class definitions. These
12
+ # properties then map (or, if using automigrate, generate) fields in your
13
+ # repository/database.
14
+ #
15
+ # If you are coming to DataMapper from another ORM framework, such as
16
+ # ActiveRecord, this is a fundamental difference in thinking. However, there
17
+ # are several advantages to defining your properties in your models:
18
+ #
19
+ # * information about your model is centralized in one place: rather than
20
+ # having to dig out migrations, xml or other configuration files.
21
+ # * having information centralized in your models, encourages you and the
22
+ # developers on your team to take a model-centric view of development.
23
+ # * it provides the ability to use Ruby's access control functions.
24
+ # * and, because DataMapper only cares about properties explicitly defined in
25
+ # your models, DataMapper plays well with legacy databases, and shares
26
+ # databases easily with other applications.
27
+ #
28
+ # == Declaring Properties
29
+ # Inside your class, you call the property method for each property you want
30
+ # to add. The only two required arguments are the name and type, everything
31
+ # else is optional.
32
+ #
33
+ # class Post
34
+ # include DataMapper::Resource
35
+ # property :title, String, :nullable => false
36
+ # # Cannot be null
37
+ # property :publish, TrueClass, :default => false
38
+ # # Default value for new records is false
39
+ # end
40
+ #
41
+ # By default, DataMapper supports the following primitive types:
42
+ #
43
+ # * TrueClass, Boolean
44
+ # * String
45
+ # * Text (limit of 65k characters by default)
46
+ # * Float
47
+ # * Integer
48
+ # * BigDecimal
49
+ # * DateTime
50
+ # * Date
51
+ # * Time
52
+ # * Object (marshalled out during serialization)
53
+ # * Class (datastore primitive is the same as String. Used for Inheritance)
54
+ #
55
+ # For more information about available Types, see DataMapper::Type
56
+ #
57
+ # == Limiting Access
58
+ # Property access control is uses the same terminology Ruby does. Properties
59
+ # are public by default, but can also be declared private or protected as
60
+ # needed (via the :accessor option).
61
+ #
62
+ # class Post
63
+ # include DataMapper::Resource
64
+ # property :title, String, :accessor => :private
65
+ # # Both reader and writer are private
66
+ # property :body, Text, :accessor => :protected
67
+ # # Both reader and writer are protected
68
+ # end
69
+ #
70
+ # Access control is also analogous to Ruby accessors and mutators, and can
71
+ # be declared using :reader and :writer, in addition to :accessor.
72
+ #
73
+ # class Post
74
+ # include DataMapper::Resource
75
+ #
76
+ # property :title, String, :writer => :private
77
+ # # Only writer is private
78
+ #
79
+ # property :tags, String, :reader => :protected
80
+ # # Only reader is protected
81
+ # end
82
+ #
83
+ # == Overriding Accessors
84
+ # The accessor for any property can be overridden in the same manner that Ruby
85
+ # class accessors can be. After the property is defined, just add your custom
86
+ # accessor:
87
+ #
88
+ # class Post
89
+ # include DataMapper::Resource
90
+ # property :title, String
91
+ #
92
+ # def title=(new_title)
93
+ # raise ArgumentError if new_title != 'Luke is Awesome'
94
+ # @title = new_title
95
+ # end
96
+ # end
97
+ #
98
+ # == Lazy Loading
99
+ # By default, some properties are not loaded when an object is fetched in
100
+ # DataMapper. These lazily loaded properties are fetched on demand when their
101
+ # accessor is called for the first time (as it is often unnecessary to
102
+ # instantiate -every- property -every- time an object is loaded). For
103
+ # instance, DataMapper::Types::Text fields are lazy loading by default,
104
+ # although you can over-ride this behavior if you wish:
105
+ #
106
+ # Example:
107
+ #
108
+ # class Post
109
+ # include DataMapper::Resource
110
+ # property :title, String # Loads normally
111
+ # property :body, DataMapper::Types::Text # Is lazily loaded by default
112
+ # end
113
+ #
114
+ # If you want to over-ride the lazy loading on any field you can set it to a
115
+ # context or false to disable it with the :lazy option. Contexts allow
116
+ # multipule lazy properties to be loaded at one time. If you set :lazy to
117
+ # true, it is placed in the :default context
118
+ #
119
+ # class Post
120
+ # include DataMapper::Resource
121
+ #
122
+ # property :title, String
123
+ # # Loads normally
124
+ #
125
+ # property :body, DataMapper::Types::Text, :lazy => false
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
133
+ # end
134
+ #
135
+ # Delaying the request for lazy-loaded attributes even applies to objects
136
+ # accessed through associations. In a sense, DataMapper anticipates that
137
+ # you will likely be iterating over objects in associations and rolls all
138
+ # of the load commands for lazy-loaded properties into one request from
139
+ # the database.
140
+ #
141
+ # Example:
142
+ #
143
+ # Widget[1].components
144
+ # # loads when the post object is pulled from database, by default
145
+ #
146
+ # Widget[1].components.first.body
147
+ # # loads the values for the body property on all objects in the
148
+ # # association, rather than just this one.
149
+ #
150
+ # Widget[1].components.first.comment
151
+ # # loads both comment and author for all objects in the association
152
+ # # since they are both in the :detailed context
153
+ #
154
+ # == Keys
155
+ # Properties can be declared as primary or natural keys on a table.
156
+ # You should a property as the primary key of the table:
157
+ #
158
+ # Examples:
159
+ #
160
+ # property :id, Serial # auto-incrementing key
161
+ # property :legacy_pk, String, :key => true # 'natural' key
162
+ #
163
+ # This is roughly equivalent to ActiveRecord's <tt>set_primary_key</tt>,
164
+ # though non-integer data types may be used, thus DataMapper supports natural
165
+ # keys. When a property is declared as a natural key, accessing the object
166
+ # using the indexer syntax <tt>Class[key]</tt> remains valid.
167
+ #
168
+ # User[1]
169
+ # # when :id is the primary key on the users table
170
+ # User['bill']
171
+ # # when :name is the primary (natural) key on the users table
172
+ #
173
+ # == Indeces
174
+ # You can add indeces for your properties by using the <tt>:index</tt>
175
+ # option. If you use <tt>true</tt> as the option value, the index will be
176
+ # automatically named. If you want to name the index yourself, use a symbol
177
+ # as the value.
178
+ #
179
+ # property :last_name, String, :index => true
180
+ # property :first_name, String, :index => :name
181
+ #
182
+ # You can create multi-column composite indeces by using the same symbol in
183
+ # all the columns belonging to the index. The columns will appear in the
184
+ # index in the order they are declared.
185
+ #
186
+ # property :last_name, String, :index => :name
187
+ # property :first_name, String, :index => :name
188
+ # # => index on (last_name, first_name)
189
+ #
190
+ # If you want to make the indeces unique, use <tt>:unique_index</tt> instead
191
+ # of <tt>:index</tt>
192
+ #
193
+ # == Inferred Validations
194
+ # If you require the dm-validations plugin, auto-validations will
195
+ # automatically be mixed-in in to your model classes:
196
+ # validation rules that are inferred when properties are declared with
197
+ # specific column restrictions.
198
+ #
199
+ # class Post
200
+ # include DataMapper::Resource
201
+ #
202
+ # property :title, String, :length => 250
203
+ # # => infers 'validates_length :title,
204
+ # :minimum => 0, :maximum => 250'
205
+ #
206
+ # property :title, String, :nullable => false
207
+ # # => infers 'validates_present :title
208
+ #
209
+ # property :email, String, :format => :email_address
210
+ # # => infers 'validates_format :email, :with => :email_address
211
+ #
212
+ # property :title, String, :length => 255, :nullable => false
213
+ # # => infers both 'validates_length' as well as
214
+ # # 'validates_present'
215
+ # # better: property :title, String, :length => 1..255
216
+ #
217
+ # end
218
+ #
219
+ # This functionality is available with the dm-validations gem, part of the
220
+ # dm-more bundle. For more information about validations, check the
221
+ # documentation for dm-validations.
222
+ #
223
+ # == Default Values
224
+ # To set a default for a property, use the <tt>:default</tt> key. The
225
+ # property will be set to the value associated with that key the first time
226
+ # it is accessed, or when the resource is saved if it hasn't been set with
227
+ # another value already. This value can be a static value, such as 'hello'
228
+ # but it can also be a proc that will be evaluated when the property is read
229
+ # before its value has been set. The property is set to the return of the
230
+ # proc. The proc is passed two values, the resource the property is being set
231
+ # for and the property itself.
232
+ #
233
+ # property :display_name, String, :default => { |r, p| r.login }
234
+ #
235
+ # Word of warning. Don't try to read the value of the property you're setting
236
+ # the default for in the proc. An infinite loop will ensue.
237
+ #
238
+ # == Embedded Values
239
+ # As an alternative to extraneous has_one relationships, consider using an
240
+ # EmbeddedValue.
241
+ #
242
+ # == Misc. Notes
243
+ # * Properties declared as strings will default to a length of 50, rather than
244
+ # 255 (typical max varchar column size). To overload the default, pass
245
+ # <tt>:length => 255</tt> or <tt>:length => 0..255</tt>. Since DataMapper
246
+ # does not introspect for properties, this means that legacy database tables
247
+ # may need their <tt>String</tt> columns defined with a <tt>:length</tt> so
248
+ # that DM does not apply an un-needed length validation, or allow overflow.
249
+ # * You may declare a Property with the data-type of <tt>Class</tt>.
250
+ # see SingleTableInheritance for more on how to use <tt>Class</tt> columns.
251
+ class Property
252
+ include Assertions
253
+
254
+ # NOTE: check is only for psql, so maybe the postgres adapter should
255
+ # define its own property options. currently it will produce a warning tho
256
+ # since PROPERTY_OPTIONS is a constant
257
+ #
258
+ # NOTE: PLEASE update PROPERTY_OPTIONS in DataMapper::Type when updating
259
+ # them here
260
+ PROPERTY_OPTIONS = [
261
+ :accessor, :reader, :writer,
262
+ :lazy, :default, :nullable, :key, :serial, :field, :size, :length,
263
+ :format, :index, :unique_index, :check, :ordinal, :auto_validation,
264
+ :validates, :unique, :track, :precision, :scale
265
+ ]
266
+
267
+ # FIXME: can we pull the keys from
268
+ # DataMapper::Adapters::DataObjectsAdapter::TYPES
269
+ # for this?
270
+ TYPES = [
271
+ TrueClass,
272
+ String,
273
+ DataMapper::Types::Text,
274
+ Float,
275
+ Integer,
276
+ BigDecimal,
277
+ DateTime,
278
+ Date,
279
+ Time,
280
+ Object,
281
+ Class,
282
+ DataMapper::Types::Discriminator,
283
+ DataMapper::Types::Serial
284
+ ]
285
+
286
+ IMMUTABLE_TYPES = [ TrueClass, Float, Integer, BigDecimal]
287
+
288
+ VISIBILITY_OPTIONS = [ :public, :protected, :private ]
289
+
290
+ DEFAULT_LENGTH = 50
291
+ DEFAULT_PRECISION = 10
292
+ DEFAULT_SCALE_BIGDECIMAL = 0
293
+ DEFAULT_SCALE_FLOAT = nil
294
+
295
+ attr_reader :primitive, :model, :name, :instance_variable_name,
296
+ :type, :reader_visibility, :writer_visibility, :getter, :options,
297
+ :default, :precision, :scale, :track, :extra_options
298
+
299
+ # Supplies the field in the data-store which the property corresponds to
300
+ #
301
+ # @return <String> name of field in data-store
302
+ # -
303
+ # @api semi-public
304
+ def field(repository_name = nil)
305
+ @field || @fields[repository_name] ||= self.model.field_naming_convention(repository_name).call(self)
306
+ end
307
+
308
+ def unique
309
+ @unique ||= @options.fetch(:unique, @serial || @key || false)
310
+ end
311
+
312
+ def hash
313
+ if @custom && !@bound
314
+ @type.bind(self)
315
+ @bound = true
316
+ end
317
+
318
+ return @model.hash + @name.hash
319
+ end
320
+
321
+ def eql?(o)
322
+ if o.is_a?(Property)
323
+ return o.model == @model && o.name == @name
324
+ else
325
+ return false
326
+ end
327
+ end
328
+
329
+ def length
330
+ @length.is_a?(Range) ? @length.max : @length
331
+ end
332
+ alias size length
333
+
334
+ def index
335
+ @index
336
+ end
337
+
338
+ def unique_index
339
+ @unique_index
340
+ end
341
+
342
+ # Returns whether or not the property is to be lazy-loaded
343
+ #
344
+ # @return <TrueClass, FalseClass> whether or not the property is to be
345
+ # lazy-loaded
346
+ # -
347
+ # @api public
348
+ def lazy?
349
+ @lazy
350
+ end
351
+
352
+ # Returns whether or not the property is a key or a part of a key
353
+ #
354
+ # @return <TrueClass, FalseClass> whether the property is a key or a part of
355
+ # a key
356
+ #-
357
+ # @api public
358
+ def key?
359
+ @key
360
+ end
361
+
362
+ # Returns whether or not the property is "serial" (auto-incrementing)
363
+ #
364
+ # @return <TrueClass, FalseClass> whether or not the property is "serial"
365
+ #-
366
+ # @api public
367
+ def serial?
368
+ @serial
369
+ end
370
+
371
+ # Returns whether or not the property can accept 'nil' as it's value
372
+ #
373
+ # @return <TrueClass, FalseClass> whether or not the property can accept 'nil'
374
+ #-
375
+ # @api public
376
+ def nullable?
377
+ @nullable
378
+ end
379
+
380
+ def custom?
381
+ @custom
382
+ end
383
+
384
+ # Provides a standardized getter method for the property
385
+ #
386
+ # @raise <ArgumentError> "+resource+ should be a DataMapper::Resource, but was ...."
387
+ #-
388
+ # @api private
389
+ def get(resource)
390
+ lazy_load(resource)
391
+
392
+ value = get!(resource)
393
+
394
+ set_original_value(resource, value)
395
+
396
+ # [YK] Why did we previously care whether options[:default] is nil.
397
+ # The default value of nil will be applied either way
398
+ if value.nil? && resource.new_record? && !resource.attribute_loaded?(name)
399
+ value = default_for(resource)
400
+ set(resource, value)
401
+ end
402
+
403
+ value
404
+ end
405
+
406
+ def get!(resource)
407
+ resource.instance_variable_get(instance_variable_name)
408
+ end
409
+
410
+ def set_original_value(resource, val)
411
+ unless resource.original_values.key?(name)
412
+ val = val.try_dup
413
+ val = val.hash if track == :hash
414
+ resource.original_values[name] = val
415
+ end
416
+ end
417
+
418
+ # Provides a standardized setter method for the property
419
+ #
420
+ # @raise <ArgumentError> "+resource+ should be a DataMapper::Resource, but was ...."
421
+ #-
422
+ # @api private
423
+ def set(resource, value)
424
+ # [YK] We previously checked for new_record? here, but lazy loading
425
+ # is blocked anyway if we're in a new record by by
426
+ # Resource#reload_attributes. This may eventually be useful for
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)
431
+
432
+ new_value = typecast(value)
433
+ old_value = get!(resource)
434
+
435
+ set_original_value(resource, old_value)
436
+
437
+ set!(resource, new_value)
438
+ end
439
+
440
+ def set!(resource, value)
441
+ resource.instance_variable_set(instance_variable_name, value)
442
+ end
443
+
444
+ # Loads lazy columns when get or set is called.
445
+ #-
446
+ # @api private
447
+ def lazy_load(resource)
448
+ # It is faster to bail out at at a new_record? rather than to process
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)
456
+ end
457
+
458
+ # typecasts values into a primitive
459
+ #
460
+ # @return <TrueClass, String, Float, Integer, BigDecimal, DateTime, Date, Time
461
+ # Class> the primitive data-type, defaults to TrueClass
462
+ #-
463
+ # @api private
464
+ def typecast(value)
465
+ return type.typecast(value, self) if type.respond_to?(:typecast)
466
+ return value if value.kind_of?(primitive) || value.nil?
467
+ begin
468
+ if primitive == TrueClass then %w[ true 1 t ].include?(value.to_s.downcase)
469
+ elsif primitive == String then value.to_s
470
+ elsif primitive == Float then value.to_f
471
+ elsif primitive == Integer
472
+ # The simplest possible implementation, i.e. value.to_i, is not
473
+ # desirable because "junk".to_i gives "0". We want nil instead,
474
+ # because this makes it clear that the typecast failed.
475
+ #
476
+ # After benchmarking, we preferred the current implementation over
477
+ # these two alternatives:
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
499
+ value
500
+ end
501
+ end
502
+
503
+ def default_for(resource)
504
+ @default.respond_to?(:call) ? @default.call(resource, self) : @default
505
+ end
506
+
507
+ def value(val)
508
+ custom? ? self.type.dump(val, self) : val
509
+ end
510
+
511
+ def inspect
512
+ "#<Property:#{@model}:#{@name}>"
513
+ end
514
+
515
+ private
516
+
517
+ def initialize(model, name, type, options = {})
518
+ assert_kind_of 'model', model, Model
519
+ assert_kind_of 'name', name, Symbol
520
+ assert_kind_of 'type', type, Class
521
+
522
+ if Fixnum == type
523
+ # It was decided that Integer is a more expressively names class to
524
+ # use instead of Fixnum. Fixnum only represents smaller numbers,
525
+ # so there was some confusion over whether or not it would also
526
+ # work with Bignum too (it will). Any Integer, which includes
527
+ # Fixnum and Bignum, can be stored in this property.
528
+ warn "#{type} properties are deprecated. Please use Integer instead"
529
+ type = Integer
530
+ end
531
+
532
+ unless TYPES.include?(type) || (DataMapper::Type > type && TYPES.include?(type.primitive))
533
+ raise ArgumentError, "+type+ was #{type.inspect}, which is not a supported type: #{TYPES * ', '}", caller
534
+ end
535
+
536
+ @extra_options = {}
537
+ (options.keys - PROPERTY_OPTIONS).each do |key|
538
+ @extra_options[key] = options.delete(key)
539
+ end
540
+
541
+ @model = model
542
+ @name = name.to_s.sub(/\?$/, '').to_sym
543
+ @type = type
544
+ @custom = DataMapper::Type > @type
545
+ @options = @custom ? @type.options.merge(options) : options
546
+ @instance_variable_name = "@#{@name}"
547
+
548
+ # TODO: This default should move to a DataMapper::Types::Text
549
+ # Custom-Type and out of Property.
550
+ @primitive = @options.fetch(:primitive, @type.respond_to?(:primitive) ? @type.primitive : @type)
551
+
552
+ @getter = TrueClass == @primitive ? "#{@name}?".to_sym : @name
553
+ @field = @options.fetch(:field, nil)
554
+ @serial = @options.fetch(:serial, false)
555
+ @key = @options.fetch(:key, @serial || false)
556
+ @default = @options.fetch(:default, nil)
557
+ @nullable = @options.fetch(:nullable, @key == false)
558
+ @index = @options.fetch(:index, false)
559
+ @unique_index = @options.fetch(:unique_index, false)
560
+ @lazy = @options.fetch(:lazy, @type.respond_to?(:lazy) ? @type.lazy : false) && !@key
561
+ @fields = {}
562
+
563
+ @track = @options.fetch(:track) do
564
+ if @custom && @type.respond_to?(:track) && @type.track
565
+ @type.track
566
+ else
567
+ IMMUTABLE_TYPES.include?(@primitive) ? :set : :get
568
+ end
569
+ end
570
+
571
+ # assign attributes per-type
572
+ if String == @primitive || Class == @primitive
573
+ @length = @options.fetch(:length, @options.fetch(:size, DEFAULT_LENGTH))
574
+ elsif BigDecimal == @primitive || Float == @primitive
575
+ @precision = @options.fetch(:precision, DEFAULT_PRECISION)
576
+
577
+ default_scale = (Float == @primitive) ? DEFAULT_SCALE_FLOAT : DEFAULT_SCALE_BIGDECIMAL
578
+ @scale = @options.fetch(:scale, default_scale)
579
+ # @scale = @options.fetch(:scale, DEFAULT_SCALE_BIGDECIMAL)
580
+
581
+ unless @precision > 0
582
+ raise ArgumentError, "precision must be greater than 0, but was #{@precision.inspect}"
583
+ end
584
+
585
+ if (BigDecimal == @primitive) || (Float == @primitive && !@scale.nil?)
586
+ unless @scale >= 0
587
+ raise ArgumentError, "scale must be equal to or greater than 0, but was #{@scale.inspect}"
588
+ end
589
+
590
+ unless @precision >= @scale
591
+ raise ArgumentError, "precision must be equal to or greater than scale, but was #{@precision.inspect} and scale was #{@scale.inspect}"
592
+ end
593
+ end
594
+ end
595
+
596
+ determine_visibility
597
+
598
+ @model.auto_generate_validations(self) if @model.respond_to?(:auto_generate_validations)
599
+ @model.property_serialization_setup(self) if @model.respond_to?(:property_serialization_setup)
600
+ end
601
+
602
+ def determine_visibility # :nodoc:
603
+ @reader_visibility = @options[:reader] || @options[:accessor] || :public
604
+ @writer_visibility = @options[:writer] || @options[:accessor] || :public
605
+
606
+ unless VISIBILITY_OPTIONS.include?(@reader_visibility) && VISIBILITY_OPTIONS.include?(@writer_visibility)
607
+ raise ArgumentError, 'property visibility must be :public, :protected, or :private', caller(2)
608
+ end
609
+ end
610
+
611
+ # Typecasts an arbitrary value to a DateTime
612
+ def typecast_to_datetime(value)
613
+ case value
614
+ when Hash then typecast_hash_to_datetime(value)
615
+ else DateTime.parse(value.to_s)
616
+ end
617
+ end
618
+
619
+ # Typecasts an arbitrary value to a Date
620
+ def typecast_to_date(value)
621
+ case value
622
+ when Hash then typecast_hash_to_date(value)
623
+ else Date.parse(value.to_s)
624
+ end
625
+ end
626
+
627
+ # Typecasts an arbitrary value to a Time
628
+ def typecast_to_time(value)
629
+ case value
630
+ when Hash then typecast_hash_to_time(value)
631
+ else Time.parse(value.to_s)
632
+ end
633
+ end
634
+
635
+ def typecast_hash_to_datetime(hash)
636
+ args = extract_time_args_from_hash(hash, :year, :month, :day, :hour, :min, :sec)
637
+ DateTime.new(*args)
638
+ rescue ArgumentError => e
639
+ t = typecast_hash_to_time(hash)
640
+ DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec)
641
+ end
642
+
643
+ def typecast_hash_to_date(hash)
644
+ args = extract_time_args_from_hash(hash, :year, :month, :day)
645
+ Date.new(*args)
646
+ rescue ArgumentError
647
+ t = typecast_hash_to_time(hash)
648
+ Date.new(t.year, t.month, t.day)
649
+ end
650
+
651
+ def typecast_hash_to_time(hash)
652
+ args = extract_time_args_from_hash(hash, :year, :month, :day, :hour, :min, :sec)
653
+ Time.local(*args)
654
+ end
655
+
656
+ # Extracts the given args from the hash. If a value does not exist, it
657
+ # uses the value of Time.now
658
+ def extract_time_args_from_hash(hash, *args)
659
+ now = Time.now
660
+ args.map { |arg| hash[arg] || hash[arg.to_s] || now.send(arg) }
661
+ end
662
+ end # class Property
663
+ end # module DataMapper