gorillib 0.1.11 → 0.4.0pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -2
  3. data/.yardopts +9 -0
  4. data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
  5. data/Gemfile +21 -14
  6. data/Guardfile +19 -0
  7. data/{LICENSE.textile → LICENSE.md} +43 -29
  8. data/README.md +47 -52
  9. data/Rakefile +31 -30
  10. data/TODO.md +32 -0
  11. data/VERSION +1 -1
  12. data/examples/builder/ironfan.rb +133 -0
  13. data/examples/model/simple.rb +17 -0
  14. data/gorillib.gemspec +106 -86
  15. data/lib/alt/kernel/call_stack.rb +56 -0
  16. data/lib/gorillib/array/wrap.rb +53 -0
  17. data/lib/gorillib/base.rb +3 -3
  18. data/lib/gorillib/builder/field.rb +5 -0
  19. data/lib/gorillib/builder.rb +260 -0
  20. data/lib/gorillib/collection/has_collection.rb +31 -0
  21. data/lib/gorillib/collection.rb +129 -0
  22. data/lib/gorillib/configurable.rb +28 -0
  23. data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
  24. data/lib/gorillib/exception/confidence.rb +17 -0
  25. data/lib/gorillib/exception/raisers.rb +78 -0
  26. data/lib/gorillib/hash/mash.rb +202 -0
  27. data/lib/gorillib/hashlike/slice.rb +53 -19
  28. data/lib/gorillib/hashlike.rb +5 -3
  29. data/lib/gorillib/io/system_helpers.rb +30 -0
  30. data/lib/gorillib/logger/log.rb +18 -0
  31. data/lib/gorillib/metaprogramming/concern.rb +124 -0
  32. data/lib/gorillib/model/active_model_conversion.rb +68 -0
  33. data/lib/gorillib/model/active_model_naming.rb +87 -0
  34. data/lib/gorillib/model/active_model_shim.rb +33 -0
  35. data/lib/gorillib/model/base.rb +341 -0
  36. data/lib/gorillib/model/defaults.rb +71 -0
  37. data/lib/gorillib/model/errors.rb +14 -0
  38. data/lib/gorillib/model/factories.rb +372 -0
  39. data/lib/gorillib/model/field.rb +146 -0
  40. data/lib/gorillib/model/named_schema.rb +53 -0
  41. data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
  42. data/lib/gorillib/model/record_schema.rb +9 -0
  43. data/lib/gorillib/model/serialization.rb +23 -0
  44. data/lib/gorillib/model/validate.rb +22 -0
  45. data/lib/gorillib/model.rb +23 -0
  46. data/lib/gorillib/pathname.rb +78 -0
  47. data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
  48. data/lib/gorillib/some.rb +11 -9
  49. data/lib/gorillib/string/constantize.rb +21 -14
  50. data/lib/gorillib/string/inflections.rb +6 -76
  51. data/lib/gorillib/string/inflector.rb +192 -0
  52. data/lib/gorillib/string/simple_inflector.rb +267 -0
  53. data/lib/gorillib/type/extended.rb +52 -0
  54. data/lib/gorillib/utils/capture_output.rb +28 -0
  55. data/lib/gorillib/utils/console.rb +131 -0
  56. data/lib/gorillib/utils/nuke_constants.rb +9 -0
  57. data/lib/gorillib/utils/stub_module.rb +33 -0
  58. data/spec/examples/builder/ironfan_spec.rb +37 -0
  59. data/spec/extlib/hash_spec.rb +64 -0
  60. data/spec/extlib/mash_spec.rb +312 -0
  61. data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
  62. data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
  63. data/spec/gorillib/builder_spec.rb +187 -0
  64. data/spec/gorillib/collection_spec.rb +20 -0
  65. data/spec/gorillib/configurable_spec.rb +62 -0
  66. data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
  67. data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
  68. data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
  69. data/spec/gorillib/exception/raisers_spec.rb +60 -0
  70. data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
  71. data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
  72. data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
  73. data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
  74. data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
  75. data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
  76. data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
  77. data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
  78. data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
  79. data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
  80. data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
  81. data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
  82. data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
  83. data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
  84. data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
  85. data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
  86. data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
  87. data/spec/gorillib/model/record/defaults_spec.rb +108 -0
  88. data/spec/gorillib/model/record/factories_spec.rb +321 -0
  89. data/spec/gorillib/model/record/overlay_spec.rb +46 -0
  90. data/spec/gorillib/model/serialization_spec.rb +48 -0
  91. data/spec/gorillib/model_spec.rb +281 -0
  92. data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
  93. data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
  94. data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
  95. data/spec/{object → gorillib/object}/try_spec.rb +3 -2
  96. data/spec/gorillib/pathname_spec.rb +114 -0
  97. data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
  98. data/spec/{string → gorillib/string}/human_spec.rb +2 -2
  99. data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
  100. data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
  101. data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
  102. data/spec/gorillib/type/extended_spec.rb +120 -0
  103. data/spec/gorillib/utils/capture_output_spec.rb +71 -0
  104. data/spec/spec_helper.rb +8 -11
  105. data/spec/support/gorillib_test_helpers.rb +66 -0
  106. data/spec/support/hashlike_fuzzing_helper.rb +31 -33
  107. data/spec/support/hashlike_helper.rb +3 -3
  108. data/spec/support/model_test_helpers.rb +81 -0
  109. data/spec/support/shared_examples/included_module.rb +20 -0
  110. metadata +177 -158
  111. data/lib/gorillib/array/average.rb +0 -13
  112. data/lib/gorillib/array/sorted_median.rb +0 -11
  113. data/lib/gorillib/array/sorted_percentile.rb +0 -11
  114. data/lib/gorillib/array/sorted_sample.rb +0 -12
  115. data/lib/gorillib/dsl_object.rb +0 -64
  116. data/lib/gorillib/hash/indifferent_access.rb +0 -207
  117. data/lib/gorillib/hash/tree_merge.rb +0 -4
  118. data/lib/gorillib/hashlike/tree_merge.rb +0 -49
  119. data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
  120. data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
  121. data/lib/gorillib/receiver/active_model_shim.rb +0 -32
  122. data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
  123. data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
  124. data/lib/gorillib/receiver/locale/en.yml +0 -27
  125. data/lib/gorillib/receiver/tree_diff.rb +0 -74
  126. data/lib/gorillib/receiver/validations.rb +0 -30
  127. data/lib/gorillib/receiver.rb +0 -402
  128. data/lib/gorillib/receiver_model.rb +0 -21
  129. data/lib/gorillib/struct/acts_as_hash.rb +0 -108
  130. data/notes/fancy_hashes_and_receivers.textile +0 -120
  131. data/notes/hash_rdocs.textile +0 -97
  132. data/spec/array/average_spec.rb +0 -24
  133. data/spec/array/sorted_median_spec.rb +0 -18
  134. data/spec/array/sorted_percentile_spec.rb +0 -24
  135. data/spec/array/sorted_sample_spec.rb +0 -28
  136. data/spec/dsl_object_spec.rb +0 -99
  137. data/spec/hash/indifferent_access_spec.rb +0 -391
  138. data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
  139. data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
  140. data/spec/receiver/acts_as_hash_spec.rb +0 -295
  141. data/spec/receiver_spec.rb +0 -551
  142. data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
  143. data/spec/struct/acts_as_hash_spec.rb +0 -422
