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,260 @@
1
+ require 'gorillib/string/simple_inflector'
2
+ require 'gorillib/model'
3
+
4
+ module Gorillib
5
+ module Builder
6
+ extend Gorillib::Concern
7
+ include Gorillib::Model
8
+
9
+ def initialize(attrs={}, &block)
10
+ receive!(attrs, &block)
11
+ end
12
+
13
+ def receive!(*args, &block)
14
+ super(*args)
15
+ if block_given?
16
+ (block.arity == 1) ? block.call(self) : self.instance_eval(&block)
17
+ end
18
+ self
19
+ end
20
+
21
+ def getset(field, *args, &block)
22
+ ArgumentError.check_arity!(args, 0..1)
23
+ if args.empty?
24
+ read_attribute(field.name)
25
+ else
26
+ write_attribute(field.name, args.first)
27
+ end
28
+ end
29
+
30
+ def getset_member(field, *args, &block)
31
+ ArgumentError.check_arity!(args, 0..1)
32
+ attrs = args.first
33
+ if attrs.is_a?(field.type)
34
+ # actual object: assign it into field
35
+ val = attrs
36
+ write_attribute(field.name, val)
37
+ else
38
+ val = read_attribute(field.name)
39
+ if val.present?
40
+ # existing item: update it with args and block
41
+ val.receive!(*args, &block) if args.present?
42
+ elsif attrs.blank?
43
+ # missing item (read): return nil
44
+ return nil
45
+ else
46
+ # missing item (write): construct item and add to collection
47
+ options = args.extract_options!.merge(:owner => self)
48
+ val = field.type.receive(*args, options, &block)
49
+ write_attribute(field.name, val)
50
+ end
51
+ end
52
+ val
53
+ end
54
+
55
+ def getset_collection_item(field, item_key, attrs={}, &block)
56
+ plural_name = field.plural_name
57
+ if attrs.is_a?(field.item_type)
58
+ # actual object: assign it into collection
59
+ val = attrs
60
+ set_collection_item(plural_name, item_key, val)
61
+ elsif has_collection_item?(plural_name, item_key)
62
+ # existing item: retrieve it, updating as directed
63
+ val = get_collection_item(plural_name, item_key)
64
+ val.receive!(attrs, &block)
65
+ else
66
+ # missing item: autovivify item and add to collection
67
+ val = field.item_type.receive({ key_method => item_key, :owner => self }.merge(attrs), &block)
68
+ set_collection_item(plural_name, item_key, val)
69
+ end
70
+ val
71
+ end
72
+
73
+ def get_collection_item(plural_name, item_key)
74
+ collection_of(plural_name)[item_key]
75
+ end
76
+
77
+ def set_collection_item(plural_name, item_key, item)
78
+ collection_of(plural_name)[item_key] = item
79
+ end
80
+
81
+ def has_collection_item?(plural_name, item_key)
82
+ collection_of(plural_name).include?(item_key)
83
+ end
84
+
85
+ def key_method
86
+ :name
87
+ end
88
+
89
+ def to_key
90
+ self.read_attribute(key_method)
91
+ end
92
+
93
+ def inspect_helper(detailed, attrs)
94
+ attrs.delete(:owner)
95
+ # detailed ? str : ([str[0..-2], " #{to_key}>"].join)
96
+ str = super(detailed, attrs)
97
+ end
98
+
99
+ def collection_of(plural_name)
100
+ self.read_attribute(plural_name)
101
+ end
102
+
103
+ module ClassMethods
104
+ include Gorillib::Model::ClassMethods
105
+
106
+ def field(field_name, type, options={})
107
+ super(field_name, type, {:field_type => ::Gorillib::Builder::GetsetField}.merge(options))
108
+ end
109
+ def member(field_name, type, options={})
110
+ field(field_name, type, {:field_type => ::Gorillib::Builder::MemberField}.merge(options))
111
+ end
112
+ def collection(field_name, item_type, options={})
113
+ field(field_name, Gorillib::Collection, {
114
+ :item_type => item_type,
115
+ :field_type => ::Gorillib::Builder::CollectionField}.merge(options))
116
+ end
117
+ def simple_field(field_name, type, options={})
118
+ field(field_name, type, {:field_type => ::Gorillib::Model::Field}.merge(options))
119
+ end
120
+
121
+ protected
122
+
123
+ def define_attribute_getset(field)
124
+ field_name = field.name; type = field.type
125
+ define_meta_module_method(field_name, field.visibility(:reader)) do |*args, &block|
126
+ begin
127
+ getset(field, *args, &block)
128
+ rescue StandardError => err ; err.polish("#{self.class}.#{field_name} type #{type} on #{args}'") rescue nil ; raise ; end
129
+ end
130
+ end
131
+
132
+ def define_member_getset(field)
133
+ field_name = field.name; type = field.type
134
+ define_meta_module_method(field_name, field.visibility(:reader)) do |*args, &block|
135
+ begin
136
+ getset_member(field, *args, &block)
137
+ rescue StandardError => err ; err.polish("#{self.class}.#{field_name} type #{type} on #{args}'") rescue nil ; raise ; end
138
+ end
139
+ end
140
+
141
+ def define_collection_getset(field)
142
+ field_name = field.name; item_type = field.item_type
143
+ define_meta_module_method(field.singular_name, field.visibility(:collection_getset)) do |*args, &block|
144
+ begin
145
+ getset_collection_item(field, *args, &block)
146
+ rescue StandardError => err ; err.polish("#{self.class}.#{field_name} c[#{item_type}] on #{args}'") rescue nil ; raise ; end
147
+ end
148
+ end
149
+
150
+ def define_collection_receiver(field)
151
+ plural_name = field.name; item_type = field.item_type; field_type = field.type
152
+ define_meta_module_method("receive_#{plural_name}", true) do |coll, &block|
153
+ begin
154
+ if coll.is_a?(field_type)
155
+ write_attribute(plural_name, coll)
156
+ else
157
+ read_attribute(plural_name).receive!(coll, &block)
158
+ end
159
+ self
160
+ rescue StandardError => err ; err.polish("#{self.class} #{plural_name} c[#{item_type}] on #{args}'") rescue nil ; raise ; end
161
+ end
162
+ end
163
+
164
+ def define_collection_tester(field)
165
+ plural_name = field.plural_name
166
+ define_meta_module_method("has_#{field.singular_name}?", field.visibility(:collection_tester)) do |item_key|
167
+ begin
168
+ collection_of(plural_name).include?(item_key)
169
+ rescue StandardError => err ; err.polish("#{self.class}.#{plural_name} having #{item_key}?'") rescue nil ; raise ; end
170
+ end
171
+ end
172
+
173
+ end
174
+ end
175
+
176
+ module FancyBuilder
177
+ extend Gorillib::Concern
178
+ include Gorillib::Builder
179
+
180
+ included do |base|
181
+ base.field :name, Symbol
182
+ end
183
+
184
+ module ClassMethods
185
+ include Gorillib::Builder::ClassMethods
186
+
187
+ def belongs_to(field_name, type, options={})
188
+ field = field(field_name, type, {:field_type => ::Gorillib::Builder::MemberField }.merge(options))
189
+ define_meta_module_method "#{field.name}_name" do
190
+ val = getset_member(field) or return nil
191
+ val.name
192
+ end
193
+ field
194
+ end
195
+
196
+ def option(field_name, options={})
197
+ type = options.delete(:type){ Whatever }
198
+ field(field_name, type, {:field_type => ::Gorillib::Builder::GetsetField }.merge(options))
199
+ end
200
+
201
+ def collects(type, clxn_name)
202
+ type_handle = type.handle
203
+ define_meta_module_method type_handle do |item_name, attrs={}, options={}, &block|
204
+ send(clxn_name, item_name, attrs, options.merge(:factory => type), &block)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ module Builder
211
+
212
+ class GetsetField < Gorillib::Model::Field
213
+ self.visibilities = visibilities.merge(:writer => false, :tester => false, :getset => true)
214
+ def inscribe_methods(model)
215
+ model.__send__(:define_attribute_getset, self)
216
+ model.__send__(:define_attribute_writer, self.name, self.type, visibility(:writer))
217
+ model.__send__(:define_attribute_tester, self.name, self.type, visibility(:tester))
218
+ model.__send__(:define_attribute_receiver, self.name, self.type, visibility(:receiver))
219
+ end
220
+ end
221
+
222
+ class MemberField < Gorillib::Model::Field
223
+ self.visibilities = visibilities.merge(:writer => false, :tester => true)
224
+ def inscribe_methods(model)
225
+ model.__send__(:define_member_getset, self)
226
+ model.__send__(:define_attribute_writer, self.name, self.type, visibility(:writer))
227
+ model.__send__(:define_attribute_tester, self.name, self.type, visibility(:tester))
228
+ model.__send__(:define_attribute_receiver, self.name, self.type, visibility(:receiver))
229
+ end
230
+ end
231
+
232
+ class CollectionField < Gorillib::Model::Field
233
+ field :singular_name, Symbol, :default => ->{ Gorillib::Inflector.singularize(name.to_s).to_sym }
234
+ field :item_type, Class, :default => Whatever
235
+
236
+ self.visibilities = visibilities.merge(:writer => false, :tester => false,
237
+ :collection_getset => :public, :collection_tester => true)
238
+
239
+ alias_method :plural_name, :name
240
+ def singular_name
241
+ @singular_name ||= Gorillib::Inflector.singularize(name.to_s).to_sym
242
+ end
243
+
244
+ def inscribe_methods(model)
245
+ item_type = self.item_type
246
+ self.default = ->{ Gorillib::Collection.new(item_type) }
247
+ raise "Plural and singular names must differ: #{self.plural_name}" if (singular_name == plural_name)
248
+ #
249
+ @visibilities[:writer] = false
250
+ model.__send__(:define_attribute_reader, self.name, self.type, visibility(:reader))
251
+ model.__send__(:define_attribute_tester, self.name, self.type, visibility(:tester))
252
+ #
253
+ model.__send__(:define_collection_receiver, self)
254
+ model.__send__(:define_collection_getset, self)
255
+ model.__send__(:define_collection_tester, self)
256
+ end
257
+ end
258
+
259
+ end
260
+ end
@@ -0,0 +1,31 @@
1
+ module Gorillib
2
+ class Collection
3
+ module HasCollection
4
+
5
+ def has_collection(clxn_name, type, key_method=:name)
6
+ plural_name = clxn_name
7
+ singular_name = Gorillib::Inflector.singularize(clxn_name.to_s).to_sym
8
+
9
+ instance_variable_set("@#{plural_name}", Gorillib::Collection.new(type, key_method))
10
+
11
+ define_singleton_method(plural_name) do
12
+ instance_variable_get("@#{plural_name}") if instance_variable_defined?("@#{plural_name}")
13
+ end
14
+
15
+ define_singleton_method(singular_name) do |item_key, attrs={}, options={}, &block|
16
+ collection = instance_variable_get("@#{clxn_name}")
17
+ val = collection.fetch(item_key) do
18
+ attrs.merge!(key_method => item_key, :owner => self) if attrs.respond_to?(:merge!)
19
+ factory = options.fetch(:factory){ type }
20
+ new_val = factory.receive(attrs)
21
+ collection << new_val
22
+ new_val
23
+ end
24
+ val.instance_exec(&block) if block
25
+ val
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,129 @@
1
+ require 'gorillib/metaprogramming/delegation'
2
+ require 'gorillib/metaprogramming/class_attribute'
3
+
4
+ module Gorillib
5
+ class Collection
6
+ # [String, Symbol] Method invoked on a new item to generate its collection key; :to_key by default
7
+ attr_accessor :key_method
8
+ # The default `key_method` invoked on a new item to generate its collection key
9
+ DEFAULT_KEY_METHOD = :to_key
10
+
11
+ # [Class, #receive] Factory for generating a new collection item.
12
+ class_attribute :factory, :instance_writer => false
13
+ singleton_class.class_eval{ protected :factory= }
14
+
15
+ # [{Symbol => Object}] The actual store of items, but not for you to mess with
16
+ attr_reader :clxn
17
+ protected :clxn
18
+
19
+ delegate :[], :[]=, :delete, :fetch, :to => :clxn
20
+ delegate :keys, :values, :each_pair, :each_value, :to => :clxn
21
+ delegate :has_key?, :include?, :length, :size, :empty?, :blank?, :to => :clxn
22
+
23
+ def initialize(factory=nil, key_method=DEFAULT_KEY_METHOD)
24
+ @key_method = key_method
25
+ @factory = factory unless factory.nil?
26
+ @clxn = {}
27
+ end
28
+
29
+ # @return [Array] an array holding the items
30
+ def to_a ; values ; end
31
+ # @return [{Symbol => Object}] a hash of key=>item pairs
32
+ def to_hash ; clxn.dup ; end
33
+
34
+ # Merge the new items in-place; given items clobber existing items
35
+ # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
36
+ # @return [Gorillib::Collection] the collection
37
+ def merge!(other)
38
+ clxn.merge!( convert_collection(other) )
39
+ self
40
+ end
41
+ alias_method :concat, :merge!
42
+ alias_method :receive!, :merge!
43
+
44
+ def self.receive(items, *args)
45
+ coll = new(*args)
46
+ coll.receive!(items)
47
+ coll
48
+ end
49
+
50
+ # Two collections are equal if they have the same class and their contents are equal
51
+ #
52
+ # @param [Gorillib::Collection, Object] other The other collection to compare
53
+ # @return [true, false] True if attributes are equal and other is instance of the same Class
54
+ def ==(other)
55
+ return false unless other.instance_of?(self.class)
56
+ clxn == other.send(:clxn)
57
+ end
58
+
59
+ # Merge the new items into a new collection; given items clobber existing items
60
+ # @param other [{Symbol => Object}, Array<Object>] a hash of key=>item pairs or a list of items
61
+ # @return [Gorillib::Collection] a new merged collection
62
+ def merge(other)
63
+ dup.merge!(other)
64
+ end
65
+
66
+ def create(*args)
67
+ item = factory.receive(*args)
68
+ self << item
69
+ item
70
+ end
71
+
72
+ def find_or_create(key)
73
+ fetch(key){ create(key_method => key) }
74
+ end
75
+
76
+ # Adds an item in-place
77
+ # @return [Gorillib::Collection] the collection
78
+ def <<(val)
79
+ merge! [val]
80
+ self
81
+ end
82
+
83
+ # @return [String] string describing the collection's array representation
84
+ def to_s ; to_a.to_s ; end
85
+ # @return [String] string describing the collection's array representation
86
+ def inspect(detailed=true)
87
+ str = "c{ "
88
+ if detailed
89
+ str << clxn.map do |key, val|
90
+ "%-15s %s" % ["#{key}:", val.inspect]
91
+ end.join(",\n ")
92
+ else
93
+ str << keys.join(", ")
94
+ end
95
+ str << " }"
96
+ end
97
+ # @return [Array] serializable array representation of the collection
98
+ def to_wire(options)
99
+ to_a.map{|el| el.respond_to?(:to_wire) ? el.to_wire(options) : el }
100
+ end
101
+ alias_method(:as_json, :to_wire)
102
+ # @return [String] JSON serialization of the collection's array representation
103
+ def to_json(*args)
104
+ p [self, options]
105
+ to_wire(*args).to_json(*args)
106
+ end
107
+
108
+ protected
109
+
110
+ def convert_value(val)
111
+ return val unless factory
112
+ return nil if val.nil?
113
+ factory.receive(val)
114
+ end
115
+
116
+ # - if the given collection responds_to `to_hash`, it is merged into the internal collection; each hash key *must* match the id of its value or results are undefined.
117
+ # - otherwise, it merges a hash generates from the id/value pairs of each object in the given collection.
118
+ def convert_collection(cc)
119
+ return cc.to_hash if cc.respond_to?(:to_hash)
120
+ cc.inject({}) do |acc, val|
121
+ val = convert_value(val)
122
+ key = val.public_send(key_method).to_sym
123
+ acc[key] = val
124
+ acc
125
+ end
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,28 @@
1
+ require 'configliere'
2
+ require 'gorillib/builder'
3
+ require 'gorillib/string/inflections'
4
+
5
+ module Gorillib
6
+ module Configurable
7
+ extend Gorillib::Concern
8
+ include Gorillib::Builder
9
+
10
+ module ClassMethods
11
+ def receive(attrs = {}, &blk)
12
+ conf = settings.load_configuration_in_order!(configuration_scope.to_s)
13
+ super(attrs.merge(conf), &blk)
14
+ end
15
+
16
+ def config(name, type, options = {})
17
+ field(name, type, options)
18
+ end
19
+ end
20
+
21
+ included do
22
+ self.class_attribute(:configuration_scope, :settings)
23
+ self.configuration_scope = self.to_s.underscore.to_sym
24
+ self.settings = Configliere::Param.new.use(:commandline, :config_file)
25
+ end
26
+
27
+ end
28
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ module Kernel
2
+ def assert(value, message="Assertion failed", error=StandardError)
3
+ raise error, message, caller unless value
4
+ end
5
+ end
6
+
7
+ class NullObject
8
+ def method_missing(*args, &block)
9
+ self
10
+ end
11
+
12
+ def nil?; true; end
13
+ end
14
+
15
+ def Maybe(value)
16
+ value.nil? ? NullObject.new : value
17
+ end
@@ -0,0 +1,78 @@
1
+ Exception.class_eval do
2
+ # @return [Array] __FILE__, __LINE__, description
3
+ def self.caller_parts
4
+ caller_line = caller[1]
5
+ mg = %r{\A([^:]+):(\d+):in \`([^\']+)\'\z}.match(caller_line) or return [caller_line, 1, 'unknown']
6
+ [mg[1], mg[2].to_i, mg[3]]
7
+ end
8
+
9
+ #
10
+ # @note !! Be sure to rescue the call to this method; few things suck worse than debugging your rescue blocks/
11
+ def polish(extra_info)
12
+ filename, _, method_name = self.class.caller_parts
13
+ method_name.gsub!(/rescue in /, '')
14
+ most_recent_line = backtrace.detect{|line| line.include?(filename) && line.include?(method_name) }
15
+ most_recent_line.sub!(/'$/, " for #{extra_info}'"[0..300])
16
+ end
17
+
18
+ end
19
+
20
+ ArgumentError.class_eval do
21
+ # Raise an error just like Ruby's native message if the array of arguments
22
+ # doesn't match the expected length or range of lengths.
23
+ #
24
+ # @example want `getset(:foo)` to be different from `getset(:foo, nil)`
25
+ # def getset(key, *args)
26
+ # ArgumentError.check_arity!(args, 0..1)
27
+ # return self[key] if args.empty?
28
+ # self[key] = args.first
29
+ # end
30
+ #
31
+ # @overload check_arity!(args, n)
32
+ # @param [Array] args splat args as handed to the caller
33
+ # @param [Integer] val expected length
34
+ # @overload check_arity!(args, x..y)
35
+ # @param [Array] args splat args as handed to the caller
36
+ # @param [#include?] val expected range/list/set of lengths
37
+ # @raise ArgumentError when there are
38
+ def self.check_arity!(args, val)
39
+ allowed_arity = val.is_a?(Integer) ? (val..val) : val
40
+ return true if allowed_arity.include?(args.length)
41
+ raise self.new("wrong number of arguments (#{args.length} for #{val})")
42
+ end
43
+
44
+ # Raise an error just like Ruby's native message if there are fewer arguments
45
+ # than expected
46
+ #
47
+ # @example want to use splat args, requiring at least one
48
+ # def assemble_path(*pathsegs)
49
+ # ArgumentError.arity_at_least!(pathsegs, 1)
50
+ # # ...
51
+ # end
52
+ #
53
+ # @param [Array] args splat args as handed to the caller
54
+ # @param [Integer] val minimum expected length
55
+ def self.arity_at_least!(args, min_arity)
56
+ check_arity!(args, min_arity .. Float::INFINITY)
57
+ end
58
+ end
59
+
60
+ NoMethodError.class_eval do
61
+ MESSAGE = "undefined method `%s' for %s:%s"
62
+
63
+ def self.undefined_method(obj)
64
+ file, line, meth = caller_parts
65
+ self.new(MESSAGE % [meth, obj, obj.class])
66
+ end
67
+
68
+ def self.unimplemented_method(obj)
69
+ file, line, meth = caller_parts
70
+ self.new("#{MESSAGE} -- not implemented yet" % [meth, obj, obj.class])
71
+ end
72
+
73
+ def self.abstract(obj)
74
+ file, line, meth = caller_parts
75
+ self.new("#{MESSAGE} -- must be implemented by the subclass" % [meth, obj, obj.class])
76
+ end
77
+
78
+ end