jruby-memcached 0.3.0 → 0.4.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,34 @@
1
+ ## 0.4.0 (Aug 16, 2012)
2
+
3
+ Bugfixes:
4
+
5
+ - set as daemon thread to avoid suspend ruby process (like rake task)
6
+
7
+ Features:
8
+
9
+ - support get with multiple keys
10
+ - add Memcached::Rails as rails cache_store
11
+ - use jruby annotation to reduce method definitions
12
+
13
+ ## 0.3.0 (Aug 7, 2012)
14
+
15
+ Features:
16
+
17
+ - rewrite with pure jruby implementation
18
+
19
+ ## 0.2.0 (Jul 29, 2012)
20
+
21
+ Bugfixes:
22
+
23
+ - set method should not be async
24
+
25
+ Features:
26
+
27
+ - allow to change hash, distribution and binary protocol
28
+
29
+ ## 0.1.0 (Jul 24, 2012)
30
+
31
+ Features:
32
+
33
+ - wrap java library spymemcached
34
+ - compatible hash and distribution algorithms with memcached.gem
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jruby-memcached (0.3.0)
4
+ jruby-memcached (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -35,11 +35,16 @@ You can get multiple values at once:
35
35
  $cache.get ['test', 'test2', 'missing']
36
36
  #=> {"test" => "hello", "test2" => "hello"}
37
37
 
38
+ ## Rails
39
+
40
+ # config/environment.rb
41
+ config.cache_store = Memcached::Rails.new(:servers => ['127.0.0.1'])
42
+
38
43
  ## Benchmarks
39
44
 
40
45
  memcached.gem is the fastest memcached gem in MRI,
41
46
  jruby-memcached is the fastest memcached gem in JRuby.
42
47
  See [benchmark][1]
43
48
 
44
- [0]:"https://github.com/evan/memcached"
45
- [1]:"https://github.com/aurorafeint/jruby-memcached/blob/master/benchmark.rb"
49
+ [0]: https://github.com/evan/memcached
50
+ [1]: https://github.com/aurorafeint/jruby-memcached/blob/master/benchmark.rb
@@ -1,5 +1,12 @@
1
1
  require 'java'
2
2
  require 'memcached/version'
3
3
  require File.join(File.dirname(__FILE__), '../target/spymemcached-ext-0.0.1.jar')
4
+ require 'com/openfeint/memcached/memcached'
4
5
 
5
- com.openfeint.memcached.MemcachedLibrary.new.load(JRuby.runtime, false)
6
+ class Memcached::Rails
7
+ attr_reader :logger
8
+
9
+ def logger=(logger)
10
+ @logger = logger
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  class Memcached
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,16 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
- com.openfeint.memcached.MemcachedLibrary.new.load(JRuby.runtime, false)
4
3
  describe Memcached do
5
4
  context "localhost" do
6
5
  before(:all) { @memcached = Memcached.new(["127.0.0.1:11211"]) }
7
6
  after(:all) { @memcached.shutdown }
8
7
 
9
8
  it "should get all servers" do
9
+ @memcached.set "foo", "bar"
10
10
  @memcached.servers.should == ["127.0.0.1:11211"]
11
11
  end
12
12
 
13
13
  context "initialize" do
14
+ it "should connect to 127.0.0.1:11211 if no server defined" do
15
+ memcached = Memcached.new
16
+ memcached.set "foo", "bar"
17
+ memcached.servers.should == ["127.0.0.1:11211"]
18
+ memcached.shutdown
19
+ end
20
+
14
21
  it "should raise error with unsupported option hash" do
15
22
  lambda { Memcached.new("127.0.0.1:11211", :hash => :unknown) }.should raise_error(Memcached::NotSupport)
16
23
  end
@@ -42,6 +49,24 @@ describe Memcached do
42
49
  @memcached.delete "key" rescue nil
43
50
  lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
44
51
  end
52
+
53
+ context "multiget" do
54
+ it "should get hash containing multiple key/value pairs" do
55
+ @memcached.set "key1", "value1"
56
+ @memcached.set "key2", "value2"
57
+ @memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
58
+ end
59
+
60
+ it "should get hash containing nil value" do
61
+ @memcached.set "key", nil, 0
62
+ @memcached.get(["key"]).should == {"key" => nil}
63
+ end
64
+
65
+ it "should get empty hash" do
66
+ @memcached.delete "key" rescue nil
67
+ @memcached.get(["key"]).should be_empty
68
+ end
69
+ end
45
70
  end
46
71
 
47
72
  context "set" do
@@ -99,6 +124,14 @@ describe Memcached do
99
124
  @memcached.delete "key" rescue nil
100
125
  lambda { @memcached.delete "key" }.should raise_error(Memcached::NotFound)
101
126
  end
127
+
128
+ #context "incr/decr" do
129
+ #it "should incr key" do
130
+ #@memcached.incr "intkey"
131
+ #@memcached.incr "intkey"
132
+ #@memcached.get("intkey").should == 1
133
+ #end
134
+ #end
102
135
  end
103
136
 
104
137
  context "flush" do
@@ -110,5 +143,50 @@ describe Memcached do
110
143
  lambda { @memcached.get "key2" }.should raise_error(Memcached::NotFound)
111
144
  end
112
145
  end
146
+
147
+ context "namespace/prefix_key" do
148
+ it "should get/set with namespace" do
149
+ memcached = Memcached.new("127.0.0.1:11211", :namespace => "jruby")
150
+ memcached.set "key", "value"
151
+ memcached.get("key").should == "value"
152
+ memcached.shutdown
153
+ @memcached.get("jrubykey").should == "value"
154
+ end
155
+
156
+ context "prefix_key" do
157
+ before(:all) { @prefix_memcached = Memcached.new("127.0.0.1:11211", :prefix_key => "jruby") }
158
+ after(:all) { @prefix_memcached.shutdown }
159
+
160
+ it "should get/set with prefix_key" do
161
+ @prefix_memcached.set "key", "value"
162
+ @prefix_memcached.get("key").should == "value"
163
+ @memcached.get("jrubykey").should == "value"
164
+ end
165
+
166
+ #it "should incr/decr with prefix_key" do
167
+ #@prefix_memcached.incr "intkey"
168
+ #@prefix_memcached.decr "intkey"
169
+ #@memcached.get("jrubyintkey").should == 0
170
+ #end
171
+
172
+ it "should add/replace with prefix_key" do
173
+ @prefix_memcached.add "newkey", "value"
174
+ @prefix_memcached.replace "newkey", "new_value"
175
+ @memcached.get("jrubynewkey").should == "new_value"
176
+ end
177
+
178
+ it "should delete with prefix_key" do
179
+ @prefix_memcached.set "key", "value"
180
+ @prefix_memcached.delete "key"
181
+ lambda { @memcached.get("jrubykey") }.should raise_error(Memcached::NotFound)
182
+ end
183
+
184
+ it "should multiget with prefix_key" do
185
+ @prefix_memcached.set "key1", "value1"
186
+ @prefix_memcached.set "key2", "value2"
187
+ @prefix_memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
188
+ end
189
+ end
190
+ end
113
191
  end
114
192
  end
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+
3
+ describe Memcached::Rails do
4
+ context "localhost" do
5
+ before(:all) { @memcached = Memcached::Rails.new(:servers => ["127.0.0.1:11211"]) }
6
+ after(:all) { @memcached.shutdown }
7
+
8
+ it "should get all servers" do
9
+ @memcached.set "foo", "bar"
10
+ @memcached.servers.should == ["127.0.0.1:11211"]
11
+ end
12
+
13
+ it "should get logger" do
14
+ require 'logger'
15
+ @memcached.logger = Logger.new(STDOUT)
16
+ @memcached.logger.should_not be_nil
17
+ end
18
+
19
+ it "should be active" do
20
+ @memcached.should be_active
21
+ end
22
+
23
+ context "get" do
24
+ it "should get value" do
25
+ @memcached.set "key", "value"
26
+ @memcached.get("key").should == "value"
27
+ end
28
+
29
+ it "should get nil if key is missing" do
30
+ @memcached.delete "key" rescue nil
31
+ @memcached.get("key").should be_nil
32
+ end
33
+ end
34
+
35
+ context "read" do
36
+ it "should get value" do
37
+ @memcached.set "key", "value"
38
+ @memcached.get("key").should == "value"
39
+ end
40
+
41
+ it "should get nil if key is missing" do
42
+ @memcached.delete "key" rescue nil
43
+ @memcached.get("key").should be_nil
44
+ end
45
+ end
46
+
47
+ context "exist?" do
48
+ it "should return true if key exists" do
49
+ @memcached.set "key", "value"
50
+ @memcached.exist?("key").should be_true
51
+ end
52
+
53
+ it "should return false if key is missing" do
54
+ @memcached.delete "key" rescue nil
55
+ @memcached.exist?("key").should be_false
56
+ end
57
+ end
58
+
59
+ context "get_multi" do
60
+ it "should get hash containing multiple key/value pairs" do
61
+ @memcached.set "key1", "value1"
62
+ @memcached.set "key2", "value2"
63
+ @memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
64
+ end
65
+
66
+ it "should get hash containing nil value" do
67
+ @memcached.set "key", nil, 0
68
+ @memcached.get(["key"]).should == {"key" => nil}
69
+ end
70
+
71
+ it "should get empty hash" do
72
+ @memcached.delete "key" rescue nil
73
+ @memcached.get(["key"]).should be_empty
74
+ end
75
+ end
76
+
77
+ context "set" do
78
+ it "should set successfully" do
79
+ @memcached.set("key", "value").should be_true
80
+ @memcached.get("key").should == "value"
81
+ end
82
+ end
83
+
84
+ context "write" do
85
+ it "should write successfully" do
86
+ @memcached.write("key", "value").should be_true
87
+ @memcached.read("key").should == "value"
88
+ end
89
+ end
90
+
91
+ context "fetch" do
92
+ it "should read if key exists" do
93
+ @memcached.write("key", "value")
94
+ @memcached.fetch("key") { "new value" }.should == "value"
95
+ end
96
+
97
+ it "should write if key is missing" do
98
+ @memcached.delete "key" rescue nil
99
+ @memcached.fetch("key") { "new value" }.should == "new value"
100
+ end
101
+ end
102
+
103
+ context "add" do
104
+ it "should add if key is missing" do
105
+ @memcached.delete "key" rescue nil
106
+ @memcached.add("key", "value").should be_true
107
+ end
108
+
109
+ it "should do nothing if key exists" do
110
+ @memcached.set "key", "value"
111
+ @memcached.add("key", "value").should be_false
112
+ end
113
+
114
+ context "with string_return_types" do
115
+ before(:all) { @string_memcached = Memcached::Rails.new("127.0.0.1:11211", :string_return_types => true) }
116
+ after(:all) { @string_memcached.quit }
117
+
118
+ it "should add if key is missing" do
119
+ @string_memcached.delete "key" rescue nil
120
+ @string_memcached.add("key", "value").should == "STORED\r\n"
121
+ end
122
+
123
+ it "should do nothing if key exists" do
124
+ @string_memcached.set "key", "value"
125
+ @string_memcached.add("key", "value").should == "NOT STORED\r\n"
126
+ end
127
+ end
128
+ end
129
+
130
+ context "delete" do
131
+ it "should delete existing key" do
132
+ @memcached.set "key", "value"
133
+ @memcached.delete "key"
134
+ @memcached.get("key").should be_nil
135
+ end
136
+
137
+ it "should do nothing if delete missing key" do
138
+ @memcached.delete "key" rescue nil
139
+ @memcached.delete "key"
140
+ @memcached.get("key").should be_nil
141
+ end
142
+ end
143
+
144
+ context "alias" do
145
+ it "should respond_to? flush_all" do
146
+ @memcached.should be_respond_to(:flush_all)
147
+ end
148
+
149
+ it "should respond_to? clear" do
150
+ @memcached.should be_respond_to(:clear)
151
+ end
152
+
153
+ it "should respond_to? :[]" do
154
+ @memcached.should be_respond_to(:"[]")
155
+ end
156
+
157
+ it "should respond_to? :[]=" do
158
+ @memcached.should be_respond_to(:"[]=")
159
+ end
160
+ end
161
+
162
+ context "read_multi" do
163
+ it "should read hash containing multiple key/value pairs" do
164
+ @memcached.write "key1", "value1"
165
+ @memcached.write "key2", "value2"
166
+ @memcached.read_multi(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
167
+ end
168
+
169
+ it "should read empty hash without params" do
170
+ @memcached.read_multi.should be_empty
171
+ end
172
+ end
173
+ end
174
+ end
@@ -1,15 +1,9 @@
1
1
  package com.openfeint.memcached;
2
2
 
3
- import java.io.IOException;
4
- import java.net.InetSocketAddress;
5
- import java.net.SocketAddress;
6
- import java.util.ArrayList;
7
- import java.util.Arrays;
8
- import java.util.List;
9
- import java.util.Map;
10
- import java.util.concurrent.ExecutionException;
3
+ import com.openfeint.memcached.error.Error;
4
+ import com.openfeint.memcached.transcoder.MarshalTranscoder;
5
+ import com.openfeint.memcached.transcoder.MarshalZlibTranscoder;
11
6
  import net.spy.memcached.AddrUtil;
12
- import net.spy.memcached.CachedData;
13
7
  import net.spy.memcached.ConnectionFactoryBuilder;
14
8
  import net.spy.memcached.ConnectionFactoryBuilder.Locator;
15
9
  import net.spy.memcached.ConnectionFactoryBuilder.Protocol;
@@ -17,127 +11,68 @@ import net.spy.memcached.DefaultHashAlgorithm;
17
11
  import net.spy.memcached.MemcachedClient;
18
12
  import net.spy.memcached.transcoders.Transcoder;
19
13
  import org.jruby.Ruby;
14
+ import org.jruby.RubyArray;
15
+ import org.jruby.RubyBoolean;
20
16
  import org.jruby.RubyClass;
21
- import org.jruby.RubyException;
22
17
  import org.jruby.RubyHash;
23
18
  import org.jruby.RubyObject;
19
+ import org.jruby.RubyString;
24
20
  import org.jruby.anno.JRubyClass;
25
21
  import org.jruby.anno.JRubyMethod;
26
- import org.jruby.exceptions.RaiseException;
27
22
  import org.jruby.runtime.ThreadContext;
28
23
  import org.jruby.runtime.builtin.IRubyObject;
29
24
 
30
- public class Memcached extends RubyObject {
31
- private Ruby ruby;
25
+ import java.io.IOException;
26
+ import java.net.InetSocketAddress;
27
+ import java.net.SocketAddress;
28
+ import java.util.ArrayList;
29
+ import java.util.List;
30
+ import java.util.Map;
31
+ import java.util.concurrent.ExecutionException;
32
32
 
33
+ @JRubyClass(name = "Memcached")
34
+ public class Memcached extends RubyObject {
33
35
  private MemcachedClient client;
34
36
 
35
37
  private Transcoder<IRubyObject> transcoder;
36
38
 
37
39
  private int ttl;
38
40
 
41
+ private String prefixKey;
42
+
39
43
  public Memcached(final Ruby ruby, RubyClass rubyClass) {
40
44
  super(ruby, rubyClass);
41
- this.ruby = ruby;
42
- }
43
45
 
44
- @JRubyMethod
45
- public IRubyObject initialize(ThreadContext context, IRubyObject servers) {
46
- return initialize(context, servers, context.nil);
46
+ ttl = 604800;
47
+ prefixKey = "";
47
48
  }
48
- @JRubyMethod
49
- public IRubyObject initialize(ThreadContext context, IRubyObject servers, IRubyObject options) {
50
- List<InetSocketAddress> addresses;
51
- if ("java.lang.String".equals(servers.getJavaClass().getName())) {
52
- addresses = AddrUtil.getAddresses(servers.convertToString().toString());
49
+
50
+ @JRubyMethod(name = "initialize", optional = 2)
51
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
52
+ Ruby ruby = context.getRuntime();
53
+ RubyHash options;
54
+ if (args.length > 1) {
55
+ options = args[1].convertToHash();
53
56
  } else {
54
- addresses = AddrUtil.getAddresses((List<String>)servers.convertToArray());
57
+ options = new RubyHash(ruby);
55
58
  }
56
- try {
57
- ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
58
-
59
- String distributionValue = null;
60
- String hashValue = null;
61
- String binaryValue = null;
62
- String defaultTTL = null;
63
- String transcoderValue = null;
64
- if (!options.isNil()) {
65
- RubyHash opts = options.convertToHash();
66
- if (opts.containsKey(ruby.newSymbol("distribution"))) {
67
- distributionValue = opts.get(ruby.newSymbol("distribution")).toString();
68
- }
69
- if (opts.containsKey(ruby.newSymbol("hash"))) {
70
- hashValue = opts.get(ruby.newSymbol("hash")).toString();
71
- }
72
- if (opts.containsKey(ruby.newSymbol("binary_protocol"))) {
73
- binaryValue = opts.get(ruby.newSymbol("binary_protocol")).toString();
74
- }
75
- if (opts.containsKey(ruby.newSymbol("default_ttl"))) {
76
- defaultTTL = opts.get(ruby.newSymbol("default_ttl")).toString();
77
- }
78
- if (opts.containsKey(ruby.newSymbol("transcoder"))) {
79
- transcoderValue = opts.get(ruby.newSymbol("transcoder")).toString();
80
- }
81
- }
82
-
83
- if (distributionValue == null) {
84
- distributionValue = "ketama";
59
+ List<String> servers = new ArrayList<String>();
60
+ if (args.length > 0) {
61
+ if (args[0] instanceof RubyString) {
62
+ servers.add(args[0].toString());
63
+ } else if (args[0] instanceof RubyArray) {
64
+ servers.addAll((List<String>) args[0].convertToArray());
85
65
  }
86
- if ("array_mod".equals(distributionValue)) {
87
- builder.setLocatorType(Locator.ARRAY_MOD);
88
- } else if ("ketama".equals(distributionValue) || "consistent_ketama".equals(distributionValue)) {
89
- builder.setLocatorType(Locator.CONSISTENT);
90
- } else {
91
- throw newNotSupport(ruby, "distribution not support");
92
- }
93
-
94
- if (hashValue == null) {
95
- hashValue = "fnv1_32";
96
- }
97
- if ("native".equals(hashValue)) {
98
- builder.setHashAlg(DefaultHashAlgorithm.NATIVE_HASH);
99
- } else if ("crc".equals(hashValue)) {
100
- builder.setHashAlg(DefaultHashAlgorithm.CRC_HASH);
101
- } else if ("fnv1_64".equals(hashValue)) {
102
- builder.setHashAlg(DefaultHashAlgorithm.FNV1_64_HASH);
103
- } else if ("fnv1a_64".equals(hashValue)) {
104
- builder.setHashAlg(DefaultHashAlgorithm.FNV1A_64_HASH);
105
- } else if ("fnv1_32".equals(hashValue)) {
106
- builder.setHashAlg(DefaultHashAlgorithm.FNV1_32_HASH);
107
- } else if ("fnv1a_32".equals(hashValue)) {
108
- builder.setHashAlg(DefaultHashAlgorithm.FNV1A_32_HASH);
109
- } else if ("ketama".equals(hashValue)) {
110
- builder.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH);
111
- } else {
112
- throw newNotSupport(ruby, "hash not support");
113
- }
114
-
115
- if ("true".equals(binaryValue)) {
116
- builder.setProtocol(Protocol.BINARY);
117
- }
118
-
119
- if (defaultTTL == null) {
120
- ttl = 604800;
121
- } else {
122
- ttl = Integer.parseInt(defaultTTL);
123
- }
124
-
125
- client = new MemcachedClient(builder.build(), addresses);
126
-
127
- if ("marshal_zlib".equals(transcoderValue)) {
128
- transcoder = new MarshalZlibTranscoder(ruby);
129
- } else {
130
- transcoder = new MarshalTranscoder(ruby);
131
- }
132
- } catch (IOException ioe) {
133
- throw context.runtime.newIOErrorFromException(ioe);
134
66
  }
135
-
136
- return context.nil;
67
+ if (servers.isEmpty()) {
68
+ servers.add("127.0.0.1:11211");
69
+ }
70
+ return init(context, servers, options);
137
71
  }
138
72
 
139
73
  @JRubyMethod
140
74
  public IRubyObject servers(ThreadContext context) {
75
+ Ruby ruby = context.getRuntime();
141
76
  List<IRubyObject> addresses = new ArrayList<IRubyObject>();
142
77
  for (SocketAddress address : client.getAvailableServers()) {
143
78
  String addressStr = address.toString();
@@ -149,136 +84,139 @@ public class Memcached extends RubyObject {
149
84
  return ruby.newArray(addresses);
150
85
  }
151
86
 
152
- @JRubyMethod
153
- public IRubyObject add(ThreadContext context, IRubyObject key, IRubyObject value) {
154
- return add(context, key, value, ruby.newFixnum(ttl));
155
- }
156
-
157
- @JRubyMethod
158
- public IRubyObject add(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
87
+ @JRubyMethod(name = "add", required = 2, optional = 3)
88
+ public IRubyObject add(ThreadContext context, IRubyObject[] args) {
89
+ Ruby ruby = context.getRuntime();
90
+ String key = getFullKey(args[0].toString());
91
+ IRubyObject value = args[1];
92
+ int timeout = getTimeout(args);
159
93
  try {
160
- boolean result = client.add(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
94
+ boolean result = client.add(key, timeout, value, transcoder).get();
161
95
  if (result == false) {
162
- throw newNotStored(ruby, "not stored");
96
+ throw Error.newNotStored(ruby, "not stored");
163
97
  }
164
98
  return context.nil;
165
99
  } catch (ExecutionException ee) {
166
- throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
100
+ throw ruby.newRuntimeError(ee.getLocalizedMessage());
167
101
  } catch (InterruptedException ie) {
168
- throw context.runtime.newThreadError(ie.getLocalizedMessage());
102
+ throw ruby.newThreadError(ie.getLocalizedMessage());
169
103
  }
170
104
  }
171
105
 
172
- @JRubyMethod
173
- public IRubyObject replace(ThreadContext context, IRubyObject key, IRubyObject value) {
174
- return replace(context, key, value, ruby.newFixnum(ttl));
175
- }
176
-
177
- @JRubyMethod
178
- public IRubyObject replace(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
106
+ @JRubyMethod(name = "replace", required = 2, optional = 3)
107
+ public IRubyObject replace(ThreadContext context, IRubyObject [] args) {
108
+ Ruby ruby = context.getRuntime();
109
+ String key = getFullKey(args[0].toString());
110
+ IRubyObject value = args[1];
111
+ int timeout = getTimeout(args);
179
112
  try {
180
- boolean result = client.replace(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
113
+ boolean result = client.replace(key, timeout, value, transcoder).get();
181
114
  if (result == false) {
182
- throw newNotStored(ruby, "not stored");
115
+ throw Error.newNotStored(ruby, "not stored");
183
116
  }
184
117
  return context.nil;
185
118
  } catch (ExecutionException ee) {
186
- throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
119
+ throw ruby.newRuntimeError(ee.getLocalizedMessage());
187
120
  } catch (InterruptedException ie) {
188
- throw context.runtime.newThreadError(ie.getLocalizedMessage());
121
+ throw ruby.newThreadError(ie.getLocalizedMessage());
189
122
  }
190
123
  }
191
124
 
192
- @JRubyMethod
193
- public IRubyObject set(ThreadContext context, IRubyObject key, IRubyObject value) {
194
- return set(context, key, value, ruby.newFixnum(ttl));
195
- }
196
-
197
- @JRubyMethod
198
- public IRubyObject set(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
125
+ @JRubyMethod(name = "set", required = 2, optional = 3)
126
+ public IRubyObject set(ThreadContext context, IRubyObject[] args) {
127
+ Ruby ruby = context.getRuntime();
128
+ String key = getFullKey(args[0].toString());
129
+ IRubyObject value = args[1];
130
+ int timeout = getTimeout(args);
199
131
  try {
200
- boolean result = client.set(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
132
+ boolean result = client.set(key, timeout, value, transcoder).get();
201
133
  if (result == false) {
202
- throw newNotStored(ruby, "not stored");
134
+ throw Error.newNotStored(ruby, "not stored");
203
135
  }
204
136
  return context.nil;
205
137
  } catch (ExecutionException ee) {
206
- throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
138
+ throw ruby.newRuntimeError(ee.getLocalizedMessage());
207
139
  } catch (InterruptedException ie) {
208
- throw context.runtime.newThreadError(ie.getLocalizedMessage());
140
+ throw ruby.newThreadError(ie.getLocalizedMessage());
209
141
  }
210
142
  }
211
143
 
212
- @JRubyMethod
213
- public IRubyObject get(ThreadContext context, IRubyObject key) {
214
- IRubyObject value = client.get(key.toString(), transcoder);
215
- if (value == null) {
216
- throw newNotFound(ruby, "not found");
144
+ @JRubyMethod(name = "get", required = 1, optional = 1)
145
+ public IRubyObject get(ThreadContext context, IRubyObject[] args) {
146
+ Ruby ruby = context.getRuntime();
147
+ IRubyObject keys = args[0];
148
+ if (keys instanceof RubyString) {
149
+ IRubyObject value = client.get(getFullKey(keys.toString()), transcoder);
150
+ if (value == null) {
151
+ throw Error.newNotFound(ruby, "not found");
152
+ }
153
+ return value;
154
+ } else if (keys instanceof RubyArray) {
155
+ RubyHash results = RubyHash.newHash(ruby);
156
+
157
+ Map<String, IRubyObject> bulkResults = client.getBulk(getFullKeys(keys.convertToArray()), transcoder);
158
+ for (String key : (List<String>) keys.convertToArray()) {
159
+ if (bulkResults.containsKey(getFullKey(key))) {
160
+ results.put(key, bulkResults.get(getFullKey(key)));
161
+ }
162
+ }
163
+ return results;
217
164
  }
218
- return value;
219
- }
220
-
221
- @JRubyMethod
222
- public IRubyObject incr(ThreadContext context, IRubyObject key) {
223
- return incr(context, key, ruby.newFixnum(1), ruby.newFixnum(ttl));
224
- }
225
-
226
- @JRubyMethod
227
- public IRubyObject incr(ThreadContext context, IRubyObject key, IRubyObject by) {
228
- return incr(context, key, by, ruby.newFixnum(ttl));
165
+ return context.nil;
229
166
  }
230
167
 
231
- @JRubyMethod
232
- public IRubyObject incr(ThreadContext context, IRubyObject key, IRubyObject by, IRubyObject timeout) {
233
- long result = client.incr(key.toString(), (int)by.convertToInteger().getLongValue(), 1, (int)timeout.convertToInteger().getLongValue());
168
+ @JRubyMethod(name = "incr", required = 1, optional = 2)
169
+ public IRubyObject incr(ThreadContext context, IRubyObject[] args) {
170
+ Ruby ruby = context.getRuntime();
171
+ String key = getFullKey(args[0].toString());
172
+ int by = getIncrDecrBy(args);
173
+ int timeout = getTimeout(args);
174
+ long result = client.incr(key, by, 1, timeout);
234
175
  return ruby.newFixnum(result);
235
176
  }
236
177
 
237
- @JRubyMethod
238
- public IRubyObject decr(ThreadContext context, IRubyObject key) {
239
- return decr(context, key, ruby.newFixnum(1), ruby.newFixnum(ttl));
240
- }
241
-
242
- @JRubyMethod
243
- public IRubyObject decr(ThreadContext context, IRubyObject key, IRubyObject by) {
244
- return decr(context, key, by, ruby.newFixnum(ttl));
245
- }
246
-
247
- @JRubyMethod
248
- public IRubyObject decr(ThreadContext context, IRubyObject key, IRubyObject by, IRubyObject timeout) {
249
- long result = client.decr(key.toString(), (int)by.convertToInteger().getLongValue(), 0, (int)timeout.convertToInteger().getLongValue());
178
+ @JRubyMethod(name = "decr", required = 1, optional = 2)
179
+ public IRubyObject decr(ThreadContext context, IRubyObject[] args) {
180
+ Ruby ruby = context.getRuntime();
181
+ String key = getFullKey(args[0].toString());
182
+ int by = getIncrDecrBy(args);
183
+ int timeout = getTimeout(args);
184
+ long result = client.decr(key, by, 0, timeout);
250
185
  return ruby.newFixnum(result);
251
186
  }
252
187
 
253
188
  @JRubyMethod
254
189
  public IRubyObject delete(ThreadContext context, IRubyObject key) {
190
+ Ruby ruby = context.getRuntime();
255
191
  try {
256
- boolean result = client.delete(key.toString()).get();
192
+ boolean result = client.delete(getFullKey(key.toString())).get();
257
193
  if (result == false) {
258
- throw newNotFound(ruby, "not found");
194
+ throw Error.newNotFound(ruby, "not found");
259
195
  }
260
196
  return context.nil;
261
197
  } catch (ExecutionException ee) {
262
- throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
198
+ throw ruby.newRuntimeError(ee.getLocalizedMessage());
263
199
  } catch (InterruptedException ie) {
264
- throw context.runtime.newThreadError(ie.getLocalizedMessage());
200
+ throw ruby.newThreadError(ie.getLocalizedMessage());
265
201
  }
266
202
  }
267
203
 
268
204
  @JRubyMethod
269
205
  public IRubyObject flush(ThreadContext context) {
206
+ Ruby ruby = context.getRuntime();
270
207
  try {
271
208
  client.flush().get();
272
209
  return context.nil;
273
210
  } catch (ExecutionException ee) {
274
- throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
211
+ throw ruby.newRuntimeError(ee.getLocalizedMessage());
275
212
  } catch (InterruptedException ie) {
276
- throw context.runtime.newThreadError(ie.getLocalizedMessage());
213
+ throw ruby.newThreadError(ie.getLocalizedMessage());
277
214
  }
278
215
  }
279
216
 
280
217
  @JRubyMethod
281
218
  public IRubyObject stats(ThreadContext context) {
219
+ Ruby ruby = context.getRuntime();
282
220
  RubyHash results = RubyHash.newHash(ruby);
283
221
  for(Map.Entry<SocketAddress, Map<String, String>> entry : client.getStats().entrySet()) {
284
222
  RubyHash serverHash = RubyHash.newHash(ruby);
@@ -290,36 +228,119 @@ public class Memcached extends RubyObject {
290
228
  return results;
291
229
  }
292
230
 
293
- @JRubyMethod
231
+ @JRubyMethod(name = {"quit", "shutdown"})
294
232
  public IRubyObject shutdown(ThreadContext context) {
295
233
  client.shutdown();
296
234
 
297
235
  return context.nil;
298
236
  }
299
237
 
300
- @JRubyClass(name="Memcached::Error", parent="RuntimeError")
301
- public static class Error {}
302
- @JRubyClass(name="Memcached::NotFound", parent="Memcached::Error")
303
- public static class NotFound extends Error {}
304
- @JRubyClass(name="Memcached::NotStored", parent="Memcached::Error")
305
- public static class NotStored extends Error {}
306
- @JRubyClass(name="Memcached::NotSupport", parent="Memcached::Error")
307
- public static class NotSupport extends Error {}
308
-
309
- static RaiseException newNotFound(Ruby ruby, String message) {
310
- return newMemcachedError(ruby, "NotFound", message);
238
+ protected int getDefaultTTL() {
239
+ return ttl;
311
240
  }
312
241
 
313
- static RaiseException newNotStored(Ruby ruby, String message) {
314
- return newMemcachedError(ruby, "NotStored", message);
242
+ protected IRubyObject init(ThreadContext context, List<String> servers, RubyHash options) {
243
+ Ruby ruby = context.getRuntime();
244
+ List<InetSocketAddress> addresses = AddrUtil.getAddresses(servers);
245
+ try {
246
+ ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
247
+
248
+ String distributionValue = "ketama";
249
+ String hashValue = "fnv1_32";
250
+ RubyBoolean binaryValue = ruby.getFalse();
251
+ String transcoderValue = null;
252
+ if (!options.isEmpty()) {
253
+ RubyHash opts = options.convertToHash();
254
+ if (opts.containsKey(ruby.newSymbol("distribution"))) {
255
+ distributionValue = opts.get(ruby.newSymbol("distribution")).toString();
256
+ }
257
+ if (opts.containsKey(ruby.newSymbol("hash"))) {
258
+ hashValue = opts.get(ruby.newSymbol("hash")).toString();
259
+ }
260
+ if (opts.containsKey(ruby.newSymbol("binary_protocol"))) {
261
+ binaryValue = (RubyBoolean) opts.get(ruby.newSymbol("binary_protocol"));
262
+ }
263
+ if (opts.containsKey(ruby.newSymbol("default_ttl"))) {
264
+ ttl = Integer.parseInt(opts.get(ruby.newSymbol("default_ttl")).toString());
265
+ }
266
+ if (opts.containsKey(ruby.newSymbol("namespace"))) {
267
+ prefixKey = opts.get(ruby.newSymbol("namespace")).toString();
268
+ }
269
+ if (opts.containsKey(ruby.newSymbol("prefix_key"))) {
270
+ prefixKey = opts.get(ruby.newSymbol("prefix_key")).toString();
271
+ }
272
+ if (opts.containsKey(ruby.newSymbol("transcoder"))) {
273
+ transcoderValue = opts.get(ruby.newSymbol("transcoder")).toString();
274
+ }
275
+ }
276
+
277
+ if ("array_mod".equals(distributionValue)) {
278
+ builder.setLocatorType(Locator.ARRAY_MOD);
279
+ } else if ("ketama".equals(distributionValue) || "consistent_ketama".equals(distributionValue)) {
280
+ builder.setLocatorType(Locator.CONSISTENT);
281
+ } else {
282
+ throw Error.newNotSupport(ruby, "distribution not support");
283
+ }
284
+ if ("native".equals(hashValue)) {
285
+ builder.setHashAlg(DefaultHashAlgorithm.NATIVE_HASH);
286
+ } else if ("crc".equals(hashValue)) {
287
+ builder.setHashAlg(DefaultHashAlgorithm.CRC_HASH);
288
+ } else if ("fnv1_64".equals(hashValue)) {
289
+ builder.setHashAlg(DefaultHashAlgorithm.FNV1_64_HASH);
290
+ } else if ("fnv1a_64".equals(hashValue)) {
291
+ builder.setHashAlg(DefaultHashAlgorithm.FNV1A_64_HASH);
292
+ } else if ("fnv1_32".equals(hashValue)) {
293
+ builder.setHashAlg(DefaultHashAlgorithm.FNV1_32_HASH);
294
+ } else if ("fnv1a_32".equals(hashValue)) {
295
+ builder.setHashAlg(DefaultHashAlgorithm.FNV1A_32_HASH);
296
+ } else if ("ketama".equals(hashValue)) {
297
+ builder.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH);
298
+ } else {
299
+ throw Error.newNotSupport(ruby, "hash not support");
300
+ }
301
+
302
+ if (ruby.getTrue() == binaryValue) {
303
+ builder.setProtocol(Protocol.BINARY);
304
+ }
305
+
306
+ builder.setDaemon(true);
307
+ client = new MemcachedClient(builder.build(), addresses);
308
+
309
+ if ("marshal_zlib".equals(transcoderValue)) {
310
+ transcoder = new MarshalZlibTranscoder(ruby);
311
+ } else {
312
+ transcoder = new MarshalTranscoder(ruby);
313
+ }
314
+ } catch (IOException ioe) {
315
+ throw ruby.newIOErrorFromException(ioe);
316
+ }
317
+
318
+ return context.nil;
319
+ }
320
+
321
+ private int getTimeout(IRubyObject[] args) {
322
+ if (args.length > 2) {
323
+ return (int) args[2].convertToInteger().getLongValue();
324
+ }
325
+ return ttl;
315
326
  }
316
327
 
317
- static RaiseException newNotSupport(Ruby ruby, String message) {
318
- return newMemcachedError(ruby, "NotSupport", message);
328
+ private int getIncrDecrBy(IRubyObject[] args) {
329
+ if (args.length > 1) {
330
+ return (int) args[1].convertToInteger().getLongValue();
331
+ }
332
+ return 1;
333
+ }
334
+
335
+ private List<String> getFullKeys(List<String> keys) {
336
+ List<String> fullKeys = new ArrayList<String>();
337
+ for (String key : keys) {
338
+ fullKeys.add(getFullKey(key));
339
+ }
340
+ return fullKeys;
319
341
  }
320
342
 
321
- private static RaiseException newMemcachedError(Ruby ruby, String klass, String message) {
322
- RubyClass errorClass = ruby.getModule("Memcached").getClass(klass);
323
- return new RaiseException(RubyException.newException(ruby, errorClass, message), true);
343
+ private String getFullKey(String key) {
344
+ return prefixKey + key;
324
345
  }
325
346
  }