sequel 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,59 +1,61 @@
1
1
  module Sequel
2
2
  class Model
3
- HOOKS = [
4
- :after_initialize,
5
- :before_create,
6
- :after_create,
7
- :before_update,
8
- :after_update,
9
- :before_save,
10
- :after_save,
11
- :before_destroy,
12
- :after_destroy
13
- ]
3
+ # Hooks that are safe for public use
4
+ HOOKS = [:after_initialize, :before_create, :after_create, :before_update,
5
+ :after_update, :before_save, :after_save, :before_destroy, :after_destroy,
6
+ :before_validation, :after_validation]
7
+
8
+ # Hooks that are only for internal use
9
+ PRIVATE_HOOKS = [:before_update_values, :before_delete]
14
10
 
15
- # Some fancy code generation here in order to define the hook class methods...
16
- HOOK_METHOD_STR = %Q{
17
- def self.%s(method = nil, &block)
18
- unless block
19
- (raise SequelError, 'No hook method specified') unless method
20
- block = proc {send method}
21
- end
22
- add_hook(%s, &block)
11
+ # Returns true if the model class or any of its ancestors have defined
12
+ # hooks for the given hook key. Notice that this method cannot detect
13
+ # hooks defined using overridden methods.
14
+ def self.has_hooks?(key)
15
+ has = hooks[key] && !hooks[key].empty?
16
+ has || ((self != Model) && superclass.has_hooks?(key))
17
+ end
18
+
19
+ ### Private Class Methods ###
20
+
21
+ # Add a hook block to the list of hook methods.
22
+ # If a non-nil tag is given and it already is in the list of hooks,
23
+ # replace it with the new block.
24
+ def self.add_hook(hook, tag, &block) #:nodoc:
25
+ unless block
26
+ (raise Error, 'No hook method specified') unless tag
27
+ block = proc {send tag}
23
28
  end
24
- }
25
-
26
- class << self
27
- # Returns true if the model class or any of its ancestors have defined
28
- # hooks for the given hook key. Notice that this method cannot detect
29
- # hooks defined using overridden methods.
30
- def has_hooks?(key)
31
- has = hooks[key] && !hooks[key].empty?
32
- has || ((self != Model) && superclass.has_hooks?(key))
29
+ h = hooks[hook]
30
+ if tag && (old = h.find{|x| x[0] == tag})
31
+ old[1] = block
32
+ else
33
+ h << [tag, block]
33
34
  end
34
- private
35
- def def_hook_method(m) #:nodoc:
36
- instance_eval(HOOK_METHOD_STR % [m.to_s, m.inspect])
37
- end
38
-
39
- # Returns the hooks hash for the model class.
40
- def hooks #:nodoc:
41
- @hooks ||= Hash.new {|h, k| h[k] = []}
42
- end
43
-
44
- def add_hook(hook, &block) #:nodoc:
45
- chain = hooks[hook]
46
- chain << block
47
- define_method(hook) do
48
- return false if super == false
49
- chain.each {|h| break false if instance_eval(&h) == false}
50
- end
51
- end
52
35
  end
53
36
 
54
- HOOKS.each do |h|
55
- define_method(h){}
56
- def_hook_method(h)
37
+ # Returns all hook methods for the given type of hook for this
38
+ # model class and its ancestors.
39
+ def self.all_hooks(hook) # :nodoc:
40
+ ((self == Model ? [] : superclass.send(:all_hooks, hook)) + hooks[hook].collect{|x| x[1]})
41
+ end
42
+
43
+ # Returns the hooks hash for this model class.
44
+ def self.hooks #:nodoc:
45
+ @hooks ||= Hash.new {|h, k| h[k] = []}
46
+ end
47
+
48
+ # Runs all hooks of type hook on the given object.
49
+ # Returns false if any hook returns false.
50
+ def self.run_hooks(hook, object) #:nodoc:
51
+ all_hooks(hook).each{|b| return false if object.instance_eval(&b) == false}
52
+ end
53
+
54
+ metaprivate :add_hook, :all_hooks, :hooks, :run_hooks
55
+
56
+ (HOOKS + PRIVATE_HOOKS).each do |hook|
57
+ instance_eval("def #{hook}(method = nil, &block); add_hook(:#{hook}, method, &block) end")
58
+ define_method(hook){model.send(:run_hooks, hook, self)}
57
59
  end
