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,372 @@
1
+ require 'pathname'
2
+ require 'gorillib/type/extended'
3
+
4
+ def Gorillib::Factory(*args)
5
+ ::Gorillib::Factory.receive(*args)
6
+ end
7
+
8
+ module Gorillib
9
+
10
+ module Factory
11
+ class FactoryMismatchError < ArgumentError ; end
12
+
13
+ def self.receive(type)
14
+ case
15
+ when type.is_a?(Proc) || type.is_a?(Method) then return Gorillib::Factory::ApplyProcFactory.new(type)
16
+ when type.respond_to?(:receive) then return type
17
+ when factories.include?(type) then return factories[type]
18
+ when type.is_a?(String) then
19
+ return Gorillib::Inflector.constantize(Gorillib::Inflector.camelize(type.gsub(/\./, '/')))
20
+ else raise ArgumentError, "Don't know which factory makes a #{type}"
21
+ end
22
+ end
23
+
24
+ private
25
+ def self.factories
26
+ @factories ||= Gorillib::Collection.new.tap{|f| f.key_method = :name }
27
+ end
28
+ public
29
+
30
+ def self.register_factory(factory, *typenames)
31
+ if typenames.blank?
32
+ typenames = [factory.typename, factory.product]
33
+ end
34
+ typenames.each{|typename| factories[typename] = factory }
35
+ end
36
+
37
+ class BaseFactory
38
+ # [Class] The type of objects produced by this factory
39
+ class_attribute :product
40
+
41
+ # [Array<Symbol>] methods that can be redefined by passing a block to an
42
+ # instance No not add to your superclass' value in-place; instead, use
43
+ # `self.redefinable_methods += [...]`.
44
+ # @see #redefine
45
+ class_attribute :redefinable_methods, :instance_writer => false
46
+ self.redefinable_methods = Set.new([:blankish?, :convert])
47
+
48
+ # [Array<Object>] objects considered to be equivalent to `nil`
49
+ class_attribute :blankish_vals
50
+ self.blankish_vals = Set.new([ nil, "" ]) # note: [] {} and false are NOT blankish by default
51
+
52
+ def initialize(options={})
53
+ @product = options.delete(:product) if options.has_key?(:product)
54
+ @blankish_vals = options.delete(:blankish_vals) if options.has_key?(:blankish_vals)
55
+ options.extract!(*redefinable_methods).each do |meth, value_or_block|
56
+ redefine(meth, value_or_block)
57
+ end
58
+ warn "Unknown options #{options.keys}" unless options.empty?
59
+ end
60
+
61
+ def self.typename
62
+ Gorillib::Inflector.underscore(product.name).to_sym
63
+ end
64
+ def typename ; self.class.typename ; end
65
+
66
+ def self.receive(*args, &block)
67
+ self.new.receive(*args, &block)
68
+ end
69
+
70
+ # A `native` object does not need any transformation; it is accepted directly.
71
+ # By default, an object is native if it `is_a?(product)`
72
+ #
73
+ # @param [Object] obj the object to convert and receive
74
+ # @return [true, false] true if the item does not need conversion
75
+ def native?(obj)
76
+ obj.is_a?(product)
77
+ end
78
+ def self.native?(obj) self.new.native?(obj) ; end
79
+
80
+ # A `blankish` object should be converted to `nil`, not a value
81
+ #
82
+ # @param [Object] obj the object to convert and receive
83
+ # @return [true, false] true if the item is equivalent to a nil value
84
+ def blankish?(obj)
85
+ blankish_vals.include?(obj)
86
+ end
87
+ def self.blankish?(obj) self.new.blankish?(obj) ; end
88
+
89
+ def redefine(meth, *args, &block)
90
+ raise ArgumentError, "Cannot redefine #{meth} -- only #{redefinable_methods.inspect} are redefinable" unless redefinable_methods.include?(meth)
91
+ if args.present?
92
+ val = args.first
93
+ case
94
+ when block_given? then raise ArgumentError, "Pass a block or a value, not both"
95
+ when val.is_a?(Proc) || val.is_a?(Method) then block = val
96
+ else block = ->(*){ val.try_dup }
97
+ end
98
+ end
99
+ define_singleton_method(meth, &block)
100
+ self
101
+ end
102
+
103
+ protected
104
+
105
+ # Raises a FactoryMismatchError.
106
+ def mismatched!(obj, message=nil, *args)
107
+ message ||= "item cannot be converted to #{product}"
108
+ message << (" got #{obj.inspect}" rescue ' (and is uninspectable)')
109
+ raise FactoryMismatchError, message, *args
110
+ end
111
+
112
+ def self.register_factory!(*args)
113
+ Gorillib::Factory.register_factory(self, *args)
114
+ end
115
+ end
116
+
117
+ class ConvertingFactory < BaseFactory
118
+ def receive(obj)
119
+ return nil if blankish?(obj)
120
+ return obj if native?(obj)
121
+ convert(obj)
122
+ rescue NoMethodError, TypeError, RangeError => err
123
+ mismatched!(obj, err.message, err.backtrace)
124
+ end
125
+ protected
126
+ # Convert a receivable object to the factory's product type. This method
127
+ # should convert an object to `native?` form or die trying; any
128
+ # polymorphism (such as converting an empty string to nil) happens in
129
+ # other methods called by `receive`.
130
+ #
131
+ # @param [Object] obj the object to convert.
132
+ def convert(obj)
133
+ obj.dup
134
+ end
135
+ end
136
+
137
+ #
138
+ # A NonConvertingFactory accepts objects that are *already* native, and
139
+ # throws a mismatch error for anything else.
140
+ #
141
+ # @example
142
+ # ff = Gorillib::Factory::NonConvertingFactory.new(:product => String, :blankish_vals => [nil])
143
+ # ff.receive(nil) #=> nil
144
+ # ff.receive("bob") #=> "bob"
145
+ # ff.receive(:bob) #=> Gorillib::Factory::FactoryMismatchError: must be an instance of String, got 3
146
+ #
147
+ class NonConvertingFactory < BaseFactory
148
+ self.blankish_vals = [nil]
149
+
150
+ def receive(obj)
151
+ return nil if blankish?(obj)
152
+ return obj if native?(obj)
153
+ mismatched!(obj, "must be an instance of #{product},")
154
+ rescue NoMethodError => err
155
+ mismatched!(obj, err.message, err.backtrace)
156
+ end
157
+ end
158
+
159
+ class IdenticalFactory < BaseFactory
160
+ self.redefinable_methods = []
161
+ self.blankish_vals = []
162
+ def native?(obj) true ; end
163
+ def blankish?(obj) false ; end
164
+ def receive(obj)
165
+ obj
166
+ end
167
+ register_factory!(:identical, :whatever)
168
+ end
169
+ ::Whatever = IdenticalFactory unless defined?(Whatever)
170
+
171
+ # __________________________________________________________________________
172
+ #
173
+ # Concrete Factories
174
+ # __________________________________________________________________________
175
+
176
+ class StringFactory < ConvertingFactory
177
+ self.product = String
178
+ self.blankish_vals -= [""]
179
+ def native?(obj) obj.respond_to?(:to_str) end
180
+ def convert(obj) obj.to_s end
181
+ register_factory!
182
+ end
183
+
184
+ class GuidFactory < StringFactory ; self.product = ::Guid ; register_factory! ; end
185
+ class HostnameFactory < StringFactory ; self.product = ::Hostname ; register_factory! ; end
186
+ class IpAddressFactory < StringFactory ; self.product = ::IpAddress ; register_factory! ; end
187
+
188
+ class BinaryFactory < StringFactory
189
+ def convert(obj)
190
+ super.force_encoding("BINARY")
191
+ end
192
+ register_factory!(:binary)
193
+ end
194
+
195
+ class PathnameFactory < ConvertingFactory
196
+ self.product = ::Pathname
197
+ def convert(obj) Pathname.new(obj) end
198
+ register_factory!
199
+ end
200
+
201
+ class SymbolFactory < ConvertingFactory
202
+ self.product = Symbol
203
+ def convert(obj) obj.to_sym end
204
+ register_factory!
205
+ end
206
+
207
+ class RegexpFactory < ConvertingFactory
208
+ self.product = Regexp
209
+ def convert(obj) Regexp.new(obj) end
210
+ register_factory!
211
+ end
212
+
213
+ class IntegerFactory < ConvertingFactory
214
+ self.product = Integer
215
+ def convert(obj) obj.to_i end
216
+ register_factory!(:int, :integer, Integer)
217
+ end
218
+ class BignumFactory < IntegerFactory
219
+ self.product = Bignum
220
+ register_factory!
221
+ end
222
+ class FloatFactory < ConvertingFactory
223
+ self.product = Float
224
+ def convert(obj) obj.to_f end
225
+ register_factory!
226
+ end
227
+ class ComplexFactory < ConvertingFactory
228
+ self.product = Complex
229
+ def convert(obj) obj.to_c end
230
+ register_factory!
231
+ end
232
+ class RationalFactory < ConvertingFactory
233
+ self.product = Rational
234
+ def convert(obj) obj.to_r end
235
+ register_factory!
236
+ end
237
+
238
+ class TimeFactory < ConvertingFactory
239
+ self.product = Time
240
+ def convert(obj)
241
+ case obj
242
+ when String
243
+ Time.parse(obj).utc
244
+ when Numeric
245
+ Time.at(obj).utc
246
+ end
247
+ rescue ArgumentError => err
248
+ warn "Cannot parse time #{obj}: #{err}"
249
+ return nil
250
+ end
251
+ register_factory!
252
+ end
253
+
254
+ # __________________________________________________________________________
255
+
256
+ class ClassFactory < NonConvertingFactory ; self.product = Class ; register_factory! ; end
257
+ class ModuleFactory < NonConvertingFactory ; self.product = Module ; register_factory! ; end
258
+ class TrueFactory < NonConvertingFactory ; self.product = TrueClass ; register_factory!(:true, TrueClass) ; end
259
+ class FalseFactory < NonConvertingFactory ; self.product = FalseClass ; register_factory!(:false, FalseClass) ; end
260
+
261
+ class ExceptionFactory < NonConvertingFactory ; self.product = Exception ; register_factory!(:exception, Exception) ; end
262
+
263
+ class NilFactory < NonConvertingFactory
264
+ self.product = NilClass
265
+ self.blankish_vals = []
266
+ register_factory!(:nil, NilClass)
267
+ end
268
+
269
+ class BooleanFactory < ConvertingFactory
270
+ self.product = [TrueClass, FalseClass]
271
+ self.blankish_vals = [nil]
272
+ def native?(obj) obj.equal?(true) || obj.equal?(false) ; end
273
+ def convert(obj) (obj.to_s == "false") ? false : true ; end
274
+ register_factory!(:boolean)
275
+ def self.typename() :boolean ; end
276
+ end
277
+
278
+ #
279
+ #
280
+ #
281
+
282
+ class EnumerableFactory < ConvertingFactory
283
+ # [#receive] factory for converting items
284
+ attr_reader :items_factory
285
+ self.redefinable_methods += [:empty_product]
286
+ self.blankish_vals = Set.new([ nil ])
287
+
288
+ def initialize(options={})
289
+ @items_factory = Gorillib::Factory.receive( options.delete(:items){ IdenticalFactory.new } )
290
+ super(options)
291
+ end
292
+
293
+ def native?(obj)
294
+ false
295
+ end
296
+
297
+ def empty_product
298
+ product.new
299
+ end
300
+
301
+ def convert(obj)
302
+ clxn = empty_product
303
+ obj.each do |val|
304
+ clxn << items_factory.receive(val)
305
+ end
306
+ clxn
307
+ end
308
+ end
309
+
310
+ class ArrayFactory < EnumerableFactory
311
+ self.product = Array
312
+ register_factory!
313
+ end
314
+
315
+ class SetFactory < EnumerableFactory
316
+ self.product = Set
317
+ register_factory!
318
+ end
319
+
320
+ class HashFactory < EnumerableFactory
321
+ # [#receive] factory for converting keys
322
+ attr_reader :keys_factory
323
+ self.product = Hash
324
+
325
+ def initialize(options={})
326
+ @keys_factory = Gorillib::Factory( options.delete(:keys){ Whatever.new } )
327
+ super(options)
328
+ end
329
+
330
+ def convert(obj)
331
+ hsh = empty_product
332
+ obj.each_pair do |key, val|
333
+ hsh[keys_factory.receive(key)] = items_factory.receive(val)
334
+ end
335
+ hsh
336
+ end
337
+ register_factory!
338
+ end
339
+
340
+ class RangeFactory < NonConvertingFactory
341
+ self.product = Range
342
+ self.blankish_vals = [ nil, [] ]
343
+ register_factory!
344
+ end
345
+
346
+ # __________________________________________________________________________
347
+
348
+ class ApplyProcFactory < ConvertingFactory
349
+ attr_reader :callable
350
+ self.blankish_vals = Set.new([nil])
351
+
352
+ def initialize(callable=nil, options={}, &block)
353
+ if block_given?
354
+ raise ArgumentError, "Pass a block or a value, not both" unless callable.nil?
355
+ callable = block
356
+ end
357
+ @callable = callable
358
+ super(options)
359
+ end
360
+ def native?(val)
361
+ false
362
+ end
363
+ def convert(obj)
364
+ callable.call(obj)
365
+ end
366
+ register_factory!(:proc)
367
+ end
368
+
369
+
370
+ end
371
+
372
+ end
@@ -0,0 +1,146 @@
1
+ module Gorillib
2
+ module Model
3
+
4
+ # Represents a field for reflection
5
+ #
6
+ # @example Usage
7
+ # Gorillib::Model::Field.new(:name => 'problems', type => Integer, :doc => 'Count of problems')
8
+ #
9
+ #
10
+ class Field
11
+ include Gorillib::Model
12
+ remove_possible_method(:type)
13
+
14
+ # [Gorillib::Model] Model owning this field
15
+ attr_reader :model
16
+
17
+ # [Hash] all options passed to the field not recognized by one of its own current fields
18
+ attr_reader :extra_attributes
19
+
20
+ # Note: `Gorillib::Model::Field` is assembled in two pieces, so that it
21
+ # can behave as a model itself. Defining `name` here, along with some
22
+ # fudge in #initialize, provides enough functionality to bootstrap.
23
+ # The fields are then defined properly at the end of the file.
24
+
25
+ attr_reader :name
26
+ attr_reader :type
27
+
28
+ class_attribute :visibilities, :instance_writer => false
29
+ self.visibilities = { :reader => :public, :writer => :public, :receiver => :public, :tester => false }
30
+
31
+
32
+ # @param [#to_sym] name Field name
33
+ # @param [#receive] type Factory for field values. To accept any object as-is, specify `Object` as the type.
34
+ # @param [Gorillib::Model] model Field's owner
35
+ # @param [Hash{Symbol => Object}] options Extended attributes
36
+ # @option options [String] doc Description of the field's purpose
37
+ # @option options [true, false, :public, :protected, :private] :reader Visibility for the reader (`#foo`) method; `false` means don't create one.
38
+ # @option options [true, false, :public, :protected, :private] :writer Visibility for the writer (`#foo=`) method; `false` means don't create one.
39
+ # @option options [true, false, :public, :protected, :private] :receiver Visibility for the receiver (`#receive_foo`) method; `false` means don't create one.
40
+ #
41
+ def initialize(name, type, model, options={})
42
+ Validate.identifier!(name)
43
+ @model = model
44
+ @name = name.to_sym
45
+ @type = self.factory_for(type)
46
+ default_visabilities = visibilities
47
+ @visibilities = default_visabilities.merge( options.extract!(*default_visabilities.keys) )
48
+ @doc = options.delete(:name){ "#{name} field" }
49
+ receive!(options)
50
+ end
51
+
52
+ # __________________________________________________________________________
53
+
54
+ # @return [String] the field name
55
+ def to_s
56
+ name.to_s
57
+ end
58
+
59
+ def factory_for(type)
60
+ Gorillib::Factory(type)
61
+ end
62
+
63
+ # @return [String] Human-readable presentation of the field definition
64
+ def inspect
65
+ args = [name.inspect, type.to_s]
66
+ "field(#{args.join(", ")})"
67
+ end
68
+
69
+ def to_hash
70
+ attributes.merge!(@visibility).merge!(@extra_attributes)
71
+ end
72
+
73
+ def ==(val)
74
+ super && (val.extra_attributes == self.extra_attributes) && (val.model == self.model)
75
+ end
76
+
77
+ def self.receive(hsh)
78
+ name = hsh.fetch(:name)
79
+ type = hsh.fetch(:type)
80
+ model = hsh.fetch(:model)
81
+ new(name, type, model, hsh)
82
+ end
83
+
84
+ #
85
+ # returns the visibility
86
+ #
87
+ # @example reader is protected, no writer:
88
+ # Foo.field :granuloxity, :reader => :protected, :writer => false
89
+ #
90
+ def visibility(meth_type)
91
+ Validate.included_in!("method type", meth_type, @visibilities.keys)
92
+ @visibilities[meth_type]
93
+ end
94
+
95
+ protected
96
+
97
+ #
98
+ #
99
+ #
100
+ def inscribe_methods(model)
101
+ model.__send__(:define_attribute_reader, self.name, self.type, visibility(:reader))
102
+ model.__send__(:define_attribute_writer, self.name, self.type, visibility(:writer))
103
+ model.__send__(:define_attribute_tester, self.name, self.type, visibility(:tester))
104
+ model.__send__(:define_attribute_receiver, self.name, self.type, visibility(:receiver))
105
+ end
106
+
107
+ public
108
+
109
+ #
110
+ # Now we can construct the actual fields.
111
+ #
112
+
113
+ # Name of this field. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]` (required)
114
+ # @macro [attach] field
115
+ # @attribute $1
116
+ # @return [$2] the $1 field $*
117
+ field :name, String, :writer => false, :doc => "The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]` (required)"
118
+
119
+ # Factory for the field's values
120
+ field :type, Class
121
+
122
+ # Field's description
123
+ field :doc, String
124
+
125
+ # remove the attr_reader method (needed for scaffolding), leaving the meta_module method to remain
126
+ remove_possible_method(:name)
127
+
128
+ end
129
+ end
130
+ end
131
+
132
+ # * aliases
133
+ # * order
134
+ # * dirty
135
+ # * lazy
136
+ # * mass assignable
137
+ # * identifier / index
138
+ # * hook
139
+ # * validates / required
140
+ # - presence => true
141
+ # - uniqueness => true
142
+ # - numericality => true # also :==, :>, :>=, :<, :<=, :odd?, :even?, :equal_to, :less_than, etc
143
+ # - length => { :< => 7 } # also :==, :>=, :<=, :is, :minimum, :maximum
144
+ # - format => { :with => /.*/ }
145
+ # - inclusion => { :in => [1,2,3] }
146
+ # - exclusion => { :in => [1,2,3] }
@@ -0,0 +1,53 @@
1
+ module Gorillib
2
+ module Model
3
+ module NamedSchema
4
+
5
+ protected
6
+
7
+ #
8
+ # Returns the meta_module -- a module extending the type, on which all the
9
+ # model methods are inscribed. This allows you to override the model methods
10
+ # and call +super()+ to get the generic behavior.
11
+ #
12
+ # The meta_module is named for the including class, but with 'Meta::'
13
+ # prepended and 'Type' appended -- so Geo::Place has meta_module
14
+ # "Meta::Geo::PlaceType"
15
+ #
16
+ def meta_module
17
+ return @_meta_module if defined?(@_meta_module)
18
+ if self.name
19
+ @_meta_module = ::Gorillib::Model::NamedSchema.get_nested_module("Meta::#{self.name}Type")
20
+ else
21
+ @_meta_module = Module.new
22
+ end
23
+ self.class_eval{ include(@_meta_module) }
24
+ @_meta_module
25
+ end
26
+
27
+ def define_meta_module_method(method_name, visibility=:public, &block)
28
+ if (visibility == false) then return ; end
29
+ if (visibility == true) then visibility = :public ; end
30
+ Validate.included_in!("visibility", visibility, [:public, :private, :protected])
31
+ meta_module.module_eval{ define_method(method_name, &block) }
32
+ meta_module.module_eval "#{visibility} :#{method_name}", __FILE__, __LINE__
33
+ end
34
+
35
+ # Returns a module for the given names, rooted at Object (so
36
+ # implicity with '::').
37
+ # @example
38
+ # get_nested_module(["This", "That", "TheOther"])
39
+ # # This::That::TheOther
40
+ def self.get_nested_module(name)
41
+ name.split('::').inject(Object) do |parent_module, module_name|
42
+ # inherit = false makes these methods be scoped to parent_module instead of universally
43
+ if parent_module.const_defined?(module_name, false)
44
+ parent_module.const_get(module_name, false)
45
+ else
46
+ parent_module.const_set(module_name.to_sym, Module.new)
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module Gorillib
2
+ module Model
3
+ class RecordSchema
4
+ field :name, String
5
+ field :doc, String
6
+ field :type, Factory
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Gorillib
2
+ module Model
3
+
4
+ def to_wire(options={})
5
+ attributes.merge(:_type => self.class.typename).inject({}) do |acc, (key,attr)|
6
+ acc[key] = attr.respond_to?(:to_wire) ? attr.to_wire(options) : attr
7
+ acc
8
+ end
9
+ end
10
+
11
+ def to_json(options={})
12
+ MultiJson.dump(to_wire(options), options)
13
+ end
14
+ alias_method(:as_json, :to_wire)
15
+
16
+ module ClassMethods
17
+ def from_tuple(*vals)
18
+ receive Hash[field_names[0..vals.length].zip(vals)]
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Gorillib
2
+ module Model
3
+ module Validate
4
+ module_function
5
+
6
+ VALID_NAME_RE = /\A[A-Za-z_][A-Za-z0-9_]*\z/
7
+ def identifier!(name)
8
+ raise TypeError, "can't convert #{name.class} into Symbol", caller unless name.respond_to? :to_sym
9
+ raise ArgumentError, "Name must start with [A-Za-z_] and subsequently contain only [A-Za-z0-9_]", caller unless name =~ VALID_NAME_RE
10
+ end
11
+
12
+ def hashlike!(desc, val)
13
+ return true if val.respond_to?(:[]) && val.respond_to?(:has_key?)
14
+ raise ArgumentError, "#{desc} should be something that behaves like a hash: #{val.inspect}", caller
15
+ end
16
+
17
+ def included_in!(desc, val, colxn)
18
+ raise ArgumentError, "#{desc} must be one of #{colxn.inspect}: got #{val.inspect}", caller unless colxn.include?(val)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'set'
2
+ #
3
+ require 'gorillib/object/blank'
4
+ require 'gorillib/object/try'
5
+ require 'gorillib/object/try_dup'
6
+ require 'gorillib/array/extract_options'
7
+ require 'gorillib/hash/keys'
8
+ require 'gorillib/hash/slice'
9
+ require 'gorillib/string/inflector'
10
+ require 'gorillib/exception/raisers'
11
+ require 'gorillib/metaprogramming/concern'
12
+ require 'gorillib/metaprogramming/class_attribute'
13
+ #
14
+ require 'gorillib/collection'
15
+ require 'gorillib/type/extended'
16
+ require 'gorillib/model/factories'
17
+ require 'gorillib/model/named_schema'
18
+ require 'gorillib/model/validate'
19
+ require 'gorillib/model/errors'
20
+ #
21
+ require 'gorillib/model/base'
22
+ require 'gorillib/model/field'
23
+ require 'gorillib/model/defaults'