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,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)