activesupport 2.3.2 → 2.3.3

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 (43) hide show
  1. data/CHANGELOG +7 -0
  2. data/lib/active_support/cache.rb +14 -1
  3. data/lib/active_support/cache/mem_cache_store.rb +16 -10
  4. data/lib/active_support/cache/strategy/local_cache.rb +1 -1
  5. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  6. data/lib/active_support/core_ext/hash/conversions.rb +13 -4
  7. data/lib/active_support/core_ext/kernel/debugger.rb +4 -2
  8. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  9. data/lib/active_support/core_ext/module/delegation.rb +12 -3
  10. data/lib/active_support/core_ext/module/model_naming.rb +8 -6
  11. data/lib/active_support/core_ext/numeric/bytes.rb +15 -9
  12. data/lib/active_support/core_ext/string/access.rb +29 -5
  13. data/lib/active_support/duration.rb +4 -2
  14. data/lib/active_support/json.rb +1 -22
  15. data/lib/active_support/json/backends/jsongem.rb +38 -0
  16. data/lib/active_support/json/backends/yaml.rb +85 -0
  17. data/lib/active_support/json/decoding.rb +23 -72
  18. data/lib/active_support/json/encoders/date.rb +9 -8
  19. data/lib/active_support/json/encoders/date_time.rb +9 -8
  20. data/lib/active_support/json/encoders/enumerable.rb +14 -9
  21. data/lib/active_support/json/encoders/false_class.rb +4 -2
  22. data/lib/active_support/json/encoders/hash.rb +21 -11
  23. data/lib/active_support/json/encoders/nil_class.rb +4 -2
  24. data/lib/active_support/json/encoders/numeric.rb +16 -0
  25. data/lib/active_support/json/encoders/object.rb +6 -2
  26. data/lib/active_support/json/encoders/regexp.rb +4 -0
  27. data/lib/active_support/json/encoders/string.rb +5 -32
  28. data/lib/active_support/json/encoders/symbol.rb +2 -2
  29. data/lib/active_support/json/encoders/time.rb +9 -8
  30. data/lib/active_support/json/encoders/true_class.rb +4 -2
  31. data/lib/active_support/json/encoding.rb +80 -9
  32. data/lib/active_support/ordered_hash.rb +28 -0
  33. data/lib/active_support/test_case.rb +7 -7
  34. data/lib/active_support/testing/deprecation.rb +2 -0
  35. data/lib/active_support/time_with_zone.rb +9 -8
  36. data/lib/active_support/vendor.rb +6 -7
  37. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +0 -1
  38. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +0 -1
  39. data/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +0 -1
  40. data/lib/active_support/vendor/{memcache-client-1.6.5 → memcache-client-1.7.4}/memcache.rb +242 -70
  41. data/lib/active_support/version.rb +1 -1
  42. data/lib/active_support/xml_mini/jdom.rb +162 -0
  43. metadata +10 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ *2.3.3 (July 20, 2009)*
2
+
3
+ * JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON-library-agnostic. [Jeremy Kemper]
4
+
5
+ * Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp]
6
+
7
+
1
8
  *2.3.2 [Final] (March 15, 2009)*
2
9
 
3
10
  * XmlMini supports LibXML and Nokogiri backends. #2084, #2190 [Bart ten Brinke, Aaron Patterson]
@@ -91,11 +91,16 @@ module ActiveSupport
91
91
  class Store
92
92
  cattr_accessor :logger
93
93
 
94
+ attr_reader :silence, :logger_off
95
+
94
96
  def silence!
95
97
  @silence = true
96
98
  self
97
99
  end
98
100
 
101
+ alias silence? silence
102
+ alias logger_off? logger_off
103
+
99
104
  # Fetches data from the cache, using the given key. If there is data in
100
105
  # the cache with the given key, then that data is returned.
101
106
  #
@@ -220,8 +225,16 @@ module ActiveSupport
220
225
  end
221
226
 
222
227
  private
