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,3 @@
1
+ require 'bigdecimal'
2
+
3
+ require File.dirname(__FILE__) + '/bigdecimal/formatting.rb'
@@ -0,0 +1,7 @@
1
+ class BigDecimal #:nodoc:
2
+
3
+ alias :_original_to_s :to_s
4
+ def to_s(format="F")
5
+ _original_to_s(format)
6
+ end
7
+ end
@@ -107,7 +107,12 @@ class Class # :nodoc:
107
107
  private
108
108
  def inherited_with_inheritable_attributes(child)
109
109
  inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
110
- child.instance_variable_set('@inheritable_attributes', inheritable_attributes.dup)
110
+
111
+ new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
112
+ memo.update(key => (value.dup rescue value))
113
+ end
114
+
115
+ child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
111
116
  end
112
117
 
113
118
  alias inherited_without_inheritable_attributes inherited
@@ -7,14 +7,14 @@ module ActiveSupport #:nodoc:
7
7
  :short => "%e %b",
8
8
  :long => "%B %e, %Y"
9
9
  }
10
-
10
+
11
11
  def self.included(klass) #:nodoc:
12
12
  klass.send(:alias_method, :to_default_s, :to_s)
13
13
  klass.send(:alias_method, :to_s, :to_formatted_s)
14
14
  end
15
-
15
+
16
16
  def to_formatted_s(format = :default)
17
- DATE_FORMATS[format] ? strftime(DATE_FORMATS[format]).strip : to_default_s
17
+ DATE_FORMATS[format] ? strftime(DATE_FORMATS[format]).strip : to_default_s
18
18
  end
19
19
 
20
20
  # To be able to keep Dates and Times interchangeable on conversions
@@ -23,11 +23,17 @@ module ActiveSupport #:nodoc:
23
23
  end
24
24
 
25
25
  def to_time(form = :local)
26
- ::Time.send(form, year, month, day)
26
+ if respond_to?(:hour)
27
+ ::Time.send(form, year, month, day, hour, min, sec)
28
+ else
29
+ ::Time.send(form, year, month, day)
30
+ end
31
+ end
32
+
33
+ def xmlschema
34
+ to_time.xmlschema
27
35
  end
28
-
29
- alias :xmlschema :to_s
30
36
  end
31
37
  end
32
38
  end
33
- end
39
+ end
@@ -1,12 +1,4 @@
1
- module Enumerable #:nodoc:
2
- def first_match
3
- match = nil
4
- each do |items|
5
- break if match = yield(items)
6
- end
7
- match
8
- end
9
-
1
+ module Enumerable
10
2
  # Collect an enumerable into sets, grouped by the result of a block. Useful,
11
3
  # for example, for grouping records by date.
12
4
  #
@@ -27,5 +19,44 @@ module Enumerable #:nodoc:
27
19
  (groups[yield(element)] ||= []) << element
28
20
  groups
29
21
  end
30
- end
22
+ end if RUBY_VERSION < '1.9'
23
+
24
+ # Calculates a sum from the elements. Examples:
25
+ #
26
+ # payments.sum { |p| p.price * p.tax_rate }
27
+ # payments.sum(&:price)
28
+ #
29
+ # This is instead of payments.inject { |sum, p| sum + p.price }
30
+ #
31
+ # Also calculates sums without the use of a block:
32
+ # [5, 15, 10].sum # => 30
33
+ #
34
+ # The default identity (sum of an empty list) is zero.
35
+ # However, you can override this default:
36
+ #
37
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
38
+ #
39
+ def sum(identity = 0, &block)
40
+ return identity unless size > 0
41
+ if block_given?
42
+ map(&block).sum
43
+ else
44
+ inject { |sum, element| sum + element }
45
+ end
46
+ end
47
+
48
+ # Convert an enumerable to a hash. Examples:
49
+ #
50
+ # people.index_by(&:login)
51
+ # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
52
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
53
+ # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
54
+ #
55
+ def index_by
56
+ inject({}) do |accum, elem|
57
+ accum[yield(elem)] = elem
58
+ accum
59
+ end
60
+ end
61
+
31
62
  end
