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,238 @@
|
|
|
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
|
+
require_relative '../exceptions'
|
|
22
|
+
|
|
23
|
+
module Garcon
|
|
24
|
+
# Class methods that are added when you include Garcon::Retry
|
|
25
|
+
#
|
|
26
|
+
module Retry
|
|
27
|
+
# Methods are also available as module-level methods as well as a mixin.
|
|
28
|
+
extend self
|
|
29
|
+
|
|
30
|
+
include Garcon::Exceptions
|
|
31
|
+
|
|
32
|
+
# Runs a code block, and retries it when an exception occurs. It is
|
|
33
|
+
# configured using four optional parameters `:tries`, `:on`, `:sleep`,
|
|
34
|
+
# `:match`, `:ensure` and runs the passed block. Should an exception
|
|
35
|
+
# occur, it'll retry for (n-1) times. Should the number of retries be
|
|
36
|
+
# reached without success, the last exception will be raised.
|
|
37
|
+
#
|
|
38
|
+
# @example open an URL, retry up to two times when an OpenURI::HTTPError
|
|
39
|
+
# occurs.
|
|
40
|
+
# retrier(tries: 3, on: OpenURI::HTTPError) do
|
|
41
|
+
# xml = open('http://example.com/test.html').read
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# @example do _something_, retry up to four times for either ArgumentErro
|
|
45
|
+
# or TimeoutError exceptions.
|
|
46
|
+
# retrier(tries: 5, on: [ArgumentError, TimeoutError]) do
|
|
47
|
+
# # _something_ code
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# @example ensure that block of code is executed, regardless of whether an
|
|
51
|
+
# exception was raised. It doesn't matter if the block exits normally,
|
|
52
|
+
# if it retries to execute block of code, or if it is terminated by an
|
|
53
|
+
# uncaught exception -- the ensure block will get run.
|
|
54
|
+
# f = File.open('testfile')
|
|
55
|
+
# ensure_cb = Proc.new do |retries|
|
|
56
|
+
# puts "total retry attempts: #{retries}"
|
|
57
|
+
# f.close
|
|
58
|
+
# end
|
|
59
|
+
# retrier(insure: ensure_cb) do
|
|
60
|
+
# # process file
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# @example sleeping: by default Retrier waits for one second between
|
|
64
|
+
# retries. You can change this and even provide your own exponential
|
|
65
|
+
# backoff scheme.
|
|
66
|
+
# retrier(sleep: 0) { } # don't pause between retries
|
|
67
|
+
# retrier(sleep: 10) { } # sleep 10s between retries
|
|
68
|
+
# retrier(sleep: ->(n) { 4**n }) { } # sleep 1, 4, 16, etc. each try
|
|
69
|
+
#
|
|
70
|
+
# @example matching error messages: you can also retry based on the
|
|
71
|
+
# exception message:
|
|
72
|
+
# retrier(matching: /IO timeout/) do |retries, exception|
|
|
73
|
+
# raise "yo, IO timeout!" if retries == 0
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
# @example block parameters: your block is called with two optional
|
|
77
|
+
# parameters; the number of tries until now, and the most recent
|
|
78
|
+
# exception.
|
|
79
|
+
# retrier do |tries, exception|
|
|
80
|
+
# puts "try #{tries} failed with error: #{exception}" if retries > 0
|
|
81
|
+
# # keep trying...
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# @param opts [Hash]
|
|
85
|
+
#
|
|
86
|
+
# @option opts [Fixnum] :tries
|
|
87
|
+
# Number of attempts to retry before raising the last exception
|
|
88
|
+
#
|
|
89
|
+
# @option opts [Fixnum] :sleep
|
|
90
|
+
# Number of seconds to wait between retries, use lambda to exponentially
|
|
91
|
+
# increasing delay between retries.
|
|
92
|
+
#
|
|
93
|
+
# @option opts [Array(Exception)] :on
|
|
94
|
+
# The type of exception(s) to catch and retry on
|
|
95
|
+
#
|
|
96
|
+
# @option opts [Regex] :matching
|
|
97
|
+
# Match based on the exception message
|
|
98
|
+
#
|
|
99
|
+
# @option opts [Block] :ensure
|
|
100
|
+
# Ensure a block of code is executed, regardless of whether an exception
|
|
101
|
+
# is raised
|
|
102
|
+
#
|
|
103
|
+
# @yield [Proc]
|
|
104
|
+
# A block that will be run, and if it raises an error, re-run until
|
|
105
|
+
# success, or timeout is finally reached.
|
|
106
|
+
#
|
|
107
|
+
# @raise [Exception]
|
|
108
|
+
# Last Exception that caused the loop to retry before giving up.
|
|
109
|
+
#
|
|
110
|
+
# @return [Proc]
|
|
111
|
+
# The value of the passed block.
|
|
112
|
+
#
|
|
113
|
+
# @api public
|
|
114
|
+
def retrier(opts = {}, &_block)
|
|
115
|
+
tries = opts.fetch(:tries, 4)
|
|
116
|
+
wait = opts.fetch(:sleep, 1)
|
|
117
|
+
on = opts.fetch(:on, StandardError)
|
|
118
|
+
match = opts.fetch(:match, /.*/)
|
|
119
|
+
insure = opts.fetch(:ensure, Proc.new {})
|
|
120
|
+
|
|
121
|
+
retries = 0
|
|
122
|
+
retry_exception = nil
|
|
123
|
+
|
|
124
|
+
begin
|
|
125
|
+
yield retries, retry_exception
|
|
126
|
+
rescue *[on] => e
|
|
127
|
+
raise unless e.message =~ match
|
|
128
|
+
raise if retries + 1 >= tries
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
sleep wait.respond_to?(:call) ? wait.call(retries) : wait
|
|
132
|
+
rescue *[on]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
retries += 1
|
|
136
|
+
retry_exception = exception
|
|
137
|
+
retry
|
|
138
|
+
ensure
|
|
139
|
+
insure.call(retries)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# `#poll` is a method for knowing when something is ready. When your
|
|
144
|
+
# block yields true, execution continues. When your block yields false,
|
|
145
|
+
# poll keeps trying until it gives up and raises an error.
|
|
146
|
+
#
|
|
147
|
+
# @example wait up to 30 seconds for the TCP socket to respond.
|
|
148
|
+
# def wait_for_server
|
|
149
|
+
# poll(30) do
|
|
150
|
+
# begin
|
|
151
|
+
# TCPSocket.new(SERVER_IP, SERVER_PORT)
|
|
152
|
+
# true
|
|
153
|
+
# rescue Exception
|
|
154
|
+
# false
|
|
155
|
+
# end
|
|
156
|
+
# end
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# @param [Integer] wait
|
|
160
|
+
# The number of seconds seconds to poll.
|
|
161
|
+
#
|
|
162
|
+
# @param [Integer] delay
|
|
163
|
+
# Number of seconds to wait after encountering a failure, default is
|
|
164
|
+
# 0.1 seconds
|
|
165
|
+
#
|
|
166
|
+
# @yield [Proc]
|
|
167
|
+
# A block that determines whether polling should continue. Return
|
|
168
|
+
# `true` if the polling is complete. Return `false` if polling should
|
|
169
|
+
# continue.
|
|
170
|
+
#
|
|
171
|
+
# @raise [Garcon::PollingError]
|
|
172
|
+
# Raised after too many failed attempts.
|
|
173
|
+
#
|
|
174
|
+
# @return [Proc]
|
|
175
|
+
# The value of the passed block.
|
|
176
|
+
#
|
|
177
|
+
# @api public
|
|
178
|
+
def poll(wait = 8, delay = 0.1)
|
|
179
|
+
try_until = Time.now + wait
|
|
180
|
+
|
|
181
|
+
while Time.now < try_until do
|
|
182
|
+
result = yield
|
|
183
|
+
return result if result
|
|
184
|
+
sleep delay
|
|
185
|
+
end
|
|
186
|
+
raise TimeoutError
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Similar to `#poll`, `#patiently` also executes an arbitrary code block.
|
|
190
|
+
# If the passed block runs without raising an error, execution proceeds
|
|
191
|
+
# normally. If an error is raised, the block is rerun after a brief
|
|
192
|
+
# delay, until the block can be run without exceptions. If exceptions
|
|
193
|
+
# continue to raise, `#patiently` gives up after a bit (default 8
|
|
194
|
+
# seconds) by re-raising the most recent exception raised by the block.
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# Returns immedialtely if no errors or as soon as error stops.
|
|
198
|
+
# patiently { ... }
|
|
199
|
+
#
|
|
200
|
+
# Increase patience to 10 seconds.
|
|
201
|
+
# patiently(10) { ... }
|
|
202
|
+
#
|
|
203
|
+
# Increase patience to 20 seconds, and delay for 3 seconds before retry.
|
|
204
|
+
# patiently(20, 3) { ... }
|
|
205
|
+
#
|
|
206
|
+
# @param [Integer] seconds
|
|
207
|
+
# number of seconds to be patient, default is 8 seconds
|
|
208
|
+
#
|
|
209
|
+
# @param [Integer] delay
|
|
210
|
+
# seconds to wait after encountering a failure, default is 0.1 seconds
|
|
211
|
+
#
|
|
212
|
+
# @yield [Proc]
|
|
213
|
+
# A block that will be run, and if it raises an error, re-run until
|
|
214
|
+
# success, or patience runs out.
|
|
215
|
+
#
|
|
216
|
+
# @raise [Exception] the most recent Exception that caused the loop to
|
|
217
|
+
# retry before giving up.
|
|
218
|
+
#
|
|
219
|
+
# @return [Proc]
|
|
220
|
+
# the value of the passed block.
|
|
221
|
+
#
|
|
222
|
+
# @api public
|
|
223
|
+
def patiently(wait = 8, delay = 0.1)
|
|
224
|
+
try_until = Time.now + wait
|
|
225
|
+
failure = nil
|
|
226
|
+
|
|
227
|
+
while Time.now < try_until do
|
|
228
|
+
begin
|
|
229
|
+
return yield
|
|
230
|
+
rescue Exception => e
|
|
231
|
+
failure = e
|
|
232
|
+
sleep delay
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
failure ? (raise failure) : (raise TimeoutError)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
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 'thread'
|
|
21
|
+
require_relative '../exceptions'
|
|
22
|
+
|
|
23
|
+
module Garcon
|
|
24
|
+
# Class methods that are added when you include Garcon::Timeout
|
|
25
|
+
#
|
|
26
|
+
module Timeout
|
|
27
|
+
# Methods are also available as module-level methods as well as a mixin.
|
|
28
|
+
extend self
|
|
29
|
+
|
|
30
|
+
include Garcon::Exceptions
|
|
31
|
+
|
|
32
|
+
# Wait the given number of seconds for the block operation to complete.
|
|
33
|
+
# Intended to be a simpler and more reliable replacement to the Ruby
|
|
34
|
+
# standard library `Timeout::timeout` method.
|
|
35
|
+
#
|
|
36
|
+
# @param [Integer] seconds
|
|
37
|
+
# Number of seconds to wait for the block to terminate. Any number may
|
|
38
|
+
# be used, including Floats to specify fractional seconds. A value of 0
|
|
39
|
+
# or nil will execute the block without any timeout.
|
|
40
|
+
#
|
|
41
|
+
# @return [Object]
|
|
42
|
+
# Result of the block if the block completed before the timeout,
|
|
43
|
+
# otherwise raises a TimeoutError exception.
|
|
44
|
+
#
|
|
45
|
+
# @raise [Garcon::TimeoutError]
|
|
46
|
+
# When the block operation does not complete in the alloted time.
|
|
47
|
+
#
|
|
48
|
+
# @see Ruby Timeout::timeout
|
|
49
|
+
#
|
|
50
|
+
# @api public
|
|
51
|
+
def timeout(seconds)
|
|
52
|
+
thread = Thread.new { Thread.current[:result] = yield }
|
|
53
|
+
thread.join(seconds) ? (return thread[:result]) : (raise TimeoutError)
|
|
54
|
+
ensure
|
|
55
|
+
Thread.kill(thread) unless thread.nil?
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Uber
|
|
2
|
+
# When included, allows to add builder on the class level.
|
|
3
|
+
#
|
|
4
|
+
# class Operation
|
|
5
|
+
# include Uber::Builder
|
|
6
|
+
#
|
|
7
|
+
# builds do |params|
|
|
8
|
+
# SignedIn if params[:current_user]
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# class SignedIn
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# The class then has to call the builder to compute a class name using the build blocks you defined.
|
|
15
|
+
#
|
|
16
|
+
# def self.build(params)
|
|
17
|
+
# class_builder.call(params).
|
|
18
|
+
# new(params)
|
|
19
|
+
# end
|
|
20
|
+
module Builder
|
|
21
|
+
def self.included(base)
|
|
22
|
+
base.class_eval do
|
|
23
|
+
def self.builders
|
|
24
|
+
@builders ||= []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
extend ClassMethods
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Constant
|
|
32
|
+
def initialize(constant) # TODO: evaluate usage of builders and implement using Uber::Options::Value.
|
|
33
|
+
@constant = constant
|
|
34
|
+
@builders = @constant.builders # only dependency, must be a Cell::Base subclass.
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call(*args)
|
|
38
|
+
build_class_for(*args)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def build_class_for(*args)
|
|
43
|
+
@builders.each do |blk|
|
|
44
|
+
klass = run_builder_block(blk, *args) and return klass
|
|
45
|
+
end
|
|
46
|
+
@constant
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def run_builder_block(block, *args)
|
|
50
|
+
block.call(*args)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module ClassMethods
|
|
55
|
+
# Adds a builder to the cell class. Builders are used in #cell to find out the concrete
|
|
56
|
+
# class for rendering. This is helpful if you frequently want to render subclasses according
|
|
57
|
+
# to different circumstances (e.g. login situations) and you don't want to place these deciders in
|
|
58
|
+
# your view code.
|
|
59
|
+
#
|
|
60
|
+
# Passes the model and options from #cell into the block.
|
|
61
|
+
#
|
|
62
|
+
# Multiple build blocks are ORed, if no builder matches the building cell is used.
|
|
63
|
+
#
|
|
64
|
+
# Example:
|
|
65
|
+
#
|
|
66
|
+
# Consider two different user box cells in your app.
|
|
67
|
+
#
|
|
68
|
+
# class AuthorizedUserBox < UserInfoBox
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# class AdminUserBox < UserInfoBox
|
|
72
|
+
# end
|
|
73
|
+
#
|
|
74
|
+
# Now you don't want to have deciders all over your views - use a declarative builder.
|
|
75
|
+
#
|
|
76
|
+
# UserInfoBox.build do |model, options|
|
|
77
|
+
# AuthorizedUserBox if options[:is_signed_in]
|
|
78
|
+
# AdminUserBox if model.admin?
|
|
79
|
+
# end
|
|
80
|
+
#
|
|
81
|
+
# In your view #cell will instantiate the right cell for you now.
|
|
82
|
+
def builds(proc=nil, &block)
|
|
83
|
+
builders << (proc.kind_of?(Proc) ? proc : block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def class_builder
|
|
87
|
+
@class_builder ||= Constant.new(self)
|
|
88
|
+
end
|
|
89
|
+
end # ClassMethods
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Uber
|
|
2
|
+
module InheritableAttr
|
|
3
|
+
def inheritable_attr(name)
|
|
4
|
+
instance_eval %Q{
|
|
5
|
+
def #{name}=(v)
|
|
6
|
+
@#{name} = v
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def #{name}
|
|
10
|
+
return @#{name} if instance_variable_defined?(:@#{name})
|
|
11
|
+
@#{name} = InheritableAttribute.inherit_for(self, :#{name})
|
|
12
|
+
end
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.inherit_for(klass, name)
|
|
17
|
+
return unless klass.superclass.respond_to?(name)
|
|
18
|
+
|
|
19
|
+
value = klass.superclass.send(name) # could be nil.
|
|
20
|
+
Clone.(value) # this could be dynamic, allowing other inheritance strategies.
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Clone
|
|
24
|
+
# The second argument allows injecting more types.
|
|
25
|
+
def self.call(value, uncloneable=uncloneable())
|
|
26
|
+
uncloneable.each { |klass| return value if value.kind_of?(klass) }
|
|
27
|
+
value.clone
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.uncloneable
|
|
31
|
+
[Symbol, TrueClass, FalseClass, NilClass]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
InheritableAttribute = InheritableAttr
|
|
37
|
+
end
|