gorillib 0.1.11 → 0.4.0pre

Sign up to get free protection for your applications and to get access to all the features.
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,207 +0,0 @@
1
- require 'gorillib/hash/keys'
2
-
3
- # This class has dubious semantics and we only have it so that
4
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
5
- # and they get the same value for both keys.
6
-
7
- module Gorillib
8
- class HashWithIndifferentAccess < Hash
9
- def extractable_options?
10
- true
11
- end
12
-
13
- def with_indifferent_access
14
- dup
15
- end
16
-
17
- def initialize(constructor = {})
18
- if constructor.is_a?(Hash)
19
- super()
20
- update(constructor)
21
- else
22
- super(constructor)
23
- end
24
- end
25
-
26
- def default(key = nil)
27
- if include?(converted = convert_key(key))
28
- self[converted]
29
- else
30
- super
31
- end
32
- end
33
-
34
- def self.new_from_hash_copying_default(hash)
35
- new(hash).tap do |new_hash|
36
- new_hash.default = hash.default
37
- end
38
- end
39
-
40
- alias_method(:regular_writer, :[]=) unless method_defined?(:regular_writer)
41
- alias_method :regular_update, :update unless method_defined?(:regular_update)
42
-
43
- # Assigns a new value to the hash:
44
- #
45
- # hash = HashWithIndifferentAccess.new
46
- # hash[:key] = "value"
47
- #
48
- def []=(key, value)
49
- regular_writer(convert_key(key), convert_value(value))
50
- end
51
-
52
- alias_method :store, :[]=
53
-
54
- # Updates the instantized hash with values from the second:
55
- #
56
- # hash_1 = HashWithIndifferentAccess.new
57
- # hash_1[:key] = "value"
58
- #
59
- # hash_2 = HashWithIndifferentAccess.new
60
- # hash_2[:key] = "New Value!"
61
- #
62
- # hash_1.update(hash_2) # => {"key"=>"New Value!"}
63
- #
64
- def update(other_hash)
65
- raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair)
66
- if other_hash.is_a? HashWithIndifferentAccess
67
- super(other_hash)
68
- else
69
- other_hash.each_pair{|key, value| regular_writer(convert_key(key), convert_value(value)) }
70
- self
71
- end
72
- end
73
-
74
- alias_method :merge!, :update
75
-
76
- # Checks the hash for a key matching the argument passed in:
77
- #
78
- # hash = HashWithIndifferentAccess.new
79
- # hash["key"] = "value"
80
- # hash.key? :key # => true
81
- # hash.key? "key" # => true
82
- #
83
- def key?(key)
84
- super(convert_key(key))
85
- end
86
-
87
- alias_method :include?, :key?
88
- alias_method :has_key?, :key?
89
- alias_method :member?, :key?
90
-
91
- # Fetches the value for the specified key, same as doing hash[key]
92
- def fetch(key, *extras)
93
- super(convert_key(key), *extras)
94
- end
95
-
96
- # Returns an array of the values at the specified indices:
97
- #
98
- # hash = HashWithIndifferentAccess.new
99
- # hash[:a] = "x"
100
- # hash[:b] = "y"
101
- # hash.values_at("a", "b") # => ["x", "y"]
102
- #
103
- def values_at(*indices)
104
- indices.collect {|key| self[convert_key(key)]}
105
- end
106
-
107
- # Returns an exact copy of the hash.
108
- def dup
109
- self.class.new(self).tap do |new_hash|
110
- new_hash.default = default
111
- end
112
- end
113
-
114
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
115
- # Does not overwrite the existing hash.
116
- def merge(hash)
117
- self.dup.update(hash)
118
- end
119
-
120
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
121
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
122
- def reverse_merge(other_hash)
123
- super self.class.new_from_hash_copying_default(other_hash)
124
- end
125
-
126
- def reverse_merge!(other_hash)
127
- replace(reverse_merge( other_hash ))
128
- end
129
-
130
- # Removes a specified key from the hash.
131
- def delete(key)
132
- super(convert_key(key))
133
- end
134
-
135
- def stringify_keys!; self end
136
- def stringify_keys; dup end
137
- undef_method :symbolize_keys! if method_defined?(:symbolize_keys!)
138
- def symbolize_keys; to_hash.symbolize_keys end
139
- def to_options!; self end
140
-
141
- # Convert to a Hash with String keys.
142
- def to_hash
143
- Hash.new(default).merge!(self)
144
- end
145
-
146
- def assoc key
147
- key = convert_key(key)
148
- return unless has_key?(key)
149
- [key, self[key]]
150
- end
151
-
152
- def rassoc val
153
- key = key(val) or return
154
- [key, self[key]]
155
- end
156
-
157
- protected
158
- def convert_key(key)
159
- key.kind_of?(Symbol) ? key.to_s : key
160
- end
161
-
162
- def convert_value(value)
163
- if value.is_a? Hash
164
- value.nested_under_indifferent_access
165
- elsif value.is_a?(Array)
166
- value.dup.replace(value.map{|e| convert_value(e) })
167
- else
168
- value
169
- end
170
- end
171
- end
172
-
173
- end
174
-
175
- module Gorillib
176
- class HashWithIndifferentSymbolKeys < Gorillib::HashWithIndifferentAccess
177
-
178
- def convert_key key
179
- return key if key.is_a?(Fixnum)
180
- key.respond_to?(:to_sym) ? key.to_sym : key
181
- end
182
- end
183
- end
184
-
185
- class Hash
186
-
187
- # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
188
- #
189
- # {:a => 1}.with_indifferent_access["a"] # => 1
190
- #
191
- def with_indifferent_access
192
- Gorillib::HashWithIndifferentAccess.new_from_hash_copying_default(self)
193
- end
194
-
195
- # Called when object is nested under an object that receives
196
- # #with_indifferent_access. This method with be called on the current object
197
- # by the enclosing object and is aliased to #with_indifferent_access by
198
- # default. Subclasses of Hash may overwrite this method to return +self+ if
199
- # converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
200
- # desirable.
201
- #
202
- # b = {:b => 1}
203
- # {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
204
- #
205
- alias nested_under_indifferent_access with_indifferent_access
206
- end
207
-
@@ -1,4 +0,0 @@
1
- require 'gorillib/hashlike/tree_merge'
2
- class Hash
3
- include Gorillib::Hashlike::TreeMerge
4
- end
@@ -1,49 +0,0 @@
1
- module Gorillib
2
- module Hashlike
3
- module TreeMerge
4
-
5
- # Recursively merges hashlike objects
6
- #
7
- # For each key in keys,
8
- # * if block_given? and yield(key,self_val,other_val) returns non-nil, set that
9
- # * if self is missing value for key, receive the attribute.
10
- # * if self's attribute is an Array, append to it.
11
- # * if self's value responds to tree_merge!, deep merge it.
12
- # * if self's value responds_to merge!, merge! it.
13
- # * otherwise, receive the value from other_hash
14
- #
15
- def tree_merge!(other_hash)
16
- return self if other_hash.blank?
17
- [self.keys, other_hash.keys].flatten.uniq.each do |key|
18
- # get other's val if any
19
- if other_hash.has_key?(key.to_sym) then other_val = other_hash[key.to_sym]
20
- elsif other_hash.has_key?(key.to_s) then other_val = other_hash[key.to_s]
21
- else next ; end
22
- # get self val if any
23
- self_val = self[key]
24
- # get block resolved result if any
25
- if block_given? && yield(key, self_val, other_val)
26
- next
27
- end
28
- # p ['hash tree_merge', key, self_val.respond_to?(:tree_merge!), self_val, '***************', other_val]
29
- #
30
- case
31
- when other_val.nil? then next
32
- when (not has_key?(key)) then self[key] = other_val
33
- when self_val.is_a?(Array) then self[key] += other_val
34
- when self_val.respond_to?(:tree_merge!) then self[key] = self_val.tree_merge!(other_val)
35
- when self_val.respond_to?(:merge!) then self[key] = self_val.merge!(other_val)
36
- else self[key] = other_val
37
- end
38
- end
39
- self
40
- end
41
-
42
- end
43
- end
44
- end
45
-
46
-
47
- class Hash
48
- include Gorillib::Hashlike::TreeMerge
49
- end
@@ -1,79 +0,0 @@
1
- require 'gorillib/array/extract_options'
2
-
3
- # Extends the class object with class and instance accessors for class attributes,
4
- # just like the native attr* accessors for instance attributes.
5
- #
6
- # Note that unlike +class_attribute+, if a subclass changes the value then that would
7
- # also change the value for parent class. Similarly if parent class changes the value
8
- # then that would change the value of subclasses too.
9
- #
10
- # class Person
11
- # cattr_accessor :hair_colors
12
- # end
13
- #
14
- # Person.hair_colors = [:brown, :black, :blonde, :red]
15
- # Person.hair_colors # => [:brown, :black, :blonde, :red]
16
- # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
17
- #
18
- # To opt out of the instance writer method, pass :instance_writer => false.
19
- # To opt out of the instance reader method, pass :instance_reader => false.
20
- #
21
- # class Person
22
- # cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false
23
- # end
24
- #
25
- # Person.new.hair_colors = [:brown] # => NoMethodError
26
- # Person.new.hair_colors # => NoMethodError
27
- class Class
28
- def cattr_reader(*syms)
29
- options = syms.extract_options!
30
- syms.each do |sym|
31
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
32
- unless defined? @@#{sym}
33
- @@#{sym} = nil
34
- end
35
-
36
- def self.#{sym}
37
- @@#{sym}
38
- end
39
- EOS
40
-
41
- unless options[:instance_reader] == false
42
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
- def #{sym}
44
- @@#{sym}
45
- end
46
- EOS
47
- end
48
- end
49
- end unless method_defined?(:cattr_reader)
50
-
51
- def cattr_writer(*syms)
52
- options = syms.extract_options!
53
- syms.each do |sym|
54
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
- unless defined? @@#{sym}
56
- @@#{sym} = nil
57
- end
58
-
59
- def self.#{sym}=(obj)
60
- @@#{sym} = obj
61
- end
62
- EOS
63
-
64
- unless options[:instance_writer] == false
65
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
66
- def #{sym}=(obj)
67
- @@#{sym} = obj
68
- end
69
- EOS
70
- end
71
- self.send("#{sym}=", yield) if block_given?
72
- end
73
- end unless method_defined?(:cattr_writer)
74
-
75
- def cattr_accessor(*syms, &blk)
76
- cattr_reader(*syms)
77
- cattr_writer(*syms, &blk)
78
- end unless method_defined?(:cattr_accessor)
79
- end
@@ -1,61 +0,0 @@
1
- require 'gorillib/array/extract_options'
2
-
3
- class Module
4
- def mattr_reader(*syms)
5
- options = syms.extract_options!
6
- syms.each do |sym|
7
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
8
- @@#{sym} = nil unless defined? @@#{sym}
9
-
10
- def self.#{sym}
11
- @@#{sym}
12
- end
13
- EOS
14
-
15
- unless options[:instance_reader] == false
16
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
17
- def #{sym}
18
- @@#{sym}
19
- end
20
- EOS
21
- end
22
- end
23
- end unless method_defined?(:mattr_reader)
24
-
25
- def mattr_writer(*syms)
26
- options = syms.extract_options!
27
- syms.each do |sym|
28
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
29
- def self.#{sym}=(obj)
30
- @@#{sym} = obj
31
- end
32
- EOS
33
-
34
- unless options[:instance_writer] == false
35
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
36
- def #{sym}=(obj)
37
- @@#{sym} = obj
38
- end
39
- EOS
40
- end
41
- end
42
- end unless method_defined?(:mattr_writer)
43
-
44
- # Extends the module object with module and instance accessors for class attributes,
45
- # just like the native attr* accessors for instance attributes.
46
- #
47
- # module AppConfiguration
48
- # mattr_accessor :google_api_key
49
- # self.google_api_key = "123456789"
50
- #
51
- # mattr_accessor :paypal_url
52
- # self.paypal_url = "www.sandbox.paypal.com"
53
- # end
54
- #
55
- # AppConfiguration.google_api_key = "overriding the api key!"
56
- def mattr_accessor(*syms)
57
- mattr_reader(*syms)
58
- mattr_writer(*syms)
59
- end unless method_defined?(:mattr_accessor)
60
-
61
- end
@@ -1,32 +0,0 @@
1
- # require 'active_model'
2
-
3
- require 'active_model/deprecated_error_methods'
4
- require 'active_model/errors'
5
- require 'active_model/naming'
6
- require 'active_model/validator'
7
- require 'active_model/translation'
8
- require 'active_model/validations'
9
- require 'active_support/i18n'
10
- I18n.load_path << File.join(File.expand_path(File.dirname(__FILE__)), 'locale/en.yml')
11
-
12
- module Receiver
13
- module ActiveModelShim
14
-
15
- def to_model
16
- self
17
- end
18
-
19
- def new_record?() true end
20
- def destroyed?() false end
21
- def errors
22
- @_errors ||= ActiveModel::Errors.new(self)
23
- end
24
-
25
- def self.included(base)
26
- base.class_eval do
27
- extend ActiveModel::Naming
28
- include ActiveModel::Validations
29
- end
30
- end
31
- end
32
- end
@@ -1,195 +0,0 @@
1
- require 'gorillib/hashlike'
2
-
3
- module Receiver
4
- #
5
- # Makes a Receiver thingie behave mostly like a hash.
6
- #
7
- # By default, the hashlike methods iterate over the receiver attributes:
8
- # instance #keys delegates to self.class.keys which calls
9
- # receiver_attr_names. If you want to filter our add to the keys list, you
10
- # can just override the class-level keys method (and call super, or not):
11
- #
12
- # def self.keys
13
- # super + [:firstname, :lastname] - [:fullname]
14
- # end
15
- #
16
- # All methods are defined naturally on [], []= and has_key? -- if you enjoy
17
- #
18
- #
19
- # in addition to the below, by including Enumerable, this also adds
20
- #
21
- # :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
22
- # :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce,
23
- # :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop,
24
- # :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep,
25
- # :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by,
26
- # :minmax, :minmax_by, :sort, :sort_by
27
- #
28
- # As opposed to hash, does *not* define
29
- #
30
- # default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity
31
- # compare_by_identity? rehash
32
- #
33
- module ActsAsHash
34
-
35
- module InstanceMethods
36
-
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
57
-
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)
86
- end
87
-
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
163
-
164
- end
165
-
166
- module ClassMethods
167
- # By default, the hashlike methods iterate over the receiver attributes.
168
- # If you want to filter our add to the keys list, override this method
169
- #
170
- # @example
171
- # def self.keys
172
- # super + [:firstname, :lastname] - [:fullname]
173
- # end
174
- #
175
- def keys
176
- receiver_attr_names
177
- end
178
- end
179
-
180
- protected
181
-
182
- def convert_key(key)
183
- raise ArgumentError, "Keys for #{self.class} must be symbols, strings or respond to #to_sym" unless key.respond_to?(:to_sym)
184
- key.to_sym
185
- end
186
-
187
- def self.included base
188
- base.class_eval do
189
- include Gorillib::Hashlike
190
- extend ClassMethods
191
- include InstanceMethods
192
- end
193
- end
194
- end
195
- end
@@ -1,42 +0,0 @@
1
- module Receiver
2
- #
3
- # adds methods to load and store from json, yaml or magic
4
- #
5
- # This will require 'json' UNLESS you have already included something (so if
6
- # you want to say require 'yajl' then do that first).
7
- #
8
- module ActsAsLoadable
9
-
10
- module ClassMethods
11
- def receive_json stream
12
- receive(JSON.load(stream))
13
- end
14
-
15
- def receive_yaml stream
16
- receive(YAML.load(stream))
17
- end
18
-
19
- #
20
- # The file is loaded with
21
- # * YAML if the filename ends in .yaml or .yml
22
- # * JSON otherwise
23
- #
24
- def receive_from_file filename
25
- stream = File.open(filename)
26
- (filename =~ /.ya?ml$/) ? receive_yaml(stream) : receive_json(stream)
27
- end
28
- end
29
-
30
- def merge_from_file! filename
31
- other_obj = self.class.receive_from_file(filename)
32
- tree_merge! other_obj
33
- end
34
-
35
- # put all the things in ClassMethods at class level
36
- def self.included base
37
- require 'yaml'
38
- require 'json' unless defined?(JSON)
39
- base.extend ClassMethods
40
- end
41
- end
42
- end
@@ -1,27 +0,0 @@
1
- en:
2
- errors:
3
- # The default format to use in full error messages.
4
- format: "%{attribute} %{message}"
5
-
6
- # The values :model, :attribute and :value are always available for interpolation
7
- # The value :count is available when applicable. Can be used for pluralization.
8
- messages:
9
- inclusion: "is not included in the list"
10
- exclusion: "is reserved"
11
- invalid: "is invalid"
12
- confirmation: "doesn't match confirmation"
13
- accepted: "must be accepted"
14
- empty: "can't be empty"
15
- blank: "can't be blank"
16
- too_long: "is too long (maximum is %{count} characters)"
17
- too_short: "is too short (minimum is %{count} characters)"
18
- wrong_length: "is the wrong length (should be %{count} characters)"
19
- not_a_number: "is not a number"
20
- not_an_integer: "must be an integer"
21
- greater_than: "must be greater than %{count}"
22
- greater_than_or_equal_to: "must be greater than or equal to %{count}"
23
- equal_to: "must be equal to %{count}"
24
- less_than: "must be less than %{count}"
25
- less_than_or_equal_to: "must be less than or equal to %{count}"
26
- odd: "must be odd"
27
- even: "must be even"