58
60
  end
59
61
  end
@@ -1,13 +1,12 @@
1
- require 'singleton'
1
+ # Add inflection methods to String, which allows the easy transformation of
2
+ # words from singular to plural,class names to table names, modularized class
3
+ # names to ones without, and class names to foreign keys.
2
4
 
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:
5
+ class String
6
+ # This module acts as a singleton returned/yielded by String.inflections,
7
+ # which is used to override or specify additional inflection rules. Examples:
9
8
  #
10
- # Inflector.inflections do |inflect|
9
+ # String.inflections do |inflect|
11
10
  # inflect.plural /^(ox)$/i, '\1\2en'
12
11
  # inflect.singular /^(ox)en/i, '\1'
13
12
  #
@@ -19,25 +18,24 @@ module Inflector
19
18
  # 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
19
  # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
21
20
  # already have been loaded.
22
- class Inflections
23
- include Singleton
21
+ module Inflections
22
+ @plurals, @singulars, @uncountables = [], [], []
24
23
 
25
- attr_reader :plurals, :singulars, :uncountables
24
+ metaattr_reader :plurals, :singulars, :uncountables
26
25
 
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])
26
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
27
+ # the options are: :plurals, :singulars, :uncountables
28
+ #
29
+ # Examples:
30
+ # clear :all
31
+ # clear :plurals
32
+ def self.clear(scope = :all)
33
+ case scope
34
+ when :all
35
+ @plurals, @singulars, @uncountables = [], [], []
36
+ else
37
+ instance_variable_set("@#{scope}", [])
38
+ end
41
39
  end
42
40
 
43
41
  # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
@@ -46,89 +44,101 @@ module Inflector
46
44
  # Examples:
47
45
  # irregular 'octopus', 'octopi'
48
46
  # irregular 'person', 'people'
49
- def irregular(singular, plural)
47
+ def self.irregular(singular, plural)
50
48
  plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
51
49
  singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
52
50
  end
53
51
 
52
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
53
+ # The replacement should always be a string that may include references to the matched data from the rule.
54
+ #
55
+ # Example:
56
+ # plural(/(x|ch|ss|sh)$/i, '\1es')
57
+ def self.plural(rule, replacement)
58
+ @plurals.insert(0, [rule, replacement])
59
+ end
60
+
61
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
62
+ # The replacement should always be a string that may include references to the matched data from the rule.
63
+ #
64
+ # Example:
65
+ # singular(/([^aeiouy]|qu)ies$/i, '\1y')
66
+ def self.singular(rule, replacement)
67
+ @singulars.insert(0, [rule, replacement])
68
+ end
69
+
54
70
  # Add uncountable words that shouldn't be attempted inflected.
55
71
  #
56
72
  # Examples:
57
73
  # uncountable "money"
58
74
  # uncountable "money", "information"
59
75
  # uncountable %w( money information rice )
60
- def uncountable(*words)
76
+ def self.uncountable(*words)
61
77
  (@uncountables << words).flatten!
62
78
  end
63
79
 
64
- # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
65
- # the options are: :plurals, :singulars, :uncountables
66
- #
67
- # Examples:
68
- # clear :all
69
- # clear :plurals
70
- def clear(scope = :all)
71
- case scope
72
- when :all
73
- @plurals, @singulars, @uncountables = [], [], []
74
- else
75
- instance_variable_set "@#{scope}", []
76
- end
77
- end
78
- end
80
+ # Setup the default inflections
81
+ plural(/$/, 's')
82
+ plural(/s$/i, 's')
83
+ plural(/(ax|test)is$/i, '\1es')
84
+ plural(/(octop|vir)us$/i, '\1i')
85
+ plural(/(alias|status)$/i, '\1es')
86
+ plural(/(bu)s$/i, '\1ses')
87
+ plural(/(buffal|tomat)o$/i, '\1oes')
88
+ plural(/([ti])um$/i, '\1a')
89
+ plural(/sis$/i, 'ses')
90
+ plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
91
+ plural(/(hive)$/i, '\1s')
92
+ plural(/([^aeiouy]|qu)y$/i, '\1ies')
93
+ plural(/(x|ch|ss|sh)$/i, '\1es')
94
+ plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
95
+ plural(/([m|l])ouse$/i, '\1ice')
96
+ plural(/^(ox)$/i, '\1en')
97
+ plural(/(quiz)$/i, '\1zes')
79
98
 
