gorillib 0.1.11 → 0.4.0pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -2
  3. data/.yardopts +9 -0
  4. data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
  5. data/Gemfile +21 -14
  6. data/Guardfile +19 -0
  7. data/{LICENSE.textile → LICENSE.md} +43 -29
  8. data/README.md +47 -52
  9. data/Rakefile +31 -30
  10. data/TODO.md +32 -0
  11. data/VERSION +1 -1
  12. data/examples/builder/ironfan.rb +133 -0
  13. data/examples/model/simple.rb +17 -0
  14. data/gorillib.gemspec +106 -86
  15. data/lib/alt/kernel/call_stack.rb +56 -0
  16. data/lib/gorillib/array/wrap.rb +53 -0
  17. data/lib/gorillib/base.rb +3 -3
  18. data/lib/gorillib/builder/field.rb +5 -0
  19. data/lib/gorillib/builder.rb +260 -0
  20. data/lib/gorillib/collection/has_collection.rb +31 -0
  21. data/lib/gorillib/collection.rb +129 -0
  22. data/lib/gorillib/configurable.rb +28 -0
  23. data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
  24. data/lib/gorillib/exception/confidence.rb +17 -0
  25. data/lib/gorillib/exception/raisers.rb +78 -0
  26. data/lib/gorillib/hash/mash.rb +202 -0
  27. data/lib/gorillib/hashlike/slice.rb +53 -19
  28. data/lib/gorillib/hashlike.rb +5 -3
  29. data/lib/gorillib/io/system_helpers.rb +30 -0
  30. data/lib/gorillib/logger/log.rb +18 -0
  31. data/lib/gorillib/metaprogramming/concern.rb +124 -0
  32. data/lib/gorillib/model/active_model_conversion.rb +68 -0
  33. data/lib/gorillib/model/active_model_naming.rb +87 -0
  34. data/lib/gorillib/model/active_model_shim.rb +33 -0
  35. data/lib/gorillib/model/base.rb +341 -0
  36. data/lib/gorillib/model/defaults.rb +71 -0
  37. data/lib/gorillib/model/errors.rb +14 -0
  38. data/lib/gorillib/model/factories.rb +372 -0
  39. data/lib/gorillib/model/field.rb +146 -0
  40. data/lib/gorillib/model/named_schema.rb +53 -0
  41. data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
  42. data/lib/gorillib/model/record_schema.rb +9 -0
  43. data/lib/gorillib/model/serialization.rb +23 -0
  44. data/lib/gorillib/model/validate.rb +22 -0
  45. data/lib/gorillib/model.rb +23 -0
  46. data/lib/gorillib/pathname.rb +78 -0
  47. data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
  48. data/lib/gorillib/some.rb +11 -9
  49. data/lib/gorillib/string/constantize.rb +21 -14
  50. data/lib/gorillib/string/inflections.rb +6 -76
  51. data/lib/gorillib/string/inflector.rb +192 -0
  52. data/lib/gorillib/string/simple_inflector.rb +267 -0
  53. data/lib/gorillib/type/extended.rb +52 -0
  54. data/lib/gorillib/utils/capture_output.rb +28 -0
  55. data/lib/gorillib/utils/console.rb +131 -0
  56. data/lib/gorillib/utils/nuke_constants.rb +9 -0
  57. data/lib/gorillib/utils/stub_module.rb +33 -0
  58. data/spec/examples/builder/ironfan_spec.rb +37 -0
  59. data/spec/extlib/hash_spec.rb +64 -0
  60. data/spec/extlib/mash_spec.rb +312 -0
  61. data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
  62. data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
  63. data/spec/gorillib/builder_spec.rb +187 -0
  64. data/spec/gorillib/collection_spec.rb +20 -0
  65. data/spec/gorillib/configurable_spec.rb +62 -0
  66. data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
  67. data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
  68. data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
  69. data/spec/gorillib/exception/raisers_spec.rb +60 -0
  70. data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
  71. data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
  72. data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
  73. data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
  74. data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
  75. data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
  76. data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
  77. data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
  78. data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
  79. data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
  80. data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
  81. data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
  82. data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
  83. data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
  84. data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
  85. data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
  86. data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
  87. data/spec/gorillib/model/record/defaults_spec.rb +108 -0
  88. data/spec/gorillib/model/record/factories_spec.rb +321 -0
  89. data/spec/gorillib/model/record/overlay_spec.rb +46 -0
  90. data/spec/gorillib/model/serialization_spec.rb +48 -0
  91. data/spec/gorillib/model_spec.rb +281 -0
  92. data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
  93. data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
  94. data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
  95. data/spec/{object → gorillib/object}/try_spec.rb +3 -2
  96. data/spec/gorillib/pathname_spec.rb +114 -0
  97. data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
  98. data/spec/{string → gorillib/string}/human_spec.rb +2 -2
  99. data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
  100. data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
  101. data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
  102. data/spec/gorillib/type/extended_spec.rb +120 -0
  103. data/spec/gorillib/utils/capture_output_spec.rb +71 -0
  104. data/spec/spec_helper.rb +8 -11
  105. data/spec/support/gorillib_test_helpers.rb +66 -0
  106. data/spec/support/hashlike_fuzzing_helper.rb +31 -33
  107. data/spec/support/hashlike_helper.rb +3 -3
  108. data/spec/support/model_test_helpers.rb +81 -0
  109. data/spec/support/shared_examples/included_module.rb +20 -0
  110. metadata +177 -158
  111. data/lib/gorillib/array/average.rb +0 -13
  112. data/lib/gorillib/array/sorted_median.rb +0 -11
  113. data/lib/gorillib/array/sorted_percentile.rb +0 -11
  114. data/lib/gorillib/array/sorted_sample.rb +0 -12
  115. data/lib/gorillib/dsl_object.rb +0 -64
  116. data/lib/gorillib/hash/indifferent_access.rb +0 -207
  117. data/lib/gorillib/hash/tree_merge.rb +0 -4
  118. data/lib/gorillib/hashlike/tree_merge.rb +0 -49
  119. data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
  120. data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
  121. data/lib/gorillib/receiver/active_model_shim.rb +0 -32
  122. data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
  123. data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
  124. data/lib/gorillib/receiver/locale/en.yml +0 -27
  125. data/lib/gorillib/receiver/tree_diff.rb +0 -74
  126. data/lib/gorillib/receiver/validations.rb +0 -30
  127. data/lib/gorillib/receiver.rb +0 -402
  128. data/lib/gorillib/receiver_model.rb +0 -21
  129. data/lib/gorillib/struct/acts_as_hash.rb +0 -108
  130. data/notes/fancy_hashes_and_receivers.textile +0 -120
  131. data/notes/hash_rdocs.textile +0 -97
  132. data/spec/array/average_spec.rb +0 -24
  133. data/spec/array/sorted_median_spec.rb +0 -18
  134. data/spec/array/sorted_percentile_spec.rb +0 -24
  135. data/spec/array/sorted_sample_spec.rb +0 -28
  136. data/spec/dsl_object_spec.rb +0 -99
  137. data/spec/hash/indifferent_access_spec.rb +0 -391
  138. data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
  139. data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
  140. data/spec/receiver/acts_as_hash_spec.rb +0 -295
  141. data/spec/receiver_spec.rb +0 -551
  142. data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
  143. data/spec/struct/acts_as_hash_spec.rb +0 -422
