activesupport 4.0.13 → 4.1.0.beta1
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 +283 -508
- data/README.rdoc +1 -1
- data/lib/active_support.rb +7 -1
- data/lib/active_support/backtrace_cleaner.rb +5 -5
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache.rb +62 -26
- data/lib/active_support/cache/file_store.rb +27 -22
- data/lib/active_support/cache/mem_cache_store.rb +2 -2
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +3 -0
- data/lib/active_support/callbacks.rb +416 -245
- data/lib/active_support/concern.rb +13 -5
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/array/access.rb +2 -0
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +24 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +5 -6
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +30 -11
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +12 -25
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/hash.rb +0 -1
- data/lib/active_support/core_ext/hash/conversions.rb +6 -3
- data/lib/active_support/core_ext/hash/deep_merge.rb +11 -22
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +27 -47
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -6
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +14 -4
- data/lib/active_support/core_ext/module/deprecation.rb +0 -2
- data/lib/active_support/core_ext/module/introspection.rb +0 -16
- data/lib/active_support/core_ext/module/method_transplanting.rb +11 -0
- data/lib/active_support/core_ext/numeric/time.rb +8 -0
- data/lib/active_support/core_ext/object.rb +1 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/inclusion.rb +4 -15
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_json.rb +4 -26
- data/lib/active_support/core_ext/object/to_param.rb +58 -1
- data/lib/active_support/core_ext/object/to_query.rb +7 -56
- data/lib/active_support/core_ext/object/try.rb +1 -1
- data/lib/active_support/core_ext/range/each.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +31 -31
- data/lib/active_support/core_ext/string/conversions.rb +9 -8
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +14 -4
- data/lib/active_support/core_ext/string/inflections.rb +11 -9
- data/lib/active_support/core_ext/string/output_safety.rb +65 -24
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +4 -4
- data/lib/active_support/core_ext/time/calculations.rb +10 -57
- data/lib/active_support/core_ext/time/conversions.rb +3 -1
- data/lib/active_support/core_ext/time/zones.rb +2 -21
- data/lib/active_support/dependencies.rb +29 -13
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/deprecation/behaviors.rb +3 -3
- data/lib/active_support/duration.rb +5 -7
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +4 -9
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +2 -6
- data/lib/active_support/inflections.rb +0 -1
- data/lib/active_support/inflector/inflections.rb +17 -17
- data/lib/active_support/inflector/methods.rb +34 -17
- data/lib/active_support/json/decoding.rb +14 -21
- data/lib/active_support/json/encoding.rb +113 -285
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber/test_helper.rb +1 -1
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/message_encryptor.rb +3 -3
- data/lib/active_support/message_verifier.rb +6 -1
- data/lib/active_support/multibyte/chars.rb +1 -2
- data/lib/active_support/multibyte/unicode.rb +27 -39
- data/lib/active_support/notifications.rb +3 -3
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/number_helper.rb +20 -311
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +62 -0
- data/lib/active_support/option_merger.rb +1 -1
- data/lib/active_support/ordered_hash.rb +0 -8
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +9 -8
- data/lib/active_support/subscriber.rb +26 -3
- data/lib/active_support/test_case.rb +9 -10
- data/lib/active_support/testing/assertions.rb +0 -30
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/declarative.rb +18 -8
- data/lib/active_support/testing/isolation.rb +13 -65
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +55 -0
- data/lib/active_support/time_with_zone.rb +4 -4
- data/lib/active_support/values/time_zone.rb +18 -15
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini.rb +2 -4
- metadata +71 -61
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
data/README.rdoc
CHANGED
@@ -14,7 +14,7 @@ The latest version of Active Support can be installed with RubyGems:
|
|
14
14
|
|
15
15
|
Source code can be downloaded as part of the Rails project on GitHub:
|
16
16
|
|
17
|
-
* https://github.com/rails/rails/tree/
|
17
|
+
* https://github.com/rails/rails/tree/master/activesupport
|
18
18
|
|
19
19
|
|
20
20
|
== License
|
data/lib/active_support.rb
CHANGED
@@ -39,7 +39,6 @@ module ActiveSupport
|
|
39
39
|
|
40
40
|
eager_autoload do
|
41
41
|
autoload :BacktraceCleaner
|
42
|
-
autoload :BasicObject
|
43
42
|
autoload :ProxyObject
|
44
43
|
autoload :Benchmarkable
|
45
44
|
autoload :Cache
|
@@ -53,6 +52,7 @@ module ActiveSupport
|
|
53
52
|
autoload :MessageEncryptor
|
54
53
|
autoload :MessageVerifier
|
55
54
|
autoload :Multibyte
|
55
|
+
autoload :NumberHelper
|
56
56
|
autoload :OptionMerger
|
57
57
|
autoload :OrderedHash
|
58
58
|
autoload :OrderedOptions
|
@@ -64,6 +64,12 @@ module ActiveSupport
|
|
64
64
|
autoload :Rescuable
|
65
65
|
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
|
66
66
|
autoload :TestCase
|
67
|
+
|
68
|
+
def self.eager_load!
|
69
|
+
super
|
70
|
+
|
71
|
+
NumberHelper.eager_load!
|
72
|
+
end
|
67
73
|
end
|
68
74
|
|
69
75
|
autoload :I18n, "active_support/i18n"
|
@@ -13,17 +13,17 @@ module ActiveSupport
|
|
13
13
|
# can focus on the rest.
|
14
14
|
#
|
15
15
|
# bc = BacktraceCleaner.new
|
16
|
-
# bc.add_filter { |line| line.gsub(Rails.root, '') }
|
17
|
-
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
|
18
|
-
# bc.clean(exception.backtrace) #
|
16
|
+
# bc.add_filter { |line| line.gsub(Rails.root, '') } # strip the Rails.root prefix
|
17
|
+
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # skip any lines from mongrel or rubygems
|
18
|
+
# bc.clean(exception.backtrace) # perform the cleanup
|
19
19
|
#
|
20
20
|
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
|
21
21
|
# and show as much data as possible, you can always call
|
22
22
|
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
|
23
23
|
# backtrace to a pristine state. If you need to reconfigure an existing
|
24
24
|
# BacktraceCleaner so that it does not filter or modify the paths of any lines
|
25
|
-
# of the backtrace, you can call BacktraceCleaner#remove_filters
|
26
|
-
# methods will give you a completely untouched backtrace.
|
25
|
+
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
|
26
|
+
# These two methods will give you a completely untouched backtrace.
|
27
27
|
#
|
28
28
|
# Inspired by the Quiet Backtrace gem by Thoughtbot.
|
29
29
|
class BacktraceCleaner
|
@@ -45,15 +45,5 @@ module ActiveSupport
|
|
45
45
|
yield
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
49
|
-
# Silence the logger during the execution of the block.
|
50
|
-
def silence
|
51
|
-
message = "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1."
|
52
|
-
ActiveSupport::Deprecation.warn message
|
53
|
-
old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
|
54
|
-
yield
|
55
|
-
ensure
|
56
|
-
logger.level = old_logger_level if logger
|
57
|
-
end
|
58
48
|
end
|
59
49
|
end
|
data/lib/active_support/cache.rb
CHANGED
@@ -3,7 +3,7 @@ require 'zlib'
|
|
3
3
|
require 'active_support/core_ext/array/extract_options'
|
4
4
|
require 'active_support/core_ext/array/wrap'
|
5
5
|
require 'active_support/core_ext/benchmark'
|
6
|
-
require 'active_support/core_ext/
|
6
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
7
7
|
require 'active_support/core_ext/numeric/bytes'
|
8
8
|
require 'active_support/core_ext/numeric/time'
|
9
9
|
require 'active_support/core_ext/object/to_param'
|
@@ -12,10 +12,10 @@ require 'active_support/core_ext/string/inflections'
|
|
12
12
|
module ActiveSupport
|
13
13
|
# See ActiveSupport::Cache::Store for documentation.
|
14
14
|
module Cache
|
15
|
-
autoload :FileStore,
|
16
|
-
autoload :MemoryStore,
|
15
|
+
autoload :FileStore, 'active_support/cache/file_store'
|
16
|
+
autoload :MemoryStore, 'active_support/cache/memory_store'
|
17
17
|
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
|
18
|
-
autoload :NullStore,
|
18
|
+
autoload :NullStore, 'active_support/cache/null_store'
|
19
19
|
|
20
20
|
# These options mean something to all cache implementations. Individual cache
|
21
21
|
# implementations may support additional options.
|
@@ -88,25 +88,24 @@ module ActiveSupport
|
|
88
88
|
end
|
89
89
|
|
90
90
|
private
|
91
|
+
def retrieve_cache_key(key)
|
92
|
+
case
|
93
|
+
when key.respond_to?(:cache_key) then key.cache_key
|
94
|
+
when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
|
95
|
+
when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
|
96
|
+
else key.to_param
|
97
|
+
end.to_s
|
98
|
+
end
|
91
99
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
# Obtains the specified cache store class, given the name of the +store+.
|
102
|
-
# Raises an error when the store class cannot be found.
|
103
|
-
def retrieve_store_class(store)
|
104
|
-
require "active_support/cache/#{store}"
|
105
|
-
rescue LoadError => e
|
106
|
-
raise "Could not find cache store adapter for #{store} (#{e})"
|
107
|
-
else
|
108
|
-
ActiveSupport::Cache.const_get(store.to_s.camelize)
|
109
|
-
end
|
100
|
+
# Obtains the specified cache store class, given the name of the +store+.
|
101
|
+
# Raises an error when the store class cannot be found.
|
102
|
+
def retrieve_store_class(store)
|
103
|
+
require "active_support/cache/#{store}"
|
104
|
+
rescue LoadError => e
|
105
|
+
raise "Could not find cache store adapter for #{store} (#{e})"
|
106
|
+
else
|
107
|
+
ActiveSupport::Cache.const_get(store.to_s.camelize)
|
108
|
+
end
|
110
109
|
end
|
111
110
|
|
112
111
|
# An abstract cache store class. There are multiple cache store
|
@@ -153,7 +152,6 @@ module ActiveSupport
|
|
153
152
|
# or +write+. To specify the threshold at which to compress values, set the
|
154
153
|
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
|
155
154
|
class Store
|
156
|
-
|
157
155
|
cattr_accessor :logger, :instance_writer => true
|
158
156
|
|
159
157
|
attr_reader :silence, :options
|
@@ -228,7 +226,7 @@ module ActiveSupport
|
|
228
226
|
#
|
229
227
|
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
230
228
|
# a cache entry is used very frequently and is under heavy load. If a
|
231
|
-
# cache expires and due to heavy load
|
229
|
+
# cache expires and due to heavy load several different processes will try
|
232
230
|
# to read data natively and then they all will try to write to cache. To
|
233
231
|
# avoid that case the first process to find an expired cache entry will
|
234
232
|
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
|
@@ -352,11 +350,40 @@ module ActiveSupport
|
|
352
350
|
results
|
353
351
|
end
|
354
352
|
|
353
|
+
# Fetches data from the cache, using the given keys. If there is data in
|
354
|
+
# the cache with the given keys, then that data is returned. Otherwise,
|
355
|
+
# the supplied block is called for each key for which there was no data,
|
356
|
+
# and the result will be written to the cache and returned.
|
357
|
+
#
|
358
|
+
# Options are passed to the underlying cache implementation.
|
359
|
+
#
|
360
|
+
# Returns an array with the data for each of the names. For example:
|
361
|
+
#
|
362
|
+
# cache.write("bim", "bam")
|
363
|
+
# cache.fetch_multi("bim", "boom") {|key| key * 2 }
|
364
|
+
# # => ["bam", "boomboom"]
|
365
|
+
#
|
366
|
+
def fetch_multi(*names)
|
367
|
+
options = names.extract_options!
|
368
|
+
options = merged_options(options)
|
369
|
+
|
370
|
+
results = read_multi(*names, options)
|
371
|
+
|
372
|
+
names.map do |name|
|
373
|
+
results.fetch(name) do
|
374
|
+
value = yield name
|
375
|
+
write(name, value, options)
|
376
|
+
value
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
355
381
|
# Writes the value to the cache, with the key.
|
356
382
|
#
|
357
383
|
# Options are passed to the underlying cache implementation.
|
358
384
|
def write(name, value, options = nil)
|
359
385
|
options = merged_options(options)
|
386
|
+
|
360
387
|
instrument(:write, name, options) do
|
361
388
|
entry = Entry.new(value, options)
|
362
389
|
write_entry(namespaced_key(name, options), entry, options)
|
@@ -368,6 +395,7 @@ module ActiveSupport
|
|
368
395
|
# Options are passed to the underlying cache implementation.
|
369
396
|
def delete(name, options = nil)
|
370
397
|
options = merged_options(options)
|
398
|
+
|
371
399
|
instrument(:delete, name) do
|
372
400
|
delete_entry(namespaced_key(name, options), options)
|
373
401
|
end
|
@@ -378,9 +406,10 @@ module ActiveSupport
|
|
378
406
|
# Options are passed to the underlying cache implementation.
|
379
407
|
def exist?(name, options = nil)
|
380
408
|
options = merged_options(options)
|
409
|
+
|
381
410
|
instrument(:exist?, name) do
|
382
411
|
entry = read_entry(namespaced_key(name, options), options)
|
383
|
-
entry && !entry.expired?
|
412
|
+
(entry && !entry.expired?) || false
|
384
413
|
end
|
385
414
|
end
|
386
415
|
|
@@ -557,6 +586,7 @@ module ActiveSupport
|
|
557
586
|
result = instrument(:generate, name, options) do |payload|
|
558
587
|
yield(name)
|
559
588
|
end
|
589
|
+
|
560
590
|
write(name, result, options)
|
561
591
|
result
|
562
592
|
end
|
@@ -580,6 +610,7 @@ module ActiveSupport
|
|
580
610
|
else
|
581
611
|
@value = value
|
582
612
|
end
|
613
|
+
|
583
614
|
@created_at = Time.now.to_f
|
584
615
|
@expires_in = options[:expires_in]
|
585
616
|
@expires_in = @expires_in.to_f if @expires_in
|
@@ -593,7 +624,7 @@ module ActiveSupport
|
|
593
624
|
# Check if the entry is expired. The +expires_in+ parameter can override
|
594
625
|
# the value set when the entry was created.
|
595
626
|
def expired?
|
596
|
-
convert_version_4beta1_entry! if defined?(@
|
627
|
+
convert_version_4beta1_entry! if defined?(@value)
|
597
628
|
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
598
629
|
end
|
599
630
|
|
@@ -630,6 +661,7 @@ module ActiveSupport
|
|
630
661
|
# serialize entries to protect against accidental cache modifications.
|
631
662
|
def dup_value!
|
632
663
|
convert_version_4beta1_entry! if defined?(@v)
|
664
|
+
|
633
665
|
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
634
666
|
if @value.is_a?(String)
|
635
667
|
@value = @value.dup
|
@@ -644,8 +676,10 @@ module ActiveSupport
|
|
644
676
|
if value && options[:compress]
|
645
677
|
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
|
646
678
|
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
|
679
|
+
|
647
680
|
return true if serialized_value_size >= compress_threshold
|
648
681
|
end
|
682
|
+
|
649
683
|
false
|
650
684
|
end
|
651
685
|
|
@@ -668,10 +702,12 @@ module ActiveSupport
|
|
668
702
|
@value = @v
|
669
703
|
remove_instance_variable(:@v)
|
670
704
|
end
|
705
|
+
|
671
706
|
if defined?(@c)
|
672
707
|
@compressed = @c
|
673
708
|
remove_instance_variable(:@c)
|
674
709
|
end
|
710
|
+
|
675
711
|
if defined?(@x) && @x
|
676
712
|
@created_at ||= Time.now.to_f
|
677
713
|
@expires_in = @x - @created_at
|
@@ -22,11 +22,15 @@ module ActiveSupport
|
|
22
22
|
extend Strategy::LocalCache
|
23
23
|
end
|
24
24
|
|
25
|
+
# Deletes all items from the cache. In this case it deletes all the entries in the specified
|
26
|
+
# file store directory except for .gitkeep. Be careful which directory is specified in your
|
27
|
+
# config file when using +FileStore+ because everything in that directory will be deleted.
|
25
28
|
def clear(options = nil)
|
26
29
|
root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
|
27
30
|
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
|
28
31
|
end
|
29
32
|
|
33
|
+
# Preemptively iterates through all stored keys and removes the ones which have expired.
|
30
34
|
def cleanup(options = nil)
|
31
35
|
options = merged_options(options)
|
32
36
|
search_dir(cache_path) do |fname|
|
@@ -36,32 +40,16 @@ module ActiveSupport
|
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
43
|
+
# Increments an already existing integer value that is stored in the cache.
|
44
|
+
# If the key is not found nothing is done.
|
39
45
|
def increment(name, amount = 1, options = nil)
|
40
|
-
|
41
|
-
lock_file(file_name) do
|
42
|
-
options = merged_options(options)
|
43
|
-
if num = read(name, options)
|
44
|
-
num = num.to_i + amount
|
45
|
-
write(name, num, options)
|
46
|
-
num
|
47
|
-
else
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
end
|
46
|
+
modify_value(name, amount, options)
|
51
47
|
end
|
52
48
|
|
49
|
+
# Decrements an already existing integer value that is stored in the cache.
|
50
|
+
# If the key is not found nothing is done.
|
53
51
|
def decrement(name, amount = 1, options = nil)
|
54
|
-
|
55
|
-
lock_file(file_name) do
|
56
|
-
options = merged_options(options)
|
57
|
-
if num = read(name, options)
|
58
|
-
num = num.to_i - amount
|
59
|
-
write(name, num, options)
|
60
|
-
num
|
61
|
-
else
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
end
|
52
|
+
modify_value(name, -amount, options)
|
65
53
|
end
|
66
54
|
|
67
55
|
def delete_matched(matcher, options = nil)
|
@@ -89,6 +77,7 @@ module ActiveSupport
|
|
89
77
|
|
90
78
|
def write_entry(key, entry, options)
|
91
79
|
file_name = key_file_path(key)
|
80
|
+
return false if options[:unless_exist] && File.exist?(file_name)
|
92
81
|
ensure_cache_path(File.dirname(file_name))
|
93
82
|
File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
|
94
83
|
true
|
@@ -175,6 +164,22 @@ module ActiveSupport
|
|
175
164
|
end
|
176
165
|
end
|
177
166
|
end
|
167
|
+
|
168
|
+
# Modifies the amount of an already existing integer value that is stored in the cache.
|
169
|
+
# If the key is not found nothing is done.
|
170
|
+
def modify_value(name, amount, options)
|
171
|
+
file_name = key_file_path(namespaced_key(name, options))
|
172
|
+
|
173
|
+
lock_file(file_name) do
|
174
|
+
options = merged_options(options)
|
175
|
+
|
176
|
+
if num = read(name, options)
|
177
|
+
num = num.to_i + amount
|
178
|
+
write(name, num, options)
|
179
|
+
num
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
178
183
|
end
|
179
184
|
end
|
180
185
|
end
|
@@ -87,7 +87,7 @@ module ActiveSupport
|
|
87
87
|
instrument(:increment, name, :amount => amount) do
|
88
88
|
@data.incr(escape_key(namespaced_key(name, options)), amount)
|
89
89
|
end
|
90
|
-
rescue Dalli::DalliError
|
90
|
+
rescue Dalli::DalliError
|
91
91
|
logger.error("DalliError (#{e}): #{e.message}") if logger
|
92
92
|
nil
|
93
93
|
end
|
@@ -101,7 +101,7 @@ module ActiveSupport
|
|
101
101
|
instrument(:decrement, name, :amount => amount) do
|
102
102
|
@data.decr(escape_key(namespaced_key(name, options)), amount)
|
103
103
|
end
|
104
|
-
rescue Dalli::DalliError
|
104
|
+
rescue Dalli::DalliError
|
105
105
|
logger.error("DalliError (#{e}): #{e.message}") if logger
|
106
106
|
nil
|
107
107
|
end
|
@@ -23,6 +23,9 @@ module ActiveSupport
|
|
23
23
|
def set_cache_for(local_cache_key, value)
|
24
24
|
@registry[local_cache_key] = value
|
25
25
|
end
|
26
|
+
|
27
|
+
def self.set_cache_for(l, v); instance.set_cache_for l, v; end
|
28
|
+
def self.cache_for(l); instance.cache_for l; end
|
26
29
|
end
|
27
30
|
|
28
31
|
# Simple memory backed cache. This cache is not thread safe and is intended only
|
@@ -1,9 +1,10 @@
|
|
1
|
-
require 'thread_safe'
|
2
1
|
require 'active_support/concern'
|
3
2
|
require 'active_support/descendants_tracker'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
4
|
require 'active_support/core_ext/class/attribute'
|
5
5
|
require 'active_support/core_ext/kernel/reporting'
|
6
6
|
require 'active_support/core_ext/kernel/singleton_class'
|
7
|
+
require 'thread'
|
7
8
|
|
8
9
|
module ActiveSupport
|
9
10
|
# Callbacks are code hooks that are run at key points in an object's life cycle.
|
@@ -76,171 +77,333 @@ module ActiveSupport
|
|
76
77
|
# save
|
77
78
|
# end
|
78
79
|
def run_callbacks(kind, &block)
|
79
|
-
|
80
|
-
|
80
|
+
cbs = send("_#{kind}_callbacks")
|
81
|
+
if cbs.empty?
|
82
|
+
yield if block_given?
|
83
|
+
else
|
84
|
+
runner = cbs.compile
|
85
|
+
e = Filters::Environment.new(self, false, nil, block)
|
86
|
+
runner.call(e).value
|
87
|
+
end
|
81
88
|
end
|
82
89
|
|
83
90
|
private
|
84
91
|
|
85
|
-
# A hook invoked
|
92
|
+
# A hook invoked every time a before callback is halted.
|
86
93
|
# This can be overridden in AS::Callback implementors in order
|
87
94
|
# to provide better debugging/logging.
|
88
95
|
def halted_callback_hook(filter)
|
89
96
|
end
|
90
97
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
98
|
+
module Conditionals # :nodoc:
|
99
|
+
class Value
|
100
|
+
def initialize(&block)
|
101
|
+
@block = block
|
102
|
+
end
|
103
|
+
def call(target, value); @block.call(value); end
|
104
|
+
end
|
105
|
+
end
|
95
106
|
|
96
|
-
|
97
|
-
|
98
|
-
deprecate_per_key_option(options)
|
99
|
-
normalize_options!(options)
|
107
|
+
module Filters
|
108
|
+
Environment = Struct.new(:target, :halted, :value, :run_block)
|
100
109
|
|
101
|
-
|
102
|
-
|
103
|
-
|
110
|
+
class End
|
111
|
+
def call(env)
|
112
|
+
block = env.run_block
|
113
|
+
env.value = !env.halted && (!block || block.call)
|
114
|
+
env
|
115
|
+
end
|
104
116
|
end
|
117
|
+
ENDING = End.new
|
105
118
|
|
106
|
-
|
107
|
-
|
108
|
-
|
119
|
+
class Before
|
120
|
+
def self.build(next_callback, user_callback, user_conditions, chain_config, filter)
|
121
|
+
halted_lambda = chain_config[:terminator]
|
122
|
+
|
123
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
124
|
+
halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
|
125
|
+
elsif chain_config.key? :terminator
|
126
|
+
halting(next_callback, user_callback, halted_lambda, filter)
|
127
|
+
elsif user_conditions.any?
|
128
|
+
conditional(next_callback, user_callback, user_conditions)
|
129
|
+
else
|
130
|
+
simple next_callback, user_callback
|
131
|
+
end
|
109
132
|
end
|
110
|
-
end
|
111
133
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
obj
|
120
|
-
end
|
134
|
+
private
|
135
|
+
|
136
|
+
def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
|
137
|
+
lambda { |env|
|
138
|
+
target = env.target
|
139
|
+
value = env.value
|
140
|
+
halted = env.halted
|
121
141
|
|
122
|
-
|
123
|
-
|
124
|
-
|
142
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
143
|
+
result = user_callback.call target, value
|
144
|
+
env.halted = halted_lambda.call(target, result)
|
145
|
+
if env.halted
|
146
|
+
target.send :halted_callback_hook, filter
|
147
|
+
end
|
148
|
+
end
|
149
|
+
next_callback.call env
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.halting(next_callback, user_callback, halted_lambda, filter)
|
154
|
+
lambda { |env|
|
155
|
+
target = env.target
|
156
|
+
value = env.value
|
157
|
+
halted = env.halted
|
158
|
+
|
159
|
+
unless halted
|
160
|
+
result = user_callback.call target, value
|
161
|
+
env.halted = halted_lambda.call(target, result)
|
162
|
+
if env.halted
|
163
|
+
target.send :halted_callback_hook, filter
|
164
|
+
end
|
165
|
+
end
|
166
|
+
next_callback.call env
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.conditional(next_callback, user_callback, user_conditions)
|
171
|
+
lambda { |env|
|
172
|
+
target = env.target
|
173
|
+
value = env.value
|
174
|
+
|
175
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
176
|
+
user_callback.call target, value
|
177
|
+
end
|
178
|
+
next_callback.call env
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.simple(next_callback, user_callback)
|
183
|
+
lambda { |env|
|
184
|
+
user_callback.call env.target, env.value
|
185
|
+
next_callback.call env
|
186
|
+
}
|
187
|
+
end
|
125
188
|
end
|
126
189
|
|
127
|
-
|
128
|
-
|
190
|
+
class After
|
191
|
+
def self.build(next_callback, user_callback, user_conditions, chain_config)
|
192
|
+
if chain_config[:skip_after_callbacks_if_terminated]
|
193
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
194
|
+
halting_and_conditional(next_callback, user_callback, user_conditions)
|
195
|
+
elsif chain_config.key?(:terminator)
|
196
|
+
halting(next_callback, user_callback)
|
197
|
+
elsif user_conditions.any?
|
198
|
+
conditional next_callback, user_callback, user_conditions
|
199
|
+
else
|
200
|
+
simple next_callback, user_callback
|
201
|
+
end
|
202
|
+
else
|
203
|
+
if user_conditions.any?
|
204
|
+
conditional next_callback, user_callback, user_conditions
|
205
|
+
else
|
206
|
+
simple next_callback, user_callback
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
|
214
|
+
lambda { |env|
|
215
|
+
env = next_callback.call env
|
216
|
+
target = env.target
|
217
|
+
value = env.value
|
218
|
+
halted = env.halted
|
219
|
+
|
220
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
221
|
+
user_callback.call target, value
|
222
|
+
end
|
223
|
+
env
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.halting(next_callback, user_callback)
|
228
|
+
lambda { |env|
|
229
|
+
env = next_callback.call env
|
230
|
+
unless env.halted
|
231
|
+
user_callback.call env.target, env.value
|
232
|
+
end
|
233
|
+
env
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.conditional(next_callback, user_callback, user_conditions)
|
238
|
+
lambda { |env|
|
239
|
+
env = next_callback.call env
|
240
|
+
target = env.target
|
241
|
+
value = env.value
|
242
|
+
|
243
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
244
|
+
user_callback.call target, value
|
245
|
+
end
|
246
|
+
env
|
247
|
+
}
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.simple(next_callback, user_callback)
|
251
|
+
lambda { |env|
|
252
|
+
env = next_callback.call env
|
253
|
+
user_callback.call env.target, env.value
|
254
|
+
env
|
255
|
+
}
|
256
|
+
end
|
129
257
|
end
|
130
258
|
|
131
|
-
|
132
|
-
|
259
|
+
class Around
|
260
|
+
def self.build(next_callback, user_callback, user_conditions, chain_config)
|
261
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
262
|
+
halting_and_conditional(next_callback, user_callback, user_conditions)
|
263
|
+
elsif chain_config.key? :terminator
|
264
|
+
halting(next_callback, user_callback)
|
265
|
+
elsif user_conditions.any?
|
266
|
+
conditional(next_callback, user_callback, user_conditions)
|
267
|
+
else
|
268
|
+
simple(next_callback, user_callback)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
|
275
|
+
lambda { |env|
|
276
|
+
target = env.target
|
277
|
+
value = env.value
|
278
|
+
halted = env.halted
|
279
|
+
|
280
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
281
|
+
user_callback.call(target, value) {
|
282
|
+
env = next_callback.call env
|
283
|
+
env.value
|
284
|
+
}
|
285
|
+
env
|
286
|
+
else
|
287
|
+
next_callback.call env
|
288
|
+
end
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
def self.halting(next_callback, user_callback)
|
293
|
+
lambda { |env|
|
294
|
+
target = env.target
|
295
|
+
value = env.value
|
296
|
+
|
297
|
+
unless env.halted
|
298
|
+
user_callback.call(target, value) {
|
299
|
+
env = next_callback.call env
|
300
|
+
env.value
|
301
|
+
}
|
302
|
+
env
|
303
|
+
else
|
304
|
+
next_callback.call env
|
305
|
+
end
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.conditional(next_callback, user_callback, user_conditions)
|
310
|
+
lambda { |env|
|
311
|
+
target = env.target
|
312
|
+
value = env.value
|
313
|
+
|
314
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
315
|
+
user_callback.call(target, value) {
|
316
|
+
env = next_callback.call env
|
317
|
+
env.value
|
318
|
+
}
|
319
|
+
env
|
320
|
+
else
|
321
|
+
next_callback.call env
|
322
|
+
end
|
323
|
+
}
|
324
|
+
end
|
325
|
+
|
326
|
+
def self.simple(next_callback, user_callback)
|
327
|
+
lambda { |env|
|
328
|
+
user_callback.call(env.target, env.value) {
|
329
|
+
env = next_callback.call env
|
330
|
+
env.value
|
331
|
+
}
|
332
|
+
env
|
333
|
+
}
|
334
|
+
end
|
133
335
|
end
|
336
|
+
end
|
134
337
|
|
135
|
-
|
136
|
-
|
338
|
+
class Callback #:nodoc:#
|
339
|
+
def self.build(chain, filter, kind, options)
|
340
|
+
new chain.name, filter, kind, options, chain.config
|
137
341
|
end
|
138
342
|
|
139
|
-
|
140
|
-
|
343
|
+
attr_accessor :kind, :name
|
344
|
+
attr_reader :chain_config
|
345
|
+
|
346
|
+
def initialize(name, filter, kind, options, chain_config)
|
347
|
+
@chain_config = chain_config
|
348
|
+
@name = name
|
349
|
+
@kind = kind
|
350
|
+
@filter = filter
|
351
|
+
@key = compute_identifier filter
|
352
|
+
@if = Array(options[:if])
|
353
|
+
@unless = Array(options[:unless])
|
141
354
|
end
|
142
355
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
356
|
+
def filter; @key; end
|
357
|
+
def raw_filter; @filter; end
|
358
|
+
|
359
|
+
def merge(chain, new_options)
|
360
|
+
options = {
|
361
|
+
:if => @if.dup,
|
362
|
+
:unless => @unless.dup
|
363
|
+
}
|
364
|
+
|
365
|
+
options[:if].concat Array(new_options.fetch(:unless, []))
|
366
|
+
options[:unless].concat Array(new_options.fetch(:if, []))
|
367
|
+
|
368
|
+
self.class.build chain, @filter, @kind, options
|
146
369
|
end
|
147
370
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
371
|
+
def matches?(_kind, _filter)
|
372
|
+
@kind == _kind && filter == _filter
|
373
|
+
end
|
151
374
|
|
152
|
-
|
375
|
+
def duplicates?(other)
|
376
|
+
case @filter
|
377
|
+
when Symbol, String
|
378
|
+
matches?(other.kind, other.filter)
|
379
|
+
else
|
380
|
+
false
|
381
|
+
end
|
153
382
|
end
|
154
383
|
|
155
384
|
# Wraps code with filter
|
156
|
-
def apply(
|
157
|
-
|
385
|
+
def apply(next_callback)
|
386
|
+
user_conditions = conditions_lambdas
|
387
|
+
user_callback = make_lambda @filter
|
388
|
+
|
389
|
+
case kind
|
158
390
|
when :before
|
159
|
-
|
160
|
-
if !halted && #{@compiled_options}
|
161
|
-
# This double assignment is to prevent warnings in 1.9.3 as
|
162
|
-
# the `result` variable is not always used except if the
|
163
|
-
# terminator code refers to it.
|
164
|
-
result = result = #{@filter}
|
165
|
-
halted = (#{chain.config[:terminator]})
|
166
|
-
if halted
|
167
|
-
halted_callback_hook(#{@raw_filter.inspect.inspect})
|
168
|
-
end
|
169
|
-
end
|
170
|
-
#{code}
|
171
|
-
RUBY_EVAL
|
391
|
+
Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter)
|
172
392
|
when :after
|
173
|
-
|
174
|
-
#{code}
|
175
|
-
if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
|
176
|
-
#{@filter}
|
177
|
-
end
|
178
|
-
RUBY_EVAL
|
393
|
+
Filters::After.build(next_callback, user_callback, user_conditions, chain_config)
|
179
394
|
when :around
|
180
|
-
|
181
|
-
<<-RUBY_EVAL
|
182
|
-
#{name}(halted) do
|
183
|
-
#{code}
|
184
|
-
value
|
185
|
-
end
|
186
|
-
RUBY_EVAL
|
395
|
+
Filters::Around.build(next_callback, user_callback, user_conditions, chain_config)
|
187
396
|
end
|
188
397
|
end
|
189
398
|
|
190
399
|
private
|
191
400
|
|
192
|
-
|
193
|
-
|
194
|
-
#
|
195
|
-
# For `set_callback :save, :around, :filter_name, if: :condition':
|
196
|
-
#
|
197
|
-
# def _conditional_callback_save_17
|
198
|
-
# if condition
|
199
|
-
# filter_name do
|
200
|
-
# yield self
|
201
|
-
# end
|
202
|
-
# else
|
203
|
-
# yield self
|
204
|
-
# end
|
205
|
-
# end
|
206
|
-
def define_conditional_callback
|
207
|
-
name = "_conditional_callback_#{@kind}_#{next_id}"
|
208
|
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
209
|
-
def #{name}(halted)
|
210
|
-
if #{@compiled_options} && !halted
|
211
|
-
#{@filter} do
|
212
|
-
yield self
|
213
|
-
end
|
214
|
-
else
|
215
|
-
yield self
|
216
|
-
end
|
217
|
-
end
|
218
|
-
RUBY_EVAL
|
219
|
-
name
|
220
|
-
end
|
221
|
-
|
222
|
-
# Options support the same options as filters themselves (and support
|
223
|
-
# symbols, string, procs, and objects), so compile a conditional
|
224
|
-
# expression based on the options.
|
225
|
-
def recompile_options!
|
226
|
-
conditions = ["true"]
|
227
|
-
|
228
|
-
unless options[:if].empty?
|
229
|
-
conditions << Array(_compile_filter(options[:if]))
|
230
|
-
end
|
231
|
-
|
232
|
-
unless options[:unless].empty?
|
233
|
-
conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
|
234
|
-
end
|
235
|
-
|
236
|
-
@compiled_options = conditions.flatten.join(" && ")
|
401
|
+
def invert_lambda(l)
|
402
|
+
lambda { |*args, &blk| !l.call(*args, &blk) }
|
237
403
|
end
|
238
404
|
|
239
405
|
# Filters support:
|
240
406
|
#
|
241
|
-
# Arrays:: Used in conditions. This is used to specify
|
242
|
-
# multiple conditions. Used internally to
|
243
|
-
# merge conditions from skip_* filters.
|
244
407
|
# Symbols:: A method to call.
|
245
408
|
# Strings:: Some content to evaluate.
|
246
409
|
# Procs:: A proc to call with the object.
|
@@ -249,87 +412,106 @@ module ActiveSupport
|
|
249
412
|
# All of these objects are compiled into methods and handled
|
250
413
|
# the same after this point:
|
251
414
|
#
|
252
|
-
# Arrays:: Merged together into a single filter.
|
253
415
|
# Symbols:: Already methods.
|
254
|
-
# Strings:: class_eval'
|
255
|
-
# Procs:: define_method
|
416
|
+
# Strings:: class_eval'd into methods.
|
417
|
+
# Procs:: using define_method compiled into methods.
|
256
418
|
# Objects::
|
257
419
|
# a method is created that calls the before_foo method
|
258
420
|
# on the object.
|
259
|
-
def
|
421
|
+
def make_lambda(filter)
|
260
422
|
case filter
|
261
|
-
when Array
|
262
|
-
filter.map {|f| _compile_filter(f)}
|
263
423
|
when Symbol
|
264
|
-
filter
|
424
|
+
lambda { |target, _, &blk| target.send filter, &blk }
|
265
425
|
when String
|
266
|
-
"
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
426
|
+
l = eval "lambda { |value| #{filter} }"
|
427
|
+
lambda { |target, value| target.instance_exec(value, &l) }
|
428
|
+
when Conditionals::Value then filter
|
429
|
+
when ::Proc
|
430
|
+
if filter.arity > 1
|
431
|
+
return lambda { |target, _, &block|
|
432
|
+
raise ArgumentError unless block
|
433
|
+
target.instance_exec(target, block, &filter)
|
434
|
+
}
|
435
|
+
end
|
271
436
|
|
272
|
-
|
437
|
+
if filter.arity <= 0
|
438
|
+
lambda { |target, _| target.instance_exec(&filter) }
|
439
|
+
else
|
440
|
+
lambda { |target, _| target.instance_exec(target, &filter) }
|
441
|
+
end
|
273
442
|
else
|
274
|
-
|
275
|
-
|
443
|
+
scopes = Array(chain_config[:scope])
|
444
|
+
method_to_call = scopes.map{ |s| public_send(s) }.join("_")
|
276
445
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
282
|
-
def #{method_name}(&blk)
|
283
|
-
#{method_name}_object.send(:#{method_to_call}, self, &blk)
|
284
|
-
end
|
285
|
-
RUBY_EVAL
|
286
|
-
|
287
|
-
method_name
|
446
|
+
lambda { |target, _, &blk|
|
447
|
+
filter.public_send method_to_call, target, &blk
|
448
|
+
}
|
288
449
|
end
|
289
450
|
end
|
290
451
|
|
291
|
-
def
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
filter
|
297
|
-
def #{kind}(context, &block) filter(context, &block) end
|
298
|
-
RUBY_EVAL
|
299
|
-
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
|
300
|
-
message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
|
301
|
-
ActiveSupport::Deprecation.warn message
|
302
|
-
def filter.around(context)
|
303
|
-
should_continue = before(context)
|
304
|
-
yield if should_continue
|
305
|
-
after(context)
|
306
|
-
end
|
452
|
+
def compute_identifier(filter)
|
453
|
+
case filter
|
454
|
+
when String, ::Proc
|
455
|
+
filter.object_id
|
456
|
+
else
|
457
|
+
filter
|
307
458
|
end
|
308
459
|
end
|
460
|
+
|
461
|
+
def conditions_lambdas
|
462
|
+
@if.map { |c| make_lambda c } +
|
463
|
+
@unless.map { |c| invert_lambda make_lambda c }
|
464
|
+
end
|
309
465
|
end
|
310
466
|
|
311
467
|
# An Array with a compile method.
|
312
|
-
class CallbackChain
|
468
|
+
class CallbackChain #:nodoc:#
|
469
|
+
include Enumerable
|
470
|
+
|
313
471
|
attr_reader :name, :config
|
314
472
|
|
315
473
|
def initialize(name, config)
|
316
474
|
@name = name
|
317
475
|
@config = {
|
318
|
-
:terminator => "false",
|
319
476
|
:scope => [ :kind ]
|
320
477
|
}.merge!(config)
|
478
|
+
@chain = []
|
479
|
+
@callbacks = nil
|
480
|
+
@mutex = Mutex.new
|
481
|
+
end
|
482
|
+
|
483
|
+
def each(&block); @chain.each(&block); end
|
484
|
+
def index(o); @chain.index(o); end
|
485
|
+
def empty?; @chain.empty?; end
|
486
|
+
|
487
|
+
def insert(index, o)
|
488
|
+
@callbacks = nil
|
489
|
+
@chain.insert(index, o)
|
490
|
+
end
|
491
|
+
|
492
|
+
def delete(o)
|
493
|
+
@callbacks = nil
|
494
|
+
@chain.delete(o)
|
495
|
+
end
|
496
|
+
|
497
|
+
def clear
|
498
|
+
@callbacks = nil
|
499
|
+
@chain.clear
|
500
|
+
self
|
501
|
+
end
|
502
|
+
|
503
|
+
def initialize_copy(other)
|
504
|
+
@callbacks = nil
|
505
|
+
@chain = other.chain.dup
|
506
|
+
@mutex = Mutex.new
|
321
507
|
end
|
322
508
|
|
323
509
|
def compile
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
510
|
+
@callbacks || @mutex.synchronize do
|
511
|
+
@callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback|
|
512
|
+
callback.apply chain
|
513
|
+
end
|
328
514
|
end
|
329
|
-
method << callbacks
|
330
|
-
|
331
|
-
method << "value"
|
332
|
-
method.join("\n")
|
333
515
|
end
|
334
516
|
|
335
517
|
def append(*callbacks)
|
@@ -340,69 +522,43 @@ module ActiveSupport
|
|
340
522
|
callbacks.each { |c| prepend_one(c) }
|
341
523
|
end
|
342
524
|
|
525
|
+
protected
|
526
|
+
def chain; @chain; end
|
527
|
+
|
343
528
|
private
|
344
529
|
|
345
530
|
def append_one(callback)
|
531
|
+
@callbacks = nil
|
346
532
|
remove_duplicates(callback)
|
347
|
-
push(callback)
|
533
|
+
@chain.push(callback)
|
348
534
|
end
|
349
535
|
|
350
536
|
def prepend_one(callback)
|
537
|
+
@callbacks = nil
|
351
538
|
remove_duplicates(callback)
|
352
|
-
unshift(callback)
|
539
|
+
@chain.unshift(callback)
|
353
540
|
end
|
354
541
|
|
355
542
|
def remove_duplicates(callback)
|
356
|
-
|
543
|
+
@callbacks = nil
|
544
|
+
@chain.delete_if { |c| callback.duplicates?(c) }
|
357
545
|
end
|
358
|
-
|
359
546
|
end
|
360
547
|
|
361
548
|
module ClassMethods
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
name = __callback_runner_name(kind)
|
368
|
-
unless object.respond_to?(name, true)
|
369
|
-
str = object.send("_#{kind}_callbacks").compile
|
370
|
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
371
|
-
def #{name}() #{str} end
|
372
|
-
protected :#{name}
|
373
|
-
RUBY_EVAL
|
374
|
-
end
|
375
|
-
name
|
376
|
-
end
|
377
|
-
|
378
|
-
def __reset_runner(symbol)
|
379
|
-
name = __callback_runner_name(symbol)
|
380
|
-
undef_method(name) if method_defined?(name)
|
381
|
-
end
|
382
|
-
|
383
|
-
def __callback_runner_name_cache
|
384
|
-
@__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) }
|
385
|
-
end
|
386
|
-
|
387
|
-
def __generate_callback_runner_name(kind)
|
388
|
-
"_run__#{self.name.hash.abs}__#{kind}__callbacks"
|
389
|
-
end
|
390
|
-
|
391
|
-
def __callback_runner_name(kind)
|
392
|
-
__callback_runner_name_cache[kind]
|
549
|
+
def normalize_callback_params(filters, block) # :nodoc:
|
550
|
+
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
551
|
+
options = filters.extract_options!
|
552
|
+
filters.unshift(block) if block
|
553
|
+
[type, filters, options.dup]
|
393
554
|
end
|
394
555
|
|
395
556
|
# This is used internally to append, prepend and skip callbacks to the
|
396
557
|
# CallbackChain.
|
397
|
-
def __update_callbacks(name
|
398
|
-
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
399
|
-
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
400
|
-
filters.unshift(block) if block
|
401
|
-
|
558
|
+
def __update_callbacks(name) #:nodoc:
|
402
559
|
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
|
403
|
-
chain = target.
|
404
|
-
yield target, chain.dup
|
405
|
-
target.__reset_runner(name)
|
560
|
+
chain = target.get_callbacks name
|
561
|
+
yield target, chain.dup
|
406
562
|
end
|
407
563
|
end
|
408
564
|
|
@@ -442,16 +598,15 @@ module ActiveSupport
|
|
442
598
|
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
|
443
599
|
# existing chain rather than appended.
|
444
600
|
def set_callback(name, *filter_list, &block)
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
end
|
601
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
602
|
+
self_chain = get_callbacks name
|
603
|
+
mapped = filters.map do |filter|
|
604
|
+
Callback.build(self_chain, filter, type, options)
|
605
|
+
end
|
451
606
|
|
607
|
+
__update_callbacks(name) do |target, chain|
|
452
608
|
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
|
453
|
-
|
454
|
-
target.send("_#{name}_callbacks=", chain)
|
609
|
+
target.set_callbacks name, chain
|
455
610
|
end
|
456
611
|
end
|
457
612
|
|
@@ -463,36 +618,34 @@ module ActiveSupport
|
|
463
618
|
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
|
464
619
|
# end
|
465
620
|
def skip_callback(name, *filter_list, &block)
|
466
|
-
|
621
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
622
|
+
|
623
|
+
__update_callbacks(name) do |target, chain|
|
467
624
|
filters.each do |filter|
|
468
625
|
filter = chain.find {|c| c.matches?(type, filter) }
|
469
626
|
|
470
627
|
if filter && options.any?
|
471
|
-
new_filter = filter.
|
628
|
+
new_filter = filter.merge(chain, options)
|
472
629
|
chain.insert(chain.index(filter), new_filter)
|
473
|
-
new_filter.recompile!(options)
|
474
630
|
end
|
475
631
|
|
476
632
|
chain.delete(filter)
|
477
633
|
end
|
478
|
-
target.
|
634
|
+
target.set_callbacks name, chain
|
479
635
|
end
|
480
636
|
end
|
481
637
|
|
482
638
|
# Remove all set callbacks for the given event.
|
483
|
-
def reset_callbacks(
|
484
|
-
callbacks =
|
639
|
+
def reset_callbacks(name)
|
640
|
+
callbacks = get_callbacks name
|
485
641
|
|
486
642
|
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
487
|
-
chain = target.
|
643
|
+
chain = target.get_callbacks(name).dup
|
488
644
|
callbacks.each { |c| chain.delete(c) }
|
489
|
-
target.
|
490
|
-
target.__reset_runner(symbol)
|
645
|
+
target.set_callbacks name, chain
|
491
646
|
end
|
492
647
|
|
493
|
-
self.
|
494
|
-
|
495
|
-
__reset_runner(symbol)
|
648
|
+
self.set_callbacks name, callbacks.dup.clear
|
496
649
|
end
|
497
650
|
|
498
651
|
# Define sets of events in the object life cycle that support callbacks.
|
@@ -504,10 +657,11 @@ module ActiveSupport
|
|
504
657
|
#
|
505
658
|
# * <tt>:terminator</tt> - Determines when a before filter will halt the
|
506
659
|
# callback chain, preventing following callbacks from being called and
|
507
|
-
# the event from being triggered. This
|
508
|
-
# result of the callback
|
660
|
+
# the event from being triggered. This should be a lambda to be executed.
|
661
|
+
# The current object and the return result of the callback will be called
|
662
|
+
# with the lambda.
|
509
663
|
#
|
510
|
-
# define_callbacks :validate, terminator:
|
664
|
+
# define_callbacks :validate, terminator: ->(target, result) { result == false }
|
511
665
|
#
|
512
666
|
# In this example, if any before validate callbacks returns +false+,
|
513
667
|
# other callbacks are not executed. Defaults to +false+, meaning no value
|
@@ -562,13 +716,30 @@ module ActiveSupport
|
|
562
716
|
# define_callbacks :save, scope: [:name]
|
563
717
|
#
|
564
718
|
# would call <tt>Audit#save</tt>.
|
565
|
-
def define_callbacks(*
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
719
|
+
def define_callbacks(*names)
|
720
|
+
options = names.extract_options!
|
721
|
+
if options.key?(:terminator) && String === options[:terminator]
|
722
|
+
ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda"
|
723
|
+
value = options[:terminator]
|
724
|
+
line = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__
|
725
|
+
options[:terminator] = lambda { |target, result| target.instance_exec(result, &line) }
|
726
|
+
end
|
727
|
+
|
728
|
+
names.each do |name|
|
729
|
+
class_attribute "_#{name}_callbacks"
|
730
|
+
set_callbacks name, CallbackChain.new(name, options)
|
570
731
|
end
|
571
732
|
end
|
733
|
+
|
734
|
+
protected
|
735
|
+
|
736
|
+
def get_callbacks(name)
|
737
|
+
send "_#{name}_callbacks"
|
738
|
+
end
|
739
|
+
|
740
|
+
def set_callbacks(name, callbacks)
|
741
|
+
send "_#{name}_callbacks=", callbacks
|
742
|
+
end
|
572
743
|
end
|
573
744
|
end
|
574
745
|
end
|