activesupport 1.3.1 → 1.4.0

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.

Files changed (55) hide show
  1. data/CHANGELOG +232 -2
  2. data/README +43 -0
  3. data/lib/active_support.rb +4 -1
  4. data/lib/active_support/breakpoint.rb +5 -0
  5. data/lib/active_support/core_ext/array.rb +2 -16
  6. data/lib/active_support/core_ext/array/conversions.rb +30 -4
  7. data/lib/active_support/core_ext/array/grouping.rb +55 -0
  8. data/lib/active_support/core_ext/bigdecimal.rb +3 -0
  9. data/lib/active_support/core_ext/bigdecimal/formatting.rb +7 -0
  10. data/lib/active_support/core_ext/class/inheritable_attributes.rb +6 -1
  11. data/lib/active_support/core_ext/date/conversions.rb +13 -7
  12. data/lib/active_support/core_ext/enumerable.rb +41 -10
  13. data/lib/active_support/core_ext/exception.rb +2 -2
  14. data/lib/active_support/core_ext/hash/conversions.rb +123 -12
  15. data/lib/active_support/core_ext/hash/indifferent_access.rb +18 -9
  16. data/lib/active_support/core_ext/integer/inflections.rb +10 -4
  17. data/lib/active_support/core_ext/load_error.rb +3 -3
  18. data/lib/active_support/core_ext/module.rb +2 -0
  19. data/lib/active_support/core_ext/module/aliasing.rb +58 -0
  20. data/lib/active_support/core_ext/module/attr_internal.rb +31 -0
  21. data/lib/active_support/core_ext/module/delegation.rb +27 -2
  22. data/lib/active_support/core_ext/name_error.rb +20 -0
  23. data/lib/active_support/core_ext/string.rb +2 -0
  24. data/lib/active_support/core_ext/string/access.rb +5 -5
  25. data/lib/active_support/core_ext/string/inflections.rb +93 -4
  26. data/lib/active_support/core_ext/string/unicode.rb +42 -0
  27. data/lib/active_support/core_ext/symbol.rb +1 -1
  28. data/lib/active_support/core_ext/time/calculations.rb +7 -5
  29. data/lib/active_support/core_ext/time/conversions.rb +1 -2
  30. data/lib/active_support/dependencies.rb +417 -50
  31. data/lib/active_support/deprecation.rb +201 -0
  32. data/lib/active_support/inflections.rb +1 -2
  33. data/lib/active_support/inflector.rb +117 -19
  34. data/lib/active_support/json.rb +14 -3
  35. data/lib/active_support/json/encoders/core.rb +21 -18
  36. data/lib/active_support/multibyte.rb +7 -0
  37. data/lib/active_support/multibyte/chars.rb +129 -0
  38. data/lib/active_support/multibyte/generators/generate_tables.rb +149 -0
  39. data/lib/active_support/multibyte/handlers/passthru_handler.rb +9 -0
  40. data/lib/active_support/multibyte/handlers/utf8_handler.rb +453 -0
  41. data/lib/active_support/multibyte/handlers/utf8_handler_proc.rb +44 -0
  42. data/lib/active_support/option_merger.rb +3 -3
  43. data/lib/active_support/ordered_options.rb +24 -23
  44. data/lib/active_support/reloadable.rb +39 -5
  45. data/lib/active_support/values/time_zone.rb +1 -1
  46. data/lib/active_support/values/unicode_tables.dat +0 -0
  47. data/lib/active_support/vendor/builder/blankslate.rb +16 -6
  48. data/lib/active_support/vendor/builder/xchar.rb +112 -0
  49. data/lib/active_support/vendor/builder/xmlbase.rb +12 -10
  50. data/lib/active_support/vendor/builder/xmlmarkup.rb +26 -7
  51. data/lib/active_support/vendor/xml_simple.rb +1021 -0
  52. data/lib/active_support/version.rb +2 -2
  53. data/lib/active_support/whiny_nil.rb +1 -1
  54. metadata +26 -4
  55. data/lib/active_support/core_ext/hash/conversions.rb.rej +0 -28
