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.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +17 -0
  3. data/.gitignore +197 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE +201 -0
  7. data/README.md +521 -0
  8. data/Rakefile +47 -0
  9. data/garcun.gemspec +83 -0
  10. data/lib/garcon.rb +290 -0
  11. data/lib/garcon/chef/chef_helpers.rb +343 -0
  12. data/lib/garcon/chef/coerce/coercer.rb +134 -0
  13. data/lib/garcon/chef/coerce/coercions/boolean_definitions.rb +34 -0
  14. data/lib/garcon/chef/coerce/coercions/date_definitions.rb +32 -0
  15. data/lib/garcon/chef/coerce/coercions/date_time_definitions.rb +32 -0
  16. data/lib/garcon/chef/coerce/coercions/fixnum_definitions.rb +34 -0
  17. data/lib/garcon/chef/coerce/coercions/float_definitions.rb +32 -0
  18. data/lib/garcon/chef/coerce/coercions/hash_definitions.rb +29 -0
  19. data/lib/garcon/chef/coerce/coercions/integer_definitions.rb +31 -0
  20. data/lib/garcon/chef/coerce/coercions/string_definitions.rb +45 -0
  21. data/lib/garcon/chef/coerce/coercions/time_definitions.rb +32 -0
  22. data/lib/garcon/chef/handler/devreporter.rb +127 -0
  23. data/lib/garcon/chef/log.rb +64 -0
  24. data/lib/garcon/chef/node.rb +100 -0
  25. data/lib/garcon/chef/provider/civilize.rb +209 -0
  26. data/lib/garcon/chef/provider/development.rb +159 -0
  27. data/lib/garcon/chef/provider/download.rb +420 -0
  28. data/lib/garcon/chef/provider/house_keeping.rb +265 -0
  29. data/lib/garcon/chef/provider/node_cache.rb +31 -0
  30. data/lib/garcon/chef/provider/partial.rb +183 -0
  31. data/lib/garcon/chef/provider/recovery.rb +80 -0
  32. data/lib/garcon/chef/provider/zip_file.rb +271 -0
  33. data/lib/garcon/chef/resource/attribute.rb +52 -0
  34. data/lib/garcon/chef/resource/base_dsl.rb +174 -0
  35. data/lib/garcon/chef/resource/blender.rb +140 -0
  36. data/lib/garcon/chef/resource/lazy_eval.rb +66 -0
  37. data/lib/garcon/chef/resource/resource_name.rb +109 -0
  38. data/lib/garcon/chef/secret_bag.rb +204 -0
  39. data/lib/garcon/chef/validations.rb +76 -0
  40. data/lib/garcon/chef_inclusions.rb +151 -0
  41. data/lib/garcon/configuration.rb +138 -0
  42. data/lib/garcon/core_ext.rb +39 -0
  43. data/lib/garcon/core_ext/array.rb +27 -0
  44. data/lib/garcon/core_ext/binding.rb +64 -0
  45. data/lib/garcon/core_ext/boolean.rb +66 -0
  46. data/lib/garcon/core_ext/duration.rb +271 -0
  47. data/lib/garcon/core_ext/enumerable.rb +34 -0
  48. data/lib/garcon/core_ext/file.rb +127 -0
  49. data/lib/garcon/core_ext/filetest.rb +62 -0
  50. data/lib/garcon/core_ext/hash.rb +279 -0
  51. data/lib/garcon/core_ext/kernel.rb +159 -0
  52. data/lib/garcon/core_ext/lazy.rb +222 -0
  53. data/lib/garcon/core_ext/method_access.rb +243 -0
  54. data/lib/garcon/core_ext/module.rb +92 -0
  55. data/lib/garcon/core_ext/nil.rb +53 -0
  56. data/lib/garcon/core_ext/numeric.rb +44 -0
  57. data/lib/garcon/core_ext/object.rb +342 -0
  58. data/lib/garcon/core_ext/pathname.rb +152 -0
  59. data/lib/garcon/core_ext/process.rb +41 -0
  60. data/lib/garcon/core_ext/random.rb +497 -0
  61. data/lib/garcon/core_ext/string.rb +312 -0
  62. data/lib/garcon/core_ext/struct.rb +49 -0
  63. data/lib/garcon/core_ext/symbol.rb +170 -0
  64. data/lib/garcon/core_ext/time.rb +234 -0
  65. data/lib/garcon/exceptions.rb +101 -0
  66. data/lib/garcon/inflections.rb +237 -0
  67. data/lib/garcon/inflections/defaults.rb +79 -0
  68. data/lib/garcon/inflections/inflections.rb +182 -0
  69. data/lib/garcon/inflections/rules_collection.rb +37 -0
  70. data/lib/garcon/secret.rb +271 -0
  71. data/lib/garcon/stash/format.rb +114 -0
  72. data/lib/garcon/stash/journal.rb +226 -0
  73. data/lib/garcon/stash/queue.rb +83 -0
  74. data/lib/garcon/stash/serializer.rb +86 -0
  75. data/lib/garcon/stash/store.rb +435 -0
  76. data/lib/garcon/task.rb +31 -0
  77. data/lib/garcon/task/atomic.rb +151 -0
  78. data/lib/garcon/task/atomic_boolean.rb +127 -0
  79. data/lib/garcon/task/condition.rb +99 -0
  80. data/lib/garcon/task/copy_on_notify_observer_set.rb +154 -0
  81. data/lib/garcon/task/copy_on_write_observer_set.rb +153 -0
  82. data/lib/garcon/task/count_down_latch.rb +92 -0
  83. data/lib/garcon/task/delay.rb +196 -0
  84. data/lib/garcon/task/dereferenceable.rb +144 -0
  85. data/lib/garcon/task/event.rb +119 -0
  86. data/lib/garcon/task/executor.rb +275 -0
  87. data/lib/garcon/task/executor_options.rb +59 -0
  88. data/lib/garcon/task/future.rb +107 -0
  89. data/lib/garcon/task/immediate_executor.rb +84 -0
  90. data/lib/garcon/task/ivar.rb +171 -0
  91. data/lib/garcon/task/lazy_reference.rb +74 -0
  92. data/lib/garcon/task/monotonic_time.rb +69 -0
  93. data/lib/garcon/task/obligation.rb +256 -0
  94. data/lib/garcon/task/observable.rb +101 -0
  95. data/lib/garcon/task/priority_queue.rb +234 -0
  96. data/lib/garcon/task/processor_count.rb +128 -0
  97. data/lib/garcon/task/read_write_lock.rb +304 -0
  98. data/lib/garcon/task/safe_task_executor.rb +58 -0
  99. data/lib/garcon/task/single_thread_executor.rb +97 -0
  100. data/lib/garcon/task/thread_pool/cached.rb +71 -0
  101. data/lib/garcon/task/thread_pool/executor.rb +294 -0
  102. data/lib/garcon/task/thread_pool/fixed.rb +61 -0
  103. data/lib/garcon/task/thread_pool/worker.rb +90 -0
  104. data/lib/garcon/task/timer.rb +44 -0
  105. data/lib/garcon/task/timer_set.rb +194 -0
  106. data/lib/garcon/task/timer_task.rb +377 -0
  107. data/lib/garcon/task/waitable_list.rb +58 -0
  108. data/lib/garcon/utility/ansi.rb +199 -0
  109. data/lib/garcon/utility/at_random.rb +77 -0
  110. data/lib/garcon/utility/crypto.rb +292 -0
  111. data/lib/garcon/utility/equalizer.rb +146 -0
  112. data/lib/garcon/utility/faker/extensions/array.rb +22 -0
  113. data/lib/garcon/utility/faker/extensions/symbol.rb +9 -0
  114. data/lib/garcon/utility/faker/faker.rb +164 -0
  115. data/lib/garcon/utility/faker/faker/company.rb +17 -0
  116. data/lib/garcon/utility/faker/faker/hacker.rb +30 -0
  117. data/lib/garcon/utility/faker/faker/version.rb +3 -0
  118. data/lib/garcon/utility/faker/locales/en-US.yml +83 -0
  119. data/lib/garcon/utility/faker/locales/en.yml +21 -0
  120. data/lib/garcon/utility/file_helper.rb +170 -0
  121. data/lib/garcon/utility/hookers.rb +178 -0
  122. data/lib/garcon/utility/interpolation.rb +90 -0
  123. data/lib/garcon/utility/memstash.rb +364 -0
  124. data/lib/garcon/utility/misc.rb +54 -0
  125. data/lib/garcon/utility/msg_from_god.rb +62 -0
  126. data/lib/garcon/utility/retry.rb +238 -0
  127. data/lib/garcon/utility/timeout.rb +58 -0
  128. data/lib/garcon/utility/uber/builder.rb +91 -0
  129. data/lib/garcon/utility/uber/callable.rb +7 -0
  130. data/lib/garcon/utility/uber/delegates.rb +13 -0
  131. data/lib/garcon/utility/uber/inheritable_attr.rb +37 -0
  132. data/lib/garcon/utility/uber/options.rb +101 -0
  133. data/lib/garcon/utility/uber/uber_version.rb +3 -0
  134. data/lib/garcon/utility/uber/version.rb +33 -0
  135. data/lib/garcon/utility/url_helper.rb +100 -0
  136. data/lib/garcon/utils.rb +29 -0
  137. data/lib/garcon/version.rb +62 -0
  138. data/lib/garcun.rb +24 -0
  139. metadata +680 -0
