automate-it 0.9.0

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 (137) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hgignore +10 -0
  4. data/.loadpath +5 -0
  5. data/.project +17 -0
  6. data/CHANGES.txt +314 -0
  7. data/Hoe.rake +40 -0
  8. data/Manifest.txt +164 -0
  9. data/README.txt +40 -0
  10. data/Rakefile +256 -0
  11. data/TESTING.txt +57 -0
  12. data/TODO.txt +50 -0
  13. data/TUTORIAL.txt +391 -0
  14. data/automate-it.gemspec +25 -0
  15. data/bin/ai +3 -0
  16. data/bin/aifield +75 -0
  17. data/bin/aissh +93 -0
  18. data/bin/aitag +134 -0
  19. data/bin/automateit +133 -0
  20. data/docs/friendly_errors.txt +50 -0
  21. data/docs/previews.txt +86 -0
  22. data/examples/basic/Rakefile +26 -0
  23. data/examples/basic/config/automateit_env.rb +16 -0
  24. data/examples/basic/config/fields.yml +3 -0
  25. data/examples/basic/config/tags.yml +7 -0
  26. data/examples/basic/dist/README.txt +9 -0
  27. data/examples/basic/dist/myapp_server.erb +30 -0
  28. data/examples/basic/install.log +15 -0
  29. data/examples/basic/lib/README.txt +10 -0
  30. data/examples/basic/recipes/README.txt +4 -0
  31. data/examples/basic/recipes/install.rb +61 -0
  32. data/examples/basic/recipes/uninstall.rb +6 -0
  33. data/gpl.txt +674 -0
  34. data/helpers/cpan_wrapper.pl +220 -0
  35. data/helpers/which.cmd +7 -0
  36. data/lib/automateit.rb +55 -0
  37. data/lib/automateit/account_manager.rb +114 -0
  38. data/lib/automateit/account_manager/base.rb +138 -0
  39. data/lib/automateit/account_manager/etc.rb +128 -0
  40. data/lib/automateit/account_manager/nscd.rb +33 -0
  41. data/lib/automateit/account_manager/passwd_expect.rb +40 -0
  42. data/lib/automateit/account_manager/passwd_pty.rb +69 -0
  43. data/lib/automateit/account_manager/posix.rb +138 -0
  44. data/lib/automateit/address_manager.rb +88 -0
  45. data/lib/automateit/address_manager/base.rb +171 -0
  46. data/lib/automateit/address_manager/bsd.rb +28 -0
  47. data/lib/automateit/address_manager/freebsd.rb +59 -0
  48. data/lib/automateit/address_manager/linux.rb +42 -0
  49. data/lib/automateit/address_manager/openbsd.rb +66 -0
  50. data/lib/automateit/address_manager/portable.rb +37 -0
  51. data/lib/automateit/address_manager/sunos.rb +34 -0
  52. data/lib/automateit/cli.rb +85 -0
  53. data/lib/automateit/common.rb +65 -0
  54. data/lib/automateit/constants.rb +35 -0
  55. data/lib/automateit/download_manager.rb +48 -0
  56. data/lib/automateit/edit_manager.rb +321 -0
  57. data/lib/automateit/error.rb +10 -0
  58. data/lib/automateit/field_manager.rb +103 -0
  59. data/lib/automateit/interpreter.rb +631 -0
  60. data/lib/automateit/package_manager.rb +257 -0
  61. data/lib/automateit/package_manager/apt.rb +27 -0
  62. data/lib/automateit/package_manager/cpan.rb +101 -0
  63. data/lib/automateit/package_manager/dpkg.rb +54 -0
  64. data/lib/automateit/package_manager/egg.rb +64 -0
  65. data/lib/automateit/package_manager/gem.rb +201 -0
  66. data/lib/automateit/package_manager/pear.rb +95 -0
  67. data/lib/automateit/package_manager/pecl.rb +80 -0
  68. data/lib/automateit/package_manager/portage.rb +69 -0
  69. data/lib/automateit/package_manager/yum.rb +65 -0
  70. data/lib/automateit/platform_manager.rb +49 -0
  71. data/lib/automateit/platform_manager/darwin.rb +30 -0
  72. data/lib/automateit/platform_manager/debian.rb +26 -0
  73. data/lib/automateit/platform_manager/freebsd.rb +29 -0
  74. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  75. data/lib/automateit/platform_manager/lsb.rb +44 -0
  76. data/lib/automateit/platform_manager/openbsd.rb +28 -0
  77. data/lib/automateit/platform_manager/struct.rb +80 -0
  78. data/lib/automateit/platform_manager/sunos.rb +39 -0
  79. data/lib/automateit/platform_manager/uname.rb +29 -0
  80. data/lib/automateit/platform_manager/windows.rb +40 -0
  81. data/lib/automateit/plugin.rb +7 -0
  82. data/lib/automateit/plugin/base.rb +32 -0
  83. data/lib/automateit/plugin/driver.rb +256 -0
  84. data/lib/automateit/plugin/manager.rb +224 -0
  85. data/lib/automateit/project.rb +493 -0
  86. data/lib/automateit/root.rb +17 -0
  87. data/lib/automateit/service_manager.rb +93 -0
  88. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  89. data/lib/automateit/service_manager/rc_update.rb +37 -0
  90. data/lib/automateit/service_manager/sysv.rb +139 -0
  91. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  92. data/lib/automateit/shell_manager.rb +316 -0
  93. data/lib/automateit/shell_manager/base_link.rb +67 -0
  94. data/lib/automateit/shell_manager/link.rb +24 -0
  95. data/lib/automateit/shell_manager/portable.rb +523 -0
  96. data/lib/automateit/shell_manager/symlink.rb +32 -0
  97. data/lib/automateit/shell_manager/which_base.rb +30 -0
  98. data/lib/automateit/shell_manager/which_unix.rb +16 -0
  99. data/lib/automateit/shell_manager/which_windows.rb +20 -0
  100. data/lib/automateit/tag_manager.rb +127 -0
  101. data/lib/automateit/tag_manager/struct.rb +121 -0
  102. data/lib/automateit/tag_manager/tag_parser.rb +93 -0
  103. data/lib/automateit/tag_manager/yaml.rb +29 -0
  104. data/lib/automateit/template_manager.rb +56 -0
  105. data/lib/automateit/template_manager/base.rb +181 -0
  106. data/lib/automateit/template_manager/erb.rb +17 -0
  107. data/lib/ext/metaclass.rb +17 -0
  108. data/lib/ext/object.rb +18 -0
  109. data/lib/ext/shell_escape.rb +7 -0
  110. data/lib/hashcache.rb +22 -0
  111. data/lib/helpful_erb.rb +63 -0
  112. data/lib/inactive_support.rb +53 -0
  113. data/lib/inactive_support/basic_object.rb +6 -0
  114. data/lib/inactive_support/clean_logger.rb +127 -0
  115. data/lib/inactive_support/core_ext/array/extract_options.rb +19 -0
  116. data/lib/inactive_support/core_ext/blank.rb +50 -0
  117. data/lib/inactive_support/core_ext/class/attribute_accessors.rb +48 -0
  118. data/lib/inactive_support/core_ext/class/inheritable_attributes.rb +140 -0
  119. data/lib/inactive_support/core_ext/enumerable.rb +63 -0
  120. data/lib/inactive_support/core_ext/hash/keys.rb +54 -0
  121. data/lib/inactive_support/core_ext/module/aliasing.rb +70 -0
  122. data/lib/inactive_support/core_ext/numeric/time.rb +91 -0
  123. data/lib/inactive_support/core_ext/string/inflections.rb +153 -0
  124. data/lib/inactive_support/core_ext/symbol.rb +14 -0
  125. data/lib/inactive_support/core_ext/time/conversions.rb +96 -0
  126. data/lib/inactive_support/duration.rb +96 -0
  127. data/lib/inactive_support/inflections.rb +53 -0
  128. data/lib/inactive_support/inflector.rb +282 -0
  129. data/lib/nested_error.rb +33 -0
  130. data/lib/nitpick.rb +33 -0
  131. data/lib/queued_logger.rb +68 -0
  132. data/lib/tempster.rb +250 -0
  133. data/misc/index_gem_repository.rb +304 -0
  134. data/misc/setup_egg.rb +12 -0
  135. data/misc/setup_gem_dependencies.sh +6 -0
  136. data/misc/setup_rubygems.sh +21 -0
  137. metadata +279 -0
