├── .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}#{hdr}>]
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 | "
" +
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 |
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 |
--------------------------------------------------------------------------------