80
- extend self
99
+ singular(/s$/i, '')
100
+ singular(/(n)ews$/i, '\1ews')
101
+ singular(/([ti])a$/i, '\1um')
102
+ singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
103
+ singular(/(^analy)ses$/i, '\1sis')
104
+ singular(/([^f])ves$/i, '\1fe')
105
+ singular(/(hive)s$/i, '\1')
106
+ singular(/(tive)s$/i, '\1')
107
+ singular(/([lr])ves$/i, '\1f')
108
+ singular(/([^aeiouy]|qu)ies$/i, '\1y')
109
+ singular(/(s)eries$/i, '\1eries')
110
+ singular(/(m)ovies$/i, '\1ovie')
111
+ singular(/(x|ch|ss|sh)es$/i, '\1')
112
+ singular(/([m|l])ice$/i, '\1ouse')
113
+ singular(/(bus)es$/i, '\1')
114
+ singular(/(o)es$/i, '\1')
115
+ singular(/(shoe)s$/i, '\1')
116
+ singular(/(cris|ax|test)es$/i, '\1is')
117
+ singular(/(octop|vir)i$/i, '\1us')
118
+ singular(/(alias|status)es$/i, '\1')
119
+ singular(/^(ox)en/i, '\1')
120
+ singular(/(vert|ind)ices$/i, '\1ex')
121
+ singular(/(matr)ices$/i, '\1ix')
122
+ singular(/(quiz)zes$/i, '\1')
81
123
 
82
- def inflections
83
- if block_given?
84
- yield Inflections.instance
85
- else
86
- Inflections.instance
87
- end
88
- end
89
-
90
- # Returns the plural form of the word in the string.
91
- #
92
- # Examples
93
- # "post".pluralize #=> "posts"
94
- # "octopus".pluralize #=> "octopi"
95
- # "sheep".pluralize #=> "sheep"
96
- # "words".pluralize #=> "words"
97
- # "the blue mailman".pluralize #=> "the blue mailmen"
98
- # "CamelOctopus".pluralize #=> "CamelOctopi"
99
- def pluralize(word)
100
- result = word.to_s.dup
124
+ irregular('person', 'people')
125
+ irregular('man', 'men')
126
+ irregular('child', 'children')
127
+ irregular('sex', 'sexes')
128
+ irregular('move', 'moves')
101
129
 
102
- if inflections.uncountables.include?(result.downcase)
103
- result
104
- else
105
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
106
- result
107
- end
130
+ uncountable(%w(equipment information rice money species series fish sheep))
108
131
  end
109
132
 
110
- # The reverse of pluralize, returns the singular form of a word in a string.
111
- #
112
- # Examples
113
- # "posts".singularize #=> "post"
114
- # "octopi".singularize #=> "octopus"
115
- # "sheep".singluarize #=> "sheep"
116
- # "word".singluarize #=> "word"
117
- # "the blue mailmen".singularize #=> "the blue mailman"
118
- # "CamelOctopi".singularize #=> "CamelOctopus"
119
- def singularize(word)
120
- result = word.to_s.dup
121
-
122
- if inflections.uncountables.include?(result.downcase)
123
- result
124
- else
125
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
126
- result
127
- end
133
+ # Yield the Inflections module if a block is given, and return
134
+ # the Inflections module.
135
+ def self.inflections
136
+ yield Inflections if block_given?
137
+ Inflections
128
138
  end
129
139
 
130
- # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
131
- # is set to ":lower" then camelize produces lowerCamelCase.
140
+ # By default, camelize converts the string to UpperCamelCase. If the argument to camelize
141
+ # is set to :lower then camelize produces lowerCamelCase.
132
142
  #
133
143
  # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
134
144
  #
@@ -137,143 +147,137 @@ module Inflector
137
147
  # "active_record".camelize(:lower) #=> "activeRecord"
138
148
  # "active_record/errors".camelize #=> "ActiveRecord::Errors"
139
149
  # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
