gorillib 0.1.1 → 0.1.2
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.
- data/VERSION +1 -1
- data/gorillib.gemspec +2 -2
- data/lib/gorillib/object/try.rb +51 -4
- data/lib/gorillib/receiver/acts_as_hash.rb +129 -104
- data/lib/gorillib/receiver.rb +70 -18
- data/spec/object/try_spec.rb +5 -4
- data/spec/receiver/acts_as_hash_spec.rb +19 -10
- data/spec/receiver_spec.rb +71 -4
- data/spec/support/hashlike_helper.rb +2 -1
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/gorillib.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{gorillib}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Infochimps"]
|
12
|
-
s.date = %q{2011-06-
|
12
|
+
s.date = %q{2011-06-24}
|
13
13
|
s.description = %q{Gorillib: infochimps lightweight subset of ruby convenience methods}
|
14
14
|
s.email = %q{coders@infochimps.org}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/gorillib/object/try.rb
CHANGED
@@ -1,10 +1,57 @@
|
|
1
|
+
unless Object.method_defined?(:try)
|
1
2
|
class Object
|
3
|
+
# Invokes the method identified by the symbol +method+, passing it any arguments
|
4
|
+
# and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
|
5
|
+
#
|
6
|
+
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
|
7
|
+
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
|
8
|
+
#
|
9
|
+
# If try is called without a method to call, it will yield any given block with the object.
|
10
|
+
#
|
11
|
+
# ==== Examples
|
12
|
+
#
|
13
|
+
# Without +try+
|
14
|
+
# @person && @person.name
|
15
|
+
# or
|
2
16
|
# @person ? @person.name : nil
|
3
|
-
#
|
17
|
+
#
|
18
|
+
# With +try+
|
4
19
|
# @person.try(:name)
|
5
|
-
|
6
|
-
|
20
|
+
#
|
21
|
+
# +try+ also accepts arguments and/or a block, for the method it is trying
|
22
|
+
# Person.try(:find, 1)
|
23
|
+
# @people.try(:collect) {|p| p.name}
|
24
|
+
#
|
25
|
+
# Without a method argument try will yield to the block unless the receiver is nil.
|
26
|
+
# @person.try { |p| "#{p.first_name} #{p.last_name}" }
|
27
|
+
#--
|
28
|
+
# +try+ behaves like +Object#send+, unless called on +NilClass+.
|
29
|
+
def try(*a, &b)
|
30
|
+
if a.empty? && block_given?
|
31
|
+
yield self
|
32
|
+
elsif !a.empty? && !respond_to?(a.first)
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
__send__(*a, &b)
|
36
|
+
end
|
7
37
|
end
|
8
|
-
|
9
38
|
end
|
10
39
|
|
40
|
+
class NilClass
|
41
|
+
# Calling +try+ on +nil+ always returns +nil+.
|
42
|
+
# It becomes specially helpful when navigating through associations that may return +nil+.
|
43
|
+
#
|
44
|
+
# === Examples
|
45
|
+
#
|
46
|
+
# nil.try(:name) # => nil
|
47
|
+
#
|
48
|
+
# Without +try+
|
49
|
+
# @person && !@person.children.blank? && @person.children.first.name
|
50
|
+
#
|
51
|
+
# With +try+
|
52
|
+
# @person.try(:children).try(:first).try(:name)
|
53
|
+
def try(*args)
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'gorillib/hashlike'
|
2
|
+
|
1
3
|
module Receiver
|
2
4
|
#
|
3
5
|
# Makes a Receiver thingie behave mostly like a hash.
|
@@ -30,113 +32,135 @@ module Receiver
|
|
30
32
|
#
|
31
33
|
module ActsAsHash
|
32
34
|
|
33
|
-
|
34
|
-
#
|
35
|
-
# Element Reference -- Retrieves the value stored for +key+.
|
36
|
-
#
|
37
|
-
# In a normal hash, a default value can be set; none is provided here.
|
38
|
-
#
|
39
|
-
# Delegates to self.send(key)
|
40
|
-
#
|
41
|
-
# @example
|
42
|
-
# hsh = { :a => 100, :b => 200 }
|
43
|
-
# hsh[:a] # => 100
|
44
|
-
# hsh[:c] # => nil
|
45
|
-
#
|
46
|
-
# @param key [Object] key to retrieve
|
47
|
-
# @return [Object] the value stored for key, nil if missing
|
48
|
-
#
|
49
|
-
def [](key)
|
50
|
-
key = convert_key(key)
|
51
|
-
self.send(key)
|
52
|
-
end
|
35
|
+
module InstanceMethods
|
53
36
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
#
|
75
|
-
# @param key [Object] key to associate
|
76
|
-
# @param val [Object] value to associate it with
|
77
|
-
# @return [Object]
|
78
|
-
#
|
79
|
-
def []=(key, val)
|
80
|
-
key = convert_key(key)
|
81
|
-
self.send("#{key}=", val)
|
82
|
-
end
|
37
|
+
# Hashlike#[]
|
38
|
+
#
|
39
|
+
# Element Reference -- Retrieves the value stored for +key+.
|
40
|
+
#
|
41
|
+
# In a normal hash, a default value can be set; none is provided here.
|
42
|
+
#
|
43
|
+
# Delegates to self.send(key)
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# hsh = { :a => 100, :b => 200 }
|
47
|
+
# hsh[:a] # => 100
|
48
|
+
# hsh[:c] # => nil
|
49
|
+
#
|
50
|
+
# @param key [Object] key to retrieve
|
51
|
+
# @return [Object] the value stored for key, nil if missing
|
52
|
+
#
|
53
|
+
def [](key)
|
54
|
+
key = convert_key(key)
|
55
|
+
self.send(key)
|
56
|
+
end
|
83
57
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
val = self[key]
|
113
|
-
self.send(:remove_instance_variable, "@#{key}")
|
114
|
-
val
|
115
|
-
elsif block_given?
|
116
|
-
block.call(key)
|
117
|
-
else
|
118
|
-
nil
|
58
|
+
# Hashlike#[]=
|
59
|
+
# Hashlike#store
|
60
|
+
#
|
61
|
+
# Element Assignment -- Associates the value given by +val+ with the key
|
62
|
+
# given by +key+.
|
63
|
+
#
|
64
|
+
# key should not have its value changed while it is in use as a key. In a
|
65
|
+
# normal hash, a String passed as a key will be duplicated and frozen. No such
|
66
|
+
# guarantee is provided here
|
67
|
+
#
|
68
|
+
# Delegates to self.send("key=", val)
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# hsh = { :a => 100, :b => 200 }
|
72
|
+
# hsh[:a] = 9
|
73
|
+
# hsh[:c] = 4
|
74
|
+
# hsh # => { :a => 9, :b => 200, :c => 4 }
|
75
|
+
#
|
76
|
+
# hsh[key] = val -> val
|
77
|
+
# hsh.store(key, val) -> val
|
78
|
+
#
|
79
|
+
# @param key [Object] key to associate
|
80
|
+
# @param val [Object] value to associate it with
|
81
|
+
# @return [Object]
|
82
|
+
#
|
83
|
+
def []=(key, val)
|
84
|
+
key = convert_key(key)
|
85
|
+
self.send("#{key}=", val)
|
119
86
|
end
|
120
|
-
end
|
121
87
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
88
|
+
# Hashlike#delete
|
89
|
+
#
|
90
|
+
# Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
|
91
|
+
# optional code block is given and the key is not found, pass in the key and
|
92
|
+
# return the result of +block+.
|
93
|
+
#
|
94
|
+
# In a normal hash, a default value can be set; none is provided here.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# hsh = { :a => 100, :b => 200 }
|
98
|
+
# hsh.delete(:a) # => 100
|
99
|
+
# hsh.delete(:z) # => nil
|
100
|
+
# hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
|
101
|
+
#
|
102
|
+
# @overload hsh.delete(key) -> val
|
103
|
+
# @param key [Object] key to remove
|
104
|
+
# @return [Object, Nil] the removed object, nil if missing
|
105
|
+
#
|
106
|
+
# @overload hsh.delete(key){|key| block } -> val
|
107
|
+
# @param key [Object] key to remove
|
108
|
+
# @yield [Object] called (with key) if key is missing
|
109
|
+
# @yieldparam key
|
110
|
+
# @return [Object, Nil] the removed object, or if missing, the return value
|
111
|
+
# of the block
|
112
|
+
#
|
113
|
+
def delete(key, &block)
|
114
|
+
key = convert_key(key)
|
115
|
+
if has_key?(key)
|
116
|
+
val = self[key]
|
117
|
+
self.send(:remove_instance_variable, "@#{key}")
|
118
|
+
val
|
119
|
+
elsif block_given?
|
120
|
+
block.call(key)
|
121
|
+
else
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Hashlike#keys
|
127
|
+
#
|
128
|
+
# Returns a new array populated with the keys from this hashlike.
|
129
|
+
#
|
130
|
+
# @see Hashlike#values.
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
|
134
|
+
# hsh.keys # => [:a, :b, :c, :d]
|
135
|
+
#
|
136
|
+
# @return [Array] list of keys
|
137
|
+
#
|
138
|
+
def keys
|
139
|
+
members & instance_variables.map{|s| convert_key(s[1..-1]) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def members
|
143
|
+
self.class.members
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Returns a hash with each key set to its associated value.
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# my_hshlike = MyHashlike.new
|
151
|
+
# my_hshlike[:a] = 100; my_hshlike[:b] = 200
|
152
|
+
# my_hshlike.to_hash # => { :a => 100, :b => 200 }
|
153
|
+
#
|
154
|
+
# @return [Hash] a new Hash instance, with each key set to its associated value.
|
155
|
+
#
|
156
|
+
def to_hash
|
157
|
+
{}.tap do |hsh|
|
158
|
+
each_pair do |key, val|
|
159
|
+
hsh[key] = val.respond_to?(:to_hash) ? val.to_hash : val
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
137
163
|
|
138
|
-
def members
|
139
|
-
self.class.members
|
140
164
|
end
|
141
165
|
|
142
166
|
module ClassMethods
|
@@ -153,7 +177,7 @@ module Receiver
|
|
153
177
|
end
|
154
178
|
end
|
155
179
|
|
156
|
-
|
180
|
+
protected
|
157
181
|
|
158
182
|
def convert_key(key)
|
159
183
|
raise ArgumentError, "Keys for #{self.class} must be symbols, strings or respond to #to_sym" unless key.respond_to?(:to_sym)
|
@@ -161,8 +185,9 @@ module Receiver
|
|
161
185
|
end
|
162
186
|
|
163
187
|
def self.included base
|
164
|
-
base.extend ClassMethods
|
165
188
|
base.send(:include, Gorillib::Hashlike)
|
189
|
+
base.extend(ClassMethods)
|
190
|
+
base.send(:include, InstanceMethods)
|
166
191
|
end
|
167
192
|
end
|
168
193
|
end
|
data/lib/gorillib/receiver.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'gorillib/object/blank'
|
2
|
+
require 'gorillib/object/try'
|
3
|
+
require 'gorillib/array/extract_options'
|
4
|
+
|
1
5
|
# dummy type for receiving True or False
|
2
6
|
class Boolean ; end unless defined?(Boolean)
|
3
7
|
|
@@ -85,19 +89,23 @@ module Receiver
|
|
85
89
|
RECEIVER_BODIES[Array] = %q{ case when v.nil? then nil when v.blank? then [] else Array(v) end }
|
86
90
|
RECEIVER_BODIES[Hash] = %q{ case when v.nil? then nil when v.blank? then {} else v end }
|
87
91
|
RECEIVER_BODIES[Boolean] = %q{ case when v.nil? then nil when v.to_s.strip.blank? then false else v.to_s.strip != "false" end }
|
88
|
-
RECEIVER_BODIES[NilClass] = %q{ raise ArgumentError, "This field must be nil, but
|
92
|
+
RECEIVER_BODIES[NilClass] = %q{ raise ArgumentError, "This field must be nil, but [#{v}] was given" unless (v.nil?) ; nil }
|
89
93
|
RECEIVER_BODIES[Object] = %q{ v } # accept and love the object just as it is
|
90
94
|
|
91
95
|
#
|
92
96
|
# Give each base class a receive method
|
93
97
|
#
|
94
98
|
RECEIVER_BODIES.each do |k,b|
|
95
|
-
if k.is_a?(Class)
|
99
|
+
if k.is_a?(Class) && b.is_a?(String)
|
96
100
|
k.class_eval <<-STR, __FILE__, __LINE__ + 1
|
97
101
|
def self.receive(v)
|
98
102
|
#{b}
|
99
103
|
end
|
100
104
|
STR
|
105
|
+
elsif k.is_a?(Class)
|
106
|
+
k.class_eval do
|
107
|
+
define_singleton_method(:receive, &b)
|
108
|
+
end
|
101
109
|
end
|
102
110
|
end
|
103
111
|
|
@@ -187,12 +195,19 @@ public
|
|
187
195
|
def rcvr name, type, info={}
|
188
196
|
name = name.to_sym
|
189
197
|
type = type_to_klass(type)
|
190
|
-
|
198
|
+
body = receiver_body_for(type, info)
|
199
|
+
if body.is_a?(String)
|
200
|
+
class_eval(%Q{
|
191
201
|
def receive_#{name}(v)
|
192
|
-
|
202
|
+
self.instance_variable_set("@#{name}", (#{body}))
|
203
|
+
end}, __FILE__, __LINE__ + 1)
|
204
|
+
else
|
205
|
+
define_method("receive_#{name}") do |*args|
|
206
|
+
v = body.call(*args)
|
193
207
|
self.instance_variable_set("@#{name}", v)
|
208
|
+
v
|
194
209
|
end
|
195
|
-
|
210
|
+
end
|
196
211
|
# careful here: don't modify parent's class_attribute in-place
|
197
212
|
self.receiver_attrs = self.receiver_attrs.dup
|
198
213
|
self.receiver_attr_names += [name] unless receiver_attr_names.include?(name)
|
@@ -254,6 +269,30 @@ public
|
|
254
269
|
defs
|
255
270
|
end
|
256
271
|
|
272
|
+
# returns an in-order traversal of the
|
273
|
+
#
|
274
|
+
def tuple_keys
|
275
|
+
return @tuple_keys if @tuple_keys
|
276
|
+
@tuple_keys = self
|
277
|
+
@tuple_keys = receiver_attrs.map do |attr, info|
|
278
|
+
info[:type].try(:tuple_keys) || attr
|
279
|
+
end.flatten
|
280
|
+
end
|
281
|
+
|
282
|
+
def consume_tuple(tuple)
|
283
|
+
obj = self.new
|
284
|
+
receiver_attrs.each do |attr, info|
|
285
|
+
if info[:type].respond_to?(:consume_tuple)
|
286
|
+
val = info[:type].consume_tuple(tuple)
|
287
|
+
else
|
288
|
+
val = tuple.shift
|
289
|
+
end
|
290
|
+
# obj.send("receive_#{attr}", val)
|
291
|
+
obj.send("#{attr}=", val)
|
292
|
+
end
|
293
|
+
obj
|
294
|
+
end
|
295
|
+
|
257
296
|
protected
|
258
297
|
def receiver_body_for type, info
|
259
298
|
type = type_to_klass(type)
|
@@ -261,16 +300,15 @@ public
|
|
261
300
|
# they have an :of => SomeType option.
|
262
301
|
case
|
263
302
|
when info[:of] && (type == Array)
|
264
|
-
|
303
|
+
receiver_type = info[:of]
|
304
|
+
lambda{|v| v.nil? ? nil : v.map{|el| receiver_type.receive(el) } }
|
265
305
|
when info[:of] && (type == Hash)
|
266
|
-
|
306
|
+
receiver_type = info[:of]
|
307
|
+
lambda{|v| v.nil? ? nil : v.inject({}){|h, (el,val)| h[el] = receiver_type.receive(val); h } }
|
267
308
|
when Receiver::RECEIVER_BODIES.include?(type)
|
268
309
|
Receiver::RECEIVER_BODIES[type]
|
269
310
|
when type.is_a?(Class)
|
270
|
-
|
271
|
-
# when (type.is_a?(Symbol) && type.to_s =~ /^[A-Z]/)
|
272
|
-
# # a hack so you can use a class not defined yet
|
273
|
-
# %Q{v.blank? ? nil : #{type}.receive(v) }
|
311
|
+
lambda{|v| v.blank? ? nil : type.receive(v) }
|
274
312
|
else
|
275
313
|
raise("Can't receive #{type} #{info}")
|
276
314
|
end
|
@@ -286,6 +324,18 @@ public
|
|
286
324
|
end
|
287
325
|
end
|
288
326
|
|
327
|
+
def to_tuple
|
328
|
+
tuple = []
|
329
|
+
self.each_value do |val|
|
330
|
+
if val.respond_to?(:to_tuple)
|
331
|
+
tuple += val.to_tuple
|
332
|
+
else
|
333
|
+
tuple << val
|
334
|
+
end
|
335
|
+
end
|
336
|
+
tuple
|
337
|
+
end
|
338
|
+
|
289
339
|
module ClassMethods
|
290
340
|
# By default, the hashlike methods iterate over the receiver attributes.
|
291
341
|
# If you want to filter our add to the keys list, override this method
|
@@ -303,13 +353,15 @@ public
|
|
303
353
|
# set up receiver attributes, and bring in methods from the ClassMethods module at class-level
|
304
354
|
def self.included base
|
305
355
|
base.class_eval do
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
356
|
+
unless method_defined?(:receiver_attrs)
|
357
|
+
class_attribute :receiver_attrs
|
358
|
+
class_attribute :receiver_attr_names
|
359
|
+
class_attribute :after_receivers
|
360
|
+
self.receiver_attrs = {} # info about the attr
|
361
|
+
self.receiver_attr_names = [] # ordered set of attr names
|
362
|
+
self.after_receivers = [] # blocks to execute following receive!
|
363
|
+
extend ClassMethods
|
364
|
+
end
|
313
365
|
end
|
314
366
|
end
|
315
367
|
end
|
data/spec/object/try_spec.rb
CHANGED
@@ -2,8 +2,8 @@ require File.dirname(__FILE__)+'/../spec_helper'
|
|
2
2
|
require 'gorillib/object/try'
|
3
3
|
|
4
4
|
class Foo
|
5
|
-
def i_am_a_method_hooray
|
6
|
-
"i was called!"
|
5
|
+
def i_am_a_method_hooray param='hooray'
|
6
|
+
"i was called! #{param}!"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -12,8 +12,9 @@ describe Object do
|
|
12
12
|
it 'returns nil if item does not #respond_to? method' do
|
13
13
|
Foo.new.try(:i_am_not_a_method).should be_nil
|
14
14
|
end
|
15
|
-
it 'calls the method if the item does #respond_to? it' do
|
16
|
-
Foo.new.try(:i_am_a_method_hooray).should == "i was called!"
|
15
|
+
it 'calls the method (with args) if the item does #respond_to? it' do
|
16
|
+
Foo.new.try(:i_am_a_method_hooray).should == "i was called! hooray!"
|
17
|
+
Foo.new.try(:i_am_a_method_hooray, 'yay').should == "i was called! yay!"
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -10,13 +10,10 @@ require GORILLIB_ROOT_DIR('spec/hashlike/hashlike_behavior_spec')
|
|
10
10
|
class SimpleReceiver
|
11
11
|
include Receiver
|
12
12
|
include Receiver::ActsAsHash
|
13
|
-
include Gorillib::Hashlike
|
14
13
|
#
|
15
14
|
rcvr_accessor :a, Integer
|
16
15
|
rcvr_accessor :b, Integer
|
17
16
|
rcvr_accessor :c, Integer
|
18
|
-
# rcvr_reader :b, Integer
|
19
|
-
# rcvr_writer :c, Integer
|
20
17
|
rcvr_accessor :nil_val, NilClass
|
21
18
|
rcvr_accessor :false_val, Boolean
|
22
19
|
rcvr_accessor :new_key, String
|
@@ -41,16 +38,19 @@ describe Receiver do
|
|
41
38
|
describe '#[] and #[]= and #store' do
|
42
39
|
it_should_behave_like :hashlike_store_and_retrieve
|
43
40
|
it_should_behave_like :references_string_and_symbol_keys_equivalently
|
41
|
+
|
44
42
|
it 'reject unknown keys' do
|
45
43
|
lambda{ @hshlike[:fnord] = 69 }.should raise_error NoMethodError, /undefined method `fnord=' for/
|
46
44
|
lambda{ @hshlike[:fnord] }.should raise_error NoMethodError, /undefined method `fnord' for/
|
47
45
|
@hshlike.delete(:fnord).should be_nil
|
48
46
|
end
|
47
|
+
|
49
48
|
it 'accepts defined but unset keys' do
|
50
49
|
@hshlike[:new_key].should be_nil
|
51
50
|
@hshlike[:new_key] = 69
|
52
51
|
@hshlike[:new_key].should == 69
|
53
52
|
end
|
53
|
+
|
54
54
|
it 'does not allow nil, Object, or other non-stringy keys' do
|
55
55
|
lambda{ @hshlike[300] = :i_haz_num }.should raise_error(ArgumentError, "Keys for SimpleReceiver must be symbols, strings or respond to #to_sym")
|
56
56
|
lambda{ @hshlike[nil] = :i_haz_nil }.should raise_error(ArgumentError, "Keys for SimpleReceiver must be symbols, strings or respond to #to_sym")
|
@@ -61,17 +61,19 @@ describe Receiver do
|
|
61
61
|
@hshlike[obj].should == :i_haz_obj
|
62
62
|
end
|
63
63
|
|
64
|
-
it '
|
64
|
+
it 'defines the right accessor methods' do
|
65
65
|
@hshlike_subklass.rcvr_writer :write_only_attr, String
|
66
66
|
@hshlike_subklass.rcvr_reader :read_only_attr, String
|
67
67
|
@hshlike_subklass.rcvr :internal_attr, String
|
68
68
|
@hshlike_subklass.class_eval{ attr_accessor :not_a_receiver }
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
[ :receive_write_only_attr, :write_only_attr=,
|
70
|
+
:receive_read_only_attr, :read_only_attr,
|
71
|
+
:receive_internal_attr,
|
72
|
+
:not_a_receiver, :not_a_receiver= ].each{ |attr| @hshlike_subklass.should be_method_defined(attr) }
|
73
|
+
[ :write_only_attr, :read_only_attr=,
|
74
|
+
:internal_attr, :internal_attr=,
|
75
|
+
:receive_not_a_receiver ].each{ |attr| @hshlike_subklass.should_not be_method_defined(attr) }
|
76
|
+
@hshlike_subklass.receiver_attr_names.should == [:a, :b, :c, :nil_val, :false_val, :new_key, :write_only_attr, :read_only_attr, :internal_attr]
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
@@ -259,6 +261,13 @@ describe Receiver do
|
|
259
261
|
|
260
262
|
describe '#to_hash' do
|
261
263
|
it_should_behave_like :hashlike_to_hash
|
264
|
+
|
265
|
+
it 'should recurse' do
|
266
|
+
@hshlike_subklass.rcvr_accessor :k2, SimpleReceiver
|
267
|
+
hsh = HashlikeHelper::HASH_TO_TEST_HL_V_A.merge(:k2 => {:a => 3, :new_key => 'hi' })
|
268
|
+
obj = @hshlike_subklass.receive(hsh)
|
269
|
+
obj.to_hash.should == {:a=>100, :b=>200, :c=>300, :nil_val=>nil, :false_val=>false, :k2=>{:a=>3, :new_key=>"hi"} }
|
270
|
+
end
|
262
271
|
end
|
263
272
|
|
264
273
|
describe '#invert' do
|
data/spec/receiver_spec.rb
CHANGED
@@ -11,6 +11,36 @@ class Wide
|
|
11
11
|
rcvr_accessor :my_int, Integer
|
12
12
|
end
|
13
13
|
|
14
|
+
require 'gorillib/receiver/acts_as_hash'
|
15
|
+
class StreetAddress
|
16
|
+
include Receiver
|
17
|
+
include Receiver::ActsAsHash
|
18
|
+
rcvr_accessor :num, Integer
|
19
|
+
rcvr_accessor :street, String
|
20
|
+
rcvr_accessor :city, String
|
21
|
+
rcvr_accessor :state, String
|
22
|
+
rcvr_accessor :zip_code, Integer
|
23
|
+
end
|
24
|
+
|
25
|
+
class Vcard
|
26
|
+
include Receiver
|
27
|
+
include Receiver::ActsAsHash
|
28
|
+
rcvr_accessor :name, String
|
29
|
+
rcvr_accessor :home_address, StreetAddress
|
30
|
+
rcvr_accessor :work_address, StreetAddress
|
31
|
+
rcvr_accessor :phone, String
|
32
|
+
end
|
33
|
+
|
34
|
+
class RecursiveReceiver < Wide
|
35
|
+
include Receiver
|
36
|
+
rcvr_accessor :a, Integer
|
37
|
+
rcvr_accessor :b, String
|
38
|
+
rcvr_accessor :wide_rcvr_a, Wide
|
39
|
+
rcvr_accessor :rec_field, RecursiveReceiver
|
40
|
+
rcvr_accessor :wide_rcvr_b, Wide
|
41
|
+
rcvr_accessor :c, String
|
42
|
+
end
|
43
|
+
|
14
44
|
describe Receiver do
|
15
45
|
before do
|
16
46
|
@klass = Class.new(Wide)
|
@@ -63,14 +93,44 @@ describe Receiver do
|
|
63
93
|
end
|
64
94
|
end
|
65
95
|
|
66
|
-
|
67
|
-
|
96
|
+
ADDRESS_TUPLE = ['Homer J Simpson', 742, 'Evergreen Terr', 'Springfield', 'AZ', 12345, 100, 'Industrial Way', 'Springfield', 'WY', 98765, '800-BITE-ME']
|
97
|
+
ADDRESS_HASH = {
|
98
|
+
:name => 'Homer J Simpson',
|
99
|
+
:home_address => { :num => 742, :street => 'Evergreen Terr', :city => 'Springfield', :state => 'AZ', :zip_code => 12345 },
|
100
|
+
:work_address => { :num => 100, :street => 'Industrial Way', :city => 'Springfield', :state => 'WY', :zip_code => 98765 },
|
101
|
+
:phone => '800-BITE-ME'}
|
102
|
+
|
103
|
+
describe '.consume_tuple' do
|
104
|
+
it 'receives a tuple, assigning to rcvrs in order' do
|
105
|
+
obj = Vcard.consume_tuple(ADDRESS_TUPLE.dup)
|
106
|
+
obj.to_hash.should == ADDRESS_HASH
|
107
|
+
end
|
68
108
|
|
69
109
|
it 'allows empty tuple'
|
70
110
|
|
71
111
|
it '?breaks? on too-long tuple'
|
72
112
|
end
|
73
113
|
|
114
|
+
describe '#to_tuple' do
|
115
|
+
it 'flattens' do
|
116
|
+
obj = Vcard.receive(ADDRESS_HASH)
|
117
|
+
obj.to_tuple.should == ADDRESS_TUPLE
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '.tuple_keys' do
|
122
|
+
it 'for a simple receiver, produces attrs in order' do
|
123
|
+
StreetAddress.tuple_keys.should == [:num, :street, :city, :state, :zip_code]
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'for a complex receiver, in-order traverses the tree' do
|
127
|
+
Vcard.tuple_keys.should == [:name, :num, :street, :city, :state, :zip_code, :num, :street, :city, :state, :zip_code, :phone]
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does not recurse endlessly' do
|
131
|
+
RecursiveReceiver.tuple_keys.should == [:my_int, :a, :b, :my_int, RecursiveReceiver, :my_int, :c]
|
132
|
+
end
|
133
|
+
end
|
74
134
|
|
75
135
|
describe '.receive_foo' do
|
76
136
|
it 'injects a superclass, so I can call super() in receive_foo'
|
@@ -194,7 +254,6 @@ describe Receiver do
|
|
194
254
|
class Foo ; include Receiver ; rcvr_accessor(:foo, Integer) ; end
|
195
255
|
@klass.rcvr_accessor :array_of_obj, Array, :of => Foo
|
196
256
|
obj = @klass.receive( :array_of_obj => [ {:foo => 3}, {:foo => 5} ] )
|
197
|
-
p obj
|
198
257
|
obj.array_of_obj.first.foo.should == 3
|
199
258
|
obj.array_of_obj.last.foo.should == 5
|
200
259
|
end
|
@@ -389,6 +448,14 @@ describe Receiver do
|
|
389
448
|
end
|
390
449
|
end
|
391
450
|
|
451
|
+
it "lets me use an anonymous class as a received type" do
|
452
|
+
@klass_2 = Class.new(Wide)
|
453
|
+
@klass_2.rcvr_accessor :maw, Integer
|
454
|
+
@klass.rcvr_accessor :k2, @klass_2
|
455
|
+
obj = @klass.receive({ :my_int => 3, :k2 => { :maw => 2 }})
|
456
|
+
obj.k2.maw.should == 2
|
457
|
+
end
|
458
|
+
|
392
459
|
it 'keeps values across a receive!' do
|
393
460
|
@klass.rcvr_accessor :repeated, Integer
|
394
461
|
@klass.rcvr_accessor :just_second, Integer
|
@@ -468,7 +535,7 @@ describe Receiver do
|
|
468
535
|
describe 'NilClass' do
|
469
536
|
it 'only accepts nil' do
|
470
537
|
@klass.rcvr_accessor :nil_field, NilClass
|
471
|
-
lambda{ @klass.receive( :nil_field => 'hello' ) }.should raise_error(ArgumentError, "This field must be nil, but
|
538
|
+
lambda{ @klass.receive( :nil_field => 'hello' ) }.should raise_error(ArgumentError, "This field must be nil, but [hello] was given")
|
472
539
|
end
|
473
540
|
end
|
474
541
|
end
|
@@ -54,7 +54,8 @@ module HashlikeHelper
|
|
54
54
|
:assert_valid_keys,
|
55
55
|
:nested_under_indifferent_access,
|
56
56
|
:stringify_keys, :stringify_keys!, :symbolize_keys, :symbolize_keys!,
|
57
|
-
:with_indifferent_access, :yaml_initialize
|
57
|
+
:with_indifferent_access, :yaml_initialize,
|
58
|
+
:extractable_options?, :deep_dup, :reverse_merge, :reverse_merge!, :slice, :slice!, :extract!, :deep_merge, :deep_merge!, :deep_compact!, :compact, :compact!, :compact_blank, :compact_blank!
|
58
59
|
]
|
59
60
|
FANCY_HASHLIKE_METHODS.each{|meth| OMITTED_METHODS_FROM_HASH << meth }
|
60
61
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: gorillib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Infochimps
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-24 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -315,7 +315,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
315
315
|
requirements:
|
316
316
|
- - ">="
|
317
317
|
- !ruby/object:Gem::Version
|
318
|
-
hash:
|
318
|
+
hash: -517886677893709781
|
319
319
|
segments:
|
320
320
|
- 0
|
321
321
|
version: "0"
|