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