140
- def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
141
- if first_letter_in_uppercase
142
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
150
+ def camelize(first_letter_in_uppercase = :upper)
151
+ if first_letter_in_uppercase == :upper
152
+ gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
143
153
  else
144
- lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
154
+ "#{first}#{camelize[1..-1]}"
145
155
  end
146
156
  end
157
+ alias_method :camelcase, :camelize
147
158
 
148
- # Capitalizes all the words and replaces some characters in the string to create
149
- # a nicer looking title. Titleize is meant for creating pretty output. It is not
150
- # used in the Rails internals.
151
- #
152
- # titleize is also aliased as as titlecase
159
+ # Singularizes and camelizes the string. Also strips out all characters preceding
160
+ # and including a period (".").
153
161
  #
154
162
  # Examples
155
- # "man from the boondocks".titleize #=> "Man From The Boondocks"
156
- # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
157
- def titleize(word)
158
- humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
163
+ # "egg_and_hams".classify #=> "EggAndHam"
164
+ # "post".classify #=> "Post"
165
+ # "schema.post".classify #=> "Post"
166
+ def classify
167
+ sub(/.*\./, '').singularize.camelize
159
168
  end
160
169
 
161
- # The reverse of +camelize+. Makes an underscored form from the expression in the string.
162
- #
163
- # Changes '::' to '/' to convert namespaces to paths.
170
+ # Constantize tries to find a declared constant with the name specified
171
+ # in the string. It raises a NameError when the name is not in CamelCase
172
+ # or is not initialized.
164
173
  #
165
174
  # Examples
166
- # "ActiveRecord".underscore #=> "active_record"
167
- # "ActiveRecord::Errors".underscore #=> active_record/errors
168
- def underscore(camel_cased_word)
169
- camel_cased_word.to_s.gsub(/::/, '/').
170
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
171
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
172
- tr("-", "_").
173
- downcase
175
+ # "Module".constantize #=> Module
176
+ # "Class".constantize #=> Class
177
+ def constantize
178
+ raise(NameError, "#{inspect} is not a valid constant name!") unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(self)
179
+ Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
174
180
  end
175
181
 
176
182
  # Replaces underscores with dashes in the string.
177
183
  #
178
184
  # Example
179
- # "puni_puni" #=> "puni-puni"
180
- def dasherize(underscored_word)
181
- underscored_word.gsub(/_/, '-')
185
+ # "puni_puni".dasherize #=> "puni-puni"
186
+ def dasherize
187
+ gsub(/_/, '-')
182
188
  end
183
189
 
184
- # Capitalizes the first word and turns underscores into spaces and strips _id.
185
- # Like titleize, this is meant for creating pretty output.
190
+ # Removes the module part from the expression in the string
186
191
  #
187
192
  # Examples
188
- # "employee_salary" #=> "Employee salary"
189
- # "author_id" #=> "Author"
190
- def humanize(lower_case_and_underscored_word)
191
- lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
193
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
194
+ # "Inflections".demodulize #=> "Inflections"
195
+ def demodulize
196
+ gsub(/^.*::/, '')
192
197
  end
193
198
 
194
- # Removes the module part from the expression in the string
199
+ # Creates a foreign key name from a class name.
200
+ # +use_underscore+ sets whether the method should put '_' between the name and 'id'.
195
201
  #
196
202
  # Examples
197
- # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
198
- # "Inflections".demodulize #=> "Inflections"
199
- def demodulize(class_name_in_module)
200
- class_name_in_module.to_s.gsub(/^.*::/, '')
203
+ # "Message".foreign_key #=> "message_id"
204
+ # "Message".foreign_key(false) #=> "messageid"
205
+ # "Admin::Post".foreign_key #=> "post_id"
206
+ def foreign_key(use_underscore = true)
207
+ "#{demodulize.underscore}#{'_' if use_underscore}id"
201
208
  end
202
209
 
203
- # Create the name of a table like Rails does for models to table names. This method
204
- # uses the pluralize method on the last word in the string.
210
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
211
+ # Like titleize, this is meant for creating pretty output.
205
212
  #
206
213
  # Examples
207
- # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
208
- # "egg_and_ham".tableize #=> "egg_and_hams"
209
- # "fancyCategory".tableize #=> "fancy_categories"
210
- def tableize(class_name)
211
- pluralize(underscore(class_name))
214
+ # "employee_salary" #=> "Employee salary"
215
+ # "author_id" #=> "Author"
216
+ def humanize
217
+ gsub(/_id$/, "").gsub(/_/, " ").capitalize
212
218
  end