@@ -28,6 +28,6 @@ class Exception # :nodoc:
28
28
  end
29
29
 
30
30
  def framework_backtrace
31
- clean_backtrace.select {|line| line =~ FrameworkRegexp}
31
+ clean_backtrace.grep FrameworkRegexp
32
32
  end
33
- end
33
+ end
@@ -1,42 +1,153 @@
1
+ require 'date'
2
+ require 'xml_simple'
3
+
1
4
  module ActiveSupport #:nodoc:
2
5
  module CoreExtensions #:nodoc:
3
6
  module Hash #:nodoc:
4
7
  module Conversions
5
8
  XML_TYPE_NAMES = {
6
9
  "Fixnum" => "integer",
10
+ "Bignum" => "integer",
11
+ "BigDecimal" => "numeric",
12
+ "Float" => "float",
7
13
  "Date" => "date",
14
+ "DateTime" => "datetime",
8
15
  "Time" => "datetime",
9
16
  "TrueClass" => "boolean",
10
17
  "FalseClass" => "boolean"
11
- }
12
-
18
+ } unless defined? XML_TYPE_NAMES
19
+
13
20
  XML_FORMATTING = {
14
21
  "date" => Proc.new { |date| date.to_s(:db) },
15
- "datetime" => Proc.new { |time| time.xmlschema }
16
- }
17
-
22
+ "datetime" => Proc.new { |time| time.xmlschema },
23
+ "binary" => Proc.new { |binary| Base64.encode64(binary) }
24
+ } unless defined? XML_FORMATTING
25
+
26
+ def self.included(klass)
27
+ klass.extend(ClassMethods)
28
+ end
29
+
18
30
  def to_xml(options = {})
19
31
  options[:indent] ||= 2
20
- options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), :root => "hash" })
32
+ options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]),
33
+ :root => "hash" })
21
34
  options[:builder].instruct! unless options.delete(:skip_instruct)
35
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
36
+ root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
22
37
 
23
- options[:builder].__send__(options[:root].to_s.dasherize) do
38
+ options[:builder].__send__(:method_missing, root) do
24
39
  each do |key, value|
25
40
  case value
26
41
  when ::Hash
27
42
  value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
28
43
  when ::Array
29
44
  value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true}))
45
+ when ::Method, ::Proc
46
+ # If the Method or Proc takes two arguments, then
47
+ # pass the suggested child element name. This is
48
+ # used if the Method or Proc will be operating over
49
+ # multiple records and needs to create an containing
50
+ # element that will contain the objects being
51
+ # serialized.
52
+ if 1 == value.arity
53
+ value.call(options.merge({ :root => key, :skip_instruct => true }))
54
+ else
55
+ value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize)
56
+ end
30
57
  else
31
- type_name = XML_TYPE_NAMES[value.class.to_s]
58
+ if value.respond_to?(:to_xml)
59
+ value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
60
+ else
61
+ type_name = XML_TYPE_NAMES[value.class.name]
62
+
63
+ key = dasherize ? key.to_s.dasherize : key.to_s
32
64
 
33
- options[:builder].tag!(key.to_s.dasherize,
34
- XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value,
35
- options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
36
- )
65
+ attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
66
+ if value.nil?
67
+ attributes[:nil] = true
68
+ end
69
+
70
+ options[:builder].tag!(key,
71
+ XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value,
72
+ attributes
73
+ )
74
+ end
37
75
  end
38
76
  end
39
77
  end
