diff --git a/.jekyll-metadata b/.jekyll-metadata
new file mode 100644
index 0000000..49700b9
Binary files /dev/null and b/.jekyll-metadata differ
diff --git a/Gemfile b/Gemfile
index 7152b0b..6063b66 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,8 @@
source "https://rubygems.org"
-gem "jekyll", "~> 2.5.3"
+gem "jekyll", "~> 3.1"
+gem 'jekyll-gist'
+gem 'kramdown'
# with Ruby 2.0 on OS X, you may need to install
# it manually with --with-iconv-dir:
#
diff --git a/Gemfile.lock b/Gemfile.lock
index fccf3fe..02e3254 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,68 +3,36 @@ GEM
specs:
albino (1.3.3)
posix-spawn (>= 0.3.6)
- blankslate (2.1.2.4)
- celluloid (0.16.0)
- timers (~> 4.0.0)
- classifier-reborn (2.0.3)
- fast-stemmer (~> 1.0)
- coffee-script (2.3.0)
- coffee-script-source
- execjs
- coffee-script-source (1.8.0)
colorator (0.1)
- execjs (2.2.2)
- fast-stemmer (1.0.2)
- ffi (1.9.6)
- hitimes (1.2.2)
+ ffi (1.9.10)
iconv (1.0.4)
- jekyll (2.5.3)
- classifier-reborn (~> 2.0)
+ jekyll (3.1.2)
colorator (~> 0.1)
- jekyll-coffeescript (~> 1.0)
- jekyll-gist (~> 1.0)
- jekyll-paginate (~> 1.0)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.3)
- liquid (~> 2.6.1)
+ liquid (~> 3.0)
mercenary (~> 0.3.3)
- pygments.rb (~> 0.6.0)
- redcarpet (~> 3.1)
+ rouge (~> 1.7)
safe_yaml (~> 1.0)
- toml (~> 0.1.0)
- jekyll-coffeescript (1.0.1)
- coffee-script (~> 2.2)
jekyll-gist (1.1.0)
- jekyll-paginate (1.1.0)
- jekyll-sass-converter (1.3.0)
- sass (~> 3.2)
- jekyll-watch (1.2.0)
- listen (~> 2.7)
- kramdown (1.5.0)
- liquid (2.6.1)
- listen (2.8.4)
- celluloid (>= 0.15.2)
+ jekyll-sass-converter (1.4.0)
+ sass (~> 3.4)
+ jekyll-watch (1.3.1)
+ listen (~> 3.0)
+ kramdown (1.10.0)
+ liquid (3.0.6)
+ listen (3.0.6)
rb-fsevent (>= 0.9.3)
- rb-inotify (>= 0.9)
+ rb-inotify (>= 0.9.7)
mercenary (0.3.5)
- parslet (1.5.0)
- blankslate (~> 2.0)
posix-spawn (0.3.9)
- pygments.rb (0.6.0)
- posix-spawn (~> 0.3.6)
- yajl-ruby (~> 1.1.0)
- rb-fsevent (0.9.4)
- rb-inotify (0.9.5)
+ rb-fsevent (0.9.7)
+ rb-inotify (0.9.7)
ffi (>= 0.5.0)
- redcarpet (3.2.2)
+ rouge (1.10.1)
safe_yaml (1.0.4)
- sass (3.4.9)
- timers (4.0.1)
- hitimes
- toml (0.1.2)
- parslet (~> 1.5.0)
- yajl-ruby (1.1.0)
+ sass (3.4.21)
PLATFORMS
ruby
@@ -72,4 +40,9 @@ PLATFORMS
DEPENDENCIES
albino
iconv
- jekyll (~> 2.5.3)
+ jekyll (~> 3.1)
+ jekyll-gist
+ kramdown
+
+BUNDLED WITH
+ 1.10.5
diff --git a/README.md b/README.md
index e40e4cc..dbd3ffe 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Project Name Documentation
+# ClojureWerkz/Spyglass
-This is a documentation site for [Project Name]. Copy or clone me and adapt for your project
+This is a documentation site for [ClojureWerkz/Spyglass](http://clojurememcached.info/). Copy or clone me and adapt for your project
that needs documentation guides similar to [rubyamqp.info](http://rubyamqp.info), [clojuremongodb.info](http://clojuremongodb.info) and so on.
@@ -28,6 +28,6 @@ In order to recompile haml and sass files for publishing, run
## License & Copyright
-Copyright (C) 2011-2012 Alexander Petrov, Michael S. Klishin.
+Copyright (C) 2011-2016 Alexander Petrov, Michael S. Klishin.
Distributed under the Eclipse Public License, the same as Clojure.
diff --git a/_config.yml b/_config.yml
index 5e01ceb..07fe6f0 100644
--- a/_config.yml
+++ b/_config.yml
@@ -1,7 +1,8 @@
source: .
destination: ./_site
-plugins: ./_plugins
-auto: true
+layouts_dir: ./_layouts
+plugins_dir: ./_plugins
exclude: [bin, CNAME, Gemfile, Gemfile.lock, README.md]
-markdown: rdiscount
+markdown: kramdown
permalink: /:title
+highlighter: rouge
diff --git a/_layouts/article.html b/_layouts/article.html
index 151ee2e..9888edd 100644
--- a/_layouts/article.html
+++ b/_layouts/article.html
@@ -9,6 +9,7 @@
+
diff --git a/_layouts/default.html b/_layouts/default.html
index b472738..bf9dabc 100644
--- a/_layouts/default.html
+++ b/_layouts/default.html
@@ -9,6 +9,8 @@
+
+
diff --git a/_plugins/gist.rb b/_plugins/gist.rb
deleted file mode 100644
index 64a6ac4..0000000
--- a/_plugins/gist.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module Jekyll
-
- #
- # Render gist
- #
- # Usage:
- #
- # {% gist gist_id %} =>
- #
- # Raises: Liquid::SyntaxError
- class Gist < ::Liquid::Tag
-
- Syntax = /(#{::Liquid::Expression}+)?/
-
- def initialize(tag_name, markup, tokens)
- if markup =~ Syntax
- @gist_id = $1
- @options = {}
- markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
- else
- raise ::Liquid::SyntaxError.new("Syntax Error in 'link' - Valid syntax: gist ")
- end
-
- super
- end
-
- def render(context)
- %{}
- end
- end
-
-end
-
-
-Liquid::Template.register_tag('gist', Jekyll::Gist)
diff --git a/articles/getting_started.md b/articles/getting_started.md
index cdc5913..1f6d404 100644
--- a/articles/getting_started.md
+++ b/articles/getting_started.md
@@ -68,11 +68,30 @@ It is recommended to stay up-to-date with new versions. New releases and importa
Spyglass has a single namespace: `clojurewerkz.spyglass.client`. Require it and use the `clojurewerkz.spyglass.client/text-connection`
function that take a server list string:
-{% gist 6911e5c9ee201c47843b %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ ;; connects to 2 Memcached servers using text protocol
+ (let [tmc-client (c/text-connection "server1.internal:11211 server2.internal:11211")]
+ tmc-client))
+```
+
`clojurewerkz.spyglass.client/bin-connection` works the same way but uses binary Memcached protocol:
-{% gist ed6bb2a42b40e95884ca %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ ;; connects to 2 Memcached servers using binary protocol
+ (let [bmc-client (c/bin-connection "server1.internal:11211 server2.internal:11211")]
+ bmc-client))
+```
## Setting keys
@@ -80,7 +99,18 @@ function that take a server list string:
To set a key, use the `clojurewerkz.spyglass.client/set` function that takes a client instance, a string key, an expiration value (as an integer) and a value
to store and returns a future:
-{% gist 79d271fa55880a21669f %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ ;; set a key that expires in 5 minutes (300 seconds)
+ res (c/set tmc "a-key" 300 "a value")]
+ ;; results of async operations in Spyglass can be @dereferenced
+ @res))
+```
### Expiration values
@@ -90,8 +120,59 @@ is larger than that, the server will consider it to be real Unix time value rath
### Transcoders
-TBD
+Transcoder converts between byte arrays and objects for storage in the cache. Spyglass has 4 builtin factory-functions to initialize the right transcoder object for your use-case:
+
+ * **integer** - transcoder that serializes and unserializes only Integers.
+ *ps: setting a new value requires explicit type conversion, (int clojure-number)*
+
+ * **long** - transcoder that serializes and unserializes only Longs.
+
+ * **whalin** - transcoder that provides compatibility with Greg Whalin's memcached client. It can handle most of Clojure primitives and datastructures, except nil, record, exception. It useful when you want to integrate with [gwhalin/Memcached-Java-Client](https://github.com/gwhalin/Memcached-Java-Client).
+
+ * **serializing** - (*default*), Transcoder that serializes and compresses Java objects. It can handle most of Clojure primitives and datastructures, except nil, record, exception.
+
+###### Usage
+
+```clojure
+(require '[clojurewerkz.spyglass.client :as c])
+(require '[clojurewerkz.spyglass.transcoders :as t] :reload)
+(def tc (c/text-connection "192.168.99.100:11211"))
+
+(def nippler (t/make-transcoder :long))
+(c/set tc "abc" 30 (long 123) nippler)
+(c/get tc "abc" nippler)
+```
+
+###### Extending transcoders
+
+There maybe use-cases when you want to use transcoders that can handle wider subset of Clojure primitives - like records. Or you want to compress or encrypt a content before sending a data out to a memcached server.
+There're 2 very good Clojure libraries just for that: [Nippy](https://github.com/ptaoussanis/nippy) or [Data.Fressian](https://github.com/clojure/data.fressian)
+
+Here's a little example how to write a special purpose transcoder by extending [Transcoder](http://www.couchbase.com/autodocs/spymemcached-2.8.4/net/spy/memcached/transcoders/Transcoder.html) interface.
+
+```clojure
+(require '[taoensso.nippy :as nippy])
+(import '[net.spy.memcached CachedData]
+ '[net.spy.memcached.transcoders Transcoder]))
+
+;;experimental Nippy transcoder
+(deftype NippyTranscoder []
+ net.spy.memcached.transcoders.Transcoder
+ (asyncDecode [this dt] false) ;tells should it be encoded async
+ (getMaxSize [this]
+ (int CachedData/MAX_SIZE))
+
+ (decode [this dt]
+ (-> dt (.getData) nippy/thaw))
+
+ (encode [this clj-obj]
+ (CachedData. (int 0) ;optional flags for decoding
+ (nippy/freeze clj-obj)
+ CachedData/MAX_SIZE)))
+```
+
+Here's a full-list of data-types Nippy transcoder supports: [https://gist.github.com/timgluz/b89ad29316d4b2633de1](https://gist.github.com/timgluz/b89ad29316d4b2633de1)
## Getting keys
@@ -99,14 +180,40 @@ TBD
To get a value synchronously, use the `clojurewerkz.spyglass.client/get` function that takes a client instance, a string key, and returns a stored value:
-{% gist 64d87ac77b93e9a013b7 %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ ;; set a key that expires in 5 minutes (300 seconds)
+ _ (c/set tmc "a-key" 300 "a value")
+ ;; fetch it back
+ val (c/get tmc "a-key")]
+ ;; clojurewerkz.spyglass.client/get returns a value synchronously, no need to deref
+ val))
+```
### Asynchronous get
-Spyglass also provides a way to fetch a value asynchronously using the `clojurewerkz.spyglass.client/async-get` function that the same arguments but returns a future:
+Spyglass also provides a way to fetch a value asynchronously using
+the `clojurewerkz.spyglass.client/async-get` function that the same arguments but returns a future:
+
-{% gist ade1b1d1a5781d027412 %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ _ (c/set tmc "a-key" 300 "a value")
+ ;; fetch the value asynchronously
+ async-val (c/async-get tmc "a-key")]
+ ;; deref the result (a future)
+ @async-val))
+```
## Getting multiple keys in a single operation
@@ -115,22 +222,59 @@ Spyglass also provides a way to fetch a value asynchronously using the `clojurew
It is possible to get multiple values in a single request using the `clojurewerkz.spyglass.client/get-multi` function that takes a client instance, a collection of keys, and returns
an immutable map of stored values:
-{% gist 218b26c955e68e411f1d %}
-
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ _ (c/set tmc "a-key" 300 "a value")
+ _ (c/set tmc "another-key" 300 "another value")
+ ;; fetch both values back in a single request
+ m (c/get-multi tmc ["a-key" "another-key"])]
+ ;; clojurewerkz.spyglass.client/get-multi returns an immutable map synchronously, no need to deref
+ (println m)))
+```
### Asynchronous multi-get
Asynchronous multi-get is available via the `clojurewerkz.spyglass.client/async-get-multi` function that returns a future that, when dereferenced, returns a map
of results:
-{% gist 0610038900a52b02a154 %}
-
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ _ (c/set tmc "a-key" 300 "a value")
+ _ (c/set tmc "another-key" 300 "another value")
+ ;; fetch both values back in a single request
+ async-val (c/async-get-multi tmc ["a-key" "another-key"])]
+ ;; clojurewerkz.spyglass.client/async-get-multi returns a future that, when dereferenced,
+ ;; returns an immutable map
+ (println @async-val)))
+```
## Deleting keys
To delete a key, use the `clojurewerkz.spyglass.client/delete` function that has the same signature as (2-arity) `clojurewerkz.spyglass.client/get`:
-{% gist 3807c05c44bbbd1d21bd %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ _ (c/set tmc "a-key" 300 "a value")
+ async-val (c/delete tmc "a-key")]
+ ;; deletes are always asynchronous so to get the returned value
+ ;; you need to @dereference it
+ @async-val))
+```
Delete operations are always asynchronous and always return a future.
@@ -139,10 +283,21 @@ Delete operations are always asynchronous and always return a future.
To touch a key means to reset or update its expiration time using the `clojurewerkz.spyglass.client/touch` function:
-{% gist cc701c1a08235ab6115d %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ _ (c/set tmc "a-key" 300 "a value")
+ ;; touch the given key to reset its expiration time
+ async-val (c/touch tmc "a-key" 450)]
+ @async-val))
+```
When touching keys, values lower than `60*60*24*30` are treated as relative offset (a number of seconds starting from current time) and values greater than that
-are treated as absolute Unix timestamps.
+are treated as absolute Unix timestamps.
## add operation
@@ -150,7 +305,17 @@ are treated as absolute Unix timestamps.
Sometimes you only need to add a value to the cache but only if does not already exist. This is what the `clojurewerkz.spyglass.client/add` function does
in a single request:
-{% gist ec5949989148630aa4fe %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ ;; adds a value only if does not already exist
+ async-val (c/add tmc "a-key" 300 "a value")]
+ (println @async-val)))
+```
This function returns a future that, when dereferenced, returns a boolean (false if the mutation did not occur, true if it did).
@@ -160,7 +325,20 @@ This function returns a future that, when dereferenced, returns a boolean (false
`clojurewerkz.spyglass.client/replace` is similar to `clojurewerkz.spyglass.client/add` but will replace a value with the given one if
there is already a value for the given key:
-{% gist f6f6d318caf9f2fa33ec %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")
+ ;; replaces a value if it is already set
+ async-val1 (c/replace tmc "a-new-key" 300 "a value")
+ _ (c/set tmc "a-new-key" 300 "a value")
+ async-val2 (c/replace tmc "a-new-key" 300 "another value")]
+ (println @async-val1)
+ (println @async-val2)))
+```
This function returns a future that, when dereferenced, returns a boolean (false if the mutation did not occur, true if it did).
@@ -184,13 +362,29 @@ TBD
To disconnect, use the `clojurewerkz.spyglass.client/shutdown` function:
-{% gist bebff41cb472cafb7d8c %}
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
-It is also possible to let all running asynchronous operations to finish by disconnecting with a timeout:
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")]
+ ;; disconnect immediately
+ (c/shutdown tmc)))
+```
-{% gist 973fb66b88e07ce2a454 %}
+It is also possible to let all running asynchronous operations to finish by disconnecting with a timeout:
+```clojure
+(ns clojurewerkz.spyglass.examples
+ (:require [clojurewerkz.spyglass.client :as c]))
+(defn -main
+ [& args]
+ (let [tmc (c/text-connection "127.0.0.1:11211")]
+ ;; disconnect in 3 seconds
+ (c/shutdown tmc 3 java.util.concurrent.TimeUnit/SECONDS)))
+```
## Wrapping up
diff --git a/assets/stylesheets/sass/styles.sass b/assets/stylesheets/sass/styles.sass
index 133504b..732dd3e 100644
--- a/assets/stylesheets/sass/styles.sass
+++ b/assets/stylesheets/sass/styles.sass
@@ -18,8 +18,6 @@ strong
background-color: #f7f7f9
border: 1px solid #e1e1e8
-
-
.jumbotron
position: relative
@@ -123,5 +121,3 @@ h2
.container
padding: 20px
background: rgba(255, 255, 255, .7)
-
-
diff --git a/assets/stylesheets/syntax.css b/assets/stylesheets/syntax.css
new file mode 100644
index 0000000..daf76ad
--- /dev/null
+++ b/assets/stylesheets/syntax.css
@@ -0,0 +1,209 @@
+.highlight table td { padding: 5px; }
+.highlight table pre { margin: 0; }
+.highlight .cm {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .cp {
+ color: #999999;
+ font-weight: bold;
+}
+.highlight .c1 {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .cs {
+ color: #999999;
+ font-weight: bold;
+ font-style: italic;
+}
+.highlight .c, .highlight .cd {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .err {
+ color: #a61717;
+ background-color: #e3d2d2;
+}
+.highlight .gd {
+ color: #000000;
+ background-color: #ffdddd;
+}
+.highlight .ge {
+ color: #000000;
+ font-style: italic;
+}
+.highlight .gr {
+ color: #aa0000;
+}
+.highlight .gh {
+ color: #999999;
+}
+.highlight .gi {
+ color: #000000;
+ background-color: #ddffdd;
+}
+.highlight .go {
+ color: #888888;
+}
+.highlight .gp {
+ color: #555555;
+}
+.highlight .gs {
+ font-weight: bold;
+}
+.highlight .gu {
+ color: #aaaaaa;
+}
+.highlight .gt {
+ color: #aa0000;
+}
+.highlight .kc {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kd {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kn {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kp {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kr {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kt {
+ color: #445588;
+ font-weight: bold;
+}
+.highlight .k, .highlight .kv {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .mf {
+ color: #009999;
+}
+.highlight .mh {
+ color: #009999;
+}
+.highlight .il {
+ color: #009999;
+}
+.highlight .mi {
+ color: #009999;
+}
+.highlight .mo {
+ color: #009999;
+}
+.highlight .m, .highlight .mb, .highlight .mx {
+ color: #009999;
+}
+.highlight .sb {
+ color: #d14;
+}
+.highlight .sc {
+ color: #d14;
+}
+.highlight .sd {
+ color: #d14;
+}
+.highlight .s2 {
+ color: #d14;
+}
+.highlight .se {
+ color: #d14;
+}
+.highlight .sh {
+ color: #d14;
+}
+.highlight .si {
+ color: #d14;
+}
+.highlight .sx {
+ color: #d14;
+}
+.highlight .sr {
+ color: #009926;
+}
+.highlight .s1 {
+ color: #d14;
+}
+.highlight .ss {
+ color: #990073;
+}
+.highlight .s {
+ color: #d14;
+}
+.highlight .na {
+ color: #008080;
+}
+.highlight .bp {
+ color: #999999;
+}
+.highlight .nb {
+ color: #0086B3;
+}
+.highlight .nc {
+ color: #445588;
+ font-weight: bold;
+}
+.highlight .no {
+ color: #008080;
+}
+.highlight .nd {
+ color: #3c5d5d;
+ font-weight: bold;
+}
+.highlight .ni {
+ color: #800080;
+}
+.highlight .ne {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nf {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nl {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nn {
+ color: #555555;
+}
+.highlight .nt {
+ color: #000080;
+}
+.highlight .vc {
+ color: #008080;
+}
+.highlight .vg {
+ color: #008080;
+}
+.highlight .vi {
+ color: #008080;
+}
+.highlight .nv {
+ color: #008080;
+}
+.highlight .ow {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .o {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .w {
+ color: #bbbbbb;
+}
+.highlight {
+ background-color: #f8f8f8;
+}