activesupport 3.1.12 → 3.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- data/CHANGELOG.md +1539 -30
- data/README.rdoc +2 -2
- data/lib/active_support.rb +1 -1
- data/lib/active_support/benchmarkable.rb +13 -18
- data/lib/active_support/buffered_logger.rb +43 -55
- data/lib/active_support/cache.rb +109 -115
- data/lib/active_support/cache/file_store.rb +6 -17
- data/lib/active_support/cache/mem_cache_store.rb +10 -2
- data/lib/active_support/cache/null_store.rb +44 -0
- data/lib/active_support/cache/strategy/local_cache.rb +2 -2
- data/lib/active_support/callbacks.rb +38 -35
- data/lib/active_support/concern.rb +5 -10
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/array/access.rb +1 -1
- data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/active_support/core_ext/array/wrap.rb +1 -1
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -2
- data/lib/active_support/core_ext/date/calculations.rb +37 -14
- data/lib/active_support/core_ext/date/freeze.rb +6 -4
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/file/atomic.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +7 -25
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
- data/lib/active_support/core_ext/io.rb +15 -0
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -7
- data/lib/active_support/core_ext/module.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -2
- data/lib/active_support/core_ext/module/delegation.rb +32 -21
- data/lib/active_support/core_ext/module/qualified_const.rb +64 -0
- data/lib/active_support/core_ext/module/reachable.rb +1 -3
- data/lib/active_support/core_ext/module/synchronization.rb +2 -0
- data/lib/active_support/core_ext/object/blank.rb +14 -2
- data/lib/active_support/core_ext/object/inclusion.rb +17 -7
- data/lib/active_support/core_ext/object/to_json.rb +2 -2
- data/lib/active_support/core_ext/range/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/inflections.rb +44 -6
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +22 -25
- data/lib/active_support/core_ext/time/calculations.rb +66 -12
- data/lib/active_support/dependencies.rb +51 -52
- data/lib/active_support/file_update_checker.rb +100 -15
- data/lib/active_support/hash_with_indifferent_access.rb +5 -1
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +9 -4
- data/lib/active_support/inflections.rb +3 -3
- data/lib/active_support/inflector/inflections.rb +53 -92
- data/lib/active_support/inflector/methods.rb +173 -9
- data/lib/active_support/json/decoding.rb +3 -17
- data/lib/active_support/json/encoding.rb +11 -14
- data/lib/active_support/memoizable.rb +12 -1
- data/lib/active_support/message_encryptor.rb +52 -20
- data/lib/active_support/message_verifier.rb +15 -4
- data/lib/active_support/notifications.rb +87 -14
- data/lib/active_support/notifications/instrumenter.rb +1 -2
- data/lib/active_support/ordered_hash.rb +7 -3
- data/lib/active_support/tagged_logging.rb +63 -0
- data/lib/active_support/testing/assertions.rb +1 -1
- data/lib/active_support/testing/mochaing.rb +2 -2
- data/lib/active_support/testing/performance/ruby.rb +1 -1
- data/lib/active_support/testing/setup_and_teardown.rb +4 -12
- data/lib/active_support/time_with_zone.rb +6 -3
- data/lib/active_support/values/time_zone.rb +3 -7
- data/lib/active_support/version.rb +3 -3
- data/lib/active_support/xml_mini.rb +3 -3
- data/lib/active_support/xml_mini/jdom.rb +4 -10
- metadata +28 -21
- checksums.yaml +0 -7
- data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
- data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
- data/lib/active_support/core_ext/kernel/requires.rb +0 -28
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
- data/lib/active_support/secure_random.rb +0 -6
@@ -1,8 +1,28 @@
|
|
1
|
+
require "active_support/core_ext/array/wrap"
|
2
|
+
require "active_support/core_ext/array/extract_options"
|
3
|
+
|
1
4
|
module ActiveSupport
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
5
|
+
# \FileUpdateChecker specifies the API used by Rails to watch files
|
6
|
+
# and control reloading. The API depends on four methods:
|
7
|
+
#
|
8
|
+
# * +initialize+ which expects two parameters and one block as
|
9
|
+
# described below;
|
10
|
+
#
|
11
|
+
# * +updated?+ which returns a boolean if there were updates in
|
12
|
+
# the filesystem or not;
|
13
|
+
#
|
14
|
+
# * +execute+ which executes the given block on initialization
|
15
|
+
# and updates the counter to the latest timestamp;
|
16
|
+
#
|
17
|
+
# * +execute_if_updated+ which just executes the block if it was updated;
|
18
|
+
#
|
19
|
+
# After initialization, a call to +execute_if_updated+ must execute
|
20
|
+
# the block only if there was really a change in the filesystem.
|
21
|
+
#
|
22
|
+
# == Examples
|
23
|
+
#
|
24
|
+
# This class is used by Rails to reload the I18n framework whenever
|
25
|
+
# they are changed upon a new request.
|
6
26
|
#
|
7
27
|
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
|
8
28
|
# I18n.reload!
|
@@ -13,24 +33,89 @@ module ActiveSupport
|
|
13
33
|
# end
|
14
34
|
#
|
15
35
|
class FileUpdateChecker
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
36
|
+
# It accepts two parameters on initialization. The first is an array
|
37
|
+
# of files and the second is an optional hash of directories. The hash must
|
38
|
+
# have directories as keys and the value is an array of extensions to be
|
39
|
+
# watched under that directory.
|
40
|
+
#
|
41
|
+
# This method must also receive a block that will be called once a path changes.
|
42
|
+
#
|
43
|
+
# == Implementation details
|
44
|
+
#
|
45
|
+
# This particular implementation checks for added and updated files,
|
46
|
+
# but not removed files. Directories lookup are compiled to a glob for
|
47
|
+
# performance. Therefore, while someone can add new files to the +files+
|
48
|
+
# array after initialization (and parts of Rails do depend on this feature),
|
49
|
+
# adding new directories after initialization is not allowed.
|
50
|
+
#
|
51
|
+
# Notice that other objects that implements FileUpdateChecker API may
|
52
|
+
# not even allow new files to be added after initialization. If this
|
53
|
+
# is the case, we recommend freezing the +files+ after initialization to
|
54
|
+
# avoid changes that won't make effect.
|
55
|
+
def initialize(files, dirs={}, &block)
|
56
|
+
@files = files
|
57
|
+
@glob = compile_glob(dirs)
|
20
58
|
@block = block
|
21
|
-
@
|
59
|
+
@updated_at = nil
|
60
|
+
@last_update_at = updated_at
|
61
|
+
end
|
62
|
+
|
63
|
+
# Check if any of the entries were updated. If so, the updated_at
|
64
|
+
# value is cached until the block is executed via +execute+ or +execute_if_updated+
|
65
|
+
def updated?
|
66
|
+
current_updated_at = updated_at
|
67
|
+
if @last_update_at < current_updated_at
|
68
|
+
@updated_at = updated_at
|
69
|
+
true
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
22
73
|
end
|
23
74
|
|
24
|
-
|
25
|
-
|
75
|
+
# Executes the given block and updates the counter to latest timestamp.
|
76
|
+
def execute
|
77
|
+
@last_update_at = updated_at
|
78
|
+
@block.call
|
79
|
+
ensure
|
80
|
+
@updated_at = nil
|
26
81
|
end
|
27
82
|
|
83
|
+
# Execute the block given if updated.
|
28
84
|
def execute_if_updated
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
85
|
+
if updated?
|
86
|
+
execute
|
87
|
+
true
|
88
|
+
else
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def updated_at #:nodoc:
|
96
|
+
@updated_at || begin
|
97
|
+
all = []
|
98
|
+
all.concat @files.select { |f| File.exists?(f) }
|
99
|
+
all.concat Dir[@glob] if @glob
|
100
|
+
all.map { |path| File.mtime(path) }.max || Time.at(0)
|
33
101
|
end
|
34
102
|
end
|
103
|
+
|
104
|
+
def compile_glob(hash) #:nodoc:
|
105
|
+
hash.freeze # Freeze so changes aren't accidently pushed
|
106
|
+
return if hash.empty?
|
107
|
+
|
108
|
+
globs = []
|
109
|
+
hash.each do |key, value|
|
110
|
+
globs << "#{key}/**/*#{compile_ext(value)}"
|
111
|
+
end
|
112
|
+
"{#{globs.join(",")}}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def compile_ext(array) #:nodoc:
|
116
|
+
array = Array.wrap(array)
|
117
|
+
return if array.empty?
|
118
|
+
".{#{array.join(",")}}"
|
119
|
+
end
|
35
120
|
end
|
36
121
|
end
|
@@ -16,6 +16,10 @@ module ActiveSupport
|
|
16
16
|
dup
|
17
17
|
end
|
18
18
|
|
19
|
+
def nested_under_indifferent_access
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
19
23
|
def initialize(constructor = {})
|
20
24
|
if constructor.is_a?(Hash)
|
21
25
|
super()
|
@@ -112,7 +116,7 @@ module ActiveSupport
|
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
115
|
-
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
|
119
|
+
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
|
116
120
|
# Does not overwrite the existing hash.
|
117
121
|
def merge(hash)
|
118
122
|
self.dup.update(hash)
|
data/lib/active_support/i18n.rb
CHANGED
@@ -2,7 +2,7 @@ begin
|
|
2
2
|
require 'i18n'
|
3
3
|
require 'active_support/lazy_load_hooks'
|
4
4
|
rescue LoadError => e
|
5
|
-
$stderr.puts "
|
5
|
+
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
|
6
6
|
raise e
|
7
7
|
end
|
8
8
|
|
@@ -10,14 +10,19 @@ module I18n
|
|
10
10
|
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
|
11
11
|
|
12
12
|
def self.reloader
|
13
|
-
@reloader ||= ActiveSupport::FileUpdateChecker.new(
|
13
|
+
@reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.reloader_paths
|
17
|
+
@reloader_paths ||= []
|
14
18
|
end
|
15
19
|
|
16
20
|
# Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
|
17
21
|
# point, no path was added to the reloader, I18n.reload! is not triggered
|
18
22
|
# on to_prepare callbacks. This will only happen on the config.after_initialize
|
19
23
|
# callback below.
|
20
|
-
initializer "i18n.callbacks" do
|
24
|
+
initializer "i18n.callbacks" do |app|
|
25
|
+
app.reloaders << I18n::Railtie.reloader
|
21
26
|
ActionDispatch::Reloader.to_prepare do
|
22
27
|
I18n::Railtie.reloader.execute_if_updated
|
23
28
|
end
|
@@ -58,8 +63,8 @@ module I18n
|
|
58
63
|
|
59
64
|
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
|
60
65
|
|
61
|
-
|
62
|
-
reloader.
|
66
|
+
reloader_paths.concat I18n.load_path
|
67
|
+
reloader.execute
|
63
68
|
|
64
69
|
@i18n_inited = true
|
65
70
|
end
|
@@ -16,8 +16,8 @@ module ActiveSupport
|
|
16
16
|
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
17
17
|
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
|
18
18
|
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
19
|
-
inflect.plural(/(
|
20
|
-
inflect.plural(/(
|
19
|
+
inflect.plural(/(m|l)ouse$/i, '\1ice')
|
20
|
+
inflect.plural(/(m|l)ice$/i, '\1ice')
|
21
21
|
inflect.plural(/^(ox)$/i, '\1en')
|
22
22
|
inflect.plural(/^(oxen)$/i, '\1')
|
23
23
|
inflect.plural(/(quiz)$/i, '\1zes')
|
@@ -35,7 +35,7 @@ module ActiveSupport
|
|
35
35
|
inflect.singular(/(s)eries$/i, '\1eries')
|
36
36
|
inflect.singular(/(m)ovies$/i, '\1ovie')
|
37
37
|
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
|
38
|
-
inflect.singular(/(
|
38
|
+
inflect.singular(/(m|l)ice$/i, '\1ouse')
|
39
39
|
inflect.singular(/(bus)es$/i, '\1')
|
40
40
|
inflect.singular(/(o)es$/i, '\1')
|
41
41
|
inflect.singular(/(shoe)s$/i, '\1')
|
@@ -20,10 +20,61 @@ module ActiveSupport
|
|
20
20
|
@__instance__ ||= new
|
21
21
|
end
|
22
22
|
|
23
|
-
attr_reader :plurals, :singulars, :uncountables, :humans
|
23
|
+
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
|
24
24
|
|
25
25
|
def initialize
|
26
|
-
@plurals, @singulars, @uncountables, @humans = [], [], [], []
|
26
|
+
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specifies a new acronym. An acronym must be specified as it will appear in a camelized string. An underscore
|
30
|
+
# string that contains the acronym will retain the acronym when passed to `camelize`, `humanize`, or `titleize`.
|
31
|
+
# A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will
|
32
|
+
# convert the acronym into a non-delimited single lowercase word when passed to +underscore+.
|
33
|
+
#
|
34
|
+
# Examples:
|
35
|
+
# acronym 'HTML'
|
36
|
+
# titleize 'html' #=> 'HTML'
|
37
|
+
# camelize 'html' #=> 'HTML'
|
38
|
+
# underscore 'MyHTML' #=> 'my_html'
|
39
|
+
#
|
40
|
+
# The acronym, however, must occur as a delimited unit and not be part of another word for conversions to recognize it:
|
41
|
+
#
|
42
|
+
# acronym 'HTTP'
|
43
|
+
# camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
|
44
|
+
# camelize 'https' #=> 'Https', not 'HTTPs'
|
45
|
+
# underscore 'HTTPS' #=> 'http_s', not 'https'
|
46
|
+
#
|
47
|
+
# acronym 'HTTPS'
|
48
|
+
# camelize 'https' #=> 'HTTPS'
|
49
|
+
# underscore 'HTTPS' #=> 'https'
|
50
|
+
#
|
51
|
+
# Note: Acronyms that are passed to `pluralize` will no longer be recognized, since the acronym will not occur as
|
52
|
+
# a delimited unit in the pluralized result. To work around this, you must specify the pluralized form as an
|
53
|
+
# acronym as well:
|
54
|
+
#
|
55
|
+
# acronym 'API'
|
56
|
+
# camelize(pluralize('api')) #=> 'Apis'
|
57
|
+
#
|
58
|
+
# acronym 'APIs'
|
59
|
+
# camelize(pluralize('api')) #=> 'APIs'
|
60
|
+
#
|
61
|
+
# `acronym` may be used to specify any word that contains an acronym or otherwise needs to maintain a non-standard
|
62
|
+
# capitalization. The only restriction is that the word must begin with a capital letter.
|
63
|
+
#
|
64
|
+
# Examples:
|
65
|
+
# acronym 'RESTful'
|
66
|
+
# underscore 'RESTful' #=> 'restful'
|
67
|
+
# underscore 'RESTfulController' #=> 'restful_controller'
|
68
|
+
# titleize 'RESTfulController' #=> 'RESTful Controller'
|
69
|
+
# camelize 'restful' #=> 'RESTful'
|
70
|
+
# camelize 'restful_controller' #=> 'RESTfulController'
|
71
|
+
#
|
72
|
+
# acronym 'McDonald'
|
73
|
+
# underscore 'McDonald' #=> 'mcdonald'
|
74
|
+
# camelize 'mcdonald' #=> 'McDonald'
|
75
|
+
def acronym(word)
|
76
|
+
@acronyms[word.downcase] = word
|
77
|
+
@acronym_regex = /#{@acronyms.values.join("|")}/
|
27
78
|
end
|
28
79
|
|
29
80
|
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
|
@@ -117,95 +168,5 @@ module ActiveSupport
|
|
117
168
|
Inflections.instance
|
118
169
|
end
|
119
170
|
end
|
120
|
-
|
121
|
-
# Returns the plural form of the word in the string.
|
122
|
-
#
|
123
|
-
# Examples:
|
124
|
-
# "post".pluralize # => "posts"
|
125
|
-
# "octopus".pluralize # => "octopi"
|
126
|
-
# "sheep".pluralize # => "sheep"
|
127
|
-
# "words".pluralize # => "words"
|
128
|
-
# "CamelOctopus".pluralize # => "CamelOctopi"
|
129
|
-
def pluralize(word)
|
130
|
-
result = word.to_s.dup
|
131
|
-
|
132
|
-
if word.empty? || inflections.uncountables.include?(result.downcase)
|
133
|
-
result
|
134
|
-
else
|
135
|
-
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
136
|
-
result
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
141
|
-
#
|
142
|
-
# Examples:
|
143
|
-
# "posts".singularize # => "post"
|
144
|
-
# "octopi".singularize # => "octopus"
|
145
|
-
# "sheep".singularize # => "sheep"
|
146
|
-
# "word".singularize # => "word"
|
147
|
-
# "CamelOctopi".singularize # => "CamelOctopus"
|
148
|
-
def singularize(word)
|
149
|
-
result = word.to_s.dup
|
150
|
-
|
151
|
-
if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
|
152
|
-
result
|
153
|
-
else
|
154
|
-
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
155
|
-
result
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Capitalizes the first word and turns underscores into spaces and strips a
|
160
|
-
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
|
161
|
-
#
|
162
|
-
# Examples:
|
163
|
-
# "employee_salary" # => "Employee salary"
|
164
|
-
# "author_id" # => "Author"
|
165
|
-
def humanize(lower_case_and_underscored_word)
|
166
|
-
result = lower_case_and_underscored_word.to_s.dup
|
167
|
-
|
168
|
-
inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
169
|
-
result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
170
|
-
end
|
171
|
-
|
172
|
-
# Capitalizes all the words and replaces some characters in the string to create
|
173
|
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
174
|
-
# used in the Rails internals.
|
175
|
-
#
|
176
|
-
# +titleize+ is also aliased as as +titlecase+.
|
177
|
-
#
|
178
|
-
# Examples:
|
179
|
-
# "man from the boondocks".titleize # => "Man From The Boondocks"
|
180
|
-
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
|
181
|
-
def titleize(word)
|
182
|
-
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
|
183
|
-
end
|
184
|
-
|
185
|
-
# Create the name of a table like Rails does for models to table names. This method
|
186
|
-
# uses the +pluralize+ method on the last word in the string.
|
187
|
-
#
|
188
|
-
# Examples
|
189
|
-
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
|
190
|
-
# "egg_and_ham".tableize # => "egg_and_hams"
|
191
|
-
# "fancyCategory".tableize # => "fancy_categories"
|
192
|
-
def tableize(class_name)
|
193
|
-
pluralize(underscore(class_name))
|
194
|
-
end
|
195
|
-
|
196
|
-
# Create a class name from a plural table name like Rails does for table names to models.
|
197
|
-
# Note that this returns a string and not a Class. (To convert to an actual class
|
198
|
-
# follow +classify+ with +constantize+.)
|
199
|
-
#
|
200
|
-
# Examples:
|
201
|
-
# "egg_and_hams".classify # => "EggAndHam"
|
202
|
-
# "posts".classify # => "Post"
|
203
|
-
#
|
204
|
-
# Singular names are not handled correctly:
|
205
|
-
# "business".classify # => "Busines"
|
206
|
-
def classify(table_name)
|
207
|
-
# strip out any leading schema name
|
208
|
-
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
|
209
|
-
end
|
210
171
|
end
|
211
172
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/inflector/inflections'
|
2
|
+
|
1
3
|
module ActiveSupport
|
2
4
|
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
|
3
5
|
# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
|
@@ -10,6 +12,30 @@ module ActiveSupport
|
|
10
12
|
module Inflector
|
11
13
|
extend self
|
12
14
|
|
15
|
+
# Returns the plural form of the word in the string.
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
# "post".pluralize # => "posts"
|
19
|
+
# "octopus".pluralize # => "octopi"
|
20
|
+
# "sheep".pluralize # => "sheep"
|
21
|
+
# "words".pluralize # => "words"
|
22
|
+
# "CamelOctopus".pluralize # => "CamelOctopi"
|
23
|
+
def pluralize(word)
|
24
|
+
apply_inflections(word, inflections.plurals)
|
25
|
+
end
|
26
|
+
|
27
|
+
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
28
|
+
#
|
29
|
+
# Examples:
|
30
|
+
# "posts".singularize # => "post"
|
31
|
+
# "octopi".singularize # => "octopus"
|
32
|
+
# "sheep".singularize # => "sheep"
|
33
|
+
# "word".singularize # => "word"
|
34
|
+
# "CamelOctopi".singularize # => "CamelOctopus"
|
35
|
+
def singularize(word)
|
36
|
+
apply_inflections(word, inflections.singulars)
|
37
|
+
end
|
38
|
+
|
13
39
|
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
|
14
40
|
# is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
|
15
41
|
#
|
@@ -25,12 +51,14 @@ module ActiveSupport
|
|
25
51
|
# though there are cases where that does not hold:
|
26
52
|
#
|
27
53
|
# "SSLError".underscore.camelize # => "SslError"
|
28
|
-
def camelize(
|
29
|
-
|
30
|
-
|
54
|
+
def camelize(term, uppercase_first_letter = true)
|
55
|
+
string = term.to_s
|
56
|
+
if uppercase_first_letter
|
57
|
+
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
|
31
58
|
else
|
32
|
-
|
59
|
+
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
33
60
|
end
|
61
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
|
34
62
|
end
|
35
63
|
|
36
64
|
# Makes an underscored, lowercase form from the expression in the string.
|
@@ -48,13 +76,68 @@ module ActiveSupport
|
|
48
76
|
def underscore(camel_cased_word)
|
49
77
|
word = camel_cased_word.to_s.dup
|
50
78
|
word.gsub!(/::/, '/')
|
51
|
-
word.gsub!(/([A-
|
79
|
+
word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
|
80
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
52
81
|
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
53
82
|
word.tr!("-", "_")
|
54
83
|
word.downcase!
|
55
84
|
word
|
56
85
|
end
|
57
86
|
|
87
|
+
# Capitalizes the first word and turns underscores into spaces and strips a
|
88
|
+
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
|
89
|
+
#
|
90
|
+
# Examples:
|
91
|
+
# "employee_salary" # => "Employee salary"
|
92
|
+
# "author_id" # => "Author"
|
93
|
+
def humanize(lower_case_and_underscored_word)
|
94
|
+
result = lower_case_and_underscored_word.to_s.dup
|
95
|
+
inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
96
|
+
result.gsub!(/_id$/, "")
|
97
|
+
result.gsub(/(_)?([a-z\d]*)/i) { "#{$1 && ' '}#{inflections.acronyms[$2] || $2.downcase}" }.gsub(/^\w/) { $&.upcase }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Capitalizes all the words and replaces some characters in the string to create
|
101
|
+
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
102
|
+
# used in the Rails internals.
|
103
|
+
#
|
104
|
+
# +titleize+ is also aliased as as +titlecase+.
|
105
|
+
#
|
106
|
+
# Examples:
|
107
|
+
# "man from the boondocks".titleize # => "Man From The Boondocks"
|
108
|
+
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
|
109
|
+
# "TheManWithoutAPast".titleize # => "The Man Without A Past"
|
110
|
+
# "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
|
111
|
+
def titleize(word)
|
112
|
+
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Create the name of a table like Rails does for models to table names. This method
|
116
|
+
# uses the +pluralize+ method on the last word in the string.
|
117
|
+
#
|
118
|
+
# Examples
|
119
|
+
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
|
120
|
+
# "egg_and_ham".tableize # => "egg_and_hams"
|
121
|
+
# "fancyCategory".tableize # => "fancy_categories"
|
122
|
+
def tableize(class_name)
|
123
|
+
pluralize(underscore(class_name))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create a class name from a plural table name like Rails does for table names to models.
|
127
|
+
# Note that this returns a string and not a Class. (To convert to an actual class
|
128
|
+
# follow +classify+ with +constantize+.)
|
129
|
+
#
|
130
|
+
# Examples:
|
131
|
+
# "egg_and_hams".classify # => "EggAndHam"
|
132
|
+
# "posts".classify # => "Post"
|
133
|
+
#
|
134
|
+
# Singular names are not handled correctly:
|
135
|
+
# "business".classify # => "Busines"
|
136
|
+
def classify(table_name)
|
137
|
+
# strip out any leading schema name
|
138
|
+
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
|
139
|
+
end
|
140
|
+
|
58
141
|
# Replaces underscores with dashes in the string.
|
59
142
|
#
|
60
143
|
# Example:
|
@@ -63,13 +146,32 @@ module ActiveSupport
|
|
63
146
|
underscored_word.gsub(/_/, '-')
|
64
147
|
end
|
65
148
|
|
66
|
-
# Removes the module part from the expression in the string
|
149
|
+
# Removes the module part from the expression in the string:
|
67
150
|
#
|
68
|
-
# Examples:
|
69
151
|
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
|
70
152
|
# "Inflections".demodulize # => "Inflections"
|
71
|
-
|
72
|
-
|
153
|
+
#
|
154
|
+
# See also +deconstantize+.
|
155
|
+
def demodulize(path)
|
156
|
+
path = path.to_s
|
157
|
+
if i = path.rindex('::')
|
158
|
+
path[(i+2)..-1]
|
159
|
+
else
|
160
|
+
path
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Removes the rightmost segment from the constant expression in the string:
|
165
|
+
#
|
166
|
+
# "Net::HTTP".deconstantize # => "Net"
|
167
|
+
# "::Net::HTTP".deconstantize # => "::Net"
|
168
|
+
# "String".deconstantize # => ""
|
169
|
+
# "::String".deconstantize # => ""
|
170
|
+
# "".deconstantize # => ""
|
171
|
+
#
|
172
|
+
# See also +demodulize+.
|
173
|
+
def deconstantize(path)
|
174
|
+
path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
|
73
175
|
end
|
74
176
|
|
75
177
|
# Creates a foreign key name from a class name.
|
@@ -127,6 +229,39 @@ module ActiveSupport
|
|
127
229
|
end
|
128
230
|
end
|
129
231
|
|
232
|
+
# Tries to find a constant with the name specified in the argument string:
|
233
|
+
#
|
234
|
+
# "Module".safe_constantize # => Module
|
235
|
+
# "Test::Unit".safe_constantize # => Test::Unit
|
236
|
+
#
|
237
|
+
# The name is assumed to be the one of a top-level constant, no matter whether
|
238
|
+
# it starts with "::" or not. No lexical context is taken into account:
|
239
|
+
#
|
240
|
+
# C = 'outside'
|
241
|
+
# module M
|
242
|
+
# C = 'inside'
|
243
|
+
# C # => 'inside'
|
244
|
+
# "C".safe_constantize # => 'outside', same as ::C
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# nil is returned when the name is not in CamelCase or the constant (or part of it) is
|
248
|
+
# unknown.
|
249
|
+
#
|
250
|
+
# "blargle".safe_constantize # => nil
|
251
|
+
# "UnknownModule".safe_constantize # => nil
|
252
|
+
# "UnknownModule::Foo::Bar".safe_constantize # => nil
|
253
|
+
#
|
254
|
+
def safe_constantize(camel_cased_word)
|
255
|
+
begin
|
256
|
+
constantize(camel_cased_word)
|
257
|
+
rescue NameError => e
|
258
|
+
raise unless e.message =~ /uninitialized constant #{const_regexp(camel_cased_word)}$/ ||
|
259
|
+
e.name.to_s == camel_cased_word.to_s
|
260
|
+
rescue ArgumentError => e
|
261
|
+
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
130
265
|
# Turns a number into an ordinal string used to denote the position in an
|
131
266
|
# ordered sequence such as 1st, 2nd, 3rd, 4th.
|
132
267
|
#
|
@@ -149,5 +284,34 @@ module ActiveSupport
|
|
149
284
|
end
|
150
285
|
end
|
151
286
|
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
# Mount a regular expression that will match part by part of the constant.
|
291
|
+
# For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
|
292
|
+
def const_regexp(camel_cased_word) #:nodoc:
|
293
|
+
parts = camel_cased_word.split("::")
|
294
|
+
last = parts.pop
|
295
|
+
|
296
|
+
parts.reverse.inject(last) do |acc, part|
|
297
|
+
part.empty? ? acc : "#{part}(::#{acc})?"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
# Applies inflection rules for +singularize+ and +pluralize+.
|
302
|
+
#
|
303
|
+
# Examples:
|
304
|
+
# apply_inflections("post", inflections.plurals) # => "posts"
|
305
|
+
# apply_inflections("posts", inflections.singulars) # => "post"
|
306
|
+
def apply_inflections(word, rules)
|
307
|
+
result = word.to_s.dup
|
308
|
+
|
309
|
+
if word.empty? || inflections.uncountables.any? { |inflection| result =~ /\b#{inflection}\Z/i }
|
310
|
+
result
|
311
|
+
else
|
312
|
+
rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
313
|
+
result
|
314
|
+
end
|
315
|
+
end
|
152
316
|
end
|
153
317
|
end
|