78
+
79
+ end
80
+
81
+ module ClassMethods
82
+ def from_xml(xml)
83
+ # TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
84
+ undasherize_keys(typecast_xml_value(XmlSimple.xml_in(xml,
85
+ 'forcearray' => false,
86
+ 'forcecontent' => true,
87
+ 'keeproot' => true,
88
+ 'contentkey' => '__content__')
89
+ ))
90
+ end
91
+
92
+ def create_from_xml(xml)
93
+ ActiveSupport::Deprecation.warn("Hash.create_from_xml has been renamed to Hash.from_xml", caller)
94
+ from_xml(xml)
95
+ end
96
+
97
+ private
98
+ def typecast_xml_value(value)
99
+ case value.class.to_s
100
+ when "Hash"
101
+ if value.has_key?("__content__")
102
+ content = translate_xml_entities(value["__content__"])
103
+ case value["type"]
104
+ when "integer" then content.to_i
105
+ when "boolean" then content.strip == "true"
106
+ when "datetime" then ::Time.parse(content).utc
107
+ when "date" then ::Date.parse(content)
108
+ else content
109
+ end
110
+ else
111
+ (value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)|
112
+ h[k] = typecast_xml_value(v)
113
+ h
114
+ end
115
+ end
116
+ when "Array"
117
+ value.map! { |i| typecast_xml_value(i) }
118
+ case value.length
119
+ when 0 then nil
120
+ when 1 then value.first
121
+ else value
122
+ end
123
+ when "String"
124
+ value
125
+ else
126
+ raise "can't typecast #{value.inspect}"
127
+ end
128
+ end
129
+
130
+ def translate_xml_entities(value)
131
+ value.gsub(/&lt;/, "<").
132
+ gsub(/&gt;/, ">").
133
+ gsub(/&quot;/, '"').
134
+ gsub(/&apos;/, "'").
135
+ gsub(/&amp;/, "&")
136
+ end
137
+
138
+ def undasherize_keys(params)
139
+ case params.class.to_s
140
+ when "Hash"
141
+ params.inject({}) do |h,(k,v)|
142
+ h[k.to_s.tr("-", "_")] = undasherize_keys(v)
143
+ h
144
+ end
145
+ when "Array"
146
+ params.map { |v| undasherize_keys(v) }
147
+ else
148
+ params
149
+ end
150
+ end
40
151
  end
41
152
  end
42
153
  end
@@ -10,14 +10,18 @@ class HashWithIndifferentAccess < Hash
10
10
  super(constructor)
11
11
  end
12
12
  end
13
-
14
- def default(key)
15
- self[key.to_s] if key.is_a?(Symbol)
16
- end
13
+
14
+ def default(key = nil)
15
+ if key.is_a?(Symbol) && include?(key = key.to_s)
16
+ self[key]
17
+ else
18
+ super
19
+ end
20
+ end
17
21
 
18
22
  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
19
23
  alias_method :regular_update, :update unless method_defined?(:regular_update)
20
-
24
+
21
25
  def []=(key, value)
22
26
  regular_writer(convert_key(key), convert_value(value))
23
27
  end
@@ -26,7 +30,7 @@ class HashWithIndifferentAccess < Hash
26
30
  other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
27
31
  self
28
32
  end
29
-
33
+
30
34
  alias_method :merge!, :update
31
35
 
32
36
  def key?(key)
@@ -48,7 +52,7 @@ class HashWithIndifferentAccess < Hash
48
52
  def dup
49
53
  HashWithIndifferentAccess.new(self)
50
54
  end
51
-
55
+
52
56
  def merge(hash)
53
57
  self.dup.update(hash)
54
58
  end
@@ -56,7 +60,10 @@ class HashWithIndifferentAccess < Hash
56
60
  def delete(key)
57
61
  super(convert_key(key))
58
62
  end
59
-
63
+
64
+ def stringify_keys!; self end
65
+ def symbolize_keys!; self end
66
+
60
67
  protected
61
68
  def convert_key(key)
62
69
  key.kind_of?(Symbol) ? key.to_s : key
@@ -71,7 +78,9 @@ module ActiveSupport #:nodoc:
71
78
  module Hash #:nodoc:
72
79
  module IndifferentAccess #:nodoc:
73
80
  def with_indifferent_access
74
- HashWithIndifferentAccess.new(self)
81
+ hash = HashWithIndifferentAccess.new(self)
82
+ hash.default = self.default
83
+ hash
75
84
  end
76
85
  end
77
86
  end
@@ -1,11 +1,17 @@
1
- require File.dirname(__FILE__) + '/../../inflector' unless defined? Inflector
1
+ require 'active_support/inflector'
2
+
2
3
  module ActiveSupport #:nodoc:
3
4
  module CoreExtensions #:nodoc:
4
5
  module Integer #:nodoc:
5
6
  module Inflections
