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
@@ -0,0 +1,77 @@
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
+ # Konstruktor is a set of helpers that makes constructor definition less
21
+ # verbose.
22
+ #
23
+ # @example
24
+ # Turn this:
25
+ # class Foo
26
+ # attr_reader :foo, :bar, :baz
27
+ #
28
+ # def initialize(foo, bar, baz)
29
+ # @foo = foo
30
+ # @bar = bar
31
+ # @baz = baz
32
+ # end
33
+ #
34
+ # def hello
35
+ # 'world'
36
+ # end
37
+ # end
38
+ #
39
+ # Into this:
40
+ # class Foo
41
+ # include Hoodie::Konstruktor
42
+ #
43
+ # takes :foo, :bar, :baz
44
+ # let(:hello) { 'world' }
45
+ # end
46
+ #
47
+ module Hoodie
48
+ module Konstruktor
49
+ def takes(*names)
50
+ attr_reader *names
51
+ include Konstruktor::Constructor(*names)
52
+ extend Konstruktor::Let
53
+ end
54
+ end
55
+
56
+ def self.Constructor(*names)
57
+ eval <<-RUBY
58
+
59
+ Module.new do
60
+ def initialize(#{names.join(', ')})
61
+ #{names.map{ |name| "@#{name} = #{name}" }.join("\n") }
62
+ end
63
+ end
64
+
65
+ RUBY
66
+ end
67
+
68
+ module Let
69
+ def let(name, &block)
70
+ define_method(name, &block)
71
+ end
72
+ end
73
+ end
74
+
75
+ class Object
76
+ extend Hoodie::Konstruktor
77
+ end
@@ -0,0 +1,83 @@
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
+ # Super simple state machine.
21
+ #
22
+ # @example
23
+ # class MyModel
24
+ # STATE_TRANSITIONS = TransitionTable.new(
25
+ # # State Input Next state Output
26
+ # [:awaiting_foo, :foo] => [:awaiting_bar, :do_stuff],
27
+ # [:awaiting_foo, :bar] => [:awaiting_foo, nil],
28
+ # [:awaiting_bar, :bar] => [:all_done, :do_other_stuff]
29
+ # )
30
+ #
31
+ # def initialize
32
+ # @state_machine = StateMachine.new(STATE_TRANSITIONS, :awaiting_foo)
33
+ # end
34
+ #
35
+ # def handle_event(event)
36
+ # action = @state_machine.send_input(event)
37
+ # send(action) unless action.nil?
38
+ # end
39
+ #
40
+ # def do_stuff
41
+ # # ...
42
+ # end
43
+ #
44
+ # def do_other_stuff
45
+ # # ...
46
+ # end
47
+ # end
48
+ #
49
+ module Hoodie
50
+ class Machine
51
+ def initialize(transition_function, initial_state)
52
+ @transition_function = transition_function
53
+ @state = initial_state
54
+ end
55
+
56
+ attr_reader :state
57
+
58
+ def send_input(input)
59
+ @state, output = @transition_function.call(@state, input)
60
+ output
61
+ end
62
+ end
63
+
64
+ class TransitionTable
65
+ class TransitionError < RuntimeError
66
+ def initialize(state, input)
67
+ super
68
+ "No transition from state #{state.inspect} for input #{input.inspect}"
69
+ end
70
+ end
71
+
72
+ def initialize(transitions)
73
+ @transitions = transitions
74
+ end
75
+
76
+ def call(state, input)
77
+ @transitions.fetch([state, input])
78
+ rescue KeyError
79
+ raise TransitionError.new(state, input)
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,56 @@
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
+ require 'rbconfig'
21
+
22
+ module Hoodie
23
+ # Finds out the current Operating System.
24
+ #
25
+ module OS
26
+ extend self
27
+
28
+ # @return [Boolean]
29
+ # Returns true if OS is Windows.
30
+ def windows?
31
+ windows = /cygwin|mswin|mingw|bccwin|wince|emx/i
32
+ (RbConfig::CONFIG['host_os'] =~ windows) != nil
33
+ end
34
+
35
+ # @return [Boolean]
36
+ # Returns true if OS is Mac.
37
+ def mac?
38
+ mac = /darwin|mac os/i
39
+ (RbConfig::CONFIG['host_os'] =~ mac) != nil
40
+ end
41
+
42
+ # @return [Boolean]
43
+ # Returns true if OS is Unix.
44
+ def unix?
45
+ unix = /solaris|bsd/i
46
+ (RbConfig::CONFIG['host_os'] =~ unix) != nil
47
+ end
48
+
49
+ # @return [Boolean]
50
+ # Returns true if OS is Linux.
51
+ def linux?
52
+ linux = /linux/i
53
+ (RbConfig::CONFIG['host_os'] =~ linux) != nil
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,235 @@
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
+ require_relative 'timeout'
21
+
22
+ module Hoodie
23
+ # Class methods that are added when you include Hoodie::Retry
24
+ #
25
+ module Retry
26
+ # Methods are also available as module-level methods as well as a mixin.
27
+ extend self
28
+
29
+ # Runs a code block, and retries it when an exception occurs. It is
30
+ # configured using four optional parameters `:tries`, `:on`, `:sleep`,
31
+ # `:match`, `:ensure` and runs the passed block. Should an exception
32
+ # occur, it'll retry for (n-1) times. Should the number of retries be
33
+ # reached without success, the last exception will be raised.
34
+ #
35
+ # @example open an URL, retry up to two times when an OpenURI::HTTPError
36
+ # occurs.
37
+ # retrier(tries: 3, on: OpenURI::HTTPError) do
38
+ # xml = open('http://example.com/test.html').read
39
+ # end
40
+ #
41
+ # @example do _something_, retry up to four times for either ArgumentErro
42
+ # or TimeoutError exceptions.
43
+ # retrier(tries: 5, on: [ArgumentError, TimeoutError]) do
44
+ # # _something_ code
45
+ # end
46
+ #
47
+ # @example ensure that block of code is executed, regardless of whether an
48
+ # exception was raised. It doesn't matter if the block exits normally,
49
+ # if it retries to execute block of code, or if it is terminated by an
50
+ # uncaught exception -- the ensure block will get run.
51
+ # f = File.open('testfile')
52
+ # ensure_cb = Proc.new do |retries|
53
+ # puts "total retry attempts: #{retries}"
54
+ # f.close
55
+ # end
56
+ # retrier(insure: ensure_cb) do
57
+ # # process file
58
+ # end
59
+ #
60
+ # @example sleeping: by default Retrier waits for one second between
61
+ # retries. You can change this and even provide your own exponential
62
+ # backoff scheme.
63
+ # retrier(sleep: 0) { } # don't pause between retries
64
+ # retrier(sleep: 10) { } # sleep 10s between retries
65
+ # retrier(sleep: ->(n) { 4**n }) { } # sleep 1, 4, 16, etc. each try
66
+ #
67
+ # @example matching error messages: you can also retry based on the
68
+ # exception message:
69
+ # retrier(matching: /IO timeout/) do |retries, exception|
70
+ # raise "yo, IO timeout!" if retries == 0
71
+ # end
72
+ #
73
+ # @example block parameters: your block is called with two optional
74
+ # parameters; the number of tries until now, and the most recent
75
+ # exception.
76
+ # retrier do |tries, exception|
77
+ # puts "try #{tries} failed with error: #{exception}" if retries > 0
78
+ # # keep trying...
79
+ # end
80
+ #
81
+ # @param opts [Hash]
82
+ #
83
+ # @option opts [Fixnum] :tries
84
+ # Number of attempts to retry before raising the last exception
85
+ #
86
+ # @option opts [Fixnum] :sleep
87
+ # Number of seconds to wait between retries, use lambda to exponentially
88
+ # increasing delay between retries.
89
+ #
90
+ # @option opts [Array(Exception)] :on
91
+ # The type of exception(s) to catch and retry on
92
+ #
93
+ # @option opts [Regex] :matching
94
+ # Match based on the exception message
95
+ #
96
+ # @option opts [Block] :ensure
97
+ # Ensure a block of code is executed, regardless of whether an exception
98
+ # is raised
99
+ #
100
+ # @yield [Proc]
101
+ # A block that will be run, and if it raises an error, re-run until
102
+ # success, or timeout is finally reached.
103
+ #
104
+ # @raise [Exception]
105
+ # Last Exception that caused the loop to retry before giving up.
106
+ #
107
+ # @return [Proc]
108
+ # The value of the passed block.
109
+ #
110
+ # @api public
111
+ def retrier(opts = {}, &_block)
112
+ tries = opts.fetch(:tries, 4)
113
+ wait = opts.fetch(:sleep, 1)
114
+ on = opts.fetch(:on, StandardError)
115
+ match = opts.fetch(:match, /.*/)
116
+ insure = opts.fetch(:ensure, Proc.new {})
117
+
118
+ retries = 0
119
+ retry_exception = nil
120
+
121
+ begin
122
+ yield retries, retry_exception
123
+ rescue *[on] => exception
124
+ raise unless exception.message =~ match
125
+ raise if retries + 1 >= tries
126
+
127
+ begin
128
+ sleep wait.respond_to?(:call) ? wait.call(retries) : wait
129
+ rescue *[on]
130
+ end
131
+
132
+ retries += 1
133
+ retry_exception = exception
134
+ retry
135
+ ensure
136
+ insure.call(retries)
137
+ end
138
+ end
139
+
140
+ # `#poll` is a method for knowing when something is ready. When your
141
+ # block yields true, execution continues. When your block yields false,
142
+ # poll keeps trying until it gives up and raises an error.
143
+ #
144
+ # @example wait up to 30 seconds for the TCP socket to respond.
145
+ # def wait_for_server
146
+ # poll(30) do
147
+ # begin
148
+ # TCPSocket.new(SERVER_IP, SERVER_PORT)
149
+ # true
150
+ # rescue Exception
151
+ # false
152
+ # end
153
+ # end
154
+ # end
155
+ #
156
+ # @param [Integer] wait
157
+ # The number of seconds seconds to poll.
158
+ #
159
+ # @param [Integer] delay
160
+ # Number of seconds to wait after encountering a failure, default is
161
+ # 0.1 seconds
162
+ #
163
+ # @yield [Proc]
164
+ # A block that determines whether polling should continue. Return
165
+ # `true` if the polling is complete. Return `false` if polling should
166
+ # continue.
167
+ #
168
+ # @raise [Hoodie::PollingError]
169
+ # Raised after too many failed attempts.
170
+ #
171
+ # @return [Proc]
172
+ # The value of the passed block.
173
+ #
174
+ # @api public
175
+ def poll(wait = 8, delay = 0.1)
176
+ try_until = Time.now + wait
177
+
178
+ while Time.now < try_until do
179
+ result = yield
180
+ return result if result
181
+ sleep delay
182
+ end
183
+ raise TimeoutError
184
+ end
185
+
186
+ # Similar to `#poll`, `#patiently` also executes an arbitrary code block.
187
+ # If the passed block runs without raising an error, execution proceeds
188
+ # normally. If an error is raised, the block is rerun after a brief
189
+ # delay, until the block can be run without exceptions. If exceptions
190
+ # continue to raise, `#patiently` gives up after a bit (default 8
191
+ # seconds) by re-raising the most recent exception raised by the block.
192
+ #
193
+ # @example
194
+ # Returns immedialtely if no errors or as soon as error stops.
195
+ # patiently { ... }
196
+ #
197
+ # Increase patience to 10 seconds.
198
+ # patiently(10) { ... }
199
+ #
200
+ # Increase patience to 20 seconds, and delay for 3 seconds before retry.
201
+ # patiently(20, 3) { ... }
202
+ #
203
+ # @param [Integer] seconds
204
+ # number of seconds to be patient, default is 8 seconds
205
+ #
206
+ # @param [Integer] delay
207
+ # seconds to wait after encountering a failure, default is 0.1 seconds
208
+ #
209
+ # @yield [Proc]
210
+ # A block that will be run, and if it raises an error, re-run until
211
+ # success, or patience runs out.
212
+ #
213
+ # @raise [Exception] the most recent Exception that caused the loop to
214
+ # retry before giving up.
215
+ #
216
+ # @return [Proc]
217
+ # the value of the passed block.
218
+ #
219
+ # @api public
220
+ def patiently(wait = 8, delay = 0.1)
221
+ try_until = Time.now + wait
222
+ failure = nil
223
+
224
+ while Time.now < try_until do
225
+ begin
226
+ return yield
227
+ rescue Exception => e
228
+ failure = e
229
+ sleep delay
230
+ end
231
+ end
232
+ failure ? (raise failure) : (raise TimeoutError)
233
+ end
234
+ end
235
+ end