213
219
 
214
- # Create a class name from a table name like Rails does for table names to models.
215
- # Note that this returns a string and not a Class. (To convert to an actual class
216
- # follow classify with constantize.)
220
+ # Returns the plural form of the word in the string.
217
221
  #
218
222
  # Examples
219
- # "egg_and_hams".classify #=> "EggAndHam"
220
- # "post".classify #=> "Post"
221
- def classify(table_name)
222
- # strip out any leading schema name
223
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
223
+ # "post".pluralize #=> "posts"
224
+ # "octopus".pluralize #=> "octopi"
225
+ # "sheep".pluralize #=> "sheep"
226
+ # "words".pluralize #=> "words"
227
+ # "the blue mailman".pluralize #=> "the blue mailmen"
228
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
229
+ def pluralize
230
+ result = dup
231
+ Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
232
+ result
224
233
  end
225
234
 
226
- # Creates a foreign key name from a class name.
227
- # +separate_class_name_and_id_with_underscore+ sets whether
228
- # the method should put '_' between the name and 'id'.
235
+ # The reverse of pluralize, returns the singular form of a word in a string.
229
236
  #
230
237
  # Examples
231
- # "Message".foreign_key #=> "message_id"
232
- # "Message".foreign_key(false) #=> "messageid"
233
- # "Admin::Post".foreign_key #=> "post_id"
234
- def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
235
- underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
238
+ # "posts".singularize #=> "post"
239
+ # "octopi".singularize #=> "octopus"
240
+ # "sheep".singluarize #=> "sheep"
241
+ # "word".singluarize #=> "word"
242
+ # "the blue mailmen".singularize #=> "the blue mailman"
243
+ # "CamelOctopi".singularize #=> "CamelOctopus"
244
+ def singularize
245
+ result = dup
246
+ Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
247
+ result
236
248
  end
237
249
 
238
- # Constantize tries to find a declared constant with the name specified
239
- # in the string. It raises a NameError when the name is not in CamelCase
240
- # or is not initialized.
250
+ # Underscores and pluralizes the string.
241
251
  #
242
252
  # Examples
243
- # "Module".constantize #=> Module
244
- # "Class".constantize #=> Class
245
- def constantize(camel_cased_word)
246
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
247
- raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
248
- end
253
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
254
+ # "egg_and_ham".tableize #=> "egg_and_hams"
255
+ # "fancyCategory".tableize #=> "fancy_categories"
256
+ def tableize
257
+ underscore.pluralize
258
+ end
249
259
 
250
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
260
+ # Capitalizes all the words and replaces some characters in the string to create
261
+ # a nicer looking title. Titleize is meant for creating pretty output.
262
+ #
263
+ # titleize is also aliased as as titlecase
264
+ #
265
+ # Examples
266
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
267
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
268
+ def titleize
269
+ underscore.humanize.gsub(/\b([a-z])/){|x| x[-1..-1].upcase}
251
270
  end
271
+ alias_method :titlecase, :titleize
252
272
 
253
- # Ordinalize turns a number into an ordinal string used to denote the
254
- # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
273
+ # The reverse of camelize. Makes an underscored form from the expression in the string.
274
+ # Also changes '::' to '/' to convert namespaces to paths.
255
275
  #
256
276
  # Examples
257
- # ordinalize(1) # => "1st"
258
- # ordinalize(2) # => "2nd"
259
- # ordinalize(1002) # => "1002nd"
260
- # ordinalize(1003) # => "1003rd"
261
- def ordinalize(number)
262
- if (11..13).include?(number.to_i % 100)
263
- "#{number}th"
264
- else
265
- case number.to_i % 10
266
- when 1
267
- "#{number}st"
268
- when 2
269
- "#{number}nd"
270
- when 3
271
- "#{number}rd"
272
- else
273
- "#{number}th"
274
- end
275
- end
277
+ # "ActiveRecord".underscore #=> "active_record"
278
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
279
+ def underscore
280
+ gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
281
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
276
282
  end
277
283
  end
278
-
279
- require File.dirname(__FILE__) + '/inflections'