228
+ def expires_in(options)
229
+ expires_in = options && options[:expires_in]
230
+
231
+ raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
232
+
233
+ expires_in || 0
234
+ end
235
+
223
236
  def log(operation, key, options)
224
- logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off
237
+ logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off?
225
238
  end
226
239
  end
227
240
  end
@@ -23,7 +23,12 @@ module ActiveSupport
23
23
  DELETED = "DELETED\r\n"
24
24
  end
25
25
 
26
- attr_reader :addresses
26
+ def self.build_mem_cache(*addresses)
27
+ addresses = addresses.flatten
28
+ options = addresses.extract_options!
29
+ addresses = ["localhost"] if addresses.empty?
30
+ MemCache.new(addresses, options)
31
+ end
27
32
 
28
33
  # Creates a new MemCacheStore object, with the given memcached server
29
34
  # addresses. Each address is either a host name, or a host-with-port string
@@ -34,15 +39,20 @@ module ActiveSupport
34
39
  # If no addresses are specified, then MemCacheStore will connect to
35
40
  # localhost port 11211 (the default memcached port).
36
41
  def initialize(*addresses)
37
- addresses = addresses.flatten
38
- options = addresses.extract_options!
39
- addresses = ["localhost"] if addresses.empty?
40
- @addresses = addresses
41
- @data = MemCache.new(addresses, options)
42
+ if addresses.first.respond_to?(:get)
43
+ @data = addresses.first
44
+ else
45
+ @data = self.class.build_mem_cache(*addresses)
46
+ end
42
47
 
43
48
  extend Strategy::LocalCache
44
49
  end
45
50
 
51
+ # Reads multiple keys from the cache.
52
+ def read_multi(*keys)
53
+ @data.get_multi keys
54
+ end
55
+
46
56
  def read(key, options = nil) # :nodoc:
47
57
  super
48
58
  @data.get(key, raw?(options))
@@ -120,10 +130,6 @@ module ActiveSupport
120
130
  end
121
131
 
122
132
  private
123
- def expires_in(options)
124
- (options && options[:expires_in]) || 0
125
- end
126
-
127
133
  def raw?(options)
128
134
  options && options[:raw]
129
135
  end
@@ -38,7 +38,7 @@ module ActiveSupport
38
38
  elsif value.nil?
39
39
  value = super
40
40
  local_cache.write(key, value || NULL) if local_cache
41
- value
41
+ value.duplicable? ? value.dup : value
42
42
  else
43
43
  # forcing the value to be immutable
44
44
  value.duplicable? ? value.dup : value
@@ -1,7 +1,7 @@
1
1
  module ActiveSupport #:nodoc:
2
2
  module CoreExtensions #:nodoc:
3
3
  module Date #:nodoc:
4
- # Enables the use of time calculations within Time itself
4
+ # Enables the use of time calculations within Date itself
5
5
  module Calculations
6
6
  def self.included(base) #:nodoc:
7
7
  base.extend ClassMethods
@@ -1,6 +1,14 @@
1
1
  require 'date'
2
+ require 'active_support/core_ext/module/attribute_accessors'
2
3
 
3
4
  module ActiveSupport #:nodoc:
5
+ # these accessors are here because people using ActiveResource and REST to integrate with other systems
6
+ # have to be able to control the default behavior of rename_key. dasherize_xml is set to true to emulate
7
+ # existing behavior. In a future version it should be set to false by default.
8
+ mattr_accessor :dasherize_xml
9
+ mattr_accessor :camelize_xml
10
+ self.dasherize_xml = true
11
+ self.camelize_xml = false
4
12
  module CoreExtensions #:nodoc:
5
13
  module Hash #:nodoc:
6
14
  module Conversions
@@ -143,10 +151,11 @@ module ActiveSupport #:nodoc:
143
151
  end
144
152
 
145
153
  def rename_key(key, options = {})
