quetzall-cloud_cache 1.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.
@@ -0,0 +1,48 @@
1
+ = cloud_cache
2
+
3
+ http://www.quetzall.com/
4
+
5
+ == DESCRIPTION:
6
+
7
+ Instant memcached, no servers required! See www.quetzall.com for more information.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ sudo gem install activesupport
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2009 FIX
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,228 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'net/http'
4
+ require 'base64'
5
+
6
+ $:.unshift(File.dirname(__FILE__))
7
+
8
+ begin
9
+ require 'digest/hmac'
10
+ USE_EMBEDDED_HMAC = false
11
+ rescue
12
+ puts "HMAC, not found in standard lib." + $!.message
13
+ require 'hmac-sha1'
14
+ USE_EMBEDDED_HMAC = true
15
+ end
16
+
17
+
18
+
19
+ module ActiveSupport
20
+ module Cache
21
+
22
+ class CloudCache < Store
23
+
24
+
25
+ attr_accessor :secret_key
26
+
27
+ def initialize(access_key, secret_key)
28
+ puts 'Creating new CloudCache'
29
+ @access_key = access_key
30
+ @secret_key = secret_key
31
+
32
+ end
33
+
34
+ def run_http(http_method, command_name, command_path, body=nil, parameters=nil, extra_headers=nil)
35
+ ts = generate_timestamp(Time.now.gmtime)
36
+ # puts 'timestamp = ' + ts
37
+ sig = generate_signature("CloudCache", command_name, ts, @secret_key)
38
+ # puts "My signature = " + sig
39
+ url = "http://cloudcache.ws/" + command_path
40
+ # puts url
41
+
42
+ user_agent = "CloudCache Ruby Client"
43
+ headers = {'User-Agent' => user_agent, 'signature' => sig, 'timestamp' => ts, 'akey' => @access_key}
44
+
45
+ if !extra_headers.nil?
46
+ extra_headers.each_pair do |k, v|
47
+ headers[k] = v
48
+ end
49
+ end
50
+
51
+
52
+ uri = URI.parse(url)
53
+ if (http_method == :put)
54
+ req = Net::HTTP::Put.new(uri.path)
55
+ req.body = body unless body.nil?
56
+ elsif (http_method == :post)
57
+ req = Net::HTTP::Post.new(uri.path)
58
+ if !parameters.nil?
59
+ req.set_form_data(parameters)
60
+ end
61
+ elsif (http_method == :delete)
62
+ req = Net::HTTP::Delete.new(uri.path)
63
+ if !parameters.nil?
64
+ req.set_form_data(parameters)
65
+ end
66
+ else
67
+ req = Net::HTTP::Get.new(uri.path)
68
+ end
69
+ headers.each_pair do |k, v|
70
+ req[k] = v
71
+ end
72
+ # req.each_header do |k, v|
73
+ # puts 'header ' + k + '=' + v
74
+ #end
75
+ res = Net::HTTP.start(uri.host, uri.port) do |http|
76
+ http.request(req)
77
+ end
78
+ case res
79
+ when Net::HTTPSuccess
80
+ # puts 'response body=' + res.body
81
+ res.body
82
+ else
83
+ res.error!
84
+ end
85
+
86
+ end
87
+
88
+ def auth()
89
+ command_name = "auth"
90
+ command_path = "auth"
91
+ run_http(:get, command_name, command_path)
92
+ end
93
+
94
+ def put(key, val, seconds_to_store=0, raw=false)
95
+ seconds_to_store = 0 if seconds_to_store.nil?
96
+ if raw
97
+ data = val.to_s
98
+ else
99
+ data = (Marshal.dump(val))
100
+ end
101
+ puts 'putting=' + data.to_s
102
+ extra_headers = seconds_to_store > 0 ? {"ttl"=>seconds_to_store} : nil
103
+ run_http(:put, "PUT", key, data, nil, extra_headers)
104
+ end
105
+
106
+
107
+ def get_multi(keys)
108
+ kj = keys.to_json
109
+ puts "keys.to_json = " + kj
110
+ extra_headers = {"keys" => kj }
111
+ puts "get_multi, extra_headers keys = " + extra_headers.keys.to_s
112
+ puts "get_multi, extra_headers vals = " + extra_headers.values.to_s
113
+ body = run_http(:get, "GET", "getmulti", nil, nil, extra_headers)
114
+ vals = ActiveSupport::JSON.decode body
115
+ vals
116
+ end
117
+
118
+ def get(key, raw=false)
119
+ begin
120
+ data = run_http(:get, "GET", key)
121
+ rescue Net::HTTPServerException
122
+ # puts $!.message
123
+ return nil if $!.message.include? "404"
124
+ raise $!
125
+ end
126
+ # puts 'data=' + data.to_s
127
+ if raw
128
+ return data
129
+ else
130
+ return Marshal.load((data))
131
+ end
132
+ end
133
+
134
+ def list_keys
135
+ body = run_http(:get, "listkeys", "listkeys")
136
+ keys = ActiveSupport::JSON.decode body # body[1..-2].split(',').collect! {|n| n.to_i}
137
+ keys
138
+ end
139
+
140
+ def stats
141
+ body = run_http(:get, "mystats", "mystats")
142
+ #keys = ActiveSupport::JSON.decode body # body[1..-2].split(',').collect! {|n| n.to_i}
143
+ body
144
+ end
145
+
146
+
147
+ def flush
148
+ body = run_http(:get, "flush", "flush")
149
+ body.strip
150
+ end
151
+
152
+ def clear
153
+ flush
154
+ end
155
+
156
+ def read(name, options={})
157
+ super
158
+ ret = get(name)
159
+ return ret
160
+ end
161
+
162
+ def write(name, value, options={})
163
+ super
164
+ put(name, value, options[:expires_in], options[:raw])
165
+ end
166
+
167
+ def delete(name, options = nil)
168
+ super
169
+ run_http(:delete, "DELETE", name)
170
+ end
171
+
172
+ def delete_matched(matcher, options = nil)
173
+ super
174
+ raise "delete_matched not yet supported by CloudCache"
175
+ end
176
+
177
+ def exist?(key, options = nil)
178
+ x = get(key)
179
+ r = true
180
+ if (x == nil)
181
+ r = false
182
+ end
183
+ r
184
+ end
185
+
186
+ def fetch(key, options = {})
187
+ if (options != {})
188
+ raise "Options on fetch() not yet supported by this library"
189
+ end
190
+ v = get(key)
191
+ v
192
+ end
193
+
194
+ def increment(key, val=1)
195
+ ret = run_http(:post, "POST", key + "/incr", nil, {"val"=>val})
196
+ ret.to_i
197
+ end
198
+
199
+ def decrement(key, val=1)
200
+ ret = run_http(:post, "POST", key + "/decr", nil, {"val"=>val})
201
+ ret.to_i
202
+ end
203
+
204
+ def silence!
205
+ super
206
+ end
207
+
208
+ def shutdown
209
+
210
+ end
211
+
212
+
213
+ def generate_timestamp(gmtime)
214
+ return gmtime.strftime("%Y-%m-%dT%H:%M:%SZ")
215
+ end
216
+
217
+ def generate_signature(service, operation, timestamp, secret_access_key)
218
+ if USE_EMBEDDED_HMAC
219
+ my_sha_hmac = HMAC::SHA1.digest(secret_access_key, service + operation + timestamp)
220
+ else
221
+ my_sha_hmac = Digest::HMAC.digest(service + operation + timestamp, secret_access_key, Digest::SHA1)
222
+ end
223
+ my_b64_hmac_digest = Base64.encode64(my_sha_hmac).strip
224
+ return my_b64_hmac_digest
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,11 @@
1
+ require 'hmac'
2
+ require 'digest/sha1'
3
+
4
+ module HMAC
5
+ class SHA1 < Base
6
+ def initialize(key = nil)
7
+ super(Digest::SHA1, 64, 20, key)
8
+ end
9
+ public_class_method :new, :digest, :hexdigest
10
+ end
11
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org>
2
+ # This library is distributed under the terms of the Ruby license.
3
+
4
+ # This module provides common interface to HMAC engines.
5
+ # HMAC standard is documented in RFC 2104:
6
+ #
7
+ # H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication",
8
+ # RFC 2104, February 1997
9
+ #
10
+ # These APIs are inspired by JCE 1.2's javax.crypto.Mac interface.
11
+ #
12
+ # <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html>
13
+
14
+ module HMAC
15
+ class Base
16
+ def initialize(algorithm, block_size, output_length, key)
17
+ @algorithm = algorithm
18
+ @block_size = block_size
19
+ @output_length = output_length
20
+ @status = STATUS_UNDEFINED
21
+ @key_xor_ipad = ''
22
+ @key_xor_opad = ''
23
+ set_key(key) unless key.nil?
24
+ end
25
+
26
+ private
27
+ def check_status
28
+ unless @status == STATUS_INITIALIZED
29
+ raise RuntimeError,
30
+ "The underlying hash algorithm has not yet been initialized."
31
+ end
32
+ end
33
+
34
+ public
35
+ def set_key(key)
36
+ # If key is longer than the block size, apply hash function
37
+ # to key and use the result as a real key.
38
+ key = @algorithm.digest(key) if key.size > @block_size
39
+ key_xor_ipad = "\x36" * @block_size
40
+ key_xor_opad = "\x5C" * @block_size
41
+ for i in 0 .. key.size - 1
42
+ key_xor_ipad[i] ^= key[i]
43
+ key_xor_opad[i] ^= key[i]
44
+ end
45
+ @key_xor_ipad = key_xor_ipad
46
+ @key_xor_opad = key_xor_opad
47
+ @md = @algorithm.new
48
+ @status = STATUS_INITIALIZED
49
+ end
50
+
51
+ def reset_key
52
+ @key_xor_ipad.gsub!(/./, '?')
53
+ @key_xor_opad.gsub!(/./, '?')
54
+ @key_xor_ipad[0..-1] = ''
55
+ @key_xor_opad[0..-1] = ''
56
+ @status = STATUS_UNDEFINED
57
+ end
58
+
59
+ def update(text)
60
+ check_status
61
+ # perform inner H
62
+ md = @algorithm.new
63
+ md.update(@key_xor_ipad)
64
+ md.update(text)
65
+ str = md.digest
66
+ # perform outer H
67
+ md = @algorithm.new
68
+ md.update(@key_xor_opad)
69
+ md.update(str)
70
+ @md = md
71
+ end
72
+ alias << update
73
+
74
+ def digest
75
+ check_status
76
+ @md.digest
77
+ end
78
+
79
+ def hexdigest
80
+ check_status
81
+ @md.hexdigest
82
+ end
83
+ alias to_s hexdigest
84
+
85
+ # These two class methods below are safer than using above
86
+ # instance methods combinatorially because an instance will have
87
+ # held a key even if it's no longer in use.
88
+ def Base.digest(key, text)
89
+ begin
90
+ hmac = self.new(key)
91
+ hmac.update(text)
92
+ hmac.digest
93
+ ensure
94
+ hmac.reset_key
95
+ end
96
+ end
97
+
98
+ def Base.hexdigest(key, text)
99
+ begin
100
+ hmac = self.new(key)
101
+ hmac.update(text)
102
+ hmac.hexdigest
103
+ ensure
104
+ hmac.reset_key
105
+ end
106
+ end
107
+
108
+ private_class_method :new, :digest, :hexdigest
109
+ end
110
+
111
+ STATUS_UNDEFINED, STATUS_INITIALIZED = 0, 1
112
+ end
@@ -0,0 +1,144 @@
1
+ require 'mini/test'
2
+ require '../lib/cloudcache'
3
+
4
+ #
5
+ # You'll need make a cloudcache.yml file in this directory that contains:
6
+ # amazon:
7
+ # access_key: ACCESS_KEY
8
+ # secret_key: SECRET
9
+ #
10
+ class CacheTests < Test::Unit::TestCase
11
+
12
+ def test_for_truth
13
+ assert true
14
+ end
15
+
16
+ def setup
17
+ puts("Setting up cache...")
18
+ props = nil
19
+ begin
20
+ props = YAML::load(File.read('cloudcache.yml'))
21
+ rescue
22
+ puts "Couldn't find cloudcache.yml file. " + $!.message
23
+ return
24
+ end
25
+ @cache = ActiveSupport::Cache::CloudCache.new("cloudcache-ruby-tests", props['amazon']['access_key'], props['amazon']['secret_key'])
26
+ end
27
+
28
+
29
+ def teardown
30
+ @cache.shutdown unless @cache.nil?
31
+ end
32
+
33
+ def test_auth
34
+ @cache.auth()
35
+ end
36
+
37
+ def test_bad_auth
38
+
39
+ temp = @cache.secret_key
40
+ @cache.secret_key = "badkey"
41
+
42
+ assert_raise Net::HTTPServerException do
43
+ test_basic_ops
44
+ end
45
+
46
+ @cache.secret_key = temp
47
+ end
48
+
49
+ def test_basic_ops
50
+ to_put = "I am a testing string. Take me apart and put me back together again."
51
+ @cache.put("s1", to_put, 0);
52
+
53
+ sleep(5)
54
+
55
+ response = @cache.get("s1")
56
+ assert_equal(to_put, response)
57
+
58
+ end
59
+
60
+ def test_not_exists
61
+ assert_nil @cache.get("does_not_exist")
62
+ end
63
+
64
+ def test_delete
65
+ to_put = "I am a testing string. Take me apart and put me back together again."
66
+ @cache.put("s1", to_put, 0)
67
+
68
+ sleep(5)
69
+
70
+ response = @cache.get("s1")
71
+ assert_equal(to_put, response)
72
+
73
+ @cache.delete("s1");
74
+
75
+ response = @cache.get("s1")
76
+ assert_nil(response)
77
+ end
78
+
79
+ def test_expiry
80
+ to_put = "I am a testing string. Take me apart and put me back together again."
81
+ @cache.put("s1", to_put, 5);
82
+
83
+ sleep(10)
84
+
85
+ response = @cache.get("s1")
86
+ assert_nil(response)
87
+ end
88
+
89
+ def test_list_keys
90
+ @cache.put("k1","v2")
91
+ sleep 1
92
+ keys = @cache.list_keys
93
+ puts("PRINTING KEYS:")
94
+ for key in keys
95
+ puts key
96
+ end
97
+ haskey = keys.index("k1")
98
+ assert_not_nil(haskey)
99
+ end
100
+
101
+ def test_counters
102
+ val = 0;
103
+ key = "counter1";
104
+ @cache.put(key, val, 50000, true);
105
+ 10.times do
106
+ val = @cache.increment(key)
107
+ end
108
+ assert_equal(10, val)
109
+
110
+ 10.times do
111
+ val = @cache.decrement(key)
112
+ end
113
+ assert_equal(0, val);
114
+
115
+ # One more to make sure it stays at 0
116
+ val = @cache.decrement(key);
117
+ assert_equal(0, val);
118
+
119
+ end
120
+
121
+ def test_flush
122
+ x = @cache.flush
123
+ assert_equal('[]', x)
124
+ end
125
+
126
+ def test_stats
127
+ x = @cache.stats
128
+ puts x
129
+ end
130
+ def test_get_multi
131
+ @cache.put("m1","v1")
132
+ @cache.put("m2","v2")
133
+
134
+ kz = Array["m1" , "m2", "m3"]
135
+ vz = @cache.get_multi(kz)
136
+
137
+ assert_equal("v1",vz["m1"]);
138
+ assert_equal("v2",vz["m2"]);
139
+ assert_nil(vz["m3"]);
140
+ end
141
+
142
+
143
+
144
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "cloud_cache"
3
+
4
+ class TestCloudCache < Test::Unit::TestCase
5
+ def test_sanity
6
+ flunk "write tests or I will kneecap you"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quetzall-cloud_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Travis Reeder
8
+ - Marc Byrd
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-06-23 00:00:00 -07:00
14
+ default_executable: cloud_cache
15
+ dependencies: []
16
+
17
+ description: Client library for Quetzall's CloudCache service.
18
+ email: travis@appoxy.com
19
+ executables:
20
+ - cloud_cache
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.txt
25
+ files:
26
+ - lib/cloud_cache.rb
27
+ - lib/hmac-sha1.rb
28
+ - lib/hmac.rb
29
+ - README.txt
30
+ has_rdoc: true
31
+ homepage: http://github.com/quetzall/cloud_cache/
32
+ post_install_message:
33
+ rdoc_options:
34
+ - --charset=UTF-8
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.2.0
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: Client library for Quetzall's CloudCache service.
56
+ test_files:
57
+ - test/cache_tests.rb
58
+ - test/test_cloud_cache.rb