@@ -1,74 +0,0 @@
1
- module Receiver
2
- def tree_diff(other)
3
- diff_hsh = {}
4
- other = other.symbolize_keys if other.respond_to?(:symbolize_keys)
5
- each do |k, v|
6
- case
7
- when v.is_a?(Array) && other[k].is_a?(Array)
8
- val = v.tree_diff(other[k])
9
- diff_hsh[k] = val unless val.blank?
10
- when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
11
- val = v.tree_diff(other[k])
12
- diff_hsh[k] = val unless val.blank?
13
- else
14
- diff_hsh[k] = v unless v == other[k]
15
- end
16
- end
17
- other_hsh = other.dup.delete_if{|k, v| has_key?(k) }
18
- diff_hsh.merge!(other_hsh)
19
- end
20
-
21
- module ActsAsHash
22
- def <=>(other)
23
- return 1 if other.blank?
24
- each_key do |k|
25
- if has_key?(k) && other.has_key?(k)
26
- cmp = self[k] <=> other[k]
27
- return cmp unless cmp == 0
28
- end
29
- end
30
- 0
31
- end
32
- end
33
- end
34
-
35
- class Array
36
- def tree_diff(other)
37
- arr = dup
38
- if other.length > arr.length then arr = arr + ([nil] * (other.length - arr.length)) end
39
- diff_ary = arr.zip(other).map do |arr_el, other_el|
40
- if arr_el.respond_to?(:tree_diff) && other_el.respond_to?(:to_hash)
41
- arr_el.tree_diff(other_el)
42
- else
43
- (arr_el == other_el) ? nil : [arr_el, other_el]
44
- end
45
- end.reject(&:blank?)
46
- end
47
- end
48
-
49
- class Hash
50
- # Returns a hash that represents the difference between two hashes.
51
- #
52
- # Examples:
53
- #
54
- # {1 => 2}.tree_diff(1 => 2) # => {}
55
- # {1 => 2}.tree_diff(1 => 3) # => {1 => 2}
56
- # {}.tree_diff(1 => 2) # => {1 => 2}
57
- # {1 => 2, 3 => 4}.tree_diff(1 => 2) # => {3 => 4}
58
- def tree_diff(other)
59
- diff_hsh = self.dup
60
- each do |k, v|
61
- case
62
- when v.is_a?(Array) && other[k].is_a?(Array)
63
- diff_hsh[k] = v.tree_diff(other[k])
64
- diff_hsh.delete(k) if diff_hsh[k].blank?
65
- when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
66
- diff_hsh[k] = v.tree_diff(other[k])
67
- diff_hsh.delete(k) if diff_hsh[k].blank?
68
- else diff_hsh.delete(k) if v == other[k]
69
- end
70
- end
71
- other_hsh = other.dup.delete_if{|k, v| has_key?(k) || has_key?(k.to_s) }
72
- diff_hsh.merge!(other_hsh)
73
- end
74
- end
@@ -1,30 +0,0 @@
1
- module Receiver
2
-
3
- # An array of strings describing any ways this fails validation
4
- def validation_errors
5
- errors = []
6
- if (ma = missing_attrs).present?
7
- errors << "Missing values for {#{ma.join(",")}}"
8
- end
9
- errors
10
- end
11
-
12
- # returns a list of required but missing attributes
13
- def missing_attrs
14
- missing = []
15
- self.class.required_rcvrs.each do |name, info|
16
- missing << name if (not attr_set?(name))
17
- end
18
- missing
19
- end
20
-
21
- # methods become class-level
22
- module ClassMethods
23
-
24
- # class method gives info for all receiver attributes with required => true
25
- def required_rcvrs
26
- receiver_attrs.select{|name, info| info[:required] }
27
- end
28
- end
29
-
30
- end
@@ -1,402 +0,0 @@
1
- require 'gorillib/object/blank'
2
- require 'gorillib/object/try'
3
- require 'gorillib/object/try_dup'
4
- require 'gorillib/array/extract_options'
5
- require 'gorillib/metaprogramming/class_attribute'
6
-
7
- # dummy type for receiving True or False
8
- class Boolean ; end unless defined?(Boolean)
9
-
10
- # Receiver lets you describe complex (even recursive!) actively-typed data models that
11
- # * are creatable or assignable from static data structures
12
- # * perform efficient type conversion when assigning from a data structure,
13
- # * but with nothing in the way of normal assignment or instantiation
14
- # * and no requirements on the initializer
15
- #
16
- # class Tweet
17
- # include Receiver
18
- # rcvr_accessor :id, Integer
19
- # rcvr_accessor :user_id, Integer
20
- # rcvr_accessor :created_at, Time
21
- # end
22
- # p Tweet.receive(:id => "7", :user_id => 9, :created_at => "20101231010203" )
23
- # # => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
24
- #
25
- # You can override receive behavior in a straightforward and predictable way:
26
- #
27
- # class TwitterUser
28
- # include Receiver
29
- # rcvr_accessor :id, Integer
30
- # rcvr_accessor :screen_name, String
31
- # rcvr_accessor :follower_ids, Array, :of => Integer
32
- # # accumulate unique follower ids
33
- # def receive_follower_ids(arr)
34
- # @follower_ids = (@follower_ids||[]) + arr.map(&:to_i)
35
- # @follower_ids.uniq!
36
- # end
37
- # end
38
- #
39
- # The receiver pattern works naturally with inheritance:
40
- #
41
- # class TweetWithUser < Tweet
42
- # rcvr_accessor :user, TwitterUser
43
- # after_receive do |hsh|
44
- # self.user_id = self.user.id if self.user
45
- # end
46
- # end
47
- # p TweetWithUser.receive(:id => 8675309, :created_at => "20101231010203", :user => { :id => 24601, :screen_name => 'bob', :follower_ids => [1, 8, 3, 4] })
48
- # => #<TweetWithUser @id=8675309, @created_at=2010-12-31 07:02:03 UTC, @user=#<TwitterUser @id=24601, @screen_name="bob", @follower_ids=[1, 8, 3, 4]>, @user_id=24601>
49
- #
50
- # TweetWithUser was able to add another receiver, applicable only to itself and its subclasses.
51
- #
52
- # The receive method works well with sparse data -- you can accumulate
53
- # attributes without trampling formerly set values:
54
- #
55
- # tw = Tweet.receive(:id => "7", :user_id => 9 )
56
- # p tw
57
- # # => #<Tweet @id=7, @user_id=9>
58
- #
59
- # tw.receive!(:created_at => "20101231010203" )
60
- # p tw
61
- # # => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
62
- #
63
- # Note the distinction between an explicit nil field and a missing field:
64
- #
65
- # tw.receive!(:user_id => nil, :created_at => "20090506070809" )
66
- # p tw
67
- # # => #<Tweet @id=7, @user_id=nil, @created_at=2009-05-06 12:08:09 UTC>
68
- #
69
- # There are helpers for default and required attributes:
70
- #
71
- # class Foo
72
- # include Receiver
73
- # rcvr_accessor :is_reqd, String, :required => true
74
- # rcvr_accessor :also_reqd, String, :required => true
75
- # rcvr_accessor :has_default, String, :default => 'hello'
76
- # end
77
- # foo_obj = Foo.receive(:is_reqd => "hi")
78
- # # => #<Foo:0x00000100bd9740 @is_reqd="hi" @has_default="hello">
79
- # foo_obj.missing_attrs
80
- # # => [:also_reqd]
81
- #
82
- module Receiver
83
-
84
- RECEIVER_BODIES = {} unless defined?(RECEIVER_BODIES)
85
- RECEIVER_BODIES[Symbol] = %q{ v.blank? ? nil : v.to_sym }
86
- RECEIVER_BODIES[Integer] = %q{ v.blank? ? nil : v.to_i }
87
- RECEIVER_BODIES[Float] = %q{ v.blank? ? nil : v.to_f }
88
- RECEIVER_BODIES[String] = %q{ v.to_s }
89
- RECEIVER_BODIES[Time] = %q{ v.nil? ? nil : Time.parse(v.to_s).utc rescue nil }
90
- RECEIVER_BODIES[Date] = %q{ v.nil? ? nil : Date.parse(v.to_s) rescue nil }
91
- RECEIVER_BODIES[Array] = %q{ case when v.nil? then nil when v.blank? then [] else Array(v) end }
92
- RECEIVER_BODIES[Hash] = %q{ case when v.nil? then nil when v.blank? then {} else v end }
93
- 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 }
94
- RECEIVER_BODIES[NilClass] = %q{ raise ArgumentError, "This field must be nil, but [#{v}] was given" unless (v.nil?) ; nil }
95
- RECEIVER_BODIES[Object] = %q{ v } # accept and love the object just as it is
96
-
97
- #
98
- # Give each base class a receive method
99
- #
100
- RECEIVER_BODIES.each do |k,b|
101
- if k.is_a?(Class) && b.is_a?(String)
102
- k.class_eval <<-STR, __FILE__, __LINE__ + 1
103
- def self.receive(v)
104
- #{b}
105
- end
106
- STR
107
- elsif k.is_a?(Class)
108
- k.class_eval do
109
- define_singleton_method(:receive, &b)
110
- end
111
- end
112
- end
113
-
114
- TYPE_ALIASES = {
115
- :null => NilClass,
116
- :boolean => Boolean,
117
- :string => String, :bytes => String,
118
- :symbol => Symbol,
119
- :int => Integer, :integer => Integer, :long => Integer,
120
- :time => Time, :date => Date,
121
- :float => Float, :double => Float,
122
- :hash => Hash, :map => Hash,
123
- :array => Array,
124
- } unless defined?(TYPE_ALIASES)
125
-
126
- #
127
- # modify object in place with new typecast values.
128
- #
129
- def receive! hsh={}
130
- raise ArgumentError, "Can't receive (it isn't hashlike): {#{hsh.inspect}}" unless hsh.respond_to?(:[]) && hsh.respond_to?(:has_key?)
131
- _receiver_fields.each do |attr|
132
- if hsh.has_key?(attr.to_sym) then val = hsh[attr.to_sym]
133
- elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s]
134
- else next ; end
135
- _receive_attr attr, val
136
- end
137
- impose_defaults!(hsh)
138
- replace_options!(hsh)
139
- run_after_receivers(hsh)
140
- self
141
- end
142
-
143
- # true if the attr is a receiver variable and it has been set
144
- def attr_set?(attr)
145
- receiver_attrs.has_key?(attr) && self.instance_variable_defined?("@#{attr}")
146
- end
147
-
148
- protected
149
-
150
- def unset!(attr)
151
- self.send(:remove_instance_variable, "@#{attr}") if self.instance_variable_defined?("@#{attr}")
152
- end
153
-
154
- def _receive_attr attr, val
155
- self.send("receive_#{attr}", val)
156
- end
157
-
158
- def _receiver_fields
159
- self.class.receiver_attr_names
160
- end
161
-
162
- def _receiver_defaults
163
- self.class.receiver_defaults
164
- end
165
-
166
- def _after_receivers
167
- self.class.after_receivers
168
- end
169
-
170
- def impose_defaults!(hsh)
171
- _receiver_defaults.each do |attr, val|
172
- next if attr_set?(attr)
173
- self.instance_variable_set "@#{attr}", val.try_dup
174
- end
175
- end
176
-
177
- # class Foo
178
- # include Receiver
179
- # include Receiver::ActsAsHash
180
- # rcvr_accessor :attribute, String, :default => 'okay' :replace => { 'bad' => 'good' }
181
- # end
182
- #
183
- # f = Foo.receive({:attribute => 'bad'})
184
- # => #<Foo:0x10156c820 @attribute="good">
185
- #
186
- def replace_options!(hsh)
187
- self.receiver_attrs.each do |attr, info|
188
- val = self.instance_variable_get("@#{attr}")
189
- if info[:replace] and info[:replace].has_key? val
190
- self.instance_variable_set "@#{attr}", info[:replace][val]
191
- end
192
- end
193
- end
194
-
195
- def run_after_receivers(hsh)
196
- _after_receivers.each do |after_receiver|
197
- self.instance_exec(hsh, &after_receiver)
198
- end
199
- end
200
-
201
- public
202
-
203
- module ClassMethods
204
-
205
- #
206
- # Returns a new instance with the given hash used to set all rcvrs.
207
- #
208
- # All args up to the last one are passed to the initializer.
209
- # The last arg must be a hash -- its attributes are set on the newly-created object
210
- #
211
- # @param hsh [Hash] attr-value pairs to set on the newly created object.
212
- # @param *args [Array] arguments to pass to the constructor
213
- # @return [Object] a new instance
214
- def receive *args
215
- hsh = args.pop || {}
216
- raise ArgumentError, "Can't receive (it isn't hashlike): {#{hsh.inspect}} -- the hsh should be the *last* arg" unless hsh.respond_to?(:[]) && hsh.respond_to?(:has_key?)
217
- obj = self.new(*args)
218
- obj.receive!(hsh)
219
- end
220
-
221
- #
222
- # define a receiver attribute.
223
- # automatically generates an attr_accessor on the class if none exists
224
- #
225
- # @option [Boolean] :required - Adds an error on validation if the attribute is never set
226
- # @option [Object] :default - After any receive! operation, attribute is set to this value unless attr_set? is true
227
- # @option [Class] :of - For collections (Array, Hash, etc), the type of the collection's items
228
- #
229
- def rcvr name, type, info={}
230
- name = name.to_sym
231
- type = type_to_klass(type)
232
- body = receiver_body_for(type, info)
233
- if body.is_a?(String)
234
- class_eval(%Q{
235
- def receive_#{name}(v)
236
- self.instance_variable_set("@#{name}", (#{body}))
237
- end}, __FILE__, __LINE__ + 1)
238
- else
239
- define_method("receive_#{name}") do |*args|
240
- v = body.call(*args)
241
- self.instance_variable_set("@#{name}", v)
242
- v
243
- end
244
- end
245
- # careful here: don't modify parent's class_attribute in-place
246
- self.receiver_attrs = self.receiver_attrs.dup
247
- self.receiver_attr_names += [name] unless receiver_attr_names.include?(name)
248
- self.receiver_attrs[name] = info.merge({ :name => name, :type => type })
249
- end
250
-
251
- # make a block to run after each time .receive! is invoked
252
- def after_receive &block
253
- self.after_receivers += [block]
254
- end
255
-
256
- # defines a receiver attribute, an attr_reader and an attr_writer
257
- # attr_reader is skipped if the getter method is already defined;
258
- # attr_writer is skipped if the setter method is already defined;
259
- def rcvr_accessor name, type, info={}
260
- attr_reader(name) unless method_defined?(name)
261
- attr_writer(name) unless method_defined?("#{name}=")
262
- rcvr name, type, info
263
- end
264
- # defines a receiver attribute and an attr_reader
265
- # attr_reader is skipped if the getter method is already defined.
266
- def rcvr_reader name, type, info={}
267
- attr_reader(name) unless method_defined?(name)
268
- rcvr name, type, info
269
- end
270
- # defines a receiver attribute and an attr_writer
271
- # attr_writer is skipped if the setter method is already defined.
272
- def rcvr_writer name, type, info={}
273
- attr_writer(name) unless method_defined?("#{name}=")
274
- rcvr name, type, info
275
- end
276
-
277
- #
278
- # Defines a receiver for attributes sent to receive! that are
279
- # * not defined as receivers
280
- # * attribute name does not start with '_'
281
- #
282
- # @example
283
- # class Foo ; include Receiver
284
- # rcvr_accessor :bob, String
285
- # rcvr_remaining :other_params
286
- # end
287
- # foo_obj = Foo.receive(:bob => 'hi, bob", :joe => 'hi, joe')
288
- # # => <Foo @bob='hi, bob' @other_params={ :joe => 'hi, joe' }>
289
- def rcvr_remaining name, info={}
290
- rcvr_reader name, Hash, info
291
- after_receive do |hsh|
292
- remaining_vals_hsh = hsh.reject{|k,v| (receiver_attrs.include?(k)) || (k.to_s =~ /^_/) }
293
- self._receive_attr name, remaining_vals_hsh
294
- end
295
- end
296
-
297
- # a hash from attribute names to their default values if given
298
- def receiver_defaults
299
- defs = {}
300
- receiver_attrs.each do |name, info|
301
- defs[name] = info[:default] if info.has_key?(:default)
302
- end
303
- defs
304
- end
305
-
306
- # returns an in-order traversal of the
307
- #
308
- def tuple_keys
309
- return @tuple_keys if @tuple_keys
310
- @tuple_keys = self
311
- @tuple_keys = receiver_attrs.map do |attr, info|
312
- info[:type].try(:tuple_keys) || attr
313
- end.flatten
314
- end
315
-
316
- def consume_tuple(tuple)
317
- obj = self.new
318
- receiver_attrs.each do |attr, info|
319
- if info[:type].respond_to?(:consume_tuple)
320
- val = info[:type].consume_tuple(tuple)
321
- else
322
- val = tuple.shift
323
- end
324
- # obj.send("receive_#{attr}", val)
325
- obj.send("#{attr}=", val)
326
- end
327
- obj
328
- end
329
-
330
- protected
331
- def receiver_body_for type, info
332
- type = type_to_klass(type)
333
- # Note that Array and Hash only need (and only get) special treatment when
334
- # they have an :of => SomeType option.
335
- case
336
- when info[:of] && (type == Array)
337
- receiver_type = info[:of]
338
- lambda{|v| v.nil? ? nil : v.map{|el| receiver_type.receive(el) } }
339
- when info[:of] && (type == Hash)
340
- receiver_type = info[:of]
341
- lambda{|v| v.nil? ? nil : v.inject({}){|h, (el,val)| h[el] = receiver_type.receive(val); h } }
342
- when Receiver::RECEIVER_BODIES.include?(type)
343
- Receiver::RECEIVER_BODIES[type]
344
- when type.is_a?(Class)
345
- lambda{|v| v.blank? ? nil : type.receive(v) }
346
- else
347
- raise("Can't receive #{type} #{info}")
348
- end
349
- end
350
-
351
- def type_to_klass(type)
352
- case
353
- when type.is_a?(Class) then return type
354
- when TYPE_ALIASES.has_key?(type) then TYPE_ALIASES[type]
355
- # when (type.is_a?(Symbol) && type.to_s =~ /^[A-Z]/) then type.to_s.constantize
356
- else raise ArgumentError, "Can\'t handle type #{type}: is it a Class or one of the TYPE_ALIASES?"
357
- end
358
- end
359
- end
360
-
361
- def to_tuple
362
- tuple = []
363
- self.each_value do |val|
364
- if val.respond_to?(:to_tuple)
365
- tuple += val.to_tuple
366
- else
367
- tuple << val
368
- end
369
- end
370
- tuple
371
- end
372
-
373
- module ClassMethods
374
- # By default, the hashlike methods iterate over the receiver attributes.
375
- # If you want to filter our add to the keys list, override this method
376
- #
377
- # @example
378
- # def self.members
379
- # super + [:firstname, :lastname] - [:fullname]
380
- # end
381
- #
382
- def members
383
- receiver_attr_names
384
- end
385
- end
386
-
387
- # set up receiver attributes, and bring in methods from the ClassMethods module at class-level
388
- def self.included base
389
- base.class_eval do
390
- unless method_defined?(:receiver_attrs)
391
- class_attribute :receiver_attrs
392
- class_attribute :receiver_attr_names
393
- class_attribute :after_receivers
394
- self.receiver_attrs = {} # info about the attr
395
- self.receiver_attr_names = [] # ordered set of attr names
396
- self.after_receivers = [] # blocks to execute following receive!
397
- extend ClassMethods
398
- end
399
- end
400
- end
401
-
402
- end
@@ -1,21 +0,0 @@
1
- require 'gorillib/hashlike'
2
- require 'gorillib/hashlike/tree_merge'
3
- require 'gorillib/receiver'
4
- require 'gorillib/receiver/acts_as_hash'
5
- require 'gorillib/receiver/acts_as_loadable'
6
- require 'gorillib/receiver/active_model_shim'
7
-
8
- module Gorillib
9
- module ReceiverModel
10
- def self.included base
11
- base.class_eval do
12
- include Receiver
13
- include Receiver::ActsAsHash
14
- include Receiver::ActsAsLoadable
15
- include Gorillib::Hashlike
16
- include Gorillib::Hashlike::TreeMerge
17
- include Receiver::ActiveModelShim
18
- end
19
- end
20
- end
21
- end
@@ -1,108 +0,0 @@
1
- module Gorillib
2
- module Struct
3
- #
4
- # Make a Struct behave mostly like a hash.
5
- #
6
- # By default, the hashlike methods iterate over the receiver attributes:
7
- # instance #keys delegates to self.class.keys which calls
8
- # receiver_attr_names. If you want to filter our add to the keys list, you
9
- # can just override the class-level keys method (and call super, or not):
10
- #
11
- # def self.keys
12
- # super + [:firstname, :lastname] - [:fullname]
13
- # end
14
- #
15
- # in addition to the below, by including Enumerable, this also adds
16
- #
17
- # :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
18
- # :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce,
19
- # :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop,
20
- # :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep,
21
- # :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by,
22
- # :minmax, :minmax_by, :sort, :sort_by
23
- #
24
- # As opposed to hash, does *not* define
25
- #
26
- # default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity
27
- # compare_by_identity? rehash
28
- #
29
- # @example
30
- # StructUsingHashlike = Struct.new(:a, :b, :c, :z) do
31
- # include Gorillib::Struct::ActsAsHash
32
- # include Gorillib::Hashlike
33
- # end
34
- # foo = StructUsingHashlike.new(1,2,3)
35
- # foo.to_hash # => { :a => 1, :b => 2, :c => 3, :z => nil }
36
- #
37
- module ActsAsHash
38
-
39
- # Hashlike#delete
40
- #
41
- # Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
42
- # optional code block is given and the key is not found, pass in the key and
43
- # return the result of +block+.
44
- #
45
- # In a normal hash, a default value can be set; none is provided here.
46
- #
47
- # @example
48
- # hsh = { :a => 100, :b => 200 }
49
- # hsh.delete(:a) # => 100
50
- # hsh.delete(:z) # => nil
51
- # hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
52
- #
53
- # @overload hsh.delete(key) -> val
54
- # @param key [Object] key to remove
55
- # @return [Object, Nil] the removed object, nil if missing
56
- #
57
- # @overload hsh.delete(key){|key| block } -> val
58
- # @param key [Object] key to remove
59
- # @yield [Object] called (with key) if key is missing
60
- # @yieldparam key
61
- # @return [Object, Nil] the removed object, or if missing, the return value
62
- # of the block
63
- #
64
- def delete(key, &block)
65
- if has_key?(key)
66
- val = self[key]
67
- self[key] = nil
68
- self.send(:remove_instance_variable, "@#{key}") if instance_variables.include?("@#{key}")
69
- val
70
- elsif block_given?
71
- block.call(key)
72
- else
73
- nil
74
- end
75
- end
76
-
77
- # Hashlike#keys
78
- #
79
- # Returns a new array populated with the keys from this hashlike.
80
- #
81
- # @see Hashlike#values.
82
- #
83
- # @example
84
- # hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
85
- # hsh.keys # => [:a, :b, :c, :d]
86
- #
87
- # @return [Array] list of keys
88
- #
89
- def keys
90
- @keys ||= members.map{|key| convert_key(key) }
91
- end
92
-
93
- def empty?
94
- keys.select{|key| not self[key].nil? }.empty?
95
- end
96
-
97
- def convert_key(key)
98
- return key if key.is_a?(Fixnum)
99
- key.respond_to?(:to_sym) ? key.to_sym : key
100
- end
101
-
102
- def size
103
- length
104
- end
105
-
106
- end
107
- end
108
- end