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,234 @@
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
+ # Add #elapse
21
+ class Time
22
+ # Tracks the elapse time of a code block.
23
+ #
24
+ # @example
25
+ # e = Time.elapse { sleep 1 }
26
+ # e.assert > 1
27
+ #
28
+ def self.elapse
29
+ raise "you need to pass a block" unless block_given?
30
+ t0 = now.to_f
31
+ yield
32
+ now.to_f - t0
33
+ end
34
+
35
+ # Return a float of time since linux epoch
36
+ #
37
+ # @example
38
+ # Time.time -> 1295953427.0005338
39
+ #
40
+ # @return [Float]
41
+ def self.time
42
+ now.to_f
43
+ end
44
+
45
+ unless const_defined?('FORMAT')
46
+ FORMAT = {
47
+ utc: '%Y-%m-%d %H:%M:%S', # => 2015-04-19 14:03:59
48
+ utcT: '%Y-%m-%dT%H:%M:%S', # => 2015-04-19T14:03:59
49
+ db: '%Y-%m-%d %H:%M:%S', # => 2015-04-19 14:03:59
50
+ database: '%Y-%m-%d %H:%M:%S', # => 2015-04-19 14:03:59
51
+ number: '%Y%m%d%H%M%S', # => 20150419140359
52
+ short: '%d %b %H:%M', # => 19 Apr 14:
53
+ time: '%H:%M', # => 14:03
54
+ long: '%B %d, %Y %H:%M', # => April 19, 2015 14:03
55
+ day1st: '%d-%m-%Y %H:%M', # => 19-04-2015 14:03
56
+ dmYHM: '%d-%m-%Y %H:%M', # => 19-04-2015 14:03
57
+ rfc822: '%a, %d %b %Y %H:%M:%S %z', # => Sun, 19 Apr 2015 14:03:59 -0700
58
+ ruby18: '%a %b %d %H:%M:%S %z %Y', # => Sun Apr 19 14:03:59 -0700 2015
59
+ nil => '%Y-%m-%d %H:%M:%S %z' # => 2015-04-19 14:03:59 -0700
60
+ }
61
+ end
62
+
63
+ # Produce time stamp for Time.now. See #stamp.
64
+ #
65
+ def self.stamp(*args)
66
+ now.stamp(*args)
67
+ end
68
+
69
+ # Create a time stamp.
70
+ #
71
+ # @example
72
+ # t = Time.at(10000)
73
+ # t.stamp(:short) # => "31 Dec 21:46"
74
+ #
75
+ # Supported formats come from the Time::FORMAT constant.
76
+ #
77
+ def stamp(format = nil)
78
+ unless String === format
79
+ format = FORMAT[format]
80
+ end
81
+ strftime(format).strip
82
+ end
83
+
84
+ unless method_defined?(:dst_adjustment)
85
+ # Adjust DST
86
+ #
87
+ def dst_adjustment(time)
88
+ self_dst = self.dst? ? 1 : 0
89
+ time_dst = time.dst? ? 1 : 0
90
+ seconds = (self - time).abs
91
+ if (seconds >= 86400 && self_dst != time_dst)
92
+ time + ((self_dst - time_dst) * 60 * 60)
93
+ else
94
+ time
95
+ end
96
+ end
97
+ end
98
+
99
+ # Like change but does not reset earlier times.
100
+ #
101
+ def set(options)
102
+ opts={}
103
+ options.each_pair do |k,v|
104
+ k = :min if k.to_s =~ /^min/
105
+ k = :sec if k.to_s =~ /^sec/
106
+ opts[k] = v.to_i
107
+ end
108
+ self.class.send(
109
+ self.utc? ? :utc : :local,
110
+ opts[:year] || self.year,
111
+ opts[:month] || self.month,
112
+ opts[:day] || self.day,
113
+ opts[:hour] || self.hour,
114
+ opts[:min] || self.min,
115
+ opts[:sec] || self.sec,
116
+ opts[:usec] || self.usec
117
+ )
118
+ end
119
+
120
+ # Returns a new Time representing the time shifted by the time-units given.
121
+ # Positive number shift the time forward, negative number shift the time
122
+ # backward.
123
+ #
124
+ # @example
125
+ # t = Time.utc(2010,10,10,0,0,0)
126
+ # t.shift( 4, :days) # => Time.utc(2010,10,14,0,0,0)
127
+ # t.shift(-4, :days) # => Time.utc(2010,10,6,0,0,0)
128
+ #
129
+ # More than one unit of time can be given.
130
+ # t.shift(4, :days, 3, :hours) # => Time.utc(2010,10,14,3,0,0)
131
+ #
132
+ # The #shift method can also take a hash.
133
+ # t.shift(:days=>4, :hours=>3) # => Time.utc(2010,10,14,3,0,0)
134
+ #
135
+ def shift(*time_units)
136
+ time_hash = Hash===time_units.last ? time_units.pop : {}
137
+ time_units = time_units.flatten
138
+ time_units << :seconds if time_units.size % 2 == 1
139
+ time_hash.each{ |units, number| time_units << number; time_units << units }
140
+
141
+ time = self
142
+ time_units.each_slice(2) do |number, units|
143
+ #next time = time.ago(-number, units) if number < 0
144
+ time = (
145
+ case units.to_s.downcase.to_sym
146
+ when :years, :year
147
+ time.set( :year=>(year + number) )
148
+ when :months, :month
149
+ if number > 0
150
+ new_month = ((month + number - 1) % 12) + 1
151
+ y = (number / 12) + (new_month < month ? 1 : 0)
152
+ time.set(:year => (year + y), :month => new_month)
153
+ else
154
+ number = -number
155
+ new_month = ((month - number - 1) % 12) + 1
156
+ y = (number / 12) + (new_month > month ? 1 : 0)
157
+ time.set(:year => (year - y), :month => new_month)
158
+ end
159
+ when :weeks, :week
160
+ time + (number * 604800)
161
+ when :days, :day
162
+ time + (number * 86400)
163
+ when :hours, :hour
164
+ time + (number * 3600)
165
+ when :minutes, :minute, :mins, :min
166
+ time + (number * 60)
167
+ when :seconds, :second, :secs, :sec, nil
168
+ time + number
169
+ else
170
+ raise ArgumentError, "unrecognized time units -- #{units}"
171
+ end
172
+ )
173
+ end
174
+ dst_adjustment(time)
175
+ end
176
+
177
+ # Alias for #shift.
178
+ alias_method :in, :shift
179
+
180
+ # Alias for #shift.
181
+ alias_method :hence, :shift unless method_defined?(:hence)
182
+
183
+ # Returns a new Time representing the time a number of time-units ago.
184
+ # This is just like #shift, but reverses the direction.
185
+ #
186
+ # @example
187
+ # t = Time.utc(2010,10,10,0,0,0)
188
+ # t.less(4, :days) # => Time.utc(2010,10,6,0,0,0)
189
+ #
190
+ def less(*time_units)
191
+ time_hash = Hash===time_units.last ? time_units.pop : {}
192
+ time_units = time_units.flatten
193
+
194
+ time_units << :seconds if time_units.size % 2 == 1
195
+
196
+ time_hash.each{ |units, number| time_units << number; time_units << units }
197
+
198
+ neg_times = []
199
+ time_units.each_slice(2){ |number, units| neg_times << -number; neg_times << units }
200
+
201
+ shift(*neg_times)
202
+ end
203
+
204
+ # Alias for #less
205
+ alias_method :ago, :less unless method_defined?(:ago)
206
+ end
207
+
208
+ class Numeric
209
+ # Reports the approximate distance in time between two Time, Date or DateTime
210
+ # objects or integers as seconds.
211
+ #
212
+ # @example
213
+ # 1.time_humanize(true) -> 1 seconds
214
+ # 36561906.time_humanize -> 1 years 2 months 3 days 4 hours 5 minutes
215
+ #
216
+ def time_humanize(include_seconds = false)
217
+ deta = self
218
+ deta, seconds = deta.divmod(60)
219
+ deta, minutes = deta.divmod(60)
220
+ deta, hours = deta.divmod(24)
221
+ deta, days = deta.divmod(30)
222
+ years, months = deta.divmod(12)
223
+
224
+ ret = ''
225
+ ret << "#{years} years " unless years == 0
226
+ ret << "#{months} months " unless months == 0
227
+ ret << "#{days} days " unless days == 0
228
+ ret << "#{hours} hours " unless hours == 0
229
+ ret << "#{minutes} minutes " unless minutes == 0
230
+ ret << "#{seconds} seconds" if include_seconds
231
+
232
+ ret.rstrip
233
+ end
234
+ end
@@ -0,0 +1,101 @@
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
+ # Include hooks to extend Resource with class and instance methods.
21
+ #
22
+ module Garcon
23
+ # When foo and bar collide, exceptions happen.
24
+ #
25
+ module Exceptions
26
+
27
+ class UnsupportedPlatform < RuntimeError
28
+ def initialize(platform)
29
+ super "This functionality is not supported on platform #{platform}."
30
+ end
31
+ end
32
+
33
+ class ValidationError < RuntimeError
34
+ attr_accessor :value, :type
35
+
36
+ def initialize(value, type = nil)
37
+ @value, @type = value, type
38
+ super(build_message)
39
+ super(detail)
40
+ end
41
+
42
+ def build_message
43
+ if type?
44
+ "#{value} is not a valid #{type}"
45
+ else
46
+ "Failed to validate #{value.inspect}"
47
+ end
48
+ end
49
+
50
+ def type?
51
+ type.nil? ? false : true
52
+ end
53
+
54
+ # Pretty string output of exception/error object useful for helpful
55
+ # debug messages.
56
+ #
57
+ def detail
58
+ if backtrace
59
+ %{#{self.class.name}: #{message}\n #{backtrace.join("\n ")}\n LOGGED FROM: #{caller[0]}}
60
+ else
61
+ %{#{self.class.name}: #{message}\n LOGGED FROM: #{caller[0]}}
62
+ end
63
+ end
64
+ end
65
+
66
+ # Raised when errors occur during configuration.
67
+ ConfigurationError = Class.new(StandardError)
68
+
69
+ # Raised when an object's methods are called when it has not been
70
+ # properly initialized.
71
+ InitializationError = Class.new(StandardError)
72
+
73
+ # If the maximum number of ReadWriteLock readers or writers is exceeded.
74
+ ResourceLimitError = Class.new(StandardError)
75
+
76
+ # Raised by an `Executor` when it is unable to process a given task,
77
+ # possibly because of a reject policy or other internal error.
78
+ RejectedExecutionError = Class.new(StandardError)
79
+
80
+ # Raised when an operation times out.
81
+ TimeoutError = Class.new(StandardError)
82
+ PollingError = Class.new(StandardError)
83
+
84
+ # Raised when node[:garcon][:databag_type] is not valid.
85
+ InvalidDataBagTypeError = Class.new(RuntimeError)
86
+
87
+ # Raised when cipher direction is invalid.
88
+ InvalidCipherError = Class.new(RuntimeError)
89
+
90
+ # Raised when no encryption key password is specified.
91
+ MissingEncryptionPasswordError = Class.new(RuntimeError)
92
+
93
+ ResourceNotFoundError = Class.new(RuntimeError)
94
+ InvalidStateError = Class.new(RuntimeError)
95
+ InvalidTransitionError = Class.new(RuntimeError)
96
+ InvalidCallbackError = Class.new(RuntimeError)
97
+ TransitionFailedError = Class.new(RuntimeError)
98
+ TransitionConflictError = Class.new(RuntimeError)
99
+ GuardFailedError = Class.new(RuntimeError)
100
+ end
101
+ end
@@ -0,0 +1,237 @@
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 'set'
21
+
22
+ module Garcon
23
+ # The Inflections transforms words from singular to plural, class names to
24
+ # table names, modularized class names to ones without, and class names to
25
+ # foreign keys. The default inflections for pluralization, singularization,
26
+ # and uncountable words are kept in inflections.rb.
27
+ #
28
+ module Inflections
29
+ # Convert input to UpperCamelCase. Will also convert '/' to '::' which is
30
+ # useful for converting paths to namespaces.
31
+ #
32
+ # @param [String] input
33
+ #
34
+ # @return [String]
35
+ #
36
+ def self.camelize(input)
37
+ input.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:\A|_)(.)/) {$1.upcase}
38
+ end
39
+
40
+ # Convert input to underscored, lowercase string. Changes '::' to '/' to
41
+ # convert namespaces to paths.
42
+ #
43
+ # @param [String] input
44
+ #
45
+ # @return [String]
46
+ #
47
+ def self.underscore(input)
48
+ word = input.gsub(/::/, '/')
49
+ underscorize(word)
50
+ end
51
+
52
+ # Convert input underscores to dashes.
53
+ #
54
+ # @param [String] input
55
+ #
56
+ # @return [String]
57
+ #
58
+ def self.dasherize(input)
59
+ input.tr('_', '-')
60
+ end
61
+
62
+ # Return unscoped constant name.
63
+ #
64
+ # @param [String] input
65
+ #
66
+ # @return [String]
67
+ #
68
+ def self.demodulize(input)
69
+ input.split('::').last
70
+ end
71
+
72
+ # Creates a foreign key name
73
+ #
74
+ # @param [String] input
75
+ #
76
+ # @return [String]
77
+ #
78
+ def self.foreign_key(input)
79
+ "#{underscorize(demodulize(input))}_id"
80
+ end
81
+
82
+ # Find a constant with the name specified in the argument string. The name
83
+ # is assumed to be the one of a top-level constant, constant scope of
84
+ # caller is igored.
85
+ #
86
+ # @param [String] input
87
+ #
88
+ # @return [Class, Module]
89
+ #
90
+ def self.constantize(input)
91
+ names = input.split('::')
92
+ names.shift if names.first.empty?
93
+
94
+ names.inject(Object) do |constant, name|
95
+ if constant.const_defined?(name)
96
+ constant.const_get(name)
97
+ else
98
+ constant.const_missing(name)
99
+ end
100
+ end
101
+ end
102
+
103
+ ORDINALIZE_TH = (4..16).to_set.freeze
104
+
105
+ # Convert a number into an ordinal string.
106
+ #
107
+ # @param [Fixnum] number
108
+ #
109
+ # @return [String]
110
+ #
111
+ def self.ordinalize(number)
112
+ abs_value = number.abs
113
+
114
+ if ORDINALIZE_TH.include?(abs_value % 100)
115
+ "#{number}th"
116
+ else
117
+ case abs_value % 10
118
+ when 1; "#{number}st"
119
+ when 2; "#{number}nd"
120
+ when 3; "#{number}rd"
121
+ end
122
+ end
123
+ end
124
+
125
+ # Convert input word string to plural
126
+ #
127
+ # @param [String] word
128
+ #
129
+ # @return [String]
130
+ #
131
+ def self.pluralize(word)
132
+ return word if uncountable?(word)
133
+ inflections.plurals.apply_to(word)
134
+ end
135
+
136
+ # Convert word to singular
137
+ #
138
+ # @param [String] word
139
+ #
140
+ # @return [String]
141
+ #
142
+ def self.singularize(word)
143
+ return word if uncountable?(word)
144
+ inflections.singulars.apply_to(word)
145
+ end
146
+
147
+ # Humanize string.
148
+ #
149
+ # @param [String] input
150
+ #
151
+ # @return [String]
152
+ #
153
+ def self.humanize(input)
154
+ result = inflections.humans.apply_to(input)
155
+ result.gsub!(/_id\z/, "")
156
+ result.tr!('_', " ")
157
+ result.capitalize!
158
+ result
159
+ end
160
+
161
+ # Tabelize input string.
162
+ #
163
+ # @param [String] input
164
+ #
165
+ # @return [String]
166
+ #
167
+ def self.tableize(input)
168
+ pluralize(underscore(input).gsub('/', '_'))
169
+ end
170
+
171
+ # Create a class name from a plural table name like Rails does for table
172
+ # names to models.
173
+ #
174
+ # @param [String] input
175
+ #
176
+ # @return [String]
177
+ #
178
+ def self.classify(table_name)
179
+ camelize(singularize(table_name.sub(/.*\./, '')))
180
+ end
181
+
182
+ # Create a snake case string with an optional namespace prepended.
183
+ #
184
+ # @param [String] input
185
+ #
186
+ # @param [String] namespace
187
+ #
188
+ # @return [String]
189
+ #
190
+ def self.snakeify(input, namespace = nil)
191
+ input = input.dup
192
+ input.sub!(/^#{namespace}(\:\:)?/, '') if namespace
193
+ input.gsub!(/[A-Z]/) {|s| "_" + s}
194
+ input.downcase!
195
+ input.sub!(/^\_/, "")
196
+ input
197
+ end
198
+
199
+ # Test if word is uncountable.
200
+ #
201
+ # @param [String] word
202
+ #
203
+ # @return [Boolean] true, if word is uncountable
204
+ #
205
+ def self.uncountable?(word)
206
+ word.empty? || inflections.uncountables.include?(word.downcase)
207
+ end
208
+
209
+ # Convert input to underscored, lowercase string
210
+ #
211
+ # @param [String] input
212
+ #
213
+ # @return [String]
214
+ #
215
+ def self.underscorize(word)
216
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
217
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
218
+ word.tr!('-', '_')
219
+ word.downcase!
220
+ word
221
+ end
222
+ private_class_method :underscorize
223
+
224
+ # Yields a singleton instance of Garcon::Inflections.
225
+ #
226
+ # @return [Garcon::Inflections]
227
+ #
228
+ def self.inflections
229
+ instance = Inflections.instance
230
+ block_given? ? yield(instance) : instance
231
+ end
232
+ end
233
+ end
234
+
235
+ require_relative 'inflections/rules_collection'
236
+ require_relative 'inflections/inflections'
237
+ require_relative 'inflections/defaults'