6
- # 1.ordinalize # => "1st"
7
- # 3.ordinalize # => "3rd"
8
- # 10.ordinalize # => "10th"
7
+ # Ordinalize turns a number into an ordinal string used to denote the
8
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
9
+ #
10
+ # Examples
11
+ # 1.ordinalize # => "1st"
12
+ # 2.ordinalize # => "2nd"
13
+ # 1002.ordinalize # => "1002nd"
14
+ # 1003.ordinalize # => "1003rd"
9
15
  def ordinalize
10
16
  Inflector.ordinalize(self)
11
17
  end
@@ -8,7 +8,7 @@ class MissingSourceFile < LoadError #:nodoc:
8
8
  def is_missing?(path)
9
9
  path.gsub(/\.rb$/, '') == self.path.gsub(/\.rb$/, '')
10
10
  end
11
-
11
+
12
12
  def self.from_message(message)
13
13
  REGEXPS.each do |regexp, capture|
14
14
  match = regexp.match(message)
@@ -16,12 +16,12 @@ class MissingSourceFile < LoadError #:nodoc:
16
16
  end
17
17
  nil
18
18
  end
19
-
19
+
20
20
  REGEXPS = [
21
21
  [/^no such file to load -- (.+)$/i, 1],
22
22
  [/^Missing \w+ (file\s*)?([^\s]+.rb)$/i, 2],
23
23
  [/^Missing API definition file in (.+)$/i, 1]
24
- ]
24
+ ] unless defined?(REGEXPS)
25
25
  end
26
26
 
27
27
  module ActiveSupport #:nodoc:
@@ -1,5 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/module/inclusion'
2
2
  require File.dirname(__FILE__) + '/module/attribute_accessors'
3
+ require File.dirname(__FILE__) + '/module/attr_internal'
3
4
  require File.dirname(__FILE__) + '/module/delegation'
4
5
  require File.dirname(__FILE__) + '/module/introspection'
5
6
  require File.dirname(__FILE__) + '/module/loading'
7
+ require File.dirname(__FILE__) + '/module/aliasing'
@@ -0,0 +1,58 @@
1
+ class Module
2
+ # Encapsulates the common pattern of:
3
+ #
4
+ # alias_method :foo_without_feature, :foo
5
+ # alias_method :foo, :foo_with_feature
6
+ #
7
+ # With this, you simply do:
8
+ #
9
+ # alias_method_chain :foo, :feature
10
+ #
11
+ # And both aliases are set up for you.
12
+ #
13
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
14
+ #
15
+ # alias_method_chain :foo?, :feature
16
+ #
17
+ # is equivalent to
18
+ #
19
+ # alias_method :foo_without_feature?, :foo?
20
+ # alias_method :foo?, :foo_with_feature?
21
+ #
22
+ # so you can safely chain foo, foo?, and foo! with the same feature.
23
+ def alias_method_chain(target, feature)
24
+ # Strip out punctuation on predicates or bang methods since
25
+ # e.g. target?_without_feature is not a valid method name.
26
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
27
+ yield(aliased_target, punctuation) if block_given?
28
+ alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
29
+ alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
30
+ end
31
+
32
+ # Allows you to make aliases for attributes, which includes
33
+ # getter, setter, and query methods.
34
+ #
35
+ # Example:
36
+ #
37
+ # class Content < ActiveRecord::Base
38
+ # # has a title attribute
39
+ # end
40
+ #
41
+ # class Email < ActiveRecord::Base
42
+ # alias_attribute :subject, :title
43
+ # end
44
+ #
45
+ # e = Email.find(1)
46
+ # e.title # => "Superstars"
47
+ # e.subject # => "Superstars"
48
+ # e.subject? # => true
49
+ # e.subject = "Megastars"
50
+ # e.title # => "Megastars"
51
+ def alias_attribute(new_name, old_name)
52
+ module_eval <<-STR, __FILE__, __LINE__+1
53
+ def #{new_name}; #{old_name}; end
54
+ def #{new_name}?; #{old_name}?; end
55
+ def #{new_name}=(v); self.#{old_name} = v; end
56
+ STR
57
+ end
58
+ end