@@ -0,0 +1,201 @@
1
+ require 'yaml'
2
+
3
+ module ActiveSupport
4
+ module Deprecation
5
+ mattr_accessor :debug
6
+ self.debug = false
7
+
8
+ # Choose the default warn behavior according to RAILS_ENV.
9
+ # Ignore deprecation warnings in production.
10
+ DEFAULT_BEHAVIORS = {
11
+ 'test' => Proc.new { |message, callstack|
12
+ $stderr.puts(message)
13
+ $stderr.puts callstack.join("\n ") if debug
14
+ },
15
+ 'development' => Proc.new { |message, callstack|
16
+ RAILS_DEFAULT_LOGGER.warn message
17
+ RAILS_DEFAULT_LOGGER.debug callstack.join("\n ") if debug
18
+ }
19
+ }
20
+
21
+ class << self
22
+ def warn(message = nil, callstack = caller)
23
+ behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced?
24
+ end
25
+
26
+ def default_behavior
27
+ if defined?(RAILS_ENV)
28
+ DEFAULT_BEHAVIORS[RAILS_ENV.to_s]
29
+ else
30
+ DEFAULT_BEHAVIORS['test']
31
+ end
32
+ end
33
+
34
+ # Have deprecations been silenced?
35
+ def silenced?
36
+ @silenced = false unless defined?(@silenced)
37
+ @silenced
38
+ end
39
+
40
+ # Silence deprecation warnings within the block.
41
+ def silence
42
+ old_silenced, @silenced = @silenced, true
43
+ yield
44
+ ensure
45
+ @silenced = old_silenced
46
+ end
47
+
48
+ attr_writer :silenced
49
+
50
+
51
+ private
52
+ def deprecation_message(callstack, message = nil)
53
+ message ||= "You are using deprecated behavior which will be removed from Rails 2.0."
54
+ "DEPRECATION WARNING: #{message} See http://www.rubyonrails.org/deprecation for details. #{deprecation_caller_message(callstack)}"
55
+ end
56
+
57
+ def deprecation_caller_message(callstack)
58
+ file, line, method = extract_callstack(callstack)
59
+ if file
60
+ if line && method
61
+ "(called from #{method} at #{file}:#{line})"
62
+ else
63
+ "(called from #{file}:#{line})"
64
+ end
65
+ end
66
+ end
67
+
68
+ def extract_callstack(callstack)
69
+ if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
70
+ md.captures
71
+ else
72
+ callstack.first
73
+ end
74
+ end
75
+ end
76
+
77
+ # Behavior is a block that takes a message argument.
78
+ mattr_accessor :behavior
79
+ self.behavior = default_behavior
80
+
81
+ # Warnings are not silenced by default.
82
+ self.silenced = false
83
+
84
+ module ClassMethods
85
+ # Declare that a method has been deprecated.
86
+ def deprecate(*method_names)
87
+ options = method_names.last.is_a?(Hash) ? method_names.pop : {}
88
+ method_names = method_names + options.keys
89
+ method_names.each do |method_name|
90
+ alias_method_chain(method_name, :deprecation) do |target, punctuation|
91
+ class_eval(<<-EOS, __FILE__, __LINE__)
92
+ def #{target}_with_deprecation#{punctuation}(*args, &block)
93
+ ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
94
+ #{target}_without_deprecation#{punctuation}(*args, &block)
95
+ end
96
+ EOS
97
+ end
98
+ end
99
+ end
100
+
101
+ def deprecated_method_warning(method_name, message=nil)
102
+ warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}"
103
+ case message
104
+ when Symbol then "#{warning} (use #{message} instead)"
105
+ when String then "#{warning} (#{message})"
106
+ else warning
107
+ end
108
+ end
109
+
110
+ def deprecation_horizon
111
+ '2.0'
112
+ end
113
+ end
114
+
115
+ module Assertions
116
+ def assert_deprecated(match = nil, &block)
117
+ result, warnings = collect_deprecations(&block)
118
+ assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
119
+ if match
120
+ match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
121
+ assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
122
+ end
123
+ result
124
+ end
125
+
126
+ def assert_not_deprecated(&block)
127
+ result, deprecations = collect_deprecations(&block)
128
+ assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
129
+ result
130
+ end
131
+
132
+ private
133
+ def collect_deprecations
134
+ old_behavior = ActiveSupport::Deprecation.behavior
135
+ deprecations = []
136
+ ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
137
+ deprecations << message
138
+ end
139
+ result = yield
140
+ [result, deprecations]
141
+ ensure
142
+ ActiveSupport::Deprecation.behavior = old_behavior
143
+ end
144
+ end
145
+
146
+ # Stand-in for @request, @attributes, @params, etc which emits deprecation
147
+ # warnings on any method call (except #inspect).
148
+ class DeprecatedInstanceVariableProxy
149
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
150
+
151
+ def initialize(instance, method, var = "@#{method}")
152
+ @instance, @method, @var = instance, method, var
153
+ end
154
+
155
+ # Don't give a deprecation warning on inspect since test/unit and error
156
+ # logs rely on it for diagnostics.
157
+ def inspect
158
+ target.inspect
159
+ end
160
+
161
+ private
162
+ def method_missing(called, *args, &block)
163
+ warn caller, called, args
164
+ target.__send__(called, *args, &block)
165
+ end
166
+
167
+ def target
168
+ @instance.__send__(@method)
169
+ end
170
+
171
+ def warn(callstack, called, args)
172
+ ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ class Module
179
+ include ActiveSupport::Deprecation::ClassMethods
180
+ end
181
+
182
+ require 'test/unit/error'
183
+
184
+ module Test
185
+ module Unit
186
+ class TestCase
187
+ include ActiveSupport::Deprecation::Assertions
188
+ end
189
+
190
+ class Error # :nodoc:
191
+ # Silence warnings when reporting test errors.
192
+ def message_with_silenced_deprecation
193
+ ActiveSupport::Deprecation.silence do
194
+ message_without_silenced_deprecation
195
+ end
196
+ end
197
+
198
+ alias_method_chain :message, :silenced_deprecation
199
+ end
200
+ end
201
+ end
@@ -11,7 +11,6 @@ Inflector.inflections do |inflect|
11
11
  inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