146
- camelize = options.has_key?(:camelize) && options[:camelize]
147
- dasherize = !options.has_key?(:dasherize) || options[:dasherize]
154
+ camelize = options.has_key?(:camelize) ? options[:camelize] : ActiveSupport.camelize_xml
155
+ dasherize = options.has_key?(:dasherize) ? options[:dasherize] : ActiveSupport.dasherize_xml
148
156
  key = key.camelize if camelize
149
- dasherize ? key.dasherize : key
157
+ key = key.dasherize if dasherize
158
+ key
150
159
  end
151
160
 
152
161
  module ClassMethods
@@ -221,7 +230,7 @@ module ActiveSupport #:nodoc:
221
230
  case params.class.to_s
222
231
  when "Hash"
223
232
  params.inject({}) do |h,(k,v)|
224
- h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v)
233
+ h[k.to_s.tr("-", "_")] = unrename_keys(v)
225
234
  h
226
235
  end
227
236
  when "Array"
@@ -2,12 +2,14 @@ module Kernel
2
2
  unless respond_to?(:debugger)
3
3
  # Starts a debugging session if ruby-debug has been loaded (call script/server --debugger to do load it).
4
4
  def debugger
5
- Rails.logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
5
+ message = "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
6
+ defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
6
7
  end
7
8
  end
8
9
 
9
10
  def breakpoint
10
- Rails.logger.info "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
11
+ message = "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
12
+ defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
11
13
  debugger
12
14
  end
13
15
  end
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/array"
2
+
1
3
  # Extends the module object with module and instance accessors for class attributes,
2
4
  # just like the native attr* accessors for instance attributes.
3
5
  #
@@ -108,12 +108,21 @@ class Module
108
108
 
109
109
  prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
110
110
 
111
- allow_nil = options[:allow_nil] && "#{to} && "
111
+ file, line = caller.first.split(':', 2)
112
+ line = line.to_i
112
113
 
113
114
  methods.each do |method|
