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,79 @@
|
|
|
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
|
+
Garcon::Inflections.inflections do |inflect|
|
|
21
|
+
inflect.plural(/$/, 's')
|
|
22
|
+
inflect.plural(/s$/i, 's')
|
|
23
|
+
inflect.plural(/^(ax|test)is$/i, '\1es')
|
|
24
|
+
inflect.plural(/(octop|vir)us$/i, '\1i')
|
|
25
|
+
inflect.plural(/(octop|vir)i$/i, '\1i')
|
|
26
|
+
inflect.plural(/(alias|status)$/i, '\1es')
|
|
27
|
+
inflect.plural(/(bu)s$/i, '\1ses')
|
|
28
|
+
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
|
|
29
|
+
inflect.plural(/([ti])um$/i, '\1a')
|
|
30
|
+
inflect.plural(/([ti])a$/i, '\1a')
|
|
31
|
+
inflect.plural(/sis$/i, 'ses')
|
|
32
|
+
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
|
33
|
+
inflect.plural(/(hive)$/i, '\1s')
|
|
34
|
+
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
|
35
|
+
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
|
|
36
|
+
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
|
37
|
+
inflect.plural(/^(m|l)ouse$/i, '\1ice')
|
|
38
|
+
inflect.plural(/^(m|l)ice$/i, '\1ice')
|
|
39
|
+
inflect.plural(/^(ox)$/i, '\1en')
|
|
40
|
+
inflect.plural(/^(oxen)$/i, '\1')
|
|
41
|
+
inflect.plural(/(quiz)$/i, '\1zes')
|
|
42
|
+
|
|
43
|
+
inflect.singular(/s$/i, '')
|
|
44
|
+
inflect.singular(/(ss)$/i, '\1')
|
|
45
|
+
inflect.singular(/(n)ews$/i, '\1ews')
|
|
46
|
+
inflect.singular(/([ti])a$/i, '\1um')
|
|
47
|
+
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
|
|
48
|
+
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
|
|
49
|
+
inflect.singular(/([^f])ves$/i, '\1fe')
|
|
50
|
+
inflect.singular(/(hive)s$/i, '\1')
|
|
51
|
+
inflect.singular(/(tive)s$/i, '\1')
|
|
52
|
+
inflect.singular(/([lr])ves$/i, '\1f')
|
|
53
|
+
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
|
54
|
+
inflect.singular(/(s)eries$/i, '\1eries')
|
|
55
|
+
inflect.singular(/(m)ovies$/i, '\1ovie')
|
|
56
|
+
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
|
|
57
|
+
inflect.singular(/^(m|l)ice$/i, '\1ouse')
|
|
58
|
+
inflect.singular(/(bus)(es)?$/i, '\1')
|
|
59
|
+
inflect.singular(/(o)es$/i, '\1')
|
|
60
|
+
inflect.singular(/(shoe)s$/i, '\1')
|
|
61
|
+
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
|
|
62
|
+
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
|
|
63
|
+
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
|
|
64
|
+
inflect.singular(/(alias|status)(es)?$/i, '\1')
|
|
65
|
+
inflect.singular(/^(ox)en/i, '\1')
|
|
66
|
+
inflect.singular(/(vert|ind)ices$/i, '\1ex')
|
|
67
|
+
inflect.singular(/(matr)ices$/i, '\1ix')
|
|
68
|
+
inflect.singular(/(quiz)zes$/i, '\1')
|
|
69
|
+
inflect.singular(/(database)s$/i, '\1')
|
|
70
|
+
|
|
71
|
+
inflect.irregular('person', 'people')
|
|
72
|
+
inflect.irregular('man', 'men')
|
|
73
|
+
inflect.irregular('child', 'children')
|
|
74
|
+
inflect.irregular('sex', 'sexes')
|
|
75
|
+
inflect.irregular('move', 'moves')
|
|
76
|
+
inflect.irregular('zombie', 'zombies')
|
|
77
|
+
|
|
78
|
+
inflect.uncountable(%w(hovercraft moose milk rain Swiss grass equipment information rice money species series fish sheep jeans))
|
|
79
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
module Inflections
|
|
22
|
+
# A singleton instance of this class is yielded by Garcon.inflections,
|
|
23
|
+
# which can then be used to specify additional inflection rules. Examples:
|
|
24
|
+
#
|
|
25
|
+
# Inflections.inflections do |inflect|
|
|
26
|
+
# inflect.plural /^(ox)$/i, '\1\2en'
|
|
27
|
+
# inflect.singular /^(ox)en/i, '\1'
|
|
28
|
+
#
|
|
29
|
+
# inflect.irregular 'octopus', 'octopi'
|
|
30
|
+
#
|
|
31
|
+
# inflect.uncountable "equipment"
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# New rules are added at the top. So in the example above, the irregular
|
|
35
|
+
# rule for octopus will now be the first of the pluralization and
|
|
36
|
+
# singularization rules that is runs. This guarantees that your rules run
|
|
37
|
+
# before any of the rules that may already have been loaded.
|
|
38
|
+
#
|
|
39
|
+
class Inflections
|
|
40
|
+
|
|
41
|
+
# Return instance
|
|
42
|
+
#
|
|
43
|
+
# @return [Inflections]
|
|
44
|
+
# @api private
|
|
45
|
+
def self.instance
|
|
46
|
+
@__instance__ ||= new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Array] plurals
|
|
50
|
+
# @api private
|
|
51
|
+
attr_reader :plurals
|
|
52
|
+
|
|
53
|
+
# @return [Array] singulars
|
|
54
|
+
# @api private
|
|
55
|
+
attr_reader :singulars
|
|
56
|
+
|
|
57
|
+
# @return [Array] uncountables
|
|
58
|
+
# @api private
|
|
59
|
+
attr_reader :uncountables
|
|
60
|
+
|
|
61
|
+
# @return [Array] humans
|
|
62
|
+
# @api private
|
|
63
|
+
attr_reader :humans
|
|
64
|
+
|
|
65
|
+
# Initialize object
|
|
66
|
+
#
|
|
67
|
+
# @return [undefined]
|
|
68
|
+
# @api private
|
|
69
|
+
def initialize
|
|
70
|
+
@plurals = RulesCollection.new
|
|
71
|
+
@singulars = RulesCollection.new
|
|
72
|
+
@humans = RulesCollection.new
|
|
73
|
+
@uncountables = Set[]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Specifies a new pluralization rule and its replacement. The rule can
|
|
77
|
+
# either be a string or a regular expression. The replacement should
|
|
78
|
+
# always be a string that may include references to the matched data from
|
|
79
|
+
# the rule.
|
|
80
|
+
#
|
|
81
|
+
# @param [String, Regexp] rule
|
|
82
|
+
# @param [String, Regexp] replacement
|
|
83
|
+
# @return [self]
|
|
84
|
+
# @api private
|
|
85
|
+
def plural(rule, replacement)
|
|
86
|
+
rule(rule, replacement, @plurals)
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Specifies a new singularization rule and its replacement. The rule can
|
|
91
|
+
# either be a string or a regular expression. The replacement should
|
|
92
|
+
# always be a string that may include references to the matched data from
|
|
93
|
+
# the rule.
|
|
94
|
+
#
|
|
95
|
+
# @param [String, Regexp] rule
|
|
96
|
+
# @param [String, Regexp] replacement
|
|
97
|
+
# @return [self]
|
|
98
|
+
# @api private
|
|
99
|
+
def singular(rule, replacement)
|
|
100
|
+
rule(rule, replacement, @singulars)
|
|
101
|
+
self
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Specifies a new irregular that applies to both pluralization and
|
|
105
|
+
# singularization at the same time. This can only be used for strings, not
|
|
106
|
+
# regular expressions. You simply pass the irregular in singular and
|
|
107
|
+
# plural form.
|
|
108
|
+
#
|
|
109
|
+
# @param [String] singular
|
|
110
|
+
# @param [String] plural
|
|
111
|
+
# @return [self]
|
|
112
|
+
# @api private
|
|
113
|
+
def irregular(singular, plural)
|
|
114
|
+
@uncountables.delete(singular)
|
|
115
|
+
@uncountables.delete(plural)
|
|
116
|
+
add_irregular(singular, plural, @plurals)
|
|
117
|
+
add_irregular(plural, singular, @singulars)
|
|
118
|
+
self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Uncountable will not be inflected
|
|
122
|
+
#
|
|
123
|
+
# @param [Enumerable<String>] words
|
|
124
|
+
# @return [self]
|
|
125
|
+
# @api private
|
|
126
|
+
def uncountable(*words)
|
|
127
|
+
@uncountables.merge(words.flatten)
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Specifies a humanized form of a string by a regular expression rule or
|
|
132
|
+
# by a string mapping. When using a regular expression based replacement,
|
|
133
|
+
# the normal humanize formatting is called after the replacement. When a
|
|
134
|
+
# string is used, the human form should be specified as desired (example:
|
|
135
|
+
# 'The name', not 'the_name')
|
|
136
|
+
#
|
|
137
|
+
# @param [String, Regexp] rule
|
|
138
|
+
# @param [String, Regexp] replacement
|
|
139
|
+
# @return [self]
|
|
140
|
+
# @api private
|
|
141
|
+
def human(rule, replacement)
|
|
142
|
+
@humans.insert(0, [rule, replacement])
|
|
143
|
+
self
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Clear all inflection rules
|
|
147
|
+
#
|
|
148
|
+
# @return [self]
|
|
149
|
+
# @api private
|
|
150
|
+
def clear
|
|
151
|
+
initialize
|
|
152
|
+
self
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
|
156
|
+
|
|
157
|
+
# Add irregular inflection
|
|
158
|
+
#
|
|
159
|
+
# @param [String] rule
|
|
160
|
+
# @param [String] replacement
|
|
161
|
+
# @return [undefined]
|
|
162
|
+
# @api private
|
|
163
|
+
def add_irregular(rule, replacement, target)
|
|
164
|
+
head, *tail = rule.chars.to_a
|
|
165
|
+
rule(/(#{head})#{tail.join}\z/i, '\1' + replacement[1..-1], target)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Add a new rule
|
|
169
|
+
#
|
|
170
|
+
# @param [String, Regexp] rule
|
|
171
|
+
# @param [String, Regexp] replacement
|
|
172
|
+
# @param [Array] target
|
|
173
|
+
# @return [undefined]
|
|
174
|
+
# @api private
|
|
175
|
+
def rule(rule, replacement, target)
|
|
176
|
+
@uncountables.delete(rule)
|
|
177
|
+
@uncountables.delete(replacement)
|
|
178
|
+
target.insert(0, [rule, replacement])
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
module Inflections
|
|
22
|
+
# Wraps inflections array
|
|
23
|
+
#
|
|
24
|
+
class RulesCollection < Array
|
|
25
|
+
# Applies first found rule to given word
|
|
26
|
+
#
|
|
27
|
+
# @param [String] word
|
|
28
|
+
# @return [String] modified word
|
|
29
|
+
# @api private
|
|
30
|
+
def apply_to(word)
|
|
31
|
+
result = word.dup
|
|
32
|
+
each { |rule, replacement| break if result.gsub!(rule, replacement) }
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,271 @@
|
|
|
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
|
+
# ______ __ __ ___ _____ ___ __ ____ ___ ______
|
|
21
|
+
# | T| T T / _] / ___/ / _] / ]| \ / _]| T
|
|
22
|
+
# | || l | / [_ ( \_ / [_ / / | D ) / [_ | |
|
|
23
|
+
# l_j l_j| _ |Y _] \__ TY _] / / | / Y _]l_j l_j
|
|
24
|
+
# | | | | || [_ / \ || [_ / \_ | \ | [_ | |
|
|
25
|
+
# | | | | || T \ || T\ || . Y| T | |
|
|
26
|
+
# l__j l__j__jl_____j \___jl_____j \____jl__j\_jl_____j l__j
|
|
27
|
+
#
|
|
28
|
+
# __ _ ___ ___ ____ ___ ____ __
|
|
29
|
+
# | l/ ] / _] / _]| \ / _]| \ | T
|
|
30
|
+
# | ' / / [_ / [_ | o )/ [_ | D )| |
|
|
31
|
+
# | \ Y _]Y _]| _/Y _]| / |__j
|
|
32
|
+
# | Y| [_ | [_ | | | [_ | \ __
|
|
33
|
+
# | . || T| T| | | T| . Y| T
|
|
34
|
+
# l__j\_jl_____jl_____jl__j l_____jl__j\_jl__j
|
|
35
|
+
|
|
36
|
+
require 'tempfile'
|
|
37
|
+
require 'fileutils'
|
|
38
|
+
require_relative 'utility/crypto'
|
|
39
|
+
require_relative 'utility/memstash'
|
|
40
|
+
|
|
41
|
+
module Garcon
|
|
42
|
+
# Creates a transient file with sensitive content, usefule when you have an
|
|
43
|
+
# excecutable that reads a password from a file but you do not wish to leave
|
|
44
|
+
# the password on the filesystem. When used in a block parameter the file is
|
|
45
|
+
# written and deleted when the block returns, optionally you can encrypt and
|
|
46
|
+
# decrypt your secret strings with salt, cipher and a splash of obfuscation.
|
|
47
|
+
#
|
|
48
|
+
module Secret
|
|
49
|
+
# A Configuration instance
|
|
50
|
+
class Configuration
|
|
51
|
+
|
|
52
|
+
# @!attribute [r] :lock
|
|
53
|
+
# @return [String] Access the shared Monitor for this instance.
|
|
54
|
+
attr_reader :lock
|
|
55
|
+
|
|
56
|
+
# @!attribute [rw] :stash
|
|
57
|
+
# @return [String] The shared Stash (in-memory cache) for this instance.
|
|
58
|
+
attr_accessor :stash
|
|
59
|
+
|
|
60
|
+
# @!attribute [rw] :queue
|
|
61
|
+
# @return [String] The shared queue object for this instance.
|
|
62
|
+
attr_accessor :queue
|
|
63
|
+
|
|
64
|
+
# Initialized a configuration instance
|
|
65
|
+
#
|
|
66
|
+
# @return [undefined]
|
|
67
|
+
#
|
|
68
|
+
# @api private
|
|
69
|
+
def initialize(options = {})
|
|
70
|
+
@lock = Monitor.new
|
|
71
|
+
@stash = MemStash.new
|
|
72
|
+
@queue = MutexPriorityQueue.new
|
|
73
|
+
@queue << Secret.tmpfile until @queue.length >= 4
|
|
74
|
+
|
|
75
|
+
yield self if block_given?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @api private
|
|
79
|
+
def to_h
|
|
80
|
+
{ lock: lock,
|
|
81
|
+
stash: stash,
|
|
82
|
+
queue: queue
|
|
83
|
+
}.freeze
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Encrypt and store the given value with the given key, either with an an
|
|
88
|
+
# argument or block. If a previous value was set it will be overwritten
|
|
89
|
+
# with the new value.
|
|
90
|
+
#
|
|
91
|
+
# @param key [Symbol, String]
|
|
92
|
+
# String or symbol representing the key.
|
|
93
|
+
#
|
|
94
|
+
# @param value [Object]
|
|
95
|
+
# Any object that represents the value.
|
|
96
|
+
#
|
|
97
|
+
# @yield [Block]
|
|
98
|
+
# Optionally specify a block that returns the value to set.
|
|
99
|
+
#
|
|
100
|
+
# @return [String]
|
|
101
|
+
# The encrypted value.
|
|
102
|
+
#
|
|
103
|
+
def self.set(key, value)
|
|
104
|
+
Garcon.secret.stash[key] = value.encrypt
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Retrieve and decrypt a value at key from the stash.
|
|
108
|
+
#
|
|
109
|
+
# @param key [Symbol, String]
|
|
110
|
+
# String or symbol representing the key.
|
|
111
|
+
#
|
|
112
|
+
# @raise [KeyError]
|
|
113
|
+
# If no such key found.
|
|
114
|
+
#
|
|
115
|
+
# @return [String]
|
|
116
|
+
# Unencrypted value.
|
|
117
|
+
#
|
|
118
|
+
def self.get(key)
|
|
119
|
+
(Garcon.secret.stash[key]).decrypt
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Creates the secrets file yields to the block, removes the secrets file
|
|
123
|
+
# when the block returns
|
|
124
|
+
#
|
|
125
|
+
# @example
|
|
126
|
+
# secret.tmp { |file| shell_out!("open_sesame --passwd-file #{file}") }
|
|
127
|
+
#
|
|
128
|
+
# @yield [Block]
|
|
129
|
+
# invokes the block
|
|
130
|
+
#
|
|
131
|
+
# @yieldreturn [Object]
|
|
132
|
+
# the result of evaluating the optional block
|
|
133
|
+
#
|
|
134
|
+
# @api public
|
|
135
|
+
def self.tmp(key, *args, &block)
|
|
136
|
+
Garcon.secret.lock.synchronize do
|
|
137
|
+
begin
|
|
138
|
+
file = queue.pop
|
|
139
|
+
atomic_write(file, get(key)) unless valid?(key, file)
|
|
140
|
+
yield file if block_given?
|
|
141
|
+
ensure
|
|
142
|
+
File.unlink(file) if File.exist?(file)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Search a text file for a matching string
|
|
148
|
+
#
|
|
149
|
+
# @return [Boolean]
|
|
150
|
+
# True if the file is present and a match was found, otherwise returns
|
|
151
|
+
# false if file does not exist and/or does not contain a match
|
|
152
|
+
#
|
|
153
|
+
# @api public
|
|
154
|
+
def self.valid?(key, file)
|
|
155
|
+
Garcon.secret.lock.synchronize do
|
|
156
|
+
return false unless File.exist?(file)
|
|
157
|
+
File.open(file, &:readlines).map! do |line|
|
|
158
|
+
return true if line.match(get(key))
|
|
159
|
+
end
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
|
165
|
+
|
|
166
|
+
def self.queue
|
|
167
|
+
until Garcon.secret.queue.length >= 2
|
|
168
|
+
Garcon.secret.queue << Secret.tmpfile
|
|
169
|
+
end
|
|
170
|
+
Garcon.secret.queue
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Write the secrets file
|
|
174
|
+
#
|
|
175
|
+
# @return [String]
|
|
176
|
+
# the path to the file
|
|
177
|
+
#
|
|
178
|
+
# @api private
|
|
179
|
+
def self.write(key, file)
|
|
180
|
+
Garcon.secret.lock.synchronize do
|
|
181
|
+
begin
|
|
182
|
+
atomic_write(file, get(key)) unless valid?(key, file)
|
|
183
|
+
ensure
|
|
184
|
+
File.chmod(00400, file)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Delete the secrets file
|
|
190
|
+
#
|
|
191
|
+
# @return [undefined]
|
|
192
|
+
#
|
|
193
|
+
# @api private
|
|
194
|
+
def self.delete(file = nil)
|
|
195
|
+
Garcon.secret.lock.synchronize do
|
|
196
|
+
if file.nil?
|
|
197
|
+
until Garcon.secret.queue.length == 0
|
|
198
|
+
tmpfile = Garcon.secret.queue.pop
|
|
199
|
+
File.unlink(tmpfile) if File.exist?(tmpfile)
|
|
200
|
+
end
|
|
201
|
+
else
|
|
202
|
+
File.unlink(file) if File.exist?(file)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Write to a file atomically. Useful for situations where you don't
|
|
208
|
+
# want other processes or threads to see half-written files.
|
|
209
|
+
#
|
|
210
|
+
# @param [String] file
|
|
211
|
+
# fill path of the file to write to
|
|
212
|
+
# @param [String] secret
|
|
213
|
+
# content to write to file
|
|
214
|
+
#
|
|
215
|
+
# @api private
|
|
216
|
+
def self.atomic_write(file, secret, tmp_dir = Dir.tmpdir)
|
|
217
|
+
tmp_file = Tempfile.new(File.basename(file), tmp_dir)
|
|
218
|
+
tmp_file.write(secret)
|
|
219
|
+
tmp_file.close
|
|
220
|
+
|
|
221
|
+
FileUtils.mv(tmp_file.path, file)
|
|
222
|
+
begin
|
|
223
|
+
File.chmod(00400, file)
|
|
224
|
+
rescue Errno::EPERM, Errno::EACCES
|
|
225
|
+
# Changing file ownership/permissions failed
|
|
226
|
+
end
|
|
227
|
+
ensure
|
|
228
|
+
tmp_file.close
|
|
229
|
+
tmp_file.unlink
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Lock a file for a block so only one process can modify it at a time
|
|
233
|
+
#
|
|
234
|
+
# @param [String] file
|
|
235
|
+
# fill path of the file to lock
|
|
236
|
+
#
|
|
237
|
+
# @yield [Block]
|
|
238
|
+
# invokes the block
|
|
239
|
+
#
|
|
240
|
+
# @yieldreturn [Object]
|
|
241
|
+
# the result of evaluating the optional block
|
|
242
|
+
#
|
|
243
|
+
# @api private
|
|
244
|
+
def self.lock_file(file, &block)
|
|
245
|
+
if File.exist?(file)
|
|
246
|
+
File.open(file, 'r+') do |f|
|
|
247
|
+
begin
|
|
248
|
+
f.flock File::LOCK_EX
|
|
249
|
+
yield
|
|
250
|
+
ensure
|
|
251
|
+
f.flock File::LOCK_UN
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
else
|
|
255
|
+
yield
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# @return [String] tmp_file
|
|
260
|
+
# @api private
|
|
261
|
+
def self.tmpfile(tmp_dir = Dir.tmpdir)
|
|
262
|
+
Tempfile.new(random_seed, tmp_dir).path.freeze
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# @return [String] random_seed
|
|
266
|
+
# @api private
|
|
267
|
+
def self.random_seed
|
|
268
|
+
SecureRandom.random_number(0x100000000).to_s(36)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|