gorillib 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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"