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.
- data/CHANGELOG +232 -2
- data/README +43 -0
- data/lib/active_support.rb +4 -1
- data/lib/active_support/breakpoint.rb +5 -0
- data/lib/active_support/core_ext/array.rb +2 -16
- data/lib/active_support/core_ext/array/conversions.rb +30 -4
- data/lib/active_support/core_ext/array/grouping.rb +55 -0
- data/lib/active_support/core_ext/bigdecimal.rb +3 -0
- data/lib/active_support/core_ext/bigdecimal/formatting.rb +7 -0
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +6 -1
- data/lib/active_support/core_ext/date/conversions.rb +13 -7
- data/lib/active_support/core_ext/enumerable.rb +41 -10
- data/lib/active_support/core_ext/exception.rb +2 -2
- data/lib/active_support/core_ext/hash/conversions.rb +123 -12
- data/lib/active_support/core_ext/hash/indifferent_access.rb +18 -9
- data/lib/active_support/core_ext/integer/inflections.rb +10 -4
- data/lib/active_support/core_ext/load_error.rb +3 -3
- data/lib/active_support/core_ext/module.rb +2 -0
- data/lib/active_support/core_ext/module/aliasing.rb +58 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +31 -0
- data/lib/active_support/core_ext/module/delegation.rb +27 -2
- data/lib/active_support/core_ext/name_error.rb +20 -0
- data/lib/active_support/core_ext/string.rb +2 -0
- data/lib/active_support/core_ext/string/access.rb +5 -5
- data/lib/active_support/core_ext/string/inflections.rb +93 -4
- data/lib/active_support/core_ext/string/unicode.rb +42 -0
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/time/calculations.rb +7 -5
- data/lib/active_support/core_ext/time/conversions.rb +1 -2
- data/lib/active_support/dependencies.rb +417 -50
- data/lib/active_support/deprecation.rb +201 -0
- data/lib/active_support/inflections.rb +1 -2
- data/lib/active_support/inflector.rb +117 -19
- data/lib/active_support/json.rb +14 -3
- data/lib/active_support/json/encoders/core.rb +21 -18
- data/lib/active_support/multibyte.rb +7 -0
- data/lib/active_support/multibyte/chars.rb +129 -0
- data/lib/active_support/multibyte/generators/generate_tables.rb +149 -0
- data/lib/active_support/multibyte/handlers/passthru_handler.rb +9 -0
- data/lib/active_support/multibyte/handlers/utf8_handler.rb +453 -0
- data/lib/active_support/multibyte/handlers/utf8_handler_proc.rb +44 -0
- data/lib/active_support/option_merger.rb +3 -3
- data/lib/active_support/ordered_options.rb +24 -23
- data/lib/active_support/reloadable.rb +39 -5
- data/lib/active_support/values/time_zone.rb +1 -1
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/vendor/builder/blankslate.rb +16 -6
- data/lib/active_support/vendor/builder/xchar.rb +112 -0
- data/lib/active_support/vendor/builder/xmlbase.rb +12 -10
- data/lib/active_support/vendor/builder/xmlmarkup.rb +26 -7
- data/lib/active_support/vendor/xml_simple.rb +1021 -0
- data/lib/active_support/version.rb +2 -2
- data/lib/active_support/whiny_nil.rb +1 -1
- metadata +26 -4
- data/lib/active_support/core_ext/hash/conversions.rb.rej +0 -28
@@ -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
|
-
|
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
|
-
|
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
|
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
|
@@ -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]),
|
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__(
|
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
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
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(/</, "<").
|
132
|
+
gsub(/>/, ">").
|
133
|
+
gsub(/"/, '"').
|
134
|
+
gsub(/'/, "'").
|
135
|
+
gsub(/&/, "&")
|
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
|
-
|
16
|
-
|
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
|
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
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|