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 CHANGED
@@ -1 +1 @@
1
- 0.1.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.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-07}
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 = [
@@ -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
- # vs
17
+ #
18
+ # With +try+
4
19
  # @person.try(:name)
5
- def try(method)
6
- send method if respond_to? method
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
- # Hashlike#[]
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
- # Hashlike#[]=
55
- # Hashlike#store
56
- #
57
- # Element Assignment -- Associates the value given by +val+ with the key
58
- # given by +key+.
59
- #
60
- # key should not have its value changed while it is in use as a key. In a
61
- # normal hash, a String passed as a key will be duplicated and frozen. No such
62
- # guarantee is provided here
63
- #
64
- # Delegates to self.send("key=", val)
65
- #
66
- # @example
67
- # hsh = { :a => 100, :b => 200 }
68
- # hsh[:a] = 9
69
- # hsh[:c] = 4
70
- # hsh # => { :a => 9, :b => 200, :c => 4 }
71
- #
72
- # hsh[key] = val -> val
73
- # hsh.store(key, val) -> val
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
- # Hashlike#delete
85
- #
86
- # Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
87
- # optional code block is given and the key is not found, pass in the key and
88
- # return the result of +block+.
89
- #
90
- # In a normal hash, a default value can be set; none is provided here.
91
- #
92
- # @example
93
- # hsh = { :a => 100, :b => 200 }
94
- # hsh.delete(:a) # => 100
95
- # hsh.delete(:z) # => nil
96
- # hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
97
- #
98
- # @overload hsh.delete(key) -> val
99
- # @param key [Object] key to remove
100
- # @return [Object, Nil] the removed object, nil if missing
101
- #
102
- # @overload hsh.delete(key){|key| block } -> val
103
- # @param key [Object] key to remove
104
- # @yield [Object] called (with key) if key is missing
105
- # @yieldparam key
106
- # @return [Object, Nil] the removed object, or if missing, the return value
107
- # of the block
108
- #
109
- def delete(key, &block)
110
- key = convert_key(key)
111
- if has_key?(key)
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
- # Hashlike#keys
123
- #
124
- # Returns a new array populated with the keys from this hashlike.
125
- #
126
- # @see Hashlike#values.
127
- #
128
- # @example
129
- # hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
130
- # hsh.keys # => [:a, :b, :c, :d]
131
- #
132
- # @return [Array] list of keys
133
- #
134
- def keys
135
- members & instance_variables.map{|s| convert_key(s[1..-1]) }
136
- end
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
- protected
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
@@ -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 {#{v}} was given" unless (v.nil?) ; nil }
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
- class_eval <<-STR, __FILE__, __LINE__ + 1
198
+ body = receiver_body_for(type, info)
199
+ if body.is_a?(String)
200
+ class_eval(%Q{
191
201
  def receive_#{name}(v)
192
- v = (#{receiver_body_for(type, info)}) ;
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
- STR
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
- %Q{ v.nil? ? nil : v.map{|el| #{info[:of]}.receive(el) } }
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
- %Q{ v.nil? ? nil : v.inject({}){|h, (el,val)| h[el] = #{info[:of]}.receive(val); h } }
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
- %Q{v.blank? ? nil : #{type}.receive(v) }
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
- class_attribute :receiver_attrs
307
- class_attribute :receiver_attr_names
308
- class_attribute :after_receivers
309
- self.receiver_attrs = {} # info about the attr
310
- self.receiver_attr_names = [] # ordered set of attr names
311
- self.after_receivers = [] # blocks to execute following receive!
312
- extend ClassMethods
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
@@ -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 'd' do
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
- raise 'finish me'
70
- end
71
-
72
- it 'has ' do
73
- @hshlike_subklass.class_eval{ attr_accessor :not_a_receiver }
74
- raise 'finish me'
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
@@ -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
- describe '.receive_tuple' do
67
- it 'receives a tuple, assigning to rcvrs in order'
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 {hello} was given")
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.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-07 00:00:00 -07:00
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: 2181950979362360273
318
+ hash: -517886677893709781
319
319
  segments:
320
320
  - 0
321
321
  version: "0"