114
- module_eval(<<-EOS, "(__DELEGATION__)", 1)
115
+ on_nil =
116
+ if options[:allow_nil]
117
+ 'return'
118
+ else
119
+ %(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
120
+ end
121
+
122
+ module_eval(<<-EOS, file, line)
115
123
  def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
116
- #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
124
+ #{on_nil} if #{to}.nil?
125
+ #{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
117
126
  end # end
118
127
  EOS
119
128
  end
@@ -1,13 +1,15 @@
1
1
  module ActiveSupport
2
2
  class ModelName < String
3
- attr_reader :singular, :plural, :cache_key, :partial_path
3
+ attr_reader :singular, :plural, :element, :collection, :partial_path
4
+ alias_method :cache_key, :collection
4
5
 
5
6
  def initialize(name)
6
7
  super
7
- @singular = underscore.tr('/', '_').freeze
8
- @plural = @singular.pluralize.freeze
9
- @cache_key = tableize.freeze
10
- @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze
8
+ @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
9
+ @plural = ActiveSupport::Inflector.pluralize(@singular).freeze
10
+ @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
11
+ @collection = ActiveSupport::Inflector.tableize(self).freeze
12
+ @partial_path = "#{@collection}/#{@element}".freeze
11
13
  end
12
14
  end
13
15
 
@@ -16,7 +18,7 @@ module ActiveSupport
16
18
  # Returns an ActiveSupport::ModelName object for module. It can be
17
19
  # used to retrieve all kinds of naming-related information.
18
20
  def model_name
19
- @model_name ||= ModelName.new(name)
21
+ @model_name ||= ::ActiveSupport::ModelName.new(name)
20
22
  end
21
23
  end
22
24
  end
@@ -3,41 +3,47 @@ module ActiveSupport #:nodoc:
3
3
  module Numeric #:nodoc:
4
4
  # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
5
5
  module Bytes
6
+ KILOBYTE = 1024
7
+ MEGABYTE = KILOBYTE * 1024
8
+ GIGABYTE = MEGABYTE * 1024
9
+ TERABYTE = GIGABYTE * 1024
10
+ PETABYTE = TERABYTE * 1024
11
+ EXABYTE = PETABYTE * 1024
12
+
6
13
  def bytes
7
14
  self
8
15
  end
9
16
  alias :byte :bytes
10
17
 
11
18
  def kilobytes
12
- self * 1024
19
+ self * KILOBYTE
13
20
  end
14
21
  alias :kilobyte :kilobytes
15
22
 
16
23
  def megabytes
17
- self * 1024.kilobytes
24
+ self * MEGABYTE
18
25
  end
19
26
  alias :megabyte :megabytes
20
27
 
21
28
  def gigabytes
22
- self * 1024.megabytes
29
+ self * GIGABYTE
23
30
  end
24
31
  alias :gigabyte :gigabytes
25
32
 
26
33
  def terabytes
27
- self * 1024.gigabytes
34
+ self * TERABYTE
28
35
  end
29
36
  alias :terabyte :terabytes
30
-
37
+
31
38
  def petabytes
32
- self * 1024.terabytes
39
+ self * PETABYTE
33
40
  end
34
41
  alias :petabyte :petabytes
35
-
42
+
36
43
  def exabytes
37
- self * 1024.petabytes
44
+ self * EXABYTE
38
45
  end
39
46
  alias :exabyte :exabytes
40
-
41
47
  end
42
48
  end
43
49
  end
@@ -41,9 +41,15 @@ module ActiveSupport #:nodoc:
41
41
  # "hello".first(2) # => "he"
42
42
  # "hello".first(10) # => "hello"
43
43
  def first(limit = 1)
44
- mb_chars[0..(limit - 1)].to_s
44
+ if limit == 0
45
+ ''
46
+ elsif limit >= size
47
+ self
48
+ else
49
+ mb_chars[0...limit].to_s
50
+ end
45
51
  end
46
-
52
+
47
53
  # Returns the last character of the string or the last +limit+ characters.
48
54
  #
49
55
  # Examples:
@@ -51,7 +57,13 @@ module ActiveSupport #:nodoc:
51
57
  # "hello".last(2) # => "lo"
52
58
  # "hello".last(10) # => "hello"
53
59
  def last(limit = 1)
54
- (mb_chars[(-limit)..-1] || self).to_s
60
+ if limit == 0
61
+ ''
62
+ elsif limit >= size
63
+ self
64
+ else
65
+ mb_chars[(-limit)..-1].to_s
66
+ end
55
67
  end
56
68
  end
57
69
  else
@@ -69,11 +81,23 @@ module ActiveSupport #:nodoc:
69
81
  end
70
82
 
71
83
  def first(limit = 1)
72
- self[0..(limit - 1)]
84
+ if limit == 0
85
+ ''
86
+ elsif limit >= size
87
+ self
88
+ else
89
+ to(limit - 1)
90
+ end
73
91
  end
74
92
 
75
93
  def last(limit = 1)
76
- from(-limit) || self
94
+ if limit == 0
95
+ ''
96
+ elsif limit >= size
97
+ self
98
+ else
99
+ from(-limit)
100
+ end
77
101
  end
78
102
  end
79
103
  end
@@ -67,10 +67,12 @@ module ActiveSupport
67
67
 
68
68
  def inspect #:nodoc:
69
69
  consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
70
- [:years, :months, :days, :minutes, :seconds].map do |length|
70
+ parts = [:years, :months, :days, :minutes, :seconds].map do |length|
71
71
  n = consolidated[length]
72
72
  "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
73
- end.compact.to_sentence(:locale => :en)
73
+ end.compact
74
+ parts = ["0 seconds"] if parts.empty?
75
+ parts.to_sentence(:locale => :en)
74
76
  end
75
77
 
76
78
  protected
@@ -1,23 +1,2 @@
1
- module ActiveSupport
2
- # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
3
- mattr_accessor :use_standard_json_time_format
4
-
5
- class << self
6
- def escape_html_entities_in_json
7
- @escape_html_entities_in_json
8
- end
9
-
10
- def escape_html_entities_in_json=(value)
11
- ActiveSupport::JSON::Encoding.escape_regex = \
12
- if value
13
- /[\010\f\n\r\t"\\><&]/
14
- else
15
- /[\010\f\n\r\t"\\]/
16
- end
17
- @escape_html_entities_in_json = value
18
- end
19
- end
20
- end
21
-
22
- require 'active_support/json/encoding'
23
1
  require 'active_support/json/decoding'
2
+ require 'active_support/json/encoding'
@@ -0,0 +1,38 @@
1
+ require 'json' unless defined?(JSON)
2
+
3
+ module ActiveSupport
4
+ module JSON
5
+ ParseError = ::JSON::ParserError unless const_defined?(:ParseError)
6
+
7
+ module Backends
8
+ module JSONGem
9
+ extend self
10
+
11
+ # Converts a JSON string into a Ruby object.
12
+ def decode(json)
13
+ data = ::JSON.parse(json)
14
+ if ActiveSupport.parse_json_times
15
+ convert_dates_from(data)
16
+ else
17
+ data
18
+ end
19
+ end
20
+
21
+ private
22
+ def convert_dates_from(data)
23
+ case data
24
+ when DATE_REGEX
25
+ DateTime.parse(data)
26
+ when Array
27
+ data.map! { |d| convert_dates_from(d) }
28
+ when Hash
29
+ data.each do |key, value|
30
+ data[key] = convert_dates_from(value)
31
+ end
32
+ else data
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,85 @@
1
+ require 'active_support/core_ext/string/starts_ends_with'
2
+
3
+ module ActiveSupport
4
+ module JSON
5
+ unless const_defined?(:ParseError)
6
+ class ParseError < StandardError
7
+ end
8
+ end
9
+
10
+ module Backends
11
+ module Yaml
12
+ extend self
13
+
14
+ # Converts a JSON string into a Ruby object.
15
+ def decode(json)
16
+ YAML.load(convert_json_to_yaml(json))
17
+ rescue ArgumentError => e
18
+ raise ParseError, "Invalid JSON string"
19
+ end
20
+
21
+ protected
22
+ # Ensure that ":" and "," are always followed by a space
23
+ def convert_json_to_yaml(json) #:nodoc:
24
+ require 'strscan' unless defined? ::StringScanner
25
+ scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
26
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
27
+ case char = scanner[1]
28
+ when '"', "'"
29
+ if !quoting
30
+ quoting = char
31
+ pos = scanner.pos
32
+ elsif quoting == char
33
+ if json[pos..scanner.pos-2] =~ DATE_REGEX
34
+ # found a date, track the exact positions of the quotes so we can remove them later.
35
+ # oh, and increment them for each current mark, each one is an extra padded space that bumps
36
+ # the position in the final YAML output
37
+ total_marks = marks.size
38
+ times << pos+total_marks << scanner.pos+total_marks
39
+ end
40
+ quoting = false
41
+ end
42
+ when ":",","
43
+ marks << scanner.pos - 1 unless quoting
44
+ end
45
+ end
46
+
47
+ if marks.empty?
48
+ json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
49
+ ustr = $1
50
+ if ustr.start_with?('u')
51
+ [ustr[1..-1].to_i(16)].pack("U")
52
+ elsif ustr == '\\'
53
+ '\\\\'
54
+ else
55
+ ustr
56
+ end
57
+ end
58
+ else
59
+ left_pos = [-1].push(*marks)
60
+ right_pos = marks << scanner.pos + scanner.rest_size
61
+ output = []
62
+ left_pos.each_with_index do |left, i|
63
+ scanner.pos = left.succ
64
+ output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
65
+ ustr = $1
66
+ if ustr.start_with?('u')
67
+ [ustr[1..-1].to_i(16)].pack("U")
68
+ elsif ustr == '\\'
69
+ '\\\\'
70
+ else
71
+ ustr
72
+ end
73
+ end
74
+ end
75
+ output = output * " "
76
+
77
+ times.each { |i| output[i-1] = ' ' }
78
+ output.gsub!(/\\\//, '/')
79
+ output
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end