@@ -0,0 +1,153 @@
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
+
22
+ # A thread safe observer set implemented using copy-on-write approach:
23
+ # every time an observer is added or removed the whole internal data structure
24
+ # is duplicated and replaced with a new one.
25
+ #
26
+ class CopyOnWriteObserverSet
27
+
28
+ def initialize
29
+ @mutex = Mutex.new
30
+ @observers = {}
31
+ end
32
+
33
+ # Adds an observer to this set. If a block is passed, the observer will be
34
+ # created by this method and no other params should be passed
35
+ #
36
+ # @param [Object] observer
37
+ # the observer to add
38
+ #
39
+ # @param [Symbol] func
40
+ # the function to call on the observer during notification. The default
41
+ # is :update.
42
+ #
43
+ # @return [Object]
44
+ # the added observer
45
+ def add_observer(observer=nil, func=:update, &block)
46
+ if observer.nil? && block.nil?
47
+ raise ArgumentError, 'should pass observer as a first argument or block'
48
+ elsif observer && block
49
+ raise ArgumentError.new('cannot provide both an observer and a block')
50
+ end
51
+
52
+ if block
53
+ observer = block
54
+ func = :call
55
+ end
56
+
57
+ begin
58
+ @mutex.lock
59
+ new_observers = @observers.dup
60
+ new_observers[observer] = func
61
+ @observers = new_observers
62
+ observer
63
+ ensure
64
+ @mutex.unlock
65
+ end
66
+ end
67
+
68
+ # @param [Object] observer
69
+ # the observer to remove
70
+ # @return [Object]
71
+ # the deleted observer
72
+ def delete_observer(observer)
73
+ @mutex.lock
74
+ new_observers = @observers.dup
75
+ new_observers.delete(observer)
76
+ @observers = new_observers
77
+ observer
78
+ ensure
79
+ @mutex.unlock
80
+ end
81
+
82
+ # Deletes all observers
83
+ # @return [CopyOnWriteObserverSet] self
84
+ def delete_observers
85
+ self.observers = {}
86
+ self
87
+ end
88
+
89
+
90
+ # @return [Integer] the observers count
91
+ def count_observers
92
+ observers.count
93
+ end
94
+
95
+ # Notifies all registered observers with optional args
96
+ #
97
+ # @param [Object] args
98
+ # Arguments to be passed to each observer
99
+ #
100
+ # @return [CopyOnWriteObserverSet] self
101
+ def notify_observers(*args, &block)
102
+ notify_to(observers, *args, &block)
103
+ self
104
+ end
105
+
106
+ # Notifies all registered observers with optional args and deletes them.
107
+ #
108
+ # @param [Object] args
109
+ # Arguments to be passed to each observer
110
+ #
111
+ # @return [CopyOnWriteObserverSet] self
112
+ def notify_and_delete_observers(*args, &block)
113
+ old = clear_observers_and_return_old
114
+ notify_to(old, *args, &block)
115
+ self
116
+ end
117
+
118
+ private # P R O P R I E T À P R I V A T A Vietato L'accesso
119
+
120
+ def notify_to(observers, *args)
121
+ if block_given? && !args.empty?
122
+ raise ArgumentError.new 'cannot give arguments and a block'
123
+ end
124
+ observers.each do |observer, function|
125
+ args = yield if block_given?
126
+ observer.send(function, *args)
127
+ end
128
+ end
129
+
130
+ def observers
131
+ @mutex.lock
132
+ @observers
133
+ ensure
134
+ @mutex.unlock
135
+ end
136
+
137
+ def observers=(new_set)
138
+ @mutex.lock
139
+ @observers = new_set
140
+ ensure
141
+ @mutex.unlock
142
+ end
143
+
144
+ def clear_observers_and_return_old
145
+ @mutex.lock
146
+ old_observers = @observers
147
+ @observers = {}
148
+ old_observers
149
+ ensure
150
+ @mutex.unlock
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,92 @@
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 'condition'
21
+
22
+ module Garcon
23
+
24
+ # A synchronization object that allows one thread to wait on multiple other
25
+ # threads. The thread that will wait creates a `CountDownLatch` and sets the
26
+ # initial value (normally equal to the number of other threads). The
27
+ # initiating thread passes the latch to the other threads then waits for the
28
+ # other threads by calling the `#wait` method. Each of the other threads
29
+ # calls `#count_down` when done with its work. When the latch counter reaches
30
+ # zero the waiting thread is unblocked and continues with its work. A
31
+ # `CountDownLatch` can be used only once. Its value cannot be reset.
32
+ #
33
+ class MutexCountDownLatch
34
+
35
+ # Create a new `CountDownLatch` with the initial `count`.
36
+ #
37
+ # @param [Fixnum] count
38
+ # The initial count
39
+ #
40
+ # @raise [ArgumentError]
41
+ # If `count` is not an integer or is less than zero.
42
+ #
43
+ def initialize(count = 1)
44
+ unless count.is_a?(Fixnum) && count >= 0
45
+ raise ArgumentError, 'count must be greater than or equal zero'
46
+ end
47
+ @mutex = Mutex.new
48
+ @condition = Condition.new
49
+ @count = count
50
+ end
51
+
52
+ # Block on the latch until the counter reaches zero or until `timeout` is
53
+ # reached.
54
+ #
55
+ # @param [Fixnum] timeout
56
+ # The number of seconds to wait for the counter or `nil` to block
57
+ # indefinitely.
58
+ # @return [Boolean]
59
+ # True if the count reaches zero else false on timeout.
60
+ #
61
+ def wait(timeout = nil)
62
+ @mutex.synchronize do
63
+ remaining = Condition::Result.new(timeout)
64
+ while @count > 0 && remaining.can_wait?
65
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
66
+ end
67
+ @count == 0
68
+ end
69
+ end
70
+
71
+ # Signal the latch to decrement the counter. Will signal all blocked
72
+ # threads when the `count` reaches zero.
73
+ #
74
+ def count_down
75
+ @mutex.synchronize do
76
+ @count -= 1 if @count > 0
77
+ @condition.broadcast if @count == 0
78
+ end
79
+ end
80
+
81
+ # The current value of the counter.
82
+ #
83
+ # @return [Fixnum]
84
+ # The current value of the counter.
85
+ #
86
+ def count
87
+ @mutex.synchronize { @count }
88
+ end
89
+ end
90
+
91
+ class CountDownLatch < MutexCountDownLatch; end
92
+ end
@@ -0,0 +1,196 @@
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 'obligation'
22
+ require_relative 'executor_options'
23
+ require_relative 'immediate_executor'
24
+
25
+ module Garcon
26
+
27
+ # Lazy evaluation of a block yielding an immutable result. Useful for
28
+ # expensive operations that may never be needed. It may be non-blocking,
29
+ # supports the `Obligation` interface, and accepts the injection of custom
30
+ # executor upon which to execute the block. Processing of block will be
31
+ # deferred until the first time `#value` is called. At that time the caller
32
+ # can choose to return immediately and let the block execute asynchronously,
33
+ # block indefinitely, or block with a timeout.
34
+ #
35
+ # When a `Delay` is created its state is set to `pending`. The value and
36
+ # reason are both `nil`. The first time the `#value` method is called the
37
+ # enclosed opration will be run and the calling thread will block. Other
38
+ # threads attempting to call `#value` will block as well. Once the operation
39
+ # is complete the *value* will be set to the result of the operation or the
40
+ # *reason* will be set to the raised exception, as appropriate. All threads
41
+ # blocked on `#value` will return. Subsequent calls to `#value` will
42
+ # immediately return the cached value. The operation will only be run once.
43
+ # This means that any side effects created by the operation will only happen
44
+ # once as well.
45
+ #
46
+ # `Delay` includes the `Garcon::Dereferenceable` mixin to support thread
47
+ # safety of the reference returned by `#value`.
48
+ #
49
+ # @!macro [attach] delay_note_regarding_blocking
50
+ # @note The default behavior of `Delay` is to block indefinitely when
51
+ # calling either `value` or `wait`, executing the delayed operation on
52
+ # the current thread. This makes the `timeout` value completely
53
+ # irrelevant. To enable non-blocking behavior, use the `executor`
54
+ # constructor option. This will cause the delayed operation to be
55
+ # execute on the given executor, allowing the call to timeout.
56
+ #
57
+ # @see Garcon::Dereferenceable
58
+ class Delay
59
+ include Obligation
60
+ include ExecutorOptions
61
+
62
+ # Create a new `Delay` in the `:pending` state.
63
+ #
64
+ # @yield the delayed operation to perform
65
+ #
66
+ # @raise [ArgumentError] if no block is given
67
+ #
68
+ def initialize(opts = {}, &block)
69
+ raise ArgumentError, 'no block given' unless block_given?
70
+ init_obligation
71
+ set_deref_options(opts)
72
+ @task_executor = get_executor_from(opts)
73
+ @task = block
74
+ @state = :pending
75
+ @computing = false
76
+ end
77
+
78
+ # Return the value this object represents after applying the options
79
+ # specified by the `#set_deref_options` method. If the delayed operation
80
+ # raised an exception this method will return nil. The execption object
81
+ # can be accessed via the `#reason` method.
82
+ #
83
+ # @param [Numeric]
84
+ # Timeout the maximum number of seconds to wait.
85
+ #
86
+ # @return [Object] the current value of the object.
87
+ #
88
+ def value(timeout = nil)
89
+ if @task_executor
90
+ super
91
+ else
92
+ mutex.synchronize do
93
+ execute = @computing = true unless @computing
94
+ if execute
95
+ begin
96
+ set_state(true, @task.call, nil)
97
+ rescue => e
98
+ set_state(false, nil, e)
99
+ end
100
+ end
101
+ end
102
+ if @do_nothing_on_deref
103
+ @value
104
+ else
105
+ apply_deref_options(@value)
106
+ end
107
+ end
108
+ end
109
+
110
+ # Return the value this object represents after applying the options
111
+ # specified by the `#set_deref_options` method. If the delayed operation
112
+ # raised an exception, this method will raise that exception (even when)
113
+ # the operation has already been executed).
114
+ #
115
+ # @param [Numeric]
116
+ # Timeout the maximum number of seconds to wait.
117
+ #
118
+ # @raise [Exception] when `#rejected?` raises `#reason`.
119
+ #
120
+ # @return [Object]
121
+ # The current value of the object.
122
+ #
123
+ def value!(timeout = nil)
124
+ if @task_executor
125
+ super
126
+ else
127
+ result = value
128
+ raise @reason if @reason
129
+ result
130
+ end
131
+ end
132
+
133
+ # Return the value this object represents after applying the options
134
+ # specified by the `#set_deref_options` method.
135
+ #
136
+ # @param [Integer]
137
+ # Timeout (nil) the maximum number of seconds to wait for the value to be
138
+ # computed. When `nil` the caller will block indefinitely.
139
+ #
140
+ # @return [Object] self
141
+ #
142
+ def wait(timeout = nil)
143
+ if @task_executor
144
+ execute_task_once
145
+ super(timeout)
146
+ else
147
+ value
148
+ end
149
+ self
150
+ end
151
+
152
+ # Reconfigures the block returning the value if still `#incomplete?`
153
+ #
154
+ # @yield the delayed operation to perform
155
+ #
156
+ # @return [true, false] if success
157
+ #
158
+ def reconfigure(&block)
159
+ mutex.lock
160
+ raise ArgumentError.new('no block given') unless block_given?
161
+ unless @computing
162
+ @task = block
163
+ true
164
+ else
165
+ false
166
+ end
167
+ ensure
168
+ mutex.unlock
169
+ end
170
+
171
+ private
172
+
173
+ # @!visibility private
174
+ def execute_task_once
175
+ mutex.lock
176
+ execute = @computing = true unless @computing
177
+ task = @task
178
+ mutex.unlock
179
+
180
+ if execute
181
+ @task_executor.post do
182
+ begin
183
+ result = task.call
184
+ success = true
185
+ rescue => e
186
+ reason = e
187
+ end
188
+ mutex.lock
189
+ set_state(success, result, reason)
190
+ event.set
191
+ mutex.unlock
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,144 @@
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
+
22
+ # Object references in Ruby are mutable. This can lead to serious problems
23
+ # when the `#value` of a concurrent object is a mutable reference. Which is
24
+ # always the case unless the value is a `Fixnum`, `Symbol`, or similar
25
+ # "primitive" data type. Most classes in this library that expose a `#value`
26
+ # getter method do so using the `Dereferenceable` mixin module.
27
+ #
28
+ # Objects with this mixin can be configured with a few options that can help
29
+ # protect the program from potentially dangerous operations.
30
+ #
31
+ # * `:dup_on_deref`: when true will call the `#dup` method on the `value`
32
+ # object every time the `#value` method is called (default: false)
33
+ # * `:freeze_on_deref`: when true will call the `#freeze` method on the
34
+ # `value` object every time the `#value` method is called (default: false)
35
+ # * `:copy_on_deref`: when given a `Proc` object the `Proc` will be run every
36
+ # time the `#value` method is called. The `Proc` will be given the current
37
+ # `value` as its only parameter and the result returned by the block will
38
+ # be the return value of the `#value` call. When `nil` this option will be
39
+ # ignored (default: nil)
40
+ #
41
+ module Dereferenceable
42
+
43
+ # Return the value this object represents after applying the options
44
+ # specified by the `#set_deref_options` method.
45
+ #
46
+ # When multiple deref options are set the order of operations is strictly
47
+ # defined. The order of deref operations is:
48
+ # * `:copy_on_deref`
49
+ # * `:dup_on_deref`
50
+ # * `:freeze_on_deref`
51
+ #
52
+ # Because of this ordering there is no need to `#freeze` an object created
53
+ # by a provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to
54
+ # `true`. Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to
55
+ # `true` is as close to the behavior of a "pure" functional language as we
56
+ # are likely to get in Ruby.
57
+ #
58
+ # This method is thread-safe and synchronized with the internal `#mutex`.
59
+ #
60
+ # @return [Object]
61
+ # the current value of the object
62
+ def value
63
+ mutex.lock
64
+ apply_deref_options(@value)
65
+ ensure
66
+ mutex.unlock
67
+ end
68
+
69
+ alias_method :deref, :value
70
+
71
+ protected # A T T E N Z I O N E A R E A P R O T E T T A
72
+
73
+ # Set the internal value of this object
74
+ #
75
+ # @param [Object] val
76
+ # the new value
77
+ def value=(val)
78
+ mutex.lock
79
+ @value = val
80
+ ensure
81
+ mutex.unlock
82
+ end
83
+
84
+ # A mutex lock used for synchronizing thread-safe operations. Methods
85
+ # defined by `Dereferenceable` are synchronized using the `Mutex` returned
86
+ # from this method. Operations performed by the including class that operate
87
+ # on the `@value` instance variable should be locked with this `Mutex`.
88
+ #
89
+ # @return [Mutex]
90
+ # the synchronization object
91
+ def mutex
92
+ @mutex
93
+ end
94
+
95
+ # Initializes the internal `Mutex`.
96
+ #
97
+ # @note
98
+ # This method *must* be called from within the constructor of the
99
+ # including class.
100
+ #
101
+ # @see #mutex
102
+ def init_mutex
103
+ @mutex = Mutex.new
104
+ end
105
+
106
+ # Set the options which define the operations #value performs before
107
+ # returning data to the caller (dereferencing).
108
+ #
109
+ # @note
110
+ # Most classes that include this module will call `#set_deref_options`
111
+ # from within the constructor, thus allowing these options to be set at
112
+ # object creation.
113
+ #
114
+ # @param [Hash] opts
115
+ # the options defining dereference behavior.
116
+ # @option opts [String] :dup_on_deref (false)
117
+ # call `#dup` before returning the data
118
+ # @option opts [String] :freeze_on_deref (false)
119
+ # call `#freeze` before returning the data
120
+ # @option opts [String] :copy_on_deref (nil)
121
+ # call the given `Proc` passing the internal value and returning the value
122
+ # returned from the proc
123
+ def set_deref_options(opts = {})
124
+ mutex.lock
125
+ @dup_on_deref = opts[:dup_on_deref] || opts[:dup]
126
+ @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
127
+ @copy_on_deref = opts[:copy_on_deref] || opts[:copy]
128
+ @nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
129
+ nil
130
+ ensure
131
+ mutex.unlock
132
+ end
133
+
134
+ # @!visibility private
135
+ def apply_deref_options(value)
136
+ return nil if value.nil?
137
+ return value if @nothing_on_deref
138
+ value = @copy_on_deref.call(value) if @copy_on_deref
139
+ value = value.dup if @dup_on_deref
140
+ value = value.freeze if @freeze_on_deref
141
+ value
142
+ end
143
+ end
144
+ end