From 221f6103ccf414319cbfdc81c2c85f20819be51c Mon Sep 17 00:00:00 2001 From: Timo Sulg Date: Sun, 13 Mar 2016 19:01:58 +0100 Subject: [PATCH] add docs for transcoders; updated jekyll; switched MD-renderer; add code-highlighting; --- .jekyll-metadata | Bin 0 -> 2144 bytes Gemfile | 4 +- Gemfile.lock | 71 +++------ README.md | 6 +- _config.yml | 7 +- _layouts/article.html | 1 + _layouts/default.html | 2 + _plugins/gist.rb | 35 ----- articles/getting_started.md | 232 +++++++++++++++++++++++++--- assets/stylesheets/sass/styles.sass | 4 - assets/stylesheets/syntax.css | 209 +++++++++++++++++++++++++ 11 files changed, 457 insertions(+), 114 deletions(-) create mode 100644 .jekyll-metadata delete mode 100644 _plugins/gist.rb create mode 100644 assets/stylesheets/syntax.css diff --git a/.jekyll-metadata b/.jekyll-metadata new file mode 100644 index 0000000000000000000000000000000000000000..49700b92fac3af14f3da828ed3f6c2870ad126e6 GIT binary patch literal 2144 zcmd6oze~eF6vu-|+A0;n*(HNp6I>Kh5Jaj3#5zPA9LkxzrdN|(%)4mQE^e+m`DdvA ziGPBFe~6dXmQwp8Z6mndkbC!epZmVcYQD2&D2LhwLWmltp5sPs?Vg1<7!V6I3}VM6 zh^oyj)JVvw+7suN8og2bHh*yHUyY#ypq$y)Las?h!3dL2KU^gb_OLz+bPS%_iihn z&$+T_wckcLKiYs~C@1sUbbxc}J2m7&FxZJx;bJ1g$5*a*?l4LA5)bBXBce9oWcooE zhPi(m%q9YRDe0+iL#rR1anApc+J-ubT;4x@k>HM&m>8P3$JxO>Pw7m&2O#FIN$v<* zAq|rCZ3JOl+Ew>ekNRq}JI!NYQrZ+#R9PH`Gb;;%%lQb9$Kq*hK%(ZFF^)VWK`8_b zn2+=hR1254a+(2CCLq 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; +}