hoodie 0.4.2 → 0.4.3
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 +4 -4
- data/{LICENSE → LICENSE.md} +1 -1
- data/Rakefile +12 -1
- data/hoodie.gemspec +6 -6
- data/lib/hoodie/hash.rb +0 -26
- data/lib/hoodie/observable.rb +309 -0
- data/lib/hoodie/stash/mem_store.rb +2 -0
- data/lib/hoodie/utils.rb +88 -155
- data/lib/hoodie/version.rb +1 -1
- metadata +18 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63d0ae0f7b0c7af6c0e944764423870c80291599
|
|
4
|
+
data.tar.gz: ed2ae198e58ac097ccc090b31e52f2b96fa21d78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 429be0f60bf5cd785c4244a55311f55ae6994c8d7a424b45a41646c9afe83c2eaf989c7cb70156b7ce2d4d4e3e8c20785815227065731431591689cdeee998c3
|
|
7
|
+
data.tar.gz: 1ab5e7087209dd755964abe345b3144c66a213160c7032290e445e7c8662cf9a946b5700d95466642dde0133b7612e7ce26452e9ab31639273b07459ee679e7c
|
data/{LICENSE → LICENSE.md}
RENAMED
|
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2014 Stefano Harding
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
data/Rakefile
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'bundler/gem_tasks'
|
|
3
3
|
require 'rspec/core/rake_task'
|
|
4
4
|
require 'rubocop/rake_task'
|
|
5
|
+
require 'yard'
|
|
5
6
|
|
|
6
7
|
desc 'Run tests'
|
|
7
8
|
RSpec::Core::RakeTask.new(:spec)
|
|
@@ -12,4 +13,14 @@ RuboCop::RakeTask.new(:rubocop) do |task|
|
|
|
12
13
|
task.fail_on_error = true
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
YARD::Config.load_plugin 'redcarpet-ext'
|
|
17
|
+
YARD::Rake::YardocTask.new do |t|
|
|
18
|
+
additional_docs = %w[ CHANGELOG.md LICENSE.md README.md ]
|
|
19
|
+
t.files = ['lib/*.rb', '-'] + additional_docs
|
|
20
|
+
t.options = ['--readme=README.md', '--markup=markdown', '--verbose']
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'Build documentation'
|
|
24
|
+
task doc: [:yard]
|
|
25
|
+
|
|
26
|
+
task default: [:spec, :rubocop, :doc, :build, :install]
|
data/hoodie.gemspec
CHANGED
|
@@ -22,10 +22,10 @@ Gem::Specification.new do |s|
|
|
|
22
22
|
# s.add_runtime_dependency 'anemone', '>= 0.7.2'
|
|
23
23
|
s.add_runtime_dependency 'hitimes'
|
|
24
24
|
|
|
25
|
-
s.add_development_dependency 'rubocop', '
|
|
26
|
-
s.add_development_dependency 'rake', '
|
|
27
|
-
s.add_development_dependency 'coveralls', '
|
|
28
|
-
s.add_development_dependency 'rspec', '
|
|
29
|
-
s.add_development_dependency 'fuubar', '
|
|
30
|
-
s.add_development_dependency 'timecop', '
|
|
25
|
+
s.add_development_dependency 'rubocop', '>= 0.26.0'
|
|
26
|
+
s.add_development_dependency 'rake', '>= 10.3.2'
|
|
27
|
+
s.add_development_dependency 'coveralls', '>= 0.7.1'
|
|
28
|
+
s.add_development_dependency 'rspec', '>= 3.1.0'
|
|
29
|
+
s.add_development_dependency 'fuubar', '>= 2.0.0'
|
|
30
|
+
s.add_development_dependency 'timecop', '>= 0.7.1'
|
|
31
31
|
end
|
data/lib/hoodie/hash.rb
CHANGED
|
@@ -110,32 +110,6 @@ class Hash
|
|
|
110
110
|
recursively_transform_keys { |key| key.to_s rescue key }
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
class UndefinedPathError < StandardError; end
|
|
114
|
-
# Recursively searchs a nested datastructure for a key and returns
|
|
115
|
-
# the value. If a block is provided its value will be returned if
|
|
116
|
-
# the key does not exist
|
|
117
|
-
#
|
|
118
|
-
# @example
|
|
119
|
-
# options = { server: { location: { row: { rack: 34 } } } }
|
|
120
|
-
# options.recursive_fetch :server, :location, :row, :rack
|
|
121
|
-
# # => 34
|
|
122
|
-
# options.recursive_fetch(:non_existent_key) { 'default' }
|
|
123
|
-
# # => "default"
|
|
124
|
-
#
|
|
125
|
-
# @return [Hash, Array, String] value for key
|
|
126
|
-
#
|
|
127
|
-
def recursive_fetch(*args, &block)
|
|
128
|
-
args.reduce(self) do |obj, arg|
|
|
129
|
-
begin
|
|
130
|
-
arg = Integer(arg) if obj.is_a? Array
|
|
131
|
-
obj.fetch(arg)
|
|
132
|
-
rescue ArgumentError, IndexError, NoMethodError => e
|
|
133
|
-
break block.call(arg) if block
|
|
134
|
-
raise UndefinedPathError, "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
113
|
def recursive_merge(other)
|
|
140
114
|
hash = dup
|
|
141
115
|
other.each do |key, value|
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
|
|
2
|
+
module Garcon
|
|
3
|
+
module Observable
|
|
4
|
+
|
|
5
|
+
# @return [Object] the added observer
|
|
6
|
+
#
|
|
7
|
+
def add_observer(*args, &block)
|
|
8
|
+
observers.add_observer(*args, &block)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# as #add_observer but it can be used for chaining
|
|
12
|
+
#
|
|
13
|
+
# @return [Observable] self
|
|
14
|
+
#
|
|
15
|
+
def with_observer(*args, &block)
|
|
16
|
+
add_observer(*args, &block)
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [Object] the deleted observer
|
|
21
|
+
#
|
|
22
|
+
def delete_observer(*args)
|
|
23
|
+
observers.delete_observer(*args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Observable] self
|
|
27
|
+
#
|
|
28
|
+
def delete_observers
|
|
29
|
+
observers.delete_observers
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Integer] the observers count
|
|
34
|
+
#
|
|
35
|
+
def count_observers
|
|
36
|
+
observers.count_observers
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected # A T T E N Z I O N E A R E A P R O T E T T A
|
|
40
|
+
|
|
41
|
+
attr_accessor :observers
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A thread safe observer set implemented using copy-on-read approach.
|
|
45
|
+
# Observers are added and removed from a thread safe collection; every time
|
|
46
|
+
# a notification is required the internal data structure is copied to
|
|
47
|
+
# prevent concurrency issues
|
|
48
|
+
#
|
|
49
|
+
class CopyOnNotifyObserverSet
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@mutex = Mutex.new
|
|
53
|
+
@observers = {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Adds an observer to this set, if a block is passed, the observer will be
|
|
57
|
+
# created by this method and no other params should be passed.
|
|
58
|
+
#
|
|
59
|
+
# @param [Object] observer
|
|
60
|
+
# the observer to add
|
|
61
|
+
# @param [Symbol] func
|
|
62
|
+
# the function to call on the observer during notification.
|
|
63
|
+
# Default is :update
|
|
64
|
+
#
|
|
65
|
+
# @return [Object]
|
|
66
|
+
# the added observer
|
|
67
|
+
#
|
|
68
|
+
def add_observer(observer = nil, func = :update, &block)
|
|
69
|
+
if observer.nil? && block.nil?
|
|
70
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
|
71
|
+
elsif observer && block
|
|
72
|
+
raise ArgumentError, 'cannot provide both an observer and a block'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if block
|
|
76
|
+
observer = block
|
|
77
|
+
func = :call
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
@mutex.lock
|
|
82
|
+
@observers[observer] = func
|
|
83
|
+
ensure
|
|
84
|
+
@mutex.unlock
|
|
85
|
+
end
|
|
86
|
+
observer
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @param [Object] observer the observer to remove
|
|
90
|
+
#
|
|
91
|
+
# @return [Object] the deleted observer
|
|
92
|
+
#
|
|
93
|
+
def delete_observer(observer)
|
|
94
|
+
@mutex.lock
|
|
95
|
+
@observers.delete(observer)
|
|
96
|
+
@mutex.unlock
|
|
97
|
+
observer
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Deletes all observers
|
|
101
|
+
#
|
|
102
|
+
# @return [CopyOnWriteObserverSet] self
|
|
103
|
+
#
|
|
104
|
+
def delete_observers
|
|
105
|
+
@mutex.lock
|
|
106
|
+
@observers.clear
|
|
107
|
+
@mutex.unlock
|
|
108
|
+
self
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @return [Integer] the observers count
|
|
112
|
+
#
|
|
113
|
+
def count_observers
|
|
114
|
+
@mutex.lock
|
|
115
|
+
result = @observers.count
|
|
116
|
+
@mutex.unlock
|
|
117
|
+
result
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Notifies all registered observers with optional args.
|
|
121
|
+
#
|
|
122
|
+
# @param [Object] args
|
|
123
|
+
# arguments to be passed to each observer
|
|
124
|
+
#
|
|
125
|
+
# @return [CopyOnWriteObserverSet] self
|
|
126
|
+
#
|
|
127
|
+
def notify_observers(*args, &block)
|
|
128
|
+
observers = duplicate_observers
|
|
129
|
+
notify_to(observers, *args, &block)
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Notifies all registered observers with optional args and deletes them.
|
|
134
|
+
#
|
|
135
|
+
# @param [Object] args
|
|
136
|
+
# arguments to be passed to each observer
|
|
137
|
+
#
|
|
138
|
+
# @return [CopyOnWriteObserverSet] self
|
|
139
|
+
#
|
|
140
|
+
def notify_and_delete_observers(*args, &block)
|
|
141
|
+
observers = duplicate_and_clear_observers
|
|
142
|
+
notify_to(observers, *args, &block)
|
|
143
|
+
self
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
|
147
|
+
|
|
148
|
+
def duplicate_and_clear_observers
|
|
149
|
+
@mutex.lock
|
|
150
|
+
observers = @observers.dup
|
|
151
|
+
@observers.clear
|
|
152
|
+
@mutex.unlock
|
|
153
|
+
observers
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def duplicate_observers
|
|
157
|
+
@mutex.lock
|
|
158
|
+
observers = @observers.dup
|
|
159
|
+
@mutex.unlock
|
|
160
|
+
observers
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def notify_to(observers, *args)
|
|
164
|
+
if block_given? && !args.empty?
|
|
165
|
+
raise ArgumentError, 'cannot give arguments and a block'
|
|
166
|
+
end
|
|
167
|
+
observers.each do |observer, function|
|
|
168
|
+
args = yield if block_given?
|
|
169
|
+
observer.send(function, *args)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# A thread safe observer set implemented using copy-on-write approach. Every
|
|
175
|
+
# time an observer is added or removed the whole internal data structure is
|
|
176
|
+
# duplicated and replaced with a new one.
|
|
177
|
+
#
|
|
178
|
+
class CopyOnWriteObserverSet
|
|
179
|
+
|
|
180
|
+
def initialize
|
|
181
|
+
@mutex = Mutex.new
|
|
182
|
+
@observers = {}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Adds an observer to this set, if a block is passed, the observer will be
|
|
186
|
+
# created by this method and no other params should be passed.
|
|
187
|
+
#
|
|
188
|
+
# @param [Object] observer
|
|
189
|
+
# the observer to add
|
|
190
|
+
# @param [Symbol] func
|
|
191
|
+
# the function to call on the observer during notification
|
|
192
|
+
# Default is :update
|
|
193
|
+
# @return [Object]
|
|
194
|
+
# the added observer
|
|
195
|
+
#
|
|
196
|
+
def add_observer(observer = nil, func = :update, &block)
|
|
197
|
+
if observer.nil? && block.nil?
|
|
198
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
|
199
|
+
elsif observer && block
|
|
200
|
+
raise ArgumentError, 'cannot provide both an observer and a block'
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if block
|
|
204
|
+
observer = block
|
|
205
|
+
func = :call
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
begin
|
|
209
|
+
@mutex.lock
|
|
210
|
+
new_observers = @observers.dup
|
|
211
|
+
new_observers[observer] = func
|
|
212
|
+
@observers = new_observers
|
|
213
|
+
observer
|
|
214
|
+
ensure
|
|
215
|
+
@mutex.unlock
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# @param [Object] observer the observer to remove
|
|
220
|
+
#
|
|
221
|
+
# @return [Object] the deleted observer
|
|
222
|
+
#
|
|
223
|
+
def delete_observer(observer)
|
|
224
|
+
@mutex.lock
|
|
225
|
+
new_observers = @observers.dup
|
|
226
|
+
new_observers.delete(observer)
|
|
227
|
+
@observers = new_observers
|
|
228
|
+
observer
|
|
229
|
+
ensure
|
|
230
|
+
@mutex.unlock
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Deletes all observers
|
|
234
|
+
#
|
|
235
|
+
# @return [CopyOnWriteObserverSet] self
|
|
236
|
+
#
|
|
237
|
+
def delete_observers
|
|
238
|
+
self.observers = {}
|
|
239
|
+
self
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# @return [Integer] the observers count
|
|
244
|
+
#
|
|
245
|
+
def count_observers
|
|
246
|
+
observers.count
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Notifies all registered observers with optional args.
|
|
250
|
+
#
|
|
251
|
+
# @param [Object] args
|
|
252
|
+
# arguments to be passed to each observer
|
|
253
|
+
#
|
|
254
|
+
# @return [CopyOnWriteObserverSet] self
|
|
255
|
+
#
|
|
256
|
+
def notify_observers(*args, &block)
|
|
257
|
+
notify_to(observers, *args, &block)
|
|
258
|
+
self
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Notifies all registered observers with optional args and deletes them.
|
|
262
|
+
#
|
|
263
|
+
# @param [Object] args
|
|
264
|
+
# arguments to be passed to each observer
|
|
265
|
+
#
|
|
266
|
+
# @return [CopyOnWriteObserverSet] self
|
|
267
|
+
#
|
|
268
|
+
def notify_and_delete_observers(*args, &block)
|
|
269
|
+
old = clear_observers_and_return_old
|
|
270
|
+
notify_to(old, *args, &block)
|
|
271
|
+
self
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
|
275
|
+
|
|
276
|
+
def notify_to(observers, *args)
|
|
277
|
+
if block_given? && !args.empty?
|
|
278
|
+
raise ArgumentError, 'cannot give arguments and a block'
|
|
279
|
+
end
|
|
280
|
+
observers.each do |observer, function|
|
|
281
|
+
args = yield if block_given?
|
|
282
|
+
observer.send(function, *args)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def observers
|
|
287
|
+
@mutex.lock
|
|
288
|
+
@observers
|
|
289
|
+
ensure
|
|
290
|
+
@mutex.unlock
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def observers=(new_set)
|
|
294
|
+
@mutex.lock
|
|
295
|
+
@observers = new_set
|
|
296
|
+
ensure
|
|
297
|
+
@mutex.unlock
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def clear_observers_and_return_old
|
|
301
|
+
@mutex.lock
|
|
302
|
+
old_observers = @observers
|
|
303
|
+
@observers = {}
|
|
304
|
+
old_observers
|
|
305
|
+
ensure
|
|
306
|
+
@mutex.unlock
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
@@ -68,8 +68,10 @@ module MemStash
|
|
|
68
68
|
#
|
|
69
69
|
# stash.set('name') { 'Trigster' }
|
|
70
70
|
# => "Trigster"
|
|
71
|
+
#
|
|
71
72
|
# stash[:cash] = 'in the hash stash cache store'
|
|
72
73
|
# => "in the hash stash cache store"
|
|
74
|
+
#
|
|
73
75
|
# data = { id: 'trig', name: 'Trigster Jay', passwd: 'f00d' }
|
|
74
76
|
# stash[:juser] = data
|
|
75
77
|
# => {
|
data/lib/hoodie/utils.rb
CHANGED
|
@@ -24,7 +24,9 @@ require 'time'
|
|
|
24
24
|
module Hoodie
|
|
25
25
|
module Utils
|
|
26
26
|
def self.included(base)
|
|
27
|
-
|
|
27
|
+
include(ClassMethods)
|
|
28
|
+
|
|
29
|
+
base.send(:include, ClassMethods)
|
|
28
30
|
end
|
|
29
31
|
private_class_method :included
|
|
30
32
|
|
|
@@ -45,8 +47,8 @@ module Hoodie
|
|
|
45
47
|
demodulize(self.class)
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
def caller_name
|
|
49
|
-
|
|
50
|
+
def caller_name(position = 0)
|
|
51
|
+
caller[position][/`.*'/][1..-2]
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def demodulize(class_name_in_module)
|
|
@@ -95,173 +97,104 @@ module Hoodie
|
|
|
95
97
|
end
|
|
96
98
|
yield if block_given?
|
|
97
99
|
end
|
|
98
|
-
end # module ClassMethods
|
|
99
|
-
|
|
100
|
-
# ============================================================================
|
|
101
|
-
|
|
102
|
-
def callable(call_her)
|
|
103
|
-
call_her.respond_to?(:call) ? call_her : lambda { call_her }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def camelize(underscored_word)
|
|
107
|
-
underscored_word.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def classify(table_name)
|
|
111
|
-
camelize singularize(table_name.to_s.sub(/.*\./, ''))
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def class_name
|
|
115
|
-
demodulize(self.class)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def caller_name
|
|
119
|
-
caller_locations(2, 1).first.label
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def demodulize(class_name_in_module)
|
|
123
|
-
class_name_in_module.to_s.sub(/^.*::/, '')
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def pluralize(word)
|
|
127
|
-
word.to_s.sub(/([^s])$/, '\1s')
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def singularize(word)
|
|
131
|
-
word.to_s.sub(/s$/, '').sub(/ie$/, 'y')
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def underscore(camel_cased_word)
|
|
135
|
-
word = camel_cased_word.to_s.dup
|
|
136
|
-
word.gsub!(/::/, '/')
|
|
137
|
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
138
|
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
139
|
-
word.tr! '-', '_'
|
|
140
|
-
word.downcase!
|
|
141
|
-
word
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Return the date and time in "HTTP-date" format as defined by RFC 7231.
|
|
145
|
-
#
|
|
146
|
-
# @return [Date,Time] in "HTTP-date" format
|
|
147
|
-
def utc_httpdate
|
|
148
|
-
Time.now.utc.httpdate
|
|
149
|
-
end
|
|
150
100
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# @return [Integer]
|
|
172
|
-
# number of columns and lines of tty, returns [0, 0] if no tty is present.
|
|
173
|
-
#
|
|
174
|
-
# @api public
|
|
175
|
-
def terminal_dimensions
|
|
176
|
-
[0, 0] unless STDOUT.tty?
|
|
177
|
-
[80, 40] if OS.windows?
|
|
178
|
-
|
|
179
|
-
if ENV['COLUMNS'] && ENV['LINES']
|
|
180
|
-
[ENV['COLUMNS'].to_i, ENV['LINES'].to_i]
|
|
181
|
-
elsif ENV['TERM'] && command_in_path?('tput')
|
|
182
|
-
[`tput cols`.to_i, `tput lines`.to_i]
|
|
183
|
-
elsif command_in_path?('stty')
|
|
184
|
-
`stty size`.scan(/\d+/).map {|s| s.to_i }
|
|
185
|
-
else
|
|
101
|
+
# Returns the columns and lines of the current tty.
|
|
102
|
+
#
|
|
103
|
+
# @return [Integer]
|
|
104
|
+
# number of columns and lines of tty, returns [0, 0] if no tty present.
|
|
105
|
+
#
|
|
106
|
+
# @api public
|
|
107
|
+
def terminal_dimensions
|
|
108
|
+
[0, 0] unless STDOUT.tty?
|
|
109
|
+
[80, 40] if OS.windows?
|
|
110
|
+
|
|
111
|
+
if ENV['COLUMNS'] && ENV['LINES']
|
|
112
|
+
[ENV['COLUMNS'].to_i, ENV['LINES'].to_i]
|
|
113
|
+
elsif ENV['TERM'] && command_in_path?('tput')
|
|
114
|
+
[`tput cols`.to_i, `tput lines`.to_i]
|
|
115
|
+
elsif command_in_path?('stty')
|
|
116
|
+
`stty size`.scan(/\d+/).map {|s| s.to_i }
|
|
117
|
+
else
|
|
118
|
+
[0, 0]
|
|
119
|
+
end
|
|
120
|
+
rescue
|
|
186
121
|
[0, 0]
|
|
187
122
|
end
|
|
188
|
-
rescue
|
|
189
|
-
[0, 0]
|
|
190
|
-
end
|
|
191
123
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
124
|
+
# Checks in PATH returns true if the command is found
|
|
125
|
+
def command_in_path?(command)
|
|
126
|
+
found = ENV['PATH'].split(File::PATH_SEPARATOR).map do |p|
|
|
127
|
+
File.exist?(File.join(p, command))
|
|
128
|
+
end
|
|
129
|
+
found.include?(true)
|
|
196
130
|
end
|
|
197
|
-
found.include?(true)
|
|
198
|
-
end
|
|
199
131
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
check_for_invalid_options(opts, defaults)
|
|
230
|
-
defaults.merge!(opts)
|
|
132
|
+
# Runs a code block, and retries it when an exception occurs. Should the
|
|
133
|
+
# number of retries be reached without success, the last exception will be
|
|
134
|
+
# raised.
|
|
135
|
+
#
|
|
136
|
+
# @param opts [Hash{Symbol => Value}]
|
|
137
|
+
# @option opts [Fixnum] :tries
|
|
138
|
+
# number of attempts to retry before raising the last exception
|
|
139
|
+
# @option opts [Fixnum] :sleep
|
|
140
|
+
# number of seconds to wait between retries, use lambda to exponentially
|
|
141
|
+
# increasing delay between retries
|
|
142
|
+
# @option opts [Array(Exception)] :on
|
|
143
|
+
# the type of exception(s) to catch and retry on
|
|
144
|
+
# @option opts [Regex] :matching
|
|
145
|
+
# match based on the exception message
|
|
146
|
+
# @option opts [Block] :ensure
|
|
147
|
+
# ensure a block of code is executed, regardless of whether an exception
|
|
148
|
+
# is raised
|
|
149
|
+
#
|
|
150
|
+
# @return [Block]
|
|
151
|
+
#
|
|
152
|
+
def retrier(opts = {}, &block)
|
|
153
|
+
defaults = {
|
|
154
|
+
tries: 2,
|
|
155
|
+
sleep: 1,
|
|
156
|
+
on: StandardError,
|
|
157
|
+
matching: /.*/,
|
|
158
|
+
:ensure => Proc.new {}
|
|
159
|
+
}
|
|
231
160
|
|
|
232
|
-
|
|
161
|
+
check_for_invalid_options(opts, defaults)
|
|
162
|
+
defaults.merge!(opts)
|
|
233
163
|
|
|
234
|
-
|
|
235
|
-
retries = 0
|
|
236
|
-
retry_exception = nil
|
|
164
|
+
return if defaults[:tries] == 0
|
|
237
165
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
raise unless exception.message =~ defaults[:matching]
|
|
242
|
-
raise if retries+1 >= defaults[:tries]
|
|
166
|
+
on_exception, tries = [defaults[:on]].flatten, defaults[:tries]
|
|
167
|
+
retries = 0
|
|
168
|
+
retry_exception = nil
|
|
243
169
|
|
|
244
|
-
# Interrupt Exception could be raised while sleeping
|
|
245
170
|
begin
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
171
|
+
yield retries, retry_exception
|
|
172
|
+
rescue *on_exception => exception
|
|
173
|
+
raise unless exception.message =~ defaults[:matching]
|
|
174
|
+
raise if retries+1 >= defaults[:tries]
|
|
175
|
+
|
|
176
|
+
# Interrupt Exception could be raised while sleeping
|
|
177
|
+
begin
|
|
178
|
+
sleep defaults[:sleep].respond_to?(:call) ?
|
|
179
|
+
defaults[:sleep].call(retries) : defaults[:sleep]
|
|
180
|
+
rescue *on_exception
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
retries += 1
|
|
184
|
+
retry_exception = exception
|
|
185
|
+
retry
|
|
186
|
+
ensure
|
|
187
|
+
defaults[:ensure].call(retries)
|
|
249
188
|
end
|
|
250
|
-
|
|
251
|
-
retries += 1
|
|
252
|
-
retry_exception = exception
|
|
253
|
-
retry
|
|
254
|
-
ensure
|
|
255
|
-
defaults[:ensure].call(retries)
|
|
256
189
|
end
|
|
257
|
-
end
|
|
258
190
|
|
|
259
|
-
|
|
191
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
|
260
192
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
193
|
+
def check_for_invalid_options(custom_options, defaults)
|
|
194
|
+
invalid_options = defaults.merge(custom_options).keys - defaults.keys
|
|
195
|
+
raise ArgumentError.new('[Retrier] Invalid options: ' \
|
|
196
|
+
"#{invalid_options.join(", ")}") unless invalid_options.empty?
|
|
197
|
+
end
|
|
265
198
|
end
|
|
266
199
|
end
|
|
267
200
|
end
|
data/lib/hoodie/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hoodie
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefano Harding
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-
|
|
11
|
+
date: 2014-12-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: hitimes
|
|
@@ -28,84 +28,84 @@ dependencies:
|
|
|
28
28
|
name: rubocop
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: 0.26.0
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 0.26.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: 10.3.2
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: 10.3.2
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: coveralls
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: 0.7.1
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: 0.7.1
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: rspec
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- - "
|
|
73
|
+
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: 3.1.0
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- - "
|
|
80
|
+
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: 3.1.0
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: fuubar
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- - "
|
|
87
|
+
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: 2.0.0
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- - "
|
|
94
|
+
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: 2.0.0
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: timecop
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- - "
|
|
101
|
+
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: 0.7.1
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- - "
|
|
108
|
+
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: 0.7.1
|
|
111
111
|
description: A collection of hipster methods and hoodie tools to make even the nerdy
|
|
@@ -118,7 +118,7 @@ files:
|
|
|
118
118
|
- ".gitattributes"
|
|
119
119
|
- ".gitignore"
|
|
120
120
|
- Gemfile
|
|
121
|
-
- LICENSE
|
|
121
|
+
- LICENSE.md
|
|
122
122
|
- README.md
|
|
123
123
|
- Rakefile
|
|
124
124
|
- hoodie.gemspec
|
|
@@ -128,6 +128,7 @@ files:
|
|
|
128
128
|
- lib/hoodie/identity_map.rb
|
|
129
129
|
- lib/hoodie/memoizable.rb
|
|
130
130
|
- lib/hoodie/obfuscate.rb
|
|
131
|
+
- lib/hoodie/observable.rb
|
|
131
132
|
- lib/hoodie/os.rb
|
|
132
133
|
- lib/hoodie/rash.rb
|
|
133
134
|
- lib/hoodie/stash.rb
|
|
@@ -156,8 +157,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
157
|
version: '0'
|
|
157
158
|
requirements: []
|
|
158
159
|
rubyforge_project:
|
|
159
|
-
rubygems_version: 2.
|
|
160
|
+
rubygems_version: 2.4.1
|
|
160
161
|
signing_key:
|
|
161
162
|
specification_version: 4
|
|
162
163
|
summary: Pragmatic hoodie concurrency hipster with ruby
|
|
163
164
|
test_files: []
|
|
165
|
+
has_rdoc:
|