README.md

# ledis

[![][lutil-logo]][lutil-logo-large]

[lutil-logo]: priv/images/ButterCrunchLettuce-2-small.png
[lutil-logo-large]: priv/images/ButterCrunchLettuce-2-large.png

*An LFE Redis Client Library*


#### Table of Contents

* [Dependences](#dependences-)
* [Installtion and Setup](#installtion-and-setup-)
* [Usage](#usage-)
  * [Difference from Redis CLI](#difference-from-redis-cli-)
* [A Note for Developers](#a-note-for-developers-)


## Dependences [↟](#table-of-contents)

You will need the following installed on your system:

* Erlang
* Redis
* rebar3


## Installtion and Setup [↟](#table-of-contents)

Here's what you need to do:

```bash
$ git clone https://github.com/lfex/ledis.git
$ cd ledis
$ make compile
```

At this point, you will be able to run an LFE REPL (shell):

```bash
$ make repl
```

If you don't have access to a Rdis deployment, you can always do this:

``` bash
$ docker run --rm -p 6379:6379 redis:latest
```

## Usage [↟](#table-of-contents)

To use ledis from the shell, just do this:

```bash
$ make repl
```

Note that, under the hood, this will activate the "dev" profile, so LFE and
some developer rebar3 LFE plugins will be downloaded the first time it is run.

```cl
Erlang/OTP 24 [erts-12.0.2] [source] [64-bit] [smp:4:4] ...

   ..-~.~_~---..
  (      \\     )    |   A Lisp-2+ on the Erlang VM
  |`-.._/_\\_.-':    |   Type (help) for usage info.
  |         g |_ \   |
  |        n    | |  |   Docs: http://docs.lfe.io/
  |       a    / /   |   Source: http://github.com/lfe/lfe
   \     l    |_/    |
    \   r     /      |   LFE v2.0.1 (abort with ^G)
     `-E___.-'

lfe>
```

```cl
lfe> (ledis:start-link)
true
lfe> (ledis:get 'foo)
#(ok undefined)
lfe> (ledis:set 'foo 'bar)
#(ok #"OK")
lfe> (ledis:get 'foo)
#(ok #"bar")
```

You may also provide an option to convert all results to string values:

```cl
lfe> (ledis:set 'foo 'bar '(#(return-type string)))
#(ok "OK")
lfe> (ledis:get 'foo '(#(return-type string)))
#(ok "bar")
```

If you would like to receive string values by default, simply update either
your project's ``lfe.config`` or your ``~/.lfe/lfe.config`` file with the
following:

```cl
#(ledis
  (#(client-process-name ledis-client)
   #(return-type string)))
```


### Difference from Redis CLI [↟](#table-of-contents)

Since LFE is a fixed-arity language, a few differences exist between ledis and
the Redis CLI. These usually are for the following scenarios:

1. Optional command arguments which have been converted to LFE as options (using
   proplists).
1. Some Redis commands conflict with LFE functions/macros, and these have been
   renamed.
1. Some variable arity commands are converted to single-arity in LFE where the
   single argument is a list.

Functions which are different in these ways have been listed below with sample
usage.

#### ``bitcount`` [↟](#table-of-contents)

```lisp
lfe> (ledis:bitcount 'foo)
#(ok #"10")
lfe> (ledis:bitcount 'foo '(#(start 2) #(end 4)))
#(ok #"4")
```

#### ``bitop`` [↟](#table-of-contents)

```lisp
lfe> (ledis:set 'key1 "foobar")
#(ok #"OK")
lfe> (ledis:set 'key2 "absdef")
#(ok #"OK")
lfe> (ledis:bitop 'AND 'dest '(key1 key2))
#(ok #"6")
lfe> (ledis:get 'dest)
#(ok #"`bc`ab")
```

#### ``bitpos`` [↟](#table-of-contents)

```lisp
lfe> (ledis:set 'mykey #b(#xff #xf0 #x00))
#(ok #"OK")
lfe> (ledis:bitpos 'mykey 0)
#(ok #"12")
lfe> (ledis:set 'mykey #b(#x00 #xff #xf0))
#(ok #"OK")
lfe> (ledis:bitpos 'mykey 1 '(#(start 1)))
#(ok #"8")
lfe> (ledis:bitpos 'mykey 1 '(#(start 2)))
#(ok #"16")
lfe> (ledis:set 'mykey #b(#x00 #x00 #x00))
#(ok #"OK")
lfe> (ledis:bitpos 'mykey 1)
#(ok #"-1")
```

#### ``blpop`` [↟](#table-of-contents)

The following would be returned if you had previously performed the ``(ledis:rpush 'list1 "banana"`` and ``(ledis:rpush 'list1 "tranbar")`` calls:

```lisp
lfe> (ledis:blpop 'list1 0)
#(ok (#"list1" #"banana"))
lfe> (ledis:blpop '(list2 list3 list1) 0)
#(ok (#"list1" #"tranbar"))
```

#### ``del`` [↟](#table-of-contents)

```lisp
lfe> (ledis:set 'mykey "Hello")
#(ok #"OK")
lfe> (ledis:del 'mykey)
#(ok #"1")
lfe> (ledis:multi-set '(key1 "val1" key2 "val2" key3 "val3"))
#(ok #"OK")
lfe> (ledis:del '(key1 key2 key3 key4))
#(ok #"3")
```

#### ``hmset`` [↟](#table-of-contents)

Normally, the ``HMSET`` Redis command is of the form
``HMSET key field value [field value ...]``, but for simplicity's sake with
regard to LFE's arity, the ``hmset`` function is called a bit differently: It
is of the form ``(hmset <key> '(<kv pair> ...))``. For instance:

```lfe
lfe> (ledis:hmset 'foo2 '(field1 bar2))
#(ok #"OK")
lfe> (ledis:hmset 'foo2 '(field2 bizaz field3 boz field4 bleez))
#(ok #"OK")
```

These can then be accessed using ``hmget``:

```lfe
lfe> (ledis:hmget 'foo2 'field4)
#(ok (#"bleez"))
lfe> (ledis:hmget 'foo2 '(field3 field2 field1))
#(ok (#"boz" #"bizaz" #"bar2"))
```

#### ``lrange`` [&#x219F;](#table-of-contents)

In addition to ``lrange/3`` and ``lrange/4`` (which offers the same signature as
associated Redis commands, with the 4-arity function also allowing options to be
passed), ledis offers two additional arities for this function. Arity-1 and
arity-2 will return the entire list at the given key (the following will be
returned if you have previously done something like ``(ledis:lpush-multi 'fruits '("banana" "tranbar" "kiwi") '())``):

```lisp
lfe> (ledis:lrange 'fruits)
#(ok
  (#"banana"
   #"tranbar"
   #"kiwi"))
```


#### ``M*`` renamed to ``multi-*`` [&#x219F;](#table-of-contents)

```lisp
lfe> (ledis:multi-set '(a 10 b 20 c 30))
#(ok #"OK")
lfe> (ledis:multi-get '(a b c))
#(ok (#"10" #"20" #"30"))
```


#### ``set`` [&#x219F;](#table-of-contents)

```lisp
lfe> (ledis:set 'mykey "athirdvalue" '(#(xx) #(px 10000)))
#(ok #"OK")
lfe> (ledis:get 'mykey)
#(ok #"athirdvalue")
lfe> (timer:sleep 10000)
ok
lfe> (ledis:get 'mykey)
#(ok undefined)
```


## Notes for Developers [&#x219F;](#table-of-contents)

### Tests

You can run the meager integration tests with the following, if you have
a local instance of Redis running:

``` bash
$ rebar3 as test lfe ltest -tintegration
```

### Supporting More of the Redis API

If you have an interest in making contributions to this library, you'll need to
make note of how the wrappring works.

* For every *n*-arity function you wish to add (wrap), you'll also need to add
  an *n+1*-arity function of the same name. This is because ledis supports
  (requires) a version of every function that takes LFE-specific options (e.g.,
  setting return types).
* Under most circumstances, this just means making an entry in the functions
  ``get-api-funcs-no-opts`` and ``get-api-funcs-with-opts`` in the file
  ``include/ledis.lfe``.
* Non-normal circumstances include supporting Redis functions of mixed arity,
  special options, and non-standard or irregular function arguments. In these
  cases, you will often be able to make an entry in ``get-api-funcs-no-opts``,
  but have to then create a custom *n+1*-arity function in ``src/ledis.lfe``.
  Sometimes you won't be able to use either of the API-generating functions and
  macros, and will instead need to implement both the *n*-arity and *n+1*-arity
  functions in ``src/ledis.lfe``.