quetzall-cloud_cache 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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