hoodie 0.5.5 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +19 -0
  4. data/Rakefile +8 -23
  5. data/hoodie.gemspec +0 -23
  6. data/lib/hoodie.rb +40 -48
  7. data/lib/hoodie/configuration.rb +39 -2
  8. data/lib/hoodie/core_ext/blank.rb +6 -6
  9. data/lib/hoodie/core_ext/hash.rb +108 -13
  10. data/lib/hoodie/core_ext/string.rb +5 -3
  11. data/lib/hoodie/core_ext/try.rb +4 -3
  12. data/lib/hoodie/inflections.rb +18 -0
  13. data/lib/hoodie/inflections/defaults.rb +17 -0
  14. data/lib/hoodie/inflections/inflections.rb +17 -0
  15. data/lib/hoodie/inflections/rules_collection.rb +17 -0
  16. data/lib/hoodie/logging.rb +22 -4
  17. data/lib/hoodie/stash.rb +83 -80
  18. data/lib/hoodie/stash/disk_store.rb +142 -118
  19. data/lib/hoodie/stash/mem_store.rb +10 -9
  20. data/lib/hoodie/stash/memoizable.rb +46 -0
  21. data/lib/hoodie/utils.rb +13 -183
  22. data/lib/hoodie/utils/ansi.rb +199 -0
  23. data/lib/hoodie/utils/crypto.rb +288 -0
  24. data/lib/hoodie/utils/equalizer.rb +146 -0
  25. data/lib/hoodie/utils/file_helper.rb +225 -0
  26. data/lib/hoodie/utils/konstruktor.rb +77 -0
  27. data/lib/hoodie/utils/machine.rb +83 -0
  28. data/lib/hoodie/utils/os.rb +56 -0
  29. data/lib/hoodie/utils/retry.rb +235 -0
  30. data/lib/hoodie/utils/timeout.rb +54 -0
  31. data/lib/hoodie/utils/url_helper.rb +104 -0
  32. data/lib/hoodie/version.rb +4 -4
  33. metadata +13 -234
  34. data/lib/hoodie/identity_map.rb +0 -96
  35. data/lib/hoodie/memoizable.rb +0 -43
  36. data/lib/hoodie/obfuscate.rb +0 -121
  37. data/lib/hoodie/observable.rb +0 -309
  38. data/lib/hoodie/os.rb +0 -43
  39. data/lib/hoodie/proxy.rb +0 -68
  40. data/lib/hoodie/rash.rb +0 -125
  41. data/lib/hoodie/timers.rb +0 -355