12
12
  inflect.plural(/(hive)$/i, '\1s')
13
13
  inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
14
- inflect.plural(/([^aeiouy]|qu)ies$/i, '\1y')
15
14
  inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
16
15
  inflect.plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
17
16
  inflect.plural(/([m|l])ouse$/i, '\1ice')
@@ -36,7 +35,7 @@ Inflector.inflections do |inflect|
36
35
  inflect.singular(/(o)es$/i, '\1')
37
36
  inflect.singular(/(shoe)s$/i, '\1')
38
37
  inflect.singular(/(cris|ax|test)es$/i, '\1is')
39
- inflect.singular(/([octop|vir])i$/i, '\1us')
38
+ inflect.singular(/(octop|vir)i$/i, '\1us')
40
39
  inflect.singular(/(alias|status)es$/i, '\1')
41
40
  inflect.singular(/^(ox)en/i, '\1')
42
41
  inflect.singular(/(vert|ind)ices$/i, '\1ex')
@@ -1,9 +1,9 @@
1
- require 'singleton'
1
+ require 'singleton'
2
2
 
3
3
  # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
4
4
  # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
5
5
  # in inflections.rb.
6
- module Inflector
6
+ module Inflector
7
7
  # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
8
8
  # inflection rules. Examples:
9
9
  #
@@ -21,20 +21,20 @@ module Inflector
21
21
  # already have been loaded.
22
22
  class Inflections
23
23
  include Singleton
24
-
24
+
25
25
  attr_reader :plurals, :singulars, :uncountables
26
-
26
+
27
27
  def initialize
28
28
  @plurals, @singulars, @uncountables = [], [], []
29
29
  end
30
-
31
- # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
30
+
31
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
32
32
  # The replacement should always be a string that may include references to the matched data from the rule.
33
33
  def plural(rule, replacement)
34
34
  @plurals.insert(0, [rule, replacement])
35
35
  end
36
-
37
- # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
36
+
37
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
38
38
  # The replacement should always be a string that may include references to the matched data from the rule.
39
39
  def singular(rule, replacement)
40
40
  @singulars.insert(0, [rule, replacement])
@@ -42,7 +42,7 @@ module Inflector
42
42
 
43
43
  # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
44
44
  # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
45
- #
45
+ #
46
46
  # Examples:
47
47
  # irregular 'octopus', 'octopi'
48
48
  # irregular 'person', 'people'
@@ -50,9 +50,9 @@ module Inflector
50
50
  plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
51
51
  singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
52
52
  end
53
-
53
+
54
54
  # Add uncountable words that shouldn't be attempted inflected.
55
- #
55
+ #
56
56
  # Examples:
57
57
  # uncountable "money"
58
58
  # uncountable "money", "information"
@@ -60,7 +60,7 @@ module Inflector
60
60
  def uncountable(*words)
61
61
  (@uncountables << words).flatten!
62
62
  end
63
-
63
+
64
64
  # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
65
65
  # the options are: :plurals, :singulars, :uncountables
66
66
  #
@@ -87,6 +87,15 @@ module Inflector
87
87
  end
