garcun 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,152 @@
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
+ # Expand a path with late-evaluated segments.
22
+ # Calls expand_path -- '~' becomes $HOME, '..' is expanded, etc.
23
+ #
24
+ # @example
25
+ # A symbol represents a segment to expand
26
+ # Pathname.register_path(:conf_dir, '/etc/openldap')
27
+ # Pathname.path_to(:conf_dir) # => '/etc/openldap'
28
+ # Pathname.path_to(:conf_dir, ldap.conf) # => '/etc/openldap/ldap.conf'
29
+ #
30
+ # @example
31
+ # References aren't expanded until they're read
32
+ # Pathname.register_path(:conf_dir, '/etc/openldap')
33
+ # Pathname.register_path(:ldap, :conf_dir, 'ldap.conf')
34
+ # Pathname.path_to(:ldap) # => '/etc/openldap/ldap.conf'
35
+ #
36
+ # @example
37
+ # If we change the conf_dir, everything under it changes as well
38
+ # Pathname.register_path(:conf_dir, '~/.openldap.d')
39
+ # Pathname.path_to(:ldap) # => '/root/openldap.d/ldap.conf'
40
+ #
41
+ # @exampl
42
+ # References can be relative, and can hold symbols themselves
43
+ # Pathname.register_path(:conf_dir, '/etc', :appname, :environment)
44
+ # Pathname.register_path(:appname, 'app_awesome')
45
+ # Pathname.register_path(:environment, 'dev')
46
+ # Pathname.path_to(:conf_dir) # => '/etc/app_awesome/dev'
47
+ #
48
+ module Pathref
49
+ ROOT_PATHS = Hash.new unless defined?(ROOT_PATHS)
50
+ extend self
51
+
52
+ # @param [Array<[String,Symbol]>] pathsegs
53
+ # Any mixture of strings (literal sub-paths) and symbols (interpreted
54
+ # as references).
55
+ #
56
+ # @return [Pathname]
57
+ # A single expanded Pathname
58
+ #
59
+ # @api public
60
+ def of(*pathsegs)
61
+ relpath_to(*pathsegs).expand_path
62
+ end
63
+ alias_method :path_to, :of
64
+
65
+ # @api public
66
+ def register_path(handle, *pathsegs)
67
+ ArgumentError.arity_at_least!(pathsegs, 1)
68
+ ROOT_PATHS[handle.to_sym] = pathsegs
69
+ end
70
+
71
+ # @api public
72
+ def register_paths(handle_paths = {})
73
+ handle_paths.each_pair do |handle, pathsegs|
74
+ register_path(handle, *pathsegs)
75
+ end
76
+ end
77
+
78
+ # @api public
79
+ def register_default_paths(handle_paths = {})
80
+ handle_paths.each_pair do |handle, pathsegs|
81
+ unless ROOT_PATHS.has_key?(handle.to_sym)
82
+ register_path(handle, *pathsegs)
83
+ end
84
+ end
85
+ end
86
+
87
+ # @api public
88
+ def unregister_path(handle)
89
+ ROOT_PATHS.delete handle.to_sym
90
+ end
91
+
92
+ # Expand a path with late-evaluated segments @see `.path_to`. Calls
93
+ # cleanpath (removing `//` double slashes and useless `..`s), but does not
94
+ # reference the filesystem or make paths absolute.
95
+ #
96
+ # @api public
97
+ def relpath_to(*pathsegs)
98
+ ArgumentError.arity_at_least!(pathsegs, 1)
99
+ pathsegs = pathsegs.flatten.map { |ps| expand_pathseg(ps) }.flatten
100
+ self.new(File.join(*pathsegs)).cleanpath(true)
101
+ end
102
+ alias_method :relative_path_to, :relpath_to
103
+
104
+ protected # A T T E N Z I O N E A R E A P R O T E T T A
105
+
106
+ # Recursively expand a path handle.
107
+ #
108
+ # @return [Array<String>]
109
+ # An array of path segments, suitable for .join
110
+ #
111
+ # @api public
112
+ def expand_pathseg(handle)
113
+ return handle unless handle.is_a?(Symbol)
114
+ pathsegs = ROOT_PATHS[handle] or raise ArgumentError,
115
+ "Don't know how to expand path reference '#{handle.inspect}'."
116
+ pathsegs.map { |ps| expand_pathseg(ps) }.flatten
117
+ end
118
+ end
119
+ end
120
+
121
+ class Pathname
122
+ extend Garcon::Pathref
123
+
124
+ class << self; alias_method :new_pathname, :new; end
125
+
126
+ # Like find, but returns an enumerable
127
+ #
128
+ def find_all
129
+ Enumerator.new{|yielder| find{|path| yielder << path } }
130
+ end
131
+
132
+ def self.receive(obj)
133
+ return obj if obj.nil?
134
+ obj.is_a?(self) ? obj : new(obj)
135
+ end
136
+
137
+ # @return [Pathname]
138
+ # The basename without extension (using self.extname as the extension).
139
+ #
140
+ # @api public
141
+ def corename
142
+ basename(self.extname)
143
+ end
144
+
145
+ # @return [String]
146
+ # Compact string rendering
147
+ #
148
+ # @api public
149
+ def inspect_compact() to_path.dump ; end
150
+
151
+ alias_method :to_str, :to_path
152
+ end
@@ -0,0 +1,41 @@
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 Process
21
+
22
+ # Turns the current script into a daemon process
23
+ # that detaches from the console. It can be shut
24
+ # down with a TERM signal.
25
+ #
26
+ def self.daemon(nochdir = nil, noclose = nil)
27
+ exit if fork
28
+ Process.setsid
29
+ exit if fork
30
+ Dir.chdir '/' unless nochdir
31
+ File.umask 0000
32
+ unless noclose
33
+ STDIN.reopen '/dev/null'
34
+ STDOUT.reopen '/dev/null', 'a'
35
+ STDERR.reopen '/dev/null', 'a'
36
+ end
37
+ trap('TERM') { exit }
38
+ return 0
39
+ end unless respond_to?(:daemon)
40
+ end
41
+
@@ -0,0 +1,497 @@
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 'securerandom'
21
+ require_relative 'hash'
22
+ require_relative 'string'
23
+ require_relative 'kernel'
24
+
25
+ # This library extends Array, String, Hash and other classes with randomization
26
+ # methods. Most of the methods are of one of two kinds. Either they "pick" a
27
+ # random element from the reciever or they randomly "shuffle" the reciever.
28
+ #
29
+ # The most common example is Array#shuffle, which simply randmomizes the
30
+ # order of an array's elements.
31
+ #
32
+ # [1,2,3].shuffle # => [2,3,1]
33
+ #
34
+ # The other methods do similar things for their respective classes.
35
+ #
36
+ # The classes are all extended via mixins which have been created within
37
+ # Ruby's Random class.
38
+ #
39
+ class Random
40
+ class << self
41
+ # Alias for Kernel#rand.
42
+ alias_method :number, :rand
43
+
44
+ public :number
45
+ end
46
+
47
+ # Module method to generate a random letter.
48
+ #
49
+ # @example
50
+ # Random.letter # => "q"
51
+ # Random.letter # => "r"
52
+ # Random.letter # => "a"
53
+ #
54
+ # @return [String]
55
+ # A random letter
56
+ #
57
+ # @api public
58
+ def self.letter
59
+ (SecureRandom.random_number(26) +
60
+ (SecureRandom.random_number(2) == 0 ? 65 : 97)).chr
61
+ end
62
+
63
+ # Random extensions for Range class.
64
+ #
65
+ module RangeExtensions
66
+ # Return a random element from the range.
67
+ #
68
+ # @example
69
+ # (1..4).at_rand # => 2
70
+ # (1..4).at_rand # => 4
71
+ #
72
+ # (1.5..2.5).at_rand # => 2.06309842754533
73
+ # (1.5..2.5).at_rand # => 1.74976944931541
74
+ #
75
+ # ('a'..'z').at_rand # => 'q'
76
+ # ('a'..'z').at_rand # => 'f'
77
+ #
78
+ # @return [String]
79
+ # A random element from range
80
+ #
81
+ # @api public
82
+ def at_rand
83
+ first, last = first(), last()
84
+ if first.respond_to?(:random_delta)
85
+ begin
86
+ first.random_delta(last, exclude_end?)
87
+ rescue
88
+ to_a.at_rand
89
+ end
90
+ else
91
+ to_a.at_rand
92
+ end
93
+ end
94
+ end
95
+
96
+ # Random extensions fo Integer class.
97
+ #
98
+ module IntegerExtensions
99
+ #
100
+ def random_delta(last, exclude_end)
101
+ first = self
102
+ last -= 1 if exclude_end
103
+ return nil if last < first
104
+ SecureRandom.random_number(last - first + 1) + first
105
+ end
106
+ end
107
+
108
+ # Random extensions for Numeric class.
109
+ #
110
+ module NumericExtensions
111
+ #
112
+ def random_delta(last, exclude_end)
113
+ first = self
114
+ return nil if last < first
115
+ return nil if exclude_end && last == first
116
+ (last - first) * SecureRandom.random_number + first
117
+ end
118
+ end
119
+
120
+ # Random extensions for Array class.
121
+ #
122
+ module ArrayExtensions
123
+ # Return a random element from the array.
124
+ #
125
+ # @example
126
+ # [1, 2, 3, 4].at_rand # => 2
127
+ # [1, 2, 3, 4].at_rand # => 4
128
+ #
129
+ def at_rand
130
+ at(SecureRandom.random_number(size))
131
+ end
132
+
133
+ # Same as #at_rand, but acts in place removing a random element from the
134
+ # array.
135
+ #
136
+ # @example
137
+ # a = [1,2,3,4]
138
+ # a.at_rand! # => 2
139
+ # a # => [1,3,4]
140
+ #
141
+ def at_rand!
142
+ delete_at(SecureRandom.random_number(size))
143
+ end
144
+
145
+ # Similar to #at_rand, but will return an array of randomly picked exclusive
146
+ # elements if given a number.
147
+ def pick(n = nil)
148
+ if n
149
+ a = self.dup
150
+ a.pick!(n)
151
+ else
152
+ at(SecureRandom.random_number(size))
153
+ end
154
+ end
155
+
156
+ # Similar to #at_rand!, but given a number will return an array of exclusive
157
+ # elements.
158
+ def pick!(n = nil)
159
+ if n
160
+ if n > self.size
161
+ r = self.dup
162
+ self.replace([])
163
+ r
164
+ else
165
+ r = []
166
+ n.times { r << delete_at(SecureRandom.random_number(size)) }
167
+ r
168
+ end
169
+ else
170
+ delete_at(SecureRandom.random_number(size))
171
+ end
172
+ end
173
+
174
+ # Random index.
175
+ #
176
+ def rand_index
177
+ SecureRandom.random_number(size)
178
+ end
179
+
180
+ # Returns a random subset of an Array. If a _number_ of elements is
181
+ # specified then returns that number of elements, otherwise returns a random
182
+ # number of elements upto the size of the Array.
183
+ #
184
+ # By defualt the returned values are exclusive of each other, but if
185
+ # _exclusive_ is set to `false`, the same values can be choosen more than
186
+ # once.
187
+ #
188
+ # When _exclusive_ is <tt>true</tt> (the default) and the _number_ given is
189
+ # greater than the size of the array, then all values are returned.
190
+ #
191
+ # @example
192
+ # [1, 2, 3, 4].rand_subset(1) # => [2]
193
+ # [1, 2, 3, 4].rand_subset(4) # => [2, 1, 3, 4]
194
+ # [1, 2, 3, 4].rand_subset # => [1, 3, 4]
195
+ # [1, 2, 3, 4].rand_subset # => [2, 3]
196
+ #
197
+ def rand_subset(number = nil, exclusive = true)
198
+ number = SecureRandom.random_number(size) unless number
199
+ number = number.to_int
200
+ return sort_by{rand}.slice(0,number) if exclusive
201
+ ri =[]; number.times { |n| ri << SecureRandom.random_number(size) }
202
+ return values_at(*ri)
203
+ end
204
+
205
+ # Generates random subarrays. Uses random numbers and bit-fiddling to assure
206
+ # performant uniform distributions even for large arrays.
207
+ #
208
+ # @example
209
+ # a = *1..5
210
+ # a.rand_subarrays(2) # => [[3, 4, 5], []]
211
+ # a.rand_subarrays(3) # => [[1], [1, 4, 5], [2, 3]]
212
+ #
213
+ def rand_subarrays(n = 1)
214
+ raise ArgumentError, 'negative argument' if n < 0
215
+ (1..n).map do
216
+ r = rand(2**self.size)
217
+ self.select.with_index { |_, i| r[i] == 1 }
218
+ end
219
+ end
220
+
221
+ # Randomize the order of an array.
222
+ #
223
+ # @example
224
+ # [1,2,3,4].shuffle # => [2,4,1,3]
225
+ #
226
+ def shuffle
227
+ dup.shuffle!
228
+ end
229
+
230
+ # As with #shuffle but modifies the array in place.
231
+ # The algorithm used here is known as a Fisher-Yates shuffle.
232
+ #
233
+ # @example
234
+ # a = [1,2,3,4]
235
+ # a.shuffle!
236
+ # a # => [2,4,1,3]
237
+ #
238
+ def shuffle!
239
+ s = size
240
+ each_index do |j|
241
+ i = SecureRandom.random_number(s-j)
242
+ tmp = self[j]
243
+ self[j] = self[j+i]
244
+ self[j+i] = tmp
245
+ end
246
+ self
247
+ end
248
+ end
249
+
250
+ # Random extensions for Hash class.
251
+ #
252
+ module HashExtensions
253
+ # Returns a random key.
254
+ #
255
+ # @example
256
+ # {:one => 1, :two => 2, :three => 3}.pick_key # => :three
257
+ #
258
+ def rand_key
259
+ keys.at(SecureRandom.random_number(keys.size))
260
+ end
261
+
262
+ # Delete a random key-value pair, returning the key.
263
+ #
264
+ # @example
265
+ # a = {:one => 1, :two => 2, :three => 3}
266
+ # a.rand_key! # => :two
267
+ # a # => {:one => 1, :three => 3}
268
+ #
269
+ def rand_key!
270
+ k,v = rand_pair
271
+ delete(k)
272
+ return k
273
+ end
274
+
275
+ alias_method :pick_key, :rand_key!
276
+
277
+ # Returns a random key-value pair.
278
+ #
279
+ # @example
280
+ # {:one => 1, :two => 2, :three => 3}.pick # => [:one, 1]
281
+ #
282
+ def rand_pair
283
+ k = rand_key
284
+ return k, fetch(k)
285
+ end
286
+
287
+ # Deletes a random key-value pair and returns that pair.
288
+ #
289
+ # @example
290
+ # a = {:one => 1, :two => 2, :three => 3}
291
+ # a.rand_pair! # => [:two, 2]
292
+ # a # => {:one => 1, :three => 3}
293
+ #
294
+ def rand_pair!
295
+ k,v = rand_pair
296
+ delete(k)
297
+ return k,v
298
+ end
299
+
300
+ alias_method :pick_pair, :rand_pair!
301
+
302
+ # Returns a random hash value.
303
+ #
304
+ # @example
305
+ # {:one => 1, :two => 2, :three => 3}.rand_value # => 2
306
+ # {:one => 1, :two => 2, :three => 3}.rand_value # => 1
307
+ #
308
+ def rand_value
309
+ fetch(rand_key)
310
+ end
311
+
312
+ # Deletes a random key-value pair and returns the value.
313
+ #
314
+ # @example
315
+ # a = {:one => 1, :two => 2, :three => 3}
316
+ # a.at_rand! # => 2
317
+ # a # => {:one => 1, :three => 3}
318
+ #
319
+ def rand_value!
320
+ k,v = rand_pair
321
+ delete(k)
322
+ return v
323
+ end
324
+
325
+ alias_method :pick, :rand_value!
326
+ alias_method :at_rand, :rand_value
327
+ alias_method :at_rand!, :rand_value!
328
+
329
+ # Returns a copy of the hash with _values_ arranged in new random order.
330
+ #
331
+ # @example
332
+ # h = {:a=>1, :b=>2, :c=>3}
333
+ # h.shuffle # => {:b=>2, :c=>1, :a>3}
334
+ #
335
+ def shuffle
336
+ ::Hash.zip(
337
+ keys.sort_by { SecureRandom.random_number },
338
+ values.sort_by { SecureRandom.random_number })
339
+ end
340
+
341
+ # Destructive shuffle_hash. Arrange the values in a new random order.
342
+ #
343
+ # @example
344
+ # h = {:a => 1, :b => 2, :c => 3}
345
+ # h.shuffle!
346
+ # h # => {:b=>2, :c=>1, :a=>3}
347
+ #
348
+ def shuffle!
349
+ self.replace(shuffle)
350
+ end
351
+
352
+ end
353
+
354
+ # Random extensions for String class.
355
+ #
356
+ module StringExtensions
357
+
358
+ def self.included(base)
359
+ base.extend(Self)
360
+ end
361
+
362
+ # Class-level methods.
363
+ module Self
364
+ # Returns a randomly generated string. One possible use is
365
+ # password initialization. Takes a max legnth of characters
366
+ # (default 8) and an optional valid char Regexp (default /\w\d/).
367
+ #
368
+ # @example
369
+ # String.random # => 'dd4qed4r'
370
+ #
371
+ def random(max_length = 8, char_re = /[\w\d]/)
372
+ unless char_re.is_a?(Regexp)
373
+ raise ArgumentError, 'second argument must be a regular expression'
374
+ end
375
+ string = ''
376
+ while string.length < max_length
377
+ ch = SecureRandom.random_number(255).chr
378
+ string << ch if ch =~ char_re
379
+ end
380
+ return string
381
+ end
382
+
383
+ # Generate a random binary string of +n_bytes+ size.
384
+ #
385
+ def random_binary(n_bytes)
386
+ #(Array.new(n_bytes) { rand(0x100) }).pack('c*')
387
+ SecureRandom.random_bytes(64)
388
+ end
389
+
390
+ # Create a random String of given length, using given character set
391
+ #
392
+ # Examples
393
+ #
394
+ # String.random
395
+ # => "D9DxFIaqR3dr8Ct1AfmFxHxqGsmA4Oz3"
396
+ #
397
+ # String.ran(10)
398
+ # => "t8BIna341S"
399
+ #
400
+ # String.ran(10, ['a'..'z'])
401
+ # => "nstpvixfri"
402
+ #
403
+ # String.ran(10, ['0'..'9'] )
404
+ # => "0982541042"
405
+ #
406
+ # String.ran(10, ['0'..'9','A'..'F'] )
407
+ # => "3EBF48AD3D"
408
+ #
409
+ def ran(len = 32, character_set = ["A".."Z", "a".."z", "0".."9"])
410
+ chars = character_set.map(&:to_a).flatten
411
+ Array.new(len){ chars.sample }.join
412
+ end
413
+ end
414
+
415
+ # Return a random separation of the string. Default separation is by
416
+ # charaacter.
417
+ #
418
+ # @example
419
+ # "Ruby rules".at_rand(' ') # => ["Ruby"]
420
+ #
421
+ def at_rand(separator = //)
422
+ self.split(separator, -1).at_rand
423
+ end
424
+
425
+ # Return a random separation while removing it from the string. Default
426
+ # separation is by character.
427
+ #
428
+ # @example
429
+ # s = "Ruby rules"
430
+ # s.at_rand!(' ') # => "Ruby"
431
+ # s # => "rules"
432
+ #
433
+ def at_rand!(separator = //)
434
+ a = self.shatter(separator)
435
+ w = []; a.each_with_index { |s, i| i % 2 == 0 ? w << s : w.last << s }
436
+ i = SecureRandom.random_number(w.size)
437
+ r = w.delete_at(i)
438
+ self.replace(w.join(''))
439
+ return r
440
+ end
441
+
442
+ # Return a random byte of _self_.
443
+ #
444
+ # @example
445
+ # "Ruby rules".rand_byte # => 121
446
+ #
447
+ def rand_byte
448
+ self[SecureRandom.random_number(size)]
449
+ end
450
+
451
+ # Destructive rand_byte. Delete a random byte of _self_ and return it.
452
+ #
453
+ # @example
454
+ # s = "Ruby rules"
455
+ # s.rand_byte! # => 121
456
+ # s # => "Rub rules"
457
+ #
458
+ def rand_byte!
459
+ i = SecureRandom.random_number(size)
460
+ rv = self[i,1]
461
+ self[i,1] = ''
462
+ rv
463
+ end
464
+
465
+ # Return a random string index.
466
+ #
467
+ # @example
468
+ # "Ruby rules".rand_index # => 3
469
+ #
470
+ def rand_index
471
+ SecureRandom.random_number(size)
472
+ end
473
+
474
+ # Return the string with seperated sections arranged in a random order. The
475
+ # default seperation is by character.
476
+ #
477
+ # @example
478
+ # "Ruby rules".shuffle # => "e lybRsuur"
479
+ #
480
+ def shuffle(separator = //)
481
+ split(separator).shuffle.join('')
482
+ end
483
+
484
+ # In place version of shuffle.
485
+ #
486
+ def shuffle!(separator = //)
487
+ self.replace(shuffle(separator))
488
+ end
489
+ end
490
+ end
491
+
492
+ Hash.send(:include, Random::HashExtensions)
493
+ Array.send(:include, Random::ArrayExtensions)
494
+ Range.send(:include, Random::RangeExtensions)
495
+ String.send(:include, Random::StringExtensions)
496
+ Integer.send(:include, Random::IntegerExtensions)
497
+ Numeric.send(:include, Random::NumericExtensions)