garcun 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitattributes +17 -0
- data/.gitignore +197 -0
- data/.rspec +2 -0
- data/Gemfile +22 -0
- data/LICENSE +201 -0
- data/README.md +521 -0
- data/Rakefile +47 -0
- data/garcun.gemspec +83 -0
- data/lib/garcon.rb +290 -0
- data/lib/garcon/chef/chef_helpers.rb +343 -0
- data/lib/garcon/chef/coerce/coercer.rb +134 -0
- data/lib/garcon/chef/coerce/coercions/boolean_definitions.rb +34 -0
- data/lib/garcon/chef/coerce/coercions/date_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/date_time_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/fixnum_definitions.rb +34 -0
- data/lib/garcon/chef/coerce/coercions/float_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/hash_definitions.rb +29 -0
- data/lib/garcon/chef/coerce/coercions/integer_definitions.rb +31 -0
- data/lib/garcon/chef/coerce/coercions/string_definitions.rb +45 -0
- data/lib/garcon/chef/coerce/coercions/time_definitions.rb +32 -0
- data/lib/garcon/chef/handler/devreporter.rb +127 -0
- data/lib/garcon/chef/log.rb +64 -0
- data/lib/garcon/chef/node.rb +100 -0
- data/lib/garcon/chef/provider/civilize.rb +209 -0
- data/lib/garcon/chef/provider/development.rb +159 -0
- data/lib/garcon/chef/provider/download.rb +420 -0
- data/lib/garcon/chef/provider/house_keeping.rb +265 -0
- data/lib/garcon/chef/provider/node_cache.rb +31 -0
- data/lib/garcon/chef/provider/partial.rb +183 -0
- data/lib/garcon/chef/provider/recovery.rb +80 -0
- data/lib/garcon/chef/provider/zip_file.rb +271 -0
- data/lib/garcon/chef/resource/attribute.rb +52 -0
- data/lib/garcon/chef/resource/base_dsl.rb +174 -0
- data/lib/garcon/chef/resource/blender.rb +140 -0
- data/lib/garcon/chef/resource/lazy_eval.rb +66 -0
- data/lib/garcon/chef/resource/resource_name.rb +109 -0
- data/lib/garcon/chef/secret_bag.rb +204 -0
- data/lib/garcon/chef/validations.rb +76 -0
- data/lib/garcon/chef_inclusions.rb +151 -0
- data/lib/garcon/configuration.rb +138 -0
- data/lib/garcon/core_ext.rb +39 -0
- data/lib/garcon/core_ext/array.rb +27 -0
- data/lib/garcon/core_ext/binding.rb +64 -0
- data/lib/garcon/core_ext/boolean.rb +66 -0
- data/lib/garcon/core_ext/duration.rb +271 -0
- data/lib/garcon/core_ext/enumerable.rb +34 -0
- data/lib/garcon/core_ext/file.rb +127 -0
- data/lib/garcon/core_ext/filetest.rb +62 -0
- data/lib/garcon/core_ext/hash.rb +279 -0
- data/lib/garcon/core_ext/kernel.rb +159 -0
- data/lib/garcon/core_ext/lazy.rb +222 -0
- data/lib/garcon/core_ext/method_access.rb +243 -0
- data/lib/garcon/core_ext/module.rb +92 -0
- data/lib/garcon/core_ext/nil.rb +53 -0
- data/lib/garcon/core_ext/numeric.rb +44 -0
- data/lib/garcon/core_ext/object.rb +342 -0
- data/lib/garcon/core_ext/pathname.rb +152 -0
- data/lib/garcon/core_ext/process.rb +41 -0
- data/lib/garcon/core_ext/random.rb +497 -0
- data/lib/garcon/core_ext/string.rb +312 -0
- data/lib/garcon/core_ext/struct.rb +49 -0
- data/lib/garcon/core_ext/symbol.rb +170 -0
- data/lib/garcon/core_ext/time.rb +234 -0
- data/lib/garcon/exceptions.rb +101 -0
- data/lib/garcon/inflections.rb +237 -0
- data/lib/garcon/inflections/defaults.rb +79 -0
- data/lib/garcon/inflections/inflections.rb +182 -0
- data/lib/garcon/inflections/rules_collection.rb +37 -0
- data/lib/garcon/secret.rb +271 -0
- data/lib/garcon/stash/format.rb +114 -0
- data/lib/garcon/stash/journal.rb +226 -0
- data/lib/garcon/stash/queue.rb +83 -0
- data/lib/garcon/stash/serializer.rb +86 -0
- data/lib/garcon/stash/store.rb +435 -0
- data/lib/garcon/task.rb +31 -0
- data/lib/garcon/task/atomic.rb +151 -0
- data/lib/garcon/task/atomic_boolean.rb +127 -0
- data/lib/garcon/task/condition.rb +99 -0
- data/lib/garcon/task/copy_on_notify_observer_set.rb +154 -0
- data/lib/garcon/task/copy_on_write_observer_set.rb +153 -0
- data/lib/garcon/task/count_down_latch.rb +92 -0
- data/lib/garcon/task/delay.rb +196 -0
- data/lib/garcon/task/dereferenceable.rb +144 -0
- data/lib/garcon/task/event.rb +119 -0
- data/lib/garcon/task/executor.rb +275 -0
- data/lib/garcon/task/executor_options.rb +59 -0
- data/lib/garcon/task/future.rb +107 -0
- data/lib/garcon/task/immediate_executor.rb +84 -0
- data/lib/garcon/task/ivar.rb +171 -0
- data/lib/garcon/task/lazy_reference.rb +74 -0
- data/lib/garcon/task/monotonic_time.rb +69 -0
- data/lib/garcon/task/obligation.rb +256 -0
- data/lib/garcon/task/observable.rb +101 -0
- data/lib/garcon/task/priority_queue.rb +234 -0
- data/lib/garcon/task/processor_count.rb +128 -0
- data/lib/garcon/task/read_write_lock.rb +304 -0
- data/lib/garcon/task/safe_task_executor.rb +58 -0
- data/lib/garcon/task/single_thread_executor.rb +97 -0
- data/lib/garcon/task/thread_pool/cached.rb +71 -0
- data/lib/garcon/task/thread_pool/executor.rb +294 -0
- data/lib/garcon/task/thread_pool/fixed.rb +61 -0
- data/lib/garcon/task/thread_pool/worker.rb +90 -0
- data/lib/garcon/task/timer.rb +44 -0
- data/lib/garcon/task/timer_set.rb +194 -0
- data/lib/garcon/task/timer_task.rb +377 -0
- data/lib/garcon/task/waitable_list.rb +58 -0
- data/lib/garcon/utility/ansi.rb +199 -0
- data/lib/garcon/utility/at_random.rb +77 -0
- data/lib/garcon/utility/crypto.rb +292 -0
- data/lib/garcon/utility/equalizer.rb +146 -0
- data/lib/garcon/utility/faker/extensions/array.rb +22 -0
- data/lib/garcon/utility/faker/extensions/symbol.rb +9 -0
- data/lib/garcon/utility/faker/faker.rb +164 -0
- data/lib/garcon/utility/faker/faker/company.rb +17 -0
- data/lib/garcon/utility/faker/faker/hacker.rb +30 -0
- data/lib/garcon/utility/faker/faker/version.rb +3 -0
- data/lib/garcon/utility/faker/locales/en-US.yml +83 -0
- data/lib/garcon/utility/faker/locales/en.yml +21 -0
- data/lib/garcon/utility/file_helper.rb +170 -0
- data/lib/garcon/utility/hookers.rb +178 -0
- data/lib/garcon/utility/interpolation.rb +90 -0
- data/lib/garcon/utility/memstash.rb +364 -0
- data/lib/garcon/utility/misc.rb +54 -0
- data/lib/garcon/utility/msg_from_god.rb +62 -0
- data/lib/garcon/utility/retry.rb +238 -0
- data/lib/garcon/utility/timeout.rb +58 -0
- data/lib/garcon/utility/uber/builder.rb +91 -0
- data/lib/garcon/utility/uber/callable.rb +7 -0
- data/lib/garcon/utility/uber/delegates.rb +13 -0
- data/lib/garcon/utility/uber/inheritable_attr.rb +37 -0
- data/lib/garcon/utility/uber/options.rb +101 -0
- data/lib/garcon/utility/uber/uber_version.rb +3 -0
- data/lib/garcon/utility/uber/version.rb +33 -0
- data/lib/garcon/utility/url_helper.rb +100 -0
- data/lib/garcon/utils.rb +29 -0
- data/lib/garcon/version.rb +62 -0
- data/lib/garcun.rb +24 -0
- metadata +680 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
|
4
|
+
# License: Apache License, Version 2.0
|
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
# Lazy ass Ruby.
|
|
21
|
+
#
|
|
22
|
+
module Lazy
|
|
23
|
+
# Raised when a demanded computation diverges (e.g. if it tries to directly
|
|
24
|
+
# use its own result)
|
|
25
|
+
#
|
|
26
|
+
class DivergenceError < Exception
|
|
27
|
+
def initialize(message = 'Computation diverges')
|
|
28
|
+
super(message)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Wraps an exception raised by a lazy computation.
|
|
33
|
+
#
|
|
34
|
+
# The reason we wrap such exceptions in LazyException is that they need to
|
|
35
|
+
# be distinguishable from similar exceptions which might normally be raised
|
|
36
|
+
# by whatever strict code we happen to be in at the time.
|
|
37
|
+
#
|
|
38
|
+
class LazyException < DivergenceError
|
|
39
|
+
attr_reader :reason
|
|
40
|
+
|
|
41
|
+
def initialize(reason)
|
|
42
|
+
@reason = reason
|
|
43
|
+
super "Exception in lazy computation: #{reason} (#{reason.class})"
|
|
44
|
+
set_backtrace(reason.backtrace.dup) if reason
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A handle for a promised computation. They are transparent, so that in
|
|
49
|
+
# most cases, a promise can be used as a proxy for the computation's result
|
|
50
|
+
# object. The one exception is truth testing -- a promise will always look
|
|
51
|
+
# true to Ruby, even if the actual result object is nil or false.
|
|
52
|
+
#
|
|
53
|
+
# If you want to test the result for truth, get the unwrapped result object
|
|
54
|
+
# via Kernel.demand.
|
|
55
|
+
#
|
|
56
|
+
class Promise
|
|
57
|
+
alias __class__ class
|
|
58
|
+
instance_methods.each do |method|
|
|
59
|
+
undef_method method unless method =~ /^(__|object_|instance_)/
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize(&computation)
|
|
63
|
+
@computation = computation
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def __synchronize__
|
|
67
|
+
yield
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Create this once here, rather than creating a proc object for every
|
|
71
|
+
# evaluation.
|
|
72
|
+
DIVERGES = lambda { raise DivergenceError.new }
|
|
73
|
+
|
|
74
|
+
# Differentiate inspection of DIVERGES lambda.
|
|
75
|
+
def DIVERGES.inspect
|
|
76
|
+
'DIVERGES'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def __result__
|
|
80
|
+
__synchronize__ do
|
|
81
|
+
if @computation
|
|
82
|
+
raise LazyException.new(@exception) if @exception
|
|
83
|
+
|
|
84
|
+
computation = @computation
|
|
85
|
+
@computation = DIVERGES
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
@result = demand(computation.call(self))
|
|
89
|
+
@computation = nil
|
|
90
|
+
rescue DivergenceError
|
|
91
|
+
raise
|
|
92
|
+
rescue Exception => e
|
|
93
|
+
@exception = e
|
|
94
|
+
raise LazyException.new(@exception)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@result
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def inspect
|
|
103
|
+
__synchronize__ do
|
|
104
|
+
if @computation
|
|
105
|
+
"#<#{__class__} computation=#{@computation.inspect}>"
|
|
106
|
+
else
|
|
107
|
+
@result.inspect
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def respond_to?(message)
|
|
113
|
+
message = message.to_sym
|
|
114
|
+
message == :__result__ or
|
|
115
|
+
message == :inspect or
|
|
116
|
+
__result__.respond_to? message
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def method_missing(*args, &block)
|
|
120
|
+
__result__.__send__(*args, &block)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Thread safe version of Promise class.
|
|
125
|
+
#
|
|
126
|
+
class PromiseSafe < Promise
|
|
127
|
+
def __synchronize__
|
|
128
|
+
current = Thread.current
|
|
129
|
+
|
|
130
|
+
Thread.critical = true
|
|
131
|
+
unless @computation
|
|
132
|
+
Thread.critical = false
|
|
133
|
+
yield
|
|
134
|
+
else
|
|
135
|
+
if @owner == current
|
|
136
|
+
Thread.critical = false
|
|
137
|
+
raise DivergenceError.new
|
|
138
|
+
end
|
|
139
|
+
while @owner
|
|
140
|
+
Thread.critical = false
|
|
141
|
+
Thread.pass
|
|
142
|
+
Thread.critical = true
|
|
143
|
+
end
|
|
144
|
+
@owner = current
|
|
145
|
+
Thread.critical = false
|
|
146
|
+
|
|
147
|
+
begin
|
|
148
|
+
yield
|
|
149
|
+
ensure
|
|
150
|
+
@owner = nil
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Future class subclasses PromiseSafe.
|
|
157
|
+
#
|
|
158
|
+
class Future < PromiseSafe
|
|
159
|
+
def initialize(&computation)
|
|
160
|
+
result = nil
|
|
161
|
+
exception = nil
|
|
162
|
+
thread = Thread.new do
|
|
163
|
+
begin
|
|
164
|
+
result = computation.call(self)
|
|
165
|
+
rescue Exception => exception
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
super do
|
|
170
|
+
raise DivergenceError.new if Thread.current == thread
|
|
171
|
+
thread.join
|
|
172
|
+
raise exception if exception
|
|
173
|
+
result
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
module Kernel
|
|
180
|
+
# The promise() function is used together with demand() to implement
|
|
181
|
+
# lazy evaluation. It returns a promise to evaluate the provided
|
|
182
|
+
# block at a future time. Evaluation can be demanded and the block's
|
|
183
|
+
# result obtained via the demand() function.
|
|
184
|
+
#
|
|
185
|
+
# Implicit evaluation is also supported: the first message sent to it will
|
|
186
|
+
# demand evaluation, after which that message and any subsequent messages
|
|
187
|
+
# will be forwarded to the result object.
|
|
188
|
+
#
|
|
189
|
+
# As an aid to circular programming, the block will be passed a promise
|
|
190
|
+
# for its own result when it is evaluated. Be careful not to force
|
|
191
|
+
# that promise during the computation, lest the computation diverge.
|
|
192
|
+
#
|
|
193
|
+
def promise(&computation)
|
|
194
|
+
Lazy::Promise.new(&computation)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Forces the result of a promise to be computed (if necessary) and returns
|
|
198
|
+
# the bare result object. Once evaluated, the result of the promise will
|
|
199
|
+
# be cached. Nested promises will be evaluated together, until the first
|
|
200
|
+
# non-promise result.
|
|
201
|
+
#
|
|
202
|
+
# If called on a value that is not a promise, it will simply return it.
|
|
203
|
+
#
|
|
204
|
+
def demand(promise)
|
|
205
|
+
if promise.respond_to? :__result__
|
|
206
|
+
promise.__result__
|
|
207
|
+
else
|
|
208
|
+
promise
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Schedules a computation to be run asynchronously in a background thread
|
|
213
|
+
# and returns a promise for its result. An attempt to demand the result of
|
|
214
|
+
# the promise will block until the computation finishes.
|
|
215
|
+
#
|
|
216
|
+
# As with Kernel.promise, this passes the block a promise for its own result.
|
|
217
|
+
# Use wisely.
|
|
218
|
+
#
|
|
219
|
+
def future(&computation)
|
|
220
|
+
Lazy::Future.new(&computation)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
|
4
|
+
# License: Apache License, Version 2.0
|
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
module Garcon
|
|
21
|
+
module Extensions
|
|
22
|
+
# MethodReader allows you to access keys of the hash via method calls. This
|
|
23
|
+
# gives you an OStruct like way to access your hash's keys. It will
|
|
24
|
+
# recognize keys either as strings or symbols.
|
|
25
|
+
#
|
|
26
|
+
# @note
|
|
27
|
+
# That while nil keys will be returned as nil, undefined keys will raise
|
|
28
|
+
# NoMethodErrors. Also note that #respond_to? has been patched to
|
|
29
|
+
# appropriately recognize key methods.
|
|
30
|
+
#
|
|
31
|
+
# @example
|
|
32
|
+
# class StashCache < Hash
|
|
33
|
+
# include Garcon::Extensions::MethodReader
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# cash = StashCache.new
|
|
37
|
+
# cash['box'] = 'full'
|
|
38
|
+
# cash.box # => 'full'
|
|
39
|
+
#
|
|
40
|
+
module MethodReader
|
|
41
|
+
def respond_to?(name, include_private = false)
|
|
42
|
+
return true if key?(name.to_s) || key?(name.to_sym)
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def method_missing(name, *args)
|
|
47
|
+
if key?(name)
|
|
48
|
+
self[name]
|
|
49
|
+
else
|
|
50
|
+
sname = name.to_s
|
|
51
|
+
if key?(sname)
|
|
52
|
+
self[sname]
|
|
53
|
+
elsif sname[-1] == '?'
|
|
54
|
+
kname = sname[0..-2]
|
|
55
|
+
key?(kname) || key?(kname.to_sym)
|
|
56
|
+
else
|
|
57
|
+
super
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# MethodWriter gives you #key_name= shortcuts for writing to your hash.
|
|
64
|
+
# Keys are written as strings, override #convert_key if you would like to
|
|
65
|
+
# have symbols or something else.
|
|
66
|
+
#
|
|
67
|
+
# @note
|
|
68
|
+
# That MethodWriter also overrides #respond_to such that any
|
|
69
|
+
# #method_name= will respond appropriately as true.
|
|
70
|
+
#
|
|
71
|
+
# @example
|
|
72
|
+
# class MyHash < Hash
|
|
73
|
+
# include Garcon::Extensions::MethodWriter
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
# h = MyHash.new
|
|
77
|
+
# h.awesome = 'sauce'
|
|
78
|
+
# h['awesome'] # => 'sauce'
|
|
79
|
+
#
|
|
80
|
+
module MethodWriter
|
|
81
|
+
def respond_to?(name, include_private = false)
|
|
82
|
+
return true if name.to_s =~ /=$/
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def method_missing(name, *args)
|
|
87
|
+
if args.size == 1 && name.to_s =~ /(.*)=$/
|
|
88
|
+
return self[convert_key(Regexp.last_match[1])] = args.first
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
super
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def convert_key(key)
|
|
95
|
+
key.to_s
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# MethodQuery gives you the ability to check for the truthiness of a key
|
|
100
|
+
# via method calls. Note that it will return false if the key is set to a
|
|
101
|
+
# non-truthful value, not if the key isn't set at all. Use #key? for
|
|
102
|
+
# checking if a key has been set.
|
|
103
|
+
#
|
|
104
|
+
# MethodQuery will check against both string and symbol names of the method
|
|
105
|
+
# for existing keys. It also patches #respond_to to appropriately detect
|
|
106
|
+
# the query methods.
|
|
107
|
+
#
|
|
108
|
+
# @example
|
|
109
|
+
# class MyHash < Hash
|
|
110
|
+
# include Garcon::Extensions::MethodQuery
|
|
111
|
+
# end
|
|
112
|
+
#
|
|
113
|
+
# h = MyHash.new
|
|
114
|
+
# h['abc'] = 123
|
|
115
|
+
# h.abc? # => true
|
|
116
|
+
# h['def'] = nil
|
|
117
|
+
# h.def? # => false
|
|
118
|
+
# h.hji? # => NoMethodError
|
|
119
|
+
#
|
|
120
|
+
module MethodQuery
|
|
121
|
+
def respond_to?(name, include_private = false)
|
|
122
|
+
if name.to_s =~ /(.*)\?$/ && (key?(Regexp.last_match[1]) ||
|
|
123
|
+
key?(Regexp.last_match[1].to_sym))
|
|
124
|
+
return true
|
|
125
|
+
end
|
|
126
|
+
super
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def method_missing(name, *args)
|
|
130
|
+
if args.empty? && name.to_s =~ /(.*)\?$/ &&
|
|
131
|
+
(key?(Regexp.last_match[1]) ||
|
|
132
|
+
key?(Regexp.last_match[1].to_sym))
|
|
133
|
+
return self[Regexp.last_match[1]] ||
|
|
134
|
+
self[Regexp.last_match[1].to_sym]
|
|
135
|
+
end
|
|
136
|
+
super
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# A macro module that will automatically include MethodReader,
|
|
141
|
+
# MethodWriter, and MethodQuery, giving you the ability to read, write,
|
|
142
|
+
# and query keys in a hash using method call shortcuts.
|
|
143
|
+
#
|
|
144
|
+
module MethodAccess
|
|
145
|
+
def self.included(base)
|
|
146
|
+
[MethodReader, MethodWriter, MethodQuery].each do |mod|
|
|
147
|
+
base.send :include, mod
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# MethodOverridingWriter gives you #key_name= shortcuts for writing to your
|
|
153
|
+
# hash. It allows methods to be overridden by #key_name= shortcuts and
|
|
154
|
+
# aliases those methods with two leading underscores.
|
|
155
|
+
#
|
|
156
|
+
# Keys are written as strings. Override #convert_key if you would like to
|
|
157
|
+
# have symbols or something else.
|
|
158
|
+
#
|
|
159
|
+
# Note that MethodOverridingWriter also overrides #respond_to_missing? such
|
|
160
|
+
# that any #method_name= will respond appropriately as true.
|
|
161
|
+
#
|
|
162
|
+
# @example
|
|
163
|
+
# class MyHash < Hash
|
|
164
|
+
# include Garcon::Extensions::MethodOverridingWriter
|
|
165
|
+
# end
|
|
166
|
+
#
|
|
167
|
+
# h = MyHash.new
|
|
168
|
+
# h.awesome = 'sauce'
|
|
169
|
+
# h['awesome'] # => 'sauce'
|
|
170
|
+
# h.zip = 'a-dee-doo-dah'
|
|
171
|
+
# h.zip # => 'a-dee-doo-dah'
|
|
172
|
+
# h.__zip # => [[['awesome', 'sauce'], ['zip', 'a-dee-doo-dah']]]
|
|
173
|
+
#
|
|
174
|
+
module MethodOverridingWriter
|
|
175
|
+
def convert_key(key)
|
|
176
|
+
key.to_s
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def method_missing(name, *args)
|
|
180
|
+
if args.size == 1 && name.to_s =~ /(.*)=$/
|
|
181
|
+
key = Regexp.last_match[1]
|
|
182
|
+
redefine_method(key) if method?(key) && !already_overridden?(key)
|
|
183
|
+
return self[convert_key(key)] = args.first
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
super
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def respond_to_missing?(name, include_private = false)
|
|
190
|
+
return true if name.to_s.end_with?('=')
|
|
191
|
+
super
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
protected
|
|
195
|
+
|
|
196
|
+
def already_overridden?(name)
|
|
197
|
+
method?("__#{name}")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def method?(name)
|
|
201
|
+
methods.map(&:to_s).include?(name)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def redefine_method(method_name)
|
|
205
|
+
eigenclass = class << self; self; end
|
|
206
|
+
eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
|
|
207
|
+
eigenclass.__send__(:define_method, method_name, -> {self[method_name]})
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# A macro module that will automatically include MethodReader,
|
|
212
|
+
# MethodOverridingWriter, and MethodQuery, giving you the ability to read,
|
|
213
|
+
# write, and query keys in a hash using method call shortcuts that can
|
|
214
|
+
# override object methods. Any overridden object method is automatically
|
|
215
|
+
# aliased with two leading underscores.
|
|
216
|
+
#
|
|
217
|
+
module MethodAccessWithOverride
|
|
218
|
+
def self.included(base)
|
|
219
|
+
[MethodReader, MethodOverridingWriter, MethodQuery].each do |mod|
|
|
220
|
+
base.send :include, mod
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
module Extensions
|
|
227
|
+
module PrettyInspect
|
|
228
|
+
def self.included(base)
|
|
229
|
+
base.send :alias_method, :hash_inspect, :inspect
|
|
230
|
+
base.send :alias_method, :inspect, :stash_inspect
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def stash_inspect
|
|
234
|
+
ret = "#<#{self.class}"
|
|
235
|
+
keys.sort_by(&:to_s).each do |key|
|
|
236
|
+
ret << " #{key}=#{self[key].inspect}"
|
|
237
|
+
end
|
|
238
|
+
ret << '>'
|
|
239
|
+
ret
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|