@@ -1,43 +0,0 @@
1
- # encoding: UTF-8
2
- #
3
- # Author: Stefano Harding <riddopic@gmail.com>
4
- #
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
- require 'rbconfig' unless defined?(RbConfig)
21
-
22
- # Finds out the current Operating System.
23
- module OS
24
- def self.windows?
25
- windows = /cygwin|mswin|mingw|bccwin|wince|emx/i
26
- (RbConfig::CONFIG['host_os'] =~ windows) != nil
27
- end
28
-
29
- def self.mac?
30
- mac = /darwin|mac os/i
31
- (RbConfig::CONFIG['host_os'] =~ mac) != nil
32
- end
33
-
34
- def self.unix?
35
- unix = /solaris|bsd/i
36
- (RbConfig::CONFIG['host_os'] =~ unix) != nil
37
- end
38
-
39
- def self.linux?
40
- linux = /linux/i
41
- (RbConfig::CONFIG['host_os'] =~ linux) != nil
42
- end
43
- end
@@ -1,68 +0,0 @@
1
- # encoding: UTF-8
2
- #
3
- # Author: Stefano Harding <riddopic@gmail.com>
4
- #
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 Hoodie
21
- # Turns object into a proxy which will forward all method missing calls.
22
- #
23
- class Proxy < Module
24
- attr_reader :name
25
-
26
- def initialize(name, options = {})
27
- attr_reader name
28
- ivar = "@#{name}"
29
-
30
- attr_reader :__proxy_kind__, :__proxy_args__
31
-
32
- define_method(:initialize) do |proxy_target, *args, &block|
33
- instance_variable_set(ivar, proxy_target)
34
-
35
- @__proxy_kind__ = options.fetch(:kind) { proxy_target.class }
36
- @__proxy_args__ = args
37
- end
38
-
39
- define_method(:__proxy_target__) do
40
- instance_variable_get(ivar)
41
- end
42
-
43
- include Methods
44
- end
45
-
46
- module Methods
47
- def respond_to_missing?(method_name, include_private)
48
- __proxy_target__.respond_to?(method_name, include_private)
49
- end
50
-
51
- def method_missing(method_name, *args, &block)
52
- if __proxy_target__.respond_to?(method_name)
53
- response = __proxy_target__.public_send(method_name, *args, &block)
54
-
55
- if response.equal?(__proxy_target__)
56
- self
57
- elsif response.kind_of?(__proxy_kind__)
58
- self.class.new(*[response]+__proxy_args__)
59
- else
60
- response
61
- end
62
- else
63
- super
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,125 +0,0 @@
1
- # encoding: UTF-8
2
- #
3
- # Author: Stefano Harding <riddopic@gmail.com>
4
- #
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
- require 'hoodie' unless defined?(Hoodie)
21
- require 'anemone' unless defined?(Anemone)
22
- require 'hoodie/memoizable' unless defined?(Memoizable)
23
-
24
- class Rash
25
- include Memoizable
26
-
27
- # Initializes a new store object.
28
- #
29
- # @param data [Hash] (optional) data to load into the stash.
30
- #
31
- # @return nothing.
32
- #
33
- def initialize(url, path)
34
- @url = url
35
- @path = path
36
- memoize [:fetch], Stash.new(DiskStash::Cache.new)
37
- @store ||= fetch
38
- end
39
-
40
- # Retrieves the value for a given key
41
- #
42
- # @param key [Symbol, String] representing the key
43
- #
44
- # @return [Hash, Array, String] value for key
45
- #
46
- def [](key)
47
- @store[key]
48
- end
49
-
50
- # Store the given value with the given key, either an an argument
51
- # or block. If a previous value was set it will be overwritten
52
- # with the new value.
53
- #
54
- # @param key [Symbol, String] string or symbol representing the key
55
- # @param value [Object] any object that represents the value (optional)
56
- # @param block [&block] that returns the value to set (optional)
57
- #
58
- # @return nothing.
59
- #
60
- def []=(key, value)
61
- @store[key] = value
62
- end
63
-
64
- # return the size of the store as an integer
65
- #
66
- # @return [Fixnum]
67
- #
68
- def size
69
- @store.size
70
- end
71
-
72
- # return all keys in the store as an array
73
- #
74
- # @return [Array<String, Symbol>] all the keys in store
75
- #
76
- def keys
77
- @store.keys
78
- end
79
-
80
- private # P R O P R I E T À P R I V A T A Vietato L'accesso
81
-
82
- # Loads a rash hash of data into the rash or create a new one.
83
- #
84
- # @return nothing.
85
- #
86
- def fetch
87
- results = []
88
- Anemone.crawl(@url, discard_page_bodies: true) do |anemone|
89
- anemone.on_pages_like(/\/#{@path}\/\w+\/\w+\.(ini|zip)$/i) do |page|
90
- results << page.to_hash
91
- end
92
- end
93
- results.reduce({}, :recursive_merge)
94
- end
95
- end
96
-
97
- # to_hash smoke cache
98
- #
99
- module Anemone
100
- class Page
101
- def to_hash
102
- file = File.basename(@url.to_s)
103
- key = File.basename(file, '.*').downcase.to_sym
104
- type = File.extname(file)[1..-1].downcase.to_sym
105
- id = key
106
- utime = Time.now.to_i
107
- key = { key => { type => {
108
- id: id,
109
- file: file,
110
- key: key,
111
- type: type,
112
- url: @url.to_s,
113
- links: links.map(&:to_s),
114
- code: @code,
115
- visited: @visited,
116
- depth: @depth,
117
- referer: @referer.to_s,
118
- fetched: @fetched,
119
- utime: utime,
120
- sha1: false,
121
- sha256: false
122
- } } }
123
- end
124
- end
125
- end
@@ -1,355 +0,0 @@
1
-
2
- require 'set'
3
- require 'hitimes'
4
- require 'forwardable'
5
-
6
- module Hoodie::Timers
7
- # An individual timer set to fire a given proc at a given time. A timer is
8
- # always connected to a Timer::Group but it would ONLY be in @group.timers
9
- # if it also has a @handle specified. Otherwise it is either PAUSED or has
10
- # been FIRED and is not recurring. You can manually enter this state by
11
- # calling #cancel and resume normal operation by calling #reset.
12
- class Timer
13
- include Comparable
14
- attr_reader :interval, :offset, :recurring
15
-
16
- def initialize(group, interval, recurring = false, offset = nil, &block)
17
- @group = group
18
- @interval = interval
19
- @recurring = recurring
20
- @block = block
21
- @offset = offset
22
- @handle = nil
23
-
24
- # If a start offset was supplied, use that, otherwise use the current
25
- # timers offset.
26
- reset(@offset || @group.current_offset)
27
- end
28
-
29
- def paused?
30
- @group.paused_timers.include? self
31
- end
32
-
33
- def pause
34
- return if paused?
35
- @group.timers.delete self
36
- @group.paused_timers.add self
37
- @handle.cancel! if @handle
38
- @handle = nil
39
- end
40
-
41
- def resume
42
- return unless paused?
43
- @group.paused_timers.delete self
44
- # This will add us back to the group:
45
- reset
46
- end
47
- alias_method :continue, :resume
48
-
49
- # Extend this timer
50
- def delay(seconds)
51
- @handle.cancel! if @handle
52
- @offset += seconds
53
- @handle = @group.events.schedule(@offset, self)
54
- end
55
-
56
- # Cancel this timer. Do not call while paused.
57
- def cancel
58
- return unless @handle
59
- @handle.cancel! if @handle
60
- @handle = nil
61
- # This timer is no longer valid:
62
- @group.timers.delete self if @group
63
- end
64
-
65
- # Reset this timer. Do not call while paused.
66
- def reset(offset = @group.current_offset)
67
- # This logic allows us to minimise the interaction with @group.timers.
68
- # A timer with a handle is always registered with the group.
69
- if @handle
70
- @handle.cancel!
71
- else
72
- @group.timers << self
73
- end
74
- @offset = Float(offset) + @interval
75
- @handle = @group.events.schedule(@offset, self)
76
- end
77
-
78
- # Fire the block.
79
- def fire(offset = @group.current_offset)
80
- if recurring == :strict
81
- # ... make the next interval strictly the last offset + the interval:
82
- reset(@offset)
83
- elsif recurring
84
- reset(offset)
85
- else
86
- @offset = offset
87
- end
88
- @block.call(offset)
89
- cancel unless recurring
90
- end
91
- alias_method :call, :fire
92
-
93
- # Number of seconds until next fire / since last fire
94
- def fires_in
95
- @offset - @group.current_offset if @offset
96
- end
97
-
98
- # Inspect a timer
99
- def inspect
100
- str = "#<Timers::Timer:#{object_id.to_s(16)} "
101
- if @offset
102
- if fires_in >= 0
103
- str << "fires in #{fires_in} seconds"
104
- else
105
- str << "fired #{fires_in.abs} seconds ago"
106
- end
107
- str << ", recurs every #{interval}" if recurring
108
- else
109
- str << 'dead'
110
- end
111
- str << '>'
112
- end
113
- end
114
-
115
- # An exclusive, monotonic timeout class.
116
- class Wait
117
- def self.for(duration, &block)
118
- if duration
119
- timeout = new(duration)
120
- timeout.while_time_remaining(&block)
121
- else
122
- loop do
123
- yield(nil)
124
- end
125
- end
126
- end
127
-
128
- def initialize(duration)
129
- @duration = duration
130
- @remaining = true
131
- end
132
-
133
- attr_reader :duration
134
- attr_reader :remaining
135
-
136
- # Yields while time remains for work to be done:
137
- def while_time_remaining(&_block)
138
- @interval = Hitimes::Interval.new
139
- @interval.start
140
- while time_remaining?
141
- yield @remaining
142
- end
143
- ensure
144
- @interval.stop
145
- @interval = nil
146
- end
147
-
148
- private # P R O P R I E T À P R I V A T A Vietato L'accesso
149
-
150
- def time_remaining?
151
- @remaining = (@duration - @interval.duration)
152
- @remaining > 0
153
- end
154
- end
155
-
156
- class Group
157
- include Enumerable
158
- extend Forwardable
159
- def_delegators :@timers, :each, :empty?
160
-
161
- def initialize
162
- @events = Events.new
163
- @timers = Set.new
164
- @paused_timers = Set.new
165
- @interval = Hitimes::Interval.new
166
- @interval.start
167
- end
168
-
169
- # Scheduled events:
170
- attr_reader :events
171
-
172
- # Active timers:
173
- attr_reader :timers
174
-
175
- # Paused timers:
176
- attr_reader :paused_timers
177
-
178
- # Call the given block after the given interval. The first argument will be
179
- # the time at which the group was asked to fire timers for.
180
- def after(interval, &block)
181
- Timer.new(self, interval, false, &block)
182
- end
183
-
184
- # Call the given block periodically at the given interval. The first
185
- # argument will be the time at which the group was asked to fire timers for.
186
- def every(interval, recur = true, &block)
187
- Timer.new(self, interval, recur, &block)
188
- end
189
-
190
- # Wait for the next timer and fire it. Can take a block, which should behave
191
- # like sleep(n), except that n may be nil (sleep forever) or a negative
192
- # number (fire immediately after return).
193
- def wait(&_block)
194
- if block_given?
195
- yield wait_interval
196
-
197
- while interval = wait_interval and interval > 0
198
- yield interval
199
- end
200
- else
201
- while interval = wait_interval and interval > 0
202
- # We cannot assume that sleep will wait for the specified time, it might be +/- a bit.
203
- sleep interval
204
- end
205
- end
206
- fire
207
- end
208
-
209
- # Interval to wait until when the next timer will fire.
210
- # - nil: no timers
211
- # - -ve: timers expired already
212
- # - 0: timers ready to fire
213
- # - +ve: timers waiting to fire
214
- def wait_interval(offset = current_offset)
215
- if handle = @events.first
216
- return handle.time - Float(offset)
217
- end
218
- end
219
-
220
- # Fire all timers that are ready.
221
- def fire(offset = current_offset)
222
- @events.fire(offset)
223
- end
224
-
225
- # Pause all timers.
226
- def pause
227
- @timers.dup.each(&:pause)
228
- end
229
-
230
- # Resume all timers.
231
- def resume
232
- @paused_timers.dup.each(&:resume)
233
- end
234
- alias_method :continue, :resume
235
-
236
- # Delay all timers.
237
- def delay(seconds)
238
- @timers.each do |timer|
239
- timer.delay(seconds)
240
- end
241
- end
242
-
243
- # Cancel all timers.
244
- def cancel
245
- @timers.dup.each(&:cancel)
246
- end
247
-
248
- # The group's current time.
249
- def current_offset
250
- @interval.to_f
251
- end
252
- end
253
-
254
- # Maintains an ordered list of events, which can be cancelled.
255
- class Events
256
- # Represents a cancellable handle for a specific timer event.
257
- class Handle
258
- def initialize(time, callback)
259
- @time = time
260
- @callback = callback
261
- end
262
-
263
- # The absolute time that the handle should be fired at.
264
- attr_reader :time
265
-
266
- # Cancel this timer, O(1).
267
- def cancel!
268
- # The simplest way to keep track of cancelled status is to nullify the
269
- # callback. This should also be optimal for garbage collection.
270
- @callback = nil
271
- end
272
-
273
- # Has this timer been cancelled? Cancelled timer's don't fire.
274
- def cancelled?
275
- @callback.nil?
276
- end
277
-
278
- def >(other)
279
- @time > other.to_f
280
- end
281
-
282
- def to_f
283
- @time
284
- end
285
-
286
- # Fire the callback if not cancelled with the given time parameter.
287
- def fire(time)
288
- if @callback
289
- @callback.call(time)
290
- end
291
- end
292
- end
293
-
294
- def initialize
295
- # A sequence of handles, maintained in sorted order, future to present.
296
- # @sequence.last is the next event to be fired.
297
- @sequence = []
298
- end
299
-
300
- # Add an event at the given time.
301
- def schedule(time, callback)
302
- handle = Handle.new(time.to_f, callback)
303
- index = bisect_left(@sequence, handle)
304
- # Maintain sorted order, O(logN) insertion time.
305
- @sequence.insert(index, handle)
306
- handle
307
- end
308
-
309
- # Returns the first non-cancelled handle.
310
- def first
311
- while handle = @sequence.last
312
- if handle.cancelled?
313
- @sequence.pop
314
- else
315
- return handle
316
- end
317
- end
318
- end
319
-
320
- # Returns the number of pending (possibly cancelled) events.
321
- def size
322
- @sequence.size
323
- end
324
-
325
- # Fire all handles for which Handle#time is less than the given time.
326
- def fire(time)
327
- pop(time).reverse_each do |handle|
328
- handle.fire(time)
329
- end
330
- end
331
-
332
- private # P R O P R I E T À P R I V A T A Vietato L'accesso
333
-
334
- # Efficiently take k handles for which Handle#time is less than the given
335
- # time.
336
- def pop(time)
337
- index = bisect_left(@sequence, time)
338
- @sequence.pop(@sequence.size - index)
339
- end
340
-
341
- # Return the left-most index where to insert item e, in a list a, assuming
342
- # a is sorted in descending order.
343
- def bisect_left(a, e, l = 0, u = a.length)
344
- while l < u
345
- m = l + (u - l).div(2)
346
- if a[m] > e
347
- l = m + 1
348
- else
349
- u = m
350
- end
351
- end
352
- l
353
- end
354
- end
355
- end