esod-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/EXAMPLES +140 -0
- data/GEM_RELEASE +19 -0
- data/README +10 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/esod-client.gemspec +118 -0
- data/esod-client.rb +35 -0
- data/lib/activesupport-2.2.2/CHANGELOG +1257 -0
- data/lib/activesupport-2.2.2/README +43 -0
- data/lib/activesupport-2.2.2/README.CFT +2 -0
- data/lib/activesupport-2.2.2/lib/active_support.rb +26 -0
- data/lib/activesupport-2.2.2/lib/active_support/base64.rb +33 -0
- data/lib/activesupport-2.2.2/lib/active_support/basic_object.rb +24 -0
- data/lib/activesupport-2.2.2/lib/active_support/buffered_logger.rb +122 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache.rb +223 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/compressed_mem_cache_store.rb +20 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/drb_store.rb +15 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/file_store.rb +72 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/mem_cache_store.rb +127 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/memory_store.rb +52 -0
- data/lib/activesupport-2.2.2/lib/active_support/cache/synchronized_memory_store.rb +47 -0
- data/lib/activesupport-2.2.2/lib/active_support/callbacks.rb +280 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext.rb +6 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash.rb +14 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/conversions.rb +259 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/deep_merge.rb +23 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/diff.rb +19 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/except.rb +25 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/indifferent_access.rb +137 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/keys.rb +52 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/reverse_merge.rb +35 -0
- data/lib/activesupport-2.2.2/lib/active_support/core_ext/hash/slice.rb +33 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor.rb +14 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder.rb +13 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb +115 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb +139 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +63 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb +328 -0
- data/lib/activesupport-2.2.2/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +1021 -0
- data/lib/activesupport-2.2.2/lib/activesupport.rb +1 -0
- data/lib/esodclient/esodclient.rb +22 -0
- data/lib/hash.rb +22 -0
- data/lib/mime-types-1.16/History.txt +107 -0
- data/lib/mime-types-1.16/Install.txt +17 -0
- data/lib/mime-types-1.16/Licence.txt +15 -0
- data/lib/mime-types-1.16/Manifest.txt +12 -0
- data/lib/mime-types-1.16/README.txt +28 -0
- data/lib/mime-types-1.16/Rakefile +316 -0
- data/lib/mime-types-1.16/lib/mime/types.rb +751 -0
- data/lib/mime-types-1.16/lib/mime/types.rb.data +1324 -0
- data/lib/mime-types-1.16/mime-types.gemspec +43 -0
- data/lib/mime-types-1.16/setup.rb +1585 -0
- data/lib/mime-types-1.16/test/test_mime_type.rb +356 -0
- data/lib/mime-types-1.16/test/test_mime_types.rb +122 -0
- data/lib/rest-client-1.2.0/README.rdoc +102 -0
- data/lib/rest-client-1.2.0/Rakefile +57 -0
- data/lib/rest-client-1.2.0/VERSION +1 -0
- data/lib/rest-client-1.2.0/bin/restclient +87 -0
- data/lib/rest-client-1.2.0/lib/rest_client.rb +2 -0
- data/lib/rest-client-1.2.0/lib/restclient.rb +108 -0
- data/lib/rest-client-1.2.0/lib/restclient/exceptions.rb +89 -0
- data/lib/rest-client-1.2.0/lib/restclient/mixin/response.rb +48 -0
- data/lib/rest-client-1.2.0/lib/restclient/net_http_ext.rb +21 -0
- data/lib/rest-client-1.2.0/lib/restclient/payload.rb +178 -0
- data/lib/rest-client-1.2.0/lib/restclient/raw_response.rb +30 -0
- data/lib/rest-client-1.2.0/lib/restclient/request.rb +287 -0
- data/lib/rest-client-1.2.0/lib/restclient/resource.rb +146 -0
- data/lib/rest-client-1.2.0/lib/restclient/response.rb +20 -0
- data/lib/rest-client-1.2.0/spec/base.rb +10 -0
- data/lib/rest-client-1.2.0/spec/exceptions_spec.rb +65 -0
- data/lib/rest-client-1.2.0/spec/master_shake.jpg +0 -0
- data/lib/rest-client-1.2.0/spec/mixin/response_spec.rb +46 -0
- data/lib/rest-client-1.2.0/spec/payload_spec.rb +131 -0
- data/lib/rest-client-1.2.0/spec/raw_response_spec.rb +17 -0
- data/lib/rest-client-1.2.0/spec/request_spec.rb +521 -0
- data/lib/rest-client-1.2.0/spec/resource_spec.rb +75 -0
- data/lib/rest-client-1.2.0/spec/response_spec.rb +21 -0
- data/lib/rest-client-1.2.0/spec/restclient_spec.rb +53 -0
- data/lib/trollop/trollop.rb +735 -0
- metadata +137 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveSupport
|
|
2
|
+
module Cache
|
|
3
|
+
class CompressedMemCacheStore < MemCacheStore
|
|
4
|
+
def read(name, options = nil)
|
|
5
|
+
if value = super(name, (options || {}).merge(:raw => true))
|
|
6
|
+
if raw?(options)
|
|
7
|
+
value
|
|
8
|
+
else
|
|
9
|
+
Marshal.load(ActiveSupport::Gzip.decompress(value))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def write(name, value, options = nil)
|
|
15
|
+
value = ActiveSupport::Gzip.compress(Marshal.dump(value)) unless raw?(options)
|
|
16
|
+
super(name, value, (options || {}).merge(:raw => true))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'drb'
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Cache
|
|
5
|
+
class DRbStore < MemoryStore #:nodoc:
|
|
6
|
+
attr_reader :address
|
|
7
|
+
|
|
8
|
+
def initialize(address = 'druby://localhost:9192')
|
|
9
|
+
super()
|
|
10
|
+
@address = address
|
|
11
|
+
@data = DRbObject.new(nil, address)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module ActiveSupport
|
|
2
|
+
module Cache
|
|
3
|
+
# A cache store implementation which stores everything on the filesystem.
|
|
4
|
+
class FileStore < Store
|
|
5
|
+
attr_reader :cache_path
|
|
6
|
+
|
|
7
|
+
def initialize(cache_path)
|
|
8
|
+
@cache_path = cache_path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def read(name, options = nil)
|
|
12
|
+
super
|
|
13
|
+
File.open(real_file_path(name), 'rb') { |f| Marshal.load(f) } rescue nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def write(name, value, options = nil)
|
|
17
|
+
super
|
|
18
|
+
ensure_cache_path(File.dirname(real_file_path(name)))
|
|
19
|
+
File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) }
|
|
20
|
+
value
|
|
21
|
+
rescue => e
|
|
22
|
+
logger.error "Couldn't create cache directory: #{name} (#{e.message})" if logger
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete(name, options = nil)
|
|
26
|
+
super
|
|
27
|
+
File.delete(real_file_path(name))
|
|
28
|
+
rescue SystemCallError => e
|
|
29
|
+
# If there's no cache, then there's nothing to complain about
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def delete_matched(matcher, options = nil)
|
|
33
|
+
super
|
|
34
|
+
search_dir(@cache_path) do |f|
|
|
35
|
+
if f =~ matcher
|
|
36
|
+
begin
|
|
37
|
+
File.delete(f)
|
|
38
|
+
rescue SystemCallError => e
|
|
39
|
+
# If there's no cache, then there's nothing to complain about
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def exist?(name, options = nil)
|
|
46
|
+
super
|
|
47
|
+
File.exist?(real_file_path(name))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
def real_file_path(name)
|
|
52
|
+
'%s/%s.cache' % [@cache_path, name.gsub('?', '.').gsub(':', '.')]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ensure_cache_path(path)
|
|
56
|
+
FileUtils.makedirs(path) unless File.exist?(path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def search_dir(dir, &callback)
|
|
60
|
+
Dir.foreach(dir) do |d|
|
|
61
|
+
next if d == "." || d == ".."
|
|
62
|
+
name = File.join(dir, d)
|
|
63
|
+
if File.directory?(name)
|
|
64
|
+
search_dir(name, &callback)
|
|
65
|
+
else
|
|
66
|
+
callback.call name
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'memcache'
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Cache
|
|
5
|
+
# A cache store implementation which stores data in Memcached:
|
|
6
|
+
# http://www.danga.com/memcached/
|
|
7
|
+
#
|
|
8
|
+
# This is currently the most popular cache store for production websites.
|
|
9
|
+
#
|
|
10
|
+
# Special features:
|
|
11
|
+
# - Clustering and load balancing. One can specify multiple memcached servers,
|
|
12
|
+
# and MemCacheStore will load balance between all available servers. If a
|
|
13
|
+
# server goes down, then MemCacheStore will ignore it until it goes back
|
|
14
|
+
# online.
|
|
15
|
+
# - Time-based expiry support. See #write and the +:expires_in+ option.
|
|
16
|
+
class MemCacheStore < Store
|
|
17
|
+
module Response # :nodoc:
|
|
18
|
+
STORED = "STORED\r\n"
|
|
19
|
+
NOT_STORED = "NOT_STORED\r\n"
|
|
20
|
+
EXISTS = "EXISTS\r\n"
|
|
21
|
+
NOT_FOUND = "NOT_FOUND\r\n"
|
|
22
|
+
DELETED = "DELETED\r\n"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :addresses
|
|
26
|
+
|
|
27
|
+
# Creates a new MemCacheStore object, with the given memcached server
|
|
28
|
+
# addresses. Each address is either a host name, or a host-with-port string
|
|
29
|
+
# in the form of "host_name:port". For example:
|
|
30
|
+
#
|
|
31
|
+
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
|
32
|
+
#
|
|
33
|
+
# If no addresses are specified, then MemCacheStore will connect to
|
|
34
|
+
# localhost port 11211 (the default memcached port).
|
|
35
|
+
def initialize(*addresses)
|
|
36
|
+
addresses = addresses.flatten
|
|
37
|
+
options = addresses.extract_options!
|
|
38
|
+
addresses = ["localhost"] if addresses.empty?
|
|
39
|
+
@addresses = addresses
|
|
40
|
+
@data = MemCache.new(addresses, options)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read(key, options = nil) # :nodoc:
|
|
44
|
+
super
|
|
45
|
+
@data.get(key, raw?(options))
|
|
46
|
+
rescue MemCache::MemCacheError => e
|
|
47
|
+
logger.error("MemCacheError (#{e}): #{e.message}")
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Writes a value to the cache.
|
|
52
|
+
#
|
|
53
|
+
# Possible options:
|
|
54
|
+
# - +:unless_exist+ - set to true if you don't want to update the cache
|
|
55
|
+
# if the key is already set.
|
|
56
|
+
# - +:expires_in+ - the number of seconds that this value may stay in
|
|
57
|
+
# the cache. See ActiveSupport::Cache::Store#write for an example.
|
|
58
|
+
def write(key, value, options = nil)
|
|
59
|
+
super
|
|
60
|
+
method = options && options[:unless_exist] ? :add : :set
|
|
61
|
+
# memcache-client will break the connection if you send it an integer
|
|
62
|
+
# in raw mode, so we convert it to a string to be sure it continues working.
|
|
63
|
+
value = value.to_s if raw?(options)
|
|
64
|
+
response = @data.send(method, key, value, expires_in(options), raw?(options))
|
|
65
|
+
response == Response::STORED
|
|
66
|
+
rescue MemCache::MemCacheError => e
|
|
67
|
+
logger.error("MemCacheError (#{e}): #{e.message}")
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete(key, options = nil) # :nodoc:
|
|
72
|
+
super
|
|
73
|
+
response = @data.delete(key, expires_in(options))
|
|
74
|
+
response == Response::DELETED
|
|
75
|
+
rescue MemCache::MemCacheError => e
|
|
76
|
+
logger.error("MemCacheError (#{e}): #{e.message}")
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def exist?(key, options = nil) # :nodoc:
|
|
81
|
+
# Doesn't call super, cause exist? in memcache is in fact a read
|
|
82
|
+
# But who cares? Reading is very fast anyway
|
|
83
|
+
!read(key, options).nil?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def increment(key, amount = 1) # :nodoc:
|
|
87
|
+
log("incrementing", key, amount)
|
|
88
|
+
|
|
89
|
+
response = @data.incr(key, amount)
|
|
90
|
+
response == Response::NOT_FOUND ? nil : response
|
|
91
|
+
rescue MemCache::MemCacheError
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def decrement(key, amount = 1) # :nodoc:
|
|
96
|
+
log("decrement", key, amount)
|
|
97
|
+
|
|
98
|
+
response = @data.decr(key, amount)
|
|
99
|
+
response == Response::NOT_FOUND ? nil : response
|
|
100
|
+
rescue MemCache::MemCacheError
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def delete_matched(matcher, options = nil) # :nodoc:
|
|
105
|
+
super
|
|
106
|
+
raise "Not supported by Memcache"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def clear
|
|
110
|
+
@data.flush_all
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def stats
|
|
114
|
+
@data.stats
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
def expires_in(options)
|
|
119
|
+
(options && options[:expires_in]) || 0
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def raw?(options)
|
|
123
|
+
options && options[:raw]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module ActiveSupport
|
|
2
|
+
module Cache
|
|
3
|
+
# A cache store implementation which stores everything into memory in the
|
|
4
|
+
# same process. If you're running multiple Ruby on Rails server processes
|
|
5
|
+
# (which is the case if you're using mongrel_cluster or Phusion Passenger),
|
|
6
|
+
# then this means that your Rails server process instances won't be able
|
|
7
|
+
# to share cache data with each other. If your application never performs
|
|
8
|
+
# manual cache item expiry (e.g. when you're using generational cache keys),
|
|
9
|
+
# then using MemoryStore is ok. Otherwise, consider carefully whether you
|
|
10
|
+
# should be using this cache store.
|
|
11
|
+
#
|
|
12
|
+
# MemoryStore is not only able to store strings, but also arbitrary Ruby
|
|
13
|
+
# objects.
|
|
14
|
+
#
|
|
15
|
+
# MemoryStore is not thread-safe. Use SynchronizedMemoryStore instead
|
|
16
|
+
# if you need thread-safety.
|
|
17
|
+
class MemoryStore < Store
|
|
18
|
+
def initialize
|
|
19
|
+
@data = {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def read(name, options = nil)
|
|
23
|
+
super
|
|
24
|
+
@data[name]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def write(name, value, options = nil)
|
|
28
|
+
super
|
|
29
|
+
@data[name] = value.freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def delete(name, options = nil)
|
|
33
|
+
super
|
|
34
|
+
@data.delete(name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def delete_matched(matcher, options = nil)
|
|
38
|
+
super
|
|
39
|
+
@data.delete_if { |k,v| k =~ matcher }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def exist?(name,options = nil)
|
|
43
|
+
super
|
|
44
|
+
@data.has_key?(name)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def clear
|
|
48
|
+
@data.clear
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module ActiveSupport
|
|
2
|
+
module Cache
|
|
3
|
+
# Like MemoryStore, but thread-safe.
|
|
4
|
+
class SynchronizedMemoryStore < MemoryStore
|
|
5
|
+
def initialize
|
|
6
|
+
super
|
|
7
|
+
@guard = Monitor.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def fetch(key, options = {})
|
|
11
|
+
@guard.synchronize { super }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(name, options = nil)
|
|
15
|
+
@guard.synchronize { super }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def write(name, value, options = nil)
|
|
19
|
+
@guard.synchronize { super }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete(name, options = nil)
|
|
23
|
+
@guard.synchronize { super }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete_matched(matcher, options = nil)
|
|
27
|
+
@guard.synchronize { super }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def exist?(name,options = nil)
|
|
31
|
+
@guard.synchronize { super }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def increment(key, amount = 1)
|
|
35
|
+
@guard.synchronize { super }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def decrement(key, amount = 1)
|
|
39
|
+
@guard.synchronize { super }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def clear
|
|
43
|
+
@guard.synchronize { super }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
module ActiveSupport
|
|
2
|
+
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
|
|
3
|
+
# before or after an alteration of the object state.
|
|
4
|
+
#
|
|
5
|
+
# Mixing in this module allows you to define callbacks in your class.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# class Storage
|
|
9
|
+
# include ActiveSupport::Callbacks
|
|
10
|
+
#
|
|
11
|
+
# define_callbacks :before_save, :after_save
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# class ConfigStorage < Storage
|
|
15
|
+
# before_save :saving_message
|
|
16
|
+
# def saving_message
|
|
17
|
+
# puts "saving..."
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# after_save do |object|
|
|
21
|
+
# puts "saved"
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# def save
|
|
25
|
+
# run_callbacks(:before_save)
|
|
26
|
+
# puts "- save"
|
|
27
|
+
# run_callbacks(:after_save)
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# config = ConfigStorage.new
|
|
32
|
+
# config.save
|
|
33
|
+
#
|
|
34
|
+
# Output:
|
|
35
|
+
# saving...
|
|
36
|
+
# - save
|
|
37
|
+
# saved
|
|
38
|
+
#
|
|
39
|
+
# Callbacks from parent classes are inherited.
|
|
40
|
+
#
|
|
41
|
+
# Example:
|
|
42
|
+
# class Storage
|
|
43
|
+
# include ActiveSupport::Callbacks
|
|
44
|
+
#
|
|
45
|
+
# define_callbacks :before_save, :after_save
|
|
46
|
+
#
|
|
47
|
+
# before_save :prepare
|
|
48
|
+
# def prepare
|
|
49
|
+
# puts "preparing save"
|
|
50
|
+
# end
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# class ConfigStorage < Storage
|
|
54
|
+
# before_save :saving_message
|
|
55
|
+
# def saving_message
|
|
56
|
+
# puts "saving..."
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# after_save do |object|
|
|
60
|
+
# puts "saved"
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# def save
|
|
64
|
+
# run_callbacks(:before_save)
|
|
65
|
+
# puts "- save"
|
|
66
|
+
# run_callbacks(:after_save)
|
|
67
|
+
# end
|
|
68
|
+
# end
|
|
69
|
+
#
|
|
70
|
+
# config = ConfigStorage.new
|
|
71
|
+
# config.save
|
|
72
|
+
#
|
|
73
|
+
# Output:
|
|
74
|
+
# preparing save
|
|
75
|
+
# saving...
|
|
76
|
+
# - save
|
|
77
|
+
# saved
|
|
78
|
+
module Callbacks
|
|
79
|
+
class CallbackChain < Array
|
|
80
|
+
def self.build(kind, *methods, &block)
|
|
81
|
+
methods, options = extract_options(*methods, &block)
|
|
82
|
+
methods.map! { |method| Callback.new(kind, method, options) }
|
|
83
|
+
new(methods)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run(object, options = {}, &terminator)
|
|
87
|
+
enumerator = options[:enumerator] || :each
|
|
88
|
+
|
|
89
|
+
unless block_given?
|
|
90
|
+
send(enumerator) { |callback| callback.call(object) }
|
|
91
|
+
else
|
|
92
|
+
send(enumerator) do |callback|
|
|
93
|
+
result = callback.call(object)
|
|
94
|
+
break result if terminator.call(result, object)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# TODO: Decompose into more Array like behavior
|
|
100
|
+
def replace_or_append!(chain)
|
|
101
|
+
if index = index(chain)
|
|
102
|
+
self[index] = chain
|
|
103
|
+
else
|
|
104
|
+
self << chain
|
|
105
|
+
end
|
|
106
|
+
self
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def find(callback, &block)
|
|
110
|
+
select { |c| c == callback && (!block_given? || yield(c)) }.first
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def delete(callback)
|
|
114
|
+
super(callback.is_a?(Callback) ? callback : find(callback))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
def self.extract_options(*methods, &block)
|
|
119
|
+
methods.flatten!
|
|
120
|
+
options = methods.extract_options!
|
|
121
|
+
methods << block if block_given?
|
|
122
|
+
return methods, options
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def extract_options(*methods, &block)
|
|
126
|
+
self.class.extract_options(*methods, &block)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class Callback
|
|
131
|
+
attr_reader :kind, :method, :identifier, :options
|
|
132
|
+
|
|
133
|
+
def initialize(kind, method, options = {})
|
|
134
|
+
@kind = kind
|
|
135
|
+
@method = method
|
|
136
|
+
@identifier = options[:identifier]
|
|
137
|
+
@options = options
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def ==(other)
|
|
141
|
+
case other
|
|
142
|
+
when Callback
|
|
143
|
+
(self.identifier && self.identifier == other.identifier) || self.method == other.method
|
|
144
|
+
else
|
|
145
|
+
(self.identifier && self.identifier == other) || self.method == other
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def eql?(other)
|
|
150
|
+
self == other
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def dup
|
|
154
|
+
self.class.new(@kind, @method, @options.dup)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def hash
|
|
158
|
+
if @identifier
|
|
159
|
+
@identifier.hash
|
|
160
|
+
else
|
|
161
|
+
@method.hash
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def call(*args, &block)
|
|
166
|
+
evaluate_method(method, *args, &block) if should_run_callback?(*args)
|
|
167
|
+
rescue LocalJumpError
|
|
168
|
+
raise ArgumentError,
|
|
169
|
+
"Cannot yield from a Proc type filter. The Proc must take two " +
|
|
170
|
+
"arguments and execute #call on the second argument."
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
def evaluate_method(method, *args, &block)
|
|
175
|
+
case method
|
|
176
|
+
when Symbol
|
|
177
|
+
object = args.shift
|
|
178
|
+
object.send(method, *args, &block)
|
|
179
|
+
when String
|
|
180
|
+
eval(method, args.first.instance_eval { binding })
|
|
181
|
+
when Proc, Method
|
|
182
|
+
method.call(*args, &block)
|
|
183
|
+
else
|
|
184
|
+
if method.respond_to?(kind)
|
|
185
|
+
method.send(kind, *args, &block)
|
|
186
|
+
else
|
|
187
|
+
raise ArgumentError,
|
|
188
|
+
"Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
|
|
189
|
+
"a block to be invoked, or an object responding to the callback method."
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def should_run_callback?(*args)
|
|
195
|
+
if options[:if]
|
|
196
|
+
evaluate_method(options[:if], *args)
|
|
197
|
+
elsif options[:unless]
|
|
198
|
+
!evaluate_method(options[:unless], *args)
|
|
199
|
+
else
|
|
200
|
+
true
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def self.included(base)
|
|
206
|
+
base.extend ClassMethods
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
module ClassMethods
|
|
210
|
+
def define_callbacks(*callbacks)
|
|
211
|
+
callbacks.each do |callback|
|
|
212
|
+
class_eval <<-"end_eval"
|
|
213
|
+
def self.#{callback}(*methods, &block)
|
|
214
|
+
callbacks = CallbackChain.build(:#{callback}, *methods, &block)
|
|
215
|
+
(@#{callback}_callbacks ||= CallbackChain.new).concat callbacks
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def self.#{callback}_callback_chain
|
|
219
|
+
@#{callback}_callbacks ||= CallbackChain.new
|
|
220
|
+
|
|
221
|
+
if superclass.respond_to?(:#{callback}_callback_chain)
|
|
222
|
+
CallbackChain.new(superclass.#{callback}_callback_chain + @#{callback}_callbacks)
|
|
223
|
+
else
|
|
224
|
+
@#{callback}_callbacks
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end_eval
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Runs all the callbacks defined for the given options.
|
|
233
|
+
#
|
|
234
|
+
# If a block is given it will be called after each callback receiving as arguments:
|
|
235
|
+
#
|
|
236
|
+
# * the result from the callback
|
|
237
|
+
# * the object which has the callback
|
|
238
|
+
#
|
|
239
|
+
# If the result from the block evaluates to false, the callback chain is stopped.
|
|
240
|
+
#
|
|
241
|
+
# Example:
|
|
242
|
+
# class Storage
|
|
243
|
+
# include ActiveSupport::Callbacks
|
|
244
|
+
#
|
|
245
|
+
# define_callbacks :before_save, :after_save
|
|
246
|
+
# end
|
|
247
|
+
#
|
|
248
|
+
# class ConfigStorage < Storage
|
|
249
|
+
# before_save :pass
|
|
250
|
+
# before_save :pass
|
|
251
|
+
# before_save :stop
|
|
252
|
+
# before_save :pass
|
|
253
|
+
#
|
|
254
|
+
# def pass
|
|
255
|
+
# puts "pass"
|
|
256
|
+
# end
|
|
257
|
+
#
|
|
258
|
+
# def stop
|
|
259
|
+
# puts "stop"
|
|
260
|
+
# return false
|
|
261
|
+
# end
|
|
262
|
+
#
|
|
263
|
+
# def save
|
|
264
|
+
# result = run_callbacks(:before_save) { |result, object| result == false }
|
|
265
|
+
# puts "- save" if result
|
|
266
|
+
# end
|
|
267
|
+
# end
|
|
268
|
+
#
|
|
269
|
+
# config = ConfigStorage.new
|
|
270
|
+
# config.save
|
|
271
|
+
#
|
|
272
|
+
# Output:
|
|
273
|
+
# pass
|
|
274
|
+
# pass
|
|
275
|
+
# stop
|
|
276
|
+
def run_callbacks(kind, options = {}, &block)
|
|
277
|
+
self.class.send("#{kind}_callback_chain").run(self, options, &block)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|