sequel 1.5.1 → 2.0.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.
- data/CHANGELOG +30 -0
- data/README +12 -15
- data/Rakefile +9 -20
- data/lib/sequel_model.rb +47 -72
- data/lib/sequel_model/association_reflection.rb +59 -0
- data/lib/sequel_model/associations.rb +99 -94
- data/lib/sequel_model/base.rb +308 -102
- data/lib/sequel_model/caching.rb +72 -27
- data/lib/sequel_model/eager_loading.rb +308 -300
- data/lib/sequel_model/hooks.rb +51 -49
- data/lib/sequel_model/inflector.rb +186 -182
- data/lib/sequel_model/plugins.rb +54 -40
- data/lib/sequel_model/record.rb +185 -220
- data/lib/sequel_model/schema.rb +27 -34
- data/lib/sequel_model/validations.rb +54 -73
- data/spec/association_reflection_spec.rb +85 -0
- data/spec/associations_spec.rb +160 -73
- data/spec/base_spec.rb +3 -3
- data/spec/eager_loading_spec.rb +132 -35
- data/spec/hooks_spec.rb +120 -20
- data/spec/inflector_spec.rb +2 -2
- data/spec/model_spec.rb +110 -78
- data/spec/plugins_spec.rb +4 -0
- data/spec/rcov.opts +1 -1
- data/spec/record_spec.rb +160 -59
- data/spec/spec.opts +0 -5
- data/spec/spec_helper.rb +12 -0
- data/spec/validations_spec.rb +23 -0
- metadata +60 -50
- data/lib/sequel_model/deprecated.rb +0 -81
- data/lib/sequel_model/inflections.rb +0 -112
- data/spec/deprecated_relations_spec.rb +0 -113
data/lib/sequel_model/hooks.rb
CHANGED
@@ -1,59 +1,61 @@
|
|
1
1
|
module Sequel
|
2
2
|
class Model
|
3
|
-
|
4
|
-
|
5
|
-
:
|
6
|
-
:
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
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
|
-
|
4
|
-
#
|
5
|
-
#
|
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
|
-
#
|
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
|
-
|
23
|
-
|
21
|
+
module Inflections
|
22
|
+
@plurals, @singulars, @uncountables = [], [], []
|
24
23
|
|
25
|
-
|
24
|
+
metaattr_reader :plurals, :singulars, :uncountables
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
#
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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
|
-
#
|
111
|
-
#
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
131
|
-
# is set to
|
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(
|
141
|
-
if first_letter_in_uppercase
|
142
|
-
|
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
|
-
|
154
|
+
"#{first}#{camelize[1..-1]}"
|
145
155
|
end
|
146
156
|
end
|
157
|
+
alias_method :camelcase, :camelize
|
147
158
|
|
148
|
-
#
|
149
|
-
#
|
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
|
-
# "
|
156
|
-
# "
|
157
|
-
|
158
|
-
|
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
|
-
#
|
162
|
-
#
|
163
|
-
#
|
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
|
-
# "
|
167
|
-
# "
|
168
|
-
def
|
169
|
-
|
170
|
-
|
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
|
181
|
-
|
185
|
+
# "puni_puni".dasherize #=> "puni-puni"
|
186
|
+
def dasherize
|
187
|
+
gsub(/_/, '-')
|
182
188
|
end
|
183
189
|
|
184
|
-
#
|
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
|
-
# "
|
189
|
-
# "
|
190
|
-
def
|
191
|
-
|
193
|
+
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
|
194
|
+
# "Inflections".demodulize #=> "Inflections"
|
195
|
+
def demodulize
|
196
|
+
gsub(/^.*::/, '')
|
192
197
|
end
|
193
198
|
|
194
|
-
#
|
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
|
-
# "
|
198
|
-
# "
|
199
|
-
|
200
|
-
|
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
|
-
#
|
204
|
-
#
|
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
|
-
# "
|
208
|
-
# "
|
209
|
-
|
210
|
-
|
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
|
-
#
|
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
|
-
# "
|
220
|
-
# "
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
#
|
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
|
-
# "
|
232
|
-
# "
|
233
|
-
# "
|
234
|
-
|
235
|
-
|
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
|
-
#
|
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
|
-
# "
|
244
|
-
# "
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
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
|
-
#
|
254
|
-
#
|
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
|
-
#
|
258
|
-
#
|
259
|
-
|
260
|
-
|
261
|
-
|
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'
|