@@ -0,0 +1,96 @@
1
+ module InactiveSupport
2
+ # Provides accurate date and time measurements using Date#advance and
3
+ # Time#advance, respectively. It mainly supports the methods on Numeric,
4
+ # such as in this example:
5
+ #
6
+ # 1.month.ago # equivalent to Time.now.advance(:months => -1)
7
+ class Duration < ActiveSupport::BasicObject
8
+ attr_accessor :value, :parts
9
+
10
+ def initialize(value, parts) #:nodoc:
11
+ @value, @parts = value, parts
12
+ end
13
+
14
+ # Adds another Duration or a Numeric to this Duration. Numeric values
15
+ # are treated as seconds.
16
+ def +(other)
17
+ if Duration === other
18
+ Duration.new(value + other.value, @parts + other.parts)
19
+ else
20
+ Duration.new(value + other, @parts + [[:seconds, other]])
21
+ end
22
+ end
23
+
24
+ # Subtracts another Duration or a Numeric from this Duration. Numeric
25
+ # values are treated as seconds.
26
+ def -(other)
27
+ self + (-other)
28
+ end
29
+
30
+ def -@ #:nodoc:
31
+ Duration.new(-value, parts.map { |type,number| [type, -number] })
32
+ end
33
+
34
+ def is_a?(klass) #:nodoc:
35
+ klass == Duration || super
36
+ end
37
+
38
+ # Returns true if <tt>other</tt> is also a Duration instance with the
39
+ # same <tt>value</tt>, or if <tt>other == value</tt>.
40
+ def ==(other)
41
+ if Duration === other
42
+ other.value == value
43
+ else
44
+ other == value
45
+ end
46
+ end
47
+
48
+ def self.===(other) #:nodoc:
49
+ other.is_a?(Duration) rescue super
50
+ end
51
+
52
+ # Calculates a new Time or Date that is as far in the future
53
+ # as this Duration represents.
54
+ def since(time = ::Time.now)
55
+ sum(1, time)
56
+ end
57
+ alias :from_now :since
58
+
59
+ # Calculates a new Time or Date that is as far in the past
60
+ # as this Duration represents.
61
+ def ago(time = ::Time.now)
62
+ sum(-1, time)
63
+ end
64
+ alias :until :ago
65
+
66
+ def inspect #:nodoc:
67
+ consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
68
+ [:years, :months, :days, :minutes, :seconds].map do |length|
69
+ n = consolidated[length]
70
+ "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
71
+ end.compact.to_sentence
72
+ end
73
+
74
+ protected
75
+
76
+ def sum(sign, time = ::Time.now) #:nodoc:
77
+ parts.inject(time) do |t,(type,number)|
78
+ if t.acts_like?(:time) || t.acts_like?(:date)
79
+ if type == :seconds
80
+ t.since(sign * number)
81
+ else
82
+ t.advance(type => sign * number)
83
+ end
84
+ else
85
+ raise ArgumentError, "expected a time or date, got #{time.inspect}"
86
+ end
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def method_missing(method, *args, &block) #:nodoc:
93
+ value.send(method, *args)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,53 @@
1
+ Inflector.inflections do |inflect|
2
+ inflect.plural(/$/, 's')
3
+ inflect.plural(/s$/i, 's')
4
+ inflect.plural(/(ax|test)is$/i, '\1es')
5
+ inflect.plural(/(octop|vir)us$/i, '\1i')
6
+ inflect.plural(/(alias|status)$/i, '\1es')
7
+ inflect.plural(/(bu)s$/i, '\1ses')
8
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
9
+ inflect.plural(/([ti])um$/i, '\1a')
10
+ inflect.plural(/sis$/i, 'ses')
11
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
12
+ inflect.plural(/(hive)$/i, '\1s')
13
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
14
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
15
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
16
+ inflect.plural(/([m|l])ouse$/i, '\1ice')
17
+ inflect.plural(/^(ox)$/i, '\1en')
18
+ inflect.plural(/(quiz)$/i, '\1zes')
19
+
20
+ inflect.singular(/s$/i, '')
21
+ inflect.singular(/(n)ews$/i, '\1ews')
22
+ inflect.singular(/([ti])a$/i, '\1um')
23
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
24
+ inflect.singular(/(^analy)ses$/i, '\1sis')
25
+ inflect.singular(/([^f])ves$/i, '\1fe')
26
+ inflect.singular(/(hive)s$/i, '\1')
27
+ inflect.singular(/(tive)s$/i, '\1')
28
+ inflect.singular(/([lr])ves$/i, '\1f')
29
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
30
+ inflect.singular(/(s)eries$/i, '\1eries')
31
+ inflect.singular(/(m)ovies$/i, '\1ovie')
32
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
33
+ inflect.singular(/([m|l])ice$/i, '\1ouse')
34
+ inflect.singular(/(bus)es$/i, '\1')
35
+ inflect.singular(/(o)es$/i, '\1')
36
+ inflect.singular(/(shoe)s$/i, '\1')
37
+ inflect.singular(/(cris|ax|test)es$/i, '\1is')
38
+ inflect.singular(/(octop|vir)i$/i, '\1us')
39
+ inflect.singular(/(alias|status)es$/i, '\1')
40
+ inflect.singular(/^(ox)en/i, '\1')
41
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
42
+ inflect.singular(/(matr)ices$/i, '\1ix')
43
+ inflect.singular(/(quiz)zes$/i, '\1')
44
+
45
+ inflect.irregular('person', 'people')
46
+ inflect.irregular('man', 'men')
47
+ inflect.irregular('child', 'children')
48
+ inflect.irregular('sex', 'sexes')
49
+ inflect.irregular('move', 'moves')
50
+ inflect.irregular('cow', 'kine')
51
+
52
+ inflect.uncountable(%w(equipment information rice money species series fish sheep))
53
+ end
@@ -0,0 +1,282 @@
1
+ require 'singleton'
2
+
3
+ # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
4
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
5
+ # in inflections.rb.
6
+ module Inflector
7
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
8
+ # inflection rules. Examples:
9
+ #
10
+ # Inflector.inflections do |inflect|
11
+ # inflect.plural /^(ox)$/i, '\1\2en'
12
+ # inflect.singular /^(ox)en/i, '\1'
13
+ #
14
+ # inflect.irregular 'octopus', 'octopi'
15
+ #
16
+ # inflect.uncountable "equipment"
17
+ # end
18
+ #
19
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
20
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
21
+ # already have been loaded.
22
+ class Inflections
23
+ include Singleton
24
+
25
+ attr_reader :plurals, :singulars, :uncountables
26
+
27
+ def initialize
28
+ @plurals, @singulars, @uncountables = [], [], []
29
+ end
30
+
31
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
32
+ # The replacement should always be a string that may include references to the matched data from the rule.
33
+ def plural(rule, replacement)
34
+ @plurals.insert(0, [rule, replacement])
35
+ end
36
+
37
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
38
+ # The replacement should always be a string that may include references to the matched data from the rule.
39
+ def singular(rule, replacement)
40
+ @singulars.insert(0, [rule, replacement])
41
+ end
42
+
43
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
44
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
45
+ #
46
+ # Examples:
47
+ # irregular 'octopus', 'octopi'
48
+ # irregular 'person', 'people'
49
+ def irregular(singular, plural)
50
+ if singular[0,1].upcase == plural[0,1].upcase
51
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
52
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
53
+ else
54
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
55
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
56
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
57
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
58
+ end
59
+ end
60
+
61
+ # Add uncountable words that shouldn't be attempted inflected.
62
+ #
63
+ # Examples:
64
+ # uncountable "money"
65
+ # uncountable "money", "information"
66
+ # uncountable %w( money information rice )
67
+ def uncountable(*words)
68
+ (@uncountables << words).flatten!
69
+ end
70
+
71
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
72
+ # the options are: :plurals, :singulars, :uncountables
73
+ #
74
+ # Examples:
75
+ # clear :all
76
+ # clear :plurals
77
+ def clear(scope = :all)
78
+ case scope
79
+ when :all
80
+ @plurals, @singulars, @uncountables = [], [], []
81
+ else
82
+ instance_variable_set "@#{scope}", []
83
+ end
84
+ end
85
+ end
86
+
87
+ extend self
88
+
89
+ def inflections
90
+ if block_given?
91
+ yield Inflections.instance
92
+ else
93
+ Inflections.instance
94
+ end
95
+ end
96
+
97
+ # Returns the plural form of the word in the string.
98
+ #
99
+ # Examples
100
+ # "post".pluralize #=> "posts"
101
+ # "octopus".pluralize #=> "octopi"
102
+ # "sheep".pluralize #=> "sheep"
103
+ # "words".pluralize #=> "words"
104
+ # "the blue mailman".pluralize #=> "the blue mailmen"
105
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
106
+ def pluralize(word)
107
+ result = word.to_s.dup
108
+
109
+ if word.empty? || inflections.uncountables.include?(result.downcase)
110
+ result
111
+ else
112
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
113
+ result
114
+ end
115
+ end
116
+
117
+ # The reverse of pluralize, returns the singular form of a word in a string.
118
+ #
119
+ # Examples
120
+ # "posts".singularize #=> "post"
121
+ # "octopi".singularize #=> "octopus"
122
+ # "sheep".singluarize #=> "sheep"
123
+ # "word".singluarize #=> "word"
124
+ # "the blue mailmen".singularize #=> "the blue mailman"
125
+ # "CamelOctopi".singularize #=> "CamelOctopus"
126
+ def singularize(word)
127
+ result = word.to_s.dup
128
+
129
+ if inflections.uncountables.include?(result.downcase)
130
+ result
131
+ else
132
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
133
+ result
134
+ end
135
+ end
136
+
137
+ # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
138
+ # is set to ":lower" then camelize produces lowerCamelCase.
139
+ #
140
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
141
+ #
142
+ # Examples
143
+ # "active_record".camelize #=> "ActiveRecord"
144
+ # "active_record".camelize(:lower) #=> "activeRecord"
145
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
146
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
147
+ def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
148
+ if first_letter_in_uppercase
149
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
150
+ else
151
+ lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
152
+ end
153
+ end
154
+
155
+ # Capitalizes all the words and replaces some characters in the string to create
156
+ # a nicer looking title. Titleize is meant for creating pretty output. It is not
157
+ # used in the Rails internals.
158
+ #
159
+ # titleize is also aliased as as titlecase
160
+ #
161
+ # Examples
162
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
163
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
164
+ def titleize(word)
165
+ humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
166
+ end
167
+
168
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
169
+ #
170
+ # Changes '::' to '/' to convert namespaces to paths.
171
+ #
172
+ # Examples
173
+ # "ActiveRecord".underscore #=> "active_record"
174
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
175
+ def underscore(camel_cased_word)
176
+ camel_cased_word.to_s.gsub(/::/, '/').
177
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
178
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
179
+ tr("-", "_").
180
+ downcase
181
+ end
182
+
183
+ # Replaces underscores with dashes in the string.
184
+ #
185
+ # Example
186
+ # "puni_puni" #=> "puni-puni"
187
+ def dasherize(underscored_word)
188
+ underscored_word.gsub(/_/, '-')
189
+ end
190
+
191
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
192
+ # Like titleize, this is meant for creating pretty output.
193
+ #
194
+ # Examples
195
+ # "employee_salary" #=> "Employee salary"
196
+ # "author_id" #=> "Author"
197
+ def humanize(lower_case_and_underscored_word)
198
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
199
+ end
200
+
201
+ # Removes the module part from the expression in the string
202
+ #
203
+ # Examples
204
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
205
+ # "Inflections".demodulize #=> "Inflections"
206
+ def demodulize(class_name_in_module)
207
+ class_name_in_module.to_s.gsub(/^.*::/, '')
208
+ end
209
+
210
+ # Create the name of a table like Rails does for models to table names. This method
211
+ # uses the pluralize method on the last word in the string.
212
+ #
213
+ # Examples
214
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
215
+ # "egg_and_ham".tableize #=> "egg_and_hams"
216
+ # "fancyCategory".tableize #=> "fancy_categories"
217
+ def tableize(class_name)
218
+ pluralize(underscore(class_name))
219
+ end
220
+
221
+ # Create a class name from a table name like Rails does for table names to models.
222
+ # Note that this returns a string and not a Class. (To convert to an actual class
223
+ # follow classify with constantize.)
224
+ #
225
+ # Examples
226
+ # "egg_and_hams".classify #=> "EggAndHam"
227
+ # "post".classify #=> "Post"
228
+ def classify(table_name)
229
+ # strip out any leading schema name
230
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
231
+ end
232
+
233
+ # Creates a foreign key name from a class name.
234
+ # +separate_class_name_and_id_with_underscore+ sets whether
235
+ # the method should put '_' between the name and 'id'.
236
+ #
237
+ # Examples
238
+ # "Message".foreign_key #=> "message_id"
239
+ # "Message".foreign_key(false) #=> "messageid"
240
+ # "Admin::Post".foreign_key #=> "post_id"
241
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
242
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
243
+ end
244
+
245
+ # Constantize tries to find a declared constant with the name specified
246
+ # in the string. It raises a NameError when the name is not in CamelCase
247
+ # or is not initialized.
248
+ #
249
+ # Examples
250
+ # "Module".constantize #=> Module
251
+ # "Class".constantize #=> Class
252
+ def constantize(camel_cased_word)
253
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
254
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
255
+ end
256
+
257
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
258
+ end
259
+
260
+ # Ordinalize turns a number into an ordinal string used to denote the
261
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
262
+ #
263
+ # Examples
264
+ # ordinalize(1) # => "1st"
265
+ # ordinalize(2) # => "2nd"
266
+ # ordinalize(1002) # => "1002nd"
267
+ # ordinalize(1003) # => "1003rd"
268
+ def ordinalize(number)
269
+ if (11..13).include?(number.to_i % 100)
270
+ "#{number}th"
271
+ else
272
+ case number.to_i % 10
273
+ when 1; "#{number}st"
274
+ when 2; "#{number}nd"
275
+ when 3; "#{number}rd"
276
+ else "#{number}th"
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ require File.dirname(__FILE__) + '/inflections'
@@ -0,0 +1,33 @@
1
+ # == NestedError
2
+ #
3
+ # An exception class that records the cause of another error. Useful when you
4
+ # need to raise a general kind of error, yet still be able to determine the
5
+ # underlying cause.
6
+ #
7
+ # Example:
8
+ #
9
+ # class MyGeneralError < NestedError; end
10
+ #
11
+ # begin
12
+ # begin
13
+ # # Cause a specific error
14
+ # 1/0 # Divide by zero error
15
+ # rescue Exception => e
16
+ # # Wrap the specific error in a general, nested error
17
+ # raise MyGeneralError("Something bad happened!", e)
18
+ # end
19
+ # rescue MyGeneralError => e
20
+ # # Intercept the nested error and inspect the cause
21
+ # puts e.message # => "Something bad happened!"
22
+ # puts e.cause.message # => "divided by 0"
23
+ # end
24
+ class NestedError < StandardError
25
+ attr_accessor :cause
26
+
27
+ # Create a NestedObject with a +message+ String and a +cause+ Exception.
28
+ def initialize(message, cause)
29
+ self.cause = cause
30
+ super(message)
31
+ end
32
+ end
33
+