88
88
  end
89
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"
90
99
  def pluralize(word)
91
100
  result = word.to_s.dup
92
101
 
@@ -98,6 +107,15 @@ module Inflector
98
107
  end
99
108
  end
100
109
 
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"
101
119
  def singularize(word)
102
120
  result = word.to_s.dup
103
121
 
@@ -109,6 +127,16 @@ module Inflector
109
127
  end
110
128
  end
111
129
 
130
+ # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
131
+ # is set to ":lower" then camelize produces lowerCamelCase.
132
+ #
133
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
134
+ #
135
+ # Examples
136
+ # "active_record".camelize #=> "ActiveRecord"
137
+ # "active_record".camelize(:lower) #=> "activeRecord"
138
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
139
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
112
140
  def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
113
141
  if first_letter_in_uppercase
114
142
  lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
@@ -117,10 +145,26 @@ module Inflector
117
145
  end
118
146
  end
119
147
 
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
153
+ #
154
+ # Examples
155
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
156
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
120
157
  def titleize(word)
121
158
  humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
122
159
  end
123
-
160
+
161
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
162
+ #
163
+ # Changes '::' to '/' to convert namespaces to paths.
164
+ #
165
+ # Examples
166
+ # "ActiveRecord".underscore #=> "active_record"
167
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
124
168
  def underscore(camel_cased_word)
125
169
  camel_cased_word.to_s.gsub(/::/, '/').
126
170
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
@@ -128,39 +172,93 @@ module Inflector
128
172
  tr("-", "_").
129
173
  downcase
130
174
  end
131
-
175
+
176
+ # Replaces underscores with dashes in the string.
177
+ #
178
+ # Example
179
+ # "puni_puni" #=> "puni-puni"
132
180
  def dasherize(underscored_word)
133
181
  underscored_word.gsub(/_/, '-')
134
182
  end
135
183
 
184
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
185
+ # Like titleize, this is meant for creating pretty output.
186
+ #
187
+ # Examples
188
+ # "employee_salary" #=> "Employee salary"
189
+ # "author_id" #=> "Author"
136
190
  def humanize(lower_case_and_underscored_word)
137
191
  lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
138
192
  end
139
193
 
194
+ # Removes the module part from the expression in the string
195
+ #
196
+ # Examples
197
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
198
+ # "Inflections".demodulize #=> "Inflections"
140
199
  def demodulize(class_name_in_module)
141
200
  class_name_in_module.to_s.gsub(/^.*::/, '')
142
201
  end
143
202
 
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.
205
+ #
206
+ # Examples
207
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
208
+ # "egg_and_ham".tableize #=> "egg_and_hams"
209
+ # "fancyCategory".tableize #=> "fancy_categories"
144
210
  def tableize(class_name)
145
211
  pluralize(underscore(class_name))
146
212
  end
147
-
213
+
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.)
217
+ #
218
+ # Examples
219
+ # "egg_and_hams".classify #=> "EggAndHam"
220
+ # "post".classify #=> "Post"
148
221
  def classify(table_name)
149
- camelize(singularize(table_name))
222
+ # strip out any leading schema name
223
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
150
224
  end
151
225
 
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'.
229
+ #
230
+ # Examples
231
+ # "Message".foreign_key #=> "message_id"
232
+ # "Message".foreign_key(false) #=> "messageid"
233
+ # "Admin::Post".foreign_key #=> "post_id"
152
234
  def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
153
235
  underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
154
236
  end
155
237
 
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.
241
+ #
242
+ # Examples
243
+ # "Module".constantize #=> Module
244
+ # "Class".constantize #=> Class
156
245
  def constantize(camel_cased_word)
157
- raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" unless
158
- /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ camel_cased_word
159
-
246
+ unless /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ camel_cased_word
247
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
248
+ end
249
+
160
250
  camel_cased_word = "::#{camel_cased_word}" unless $1
161
251
  Object.module_eval(camel_cased_word, __FILE__, __LINE__)
162
252
  end
163
253
 
254
+ # Ordinalize turns a number into an ordinal string used to denote the
255
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
256
+ #
257
+ # Examples
258
+ # ordinalize(1) # => "1st"
259
+ # ordinalize(2) # => "2nd"
260
+ # ordinalize(1002) # => "1002nd"
261
+ # ordinalize(1003) # => "1003rd"
164
262
  def ordinalize(number)
165
263
  if (11..13).include?(number.to_i % 100)
166
264
  "#{number}th"