@@ -0,0 +1,341 @@
1
+
2
+ module Gorillib
3
+
4
+ # Provides a set of class methods for defining a field schema and instance
5
+ # methods for reading and writing attributes.
6
+ #
7
+ # @example Usage
8
+ # class Person
9
+ # include Gorillib::Model
10
+ #
11
+ # field :name, String, :doc => 'Full name of person'
12
+ # field :height, Float, :doc => 'Height in meters'
13
+ # end
14
+ #
15
+ # person = Person.new
16
+ # person.name = "Bob Dobbs, Jr"
17
+ # puts person #=> #<Person name="Bob Dobbs, Jr">
18
+ #
19
+ module Model
20
+ extend Gorillib::Concern
21
+
22
+ # Returns a Hash of all attributes
23
+ #
24
+ # @example Get attributes
25
+ # person.attributes # => { :name => "Ben Poweski" }
26
+ #
27
+ # @return [{Symbol => Object}] The Hash of all attributes
28
+ def attributes
29
+ self.class.field_names.inject(Hash.new) do |hsh, fn|
30
+ hsh[fn] = read_attribute(fn)
31
+ hsh
32
+ end
33
+ end
34
+
35
+ # Returns a Hash of all attributes *that have been set*
36
+ #
37
+ # @example Get attributes (smurfette is unarmed)
38
+ # smurfette.attributes # => { :name => "Smurfette", :weapon => nil }
39
+ # smurfette.compact_attributes # => { :name => "Smurfette" }
40
+ #
41
+ # @return [{Symbol => Object}] The Hash of all *set* attributes
42
+ def compact_attributes
43
+ self.class.field_names.inject(Hash.new) do |hsh, fn|
44
+ hsh[fn] = read_attribute(fn) if attribute_set?(fn)
45
+ hsh
46
+ end
47
+ end
48
+
49
+ #
50
+ # Accept the given attributes, converting each value to the appropriate
51
+ # type, constructing included models and collections, and other triggers as
52
+ # defined.
53
+ #
54
+ # Use `#receive!` to accept 'dirty' data -- from JSON, from a nested hash,
55
+ # or some such. Use `#update_attributes` if your data is already type safe.
56
+ #
57
+ # @param [{Symbol => Object}] hsh The values to receive
58
+ # @return [Gorillib::Model] the object itself
59
+ def receive!(hsh={})
60
+ if hsh.respond_to?(:attributes) then hsh = hsh.attributes ; end
61
+ Gorillib::Model::Validate.hashlike!("attributes hash for #{self.inspect}", hsh)
62
+ hsh = hsh.symbolize_keys
63
+ self.class.fields.each do |field_name, field|
64
+ next unless hsh.has_key?(field_name)
65
+ self.public_send(:"receive_#{field_name}", hsh[field_name])
66
+ end
67
+ handle_extra_attributes( hsh.reject{|field_name,val| self.class.has_field?(field_name) } )
68
+ self
69
+ end
70
+
71
+ def handle_extra_attributes(attrs)
72
+ @extra_attributes ||= Hash.new
73
+ @extra_attributes.merge!(attrs)
74
+ end
75
+
76
+ #
77
+ # Accept the given attributes, adopting each value directly.
78
+ #
79
+ # Use `#receive!` to accept 'dirty' data -- from JSON, from a nested hash,
80
+ # or some such. Use `#update_attributes` if your data is already type safe.
81
+ #
82
+ # @param [{Symbol => Object}] hsh The values to update with
83
+ # @return [Gorillib::Model] the object itself
84
+ def update_attributes(hsh)
85
+ if hsh.respond_to?(:attributes) then hsh = hsh.attributes ; end
86
+ Gorillib::Model::Validate.hashlike!("attributes hash", hsh)
87
+ self.class.fields.each do |attr, field|
88
+ if hsh.has_key?(attr) then val = hsh[attr]
89
+ elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s]
90
+ else next ; end
91
+ write_attribute(attr, val)
92
+ end
93
+ self
94
+ end
95
+
96
+ # Read a value from the model's attributes.
97
+ #
98
+ # @example Reading an attribute
99
+ # person.read_attribute(:name)
100
+ #
101
+ # @param [String, Symbol, #to_s] field_name Name of the attribute to get.
102
+ #
103
+ # @raise [UnknownAttributeError] if the attribute is unknown
104
+ # @return [Object] The value of the attribute, or nil if it is unset
105
+ def read_attribute(field_name)
106
+ check_field(field_name)
107
+ if instance_variable_defined?("@#{field_name}")
108
+ instance_variable_get("@#{field_name}")
109
+ else
110
+ read_unset_attribute(field_name)
111
+ end
112
+ end
113
+
114
+ # Write the value of a single attribute.
115
+ #
116
+ # @example Writing an attribute
117
+ # person.write_attribute(:name, "Benjamin")
118
+ #
119
+ # @param [String, Symbol, #to_s] field_name Name of the attribute to update.
120
+ # @param [Object] val The value to set for the attribute.
121
+ #
122
+ # @raise [UnknownAttributeError] if the attribute is unknown
123
+ # @return [Object] the attribute's value
124
+ def write_attribute(field_name, val)
125
+ check_field(field_name)
126
+ instance_variable_set("@#{field_name}", val)
127
+ end
128
+
129
+ # Unset an attribute. Subsequent reads of the attribute will return `nil`,
130
+ # and `attribute_set?` for that field will return false.
131
+ #
132
+ # @example Unsetting an attribute
133
+ # obj.write_attribute(:foo, nil)
134
+ # [ obj.read_attribute(:foo), obj.attribute_set?(:foo) ] # => [ nil, true ]
135
+ # person.unset_attribute(:height)
136
+ # [ obj.read_attribute(:foo), obj.attribute_set?(:foo) ] # => [ nil, false ]
137
+ #
138
+ # @param [String, Symbol, #to_s] field_name Name of the attribute to unset.
139
+ #
140
+ # @raise [UnknownAttributeError] if the attribute is unknown
141
+ # @return [Object] the former value if it was set, nil if it was unset
142
+ def unset_attribute(field_name)
143
+ check_field(field_name)
144
+ if instance_variable_defined?("@#{field_name}")
145
+ val = instance_variable_get("@#{field_name}")
146
+ remove_instance_variable("@#{field_name}")
147
+ return val
148
+ else
149
+ return nil
150
+ end
151
+ end
152
+
153
+ # True if the attribute is set.
154
+ #
155
+ # Note that an attribute can have the value nil but be set.
156
+ #
157
+ # @param [String, Symbol, #to_s] field_name Name of the attribute to check.
158
+ #
159
+ # @raise [UnknownAttributeError] if the attribute is unknown
160
+ # @return [true, false]
161
+ def attribute_set?(field_name)
162
+ check_field(field_name)
163
+ instance_variable_defined?("@#{field_name}")
164
+ end
165
+
166
+ # Two models are equal if they have the same class and their attributes
167
+ # are equal.
168
+ #
169
+ # @example Compare for equality.
170
+ # model == other
171
+ #
172
+ # @param [Gorillib::Model, Object] other The other model to compare
173
+ #
174
+ # @return [true, false] True if attributes are equal and other is instance of the same Class
175
+ def ==(other)
176
+ return false unless other.instance_of?(self.class)
177
+ attributes == other.attributes
178
+ end
179
+
180
+ # override inspect_helper (not this) in your descendant class
181
+ # @return [String] Human-readable presentation of the attributes
182
+ def inspect(detailed=true)
183
+ inspect_helper(detailed, compact_attributes)
184
+ end
185
+
186
+ # assembles just the given attributes into the inspect string.
187
+ # @return [String] Human-readable presentation of the attributes
188
+ def inspect_helper(detailed, attrs)
189
+ str = "#<" << self.class.name.to_s
190
+ if detailed && attrs.present?
191
+ str << " "
192
+ str << attrs.map do |attr, val|
193
+ "#{attr}=#{val.is_a?(Gorillib::Model) || val.is_a?(Gorillib::Collection) ? val.inspect(false) : val.inspect}"
194
+ end.join(", ")
195
+ end
196
+ str << ">"
197
+ end
198
+ private :inspect_helper
199
+
200
+ protected
201
+
202
+ # @return [true] if the field exists
203
+ # @raise [UnknownFieldError] if the field is missing
204
+ def check_field(field_name)
205
+ return true if self.class.has_field?(field_name)
206
+ raise UnknownFieldError, "unknown field: #{field_name} for #{self}"
207
+ end
208
+
209
+ module ClassMethods
210
+
211
+ def typename
212
+ Gorillib::Inflector.underscore(self.name).gsub(%r{/}, '.')
213
+ end
214
+
215
+ #
216
+ # Receive external data, type-converting and creating contained models as necessary
217
+ #
218
+ # @return [Gorillib::Model] the new object
219
+ def receive(attrs={}, &block)
220
+ return nil if attrs.nil?
221
+ return attrs if attrs.is_a?(self)
222
+ Gorillib::Model::Validate.hashlike!("attributes for #{self}", attrs)
223
+ klass = attrs.has_key?(:_type) ? Gorillib::Factory(attrs[:_type]) : self
224
+ warn "factory #{self} doesn't match type specified in #{attrs}" unless klass <= self
225
+ obj = klass.new
226
+ obj.receive!(attrs, &block)
227
+ obj
228
+ end
229
+
230
+ # Defines a new field
231
+ #
232
+ # For each field that is defined, a getter and setter will be added as
233
+ # an instance method to the model. An Field instance will be added to
234
+ # result of the fields class method.
235
+ #
236
+ # @example
237
+ # field :height, Integer
238
+ #
239
+ # @param [Symbol] field_name The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]`
240
+ # @param [Class] type The field's type (required)
241
+ # @option options [String] doc Documentation string for the field (optional)
242
+ # @option options [Proc, Object] default Default value, or proc that instance can evaluate to find default value
243
+ #
244
+ # @return Gorillib::Model::Field
245
+ def field(field_name, type, options={})
246
+ options = options.symbolize_keys
247
+ field_type = options.delete(:field_type){ ::Gorillib::Model::Field }
248
+ fld = field_type.new(field_name, type, self, options)
249
+ @_own_fields[fld.name] = fld
250
+ _reset_descendant_fields
251
+ fld.send(:inscribe_methods, self)
252
+ fld
253
+ end
254
+
255
+ # @return [{Symbol => Gorillib::Model::Field}]
256
+ def fields
257
+ return @_fields if defined?(@_fields)
258
+ @_fields = ancestors.reverse.inject({}){|acc, ancestor| acc.merge!(ancestor.try(:_own_fields) || {}) }
259
+ end
260
+
261
+ # @return [true, false] true if the field is defined on this class
262
+ def has_field?(field_name)
263
+ fields.has_key?(field_name.to_sym)
264
+ end
265
+
266
+ # @return [Array<Symbol>] The attribute names
267
+ def field_names
268
+ fields.keys
269
+ end
270
+
271
+ # @return Class name and its attributes
272
+ #
273
+ # @example Inspect the model's definition.
274
+ # Person.inspect #=> Person[first_name, last_name]
275
+ def inspect
276
+ "#{self.name || 'anon'}[#{ field_names.join(", ") }]"
277
+ end
278
+
279
+ protected
280
+
281
+ attr_reader :_own_fields
282
+
283
+ # Ensure that classes inherit all their parents' fields, even if fields
284
+ # are added after the child class is defined.
285
+ def _reset_descendant_fields
286
+ ObjectSpace.each_object(::Class) do |klass|
287
+ klass.__send__(:remove_instance_variable, '@_fields') if klass <= self && klass.instance_variable_defined?('@_fields')
288
+ end
289
+ end
290
+
291
+ # define the reader method `#foo` for a field named `:foo`
292
+ def define_attribute_reader(field_name, field_type, visibility)
293
+ define_meta_module_method(field_name, visibility) do
294
+ begin
295
+ read_attribute(field_name)
296
+ rescue StandardError => err ; err.polish("#{self.class}.#{field_name}") rescue nil ; raise ; end
297
+ end
298
+ end
299
+
300
+ # define the writer method `#foo=` for a field named `:foo`
301
+ def define_attribute_writer(field_name, field_type, visibility)
302
+ define_meta_module_method("#{field_name}=", visibility) do |val|
303
+ write_attribute(field_name, val)
304
+ end
305
+ end
306
+
307
+ # define the present method `#foo?` for a field named `:foo`
308
+ def define_attribute_tester(field_name, field_type, visibility)
309
+ define_meta_module_method("#{field_name}?", visibility) do
310
+ attribute_set?(field_name)
311
+ end
312
+ end
313
+
314
+ def define_attribute_receiver(field_name, field_type, visibility)
315
+ define_meta_module_method("receive_#{field_name}", visibility) do |val|
316
+ begin
317
+ val = field_type.receive(val)
318
+ write_attribute(field_name, val)
319
+ self
320
+ rescue StandardError => err ; err.polish("#{self.class}.#{field_name} type #{type} on #{val}") rescue nil ; raise ; end
321
+ end
322
+ end
323
+
324
+ def inherited(base)
325
+ base.instance_eval do
326
+ @_own_fields ||= {}
327
+ end
328
+ super
329
+ end
330
+ end
331
+
332
+ self.included do |base|
333
+ base.instance_eval do
334
+ extend Gorillib::Model::NamedSchema
335
+ extend Gorillib::Model::ClassMethods
336
+ @_own_fields ||= {}
337
+ end
338
+ end
339
+
340
+ end
341
+ end
@@ -0,0 +1,71 @@
1
+ module Gorillib
2
+ module Model
3
+
4
+ Field.class_eval do
5
+ field :default, :whatever
6
+
7
+ # @return [true, false] true if the field has a default value/proc set
8
+ def has_default?
9
+ attribute_set?(:default)
10
+ end
11
+ end
12
+
13
+ # This is called by `read_attribute` if an attribute is unset; you should
14
+ # not call this directly. You might use this to provide defaults, or lazy
15
+ # access, or layered resolution.
16
+ #
17
+ # @param [String, Symbol, #to_s] field_name Name of the attribute to unset.
18
+ # @return [nil] Ze goggles! Zey do nussing!
19
+ def read_unset_attribute(field_name)
20
+ field = self.class.fields[field_name]
21
+ return unless field.has_default?
22
+ write_attribute(field.name, attribute_default(field))
23
+ end
24
+
25
+ # FieldDefaults allows defaults to be declared for your fields
26
+ #
27
+ # Defaults are declared by passing the :default option to the field
28
+ # class method. If you need the default to be dynamic, pass a lambda, Proc,
29
+ # or any object that responds to #call as the value to the :default option
30
+ # and the result will calculated on initialization. These dynamic defaults
31
+ # can depend on the values of other fields.
32
+ #
33
+ # @example Usage
34
+ # class Person
35
+ # field :first_name, String, :default => "John"
36
+ # field :last_name, String, :default => "Doe"
37
+ # end
38
+ #
39
+ # person = Person.new
40
+ # person.first_name #=> "John"
41
+ # person.last_name #=> "Doe"
42
+ #
43
+ # @example Dynamic Default
44
+ # class Event
45
+ # field :start_date, Date
46
+ # field :end_date, Date, :default => ->{ start_date }
47
+ # end
48
+ #
49
+ # event = Event.receive(:start_date => "2012-01-01")
50
+ # event.end_date.to_s #=> "2012-01-01"
51
+ #
52
+
53
+ protected
54
+
55
+ # the actual default value to assign to the attribute
56
+ def attribute_default(field)
57
+ return unless field.has_default?
58
+ val = field.default
59
+ case
60
+ when (val.is_a?(Proc) || val.is_a?(UnboundMethod)) && (val.arity == 0)
61
+ self.instance_exec(&val)
62
+ when val.respond_to?(:call)
63
+ val.call(self, field.name)
64
+ else
65
+ val.try_dup
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,14 @@
1
+ module Gorillib
2
+ module Model
3
+
4
+ # All exceptions defined by Gorillib::Model include this module.
5
+ module Error
6
+ end
7
+
8
+ # Exception raised if attempting to assign unknown fields
9
+ class UnknownFieldError < ::NoMethodError
10
+ include Gorillib::Model::Error
11
+ end
12
+
13
+ end
14
+ end