activesupport 4.0.0.beta1 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.rdoc +2 -2
- data/lib/active_support/cache.rb +62 -49
- data/lib/active_support/cache/file_store.rb +2 -2
- data/lib/active_support/cache/strategy/local_cache.rb +48 -37
- data/lib/active_support/callbacks.rb +27 -8
- data/lib/active_support/core_ext.rb +2 -2
- data/lib/active_support/core_ext/array/conversions.rb +5 -3
- data/lib/active_support/core_ext/array/uniq_by.rb +2 -2
- data/lib/active_support/core_ext/benchmark.rb +7 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +31 -27
- data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +50 -24
- data/lib/active_support/core_ext/module/deprecation.rb +2 -2
- data/lib/active_support/core_ext/string.rb +0 -1
- data/lib/active_support/core_ext/string/conversions.rb +6 -8
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +2 -2
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/log_subscriber.rb +9 -46
- data/lib/active_support/message_encryptor.rb +9 -9
- data/lib/active_support/message_verifier.rb +3 -3
- data/lib/active_support/notifications.rb +22 -1
- data/lib/active_support/notifications/instrumenter.rb +2 -2
- data/lib/active_support/number_helper.rb +3 -2
- data/lib/active_support/per_thread_registry.rb +52 -0
- data/lib/active_support/subscriber.rb +93 -0
- data/lib/active_support/testing/constant_lookup.rb +2 -0
- data/lib/active_support/time_with_zone.rb +2 -0
- data/lib/active_support/values/time_zone.rb +5 -3
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +6 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- metadata +14 -7
- data/lib/active_support/core_ext/string/xchar.rb +0 -18
@@ -61,6 +61,8 @@ module ActiveSupport
|
|
61
61
|
extend ActiveSupport::DescendantsTracker
|
62
62
|
end
|
63
63
|
|
64
|
+
CALLBACK_FILTER_TYPES = [:before, :after, :around]
|
65
|
+
|
64
66
|
# Runs the callbacks for the given event.
|
65
67
|
#
|
66
68
|
# Calls the before and around callbacks in the order they were set, yields
|
@@ -131,7 +133,13 @@ module ActiveSupport
|
|
131
133
|
end
|
132
134
|
|
133
135
|
def matches?(_kind, _filter)
|
134
|
-
|
136
|
+
if @_is_object_filter
|
137
|
+
_filter_matches = @filter.to_s.start_with?(_method_name_for_object_filter(_kind, _filter, false))
|
138
|
+
else
|
139
|
+
_filter_matches = (@filter == _filter)
|
140
|
+
end
|
141
|
+
|
142
|
+
@kind == _kind && _filter_matches
|
135
143
|
end
|
136
144
|
|
137
145
|
def duplicates?(other)
|
@@ -234,6 +242,16 @@ module ActiveSupport
|
|
234
242
|
@compiled_options = conditions.flatten.join(" && ")
|
235
243
|
end
|
236
244
|
|
245
|
+
def _method_name_for_object_filter(kind, filter, append_next_id = true)
|
246
|
+
class_name = filter.kind_of?(Class) ? filter.to_s : filter.class.to_s
|
247
|
+
class_name.gsub!(/<|>|#/, '')
|
248
|
+
class_name.gsub!(/\/|:/, "_")
|
249
|
+
|
250
|
+
method_name = "_callback_#{kind}_#{class_name}"
|
251
|
+
method_name << "_#{next_id}" if append_next_id
|
252
|
+
method_name
|
253
|
+
end
|
254
|
+
|
237
255
|
# Filters support:
|
238
256
|
#
|
239
257
|
# Arrays:: Used in conditions. This is used to specify
|
@@ -255,7 +273,8 @@ module ActiveSupport
|
|
255
273
|
# a method is created that calls the before_foo method
|
256
274
|
# on the object.
|
257
275
|
def _compile_filter(filter)
|
258
|
-
|
276
|
+
@_is_object_filter = false
|
277
|
+
|
259
278
|
case filter
|
260
279
|
when Array
|
261
280
|
filter.map {|f| _compile_filter(f)}
|
@@ -264,11 +283,14 @@ module ActiveSupport
|
|
264
283
|
when String
|
265
284
|
"(#{filter})"
|
266
285
|
when Proc
|
286
|
+
method_name = "_callback_#{@kind}_#{next_id}"
|
267
287
|
@klass.send(:define_method, method_name, &filter)
|
268
288
|
return method_name if filter.arity <= 0
|
269
289
|
|
270
290
|
method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
|
271
291
|
else
|
292
|
+
method_name = _method_name_for_object_filter(kind, filter)
|
293
|
+
@_is_object_filter = true
|
272
294
|
@klass.send(:define_method, "#{method_name}_object") { filter }
|
273
295
|
|
274
296
|
_normalize_legacy_filter(kind, filter)
|
@@ -314,14 +336,11 @@ module ActiveSupport
|
|
314
336
|
@config = {
|
315
337
|
:terminator => "false",
|
316
338
|
:scope => [ :kind ]
|
317
|
-
}.merge(config)
|
339
|
+
}.merge!(config)
|
318
340
|
end
|
319
341
|
|
320
342
|
def compile
|
321
|
-
method =
|
322
|
-
method << "value = nil"
|
323
|
-
method << "halted = false"
|
324
|
-
|
343
|
+
method = ["value = nil", "halted = false"]
|
325
344
|
callbacks = "value = !halted && (!block_given? || yield)"
|
326
345
|
reverse_each do |callback|
|
327
346
|
callbacks = callback.apply(callbacks)
|
@@ -395,7 +414,7 @@ module ActiveSupport
|
|
395
414
|
# This is used internally to append, prepend and skip callbacks to the
|
396
415
|
# CallbackChain.
|
397
416
|
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
|
398
|
-
type =
|
417
|
+
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
399
418
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
400
419
|
filters.unshift(block) if block
|
401
420
|
|
@@ -12,7 +12,7 @@ class Array
|
|
12
12
|
# pass an option key that doesn't exist in the list below, it will raise an
|
13
13
|
# <tt>ArgumentError</tt>.
|
14
14
|
#
|
15
|
-
# Options
|
15
|
+
# ==== Options
|
16
16
|
#
|
17
17
|
# * <tt>:words_connector</tt> - The sign or word used to join the elements
|
18
18
|
# in arrays with two or more elements (default: ", ").
|
@@ -24,6 +24,8 @@ class Array
|
|
24
24
|
# the connector options defined on the 'support.array' namespace in the
|
25
25
|
# corresponding dictionary file.
|
26
26
|
#
|
27
|
+
# ==== Examples
|
28
|
+
#
|
27
29
|
# [].to_sentence # => ""
|
28
30
|
# ['one'].to_sentence # => "one"
|
29
31
|
# ['one', 'two'].to_sentence # => "one and two"
|
@@ -38,10 +40,10 @@ class Array
|
|
38
40
|
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
|
39
41
|
# # => "one or two or at least three"
|
40
42
|
#
|
41
|
-
#
|
43
|
+
# Using <tt>:locale</tt> option:
|
42
44
|
#
|
43
45
|
# # Given this locale dictionary:
|
44
|
-
# #
|
46
|
+
# #
|
45
47
|
# # es:
|
46
48
|
# # support:
|
47
49
|
# # array:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Array
|
2
|
-
# *DEPRECATED*: Use
|
2
|
+
# *DEPRECATED*: Use <tt>Array#uniq</tt> instead.
|
3
3
|
#
|
4
4
|
# Returns a unique array based on the criteria in the block.
|
5
5
|
#
|
@@ -9,7 +9,7 @@ class Array
|
|
9
9
|
uniq(&block)
|
10
10
|
end
|
11
11
|
|
12
|
-
# *DEPRECATED*: Use
|
12
|
+
# *DEPRECATED*: Use <tt>Array#uniq!</tt> instead.
|
13
13
|
#
|
14
14
|
# Same as +uniq_by+, but modifies +self+.
|
15
15
|
def uniq_by!(&block)
|
@@ -44,7 +44,8 @@ class Class
|
|
44
44
|
# Base.setting # => []
|
45
45
|
# Subclass.setting # => [:foo]
|
46
46
|
#
|
47
|
-
# For convenience,
|
47
|
+
# For convenience, an instance predicate method is defined as well.
|
48
|
+
# To skip it, pass <tt>instance_predicate: false</tt>.
|
48
49
|
#
|
49
50
|
# Subclass.setting? # => false
|
50
51
|
#
|
@@ -72,43 +73,46 @@ class Class
|
|
72
73
|
# double assignment is used to avoid "assigned but unused variable" warning
|
73
74
|
instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
74
75
|
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
76
|
+
instance_predicate = options.fetch(:instance_predicate, true)
|
75
77
|
|
76
|
-
# We use class_eval here rather than define_method because class_attribute
|
77
|
-
# may be used in a performance sensitive context therefore the overhead that
|
78
|
-
# define_method introduces may become significant.
|
79
78
|
attrs.each do |name|
|
80
|
-
|
81
|
-
|
82
|
-
def self.#{name}?() !!#{name} end
|
79
|
+
define_singleton_method(name) { nil }
|
80
|
+
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
83
81
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
82
|
+
ivar = "@#{name}"
|
83
|
+
|
84
|
+
define_singleton_method("#{name}=") do |val|
|
85
|
+
singleton_class.class_eval do
|
86
|
+
remove_possible_method(name)
|
87
|
+
define_method(name) { val }
|
88
|
+
end
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
if singleton_class?
|
91
|
+
class_eval do
|
92
|
+
remove_possible_method(name)
|
93
|
+
define_method(name) do
|
94
|
+
if instance_variable_defined? ivar
|
95
|
+
instance_variable_get ivar
|
96
|
+
else
|
97
|
+
singleton_class.send name
|
95
98
|
end
|
96
99
|
end
|
97
100
|
end
|
98
|
-
val
|
99
101
|
end
|
102
|
+
val
|
103
|
+
end
|
100
104
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
!!#{name}
|
105
|
+
if instance_reader
|
106
|
+
remove_possible_method name
|
107
|
+
define_method(name) do
|
108
|
+
if instance_variable_defined?(ivar)
|
109
|
+
instance_variable_get ivar
|
110
|
+
else
|
111
|
+
self.class.public_send name
|
109
112
|
end
|
110
113
|
end
|
111
|
-
|
114
|
+
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
115
|
+
end
|
112
116
|
|
113
117
|
attr_writer name if instance_writer
|
114
118
|
end
|
@@ -32,7 +32,7 @@ class Class
|
|
32
32
|
def cattr_reader(*syms)
|
33
33
|
options = syms.extract_options!
|
34
34
|
syms.each do |sym|
|
35
|
-
raise NameError.new(
|
35
|
+
raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
|
36
36
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
37
37
|
unless defined? @@#{sym}
|
38
38
|
@@#{sym} = nil
|
@@ -93,7 +93,7 @@ class Class
|
|
93
93
|
def cattr_writer(*syms)
|
94
94
|
options = syms.extract_options!
|
95
95
|
syms.each do |sym|
|
96
|
-
raise NameError.new(
|
96
|
+
raise NameError.new("invalid class attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
|
97
97
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
98
98
|
unless defined? @@#{sym}
|
99
99
|
@@#{sym} = nil
|
@@ -93,7 +93,7 @@ module DateAndTime
|
|
93
93
|
|
94
94
|
# Returns a new date/time at the end of the quarter.
|
95
95
|
# Example: 31st March, 30th June, 30th September.
|
96
|
-
#
|
96
|
+
# DateTime objects will have a time set to 23:59:59.
|
97
97
|
def end_of_quarter
|
98
98
|
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
|
99
99
|
beginning_of_month.change(:month => last_quarter_month).end_of_month
|
@@ -109,11 +109,11 @@ module DateAndTime
|
|
109
109
|
alias :at_beginning_of_year :beginning_of_year
|
110
110
|
|
111
111
|
# Returns a new date/time representing the given day in the next week.
|
112
|
-
#
|
113
|
-
# +Date.beginning_of_week+ or +config.beginning_of_week+
|
114
|
-
# DateTime objects have their time set to 0:00.
|
115
|
-
def next_week(
|
116
|
-
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(
|
112
|
+
# The +given_day_in_next_week+ defaults to the beginning of the week
|
113
|
+
# which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
|
114
|
+
# when set. +DateTime+ objects have their time set to 0:00.
|
115
|
+
def next_week(given_day_in_next_week = Date.beginning_of_week)
|
116
|
+
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)) }
|
117
117
|
end
|
118
118
|
|
119
119
|
# Short-hand for months_since(1).
|
@@ -1,8 +1,10 @@
|
|
1
1
|
class Module
|
2
|
-
# Provides a delegate class method to easily expose contained objects'
|
3
|
-
# as your own.
|
4
|
-
#
|
5
|
-
#
|
2
|
+
# Provides a +delegate+ class method to easily expose contained objects'
|
3
|
+
# public methods as your own.
|
4
|
+
#
|
5
|
+
# The macro receives one or more method names (specified as symbols or
|
6
|
+
# strings) and the name of the target object via the <tt>:to</tt> option
|
7
|
+
# (also a symbol or string).
|
6
8
|
#
|
7
9
|
# Delegation is particularly useful with Active Record associations:
|
8
10
|
#
|
@@ -89,29 +91,44 @@ class Module
|
|
89
91
|
# invoice.customer_name # => 'John Doe'
|
90
92
|
# invoice.customer_address # => 'Vimmersvej 13'
|
91
93
|
#
|
92
|
-
# If the
|
93
|
-
#
|
94
|
-
#
|
94
|
+
# If the target is +nil+ and does not respond to the delegated method a
|
95
|
+
# +NoMethodError+ is raised, as with any other value. Sometimes, however, it
|
96
|
+
# makes sense to be robust to that situation and that is the purpose of the
|
97
|
+
# <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
|
98
|
+
# responds to the method, everything works as usual. But if it is +nil+ and
|
99
|
+
# does not respond to the delegated method, +nil+ is returned.
|
95
100
|
#
|
96
|
-
# class
|
97
|
-
#
|
98
|
-
#
|
99
|
-
# @bar = bar
|
100
|
-
# end
|
101
|
-
# delegate :zoo, to: :bar
|
101
|
+
# class User < ActiveRecord::Base
|
102
|
+
# has_one :profile
|
103
|
+
# delegate :age, to: :profile
|
102
104
|
# end
|
103
105
|
#
|
104
|
-
#
|
106
|
+
# User.new.age # raises NoMethodError: undefined method `age'
|
107
|
+
#
|
108
|
+
# But if not having a profile yet is fine and should not be an error
|
109
|
+
# condition:
|
110
|
+
#
|
111
|
+
# class User < ActiveRecord::Base
|
112
|
+
# has_one :profile
|
113
|
+
# delegate :age, to: :profile, allow_nil: true
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# User.new.age # nil
|
117
|
+
#
|
118
|
+
# Note that if the target is not +nil+ then the call is attempted regardless of the
|
119
|
+
# <tt>:allow_nil</tt> option, and thus an exception is still raised if said object
|
120
|
+
# does not respond to the method:
|
105
121
|
#
|
106
122
|
# class Foo
|
107
|
-
#
|
108
|
-
# def initialize(bar = nil)
|
123
|
+
# def initialize(bar)
|
109
124
|
# @bar = bar
|
110
125
|
# end
|
111
|
-
#
|
126
|
+
#
|
127
|
+
# delegate :name, to: :@bar, allow_nil: true
|
112
128
|
# end
|
113
129
|
#
|
114
|
-
# Foo.new.
|
130
|
+
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
|
131
|
+
#
|
115
132
|
def delegate(*methods)
|
116
133
|
options = methods.pop
|
117
134
|
unless options.is_a?(Hash) && to = options[:to]
|
@@ -142,22 +159,31 @@ class Module
|
|
142
159
|
# methods still accept two arguments.
|
143
160
|
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
|
144
161
|
|
162
|
+
# The following generated methods call the target exactly once, storing
|
163
|
+
# the returned value in a dummy variable.
|
164
|
+
#
|
165
|
+
# Reason is twofold: On one hand doing less calls is in general better.
|
166
|
+
# On the other hand it could be that the target has side-effects,
|
167
|
+
# whereas conceptualy, from the user point of view, the delegator should
|
168
|
+
# be doing one call.
|
145
169
|
if allow_nil
|
146
|
-
module_eval(<<-EOS, file, line -
|
170
|
+
module_eval(<<-EOS, file, line - 3)
|
147
171
|
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
|
148
|
-
|
149
|
-
|
172
|
+
_ = #{to} # _ = client
|
173
|
+
if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
|
174
|
+
_.#{method}(#{definition}) # _.name(*args, &block)
|
150
175
|
end # end
|
151
176
|
end # end
|
152
177
|
EOS
|
153
178
|
else
|
154
179
|
exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
|
155
180
|
|
156
|
-
module_eval(<<-EOS, file, line -
|
181
|
+
module_eval(<<-EOS, file, line - 2)
|
157
182
|
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
|
158
|
-
#{to}
|
183
|
+
_ = #{to} # _ = client
|
184
|
+
_.#{method}(#{definition}) # _.name(*args, &block)
|
159
185
|
rescue NoMethodError # rescue NoMethodError
|
160
|
-
if
|
186
|
+
if _.nil? # if _.nil?
|
161
187
|
#{exception} # # add helpful message to the exception
|
162
188
|
else # else
|
163
189
|
raise # raise
|
@@ -14,8 +14,8 @@ class Module
|
|
14
14
|
# method where you can implement your custom warning behavior.
|
15
15
|
#
|
16
16
|
# class MyLib::Deprecator
|
17
|
-
# def deprecation_warning(deprecated_method_name, message, caller_backtrace)
|
18
|
-
# message = "#{
|
17
|
+
# def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
|
18
|
+
# message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
|
19
19
|
# Kernel.warn message
|
20
20
|
# end
|
21
21
|
# end
|
@@ -4,7 +4,6 @@ require 'active_support/core_ext/string/multibyte'
|
|
4
4
|
require 'active_support/core_ext/string/starts_ends_with'
|
5
5
|
require 'active_support/core_ext/string/inflections'
|
6
6
|
require 'active_support/core_ext/string/access'
|
7
|
-
require 'active_support/core_ext/string/xchar'
|
8
7
|
require 'active_support/core_ext/string/behavior'
|
9
8
|
require 'active_support/core_ext/string/output_safety'
|
10
9
|
require 'active_support/core_ext/string/exclude'
|
@@ -20,19 +20,17 @@ class String
|
|
20
20
|
return if parts.empty?
|
21
21
|
|
22
22
|
now = Time.now
|
23
|
-
|
24
|
-
utc_offset = form == :utc ? 0 : now.utc_offset
|
25
|
-
adjustment = offset ? offset - utc_offset : 0
|
26
|
-
|
27
|
-
Time.send(
|
28
|
-
form,
|
23
|
+
time = Time.new(
|
29
24
|
parts.fetch(:year, now.year),
|
30
25
|
parts.fetch(:mon, now.month),
|
31
26
|
parts.fetch(:mday, now.day),
|
32
27
|
parts.fetch(:hour, 0),
|
33
28
|
parts.fetch(:min, 0),
|
34
|
-
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0)
|
35
|
-
|
29
|
+
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
|
30
|
+
parts.fetch(:offset, form == :utc ? 0 : nil)
|
31
|
+
)
|
32
|
+
|
33
|
+
form == :utc ? time.utc : time.getlocal
|
36
34
|
end
|
37
35
|
|
38
36
|
# Converts a string to a Date value.
|