gorillib 0.1.11 → 0.4.0pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +1 -2
- data/.yardopts +9 -0
- data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
- data/Gemfile +21 -14
- data/Guardfile +19 -0
- data/{LICENSE.textile → LICENSE.md} +43 -29
- data/README.md +47 -52
- data/Rakefile +31 -30
- data/TODO.md +32 -0
- data/VERSION +1 -1
- data/examples/builder/ironfan.rb +133 -0
- data/examples/model/simple.rb +17 -0
- data/gorillib.gemspec +106 -86
- data/lib/alt/kernel/call_stack.rb +56 -0
- data/lib/gorillib/array/wrap.rb +53 -0
- data/lib/gorillib/base.rb +3 -3
- data/lib/gorillib/builder/field.rb +5 -0
- data/lib/gorillib/builder.rb +260 -0
- data/lib/gorillib/collection/has_collection.rb +31 -0
- data/lib/gorillib/collection.rb +129 -0
- data/lib/gorillib/configurable.rb +28 -0
- data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
- data/lib/gorillib/exception/confidence.rb +17 -0
- data/lib/gorillib/exception/raisers.rb +78 -0
- data/lib/gorillib/hash/mash.rb +202 -0
- data/lib/gorillib/hashlike/slice.rb +53 -19
- data/lib/gorillib/hashlike.rb +5 -3
- data/lib/gorillib/io/system_helpers.rb +30 -0
- data/lib/gorillib/logger/log.rb +18 -0
- data/lib/gorillib/metaprogramming/concern.rb +124 -0
- data/lib/gorillib/model/active_model_conversion.rb +68 -0
- data/lib/gorillib/model/active_model_naming.rb +87 -0
- data/lib/gorillib/model/active_model_shim.rb +33 -0
- data/lib/gorillib/model/base.rb +341 -0
- data/lib/gorillib/model/defaults.rb +71 -0
- data/lib/gorillib/model/errors.rb +14 -0
- data/lib/gorillib/model/factories.rb +372 -0
- data/lib/gorillib/model/field.rb +146 -0
- data/lib/gorillib/model/named_schema.rb +53 -0
- data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
- data/lib/gorillib/model/record_schema.rb +9 -0
- data/lib/gorillib/model/serialization.rb +23 -0
- data/lib/gorillib/model/validate.rb +22 -0
- data/lib/gorillib/model.rb +23 -0
- data/lib/gorillib/pathname.rb +78 -0
- data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
- data/lib/gorillib/some.rb +11 -9
- data/lib/gorillib/string/constantize.rb +21 -14
- data/lib/gorillib/string/inflections.rb +6 -76
- data/lib/gorillib/string/inflector.rb +192 -0
- data/lib/gorillib/string/simple_inflector.rb +267 -0
- data/lib/gorillib/type/extended.rb +52 -0
- data/lib/gorillib/utils/capture_output.rb +28 -0
- data/lib/gorillib/utils/console.rb +131 -0
- data/lib/gorillib/utils/nuke_constants.rb +9 -0
- data/lib/gorillib/utils/stub_module.rb +33 -0
- data/spec/examples/builder/ironfan_spec.rb +37 -0
- data/spec/extlib/hash_spec.rb +64 -0
- data/spec/extlib/mash_spec.rb +312 -0
- data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
- data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
- data/spec/gorillib/builder_spec.rb +187 -0
- data/spec/gorillib/collection_spec.rb +20 -0
- data/spec/gorillib/configurable_spec.rb +62 -0
- data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
- data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
- data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
- data/spec/gorillib/exception/raisers_spec.rb +60 -0
- data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
- data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
- data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
- data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
- data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
- data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
- data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
- data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
- data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
- data/spec/gorillib/model/record/defaults_spec.rb +108 -0
- data/spec/gorillib/model/record/factories_spec.rb +321 -0
- data/spec/gorillib/model/record/overlay_spec.rb +46 -0
- data/spec/gorillib/model/serialization_spec.rb +48 -0
- data/spec/gorillib/model_spec.rb +281 -0
- data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
- data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
- data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
- data/spec/{object → gorillib/object}/try_spec.rb +3 -2
- data/spec/gorillib/pathname_spec.rb +114 -0
- data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
- data/spec/{string → gorillib/string}/human_spec.rb +2 -2
- data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
- data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
- data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
- data/spec/gorillib/type/extended_spec.rb +120 -0
- data/spec/gorillib/utils/capture_output_spec.rb +71 -0
- data/spec/spec_helper.rb +8 -11
- data/spec/support/gorillib_test_helpers.rb +66 -0
- data/spec/support/hashlike_fuzzing_helper.rb +31 -33
- data/spec/support/hashlike_helper.rb +3 -3
- data/spec/support/model_test_helpers.rb +81 -0
- data/spec/support/shared_examples/included_module.rb +20 -0
- metadata +177 -158
- data/lib/gorillib/array/average.rb +0 -13
- data/lib/gorillib/array/sorted_median.rb +0 -11
- data/lib/gorillib/array/sorted_percentile.rb +0 -11
- data/lib/gorillib/array/sorted_sample.rb +0 -12
- data/lib/gorillib/dsl_object.rb +0 -64
- data/lib/gorillib/hash/indifferent_access.rb +0 -207
- data/lib/gorillib/hash/tree_merge.rb +0 -4
- data/lib/gorillib/hashlike/tree_merge.rb +0 -49
- data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
- data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
- data/lib/gorillib/receiver/active_model_shim.rb +0 -32
- data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
- data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
- data/lib/gorillib/receiver/locale/en.yml +0 -27
- data/lib/gorillib/receiver/tree_diff.rb +0 -74
- data/lib/gorillib/receiver/validations.rb +0 -30
- data/lib/gorillib/receiver.rb +0 -402
- data/lib/gorillib/receiver_model.rb +0 -21
- data/lib/gorillib/struct/acts_as_hash.rb +0 -108
- data/notes/fancy_hashes_and_receivers.textile +0 -120
- data/notes/hash_rdocs.textile +0 -97
- data/spec/array/average_spec.rb +0 -24
- data/spec/array/sorted_median_spec.rb +0 -18
- data/spec/array/sorted_percentile_spec.rb +0 -24
- data/spec/array/sorted_sample_spec.rb +0 -28
- data/spec/dsl_object_spec.rb +0 -99
- data/spec/hash/indifferent_access_spec.rb +0 -391
- data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
- data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
- data/spec/receiver/acts_as_hash_spec.rb +0 -295
- data/spec/receiver_spec.rb +0 -551
- data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
- 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
|