gorillib 0.1.11 → 0.4.0pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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