├── .gems ├── .gems.dev ├── .github └── workflows │ ├── deploy.yml │ └── pull_request.yml ├── .gitignore ├── .ruby-version ├── README.md ├── Rakefile ├── app.rb ├── config.ru ├── deploy ├── downloads.json ├── lib ├── interactive │ ├── commands.rb │ ├── namespace.rb │ ├── redis.rb │ └── session.rb ├── reference.rb └── template.rb ├── makefile ├── public ├── app.js ├── images │ ├── companies │ │ └── bump.png │ ├── favicon.png │ ├── pivotal.png │ ├── redis-300dpi.png │ ├── redis-gray.png │ ├── redis-logo.svg │ ├── redis-small.png │ ├── redis-white.png │ ├── redis.png │ ├── redisdoc │ │ ├── 2idx_0.png │ │ ├── 2idx_1.png │ │ ├── 2idx_2.png │ │ ├── lru_comparison.png │ │ └── pipeline_iops.png │ ├── redislabs.png │ ├── shuttleworth.png │ └── vmware.png ├── opensearch.xml ├── presentation │ ├── Pnoordhuis_whats_new_in_2_2.pdf │ └── Redis_Cluster.pdf └── styles.css ├── scripts └── generate_interactive_commands.rb ├── test ├── clients.rb ├── command_reference.rb ├── formatting.rb ├── helper.rb ├── interactive │ └── namespace.rb ├── json.rb ├── sitemap.rb └── topics.rb └── views ├── 404.haml ├── avatar.haml ├── buzz.haml ├── clients.haml ├── commands.haml ├── commands └── name.haml ├── comments.haml ├── community.md ├── documentation.md ├── download.haml ├── grid.scss ├── home.haml ├── interactive.haml ├── layout.haml ├── modules.haml ├── normalize.scss ├── support.md └── topics └── name.haml /.gems: -------------------------------------------------------------------------------- 1 | redis -v 4.2.1 2 | cuba -v 3.8.0 3 | haml -v 4.0.7 4 | htmlentities -v 4.3.4 5 | oga -v 2.7 6 | ohm -v 0.1.5 7 | redcarpet -v 2.1.1 8 | tilt -v 2.0.5 9 | -------------------------------------------------------------------------------- /.gems.dev: -------------------------------------------------------------------------------- 1 | capybara -v 2.10.1 2 | cutest -v 1.2.3 3 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy website 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2.1.1 12 | - shell: bash 13 | env: 14 | DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} 15 | run: | 16 | curl -G https://redis.io/deploy --data-urlencode token=$DEPLOY_TOKEN -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Test pull request 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | check: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Install aspell 12 | run: sudo apt-get install aspell 13 | - run: docker run -d -p 6379:6379 redis 14 | - uses: ruby/setup-ruby@v1 15 | with: 16 | ruby-version: 2.6 17 | - uses: actions/checkout@v2.1.1 18 | - name: Install dep gem 19 | run: gem install dep 20 | - name: Install dependencies 21 | run: dep install 22 | - name: Install dev dependencies 23 | run: dep -f .gems.dev install 24 | - name: Run tests 25 | run: make test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | /.sass-cache 3 | /redis-doc 4 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | system 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redis-io 2 | 3 | This repository holds the source code for the website that runs [redis.io](http://redis.io). 4 | 5 | ## Getting started 6 | 7 | The required gems are listed in the .gems file. To get up and running, 8 | preferably using a gemset, run: 9 | 10 | gem install dep 11 | dep install 12 | 13 | Now you need to clone the [redis-doc](https://github.com/redis/redis-doc) 14 | project, and set its path in the `REDIS_DOC` environment variable before starting 15 | the server or running the tests. 16 | 17 | Finally, you need to have a `redis-server` running on port 6379. 18 | 19 | To start the website: 20 | 21 | REDIS_DOC=/path/to/redis-doc rackup 22 | 23 | To run the tests, you also need to install `cutest` and `capybara`: 24 | 25 | gem install cutest capybara 26 | 27 | Now, just run: 28 | 29 | REDIS_DOC=/path/to/redis-doc rake 30 | 31 | Or to run the tests in a particular file: 32 | 33 | REDIS_DOC=/path/to/redis-doc cutest test/some_file.rb 34 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :default => :test 2 | 3 | task :test do 4 | require "cutest" 5 | 6 | Cutest.run(Dir["test/**/*.rb"]) 7 | end 8 | 9 | task :formatting do 10 | require "cutest" 11 | 12 | Cutest.run(Dir["test/formatting.rb"]) 13 | end 14 | 15 | task :update do 16 | sh "rm -rf redis-doc" 17 | sh "git clone -q --depth 1 git://github.com/redis/redis-doc.git" 18 | sh "rm -rf redis-doc/.git" 19 | end 20 | 21 | desc "Deploy" 22 | task :deploy do 23 | script = <<-EOS 24 | cd ~/redis-doc 25 | git pull 26 | cd ~/redis-io 27 | git pull 28 | rvm 1.9.2 gem install dep --no-ri --no-rdoc 29 | (rvm 1.9.2 exec dep check || rvm 1.9.2 exec dep install) 30 | rvm 1.9.2 exec compass compile -c config/sass.rb views/styles.sass 31 | kill -s INT $(cat log/redis-io.pid) 32 | rvm 1.9.2 exec unicorn -D -c unicorn.rb -E production 33 | EOS 34 | 35 | sh "ssh redis-io '#{script.split("\n").map(&:strip).join(" && ")}'" 36 | end 37 | -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ROOT_PATH = File.expand_path(File.dirname(__FILE__)) 4 | 5 | require "cuba" 6 | require "cuba/render" 7 | require "date" 8 | require "digest/md5" 9 | require "haml" 10 | require "htmlentities" 11 | require "json" 12 | require "oga" 13 | require "ohm" 14 | require "open-uri" 15 | require "rack/static" 16 | require "redcarpet" 17 | require "redis" 18 | require "fileutils" 19 | 20 | require File.expand_path("lib/reference", ROOT_PATH) 21 | require File.expand_path("lib/template", ROOT_PATH) 22 | 23 | require File.expand_path("lib/interactive/namespace", ROOT_PATH) 24 | require File.expand_path("lib/interactive/session", ROOT_PATH) 25 | 26 | Encoding.default_external = Encoding::UTF_8 27 | 28 | DOWNLOADS = JSON.parse(File.read("downloads.json"), symbolize_names: true) 29 | 30 | STABLE_VERSION = DOWNLOADS.fetch(:channels).fetch(:stable).fetch(:version) 31 | 32 | module Kernel 33 | private 34 | 35 | def documentation_path 36 | $documentation_path ||= File.expand_path(ENV["REDIS_DOC"] || "redis-doc") 37 | end 38 | 39 | def commands 40 | $commands ||= Reference.new(JSON.parse(File.read(documentation_path + "/commands.json"))) 41 | end 42 | 43 | def new_redis_connection 44 | Redis.new(url: ENV["REDISCLOUD_URL"]) 45 | end 46 | 47 | def redis 48 | $redis ||= new_redis_connection 49 | end 50 | 51 | def redis_versions 52 | $redis_versions ||= redis.hgetall("versions") 53 | end 54 | 55 | def related_topics_for(command) 56 | # For now this is a quick and dirty way of figuring out related topics. 57 | 58 | path = "#{documentation_path}/topics/#{command.group}.md" 59 | 60 | return [] unless File.exist?(path) 61 | 62 | _, title = topic(path) 63 | 64 | [[title, "/topics/#{command.group}"]] 65 | end 66 | 67 | def related_commands_for(group) 68 | commands.select do |command| 69 | command.group == group && command.is_listed? 70 | end.sort_by(&:name) 71 | end 72 | 73 | def update_redis_versions 74 | tags = `git ls-remote -t https://github.com/redis/redis.git` 75 | 76 | versions = tags.scan(%r{refs/tags/(v?(?:\d\.?)*\-(?:stable|rc\w+|alpha\w+))}).flatten.uniq 77 | 78 | stable, development = versions.partition { |v| v =~ /^v/ } 79 | 80 | redis.hmset( 81 | "versions", 82 | "stable", stable.sort.last, 83 | "development", development.sort.last 84 | ) 85 | end 86 | 87 | def clean_version(version) 88 | version[/((?:\d\.?)+)/, 1] 89 | end 90 | 91 | def version_name(tag) 92 | tag[/v?(.*)/, 1].sub(/\-stable$/, "") 93 | end 94 | end 95 | 96 | Ohm.redis = redis 97 | 98 | class App < Cuba 99 | plugin Cuba::Render 100 | 101 | settings[:render][:template_engine] = "haml" 102 | 103 | use Rack::Static, 104 | urls: ["/images", "/presentation", "/opensearch.xml", "/styles.css", "/app.js"], 105 | root: File.join(ROOT_PATH, "public") 106 | 107 | def custom_render(path, locals = {}, options = {}) 108 | res.headers["Content-Type"] ||= "text/html; charset=utf-8" 109 | res.write(custom_view(path, locals, options)) 110 | end 111 | 112 | def custom_view(path, locals = {}, options = {}) 113 | options = { 114 | fenced_code_blocks: true, 115 | superscript: true, 116 | layout: true 117 | }.merge(options) 118 | 119 | path_with_extension = File.extname(path).empty? ? 120 | "#{path}.#{settings[:render][:template_engine]}" : 121 | path 122 | 123 | if path_with_extension.start_with?("/") 124 | expanded_path = path_with_extension 125 | else 126 | expanded_path = File.expand_path(path_with_extension, File.join(settings[:render][:views])) 127 | end 128 | 129 | layout_path = File.expand_path("#{settings[:render][:layout]}.#{settings[:render][:template_engine]}", File.join(settings[:render][:views])) 130 | 131 | data = _render(expanded_path, locals, options) 132 | 133 | unless options[:layout] == false 134 | data = _render(layout_path, locals.merge(content: data), options) 135 | end 136 | 137 | if expanded_path.start_with?(documentation_path) 138 | filter_interactive_examples(data) 139 | elsif expanded_path.start_with?(ROOT_PATH) && options[:anchors] != false 140 | add_header_ids(data) 141 | else 142 | data 143 | end 144 | end 145 | 146 | # Setup a new interactive session for every
 with @cli
147 |   def filter_interactive_examples(data)
148 |     namespace = Digest::MD5.hexdigest([rand(2**32), Time.now.usec, Process.pid].join("-"))
149 |     session = ::Interactive::Session.create(namespace)
150 | 
151 |     data.gsub %r{
\s*\s*(.*?)\s*\s*
}m do |match| 152 | lines = $1.split(/\n+/m).map(&:strip) 153 | _render("views/interactive.haml", session: session, lines: lines) 154 | end 155 | end 156 | 157 | def add_header_ids(data) 158 | data.gsub %r{(<(?h.)>(?
.*?))} do |match| 159 | found = $~ 160 | hdr = found[:hdr] 161 | section = found[:section] 162 | # convert spaces to underscores 163 | id = anchorize(section) 164 | %Q[<#{hdr} >*#{section}] 165 | end 166 | end 167 | 168 | def anchorize(str) 169 | str.downcase.gsub(/[\s+]/, '-').gsub(/[^[:alnum:]-]/, "") 170 | end 171 | 172 | def anchorize_language(str) 173 | # Addresses languages such as C++ and C# 174 | anchorize(str.gsub(/\+/, '-plus').gsub(/#/, '-sharp')) 175 | end 176 | 177 | def topic(template) 178 | body = custom_view(template, {}, layout: false) 179 | title = body[%r{

