gorillib 0.1.11 → 0.4.0pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +1 -2
- data/.yardopts +9 -0
- data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
- data/Gemfile +21 -14
- data/Guardfile +19 -0
- data/{LICENSE.textile → LICENSE.md} +43 -29
- data/README.md +47 -52
- data/Rakefile +31 -30
- data/TODO.md +32 -0
- data/VERSION +1 -1
- data/examples/builder/ironfan.rb +133 -0
- data/examples/model/simple.rb +17 -0
- data/gorillib.gemspec +106 -86
- data/lib/alt/kernel/call_stack.rb +56 -0
- data/lib/gorillib/array/wrap.rb +53 -0
- data/lib/gorillib/base.rb +3 -3
- data/lib/gorillib/builder/field.rb +5 -0
- data/lib/gorillib/builder.rb +260 -0
- data/lib/gorillib/collection/has_collection.rb +31 -0
- data/lib/gorillib/collection.rb +129 -0
- data/lib/gorillib/configurable.rb +28 -0
- data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
- data/lib/gorillib/exception/confidence.rb +17 -0
- data/lib/gorillib/exception/raisers.rb +78 -0
- data/lib/gorillib/hash/mash.rb +202 -0
- data/lib/gorillib/hashlike/slice.rb +53 -19
- data/lib/gorillib/hashlike.rb +5 -3
- data/lib/gorillib/io/system_helpers.rb +30 -0
- data/lib/gorillib/logger/log.rb +18 -0
- data/lib/gorillib/metaprogramming/concern.rb +124 -0
- data/lib/gorillib/model/active_model_conversion.rb +68 -0
- data/lib/gorillib/model/active_model_naming.rb +87 -0
- data/lib/gorillib/model/active_model_shim.rb +33 -0
- data/lib/gorillib/model/base.rb +341 -0
- data/lib/gorillib/model/defaults.rb +71 -0
- data/lib/gorillib/model/errors.rb +14 -0
- data/lib/gorillib/model/factories.rb +372 -0
- data/lib/gorillib/model/field.rb +146 -0
- data/lib/gorillib/model/named_schema.rb +53 -0
- data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
- data/lib/gorillib/model/record_schema.rb +9 -0
- data/lib/gorillib/model/serialization.rb +23 -0
- data/lib/gorillib/model/validate.rb +22 -0
- data/lib/gorillib/model.rb +23 -0
- data/lib/gorillib/pathname.rb +78 -0
- data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
- data/lib/gorillib/some.rb +11 -9
- data/lib/gorillib/string/constantize.rb +21 -14
- data/lib/gorillib/string/inflections.rb +6 -76
- data/lib/gorillib/string/inflector.rb +192 -0
- data/lib/gorillib/string/simple_inflector.rb +267 -0
- data/lib/gorillib/type/extended.rb +52 -0
- data/lib/gorillib/utils/capture_output.rb +28 -0
- data/lib/gorillib/utils/console.rb +131 -0
- data/lib/gorillib/utils/nuke_constants.rb +9 -0
- data/lib/gorillib/utils/stub_module.rb +33 -0
- data/spec/examples/builder/ironfan_spec.rb +37 -0
- data/spec/extlib/hash_spec.rb +64 -0
- data/spec/extlib/mash_spec.rb +312 -0
- data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
- data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
- data/spec/gorillib/builder_spec.rb +187 -0
- data/spec/gorillib/collection_spec.rb +20 -0
- data/spec/gorillib/configurable_spec.rb +62 -0
- data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
- data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
- data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
- data/spec/gorillib/exception/raisers_spec.rb +60 -0
- data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
- data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
- data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
- data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
- data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
- data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
- data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
- data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
- data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
- data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
- data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
- data/spec/gorillib/model/record/defaults_spec.rb +108 -0
- data/spec/gorillib/model/record/factories_spec.rb +321 -0
- data/spec/gorillib/model/record/overlay_spec.rb +46 -0
- data/spec/gorillib/model/serialization_spec.rb +48 -0
- data/spec/gorillib/model_spec.rb +281 -0
- data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
- data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
- data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
- data/spec/{object → gorillib/object}/try_spec.rb +3 -2
- data/spec/gorillib/pathname_spec.rb +114 -0
- data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
- data/spec/{string → gorillib/string}/human_spec.rb +2 -2
- data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
- data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
- data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
- data/spec/gorillib/type/extended_spec.rb +120 -0
- data/spec/gorillib/utils/capture_output_spec.rb +71 -0
- data/spec/spec_helper.rb +8 -11
- data/spec/support/gorillib_test_helpers.rb +66 -0
- data/spec/support/hashlike_fuzzing_helper.rb +31 -33
- data/spec/support/hashlike_helper.rb +3 -3
- data/spec/support/model_test_helpers.rb +81 -0
- data/spec/support/shared_examples/included_module.rb +20 -0
- metadata +177 -158
- data/lib/gorillib/array/average.rb +0 -13
- data/lib/gorillib/array/sorted_median.rb +0 -11
- data/lib/gorillib/array/sorted_percentile.rb +0 -11
- data/lib/gorillib/array/sorted_sample.rb +0 -12
- data/lib/gorillib/dsl_object.rb +0 -64
- data/lib/gorillib/hash/indifferent_access.rb +0 -207
- data/lib/gorillib/hash/tree_merge.rb +0 -4
- data/lib/gorillib/hashlike/tree_merge.rb +0 -49
- data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
- data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
- data/lib/gorillib/receiver/active_model_shim.rb +0 -32
- data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
- data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
- data/lib/gorillib/receiver/locale/en.yml +0 -27
- data/lib/gorillib/receiver/tree_diff.rb +0 -74
- data/lib/gorillib/receiver/validations.rb +0 -30
- data/lib/gorillib/receiver.rb +0 -402
- data/lib/gorillib/receiver_model.rb +0 -21
- data/lib/gorillib/struct/acts_as_hash.rb +0 -108
- data/notes/fancy_hashes_and_receivers.textile +0 -120
- data/notes/hash_rdocs.textile +0 -97
- data/spec/array/average_spec.rb +0 -24
- data/spec/array/sorted_median_spec.rb +0 -18
- data/spec/array/sorted_percentile_spec.rb +0 -24
- data/spec/array/sorted_sample_spec.rb +0 -28
- data/spec/dsl_object_spec.rb +0 -99
- data/spec/hash/indifferent_access_spec.rb +0 -391
- data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
- data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
- data/spec/receiver/acts_as_hash_spec.rb +0 -295
- data/spec/receiver_spec.rb +0 -551
- data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
- data/spec/struct/acts_as_hash_spec.rb +0 -422
@@ -0,0 +1,202 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Convert to Mash. This class has semantics of ActiveSupport's
|
4
|
+
# HashWithIndifferentAccess and we only have it so that people can write
|
5
|
+
# params[:key] instead of params['key'].
|
6
|
+
#
|
7
|
+
# @return [Mash] This hash as a Mash for string or symbol key access.
|
8
|
+
def to_mash
|
9
|
+
hash = Mash.new(self)
|
10
|
+
hash.default = default
|
11
|
+
hash
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Create a hash with *only* key/value pairs in receiver and +allowed+
|
16
|
+
#
|
17
|
+
# { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 }
|
18
|
+
#
|
19
|
+
# @param allowed [Array[String, Symbol]] The hash keys to include.
|
20
|
+
#
|
21
|
+
# @return [Hash] A new hash with only the selected keys.
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def only(*allowed)
|
25
|
+
hash = {}
|
26
|
+
allowed.each {|k| hash[k] = self[k] if self.has_key?(k) }
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Create a hash with all key/value pairs in receiver *except* +rejected+
|
32
|
+
#
|
33
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
34
|
+
# #=> { :two => 2, :three => 3 }
|
35
|
+
#
|
36
|
+
# @param rejected [Array[String, Symbol]] The hash keys to exclude.
|
37
|
+
#
|
38
|
+
# @return [Hash] A new hash without the selected keys.
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def except(*rejected)
|
42
|
+
hash = self.dup
|
43
|
+
rejected.each {|k| hash.delete(k) }
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# This class has dubious semantics and we only have it so that people can write
|
49
|
+
# params[:key] instead of params['key'].
|
50
|
+
class Mash < Hash
|
51
|
+
|
52
|
+
# @param constructor [Object]
|
53
|
+
# The default value for the mash. Defaults to an empty hash.
|
54
|
+
#
|
55
|
+
# @overload [Alternatives]
|
56
|
+
# If constructor is a Hash, a new mash will be created based on the keys of
|
57
|
+
# the hash and no default value will be set.
|
58
|
+
def initialize(constructor = {})
|
59
|
+
if constructor.is_a?(Hash)
|
60
|
+
super()
|
61
|
+
update(constructor)
|
62
|
+
else
|
63
|
+
super(constructor)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param key<Object> The default value for the mash. Defaults to nil.
|
68
|
+
#
|
69
|
+
# @overload [Alternatives]
|
70
|
+
# If key is a Symbol and it is a key in the mash, then the default value will
|
71
|
+
# be set to the value matching the key.
|
72
|
+
def default(key = nil)
|
73
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
74
|
+
self[key]
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
81
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
82
|
+
|
83
|
+
# @param key<Object> The key to set.
|
84
|
+
# @param value<Object>
|
85
|
+
# The value to set the key to.
|
86
|
+
#
|
87
|
+
# @see Mash#convert_key
|
88
|
+
# @see Mash#convert_value
|
89
|
+
def []=(key, value)
|
90
|
+
regular_writer(convert_key(key), convert_value(value))
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param other_hash<Hash>
|
94
|
+
# A hash to update values in the mash with. The keys and the values will be
|
95
|
+
# converted to Mash format.
|
96
|
+
#
|
97
|
+
# @return [Mash] The updated mash.
|
98
|
+
def update(other_hash)
|
99
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :merge!, :update
|
104
|
+
|
105
|
+
# @param key<Object> The key to check for. This will be run through convert_key.
|
106
|
+
#
|
107
|
+
# @return [Boolean] True if the key exists in the mash.
|
108
|
+
def key?(key)
|
109
|
+
super(convert_key(key))
|
110
|
+
end
|
111
|
+
|
112
|
+
# def include? def has_key? def member?
|
113
|
+
alias_method :include?, :key?
|
114
|
+
alias_method :has_key?, :key?
|
115
|
+
alias_method :member?, :key?
|
116
|
+
|
117
|
+
# @param <Object> key The key to fetch. This will be run through convert_key.
|
118
|
+
# @param <Array> extras Default value.
|
119
|
+
#
|
120
|
+
# @return [Object] The value at key or the default value.
|
121
|
+
def fetch(key, *extras)
|
122
|
+
super(convert_key(key), *extras)
|
123
|
+
end
|
124
|
+
|
125
|
+
# @param indices [Array]
|
126
|
+
# The keys to retrieve values for. These will be run through +convert_key+.
|
127
|
+
#
|
128
|
+
# @return [Array] The values at each of the provided keys
|
129
|
+
def values_at(*indices)
|
130
|
+
indices.collect {|key| self[convert_key(key)]}
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param hash<Hash> The hash to merge with the mash.
|
134
|
+
#
|
135
|
+
# @return [Mash] A new mash with the hash values merged in.
|
136
|
+
def merge(hash)
|
137
|
+
self.dup.update(hash)
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param key<Object>
|
141
|
+
# The key to delete from the mash.\
|
142
|
+
def delete(key)
|
143
|
+
super(convert_key(key))
|
144
|
+
end
|
145
|
+
|
146
|
+
# @param [Array[(String, Symbol)]] rejected The mash keys to exclude.
|
147
|
+
#
|
148
|
+
# @return [Mash] A new mash without the selected keys.
|
149
|
+
#
|
150
|
+
# @example
|
151
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
152
|
+
# #=> { "two" => 2, "three" => 3 }
|
153
|
+
def except(*rejected)
|
154
|
+
super(*rejected.map {|k| convert_key(k)})
|
155
|
+
end
|
156
|
+
|
157
|
+
# Used to provide the same interface as Hash.
|
158
|
+
#
|
159
|
+
# @return [Mash] This mash unchanged.
|
160
|
+
def stringify_keys!; self end
|
161
|
+
|
162
|
+
# @return [Hash] The mash as a Hash with symbolized keys.
|
163
|
+
def symbolize_keys
|
164
|
+
h = Hash.new(default)
|
165
|
+
each { |key, val| h[key.to_sym] = val }
|
166
|
+
h
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [Hash] The mash as a Hash with string keys.
|
170
|
+
def to_hash
|
171
|
+
Hash.new(default).merge(self)
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
# @param key [Object] The key to convert.
|
176
|
+
#
|
177
|
+
# @return [Object]
|
178
|
+
# The converted key. If the key was a symbol, it will be converted to a
|
179
|
+
# string.
|
180
|
+
#
|
181
|
+
# @api private
|
182
|
+
def convert_key(key)
|
183
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
184
|
+
end
|
185
|
+
|
186
|
+
# @param value [Object] The value to convert.
|
187
|
+
#
|
188
|
+
# @return [Object]
|
189
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
190
|
+
# their Mash equivalents.
|
191
|
+
#
|
192
|
+
# @api private
|
193
|
+
def convert_value(value)
|
194
|
+
if value.class == Hash
|
195
|
+
value.to_mash
|
196
|
+
elsif value.is_a?(Array)
|
197
|
+
value.collect { |e| convert_value(e) }
|
198
|
+
else
|
199
|
+
value
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module Gorillib
|
2
2
|
module Hashlike
|
3
3
|
module Slice
|
4
|
-
# Slice a hash to include only the given allowed_keys.
|
5
|
-
# limiting an options hash to valid keys before passing to a method:
|
4
|
+
# Slice a hash to include only the given allowed_keys.
|
6
5
|
#
|
6
|
+
# @return the sliced hash
|
7
|
+
#
|
8
|
+
# @example limit an options hash to valid keys before passing to a method:
|
7
9
|
# def search(criteria = {})
|
8
10
|
# assert_valid_keys(:mass, :velocity, :time)
|
9
11
|
# end
|
10
|
-
#
|
11
12
|
# search(options.slice(:mass, :velocity, :time))
|
12
13
|
#
|
13
14
|
# If you have an array of keys you want to limit to, you should splat them:
|
@@ -21,8 +22,10 @@ module Gorillib
|
|
21
22
|
hash
|
22
23
|
end unless method_defined?(:slice)
|
23
24
|
|
24
|
-
#
|
25
|
-
#
|
25
|
+
# Replace the hash with only the given allowed_keys.
|
26
|
+
#
|
27
|
+
# @return a hash containing the removed key/value pairs
|
28
|
+
#
|
26
29
|
# @example
|
27
30
|
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
28
31
|
# hsh.slice!(:a, :b)
|
@@ -37,21 +40,9 @@ module Gorillib
|
|
37
40
|
omit
|
38
41
|
end unless method_defined?(:slice!)
|
39
42
|
|
40
|
-
# This also works, and doesn't require #replace method, but is uglier and
|
41
|
-
# wasn't written by Railsians. I'm not sure that slice! is interesting if
|
42
|
-
# you're a duck-typed Hash but not is_a?(Hash), so we'll just leave it at the
|
43
|
-
# active_record implementation.
|
44
|
-
#
|
45
|
-
# def slice!(*allowed_keys)
|
46
|
-
# allowed_keys = allowed_keys.map!{|key| convert_key(key) } if respond_to?(:convert_key)
|
47
|
-
# omit_keys = self.keys - allowed_keys
|
48
|
-
# omit = slice(*omit_keys)
|
49
|
-
# omit_keys.each{|k| delete(k) }
|
50
|
-
# omit
|
51
|
-
# end
|
52
|
-
|
53
43
|
# Removes the given allowed_keys from the hash
|
54
|
-
#
|
44
|
+
#
|
45
|
+
# @return a hash containing the removed key/value pairs
|
55
46
|
#
|
56
47
|
# @example
|
57
48
|
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
@@ -62,6 +53,49 @@ module Gorillib
|
|
62
53
|
def extract!(*allowed_keys)
|
63
54
|
slice!(*self.keys - allowed_keys)
|
64
55
|
end unless method_defined?(:extract!)
|
56
|
+
|
57
|
+
# Return a hash that includes everything but the given keys.
|
58
|
+
#
|
59
|
+
# @example Exclude a blacklist of parameters
|
60
|
+
# @person.update_attributes(params[:person].except(:admin))
|
61
|
+
#
|
62
|
+
# If the receiver responds to +convert_key+, the method is called on each of the
|
63
|
+
# arguments. This allows +except+ to play nice with hashes with indifferent access
|
64
|
+
# for instance:
|
65
|
+
#
|
66
|
+
# @example mash, hash, it does the right thing:
|
67
|
+
# {:a => 1}.to_mash.except(:a) # => {}
|
68
|
+
# {:a => 1}.to_mash.except('a') # => {}
|
69
|
+
#
|
70
|
+
def except(*keys)
|
71
|
+
dup.except!(*keys)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Replaces the hash without the given keys.
|
75
|
+
def except!(*keys)
|
76
|
+
keys.each{|key| delete(key) }
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def only(*allowed)
|
81
|
+
dup.only!(*allowed)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create a hash with *only* key/value pairs in receiver and +allowed+
|
85
|
+
#
|
86
|
+
# @example Limit a set of parameters to everything but a few known toggles:
|
87
|
+
# { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 }
|
88
|
+
#
|
89
|
+
# @param [Array[String, Symbol]] allowed The hash keys to include.
|
90
|
+
#
|
91
|
+
# @return [Hash] A new hash with only the selected keys.
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
def only!(*allowed)
|
95
|
+
allowed = allowed.to_set
|
96
|
+
select!{|key, val| allowed.include?(key) }
|
97
|
+
end
|
98
|
+
|
65
99
|
end
|
66
100
|
end
|
67
101
|
end
|
data/lib/gorillib/hashlike.rb
CHANGED
@@ -199,7 +199,7 @@ module Gorillib
|
|
199
199
|
# hsh.values_at(:c, :a, :c, :z, :a)
|
200
200
|
# # => [300, 100, 300, nil, 100]
|
201
201
|
#
|
202
|
-
# @param
|
202
|
+
# @param allowed_keys [Object] the keys to retrieve.
|
203
203
|
# @return [Array] the values, in order according to allowed_keys.
|
204
204
|
#
|
205
205
|
def values_at(*allowed_keys)
|
@@ -304,7 +304,7 @@ module Gorillib
|
|
304
304
|
# hsh.values_of(:c, :a, :c, :z, :a)
|
305
305
|
# # => [300, 100, 300, nil, 100]
|
306
306
|
#
|
307
|
-
# @param
|
307
|
+
# @param allowed_keys [Object] the keys to retrieve.
|
308
308
|
# @return [Array] the values, in order according to allowed_keys.
|
309
309
|
#
|
310
310
|
def values_of(*allowed_keys)
|
@@ -794,7 +794,9 @@ module Gorillib
|
|
794
794
|
# hsh.flatten(nil)
|
795
795
|
# # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
|
796
796
|
#
|
797
|
-
# @
|
797
|
+
# @overload flatten
|
798
|
+
# @overload flatten(level)
|
799
|
+
# @param level [Integer] the level of recursion to flatten, 0 by default.
|
798
800
|
# @return [Array] the flattened key-value array.
|
799
801
|
#
|
800
802
|
def flatten(*args)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Gorillib::CheckedPopen
|
2
|
+
module_function
|
3
|
+
|
4
|
+
def checked_popen(command, mode, fail_action, io_class=IO)
|
5
|
+
check_child_exit_status do
|
6
|
+
io_class.popen(command, mode) do |process|
|
7
|
+
yield(process)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rescue Errno::EPIPE
|
11
|
+
fail_action.call
|
12
|
+
end
|
13
|
+
|
14
|
+
# @private
|
15
|
+
NO_EXIT_STATUS = OpenStruct.new(:exitstatus => 0)
|
16
|
+
|
17
|
+
def check_child_exit_status
|
18
|
+
result = yield
|
19
|
+
status = $? || NO_EXIT_STATUS
|
20
|
+
unless [0, 172].include?(status.exitstatus)
|
21
|
+
raise ArgumentError, "Command exited with status '#{status.exitstatus}'"
|
22
|
+
end
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
::IO.class_eval do
|
29
|
+
include Gorillib::CheckedPopen
|
30
|
+
end
|
data/lib/gorillib/logger/log.rb
CHANGED
@@ -7,6 +7,24 @@ require 'logger'
|
|
7
7
|
#
|
8
8
|
::Log = Logger.new($stderr) unless defined?(::Log)
|
9
9
|
|
10
|
+
# unless defined?(Log)
|
11
|
+
# require 'log4r'
|
12
|
+
# Log = Log4r::Logger.new('wukong')
|
13
|
+
# Log.outputters = Log4r::Outputter.stderr
|
14
|
+
# # require 'logger'
|
15
|
+
# # Log = Logger.new(STDERR)
|
16
|
+
# end
|
17
|
+
|
18
|
+
# require 'log_buddy'; LogBuddy.init :log_to_stdout => false, :logger => Log
|
19
|
+
# LogBuddy::Utils.module_eval do
|
20
|
+
# def arg_and_blk_debug(arg, blk)
|
21
|
+
# result = eval(arg, blk.binding)
|
22
|
+
# result_str = obj_to_string(result, :quote_strings => true)
|
23
|
+
# LogBuddy.debug(%[#{arg} = #{result_str}])
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
|
27
|
+
|
10
28
|
def Log.dump *args
|
11
29
|
self.debug([
|
12
30
|
args.map(&:inspect),
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Gorillib
|
2
|
+
# A typical module looks like this:
|
3
|
+
#
|
4
|
+
# module M
|
5
|
+
# def self.included(base)
|
6
|
+
# base.extend ClassMethods
|
7
|
+
# scope :disabled, where(:disabled => true)
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# module ClassMethods
|
11
|
+
# ...
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# By using <tt>Gorillib::Concern</tt> the above module could instead be written as:
|
16
|
+
#
|
17
|
+
# require 'active_support/concern'
|
18
|
+
#
|
19
|
+
# module M
|
20
|
+
# extend Gorillib::Concern
|
21
|
+
#
|
22
|
+
# included do
|
23
|
+
# scope :disabled, where(:disabled => true)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# module ClassMethods
|
27
|
+
# ...
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
|
32
|
+
# module which depends on the former, we would typically write the following:
|
33
|
+
#
|
34
|
+
# module Foo
|
35
|
+
# def self.included(base)
|
36
|
+
# base.class_eval do
|
37
|
+
# def self.method_injected_by_foo
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# module Bar
|
45
|
+
# def self.included(base)
|
46
|
+
# base.method_injected_by_foo
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# class Host
|
51
|
+
# include Foo # We need to include this dependency for Bar
|
52
|
+
# include Bar # Bar is the module that Host really needs
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We could try to hide
|
56
|
+
# these from +Host+ directly including +Foo+ in +Bar+:
|
57
|
+
#
|
58
|
+
# module Bar
|
59
|
+
# include Foo
|
60
|
+
# def self.included(base)
|
61
|
+
# base.method_injected_by_foo
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# class Host
|
66
|
+
# include Bar
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt> is the +Bar+ module,
|
70
|
+
# not the +Host+ class. With <tt>Gorillib::Concern</tt>, module dependencies are properly resolved:
|
71
|
+
#
|
72
|
+
# require 'active_support/concern'
|
73
|
+
#
|
74
|
+
# module Foo
|
75
|
+
# extend Gorillib::Concern
|
76
|
+
# included do
|
77
|
+
# class_eval do
|
78
|
+
# def self.method_injected_by_foo
|
79
|
+
# ...
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# module Bar
|
86
|
+
# extend Gorillib::Concern
|
87
|
+
# include Foo
|
88
|
+
#
|
89
|
+
# included do
|
90
|
+
# self.method_injected_by_foo
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# class Host
|
95
|
+
# include Bar # works, Bar takes care now of its dependencies
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
module Concern
|
99
|
+
def self.extended(base)
|
100
|
+
base.instance_variable_set("@_dependencies", [])
|
101
|
+
end
|
102
|
+
|
103
|
+
def append_features(base)
|
104
|
+
if base.instance_variable_defined?("@_dependencies")
|
105
|
+
base.instance_variable_get("@_dependencies") << self
|
106
|
+
return false
|
107
|
+
else
|
108
|
+
return false if base < self
|
109
|
+
@_dependencies.each { |dep| base.send(:include, dep) }
|
110
|
+
super
|
111
|
+
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
|
112
|
+
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def included(base = nil, &block)
|
117
|
+
if base.nil?
|
118
|
+
@_included_block = block
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Gorillib::Model
|
2
|
+
# == Active Model Conversions
|
3
|
+
#
|
4
|
+
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
|
5
|
+
#
|
6
|
+
# Let's take for example this non-persisted object.
|
7
|
+
#
|
8
|
+
# class ContactMessage
|
9
|
+
# include ActiveModel::Conversion
|
10
|
+
#
|
11
|
+
# # ContactMessage are never persisted in the DB
|
12
|
+
# def persisted?
|
13
|
+
# false
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# cm = ContactMessage.new
|
18
|
+
# cm.to_model == self # => true
|
19
|
+
# cm.to_key # => nil
|
20
|
+
# cm.to_param # => nil
|
21
|
+
# cm.to_path # => "contact_messages/contact_message"
|
22
|
+
#
|
23
|
+
module Conversion
|
24
|
+
extend Gorillib::Concern
|
25
|
+
|
26
|
+
# If your object is already designed to implement all of the Active Model
|
27
|
+
# you can use the default <tt>:to_model</tt> implementation, which simply
|
28
|
+
# returns self.
|
29
|
+
#
|
30
|
+
# If your model does not act like an Active Model object, then you should
|
31
|
+
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
|
32
|
+
# your object with Active Model compliant methods.
|
33
|
+
def to_model
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an Enumerable of all key attributes if any is set, regardless
|
38
|
+
# if the object is persisted or not.
|
39
|
+
#
|
40
|
+
# Note the default implementation uses persisted? just because all objects
|
41
|
+
# in Ruby 1.8.x responds to <tt>:id</tt>.
|
42
|
+
def to_key
|
43
|
+
persisted? ? [id] : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns a string representing the object's key suitable for use in URLs,
|
47
|
+
# or nil if <tt>persisted?</tt> is false.
|
48
|
+
def to_param
|
49
|
+
persisted? ? to_key.join('-') : nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a string identifying the path associated with the object.
|
53
|
+
# ActionPack uses this to find a suitable partial to represent the object.
|
54
|
+
def to_partial_path
|
55
|
+
self.class._to_partial_path
|
56
|
+
end
|
57
|
+
|
58
|
+
module ClassMethods #:nodoc:
|
59
|
+
# Provide a class level cache for the to_path. This is an
|
60
|
+
# internal method and should not be accessed directly.
|
61
|
+
def _to_partial_path #:nodoc:
|
62
|
+
@_to_partial_path ||= begin
|
63
|
+
"#{model_name.collection}/#{model_name.element}".freeze
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Gorillib::Model
|
2
|
+
class Name < String
|
3
|
+
attr_accessor :namespace
|
4
|
+
attr_reader :singular, :element, :collection, :partial_path, :param_key, :i18n_key
|
5
|
+
|
6
|
+
alias_method :cache_key, :collection
|
7
|
+
|
8
|
+
class_attribute :inflector
|
9
|
+
self.inflector ||= Gorillib::String::Inflector
|
10
|
+
|
11
|
+
def initialize(klass, namespace = nil, name = nil)
|
12
|
+
name ||= klass.name
|
13
|
+
raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank?
|
14
|
+
|
15
|
+
super(name)
|
16
|
+
|
17
|
+
@klass = klass
|
18
|
+
@singular = _singularize(self).freeze
|
19
|
+
@element = inflector.underscore(inflector.demodulize(self)).freeze
|
20
|
+
@human = inflector.humanize(@element).freeze
|
21
|
+
@i18n_key = inflector.underscore(self).to_sym
|
22
|
+
#
|
23
|
+
self.namespace = namespace
|
24
|
+
self.plural = inflector.pluralize(@singular)
|
25
|
+
end
|
26
|
+
|
27
|
+
def plural=(str)
|
28
|
+
@plural = str.dup.freeze
|
29
|
+
@collection = inflector.underscore(@plural).freeze
|
30
|
+
@partial_path = "#{@collection}/#{@element}".freeze
|
31
|
+
str
|
32
|
+
end
|
33
|
+
|
34
|
+
def namespace=(ns)
|
35
|
+
if ns.present?
|
36
|
+
@unnamespaced = self.sub(/^#{ns.name}::/, '')
|
37
|
+
@param_key = _singularize(@unnamespaced).freeze
|
38
|
+
else
|
39
|
+
@unnamespaced = nil
|
40
|
+
@param_key = @singular.freeze
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def singular_route_key
|
45
|
+
inflector.singularize(route_key).freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
def route_key
|
49
|
+
rk = (namespace ? inflector.pluralize(param_key) : plural.dup)
|
50
|
+
rk << "_index" if plural == singular
|
51
|
+
rk.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def _singularize(string, replacement='_')
|
57
|
+
inflector.underscore(string).tr('/', replacement)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# == Active Model Naming
|
62
|
+
#
|
63
|
+
# Creates a +model_name+ method on your object.
|
64
|
+
#
|
65
|
+
# To implement, just extend ActiveModel::Naming in your object:
|
66
|
+
#
|
67
|
+
# class BookCover
|
68
|
+
# extend ActiveModel::Naming
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# BookCover.model_name # => "BookCover"
|
72
|
+
# BookCover.model_name.human # => "Book cover"
|
73
|
+
#
|
74
|
+
# BookCover.model_name.i18n_key # => :book_cover
|
75
|
+
# BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
|
76
|
+
#
|
77
|
+
# Providing the functionality that ActiveModel::Naming provides in your object
|
78
|
+
# is required to pass the Active Model Lint test. So either extending the provided
|
79
|
+
# method below, or rolling your own is required.
|
80
|
+
module Naming
|
81
|
+
# Returns a Name object for module, which can be used to retrieve all kinds
|
82
|
+
# of naming-related information.
|
83
|
+
def model_name
|
84
|
+
@_model_name ||= Gorillib::Model::Name.new(self, namespace)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "active_model"
|
2
|
+
|
3
|
+
module Gorillib
|
4
|
+
module Model
|
5
|
+
|
6
|
+
# Provides the minimum functionality to pass the ActiveModel lint tests
|
7
|
+
#
|
8
|
+
# @example Usage
|
9
|
+
# class Person
|
10
|
+
# include Gorillib::Model::ActiveModelShim
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
module ActiveModelShim
|
14
|
+
extend Gorillib::Concern
|
15
|
+
extend ActiveModel::Naming
|
16
|
+
include Gorillib::Model::Conversion
|
17
|
+
include ActiveModel::Validations
|
18
|
+
|
19
|
+
# @return [false]
|
20
|
+
def persisted?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def attribute_method?(attr_name)
|
25
|
+
self.class.has_field?(attr_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
end # ActiveModelShim::ClassMethods
|
30
|
+
end # ActiveModelShim
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|