@@ -4,12 +4,11 @@ A non blocking Redis client based on Akka I/O
44
55### Key features of the library
66
7- - Non blocking
8- - Full set of Redis commands
9- - Scripting support
10- - Transparent typeclass based serialization
11- - Out of the box integration support with Json serialization libraries
12- - Composable with Futures
7+ - [ Non blocking, compositional with Futures] ( #non-blocking-compositional-with-futures )
8+ - [ Full set of Redis commands] ( #full-set-of-redis-commands )
9+ - [ Scripting support] ( #scripting-support )
10+ - [ Transparent typeclass based serialization] ( #typeclass-based-transparent-serialization )
11+ - [ Out of the box integration support with Json serialization libraries] ( #integration-support-with-json-serializing-libraries )
1312
1413### Project Dependencies
1514
@@ -37,7 +36,14 @@ implicit val timeout = AkkaTimeout(5 seconds)
3736val client = RedisClient (" localhost" , 6379 )
3837```
3938
40- #### Non Blocking Compositional Gets and Sets
39+ ** Note**
40+
41+ The below examples are taken from the test cases.
42+
43+ Every API call returns Scala [ ` Future ` ] ( http://www.scala-lang.org/api/current/index.html#scala.concurrent.Future ) and ` _.futureValue ` is just a helper provided by [ ScalaTest] ( http://scalatest.org ) for ease of test. You won't need this in most cases because it blocks current thread.
44+
45+
46+ #### Non Blocking, Compositional with Futures
4147
4248``` scala
4349describe(" set" ) {
@@ -74,7 +80,42 @@ describe("get") {
7480}
7581```
7682
77- #### List Operations
83+ ``` scala
84+ describe(" non blocking apis using futures" ) {
85+ // ...
86+
87+ it(" should compose with sequential combinator" ) {
88+ val key = " client_key_seq"
89+
90+ val res = for {
91+ p <- client.lpush(key, 0 to 100 )
92+ if p > 0
93+ r <- client.lrange[Long ](key, 0 , - 1 )
94+ } yield (p, r)
95+
96+ val (count, list) = res.futureValue
97+ count should equal (101 )
98+ list.reverse should equal (0 to 100 )
99+ }
100+ }
101+
102+ describe(" error handling using promise failure" ) {
103+ it(" should give error trying to lpush on a key that has a non list value" ) {
104+ val key = " client_err"
105+ client.set(key, " value200" ).futureValue should be (true )
106+
107+ val thrown = evaluating {
108+ client.lpush(key, 1200 ).futureValue
109+ } should produce [TestFailedException ]
110+
111+ thrown.getCause.getMessage should equal (" ERR Operation against a key holding the wrong kind of value" )
112+ }
113+ }
114+ ```
115+
116+ #### Full set of Redis commands
117+
118+ ##### List Operations
78119
79120``` scala
80121describe(" lpush" ) {
@@ -97,28 +138,39 @@ describe("lpush") {
97138}
98139```
99140
100- #### Other Data Structure support as per Redis
141+ ##### Other Data Structure support as per Redis
101142
102143``` scala
103144// Hash
104145describe(" hmget" ) {
105- it(" should set and get maps" ) {
106- val key = " hmget1"
107- client.hmset(key, Map (" field1" -> " val1" , " field2" -> " val2" ))
108- client.hmget(key, " field1" ).futureValue should equal (Map (" field1" -> " val1" ))
109- client.hmget(key, " field1" , " field2" ).futureValue should equal (Map (" field1" -> " val1" , " field2" -> " val2" ))
110- client.hmget(key, " field1" , " field2" , " field3" ).futureValue should equal (Map (" field1" -> " val1" , " field2" -> " val2" ))
111- }
146+ it(" should set and get maps" ) {
147+ val key = " hmget1"
148+ client.hmset(key, Map (" field1" -> " val1" , " field2" -> " val2" ))
149+
150+ client
151+ .hmget(key, " field1" )
152+ .futureValue should equal (Map (" field1" -> " val1" ))
153+
154+ client
155+ .hmget(key, " field1" , " field2" )
156+ .futureValue should equal (Map (" field1" -> " val1" , " field2" -> " val2" ))
157+
158+ client
159+ .hmget(key, " field1" , " field2" , " field3" )
160+ .futureValue should equal (Map (" field1" -> " val1" , " field2" -> " val2" ))
161+ }
112162}
113163
114164// Set
115165describe(" spop" ) {
116166 it(" should pop a random element" ) {
117167 val key = " spop1"
118- client.sadd(key, " foo" ).futureValue should equal (1 )
119- client.sadd(key, " bar" ).futureValue should equal (1 )
120- client.sadd(key, " baz" ).futureValue should equal (1 )
121- client.spop(key).futureValue should (equal (Some (" foo" )) or equal (Some (" bar" )) or equal (Some (" baz" )))
168+ client.sadd(key, " foo" )
169+ client.sadd(key, " bar" )
170+
171+ client
172+ .spop(key)
173+ .futureValue should (equal (Some (" foo" )) or equal (Some (" bar" )))
122174 }
123175
124176 it(" should return nil if the key does not exist" ) {
@@ -132,89 +184,31 @@ describe("z(rev)rangeByScoreWithScore") {
132184 it (" should return the elements between min and max" ) {
133185 add
134186
135- client.zrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , None ).futureValue should equal (
136- List ((" alan kay" , 1940.0 ), (" richard stallman" , 1953.0 ), (" yukihiro matsumoto" , 1965.0 ), (" linus torvalds" , 1969.0 )))
187+ client
188+ .zrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , None )
189+ .futureValue should equal (List (
190+ (" alan kay" , 1940.0 ), (" richard stallman" , 1953.0 ),
191+ (" yukihiro matsumoto" , 1965.0 ), (" linus torvalds" , 1969.0 )
192+ ))
137193
138- client.zrevrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , None ).futureValue should equal (
139- List ((" linus torvalds" , 1969.0 ), (" yukihiro matsumoto" , 1965.0 ), (" richard stallman" , 1953.0 ),(" alan kay" , 1940.0 )))
194+ client
195+ .zrevrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , None )
196+ .futureValue should equal (List (
197+ (" linus torvalds" , 1969.0 ), (" yukihiro matsumoto" , 1965.0 ),
198+ (" richard stallman" , 1953.0 ),(" alan kay" , 1940.0 )
199+ ))
140200
141- client.zrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , Some (3 , 1 )).futureValue should equal (
142- List ((" linus torvalds" , 1969.0 )))
201+ client
202+ .zrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , Some (3 , 1 ))
203+ .futureValue should equal (List ((" linus torvalds" , 1969.0 )))
143204
144- client.zrevrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , Some (3 , 1 )).futureValue should equal (
145- List ((" alan kay" , 1940.0 )))
205+ client
206+ .zrevrangeByScoreWithScores(" hackers" , 1940 , true , 1969 , true , Some (3 , 1 ))
207+ .futureValue should equal (List ((" alan kay" , 1940.0 )))
146208 }
147209}
148210 ```
149211
150- #### With Operations that compose
151-
152- ``` scala
153- describe(" non blocking apis using futures" ) {
154- it(" get and set should be non blocking" ) {
155- @ volatile var callbackExecuted = false
156-
157- val ks = (1 to 10 ).map(i => s " client_key_ $i" )
158- val kvs = ks.zip(1 to 10 )
159-
160- val sets : Seq [Future [Boolean ]] = kvs map {
161- case (k, v) => client.set(k, v)
162- }
163-
164- val setResult = Future .sequence(sets) map { r : Seq [Boolean ] =>
165- callbackExecuted = true
166- r
167- }
168-
169- callbackExecuted should be (false )
170- setResult.futureValue should contain only (true )
171- callbackExecuted should be (true )
172-
173- callbackExecuted = false
174- val gets : Seq [Future [Option [Long ]]] = ks.map { k => client.get[Long ](k) }
175- val getResult = Future .sequence(gets).map { rs =>
176- callbackExecuted = true
177- rs.flatten.sum
178- }
179-
180- callbackExecuted should be (false )
181- getResult.futureValue should equal (55 )
182- callbackExecuted should be (true )
183- }
184-
185- it(" should compose with sequential combinator" ) {
186- val key = " client_key_seq"
187- val values = (1 to 100 ).toList
188- val pushResult = client.lpush(key, 0 , values:_* )
189- val getResult = client.lrange[Long ](key, 0 , - 1 )
190-
191- val res = for {
192- p <- pushResult.mapTo[Long ]
193- if p > 0
194- r <- getResult.mapTo[List [Long ]]
195- } yield (p, r)
196-
197- val (count, list) = res.futureValue
198- count should equal (101 )
199- list.reverse should equal (0 to 100 )
200- }
201- }
202- ```
203-
204- ``` scala
205- describe(" error handling using promise failure" ) {
206- it(" should give error trying to lpush on a key that has a non list value" ) {
207- val key = " client_err"
208- val v = client.set(key, " value200" )
209- v.futureValue should be (true )
210-
211- val x = client.lpush(key, 1200 )
212- val thrown = evaluating { x.futureValue } should produce [TestFailedException ]
213- thrown.getCause.getMessage should equal (" ERR Operation against a key holding the wrong kind of value" )
214- }
215- }
216- ```
217-
218212#### Scripting support
219213
220214``` scala
@@ -312,25 +306,39 @@ describe("eval") {
312306``` scala
313307import DefaultFormats ._
314308
315- client.hmset(" hash" , Map (" field1" -> " 1" , " field2" -> 2 )).futureValue should be (true )
316- client.hmget[String ](" hash" , " field1" , " field2" ).futureValue should be(Map (" field1" -> " 1" , " field2" -> " 2" ))
317- client.hmget[Int ](" hash" , " field1" , " field2" ).futureValue should be(Map (" field1" -> 1 , " field2" -> 2 ))
318- client.hmget[Int ](" hash" , " field1" , " field2" , " field3" ).futureValue should be(Map (" field1" -> 1 , " field2" -> 2 ))
309+ client
310+ .hmset(" hash" , Map (" field1" -> " 1" , " field2" -> 2 ))
311+ .futureValue should be (true )
312+
313+ client
314+ .hmget[String ](" hash" , " field1" , " field2" )
315+ .futureValue should equal (Map (" field1" -> " 1" , " field2" -> " 2" ))
316+
317+ client
318+ .hmget[Int ](" hash" , " field1" , " field2" )
319+ .futureValue should equal (Map (" field1" -> 1 , " field2" -> 2 ))
320+
321+ client
322+ .hmget[Int ](" hash" , " field1" , " field2" , " field3" )
323+ .futureValue should equal (Map (" field1" -> 1 , " field2" -> 2 ))
319324```
320325
321326###### Should use a provided implicit Format typeclass
322327
323328``` scala
324329import DefaultFormats ._
325330
326- client.hmset(" hash" , Map (" field1" -> " 1" , " field2" -> 2 )).futureValue should be (true )
327- client.hmget(" hash" , " field1" , " field2" ).futureValue should be(Map (" field1" -> " 1" , " field2" -> " 2" ))
331+ client.hmset(" hash" , Map (" field1" -> " 1" , " field2" -> 2 ))
328332
329333implicit val intFormat = Format [Int ](java.lang.Integer .parseInt, _.toString)
330334
331- client.hmget[Int ](" hash" , " field1" , " field2" ).futureValue should be(Map (" field1" -> 1 , " field2" -> 2 ))
332- client.hmget[String ](" hash" , " field1" , " field2" ).futureValue should be(Map (" field1" -> " 1" , " field2" -> " 2" ))
333- client.hmget[Int ](" hash" , " field1" , " field2" , " field3" ).futureValue should be(Map (" field1" -> 1 , " field2" -> 2 ))
335+ client
336+ .hmget[Int ](" hash" , " field1" , " field2" )
337+ .futureValue should equal (Map (" field1" -> 1 , " field2" -> 2 ))
338+
339+ client
340+ .hmget[String ](" hash" , " field1" , " field2" )
341+ .futureValue should be(Map (" field1" -> " 1" , " field2" -> " 2" ))
334342```
335343
336344###### Easy to have custom Format for case classes
@@ -340,7 +348,6 @@ case class Person(id: Int, name: String)
340348
341349val debasish = Person (1 , " Debasish Gosh" )
342350val jisoo = Person (2 , " Jisoo Park" )
343- val people = List (debasish, jisoo)
344351
345352implicit val customPersonFormat =
346353 new Format [Person ] {
@@ -358,32 +365,30 @@ implicit val customPersonFormat =
358365 }
359366 }
360367
361- val write = implicitly[Write [Person ]].write _
362- val read = implicitly[Read [Person ]].read _
368+ client.set(" debasish" , debasish)
363369
364- read(write( debasish)) should equal (debasish)
370+ client.get[ Person ]( " debasish" ).futureValue should equal (Some ( debasish) )
365371```
366372
367373###### Integration support with Json Serializing libraries
368374
369375``` scala
370376import spray .json .DefaultJsonProtocol ._
371- import SprayJsonSupport ._
377+ import com . redis . serialization . SprayJsonSupport ._
372378
373379implicit val personFormat = jsonFormat2(Person )
374380
375- val write = implicitly[ Write [ Person ]].write _
376- val read = implicitly[ Read [ Person ]].read _
381+ client.set( " debasish " , debasish)
382+ client.set( " people " , List (debasish, jisoo))
377383
378- val writeL = implicitly[Write [List [Person ]]].write _
379- val readL = implicitly[Read [List [Person ]]].read _
380-
381- read(write(debasish)) should equal (debasish)
382- readL(writeL(people)) should equal (people)
384+ client.get[Person ](" debasish" ).futureValue should equal (Some (debasish))
385+ client.get[List [Person ]](" people" ).futureValue should equal (Some (List (debasish, jisoo)))
383386```
384387
385388For more examples on serialization, have a look at the test cases.
386389
390+ _ Third-party libraries are not installed by default. You need to provide them by yourself._
391+
387392### License
388393
389394This software is licensed under the Apache 2 license, quoted below.
0 commit comments