(.+?)

}, 1] # Nokogiri may be overkill 180 | 181 | return body, title 182 | end 183 | 184 | def gravatar_hash(email) 185 | Digest::MD5.hexdigest(email) 186 | end 187 | 188 | def not_found(locals = {path: nil}) 189 | res.status = 404 190 | res.write(custom_view("404", locals)) 191 | end 192 | 193 | define do 194 | on get, "" do 195 | custom_render("home", {}, anchors: false) 196 | end 197 | 198 | on get, "buzz" do 199 | custom_render("buzz", {}, anchors: false) 200 | end 201 | 202 | on get, "download" do 203 | custom_render("download") 204 | end 205 | 206 | on get, /(download|community|documentation|support)/ do |topic| 207 | @body, @title = topic("#{topic}.md") 208 | custom_render("topics/name") 209 | end 210 | 211 | on get, "commands" do 212 | on :name do |name| 213 | @name = name 214 | @title = @name.upcase.sub("-", " ") 215 | @command = commands[@title] 216 | 217 | if @command.nil? 218 | res.redirect "https://www.google.com/search?q=#{CGI.escape(name)}+site%3Aredis.io", 307 219 | halt res.finish 220 | end 221 | 222 | @related_commands = related_commands_for(@command.group) 223 | @related_topics = related_topics_for(@command) 224 | 225 | custom_render("commands/name") 226 | end 227 | 228 | on default do 229 | @commands = commands 230 | @title = "Command reference" 231 | 232 | custom_render("commands") 233 | end 234 | end 235 | 236 | on post, "session", /([0-9a-f]{32})/i do |id| 237 | if session = ::Interactive::Session.find(id) 238 | res.write session.run(req.params["command"].to_s) 239 | else 240 | res.status = 404 241 | res.write "ERR Session does not exist or has timed out." 242 | end 243 | end 244 | 245 | on get, "clients" do 246 | @clients = JSON.parse(File.read(documentation_path + "/clients.json")) 247 | @redis_tools = JSON.parse(File.read(documentation_path + "/tools.json")) 248 | 249 | @clients_by_language = @clients.group_by { |info| info["language"] }.sort_by { |name, _| name.downcase } 250 | 251 | custom_render("clients") 252 | end 253 | 254 | on get, "modules" do 255 | @modules = JSON.parse(File.read(documentation_path + "/modules.json")) 256 | @modules = @modules.sort_by {|m| -m["stars"]} 257 | custom_render("modules") 258 | end 259 | 260 | on get, "topics/:name" do |name| 261 | path = "/topics/#{name}.md" 262 | 263 | if File.exist?(File.join(documentation_path, path)) 264 | @css = [:topics, name] 265 | @body, @title = topic(File.join(documentation_path, path)) 266 | @related_commands = related_commands_for(name) 267 | 268 | custom_render("topics/name") 269 | else 270 | not_found(path: path) 271 | end 272 | end 273 | 274 | on get, "deploy" do 275 | if ENV["DEPLOY_TOKEN"] && req.GET["token"] == ENV["DEPLOY_TOKEN"] 276 | FileUtils.touch("deploy.txt") 277 | else 278 | res.status = 401 279 | end 280 | end 281 | 282 | on get, extension("json") do |file| 283 | res.headers["Cache-Control"] = "public, max-age=29030400" if req.query_string =~ /[0-9]{10}/ 284 | res.headers["Content-Type"] = "application/json;charset=UTF-8" 285 | res.write File.read(documentation_path + "/#{file}.json") 286 | end 287 | 288 | on get, extension("js") do |file| 289 | res.headers["Cache-Control"] = "public, max-age=29030400" if req.query_string =~ /[0-9]{10}/ 290 | res.headers["Content-Type"] = "text/javascript; charset=utf-8" 291 | res.write File.read("views/#{file}.js") 292 | end 293 | 294 | on post, "commits/payload" do 295 | update_redis_versions 296 | end 297 | end 298 | end 299 | 300 | Cuba.define { 301 | begin 302 | run App 303 | rescue Exception => e 304 | res.status = 500 305 | res.write "I'm sorry, Dave. I'm afraid I can't do that." 306 | end 307 | } 308 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require File.expand_path("app", File.dirname(__FILE__)) 2 | 3 | run Cuba 4 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while true; do 4 | inotifywait deploy.txt 5 | make deploy 6 | done 7 | -------------------------------------------------------------------------------- /downloads.json: -------------------------------------------------------------------------------- 1 | { 2 | "channels": { 3 | "unstable": { 4 | "branch": "unstable", 5 | "notes": "This is where all the development happens. Only for hard-core hackers. Use only if you need to test the latest features or performance improvements. This is going to be the next Redis release in a few months." 6 | }, 7 | 8 | "release-candidate": { 9 | "branch": "7.0", 10 | "version": "7.0-rc2", 11 | "notes": "Redis 7.0 includes several new user-facing features, significant performance optimizations, and many other improvements. It also includes changes that potentially break backwards compatibility with older versions." 12 | }, 13 | 14 | "stable": { 15 | "branch": "6.2", 16 | "version": "6.2.6", 17 | "notes": "Redis 6.2 includes many new commands and improvements, but no big features. It mainly makes Redis more complete and addresses issues that have been requested by many users frequently or for a long time." 18 | }, 19 | 20 | "docker": { 21 | "name": "Docker Hub", 22 | "url": "https://hub.docker.com/_/redis/", 23 | "notes": "It is possible to get Docker images of Redis from the Docker Hub. Multiple versions are available, usually updated in a short time after a new release is available." 24 | }, 25 | 26 | "cloud": { 27 | "name": "In the Cloud", 28 | "url": "https://redis.com/try-free", 29 | "notes": "Get a free-for-life Redis instance with Redis Cloud Essentials." 30 | } 31 | }, 32 | 33 | "other": { 34 | "old": { 35 | "branch": "6.0", 36 | "version": "6.0.16", 37 | "notes": "Redis 6.0 introduces SSL, the new RESP3 protocol, ACLs, client side caching, diskless replicas, I/O threads, faster RDB loading, new modules APIs and many more improvements." 38 | }, 39 | 40 | "older": { 41 | "branch": "5.0", 42 | "version": "5.0.14", 43 | "notes": "Redis 5.0 is the first version of Redis to introduce the new stream data type with consumer groups, sorted sets blocking pop operations, LFU/LRU info in RDB, Cluster manager inside redis-cli, active defragmentation V2, HyperLogLogs improvements and many other improvements. Redis 5 was release as GA in October 2018." 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/interactive/commands.rb: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by: 2 | # scripts/generate_interactive_commands.rb. 3 | # 4 | # Do not edit. 5 | # 6 | 7 | module Interactive 8 | COMMANDS = { 9 | # connection 10 | "client info" => [], 11 | "echo" => [:zip, [nil]], 12 | "ping" => [:custom], 13 | 14 | # generic 15 | "del" => [:all], 16 | "dump" => [:first], 17 | "exists" => [:all], 18 | "expire" => [:first], 19 | "expireat" => [:first], 20 | "keys" => [:zip, [nil]], 21 | "persist" => [:first], 22 | "pexpire" => [:first], 23 | "pexpireat" => [:first], 24 | "pttl" => [:first], 25 | "rename" => [:all], 26 | "renamenx" => [:all], 27 | "scan" => [:custom], 28 | "sort" => [:custom], 29 | "touch" => [:all], 30 | "ttl" => [:first], 31 | "type" => [:first], 32 | "unlink" => [:all], 33 | "wait" => [:zip, [nil, nil]], 34 | 35 | # geo 36 | "geoadd" => [:first], 37 | "geodist" => [:first], 38 | "geohash" => [:first], 39 | "geopos" => [:first], 40 | "georadius" => [:custom], 41 | "georadiusbymember" => [:custom], 42 | "geosearch" => [:first], 43 | "geosearchstore" => [:custom], 44 | 45 | # hash 46 | "hdel" => [:first], 47 | "hexists" => [:first], 48 | "hget" => [:first], 49 | "hgetall" => [:first], 50 | "hincrby" => [:first], 51 | "hincrbyfloat" => [:first], 52 | "hkeys" => [:first], 53 | "hlen" => [:first], 54 | "hmget" => [:first], 55 | "hmset" => [:first], 56 | "hscan" => [:first], 57 | "hset" => [:first], 58 | "hsetnx" => [:first], 59 | "hstrlen" => [:first], 60 | "hvals" => [:first], 61 | 62 | # hyperloglog 63 | "pfadd" => [:first], 64 | "pfcount" => [:all], 65 | "pfmerge" => [:all], 66 | 67 | # list 68 | "blmove" => [:zip, [:key, :key, nil, nil, nil]], 69 | "lindex" => [:first], 70 | "linsert" => [:first], 71 | "llen" => [:first], 72 | "lmove" => [:zip, [:key, :key, nil, nil]], 73 | "lpop" => [:first], 74 | "lpos" => [:first], 75 | "lpush" => [:first], 76 | "lpushx" => [:first], 77 | "lrange" => [:first], 78 | "lrem" => [:first], 79 | "lset" => [:first], 80 | "ltrim" => [:first], 81 | "rpop" => [:first], 82 | "rpoplpush" => [:all], 83 | "rpush" => [:first], 84 | "rpushx" => [:first], 85 | 86 | # server 87 | "command" => [], 88 | "command count" => [], 89 | "command getkeys" => [], 90 | "command info" => [:custom], 91 | "info" => [:custom], 92 | "lastsave" => [], 93 | "role" => [], 94 | "time" => [], 95 | 96 | # set 97 | "sadd" => [:first], 98 | "scard" => [:first], 99 | "sdiff" => [:all], 100 | "sdiffstore" => [:all], 101 | "sinter" => [:all], 102 | "sinterstore" => [:all], 103 | "sismember" => [:first], 104 | "smembers" => [:first], 105 | "smismember" => [:first], 106 | "smove" => [:zip, [:key, :key, nil]], 107 | "spop" => [:first], 108 | "srandmember" => [:first], 109 | "srem" => [:first], 110 | "sscan" => [:first], 111 | "sunion" => [:all], 112 | "sunionstore" => [:all], 113 | 114 | # sorted-set 115 | "zadd" => [:first], 116 | "zcard" => [:first], 117 | "zcount" => [:first], 118 | "zdiff" => [:custom], 119 | "zdiffstore" => [:custom], 120 | "zincrby" => [:first], 121 | "zinter" => [:custom], 122 | "zinterstore" => [:custom], 123 | "zlexcount" => [:first], 124 | "zmscore" => [:first], 125 | "zpopmax" => [:first], 126 | "zpopmin" => [:first], 127 | "zrange" => [:first], 128 | "zrangebylex" => [:first], 129 | "zrangebyscore" => [:first], 130 | "zrank" => [:first], 131 | "zrem" => [:first], 132 | "zremrangebylex" => [:first], 133 | "zremrangebyrank" => [:first], 134 | "zremrangebyscore" => [:first], 135 | "zrevrange" => [:first], 136 | "zrevrangebylex" => [:first], 137 | "zrevrangebyscore" => [:first], 138 | "zrevrank" => [:first], 139 | "zscan" => [:first], 140 | "zscore" => [:first], 141 | "zunion" => [:custom], 142 | "zunionstore" => [:custom], 143 | 144 | # stream 145 | "xadd" => [:first], 146 | "xdel" => [:first], 147 | "xlen" => [:first], 148 | "xrange" => [:first], 149 | "xrevrange" => [:first], 150 | "xtrim" => [:first], 151 | 152 | # string 153 | "append" => [:first], 154 | "bitcount" => [:first], 155 | "bitfield" => [:first], 156 | "bitop" => [:custom], 157 | "bitpos" => [:first], 158 | "decr" => [:first], 159 | "decrby" => [:first], 160 | "get" => [:first], 161 | "getbit" => [:first], 162 | "getrange" => [:first], 163 | "getset" => [:first], 164 | "incr" => [:first], 165 | "incrby" => [:first], 166 | "incrbyfloat" => [:first], 167 | "mget" => [:all], 168 | "mset" => [:zip, [:key, nil]], 169 | "msetnx" => [:zip, [:key, nil]], 170 | "psetex" => [:first], 171 | "set" => [:first], 172 | "setbit" => [:first], 173 | "setex" => [:first], 174 | "setnx" => [:first], 175 | "setrange" => [:first], 176 | "stralgo" => [:custom], 177 | "strlen" => [:first], 178 | 179 | }.freeze 180 | 181 | SUBCOMMANDS = { 182 | "client" => 1, 183 | 184 | }.freeze 185 | end 186 | 187 | -------------------------------------------------------------------------------- /lib/interactive/namespace.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/commands") 2 | 3 | module Interactive 4 | 5 | def self.namespace(ns, args) 6 | pattern(args).map do |arg,type| 7 | if type == :key || type == :ns 8 | [ns, arg].join(":") 9 | else 10 | arg 11 | end 12 | end 13 | end 14 | 15 | def self.keys(args) 16 | pattern(args).map do |arg,type| 17 | arg if type == :key 18 | end.compact 19 | end 20 | 21 | def self.command_name(args) 22 | name = args.shift 23 | subcommands = SUBCOMMANDS[name.downcase] 24 | if not subcommands.nil? 25 | name = name + " " + args.shift(subcommands).join(" ") 26 | end 27 | return name 28 | end 29 | 30 | def self.pattern(args) 31 | args = args.dup 32 | name = command_name(args) 33 | return [] if COMMANDS[name.downcase].nil? 34 | type, pattern = COMMANDS[name.downcase] 35 | out = [] 36 | 37 | case type 38 | when :first 39 | out[0] = :key 40 | when :all 41 | out = args.size.times.map { :key } 42 | when :zip 43 | out = args.zip(pattern.cycle).map do |arg, type| 44 | :key if type == :key 45 | end 46 | when :custom 47 | case name.downcase 48 | when "info", "geoencode", "ping" 49 | # Commands without keys 50 | nil 51 | when "bitop" 52 | # BITOP arg positions 1,2,3 are always keys 53 | out[1,3] = 3.times.map{:key} 54 | when "zunionstore", "zinterstore" 55 | # Destination key 56 | if args.size >= 1 57 | out[0] = :key 58 | 59 | # Number of input keys 60 | if args.size >= 3 61 | numkeys = args[1].to_i 62 | out[2,numkeys] = numkeys.times.map { :key } 63 | end 64 | end 65 | when "sort" 66 | tmpargs = args.dup 67 | 68 | # Key to sort 69 | if !tmpargs.empty? 70 | tmpargs.shift 71 | out << :key 72 | 73 | while keyword = tmpargs.shift 74 | out << nil 75 | if !tmpargs.empty? 76 | case keyword.downcase 77 | when "get" 78 | if tmpargs.shift == "#" 79 | out << nil 80 | else 81 | out << :key 82 | end 83 | when "by", "store" 84 | tmpargs.shift 85 | out << :key 86 | when "limit" 87 | break if tmpargs.size < 2 88 | 2.times { tmpargs.shift; out << nil } 89 | end 90 | end 91 | end 92 | end 93 | when "geosearchstore" 94 | out = [:key, :key] 95 | when "zdiff", "zinter", "zunion" 96 | numkeys = args[0].to_i 97 | out[1,numkeys] = numkeys.times.map { :key } 98 | when "zdiffstore" 99 | numkeys = args[1].to_i 100 | out[0] = :key 101 | out[2,numkeys] = numkeys.times.map { :key } 102 | when "zrangestore" 103 | out = [:key, :key] 104 | when "georadius","georadiusbymember" 105 | tmpargs = args.dup 106 | 107 | # First key with the sorted set 108 | if !tmpargs.empty? 109 | tmpargs.shift 110 | out << :key 111 | 112 | while keyword = tmpargs.shift 113 | out << nil 114 | if !tmpargs.empty? 115 | case keyword.downcase 116 | when "store", "storedist" 117 | tmpargs.shift 118 | out << :key 119 | end 120 | end 121 | end 122 | end 123 | else 124 | raise "Don't know what to do for \"#{name.downcase}\"" 125 | end 126 | end 127 | 128 | # Hack KEYS command 129 | if name.downcase == "keys" 130 | out[0] = :ns 131 | end 132 | 133 | cmd = name.split(" ") 134 | out = args.zip(out).to_a 135 | [*cmd, *out] 136 | end 137 | end 138 | 139 | -------------------------------------------------------------------------------- /lib/interactive/redis.rb: -------------------------------------------------------------------------------- 1 | module Interactive 2 | 3 | class LineReply < String; end 4 | class StatusReply < LineReply; end 5 | class ErrorReply < LineReply; end 6 | 7 | module RedisHacks 8 | 9 | def format_status_reply(line) 10 | StatusReply.new(line.strip) 11 | end 12 | 13 | def format_error_reply(line) 14 | ErrorReply.new(line.strip) 15 | end 16 | end 17 | 18 | def self.redis 19 | @redis ||= 20 | begin 21 | redis = new_redis_connection 22 | class << redis._client.connection 23 | include RedisHacks 24 | end 25 | redis 26 | end 27 | end 28 | end 29 | 30 | -------------------------------------------------------------------------------- /lib/interactive/session.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/redis") 2 | 3 | module Interactive 4 | 5 | UNESCAPES = { 6 | 'a' => "\x07", 'b' => "\x08", 't' => "\x09", 7 | 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c", 8 | 'r' => "\x0d", 'e' => "\x1b", "\\\\" => "\x5c", 9 | "\"" => "\x22", "'" => "\x27" 10 | } 11 | 12 | # Create and find actions have race conditions, but I don't 13 | # really care about them right now... 14 | class Session 15 | 16 | TIMEOUT = 3600 17 | 18 | # Create new instance. 19 | def self.create(namespace) 20 | raise "Already exists" if redis.zscore("sessions", namespace) 21 | touch(namespace) 22 | new(namespace) 23 | end 24 | 25 | # Return instance if namespace exists in sorted set. 26 | def self.find(namespace) 27 | if timestamp = redis.zscore("sessions", namespace) 28 | if Time.now.to_i - timestamp.to_i < TIMEOUT 29 | touch(namespace) 30 | new(namespace) 31 | end 32 | end 33 | end 34 | 35 | # Try to clean up old sessions 36 | def self.clean! 37 | now = Time.now.to_i 38 | threshold = now - TIMEOUT 39 | namespace, timestamp = redis.zrangebyscore("sessions", "-inf", threshold, 40 | :with_scores => true, :limit => [0, 1]) 41 | return if namespace.nil? 42 | 43 | if redis.zrem("sessions", namespace) 44 | keys = redis.smembers("session:#{namespace}:keys") 45 | redis.del(*keys.map { |key| "#{namespace}:#{key}" }) if !keys.empty? 46 | redis.del("session:#{namespace}:keys") 47 | redis.del("session:#{namespace}:commands") 48 | end 49 | end 50 | 51 | # This should only be created through #new or #create 52 | private_class_method :new 53 | 54 | attr :namespace 55 | 56 | def initialize(namespace) 57 | @namespace = namespace 58 | self.class.clean! 59 | end 60 | 61 | def run(line) 62 | _run(line) 63 | rescue => error 64 | format_reply(ErrorReply.new("ERR " + error.message)) 65 | end 66 | 67 | private 68 | 69 | def self.touch(namespace) 70 | redis.zadd("sessions", Time.now.to_i, namespace) 71 | end 72 | 73 | def register(arguments) 74 | # TODO: Only store keys that receive writes. 75 | keys = ::Interactive.keys(arguments) 76 | redis.pipelined do 77 | keys.each do |key| 78 | redis.sadd("session:#{namespace}:keys", key) 79 | end 80 | end 81 | 82 | # Command counter, not yet used 83 | redis.incr("session:#{namespace}:commands") 84 | end 85 | 86 | def unescape_literal(str) 87 | # Escape all the things 88 | str.gsub(/\\(?:([#{UNESCAPES.keys.join}])|u([\da-fA-F]{4}))|\\0?x([\da-fA-F]{2})/) { 89 | if $1 90 | if $1 == '\\' then '\\' else UNESCAPES[$1] end 91 | elsif $2 # escape \u0000 unicode 92 | ["#$2".hex].pack('U*') 93 | elsif $3 # escape \0xff or \xff 94 | [$3].pack('H2') 95 | end 96 | } 97 | end 98 | 99 | # Parse a command line style list of arguments that can be optionally 100 | # delimited by '' or "" quotes, and return it as an array of arguments. 101 | # 102 | # Strings delimited by "" are unescaped by converting escape characters 103 | # such as \n \x.. to their value according to the unescape_literal() 104 | # function. 105 | # 106 | # Example of line that this function can parse: 107 | # 108 | # "Hello World\n" other arguments 'this is a single argument' 109 | # 110 | # The above example will return an array of four strings. 111 | def cli_split(line) 112 | argv = [] 113 | arg = "" 114 | inquotes = false 115 | pos = 0 116 | while pos < line.length 117 | char = line[pos..pos] # Current character 118 | isspace = char =~ /\s/ 119 | 120 | # Skip empty spaces if we are between strings 121 | if !inquotes && isspace 122 | if arg.length != 0 123 | argv << arg 124 | arg = "" 125 | end 126 | pos += 1 127 | next 128 | end 129 | 130 | # Append current char to string 131 | arg << char 132 | pos += 1 133 | 134 | if arg.length == 1 && (char == '"' || char == '\'') 135 | inquotes = char 136 | elsif arg.length > 1 && inquotes && char == inquotes 137 | inquotes = false 138 | end 139 | end 140 | # Put the last argument into the array 141 | argv << arg if arg.length != 0 142 | 143 | # We need to make some post-processing. 144 | # For strings delimited by '' we just strip initial and final '. 145 | # For strings delimited by "" we call unescape_literal(). 146 | # This is not perfect but should be enough for redis.io interactive 147 | # editing. 148 | argv.map {|x| 149 | if x[0..0] == '"' 150 | unescape_literal(x[1..-2]) 151 | elsif x[0..0] == '\'' 152 | x[1..-2] 153 | else 154 | x 155 | end 156 | } 157 | end 158 | 159 | def _run(line) 160 | begin 161 | arguments = cli_split(line) 162 | rescue => error 163 | raise error.message.split(":").first 164 | end 165 | 166 | if arguments.empty? 167 | raise "No command" 168 | end 169 | 170 | if arguments.size > 100 || arguments.any? { |arg| arg.size > 100 } 171 | raise "Web-based interface is limited" 172 | end 173 | 174 | case arguments[0].downcase 175 | when "setbit" 176 | if arguments[2].to_i >= 2048 177 | raise "Web-based interface is limited" 178 | end 179 | when "setrange" 180 | if arguments[2].to_i + arguments[3].to_s.size >= 256 181 | raise "Web-based interface is limited" 182 | end 183 | end 184 | 185 | namespaced = ::Interactive.namespace(namespace, arguments) 186 | if namespaced.empty? 187 | raise "Unknown or disabled command '%s'" % arguments.first 188 | end 189 | 190 | # Register the call 191 | register(arguments) 192 | 193 | # Make the call 194 | reply = ::Interactive.redis.call(*namespaced) 195 | 196 | case arguments.first.downcase 197 | when "keys" 198 | # Strip namespace for KEYS 199 | if reply.respond_to?(:map) 200 | format_reply(reply.map { |key| key[/^\w+:(.*)$/,1] }) 201 | else 202 | format_reply(reply) 203 | end 204 | when "info" 205 | # Don't #inspect the string reply for INFO 206 | reply.to_s 207 | else 208 | format_reply(reply) 209 | end 210 | end 211 | 212 | def format_reply(reply, prefix = "") 213 | case reply 214 | when LineReply 215 | reply.to_s + "\n" 216 | when Integer 217 | "(integer) " + reply.to_s + "\n" 218 | when String 219 | reply.inspect + "\n" 220 | when NilClass 221 | "(nil)\n" 222 | when Array 223 | if reply.empty? 224 | "(empty list or set)\n" 225 | else 226 | out = "" 227 | index_size = reply.size.to_s.size 228 | reply.each_with_index do |element, index| 229 | out << prefix if index > 0 230 | out << "%#{index_size}d) " % (index + 1) 231 | out << format_reply(element, prefix + (" " * (index_size + 2))) 232 | end 233 | out 234 | end 235 | else 236 | raise "Don't know how to format #{reply.inspect}" 237 | end 238 | end 239 | end 240 | end 241 | 242 | -------------------------------------------------------------------------------- /lib/reference.rb: -------------------------------------------------------------------------------- 1 | class Reference 2 | GROUPS = { 3 | "generic" => "Keys", 4 | "string" => "Strings", 5 | "hash" => "Hashes", 6 | "list" => "Lists", 7 | "set" => "Sets", 8 | "sorted-set" => "Sorted Sets", 9 | "hyperloglog" => "HyperLogLog", 10 | "pubsub" => "Pub/Sub", 11 | "transactions" => "Transactions", 12 | "scripting" => "Scripting", 13 | "connection" => "Connection", 14 | "server" => "Server", 15 | "cluster" => "Cluster", 16 | "geo" => "Geo", 17 | "stream" => "Streams", 18 | "bitmap" => "Bitmaps", 19 | "cluster" => "Cluster", 20 | "sentinel" => "Sentinel" 21 | } 22 | 23 | class Command 24 | class Argument 25 | attr :argument 26 | 27 | def initialize(argument) 28 | @argument = argument 29 | end 30 | 31 | def type 32 | argument["type"] 33 | end 34 | 35 | def optional? 36 | argument["optional"] || false 37 | end 38 | 39 | def multiple? 40 | argument["multiple"] || false 41 | end 42 | 43 | def multiple_token? 44 | argument["multiple_token"] || false 45 | end 46 | 47 | def to_s 48 | if type == "block" 49 | res = block(argument) 50 | elsif type == "oneof" 51 | res = oneof(argument) 52 | elsif type != "pure-token" 53 | res = argument["name"] 54 | else 55 | res = "" 56 | end 57 | 58 | token = argument["token"] 59 | if token == "" 60 | token = "\"\"" 61 | end 62 | if multiple_token? 63 | res = "#{res} [#{token} #{res} ...]" 64 | elsif multiple? 65 | res = "#{res} [#{res} ...]" 66 | end 67 | 68 | if token 69 | res = "#{token} #{res}" 70 | res = res.strip! || res 71 | end 72 | 73 | optional? ? "[#{res}]" : res 74 | end 75 | 76 | private 77 | 78 | def block(argument) 79 | argument["arguments"].map do |entry| 80 | Argument.new(entry) 81 | end.join(" ") 82 | end 83 | 84 | def oneof(argument) 85 | argument["arguments"].map do |entry| 86 | Argument.new(entry) 87 | end.join("|") 88 | end 89 | end 90 | 91 | attr :name 92 | attr :command 93 | attr :group 94 | 95 | def initialize(name, command) 96 | @name = name 97 | @command = command 98 | end 99 | 100 | def to_s 101 | @to_s ||= [name, *arguments].join(" ") 102 | end 103 | 104 | def since 105 | command["since"] 106 | end 107 | 108 | def group 109 | command["group"] 110 | end 111 | 112 | def complexity 113 | command["complexity"] 114 | end 115 | 116 | def deprecated_since 117 | command["deprecated_since"] 118 | end 119 | 120 | def replaced_by 121 | command["replaced_by"] 122 | end 123 | 124 | def history 125 | command["history"] 126 | end 127 | 128 | def is_helpsubcommand? 129 | name.downcase.end_with?(" help") 130 | end 131 | 132 | def is_purecontainer? 133 | command["arity"] == -2 && !command["arguments"] 134 | end 135 | 136 | def is_listed? 137 | !is_purecontainer? && !is_helpsubcommand? 138 | end 139 | 140 | def to_param 141 | name.downcase.gsub(" ", "-") 142 | end 143 | 144 | def arguments 145 | (command["arguments"] || []).map do |argument| 146 | Argument.new(argument) 147 | end 148 | end 149 | 150 | include Comparable 151 | 152 | def ==(other) 153 | name == other.name 154 | end 155 | alias eql? == 156 | 157 | def hash 158 | name.hash 159 | end 160 | end 161 | 162 | include Enumerable 163 | 164 | def initialize(commands) 165 | @commands = commands 166 | end 167 | 168 | def [](name) 169 | Command.new(name, @commands[name]) if @commands[name] 170 | end 171 | 172 | def each 173 | @commands.each do |name, attrs| 174 | yield Command.new(name, attrs) 175 | end 176 | end 177 | 178 | def sample 179 | key = @commands.keys.sample 180 | 181 | Command.new(key, @commands[key]) 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/template.rb: -------------------------------------------------------------------------------- 1 | require "tilt/redcarpet" 2 | 3 | class RedisTemplate < Tilt::Redcarpet2Template 4 | SECTIONS = { 5 | "description" => "Description", 6 | "examples" => "Examples", 7 | "return" => "Return value", 8 | "history" => "History" 9 | } 10 | 11 | REPLY_TYPES = { 12 | "nil" => "Null reply", 13 | "simple-string" => "Simple string reply", 14 | "integer" => "Integer reply", 15 | "bulk-string" => "Bulk string reply", 16 | "array" => "Array reply" 17 | } 18 | 19 | def sections(source) 20 | source.gsub(/^\@(\w+)$/) do 21 | title = SECTIONS[$1] 22 | "## #{title}\n" 23 | end 24 | end 25 | 26 | # Prefix commands that should *not* be autolinked with "!". 27 | def autolink_commands(source) 28 | source.gsub(/\B`(!?[A-Z\- ]+)`\B/) do 29 | name = $1 30 | command = commands[name] 31 | 32 | if command 33 | "[#{name}](/commands/#{name.downcase.gsub(' ', '-')})" 34 | else 35 | name.gsub!(/^!/, "") 36 | "`#{name}`" 37 | end 38 | end 39 | end 40 | 41 | def reply_types(source) 42 | source.gsub(/@(#{REPLY_TYPES.keys.join("|")})\-reply/) do 43 | type = $1 44 | "[#{REPLY_TYPES[type]}](/topics/protocol##{type}-reply)" 45 | end 46 | end 47 | 48 | def formulas(source) 49 | source.gsub(/(O\(.+?\)[\+\s\.,])/) do 50 | %Q[#{$1}] 51 | end 52 | end 53 | 54 | def erb(data) 55 | ERB.new(data).result(binding) 56 | end 57 | 58 | def preprocess(data) 59 | data = erb(data) 60 | data = sections(data) 61 | data = autolink_commands(data) 62 | data = reply_types(data) 63 | data = formulas(data) 64 | data 65 | end 66 | 67 | def prepare 68 | @data = preprocess(@data) 69 | super 70 | end 71 | end 72 | 73 | Tilt.register "md", RedisTemplate 74 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | TEST_FILES=$(shell find test -name '*.rb') 2 | 3 | test: 4 | test -x redis-doc || git clone https://github.com/redis/redis-doc 5 | cutest $(TEST_FILES) 6 | 7 | deploy: 8 | cd /srv/redis-doc && git pull 9 | cd /srv/redis-io && git stash && git pull 10 | # bash --login -c "cd /srv/redis-io && rvm use 2.7.0 && REDIS_DOC=/srv/redis-doc /srv/redis-io/scripts/generate_interactive_commands.rb > /srv/redis-io/lib/interactive/commands.rb" 11 | service redis-io-app restart 12 | 13 | .PHONY: deploy test 14 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Slideout=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o self._tolerance) ? self.open() : self.close(); 182 | } 183 | self._moved = false; 184 | }); 185 | 186 | /** 187 | * Translates panel on touchmove 188 | */ 189 | this.panel.addEventListener(touch.move, function(eve) { 190 | 191 | if (scrolling || self._preventOpen) { return; } 192 | 193 | var dif_x = eve.touches[0].clientX - self._startOffsetX; 194 | var translateX = self._currentOffsetX = dif_x; 195 | 196 | if (Math.abs(translateX) > self._padding) { return; } 197 | 198 | if (Math.abs(dif_x) > 20) { 199 | self._opening = true; 200 | 201 | if (self._opened && dif_x > 0 || !self._opened && dif_x < 0) { return; } 202 | 203 | if (!self._moved && html.className.search('slideout-open') === -1) { 204 | html.className += ' slideout-open'; 205 | } 206 | 207 | if (dif_x <= 0) { 208 | translateX = dif_x + self._padding; 209 | self._opening = false; 210 | } 211 | 212 | self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translate3d(' + translateX + 'px, 0, 0)'; 213 | 214 | self._moved = true; 215 | } 216 | 217 | }); 218 | 219 | }; 220 | 221 | /** 222 | * Expose Slideout 223 | */ 224 | module.exports = Slideout; 225 | 226 | },{"decouple":2}],2:[function(require,module,exports){ 227 | 'use strict'; 228 | 229 | var requestAnimFrame = (function() { 230 | return window.requestAnimationFrame || 231 | window.webkitRequestAnimationFrame || 232 | function (callback) { 233 | window.setTimeout(callback, 1000 / 60); 234 | }; 235 | }()); 236 | 237 | function decouple(node, event, fn) { 238 | var eve, 239 | tracking = false; 240 | 241 | function captureEvent(e) { 242 | eve = e; 243 | track(); 244 | } 245 | 246 | function track() { 247 | if (!tracking) { 248 | requestAnimFrame(update); 249 | tracking = true; 250 | } 251 | } 252 | 253 | function update() { 254 | fn.call(node, eve); 255 | tracking = false; 256 | } 257 | 258 | node.addEventListener(event, captureEvent, false); 259 | } 260 | 261 | /** 262 | * Expose decouple 263 | */ 264 | module.exports = decouple; 265 | 266 | },{}]},{},[1])(1) 267 | }); 268 | 269 | 270 | 271 | ;(function($) { 272 | 273 | var cssPrefix = null; 274 | 275 | if ($.browser.mozilla) cssPrefix = "moz"; 276 | else if ($.browser.webkit) cssPrefix = "webkit"; 277 | else if ($.browser.opera) cssPrefix = "o"; 278 | 279 | $.cssHooks["columnCount"] = { 280 | get: function(element, computed) { 281 | var browserSpecificName = "-" + cssPrefix + "-column-count"; 282 | 283 | if (computed) { 284 | return $.css(element, browserSpecificName); 285 | } 286 | else { 287 | return element.style[browserSpecificName]; 288 | } 289 | } 290 | } 291 | 292 | function commandReference() { 293 | var $groups = $("#commands nav a") 294 | 295 | $groups.click(function() { 296 | window.location.hash = this.getAttribute("href").substring(1) 297 | 298 | filterCommandReference() 299 | 300 | return false 301 | }) 302 | 303 | var filter = document.querySelector('.command-reference-filter'); 304 | 305 | filter.addEventListener('change', function(e) { 306 | window.location.hash = e.target.value; 307 | }); 308 | 309 | window.onhashchange = function() { 310 | filterCommandReference(); 311 | } 312 | } 313 | 314 | function filterCommandReference() { 315 | var $commands = $("#commands ul") 316 | 317 | var group = window.location.hash.substring(1) 318 | 319 | if (group.length == 0) { 320 | $commands.children().show() 321 | // $commands.css("height", "auto") 322 | } 323 | else { 324 | $commands.find("li[data-group='" + group + "']").show() 325 | $commands.find("li[data-group!='" + group + "']").hide() 326 | } 327 | 328 | var $groups = $("#commands nav a") 329 | 330 | $groups.removeClass("current") 331 | 332 | $groups.filter("[href='#" + group + "']").addClass("current") 333 | 334 | document.querySelector('.command-reference-filter').value = group; 335 | } 336 | 337 | function autolink(text) { 338 | return text.replace(/(https?:\/\/[-\w\.]+:?\/[\w\/_\-\.]*(\?\S+)?)/, "$1"); 339 | } 340 | 341 | function massageTweet(text) { 342 | text = text.replace(/^.* @\w+: /, ""); 343 | 344 | return autolink(text); 345 | } 346 | 347 | function searchCommandReference() { 348 | var $commands = $('li') 349 | 350 | $('.js-command-reference-search').bind('input', function(ev) { 351 | window.location.hash = ''; 352 | 353 | if (ev.keyCode === 13) { 354 | var name = $commands.filter(':visible')[0].getAttribute('data-name'); 355 | 356 | window.location = '/commands/' + name.replace(/ /g, '-'); 357 | 358 | return; 359 | } 360 | 361 | var val = $(this).val().toLowerCase().replace(/[^a-z0-9 ]/g, ''); 362 | 363 | if (val === '') { 364 | $commands.show() 365 | } else { 366 | $commands.hide() 367 | $('li[data-name*="' + val + '"]').show() 368 | } 369 | }) 370 | } 371 | 372 | function buzz() { 373 | var $buzz = $("#buzz"); 374 | 375 | if ($buzz.length == 0) return; 376 | 377 | var $ul = $buzz.find("ul"); 378 | var count = 0; 379 | var limit = parseInt($buzz.attr("data-limit")); 380 | var users = {}; 381 | 382 | $.getJSON("https://redis-buzz.herokuapp.com/?callback=?", function(response) { 383 | $.each(response, function() { 384 | 385 | if (count++ == limit) { return false; } 386 | 387 | if (this.retweeted_status) { 388 | var status = this.retweeted_status; 389 | } else { 390 | var status = this; 391 | } 392 | 393 | $ul.append( 394 | "
  • " + 395 | "" + 396 | "" + status.user.screen_name + "" + 397 | " " + 398 | massageTweet(status.text) + 399 | "
  • " 400 | ); 401 | }); 402 | }); 403 | } 404 | 405 | // Easily set caret position in input field 406 | $.fn.setSelection = function(start, end) { 407 | var i, size = this.size(); 408 | 409 | // Only set caret by default 410 | if (end === undefined) end = start; 411 | 412 | for (i = 0; i < size; i++) { 413 | var element = this.get(i); 414 | if (element.createTextRange) { // IE 415 | var range = element.createTextRange(); 416 | range.collapse(true); 417 | range.moveEnd('character', end); 418 | range.moveStart('character', start); 419 | range.select(); 420 | } else if (element.setSelectionRange) { // Other browsers 421 | element.setSelectionRange(start, end); 422 | } 423 | } 424 | } 425 | 426 | function examples() { 427 | $('div.example').each(function() { 428 | var $example = $(this); 429 | var $form = $example.find("form"); 430 | var $input = $form.find("input"); 431 | 432 | $input.keydown(function(event) { 433 | var count = $example.find(".command").size(); 434 | var index = $input.data("index"); 435 | if (index == undefined) index = count; 436 | 437 | if (event.keyCode == 38) { 438 | index--; // up 439 | } else if (event.keyCode == 40) { 440 | index++; // down 441 | } else { 442 | return; 443 | } 444 | 445 | // Out of range at the positive side of the range makes sure 446 | // we can get back to an empty value. 447 | if (index >= 0 && index <= count) { 448 | $input.data("index", index); 449 | $input.val($example.find(".command").eq(index).text()); 450 | $input.setSelection($input.val().length); 451 | } 452 | 453 | return false; 454 | }); 455 | 456 | $form.submit(function(event) { 457 | if ($input.val().length == 0) 458 | return false; 459 | 460 | // Append command to execute 461 | var ps1 = $("") 462 | .addClass("monospace") 463 | .addClass("prompt") 464 | .html("redis> "); 465 | var cmd = $("") 466 | .addClass("monospace") 467 | .addClass("command") 468 | .text($input.val()); 469 | $form.before(ps1); 470 | $form.before(cmd); 471 | 472 | // Hide form 473 | $form.hide(); 474 | 475 | // POST command to app 476 | $.ajax({ 477 | type: "post", 478 | url: "/session/" + $example.attr("data-session"), 479 | data: $form.serialize(), 480 | complete: function(xhr, textStatus) { 481 | var data = xhr.responseText; 482 | var pre = $("
    ").text(data);
    483 |           $form.before(pre);
    484 | 
    485 |           // Reset input field and show form
    486 |           $input.val("");
    487 |           $input.removeData("index");
    488 |           $form.show();
    489 |         }
    490 |       });
    491 | 
    492 |       return false;
    493 |     });
    494 |   });
    495 | 
    496 |   // Only focus field when it is visible
    497 |   var $first = $('div.example:first :text');
    498 |   if ($first.size() > 0) {
    499 |     var windowTop = $(window).scrollTop();
    500 |     var windowBottom = windowTop + $(window).height();
    501 |     var elemTop = $first.offset().top;
    502 |     var elemBottom = elemTop + $first.height();
    503 |     if (elemTop >= windowTop && elemBottom < windowBottom) {
    504 |       $first.focus();
    505 |     }
    506 |   }
    507 | }
    508 | 
    509 | $(document).ready(function() {
    510 |   var slideout = new Slideout({
    511 |     'panel': document.querySelector('.site-wrapper'),
    512 |     'menu': document.querySelector('.mobile-menu'),
    513 |     'padding': 256,
    514 |     'tolerance': 70
    515 |   });
    516 | 
    517 |   document.querySelector('.js-slideout-toggle').addEventListener('click', function() {
    518 |     slideout.toggle();
    519 |   });
    520 | 
    521 |   document.querySelector('.mobile-menu').addEventListener('click', function(eve) {
    522 |     if (eve.target.nodeName === 'A') { slideout.close(); }
    523 |   });
    524 | 
    525 |   if (document.getElementById('commands')) {
    526 |     commandReference();
    527 |     filterCommandReference();
    528 |     searchCommandReference()
    529 |   }
    530 | 
    531 |   buzz()
    532 | 
    533 |   examples()
    534 | })
    535 | 
    536 | })(jQuery);
    537 | 
    
    
    --------------------------------------------------------------------------------
    /public/images/companies/bump.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/companies/bump.png
    
    
    --------------------------------------------------------------------------------
    /public/images/favicon.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/favicon.png
    
    
    --------------------------------------------------------------------------------
    /public/images/pivotal.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/pivotal.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redis-300dpi.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redis-300dpi.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redis-gray.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redis-gray.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redis-logo.svg:
    --------------------------------------------------------------------------------
     1 | 
     2 | 
     3 | 
     4 | 
     6 | 
     7 | 
     8 | 	
     9 | 		
    13 | 		
    19 | 		
    25 | 		
    29 | 		
    39 | 	
    40 | 	
    41 | 		
    45 | 		
    49 | 		
    53 | 		
    57 | 		
    61 | 		
    65 | 		
    67 | 		
    68 | 		
    69 | 	
    70 | 	
    71 | 	
    72 | 
    73 | 
    74 | 
    
    
    --------------------------------------------------------------------------------
    /public/images/redis-small.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redis-small.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redis-white.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redis-white.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redis.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redis.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redisdoc/2idx_0.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redisdoc/2idx_0.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redisdoc/2idx_1.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redisdoc/2idx_1.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redisdoc/2idx_2.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redisdoc/2idx_2.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redisdoc/lru_comparison.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redisdoc/lru_comparison.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redisdoc/pipeline_iops.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redisdoc/pipeline_iops.png
    
    
    --------------------------------------------------------------------------------
    /public/images/redislabs.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/redislabs.png
    
    
    --------------------------------------------------------------------------------
    /public/images/shuttleworth.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/shuttleworth.png
    
    
    --------------------------------------------------------------------------------
    /public/images/vmware.png:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/images/vmware.png
    
    
    --------------------------------------------------------------------------------
    /public/opensearch.xml:
    --------------------------------------------------------------------------------
     1 | 
     2 | 
     3 |   redis.io
     4 |   Look up a Redis command
     5 |   redis-db@googlegroups.com
     6 |   
     7 |   data:image/png;base64,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
     8 |   
     9 |   false
    10 |   en
    11 |   UTF-8
    12 |   UTF-8
    13 |   
    16 | 
    17 | 
    18 | 
    19 | 
    
    
    --------------------------------------------------------------------------------
    /public/presentation/Pnoordhuis_whats_new_in_2_2.pdf:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/presentation/Pnoordhuis_whats_new_in_2_2.pdf
    
    
    --------------------------------------------------------------------------------
    /public/presentation/Redis_Cluster.pdf:
    --------------------------------------------------------------------------------
    https://raw.githubusercontent.com/redis/redis-io/8407239da448d843968d53a412e9c84138a60f1c/public/presentation/Redis_Cluster.pdf
    
    
    --------------------------------------------------------------------------------
    /public/styles.css:
    --------------------------------------------------------------------------------
      1 | @import url("//fonts.googleapis.com/css?family=Open+Sans:300,400,700");
      2 | @import url("//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css");
      3 | /* normalize.css v3.0.2 | MIT License | git.io/normalize */
      4 | /** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
      5 | html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ }
      6 | 
      7 | /** Remove default margin. */
      8 | body { margin: 0; }
      9 | 
     10 | /* HTML5 display definitions ========================================================================== */
     11 | /** Correct `block` display not defined for any HTML5 element in IE 8/9. Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. Correct `block` display not defined for `main` in IE 11. */
     12 | article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; }
     13 | 
     14 | /** 1. Correct `inline-block` display not defined in IE 8/9. 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. */
     15 | audio, canvas, progress, video { display: inline-block; /* 1 */ vertical-align: baseline; /* 2 */ }
     16 | 
     17 | /** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
     18 | audio:not([controls]) { display: none; height: 0; }
     19 | 
     20 | /** Address `[hidden]` styling not present in IE 8/9/10. Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. */
     21 | [hidden], template { display: none; }
     22 | 
     23 | /* Links ========================================================================== */
     24 | /** Remove the gray background color from active links in IE 10. */
     25 | a { background-color: transparent; }
     26 | 
     27 | /** Improve readability when focused and also mouse hovered in all browsers. */
     28 | a:active, a:hover { outline: 0; }
     29 | 
     30 | /* Text-level semantics ========================================================================== */
     31 | /** Address styling not present in IE 8/9/10/11, Safari, and Chrome. */
     32 | abbr[title] { border-bottom: 1px dotted; }
     33 | 
     34 | /** Address style set to `bolder` in Firefox 4+, Safari, and Chrome. */
     35 | b, strong { font-weight: bold; }
     36 | 
     37 | /** Address styling not present in Safari and Chrome. */
     38 | dfn { font-style: italic; }
     39 | 
     40 | /** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari, and Chrome. */
     41 | h1 { font-size: 2em; margin: 0.67em 0; }
     42 | 
     43 | /** Address styling not present in IE 8/9. */
     44 | mark { background: #ff0; color: #000; }
     45 | 
     46 | /** Address inconsistent and variable font size in all browsers. */
     47 | small { font-size: 80%; }
     48 | 
     49 | /** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
     50 | sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
     51 | 
     52 | sup { top: -0.5em; }
     53 | 
     54 | sub { bottom: -0.25em; }
     55 | 
     56 | /* Embedded content ========================================================================== */
     57 | /** Remove border when inside `a` element in IE 8/9/10. */
     58 | img { border: 0; }
     59 | 
     60 | /** Correct overflow not hidden in IE 9/10/11. */
     61 | svg:not(:root) { overflow: hidden; }
     62 | 
     63 | /* Grouping content ========================================================================== */
     64 | /** Address margin not present in IE 8/9 and Safari. */
     65 | figure { margin: 1em 40px; }
     66 | 
     67 | /** Address differences between Firefox and other browsers. */
     68 | hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; }
     69 | 
     70 | /** Contain overflow in all browsers. */
     71 | pre { overflow: auto; }
     72 | 
     73 | /** Address odd `em`-unit font size rendering in all browsers. */
     74 | code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; }
     75 | 
     76 | /* Forms ========================================================================== */
     77 | /** Known limitation: by default, Chrome and Safari on OS X allow very limited styling of `select`, unless a `border` property is set. */
     78 | /** 1. Correct color not being inherited. Known issue: affects color of disabled elements. 2. Correct font properties not being inherited. 3. Address margins set differently in Firefox 4+, Safari, and Chrome. */
     79 | button, input, optgroup, select, textarea { color: inherit; /* 1 */ font: inherit; /* 2 */ margin: 0; /* 3 */ }
     80 | 
     81 | /** Address `overflow` set to `hidden` in IE 8/9/10/11. */
     82 | button { overflow: visible; }
     83 | 
     84 | /** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. Correct `select` style inheritance in Firefox. */
     85 | button, select { text-transform: none; }
     86 | 
     87 | /** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
     88 | button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }
     89 | 
     90 | /** Re-set default cursor for disabled elements. */
     91 | button[disabled], html input[disabled] { cursor: default; }
     92 | 
     93 | /** Remove inner padding and border in Firefox 4+. */
     94 | button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
     95 | 
     96 | /** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
     97 | input { line-height: normal; }
     98 | 
     99 | /** It's recommended that you don't attempt to style these elements. Firefox's implementation doesn't respect box-sizing, padding, or width.  1. Address box sizing set to `content-box` in IE 8/9/10. 2. Remove excess padding in IE 8/9/10. */
    100 | input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }
    101 | 
    102 | /** Fix the cursor style for Chrome's increment/decrement buttons. For certain `font-size` values of the `input`, it causes the cursor style of the decrement button to change from `default` to `text`. */
    103 | input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; }
    104 | 
    105 | /** 1. Address `appearance` set to `searchfield` in Safari and Chrome. 2. Address `box-sizing` set to `border-box` in Safari and Chrome (include `-moz` to future-proof). */
    106 | input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }
    107 | 
    108 | /** Remove inner padding and search cancel button in Safari and Chrome on OS X. Safari (but not Chrome) clips the cancel button when the search input has padding (and `textfield` appearance). */
    109 | input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
    110 | 
    111 | /** Define consistent border, margin, and padding. */
    112 | fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
    113 | 
    114 | /** 1. Correct `color` not being inherited in IE 8/9/10/11. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
    115 | legend { border: 0; /* 1 */ padding: 0; /* 2 */ }
    116 | 
    117 | /** Remove default vertical scrollbar in IE 8/9/10/11. */
    118 | textarea { overflow: auto; }
    119 | 
    120 | /** Don't inherit the `font-weight` (applied by a rule above). NOTE: the default cannot safely be changed in Chrome and Safari on OS X. */
    121 | optgroup { font-weight: bold; }
    122 | 
    123 | /* Tables ========================================================================== */
    124 | /** Remove most spacing between table cells. */
    125 | table { border-collapse: collapse; border-spacing: 0; }
    126 | 
    127 | td, th { padding: 0; }
    128 | 
    129 | body { width: 100%; *zoom: 1; }
    130 | body:before, body:after { content: ""; display: table; }
    131 | body:after { clear: both; }
    132 | 
    133 | body, textarea { font: 16px/1.6 "Open Sans", Helvetica, sans-serif; }
    134 | 
    135 | .anchor { display: block; position: relative; z-index: -1; top: -45px; }
    136 | 
    137 | h1, h2, h3 { position: relative; z-index: 1; }
    138 | h1 a.anchor-link, h2 a.anchor-link, h3 a.anchor-link { display: none; font-family: monospace; font-size: 25px; height: 1em; left: 0; margin-left: 30px; margin-top: 4px; padding-left: 30px; padding-right: 8px; position: absolute; text-align: center; text-decoration: none; width: 1em; }
    139 | h1:hover a.anchor-link, h2:hover a.anchor-link, h3:hover a.anchor-link { color: #888888; display: block; margin-left: -30px; padding-left: 8px; }
    140 | h1:hover a.anchor-link:hover, h2:hover a.anchor-link:hover, h3:hover a.anchor-link:hover { color: black; }
    141 | 
    142 | h1 { font-size: 24px; margin: 18px 0 2px 0; }
    143 | 
    144 | h2 { font-size: 18px; margin: 14px 0 8px 0; }
    145 | 
    146 | h3 { font-size: 15px; margin: 13px 0 6px 0; }
    147 | 
    148 | @media only screen and (min-width: 992px) { h1 { font-size: 32px; }
    149 |   h2 { font-size: 22px; }
    150 |   h3 { font-size: 18px; } }
    151 | code { font-family: "Menlo", "Monaco", monospace; }
    152 | 
    153 | a { color: #0066aa; text-decoration: none; }
    154 | a:hover { text-decoration: underline; color: #0099ff; }
    155 | 
    156 | .container, .site-content { margin: 0 auto; max-width: 900px; }
    157 | 
    158 | body { background-color: white; color: #333333; padding: 0; margin: 0; }
    159 | 
    160 | .site-footer { color: #777777; font-size: 11px; margin: 20px; border-top: 1px solid #eeeeee; padding: 10px 0 0 0; }
    161 | .site-footer a { color: #444444; }
    162 | 
    163 | @media only screen and (min-width: 992px) { .site-footer { background: url(/images/redis-small.png) no-repeat left top; padding: 0 0 0 50px; max-width: 840px; margin: 5em auto 3em auto; position: relative; border-top-width: 0; }
    164 |   .site-footer p { line-height: 34px; margin: 0; } }
    165 | .text { *zoom: 1; color: #333333; margin: 0 auto; padding: 20px; }
    166 | .text:before, .text:after { content: ""; display: table; }
    167 | .text:after { clear: both; }
    168 | .text aside { position: relative; z-index: 2; }
    169 | .text p { margin: 5px 0; }
    170 | .text > p:first-of-type, .text > * > p:first-of-type { margin-top: 15px; }
    171 | .text em { font-style: italic; }
    172 | .text sup { line-height: 100%; vertical-align: super; font-size: 70%; }
    173 | .text pre code, .text .example { display: inline-block; padding: 15px; border: 1px solid #eeeeee; margin: 10px 0; background-color: #fefefe; }
    174 | .text .metadata { border-left: 3px solid #dfdfdf; color: #666666; font-size: 0.9em; margin: 0 0 1.5em 0; padding: 5px 15px; background: #fcfcfc; }
    175 | .text .deprecation { border-left: 3px solid #d42e15; color: #333333; font-size: 0.9em; margin: 0 0 1.5em 0; padding: 5px 15px; background: #fcfcfc; }
    176 | .text .example { max-height: 400px; max-width: 390px; overflow: auto; }
    177 | .text .example .monospace, .text .example pre, .text .example input { margin: 0; padding: 0; line-height: 20px; font-size: 12px; font-family: Menlo, monospace; }
    178 | .text .example pre { clear: both; }
    179 | .text .example .prompt, .text .example .command { float: left; }
    180 | .text .example .prompt { width: 50px; color: #888888; }
    181 | .text .example .command, .text .example input { font-weight: bold; }
    182 | .text .example input { width: 325px; margin-left: -1px; outline: none; border: 0; color: #333333; float: left; }
    183 | .text h1.command { padding-left: 20px; }
    184 | .text h1.command span { display: inline-block; }
    185 | .text h1.command span.name { margin-left: -20px; }
    186 | .text h1.command span.arg { display: inline; }
    187 | .text h1:first-child { margin-top: 0; }
    188 | 
    189 | #comments { margin-top: 15px; }
    190 | 
    191 | .wide-callout { background: #fffcc2; border-color: #f0ec8e; border-style: solid; border-width: 0 0 3px 0; display: block; padding: 0.5em 0; text-align: center; margin: 0; }
    192 | 
    193 | .home-section { padding: 20px; border-bottom: 1px solid #eeeeee; }
    194 | .home-section:last-of-type { border-bottom-width: 0; }
    195 | 
    196 | .home-callout { max-width: 100%; }
    197 | .home-callout .title { font-size: 20px; }
    198 | 
    199 | @media only screen and (min-width: 992px) { .home-intro { display: block; width: 102.08333%; margin: 0 -1.04167%; *zoom: 1; }
    200 |   .home-intro:before, .home-intro:after { content: ""; display: table; }
    201 |   .home-intro:after { clear: both; }
    202 |   .home-intro > section { display: inline; float: left; width: 31.25%; margin: 0 1.04167%; } }
    203 | #clients h2 { margin: 2em 0 1em 0; text-align: left; }
    204 | #clients .icon { color: #333333; font-size: 20px; }
    205 | #clients .icon-star { color: #ffe00d; }
    206 | #clients table { table-layout: fixed; }
    207 | #clients table td, #clients table th { padding: 0.8em; border-bottom: 1px solid #efefef; }
    208 | #clients table td.description { color: #666666; font-size: 0.9em; }
    209 | #clients table td.authors { text-align: right; }
    210 | #clients col.homepage, #clients col.recommended, #clients col.active, #clients col.repository { width: 1em; }
    211 | #clients col.authors { width: 120px; }
    212 | #clients .twitter-avatar { width: 36px; height: 36px; }
    213 | #clients .languages { list-style-type: none; margin: 1.5em 0 0 0; padding: 1.5em 0 0 0; border-top: 1px solid #efefef; }
    214 | #clients .languages li { display: inline-block; width: 7em; margin: 0.3em; }
    215 | 
    216 | article { overflow: hidden; *zoom: 1; }
    217 | article p, article ul { margin: 7px 0; }
    218 | article ul { list-style-type: disc; padding-left: 25px; }
    219 | article ol { list-style-type: decimal; }
    220 | article ol li { margin: 4px 0; }
    221 | article ol ol { list-style-type: lower-alpha; }
    222 | article h2, article h3 { font-weight: 500; }
    223 | article h2 { margin-top: 22px; }
    224 | article h3 { margin-top: 18px; }
    225 | article aside > *:first-child { margin-top: 0; }
    226 | article aside h2 { font-size: 16px; font-weight: 300; }
    227 | article table { margin: 10px 0; }
    228 | article table tr td { vertical-align: top; padding: 6px 10px; border-bottom: 1px solid #dddddd; }
    229 | article table tr:last-child td { border-bottom-width: 0; }
    230 | article table tr.current { background-color: #feffe8; }
    231 | article table.versions td:first-child { font-size: 22px; line-height: 22px; }
    232 | 
    233 | @media only screen and (min-width: 992px) { article { display: block; width: 102.08333%; margin: 0 -1.04167%; overflow: hidden; *zoom: 1; }
    234 |   .article-main { display: inline; float: left; width: 64.58333%; margin: 0 1.04167%; }
    235 |   .article-aside { display: inline; float: left; width: 31.25%; margin: 0 1.04167%; }
    236 |   aside { border-left: 1px solid #dfdfdf; margin: 0 0 15px 20px; padding: 0 0 0 20px; } }
    237 | body.topics.whos-using-redis ul:first-of-type { text-align: center; list-style-type: none; margin: 15px; }
    238 | body.topics.whos-using-redis ul:first-of-type li { margin: 10px 10px; padding: 0; display: inline-block; vertical-align: middle; }
    239 | body.topics.whos-using-redis ul:first-of-type li img { vertical-align: middle; max-width: 200px; max-height: 76px; }
    240 | 
    241 | #commands { text-align: center; }
    242 | #commands ul { overflow: hidden; *zoom: 1; margin: 1em 0; text-align: left; padding: 0; }
    243 | #commands li { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
    244 | #commands li a { display: block; padding: 1em; color: #333333; text-decoration: none; }
    245 | #commands li a:hover, #commands li a:focus { color: #333333; background: #ecf7fa; }
    246 | #commands li .command { white-space: nowrap; overflow: hidden; -ms-text-overflow: ellipsis; -o-text-overflow: ellipsis; text-overflow: ellipsis; font-family: "Monaco", "Menlo", monospace; display: block; margin: 0 0 2px 0; color: #0066aa; }
    247 | #commands li .command .args { color: #555555; }
    248 | #commands li .summary { display: block; font-size: 0.8em; line-height: 1.5em; }
    249 | #commands nav { margin: 0 auto; background-color: #f0f0f0; width: 100%; font-size: 0.85em; display: block; padding: 10px 0; }
    250 | #commands nav a { color: #555555; text-decoration: none; display: inline-block; padding: 0.3em 0.5em; }
    251 | #commands nav a.current { background-color: #666666; color: white; }
    252 | #commands nav label { margin: 0 10px; }
    253 | #commands nav label span { display: none; }
    254 | #commands nav select, #commands nav input { border: 1px solid #bbbbbb; font-size: 14px; margin: 0 5px; }
    255 | #commands nav input { border-radius: 5px; padding: 5px 5px 6px 5px; width: 100px; }
    256 | #commands nav input:focus { outline: none; }
    257 | #commands nav select { height: 32px; }
    258 | 
    259 | h1.command { margin-bottom: 0.5em; }
    260 | 
    261 | .betacmd { background-color: #d42e15; border-radius: 0.5em; color: white; padding: 0.2em 0.3em; }
    262 | 
    263 | @media only screen and (min-width: 992px) { #commands .command { font-size: 0.9em; }
    264 |   #commands .command .args { font-size: 0.8em; }
    265 |   #commands nav label { margin: 0 10px 0 0; }
    266 |   #commands nav label span { display: inline; }
    267 |   #commands nav input { width: 200px; }
    268 |   #commands ul li { float: left; margin: 1em 1.5%; overflow: hidden; width: 30%; }
    269 |   #commands ul li a { -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; border-radius: 5px; background-color: #fafafa; height: 6em; } }
    270 | #buzz { text-align: right; }
    271 | #buzz h2 { margin-bottom: 20px; text-align: center; }
    272 | #buzz ul { margin: 7px 0; text-align: left; }
    273 | #buzz li { margin: 0 0 8px 0; padding-left: 34px; position: relative; min-height: 26px; word-wrap: break-word; }
    274 | #buzz li a:first-child { display: block; height: 26px; left: 0; position: absolute; top: 3px; width: 26px; }
    275 | #buzz li a:first-child img { height: 26px; width: 26px; }
    276 | 
    277 | .download-versions { list-style-type: none; margin: 0; padding: 0; }
    278 | .download-versions > li { display: grid; grid-template-rows: min-content 1fr min-content; margin-bottom: 20px; border-bottom: 1px solid #eeeeee; padding-bottom: 20px; }
    279 | 
    280 | .download-version { color: #999999; }
    281 | 
    282 | .download-links { margin: 15px 0 0 0; text-align: center; }
    283 | 
    284 | .download-link { display: inline-block; color: #333333; text-align: center; width: 80px; border-radius: 5px; padding: 10px; margin: 10px; border: 1px solid #dddddd; line-height: 1.4; }
    285 | .download-link .fa { font-size: 20px; margin-bottom: 8px; display: block; }
    286 | .download-link:hover { text-decoration: none; background: #fafafa; }
    287 | 
    288 | @media only screen and (min-width: 992px) { .download-versions { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 1rem;} }
    289 | 
    290 | body { width: 100%; height: 100%; }
    291 | 
    292 | .slideout-menu { position: fixed; left: 0; top: 0; bottom: 0; right: 0; z-index: 0; width: 256px; overflow-y: auto; -webkit-overflow-scrolling: touch; display: none; }
    293 | 
    294 | .slideout-panel { position: relative; z-index: 1; height: 100%; }
    295 | 
    296 | .slideout-open, .slideout-open body, .slideout-open .slideout-panel { overflow: hidden; }
    297 | 
    298 | .slideout-open .slideout-menu { display: block; }
    299 | 
    300 | .site-header { overflow: hidden; *zoom: 1; background-color: #222222; padding: 0; width: 100%; }
    301 | 
    302 | .desktop-header { padding: 0.7em 0; white-space: nowrap; overflow: hidden; }
    303 | .desktop-header .home { margin-right: 0.8em; }
    304 | .desktop-header a { line-height: 40px; display: inline-block; color: #f9f9f9; padding: 0 0.4em; text-decoration: none; }
    305 | .desktop-header a:first-child { margin-left: 0; }
    306 | .desktop-header a:hover { text-decoration: none; color: white; }
    307 | .desktop-header .tryfree { border-radius: 6px!important; background-color: #a51f17; margin-left: 0.8em; }
    308 | 
    309 | .site-header .desktop-header { display: none; }
    310 | 
    311 | .site-header .mobile-header { display: grid; grid-template-columns: auto 1fr auto;}
    312 | .site-header .mobile-header .btn-hamburger { border: 0; color: #eeeeee; padding: 0; background: transparent; outline: none; margin-left: 0.5em;}
    313 | .site-header .mobile-header a.home { justify-self: center; align-self: center; }
    314 | .site-header .mobile-header a.home img { width: 80px; height: 27px; padding: 0.2em; }
    315 | .site-header .mobile-header a.tryfree { padding: 0 0.4em 0 0.4em; margin: 0.4em; border-radius: 6px!important; color: white; background-color: #a51f17; }
    316 | 
    317 | @media only screen and (min-width: 992px) { .site-header .desktop-header { display: block; }
    318 |   .site-header .mobile-header { display: none; } }
    319 | .site-header .home img { height: 40px; width: 120px; vertical-align: middle; }
    320 | 
    321 | 
    322 | .site-wrapper { background: white; }
    323 | 
    324 | .mobile-menu { background-color: #424242; }
    325 | .mobile-menu a { color: white; }
    326 | .mobile-menu a:hover { text-decoration: underline; }
    327 | .mobile-menu .menu-header { border-bottom: 1px solid #58595a; padding: 15px; background: url("/images/redis-gray.png") no-repeat center center; background-size: 80px 71px; min-height: 71px; text-align: center; }
    328 | .mobile-menu .menu-header-title { font-weight: 400; letter-spacing: 0.5px; margin: 0; }
    329 | .mobile-menu .menu-section { margin: 25px 0; }
    330 | .mobile-menu .menu-section-title { text-transform: uppercase; color: #85888d; font-weight: 200; font-size: 13px; letter-spacing: 1px; padding: 0 20px; margin: 0; }
    331 | .mobile-menu .menu-section-list { padding: 0; margin: 10px 0; list-style: none; }
    332 | .mobile-menu .menu-section-list a { display: block; padding: 10px 20px; }
    333 | .mobile-menu .menu-section-list a:hover { background-color: rgba(255, 255, 255, 0.1); text-decoration: none; }
    334 | .mobile-menu .logo { width: 120px; height: 106px; }
    335 | 
    
    
    --------------------------------------------------------------------------------
    /scripts/generate_interactive_commands.rb:
    --------------------------------------------------------------------------------
      1 | #!/usr/bin/env ruby
      2 | 
      3 | require "./app"
      4 | require "erb"
      5 | 
      6 | # Treat the subcommands of these commands as part of the command.
      7 | SUBCOMMANDS = %w(
      8 |   client
      9 | ).freeze
     10 | 
     11 | # Explicitly allow certain groups (don't allow "server", "connection" and
     12 | # "pubsub" commands by default).
     13 | ALLOW_GROUPS = %w(
     14 |   generic
     15 |   hash
     16 |   list
     17 |   set
     18 |   sorted-set
     19 |   hyperloglog
     20 |   string
     21 |   scripting
     22 |   geo
     23 |   stream
     24 |   bitmap
     25 | ).freeze
     26 | 
     27 | # Override ALLOW_GROUPS for some commands.
     28 | ALLOW_COMMANDS = %w(
     29 |   ping
     30 |   echo
     31 |   info
     32 |   lastsave
     33 |   time
     34 |   role
     35 |   command
     36 |   client\ info
     37 | ).freeze
     38 | 
     39 | # Explicitly deny some commands.
     40 | DENY_COMMANDS = %w(
     41 |   blpop
     42 |   brpop
     43 |   brpoplpush
     44 |   bzpopmin
     45 |   bzpopmax
     46 |   copy
     47 |   select
     48 |   move
     49 |   randomkey
     50 |   script
     51 |   eval
     52 |   evalsha
     53 |   object
     54 |   migrate
     55 |   xread
     56 |   xclaim
     57 |   xreadgroup
     58 |   xack
     59 |   xgroup
     60 |   xinfo
     61 |   xpending
     62 |   restore
     63 | ).freeze
     64 | 
     65 | def allowed_commands
     66 |   @allowed_commands ||= commands.select do |cmd|
     67 |     name = cmd.name.split(/\s+/).first.downcase
     68 |     fullname = cmd.name.downcase
     69 | 
     70 |     !DENY_COMMANDS.include?(name) &&
     71 |       (ALLOW_COMMANDS.include?(name) ||
     72 |          (SUBCOMMANDS.include?(name) &&
     73 |           ALLOW_COMMANDS.include?(fullname)) ||
     74 |        ALLOW_GROUPS.include?(cmd.group))
     75 |   end
     76 | end
     77 | 
     78 | def patterns
     79 |   @patterns ||= Hash[*allowed_commands.map do |cmd|
     80 |     args = cmd.arguments
     81 |     if args.empty?
     82 |       pattern = []
     83 |     elsif args[0].type == ["key"] && !args[0].multiple? &&
     84 |       args[1..-1].none? { |arg| arg.type.include?("key") }
     85 |       # Only namespace the first argument
     86 |       pattern = [:first]
     87 |     elsif args.all? { |arg| arg.type == ["key"] }
     88 |       # Namespace all arguments
     89 |       pattern = [:all]
     90 |     elsif args.all? { |arg| !arg.multiple? && !arg.optional? && arg.type.size == 1 }
     91 |       # Constant number of arguments can be zipped
     92 |       pattern = [:zip, args.map { |arg| :key if arg.type == ["key"] } ]
     93 |     elsif args.size == 1 && args[0].multiple? && args[0].type.include?("key")
     94 |       # Single variadic argument with key can be zipped
     95 |       pattern = [:zip, args[0].type.map { |type| :key if type == "key" } ]
     96 |     else
     97 |       # Non-standard
     98 |       pattern = [:custom]
     99 |     end
    100 | 
    101 |     [cmd.name.downcase, pattern]
    102 |   end.compact.flatten(1)]
    103 | end
    104 | 
    105 | by_group = allowed_commands.group_by(&:group).sort.map do |group,commands|
    106 |   lines = commands.sort_by(&:name).map do |cmd|
    107 |     %{"%s" => %s} % [cmd.name.downcase, patterns[cmd.name.downcase].inspect]
    108 |   end
    109 | 
    110 |   [group, lines]
    111 | end
    112 | 
    113 | def file
    114 |   __FILE__
    115 | end
    116 | 
    117 | template = <<-TPL
    118 | # This file is automatically generated by:
    119 | #   <%= file %>.
    120 | #
    121 | # Do not edit.
    122 | #
    123 | 
    124 | module Interactive
    125 |   COMMANDS = {
    126 |   <%- by_group.each do |group,commands| -%>
    127 |     # <%= group %>
    128 |     <%- commands.each do |command| -%>
    129 |     <%= command %>,
    130 |     <%- end %>
    131 |   <%- end -%>
    132 |   }.freeze
    133 | 
    134 |   SUBCOMMANDS = {
    135 |   <%- SUBCOMMANDS.each do |command| -%>
    136 |      "<%= command %>" => 1,
    137 |   <%- end %>
    138 |   }.freeze
    139 | end
    140 | 
    141 | TPL
    142 | 
    143 | STDOUT.puts ERB.new(template, 0, "-").result(binding)
    144 | 
    145 | 
    
    
    --------------------------------------------------------------------------------
    /test/clients.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | scope do
     4 |   test "Clients page" do
     5 |     visit "/clients"
     6 | 
     7 |     assert has_css?("h2", text: "Ruby")
     8 |     assert has_content?("redis-rb")
     9 |   end
    10 | end
    11 | 
    
    
    --------------------------------------------------------------------------------
    /test/command_reference.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | scope do
     4 |   test "Command reference" do
     5 |     visit "/commands"
     6 | 
     7 |     assert has_content?("ECHO")
     8 |     assert has_content?("Echo the given string")
     9 |   end
    10 | 
    11 |   test "Command page" do
    12 |     visit "/commands"
    13 | 
    14 |     click_link_or_button "ECHO"
    15 | 
    16 |     assert has_title?("ECHO")
    17 | 
    18 |     within "h1" do
    19 |       assert has_content?("ECHO")
    20 |       assert has_content?("message")
    21 |     end
    22 | 
    23 |     within "article" do
    24 |       assert has_css?("p", text: "Returns message.")
    25 |       assert has_content?("Available since 1.0.0")
    26 |     end
    27 |   end
    28 | 
    29 |   test "Command page with complex arguments" do
    30 |     visit "/commands"
    31 | 
    32 |     click_link(href: /\/sort$/)
    33 | 
    34 |     within "h1" do
    35 |       assert has_content?("[BY pattern]")
    36 |       assert has_content?("[LIMIT offset count]")
    37 |       assert has_content?("[ASC|DESC]")
    38 |       assert has_content?("[ALPHA]")
    39 |       assert has_content?("[STORE destination]")
    40 |     end
    41 |   end
    42 | 
    43 |   test "Commands with spaces" do
    44 |     visit "/commands"
    45 | 
    46 |     click_link_or_button "OBJECT ENCODING"
    47 | 
    48 |     assert has_title?("OBJECT ENCODING")
    49 |     assert has_css?("h1", text: "OBJECT ENCODING")
    50 |   end
    51 | 
    52 |   test "Missing command" do
    53 |     visit "/commands/foobar"
    54 | 
    55 |     assert_equal page.current_url, "https://www.google.com/search?q=foobar+site%3Aredis.io"
    56 |   end
    57 | end
    58 | 
    
    
    --------------------------------------------------------------------------------
    /test/formatting.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | require File.expand_path("../lib/reference", File.dirname(__FILE__))
     4 | 
     5 | reference = Reference.new(JSON.parse(File.read("#{documentation_path}/commands.json")))
     6 | 
     7 | setup do
     8 |   reference
     9 | end
    10 | 
    11 | test "OBJECT ENCODING" do |reference|
    12 |   res = "OBJECT ENCODING key"
    13 |   assert_equal reference["OBJECT ENCODING"].to_s, res
    14 | end
    15 | 
    16 | test "DEL" do |reference|
    17 |   res = "DEL key [key ...]"
    18 |   assert_equal reference["DEL"].to_s, res
    19 | end
    20 | 
    21 | test "DISCARD" do |reference|
    22 |   res = "DISCARD"
    23 |   assert_equal reference["DISCARD"].to_s, res
    24 | end
    25 | 
    26 | test "SORT" do |reference|
    27 |   res = "SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]]" +
    28 |         " [ASC|DESC] [ALPHA] [STORE destination]"
    29 |   assert_equal reference["SORT"].to_s, res
    30 | end
    31 | 
    32 | test "UNSUBSCRIBE" do |reference|
    33 |   res = "UNSUBSCRIBE [channel [channel ...]]"
    34 |   assert_equal reference["UNSUBSCRIBE"].to_s, res
    35 | end
    36 | 
    37 | test "ZINTERSTORE" do |reference|
    38 |   res = "ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]]" +
    39 |         " [AGGREGATE SUM|MIN|MAX]"
    40 |   assert_equal reference["ZINTERSTORE"].to_s, res
    41 | end
    42 | 
    
    
    --------------------------------------------------------------------------------
    /test/helper.rb:
    --------------------------------------------------------------------------------
     1 | require "cuba/capybara"
     2 | require File.expand_path("../app", File.dirname(__FILE__))
     3 | 
     4 | begin
     5 |   require "ruby-debug"
     6 | rescue LoadError
     7 | end
     8 | 
     9 | Capybara.default_selector = :css
    10 | 
    
    
    --------------------------------------------------------------------------------
    /test/interactive/namespace.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | require File.expand_path("../../lib/interactive/namespace", File.dirname(__FILE__))
     4 | 
     5 | scope do
     6 |   test "Namespace for static arguments" do
     7 |     assert ["set", "ns:key", "foo"] == \
     8 |       Interactive.namespace("ns", ["set", "key", "foo"])
     9 | 
    10 |     assert ["ping"] == \
    11 |       Interactive.namespace("ns", ["ping"])
    12 | 
    13 |     assert ["echo", "foo"] == \
    14 |       Interactive.namespace("ns", ["echo", "foo"])
    15 |   end
    16 | 
    17 |   test "Namespace with optional arguments" do
    18 |     assert ["zrange", "ns:key", "0", "-1", "withscores"] == \
    19 |       Interactive.namespace("ns", ["zrange", "key", "0", "-1", "withscores"])
    20 |   end
    21 | 
    22 |   test "Namespace with variable number of arguments" do
    23 |     assert ["mset", "ns:key1", "foo", "ns:key2", "foo"] == \
    24 |       Interactive.namespace("ns", ["mset", "key1", "foo", "key2", "foo"])
    25 | 
    26 |     assert ["hmset", "ns:key1", "field1", "foo", "field2", "bar"] == \
    27 |       Interactive.namespace("ns", ["hmset", "key1", "field1", "foo", "field2", "bar"])
    28 |   end
    29 | 
    30 |   test "Namespace ZUNIONSTORE/ZINTERSTORE" do
    31 |     for cmd in %w(zunionstore zinterstore)
    32 |       assert [cmd, "ns:dst", "2", "ns:key1", "ns:key2", "weights", "1.0", "2.0"] == \
    33 |         Interactive.namespace("ns", [cmd, "dst", "2", "key1", "key2", "weights", "1.0", "2.0"])
    34 |     end
    35 |   end
    36 | 
    37 |   test "Namespace SORT" do
    38 |     command = %w(sort key by foo by bar get foo get bar store dst limit 0 10 asc)
    39 |     expected = %w(sort ns:key by ns:foo by ns:bar get ns:foo get ns:bar store ns:dst limit 0 10 asc)
    40 |     assert expected == Interactive.namespace("ns", command)
    41 |   end
    42 | 
    43 |   test "Namespace GEORADIUS" do
    44 |     command = %w(georadius key 10 20 40 km withcoord store foo storedist bar)
    45 |     expected = %w(georadius ns:key 10 20 40 km withcoord store ns:foo storedist ns:bar)
    46 |     assert expected == Interactive.namespace("ns", command)
    47 |   end
    48 | end
    49 | 
    50 | 
    
    
    --------------------------------------------------------------------------------
    /test/json.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | scope do
     4 |   test do
     5 |     visit "/commands.json"
     6 | 
     7 |     assert has_content?("\"APPEND\": {")
     8 | 
     9 |     visit "/clients.json"
    10 | 
    11 |     assert has_content?("\"name\": \"redis-rb\"")
    12 |   end
    13 | end
    14 | 
    
    
    --------------------------------------------------------------------------------
    /test/sitemap.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | prepare do
     4 |   redis.flushdb
     5 |   redis.hmset("versions", "stable", "2.0.4", "development", "2.1.9")
     6 | end
     7 | 
     8 | scope do
     9 |   test "Sitemap" do
    10 |     visit "/"
    11 | 
    12 |     find(".desktop-header").click_link_or_button "Commands"
    13 | 
    14 |     assert has_content?("PING")
    15 | 
    16 |     find(".desktop-header").click_link_or_button "Clients"
    17 | 
    18 |     assert has_content?("redis-rb")
    19 | 
    20 |     find(".desktop-header").click_link_or_button "Download"
    21 | 
    22 |     assert has_content?("Run Redis with:")
    23 | 
    24 |     find(".desktop-header").click_link_or_button "Community"
    25 | 
    26 |     assert has_css?("a[href='http://groups.google.com/group/redis-db']", text: "mailing list")
    27 |     assert has_css?("a[href='http://twitter.com/redisfeed']", text: "Redis news feed")
    28 | 
    29 |     find(".desktop-header").click_link_or_button "Documentation"
    30 | 
    31 |     click_link_or_button "full list of commands"
    32 | 
    33 |     assert has_content?("PING")
    34 |   end
    35 | end
    36 | 
    
    
    --------------------------------------------------------------------------------
    /test/topics.rb:
    --------------------------------------------------------------------------------
     1 | require "./test/helper"
     2 | 
     3 | scope do
     4 |   test "Topic" do
     5 |     visit "/topics/replication"
     6 | 
     7 |     assert has_title?("Replication")
     8 |     assert has_xpath?("//h1", text: "Replication")
     9 |     assert has_xpath?("//h2", text: "Configuration")
    10 |   end
    11 | 
    12 |   test "Missing topic" do
    13 |     visit "/topics/foobar"
    14 | 
    15 |     assert has_content?("Sorry")
    16 |     assert has_content?("topics/foobar.md")
    17 |     assert page.driver.response.status == 404
    18 |   end
    19 | end
    20 | 
    
    
    --------------------------------------------------------------------------------
    /views/404.haml:
    --------------------------------------------------------------------------------
     1 | .site-content
     2 |   .text
     3 |     %h1 Sorry, I can't find that page :-/
     4 | 
     5 |     - if path
     6 |       %p
     7 |         If you think this page should exist, add a
     8 | 
     9 |         %code
    10 |           != path
    11 | 
    12 |         file to the repository and send a pull request.
    13 | 
    
    
    --------------------------------------------------------------------------------
    /views/avatar.haml:
    --------------------------------------------------------------------------------
     1 | - size ||= 32
     2 | - alt ||= ""
     3 | 
     4 | %img(src="http://www.gravatar.com/avatar/#{gravatar_hash email}.jpg?s=#{size}"
     5 |   width="#{size}"
     6 |   height="#{size}"
     7 |   alt="#{alt}"
     8 |   title="#{alt}"
     9 | )
    10 | 
    
    
    --------------------------------------------------------------------------------
    /views/buzz.haml:
    --------------------------------------------------------------------------------
    1 | .site-content
    2 |   .text
    3 |     %h1 Buzz
    4 | 
    5 |     %section#buzz(data-limit="30")
    6 |       %ul
    7 | 
    
    
    --------------------------------------------------------------------------------
    /views/clients.haml:
    --------------------------------------------------------------------------------
      1 | .site-content
      2 |   .text
      3 |     %section#clients
      4 | 
      5 |       %h1 Clients
      6 | 
      7 |       %p
      8 |         The recommended client(s) for a language are marked with a .
      9 | 
     10 |       %p
     11 |         Clients with some activity in the official repository within the latest six months are marked with a .
     12 | 
     13 |       %p
     14 |         Want
     15 |         %strong your client listed here?
     16 |         Please fork the
     17 |         %a(href="https://github.com/redis/redis-doc") redis-doc repository
     18 |         and edit the clients.json file.
     19 |         %strong Submit a pull request
     20 |         and you are done.
     21 | 
     22 |       .languages
     23 |         Browse by language:
     24 | 
     25 |         %ul
     26 |           - @clients_by_language.each do |language, _|
     27 |             %li
     28 |               %a(href="##{anchorize_language(language)}")= language
     29 | 
     30 |       %table
     31 |         %col(class="name")
     32 |         %col(class="active")
     33 |         %col(class="recommended")
     34 |         %col(class="homepage")
     35 |         %col(class="repository")
     36 |         %col(class="description")
     37 |         %col(class="authors")
     38 | 
     39 |         - @clients_by_language.each do |language, clients|
     40 |           %tr
     41 |             %th(colspan="7")
     42 |               %h2(id="#{anchorize_language(language)}")= language
     43 | 
     44 |           - clients.sort_by { |c| [c["active"] ? 0 : 1, c["recommended"] ? 0 : 1, c["name"].downcase] }.each do |client|
     45 |             %tr
     46 |               %td
     47 |                 = client["name"]
     48 |               %td
     49 |                 - if client["active"]
     50 |                   %i(class="fa fa-smile-o icon")
     51 | 
     52 |               %td
     53 |                 - if client["recommended"]
     54 |                   %i(class="fa fa-star icon icon-star")
     55 | 
     56 |               %td
     57 |                 - if client["url"]
     58 |                   %a(href="#{client["url"]}" class="icon")
     59 |                     %i(class="fa fa-home")
     60 | 
     61 |               %td
     62 |                 - if client["repository"]
     63 |                   %a(href="#{client["repository"]}" class="icon")
     64 |                     %i(class="fa fa-code-fork")
     65 | 
     66 |               %td.description
     67 |                 = client["description"]
     68 | 
     69 |               %td.authors
     70 |                 - if client["authors"]
     71 |                   - client["authors"].each do |author|
     72 |                     %a(href="https://twitter.com/#{author}")
     73 |                       = "@#{author} "
     74 | 
     75 |       %h1(style="margin-top:80px") Higher level libraries and tools
     76 | 
     77 |       %p
     78 |         This is an additional list of libraries that are not direct layers on top of the Redis API, but higher level libraries such as ORMs, messaging libraries, and other misc tools that are designed for Redis.
     79 | 
     80 |         %table
     81 |           - @redis_tools.each do |tool|
     82 |             - tool["description"] += " (#{tool['language']})"
     83 |             %tr
     84 |               %td
     85 |                 = tool["name"]
     86 | 
     87 |               %td
     88 |                 - if tool["repository"]
     89 |                   %a(href="#{tool["repository"]}") Repository
     90 | 
     91 |                 - if tool["url"]
     92 |                   %a(href="#{tool["url"]}") Homepage
     93 | 
     94 |               %td
     95 |                 - tool["authors"].each do |author|
     96 |                   %a(href="http://twitter.com/#{author}")= author
     97 | 
     98 |               %td
     99 |                 = tool["description"]
    100 | 
    101 | 
    
    
    --------------------------------------------------------------------------------
    /views/commands.haml:
    --------------------------------------------------------------------------------
     1 | %section#commands
     2 |   %nav
     3 |     .container
     4 |       %label
     5 |         %span Filter by group:
     6 | 
     7 |         %select.command-reference-filter
     8 |           %option(value="") All
     9 | 
    10 |           - Reference::GROUPS.sort_by(&:last).each do |name, description|
    11 |             %option(value="#{name}")= description
    12 | 
    13 |       or
    14 | 
    15 |       %label
    16 |         %span search for:
    17 |         %input.js-command-reference-search(placeholder="e.g. #{@commands.sample.name}" autofocus autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
    18 | 
    19 |   .container
    20 |     %ul
    21 |       - @commands.each do |command|
    22 |         - if command.is_listed?
    23 |           %li(data-group="#{command.command["group"]}" data-name="#{command.name.downcase}")
    24 |             %a(href="/commands/#{command.to_param}")
    25 |               %span.command
    26 |                 = command.name
    27 | 
    28 |                 %span.args
    29 |                   - command.arguments.each do |argument|
    30 |                     = argument
    31 | 
    32 |               %span.summary= command.command["summary"]
    33 | 
    
    
    --------------------------------------------------------------------------------
    /views/commands/name.haml:
    --------------------------------------------------------------------------------
     1 | .site-content
     2 |   .text
     3 | 
     4 |     %h1.command
     5 |       %span.name= @command.name
     6 |       - @command.arguments.each do |arg|
     7 |         %span.arg= arg
     8 | 
     9 |     %article
    10 |       .article-main
    11 | 
    12 |         %div.metadata
    13 |           - if @command.since
    14 |             %p Available since #{@command.since}.
    15 |           - else
    16 |             %p
    17 |               %span.betacmd Beta
    18 |               Not yet available in a stable version of Redis.
    19 |               Download unstable if you want to test this command.
    20 | 
    21 |           - if @command.complexity
    22 |             %p Time complexity: #{@command.complexity}
    23 | 
    24 |         - if @command.deprecated_since
    25 |           %div.deprecation
    26 |             %p
    27 |               Deprecation notice:
    28 |               as of Redis version #{@command.deprecated_since} this command is
    29 |               considered as deprecated. While it is unlikely that it will be
    30 |               completely removed, prefer using #{@command.replaced_by} in its
    31 |               stead.
    32 | 
    33 |         ~ custom_view("#{documentation_path}/commands/#{@name.downcase}.md", {}, layout: false)
    34 | 
    35 |         - if @command.history
    36 |           %h2 History
    37 |           %ul
    38 |             - @command.history.each do |entry|
    39 |               %li
    40 |                 Redis version >= #{entry[0]}: #{entry[1]}
    41 | 
    42 |       .article-aside
    43 |         %aside
    44 | 
    45 |           - unless @related_topics.empty?
    46 |             %h2 Related topics
    47 | 
    48 |             %ul
    49 |               - @related_topics.each do |name, url|
    50 |                 %li
    51 |                   %a(href="#{url}")= name
    52 | 
    53 |           %h2
    54 |             Related commands
    55 | 
    56 |           %ul
    57 |             - @related_commands.each do |command|
    58 |               %li
    59 |                 %a(href="/commands/#{command.to_param}")
    60 |                   - if command == @command
    61 |                     %strong= command.name
    62 |                   - else
    63 |                     = command.name
    64 | 
    
    
    --------------------------------------------------------------------------------
    /views/comments.haml:
    --------------------------------------------------------------------------------
     1 | %aside#comments
     2 |   %ul
     3 |     - Comment.find(url: req.path).sort(order: "desc").each do |comment|
     4 |       %li
     5 |         = partial "avatar", email: comment.user.email, size: 32
     6 | 
     7 |         %article
     8 |           - time = Time.at(comment.last_modified.to_i).utc
     9 | 
    10 |           %time(datetime="#{time.iso8601}" pubdate="pubdate")
    11 |             = time.strftime("%b %e, %Y %I.%M%P")
    12 | 
    13 |           ~ RDiscount.new(comment.body, :smart, :filter_html).to_html
    14 | 
    15 |   %form.user.logged-in(method="post" action="/comments" style="display: none")
    16 |     %input(type="hidden" name="url" value="#{req.path}")
    17 | 
    18 |     %img.user.gravatar(src="" width="32" height="32")
    19 | 
    20 |     %textarea(name="body")
    21 | 
    22 |     %input(type="submit" value="Post")
    23 | 
    24 |   %p.user.anonymous
    25 |     %a(href="/login") Log in to comment
    26 | 
    
    
    --------------------------------------------------------------------------------
    /views/community.md:
    --------------------------------------------------------------------------------
     1 | Community
     2 | ===
     3 | 
     4 | To get help or provide any feedback, the main channel is the Redis mailing list:
     5 | 
     6 | * Join the [mailing list](http://groups.google.com/group/redis-db) (Subscribe via [email](mailto:redis-db+subscribe@googlegroups.com)).
     7 | 
     8 | For bug reports please just use [Github](https://github.com/redis/redis).
     9 | 
    10 | Other places where you can find people interested in Redis:
    11 | 
    12 | * Meet people interested in Redis at the [Redis Discord server](https://discord.gg/redis).
    13 | * The [Redis tag on Stack Overflow](http://stackoverflow.com/questions/tagged/redis?sort=newest&pageSize=30).
    14 | * Follow [Redis news feed](http://twitter.com/redisfeed) on Twitter.
    15 | * The Redis community uses a Reddit sub for news and certain announcements (that also always go to the ML): [/r/redis sub on Reddit](https://www.reddit.com/r/redis/).
    16 | 
    17 | Project governance
    18 | ---
    19 | 
    20 | Redis has adopted a light governance model that is intended to be a meritocracy, aiming to empower individuals who demonstrate a long-term commitment and make significant contributions.
    21 | 
    22 | For more information refer to the [Governance page](/topics/governance).
    23 | 
    24 | Conferences and meetups
    25 | ---
    26 | 
    27 | * [Redis Live](https://meetups.redis.com/redis-live/)
    28 | * [RedisConf Annual Conference](https://redis.com/redisconf)
    29 | * [Redis Day Seattle](https://connect.redis.com/redisdayseattle/mktg)
    30 | * [Redis Day Bangalore](https://connect.redis.com/redisdaybangalore)
    31 | * [London Redis Meetup Group](https://www.meetup.com/Redis-London)
    32 | * [San Francisco Meetup Group](http://sfmeetup.redis.io)
    33 | * [New York Meetup Group](https://www.meetup.com/New-York-REDIS-Meetup)
    34 | * [#RedisTLV (Tel Aviv Redis) Meetup Group](https://www.meetup.com/Tel-Aviv-Redis-Meetup)
    35 | * [Paris Redis Meetup](https://www.meetup.com/Paris-Redis-Meetup/)
    36 | 
    37 | Contributing to Redis
    38 | ---
    39 | 
    40 | Would you like to contribute a feature to Redis?
    41 | 
    42 | 1. Drop a message to the [mailing list](http://groups.google.com/group/redis-db) with your proposal. Make sure you explain what the use case is and how the API would look like.
    43 | 
    44 | 2. If you get good feedbacks, do the following to submit a patch:
    45 | 
    46 |     1. Fork [the official repository](http://github.com/redis/redis).
    47 |     2. Clone your fork: `git clone git@github.com:/redis.git`
    48 |     3. Make sure tests are passing for you: `make && make test`
    49 |     4. Create a topic branch: `git checkout -b new-feature`
    50 |     5. Add tests and code for your changes.
    51 |     6. Once you're done, make sure all tests still pass: `make && make test`
    52 |     7. Commit and push to your fork.
    53 |     8. [Create an issue](https://github.com/redis/redis/issues) with a link to your patch.
    54 |     9. Sit back and enjoy.
    55 | 
    56 | There are other ways to help:
    57 | 
    58 | * [Fix a bug or share your experience on issues](https://github.com/redis/redis/issues)
    59 | 
    60 | * Improve the [documentation](http://github.com/redis/redis-doc)
    61 | 
    62 | * Help maintain or create new [client libraries](/clients)
    63 | 
    64 | * Improve [this very website](http://github.com/redis/redis-io)
    65 | 
    
    
    --------------------------------------------------------------------------------
    /views/documentation.md:
    --------------------------------------------------------------------------------
      1 | Documentation
      2 | ===
      3 | 
      4 | Note: The Redis Documentation is also available in raw (computer friendly) format in the [redis-doc github repository](http://github.com/redis/redis-doc). The Redis Documentation is released under the [Creative Commons Attribution-ShareAlike 4.0 International license](https://creativecommons.org/licenses/by-sa/4.0/).
      5 | 
      6 | Programming with Redis
      7 | ---
      8 | 
      9 | * [The full list of commands](/commands) implemented by Redis, along with thorough documentation for each of them.
     10 | * [Pipelining](/topics/pipelining): Learn how to send multiple commands
     11 | at once, saving on round trip time.
     12 | * [Redis Pub/Sub](topics/pubsub): Redis is a fast and stable Publish/Subscribe messaging system! Check it out.
     13 | * [Memory optimization](/topics/memory-optimization): Understand how
     14 | Redis uses RAM and learn some tricks to use less of it.
     15 | * [Expires](/commands/expire): Redis allows to set a time to live different for every key so that the key will be automatically removed from the server when it expires.
     16 | * [Redis as an LRU cache](/topics/lru-cache): How to configure and use Redis as a cache with a fixed amount of memory and auto eviction of keys.
     17 | * [Redis transactions](/topics/transactions): It is possible to group commands together so that they are executed as a single transaction.
     18 | * [Client side caching](/topics/client-side-caching): Starting with version 6 Redis supports server assisted client side caching. This document describes how to use it.
     19 | * [Mass insertion of data](/topics/mass-insert): How to add a big amount of pre existing or generated data to a Redis instance in a short time.
     20 | * [Partitioning](/topics/partitioning): How to distribute your data among multiple Redis instances.
     21 | * [Distributed locks](/topics/distlock): Implementing a distributed lock manager with Redis.
     22 | * [Redis keyspace notifications](/topics/notifications): Get notifications of keyspace events via Pub/Sub (Redis 2.8 or greater).
     23 | * [Creating secondary indexes with Redis](/topics/indexes): Use Redis data structures to create secondary indexes, composed indexes and traverse graphs.
     24 | 
     25 | Redis programmability
     26 | ---
     27 | 
     28 | * [Redis Programability](/topics/programmability): An overview of programmability in Redis.
     29 | * [Redis Lua API](/topics/lua-api): Reference about the embedded [Lua 5.1](https://lua.org) interepreter runtime environment and APIs.
     30 | * [Introduction to Eval Scripts](/topics/eval-intro): An introduction about using cached scripts.
     31 | * [Introduction to Redis Functions](/topics/functions-intro): An introduction about using functions.
     32 | * [Debugging Lua scripts](/topics/ldb): An overveiw of the native Redis Lua debugger for cached scripts.
     33 | 
     34 | Redis modules API
     35 | ---
     36 | 
     37 | * [Introduction to Redis modules](/topics/modules-intro). A good place to start learing about Redis 4.0 modules programming.
     38 | * [Implementing native data types](/topics/modules-native-types). Modules scan implement new data types (data structures and more) that look like built-in data types. This documentation covers the API to do so.
     39 | * [Blocking operations](topics/modules-blocking-ops)  with modules. This is still an experimental API, but a very powerful one to write commands that can block the client (without blocking Redis) and can execute tasks in other threads.
     40 | * [Redis modules API reference](topics/modules-api-ref). Directly generated from the top comments in the source code inside `src/module.c`. Contains many low level details about API usage.
     41 | 
     42 | Tutorials & FAQ
     43 | ---
     44 | 
     45 | * [Introduction to Redis data types](/topics/data-types-intro): This is a good starting point to learn the Redis API and data model.
     46 | * [Introduction to Redis streams](/topics/streams-intro): A detailed description of the Redis 5 new data type, the Stream.
     47 | * [Writing a simple Twitter clone with PHP and Redis](/topics/twitter-clone)
     48 | * [Auto complete with Redis](http://autocomplete.redis.io)
     49 | * [Data types short summary](/topics/data-types): A short summary of the different types of values that Redis supports, not as updated and info rich as the first tutorial listed in this section.
     50 | * [FAQ](/topics/faq): Some common questions about Redis.
     51 | 
     52 | Administration
     53 | ---
     54 | * [Quick Start](/topics/quickstart): How to quickly install and configure Redis. This targets people without prior experience with Redis.
     55 | * [Redis-cli](/topics/rediscli): Learn how to master the Redis command line interface, something you'll be using a lot in order to administer, troubleshoot and experiment with Redis.
     56 | * [Configuration](/topics/config): How to configure redis.
     57 | * [Replication](/topics/replication): What you need to know in order to
     58 | set up master-replicas replication.
     59 | * [Persistence](/topics/persistence): Know your options when configuring
     60 | Redis' durability.
     61 | * [Redis Administration](/topics/admin): Selected administration topics.
     62 | * [Security](/topics/security): An overview of Redis security.
     63 | * [Redis Access Control Lists](/topics/acl): Starting with version 6 Redis supports ACLs. It is possible to configure users able to run only selected commands and able to access only specific key patterns.
     64 | * [Encryption](/topics/encryption): How to encrypt Redis client-server communication.
     65 | * [Signals Handling](/topics/signals): How Redis handles signals.
     66 | * [Connections Handling](/topics/clients): How Redis handles clients connections.
     67 | * [High Availability](/topics/sentinel): Redis Sentinel is the official high availability solution for Redis.
     68 | * [Redis Releases](/topics/releases): Redis development cycle and version numbering.
     69 | 
     70 | Performance
     71 | ---
     72 | * [Latency monitoring](/topics/latency-monitor): Redis integrated latency monitoring and reporting capabilities are helpful to tune Redis instances for low latency workloads.
     73 | * [Benchmarks](/topics/benchmarks): See how fast Redis is in different platforms.
     74 | * [Redis on-CPU profiling and tracing](/topics/performance-on-cpu): See how to perform on-CPU resource bottlenecks analysis in Redis.
     75 | 
     76 | Embedded and IoT
     77 | ---
     78 | 
     79 | * [Redis on ARM and Raspberry Pi](/topics/ARM): Starting with Redis 4.0 ARM and the Raspberry Pi are officially supported platforms. This page contains general information and benchmarks.
     80 | * [A reference implementation of Redis for IoT and Edge Computing can be found here](https://redis.com/redis-enterprise/redis-edge/).
     81 | 
     82 | Troubleshooting
     83 | ---
     84 | 
     85 | * [Redis problems?](/topics/problems): Bugs? High latency? Other issues? Use [our problems troubleshooting page](/topics/problems) as a starting point to find more information.
     86 | 
     87 | Redis Cluster
     88 | ---
     89 | 
     90 | * [Redis Cluster tutorial](/topics/cluster-tutorial): a gentle introduction and setup guide to Redis Cluster.
     91 | * [Redis Cluster specification](/topics/cluster-spec): the more formal description of the behavior and algorithms used in Redis Cluster.
     92 | 
     93 | Command runtime introspection
     94 | ---
     95 | 
     96 | * [Command key specifications](/topics/key-specs): as of Redis 7.0, the server reports how to extract the names of keys accessed by every command.
     97 | * [Command tips](/topics/command-tips): tips communicate non-trivial execution modes and post-processing information about commands.
     98 | * [Command arguments](/topics/command-arguments): an overview of command arguments as returned by the `COMMAND DOCS` command.
     99 | 
    100 | Other distributed systems based on Redis
    101 | ---
    102 | 
    103 | * [Redis CRDTs](https://redis.com/redis-enterprise/technology/active-active-geo-distribution/) an active-active geo-distribution solutions for Redis.
    104 | * [Roshi](https://github.com/soundcloud/roshi) is a large-scale CRDT set implementation for timestamped events based on Redis and implemented in Go. It was initially developed for [the SoundCloud stream](http://developers.soundcloud.com/blog/roshi-a-crdt-system-for-timestamped-events).
    105 | 
    106 | Redis on SSD and persistent memory
    107 | ---
    108 | 
    109 | * [Redis on Flash](https://redis.com/redis-enterprise/technology/redis-on-flash/) by Redis Ltd. extends DRAM capacity with SSD and persistent memory.
    110 | 
    111 | Specifications
    112 | ---
    113 | 
    114 | * [Redis Design Drafts](/topics/rdd): Design drafts of new proposals.
    115 | * [Redis Protocol specification](/topics/protocol): if you're implementing a
    116 | client, or out of curiosity, learn how to communicate with Redis at a
    117 | low level.
    118 | * [Redis RDB format](https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format) specification, and [RDB version history](https://github.com/sripathikrishnan/redis-rdb-tools/blob/master/docs/RDB_Version_History.textile).
    119 | * [Internals](/topics/internals): Learn details about how Redis is implemented under the hood.
    120 | 
    121 | Resources
    122 | ---
    123 | 
    124 | * [Redis Cheat Sheet](http://www.cheatography.com/tasjaevan/cheat-sheets/redis/): Online or printable function reference for Redis.
    125 | 
    126 | Use cases
    127 | ---
    128 | * [Who is using Redis](/topics/whos-using-redis)
    129 | 
    130 | Books
    131 | ---
    132 | 
    133 | The following is a list of books covering Redis that are already published. Books are ordered by release date (newer books first).
    134 | 
    135 | * [Mastering Redis (Packt, 2016)](https://www.packtpub.com/big-data-and-business-intelligence/mastering-redis) by [Jeremy Nelson](https://www.packtpub.com/books/info/authors/jeremy-nelson).
    136 | * [Redis Essentials (Packt, 2015)](http://www.amazon.com/Redis-Essentials-Maxwell-Dayvson-Silva-ebook/dp/B00ZXFCFLO) by [Maxwell Da Silva](http://twitter.com/dayvson) and [Hugo Tavares](https://twitter.com/hltbra)
    137 | * [Redis in Action (Manning, 2013)](http://www.manning.com/carlson/) by [Josiah L. Carlson](http://twitter.com/dr_josiah) (early access edition).
    138 | * [Instant Redis Optimization How-to (Packt, 2013)](http://www.packtpub.com/redis-optimization-how-to/book) by [Arun Chinnachamy](http://twitter.com/ArunChinnachamy).
    139 | * [Instant Redis Persistence (Packt, 2013)](http://www.packtpub.com/redis-persistence/book) by Matt Palmer.
    140 | * [The Little Redis Book (Free Book, 2012)](http://openmymind.net/2012/1/23/The-Little-Redis-Book/) by [Karl Seguin](http://twitter.com/karlseguin) is a great *free* and concise book that will get you started with Redis.
    141 | * [Redis Cookbook (O'Reilly Media, 2011)](http://shop.oreilly.com/product/0636920020127.do) by [Tiago Macedo](http://twitter.com/tmacedo) and [Fred Oliveira](http://twitter.com/f).
    142 | 
    143 | The following books have Redis related content but are not specifically about Redis:
    144 | 
    145 | * [Seven databases in seven weeks (The Pragmatic Bookshelf, 2012)](http://pragprog.com/book/rwdata/seven-databases-in-seven-weeks).
    146 | * [Mining the Social Web (O'Reilly Media, 2011)](http://shop.oreilly.com/product/0636920010203.do)
    147 | * [Professional NoSQL (Wrox, 2011)](http://www.wrox.com/WileyCDA/WroxTitle/Professional-NoSQL.productCd-047094224X.html)
    148 | 
    149 | Credits
    150 | ---
    151 | 
    152 | Redis is developed and maintained by the [Redis community](/community).
    153 | 
    154 | The project was created, developed and maintained by [Salvatore Sanfilippo](http://twitter.com/antirez) until [June 30th, 2020](http://antirez.com/news/133). In the past [Pieter Noordhuis](http://twitter.com/pnoordhuis) and [Matt Stancliff](https://matt.sh) provided a very significant amount of code and ideas to both the Redis core and client libraries.
    155 | 
    156 | The full list of Redis contributors can be found in the [Redis contributors page at Github](https://github.com/redis/redis/graphs/contributors). However there are other forms of contributions such as ideas, testing, and bug reporting. When it is possible, contributions are acknowledged in commit messages. The [mailing list archives](http://groups.google.com/group/redis-db) and the [Github issues page](https://github.com/redis/redis/issues) are good sources to find people active in the Redis community providing ideas and helping other users.
    157 | 
    158 | Sponsors
    159 | ---
    160 | 
    161 | The work [Salvatore Sanfilippo](http://antirez.com) does in order to develop Redis is sponsored by [Redis Ltd.](http://redis.com) Other sponsors and past sponsors of the Redis project are listed in the [Sponsors page](/topics/sponsors).
    162 | 
    163 | License, Trademark and Logo
    164 | ---
    165 | 
    166 | * Redis is released under the three clause BSD license. You can find [additional information in our license page](/topics/license).
    167 | * The Redis trademark and logos are owned by Redis Ltd., please read the [Redis trademark guidelines](/topics/trademark) for our policy about the use of the Redis trademarks and logo.
    168 | 
    
    
    --------------------------------------------------------------------------------
    /views/download.haml:
    --------------------------------------------------------------------------------
      1 | .site-content
      2 |   .text
      3 |     %h1 Download
      4 | 
      5 |     %p
      6 |       Stable releases liberally follow the usual
      7 |       %strong major.minor.patch
      8 |       semantic versioning schema.
      9 | 
     10 |     %ul.download-versions
     11 |       - DOWNLOADS[:channels].each do |channel, download|
     12 |         %li
     13 |           %h2
     14 |             = download[:name] || channel.capitalize
     15 | 
     16 |             - if download[:branch] =~ /^\d/
     17 |               %span.download-version= "(#{download[:branch]})"
     18 | 
     19 |           = download[:notes]
     20 | 
     21 |           .download-links
     22 | 
     23 |             - if channel != :unstable && channel != :docker && channel != :cloud
     24 |               %a.download-link(href="https://raw.githubusercontent.com/redis/redis/#{download[:branch]}/00-RELEASENOTES")
     25 |                 %i.fa.fa-file-text-o
     26 |                 Release notes
     27 | 
     28 |             - if channel == :stable
     29 |               - download_link = "https://download.redis.io/releases/redis-#{download[:version]}.tar.gz"
     30 |             - elsif channel == :docker || channel == :cloud
     31 |               - download_link = download[:url]
     32 |             - else
     33 |               - download_link = "https://github.com/redis/redis/archive/#{download[:version] || download[:branch]}.tar.gz"
     34 | 
     35 |             - if channel == :docker
     36 |               %a.download-link(href="#{download_link}")
     37 |                 %i.fa.fa-external-link
     38 |                 Docker Hub
     39 |             - elsif channel == :cloud
     40 |               %a.download-link(href="#{download_link}")
     41 |                 %i.fa.fa-external-link
     42 |                 Redis Cloud
     43 |             - else
     44 |               %a.download-link(href="#{download_link}")
     45 |                 %i.fa.fa-arrow-circle-o-down
     46 |                 Download
     47 |                 = download[:version] || download[:branch]
     48 | 
     49 |     %h2 Other versions
     50 | 
     51 |     - DOWNLOADS[:other].each do |name, download|
     52 |       %h3
     53 |         = name.capitalize
     54 | 
     55 |         - if download[:branch] =~ /^\d/
     56 |           %span.download-version= "(#{download[:branch]})"
     57 | 
     58 |       = download[:notes]
     59 | 
     60 |       - if download[:version]
     61 |         %br
     62 |         See the
     63 | 
     64 |         %a(href="https://raw.githubusercontent.com/redis/redis/#{download[:branch]}/00-RELEASENOTES")
     65 |           release notes
     66 | 
     67 |         or
     68 | 
     69 |         - download_link = "https://download.redis.io/releases/redis-#{download[:version]}.tar.gz"
     70 | 
     71 |         %a(href="#{download_link}")
     72 |           download #{download[:version]}.
     73 | 
     74 |       - else
     75 |         %a(href="#{download[:url]}") Learn more
     76 | 
     77 |     %h3 Other
     78 | 
     79 |     Historical downloads are still available on
     80 |     = succeed "." do
     81 |       %a(href="https://download.redis.io/releases/") https://download.redis.io/releases/
     82 | 
     83 |     %p
     84 |       %strong Scripts and other automatic downloads
     85 |       can easily access the tarball of the latest Redis stable version at
     86 |       = succeed "," do
     87 |         %a{:href => "https://download.redis.io/redis-stable.tar.gz"} https://download.redis.io/redis-stable.tar.gz
     88 |       and its respective SHA256 sum at
     89 |       = succeed "." do
     90 |         %a{:href => "https://download.redis.io/redis-stable.tar.gz.SHA256SUM"} https://download.redis.io/redis-stable.tar.gz.SHA256SUM
     91 |       The source code of the latest stable release is
     92 |       = succeed "," do
     93 |         %a{:href => "https://download.redis.io/redis-stable"} always browsable here
     94 |       use the file
     95 |       %strong src/version.h
     96 |       in order to extract the version in an automatic way.
     97 | 
     98 |     %h2 How to verify files for integrity
     99 | 
    100 |     %p
    101 |       The Github repository
    102 |       %a(href="https://github.com/redis/redis-hashes/blob/master/README") redis-hashes
    103 |       contains a README file with SHA1 digests of released tarball archives.
    104 |       Note: the generic redis-stable.tar.gz tarball does not match any hash because it is modified to untar to the redis-stable directory.
    105 | 
    106 |     %h2 Installation
    107 |     %h3 From source code
    108 |     %p Download, extract and compile Redis with:
    109 |     %pre
    110 |       %code
    111 |         :preserve
    112 |           $ wget https://download.redis.io/releases/redis-#{STABLE_VERSION}.tar.gz
    113 |           $ tar xzf redis-#{STABLE_VERSION}.tar.gz
    114 |           $ cd redis-#{STABLE_VERSION}
    115 |           $ make
    116 |     %p
    117 |       The binaries that are now compiled are available in the
    118 |       %code src
    119 |       directory. Run Redis with:
    120 |     %pre
    121 |       %code
    122 |         :preserve
    123 |           $ src/redis-server
    124 |     %p You can interact with Redis using the built-in client:
    125 |     %pre
    126 |       %code
    127 |         :preserve
    128 |           $ src/redis-cli
    129 |           redis> set foo bar
    130 |           OK
    131 |           redis> get foo
    132 |           "bar"
    133 |     %h3 From the official Debian/Ubuntu APT Repository [Beta]
    134 |     %p
    135 |       You can install recent stable versions of Redis from the official
    136 |       packages.redis.io APT repository. Add the repository to the
    137 |       apt index, update it and install:
    138 |     %pre
    139 |       %code
    140 |         :preserve
    141 |           $ curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
    142 |           $ echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
    143 |           $ sudo apt-get update
    144 |           $ sudo apt-get install redis
    145 |     %h3 From the official Ubuntu PPA
    146 |     %p
    147 |       You can install the latest stable version of Redis from the
    148 |       redislabs/redis package repository. Add the repository to the
    149 |       apt index, update it and install:
    150 |     %pre
    151 |       %code
    152 |         :preserve
    153 |           $ sudo add-apt-repository ppa:redislabs/redis
    154 |           $ sudo apt-get update
    155 |           $ sudo apt-get install redis
    156 |     %h3 From Snapcraft
    157 |     %p 
    158 |       You can install the latest stable version of Redis from the Snapcraft
    159 |       marketplace:
    160 |     %pre
    161 |       %code
    162 |         :preserve
    163 |           $ sudo snap install redis
    164 |     %p
    165 |       Are you new to Redis? Try our
    166 |       = succeed "." do
    167 |         %a{:href => "http://try.redis.io"} online, interactive tutorial
    168 | 
    169 | 
    
    
    --------------------------------------------------------------------------------
    /views/grid.scss:
    --------------------------------------------------------------------------------
     1 | /////////////////
     2 | // Semantic.gs // for SCSS: http://sass-lang.com/
     3 | /////////////////
     4 | 
     5 | // Defaults which you can freely override
     6 | $column-width: 60px;
     7 | $gutter-width: 20px;
     8 | $columns: 12;
     9 | 
    10 | // Utility function — you should never need to modify this
    11 | @function gridsystem-width($columns:$columns) {
    12 | 	@return ($column-width * $columns) + ($gutter-width * $columns);
    13 | }
    14 | 
    15 | // Set $total-width to 100% for a fluid layout
    16 | $total-width: 100%;
    17 | 
    18 | // Uncomment these two lines and the star-hack width/margin lines below to enable sub-pixel fix for IE6 & 7. See http://tylertate.com/blog/2012/01/05/subpixel-rounding.html
    19 | // $min-width: 999999;
    20 | // $correction: 0.5 / $min-width * 100;
    21 | 
    22 | // The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/
    23 | @mixin clearfix() {
    24 | 	*zoom:1;
    25 | 
    26 | 	&:before,
    27 | 	&:after {
    28 | 	    content:"";
    29 | 	    display:table;
    30 | 	}
    31 | 	&:after {
    32 | 	    clear:both;
    33 | 	}
    34 | }
    35 | 
    36 | 
    37 | //////////
    38 | // GRID //
    39 | //////////
    40 | 
    41 | body {
    42 | 	width: 100%;
    43 | 	@include clearfix();
    44 | }
    45 | 
    46 | @mixin row($columns:$columns) {
    47 | 	display: block;
    48 | 	width: $total-width*(($gutter-width + gridsystem-width($columns))/gridsystem-width($columns));
    49 | 	margin: 0 $total-width*((($gutter-width*.5)/gridsystem-width($columns))*-1);
    50 | 	// *width: $total-width*(($gutter-width + gridsystem-width($columns))/gridsystem-width($columns))-$correction;
    51 | 	// *margin: 0 $total-width*((($gutter-width*.5)/gridsystem-width($columns))*-1)-$correction;
    52 | 	@include clearfix();
    53 | }
    54 | @mixin column($x,$columns:$columns) {
    55 | 	display: inline;
    56 | 	float: left;
    57 | 	width: $total-width*(((($gutter-width+$column-width)*$x)-$gutter-width) / gridsystem-width($columns));
    58 | 	margin: 0 $total-width*(($gutter-width*.5)/gridsystem-width($columns));
    59 | 	// *width: $total-width*(((($gutter-width+$column-width)*$x)-$gutter-width) / gridsystem-width($columns))-$correction;
    60 | 	// *margin: 0 $total-width*(($gutter-width*.5)/gridsystem-width($columns))-$correction;
    61 | }
    62 | @mixin push($offset:1) {
    63 | 	margin-left: $total-width*((($gutter-width+$column-width)*$offset) / gridsystem-width($columns)) + $total-width*(($gutter-width*.5)/gridsystem-width($columns));
    64 | }
    65 | @mixin pull($offset:1) {
    66 | 	margin-right: $total-width*((($gutter-width+$column-width)*$offset) / gridsystem-width($columns)) + $total-width*(($gutter-width*.5)/gridsystem-width($columns));
    67 | }
    68 | 
    
    
    --------------------------------------------------------------------------------
    /views/home.haml:
    --------------------------------------------------------------------------------
     1 | .site-content
     2 |   %section.home-callout.home-section
     3 |     .title
     4 |       != Oga.parse_html(custom_view("#{documentation_path}/topics/introduction.md")).at_xpath("//p").text
     5 | 
     6 |       %a.learn-more(href="/topics/introduction") Learn more →
     7 | 
     8 |   .home-intro.home-section
     9 | 
    10 |     %section
    11 |       %h2 Try it
    12 | 
    13 |       %p
    14 |         Ready for a test drive? Check this
    15 |         %a(href="http://try.redis.io") interactive tutorial
    16 |         that will walk you through the most important features of
    17 |         Redis.
    18 | 
    19 |     %section
    20 |       %h2 Download it
    21 | 
    22 |       %p
    23 |         %a(href="https://download.redis.io/releases/redis-#{STABLE_VERSION}.tar.gz") Redis #{STABLE_VERSION} is the latest stable version.
    24 |         Interested in release candidates or unstable versions?
    25 |         %a(href="/download") Check the downloads page.
    26 | 
    27 |     %section
    28 |       %h2 Quick links
    29 |       %p
    30 |         Follow day-to-day Redis on Twitter and
    31 |         GitHub.
    32 |         Get help or help others by subscribing to our mailing list, we are 5,000 and counting!
    33 | 
    34 |   .home-buzz.home-section
    35 |     %section#buzz(data-limit="5")
    36 |       %h2 Redis News
    37 | 
    38 |       %ul
    39 | 
    40 |       %a(href="/buzz") More...
    41 | 
    
    
    --------------------------------------------------------------------------------
    /views/interactive.haml:
    --------------------------------------------------------------------------------
     1 | %div(class="example" data-session="#{session.namespace}")
     2 |   - lines.each do |line|
     3 |     %span(class="monospace prompt")= "redis> "
     4 |     %span(class="monospace command")= line
     5 |     %pre<= session.run(HTMLEntities.new.decode(line))
     6 |   %form>
     7 |     %span(class="monospace prompt")= "redis> "
     8 |     %input(type="text" name="command" autocomplete="off" spellcheck="false")
     9 | 
    10 | 
    
    
    --------------------------------------------------------------------------------
    /views/layout.haml:
    --------------------------------------------------------------------------------
     1 | !!! 5
     2 | 
     3 | %html
     4 |   %head
     5 |     %meta(charset="utf-8")
     6 |     %title= [@title, "Redis"].compact.join(" – ")
     7 |     %link(rel="stylesheet" href="/styles.css")
     8 |     %link(rel="shortcut icon" href="/images/favicon.png")
     9 |     %link(rel="search" type="application/opensearchdescription+xml" title="Look up a Redis command" href="/opensearch.xml")
    10 |     %meta(name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0")
    11 | 
    12 |     :javascript
    13 |       (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    14 |       new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    15 |       j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    16 |       'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    17 |       })(window,document,'script','dataLayer','GTM-T4MTBKP');
    18 | 
    19 | 
    20 |   %body(class="#{@css.join(" ") if @css}")
    21 | 
    22 |     %noscript
    23 |       %iframe(src="https://www.googletagmanager.com/ns.html?id=GTM-T4MTBKP"
    24 |               height="0" width="0"
    25 |               style="display:none;visibility:hidden")
    26 | 
    27 |     .mobile-menu.slideout-menu
    28 |       %header.menu-header
    29 | 
    30 |       %section.menu-section
    31 |         %ul.menu-section-list
    32 |           %li
    33 |             %a.home(href="/") Home
    34 |           %li
    35 |             %a(href="/commands") Commands
    36 |           %li
    37 |             %a(href="/clients") Clients
    38 |           %li
    39 |             %a(href="/documentation") Documentation
    40 |           %li
    41 |             %a(href="/community") Community
    42 |           %li
    43 |             %a(href="/download") Download
    44 |           %li
    45 |             %a(href="/modules") Modules
    46 |           %li
    47 |             %a(href="/support") Support
    48 | 
    49 |     .site-wrapper
    50 | 
    51 |       %header.site-header
    52 |         %nav.container
    53 |           .mobile-header
    54 |             %button.btn-hamburger.js-slideout-toggle
    55 |               %span.fa.fa-bars
    56 | 
    57 |             %a.home(href="/")
    58 |               %img(src="/images/redis-white.png" alt="Redis")
    59 | 
    60 |             %a.tryfree(href="https://redis.com/try-free") Try Free
    61 | 
    62 |           .desktop-header
    63 |             %a.home(href="/")
    64 |               %img(src="/images/redis-white.png" alt="Redis")
    65 |             %a(href="/commands") Commands
    66 |             %a(href="/clients") Clients
    67 |             %a(href="/documentation") Documentation
    68 |             %a(href="/community") Community
    69 |             %a(href="/download") Download
    70 |             %a(href="/modules") Modules
    71 |             %a(href="/support") Support
    72 |             %a.tryfree(href="https://redis.com/try-free") Try Free
    73 | 
    74 |       - if Date.today < Date.new(2022, 4, 1)
    75 |         %aside.wide-callout
    76 |           .container
    77 |             %a(href="https://redis.com/redisdays/") RedisDays registration
    78 |             is open! Don’t miss our big announcements.
    79 | 
    80 |       = content
    81 | 
    82 |       %footer.site-footer
    83 |         .container
    84 |           %p
    85 |             This website is
    86 |             open source software
    87 |             and is sponsored by
    88 |             Redis Ltd.
    89 |             See all credits.
    90 | 
    91 |     %script(src="https://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js")
    92 |     %script(src="/app.js?#{File.mtime("public/app.js").to_i}")
    93 | 
    
    
    --------------------------------------------------------------------------------
    /views/modules.haml:
    --------------------------------------------------------------------------------
     1 | .site-content
     2 |   .text
     3 |     %section#clients
     4 | 
     5 |       %h1 Redis Modules
     6 | 
     7 |       %p
     8 |         This is a list of Redis modules, for Redis v4.0 or greater, ordered by Github stars. This list contains two set of modules: modules under an OSI approved license, and modules that are under some proprietary license. Non OSI modules are clearly flagged as not open source. Also to have the source code hosted at Github is currently mandatory. To add your module here please send a pull request for the modules.json file in the Redis-doc repository. More information about Redis modules can be found here.
     9 | 
    10 |       %table
    11 |         %col(class="name")
    12 |         %col(class="license")
    13 |         %col(class="stars")
    14 |         %col(class="repository")
    15 |         %col(class="description")
    16 |         %col(class="authors")
    17 | 
    18 |         - @modules.each do |mod|
    19 |           %tr
    20 |             %td
    21 |               = mod["name"]
    22 | 
    23 |             %td
    24 |               - if mod["repository"]
    25 |                 %a(href="#{mod["repository"]}" class="icon")
    26 |                   %i(class="fa fa-home")
    27 | 
    28 |             %td.description
    29 |               = mod["description"]
    30 | 
    31 |             %td.authors
    32 |               - if mod["authors"]
    33 |                 - mod["authors"].each do |author|
    34 |                   %a(href="https://github.com/#{author}") #{author}
    35 | 
    36 |             %td
    37 |               = mod["license"]
    38 | 
    39 |             %td
    40 |               #{mod["stars"]}
    41 |               %i(class="fa fa-star icon-star")
    42 | 
    
    
    --------------------------------------------------------------------------------
    /views/normalize.scss:
    --------------------------------------------------------------------------------
      1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
      2 | 
      3 | /**
      4 |  * 1. Set default font family to sans-serif.
      5 |  * 2. Prevent iOS text size adjust after orientation change, without disabling
      6 |  *    user zoom.
      7 |  */
      8 | 
      9 | html {
     10 |   font-family: sans-serif; /* 1 */
     11 |   -ms-text-size-adjust: 100%; /* 2 */
     12 |   -webkit-text-size-adjust: 100%; /* 2 */
     13 | }
     14 | 
     15 | /**
     16 |  * Remove default margin.
     17 |  */
     18 | 
     19 | body {
     20 |   margin: 0;
     21 | }
     22 | 
     23 | /* HTML5 display definitions
     24 |    ========================================================================== */
     25 | 
     26 | /**
     27 |  * Correct `block` display not defined for any HTML5 element in IE 8/9.
     28 |  * Correct `block` display not defined for `details` or `summary` in IE 10/11
     29 |  * and Firefox.
     30 |  * Correct `block` display not defined for `main` in IE 11.
     31 |  */
     32 | 
     33 | article,
     34 | aside,
     35 | details,
     36 | figcaption,
     37 | figure,
     38 | footer,
     39 | header,
     40 | hgroup,
     41 | main,
     42 | menu,
     43 | nav,
     44 | section,
     45 | summary {
     46 |   display: block;
     47 | }
     48 | 
     49 | /**
     50 |  * 1. Correct `inline-block` display not defined in IE 8/9.
     51 |  * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
     52 |  */
     53 | 
     54 | audio,
     55 | canvas,
     56 | progress,
     57 | video {
     58 |   display: inline-block; /* 1 */
     59 |   vertical-align: baseline; /* 2 */
     60 | }
     61 | 
     62 | /**
     63 |  * Prevent modern browsers from displaying `audio` without controls.
     64 |  * Remove excess height in iOS 5 devices.
     65 |  */
     66 | 
     67 | audio:not([controls]) {
     68 |   display: none;
     69 |   height: 0;
     70 | }
     71 | 
     72 | /**
     73 |  * Address `[hidden]` styling not present in IE 8/9/10.
     74 |  * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
     75 |  */
     76 | 
     77 | [hidden],
     78 | template {
     79 |   display: none;
     80 | }
     81 | 
     82 | /* Links
     83 |    ========================================================================== */
     84 | 
     85 | /**
     86 |  * Remove the gray background color from active links in IE 10.
     87 |  */
     88 | 
     89 | a {
     90 |   background-color: transparent;
     91 | }
     92 | 
     93 | /**
     94 |  * Improve readability when focused and also mouse hovered in all browsers.
     95 |  */
     96 | 
     97 | a:active,
     98 | a:hover {
     99 |   outline: 0;
    100 | }
    101 | 
    102 | /* Text-level semantics
    103 |    ========================================================================== */
    104 | 
    105 | /**
    106 |  * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
    107 |  */
    108 | 
    109 | abbr[title] {
    110 |   border-bottom: 1px dotted;
    111 | }
    112 | 
    113 | /**
    114 |  * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
    115 |  */
    116 | 
    117 | b,
    118 | strong {
    119 |   font-weight: bold;
    120 | }
    121 | 
    122 | /**
    123 |  * Address styling not present in Safari and Chrome.
    124 |  */
    125 | 
    126 | dfn {
    127 |   font-style: italic;
    128 | }
    129 | 
    130 | /**
    131 |  * Address variable `h1` font-size and margin within `section` and `article`
    132 |  * contexts in Firefox 4+, Safari, and Chrome.
    133 |  */
    134 | 
    135 | h1 {
    136 |   font-size: 2em;
    137 |   margin: 0.67em 0;
    138 | }
    139 | 
    140 | /**
    141 |  * Address styling not present in IE 8/9.
    142 |  */
    143 | 
    144 | mark {
    145 |   background: #ff0;
    146 |   color: #000;
    147 | }
    148 | 
    149 | /**
    150 |  * Address inconsistent and variable font size in all browsers.
    151 |  */
    152 | 
    153 | small {
    154 |   font-size: 80%;
    155 | }
    156 | 
    157 | /**
    158 |  * Prevent `sub` and `sup` affecting `line-height` in all browsers.
    159 |  */
    160 | 
    161 | sub,
    162 | sup {
    163 |   font-size: 75%;
    164 |   line-height: 0;
    165 |   position: relative;
    166 |   vertical-align: baseline;
    167 | }
    168 | 
    169 | sup {
    170 |   top: -0.5em;
    171 | }
    172 | 
    173 | sub {
    174 |   bottom: -0.25em;
    175 | }
    176 | 
    177 | /* Embedded content
    178 |    ========================================================================== */
    179 | 
    180 | /**
    181 |  * Remove border when inside `a` element in IE 8/9/10.
    182 |  */
    183 | 
    184 | img {
    185 |   border: 0;
    186 | }
    187 | 
    188 | /**
    189 |  * Correct overflow not hidden in IE 9/10/11.
    190 |  */
    191 | 
    192 | svg:not(:root) {
    193 |   overflow: hidden;
    194 | }
    195 | 
    196 | /* Grouping content
    197 |    ========================================================================== */
    198 | 
    199 | /**
    200 |  * Address margin not present in IE 8/9 and Safari.
    201 |  */
    202 | 
    203 | figure {
    204 |   margin: 1em 40px;
    205 | }
    206 | 
    207 | /**
    208 |  * Address differences between Firefox and other browsers.
    209 |  */
    210 | 
    211 | hr {
    212 |   -moz-box-sizing: content-box;
    213 |   box-sizing: content-box;
    214 |   height: 0;
    215 | }
    216 | 
    217 | /**
    218 |  * Contain overflow in all browsers.
    219 |  */
    220 | 
    221 | pre {
    222 |   overflow: auto;
    223 | }
    224 | 
    225 | /**
    226 |  * Address odd `em`-unit font size rendering in all browsers.
    227 |  */
    228 | 
    229 | code,
    230 | kbd,
    231 | pre,
    232 | samp {
    233 |   font-family: monospace, monospace;
    234 |   font-size: 1em;
    235 | }
    236 | 
    237 | /* Forms
    238 |    ========================================================================== */
    239 | 
    240 | /**
    241 |  * Known limitation: by default, Chrome and Safari on OS X allow very limited
    242 |  * styling of `select`, unless a `border` property is set.
    243 |  */
    244 | 
    245 | /**
    246 |  * 1. Correct color not being inherited.
    247 |  *    Known issue: affects color of disabled elements.
    248 |  * 2. Correct font properties not being inherited.
    249 |  * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
    250 |  */
    251 | 
    252 | button,
    253 | input,
    254 | optgroup,
    255 | select,
    256 | textarea {
    257 |   color: inherit; /* 1 */
    258 |   font: inherit; /* 2 */
    259 |   margin: 0; /* 3 */
    260 | }
    261 | 
    262 | /**
    263 |  * Address `overflow` set to `hidden` in IE 8/9/10/11.
    264 |  */
    265 | 
    266 | button {
    267 |   overflow: visible;
    268 | }
    269 | 
    270 | /**
    271 |  * Address inconsistent `text-transform` inheritance for `button` and `select`.
    272 |  * All other form control elements do not inherit `text-transform` values.
    273 |  * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
    274 |  * Correct `select` style inheritance in Firefox.
    275 |  */
    276 | 
    277 | button,
    278 | select {
    279 |   text-transform: none;
    280 | }
    281 | 
    282 | /**
    283 |  * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
    284 |  *    and `video` controls.
    285 |  * 2. Correct inability to style clickable `input` types in iOS.
    286 |  * 3. Improve usability and consistency of cursor style between image-type
    287 |  *    `input` and others.
    288 |  */
    289 | 
    290 | button,
    291 | html input[type="button"], /* 1 */
    292 | input[type="reset"],
    293 | input[type="submit"] {
    294 |   -webkit-appearance: button; /* 2 */
    295 |   cursor: pointer; /* 3 */
    296 | }
    297 | 
    298 | /**
    299 |  * Re-set default cursor for disabled elements.
    300 |  */
    301 | 
    302 | button[disabled],
    303 | html input[disabled] {
    304 |   cursor: default;
    305 | }
    306 | 
    307 | /**
    308 |  * Remove inner padding and border in Firefox 4+.
    309 |  */
    310 | 
    311 | button::-moz-focus-inner,
    312 | input::-moz-focus-inner {
    313 |   border: 0;
    314 |   padding: 0;
    315 | }
    316 | 
    317 | /**
    318 |  * Address Firefox 4+ setting `line-height` on `input` using `!important` in
    319 |  * the UA stylesheet.
    320 |  */
    321 | 
    322 | input {
    323 |   line-height: normal;
    324 | }
    325 | 
    326 | /**
    327 |  * It's recommended that you don't attempt to style these elements.
    328 |  * Firefox's implementation doesn't respect box-sizing, padding, or width.
    329 |  *
    330 |  * 1. Address box sizing set to `content-box` in IE 8/9/10.
    331 |  * 2. Remove excess padding in IE 8/9/10.
    332 |  */
    333 | 
    334 | input[type="checkbox"],
    335 | input[type="radio"] {
    336 |   box-sizing: border-box; /* 1 */
    337 |   padding: 0; /* 2 */
    338 | }
    339 | 
    340 | /**
    341 |  * Fix the cursor style for Chrome's increment/decrement buttons. For certain
    342 |  * `font-size` values of the `input`, it causes the cursor style of the
    343 |  * decrement button to change from `default` to `text`.
    344 |  */
    345 | 
    346 | input[type="number"]::-webkit-inner-spin-button,
    347 | input[type="number"]::-webkit-outer-spin-button {
    348 |   height: auto;
    349 | }
    350 | 
    351 | /**
    352 |  * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
    353 |  * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
    354 |  *    (include `-moz` to future-proof).
    355 |  */
    356 | 
    357 | input[type="search"] {
    358 |   -webkit-appearance: textfield; /* 1 */
    359 |   -moz-box-sizing: content-box;
    360 |   -webkit-box-sizing: content-box; /* 2 */
    361 |   box-sizing: content-box;
    362 | }
    363 | 
    364 | /**
    365 |  * Remove inner padding and search cancel button in Safari and Chrome on OS X.
    366 |  * Safari (but not Chrome) clips the cancel button when the search input has
    367 |  * padding (and `textfield` appearance).
    368 |  */
    369 | 
    370 | input[type="search"]::-webkit-search-cancel-button,
    371 | input[type="search"]::-webkit-search-decoration {
    372 |   -webkit-appearance: none;
    373 | }
    374 | 
    375 | /**
    376 |  * Define consistent border, margin, and padding.
    377 |  */
    378 | 
    379 | fieldset {
    380 |   border: 1px solid #c0c0c0;
    381 |   margin: 0 2px;
    382 |   padding: 0.35em 0.625em 0.75em;
    383 | }
    384 | 
    385 | /**
    386 |  * 1. Correct `color` not being inherited in IE 8/9/10/11.
    387 |  * 2. Remove padding so people aren't caught out if they zero out fieldsets.
    388 |  */
    389 | 
    390 | legend {
    391 |   border: 0; /* 1 */
    392 |   padding: 0; /* 2 */
    393 | }
    394 | 
    395 | /**
    396 |  * Remove default vertical scrollbar in IE 8/9/10/11.
    397 |  */
    398 | 
    399 | textarea {
    400 |   overflow: auto;
    401 | }
    402 | 
    403 | /**
    404 |  * Don't inherit the `font-weight` (applied by a rule above).
    405 |  * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
    406 |  */
    407 | 
    408 | optgroup {
    409 |   font-weight: bold;
    410 | }
    411 | 
    412 | /* Tables
    413 |    ========================================================================== */
    414 | 
    415 | /**
    416 |  * Remove most spacing between table cells.
    417 |  */
    418 | 
    419 | table {
    420 |   border-collapse: collapse;
    421 |   border-spacing: 0;
    422 | }
    423 | 
    424 | td,
    425 | th {
    426 |   padding: 0;
    427 | }
    428 | 
    
    
    --------------------------------------------------------------------------------
    /views/support.md:
    --------------------------------------------------------------------------------
     1 | Support
     2 | ===
     3 | 
     4 | Commercial Support
     5 | ---
     6 | 
     7 | [Redis Ltd.](https://redis.com) is the official sponsor of open source Redis.
     8 | 
     9 | In addition, Redis Ltd. provides commercial support with Redis Enterprise, available as [a serverless fully-managed cloud service](https://redis.com/products/redis-cloud/) and an [on-premises software deployment](https://redis.com/redis-enterprise/advantages).
    10 | 
    11 | Redis Enterprise simplifies the management of Redis at scale and includes [advanced security](https://redisl.com/enterprise-grade-redis-security/), [active-active geo distribution](https://redis.com/redis-enterprise/technology/active-active-geo-distribution/), and on-call customer support.
    12 | 
    13 | Redis Ltd. also develops several Redis modules that extend the functionality of Redis. These include [RediSearch](https://oss.redis.com/redisearch/) (querying, indexing, and full-text search for Redis), [RedisJSON](https://oss.redis.com/redisjson/) (JSON for Redis), [RedisAI] (https://oss.redis.com/redisai/) (running AI inferencing closer to where you data lives, in Redis), [RedisGraph] (https://oss.redis.com/redisgraph) (a Graph database on Redis), [RedisTimeSeries](https://oss.redis.com/redistimeseries/) (a time series database on Redis), [RedisBloom](https://oss.redis.com/redisbloom/) (advanced probabilistic data-structures for Redis: bloom and cuckoo filters, count-min sketch, top-k) and [RedisGears](https://oss.redis.com/redisgears/) (a distributed programmability engine for Redis).
    14 | 
    15 | 
    16 | Community Support
    17 | ---
    18 | 
    19 | Visit the [Community](/community) page for public support options and details on how to contribute to Redis.
    20 | 
    
    
    --------------------------------------------------------------------------------
    /views/topics/name.haml:
    --------------------------------------------------------------------------------
     1 | .site-content
     2 |   .text
     3 | 
     4 |     %article#topic
     5 | 
     6 |       - unless @related_commands.nil? || @related_commands.empty?
     7 |         %aside
     8 |           %h2
     9 |             Related commands
    10 | 
    11 |           %ul
    12 |             - @related_commands.each do |command|
    13 |               %li
    14 |                 %a(href="/commands/#{command.to_param}")= command.name
    15 | 
    16 |       ~ @body
    17 | 
    
    
    --------------------------------------------------------------------------------