├── .DS_Store ├── .coveralls.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── redis_web_manager_manifest.js │ ├── javascripts │ │ └── redis_web_manager │ │ │ ├── application.js │ │ │ ├── bootstrap.js │ │ │ ├── chartjs.js │ │ │ ├── dashboard.js │ │ │ ├── jquery.js │ │ │ ├── keys.js │ │ │ └── popper.js │ └── stylesheets │ │ └── redis_web_manager │ │ ├── application.css │ │ ├── bootstrap.css │ │ └── dashboard.css ├── controllers │ └── redis_web_manager │ │ ├── actions_controller.rb │ │ ├── application_controller.rb │ │ ├── clients_controller.rb │ │ ├── configuration_controller.rb │ │ ├── dashboard_controller.rb │ │ ├── information_controller.rb │ │ └── keys_controller.rb ├── helpers │ └── redis_web_manager │ │ ├── application_helper.rb │ │ ├── clients_helper.rb │ │ ├── dashboard_helper.rb │ │ └── keys_helper.rb └── views │ ├── layouts │ └── redis_web_manager │ │ └── application.html.erb │ └── redis_web_manager │ ├── clients │ └── index.html.erb │ ├── configuration │ └── index.html.erb │ ├── dashboard │ └── index.html.erb │ ├── information │ └── index.html.erb │ ├── keys │ ├── _search.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ └── show.html.erb │ └── shared │ ├── _header.html.erb │ └── _status.html.erb ├── bin └── rails ├── config └── routes.rb ├── gemfiles ├── Gemfile-5-2 ├── Gemfile-6-0 └── Gemfile-7-0 ├── images ├── images_clients.png ├── images_dashboard.png └── images_keys.png ├── lib ├── redis_web_manager.rb └── redis_web_manager │ ├── action.rb │ ├── base.rb │ ├── connection.rb │ ├── data.rb │ ├── engine.rb │ ├── info.rb │ └── version.rb ├── redis_web_manager.gemspec └── spec ├── controllers └── redis_web_manager │ ├── actions_controller_spec.rb │ ├── application_controller_spec.rb │ ├── clients_controller_spec.rb │ ├── configuration_controller_spec.rb │ ├── dashboard_controller_spec.rb │ ├── information_controller_spec.rb │ └── keys_controller_spec.rb ├── dummy ├── .ruby-version ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── application_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ └── views │ │ └── layouts │ │ └── application.html.erb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ └── update ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── application_controller_renderer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── content_security_policy.rb │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ └── spring.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ └── favicon.ico ├── helpers ├── application_helper_spec.rb ├── clients_helper_spec.rb ├── dashboard_helper_spec.rb └── keys_helper_spec.rb ├── rails_helper.rb ├── redis_web_manager_action_spec.rb ├── redis_web_manager_connection_spec.rb ├── redis_web_manager_data_spec.rb ├── redis_web_manager_info_spec.rb ├── redis_web_manager_spec.rb └── spec_helper.rb /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/.DS_Store -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-pro 2 | repo_token: A7Nr6g7XhQgiadtP7z5SZjlMYWVTFt0rx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | # IDE 14 | /.idea 15 | .idea/workspace.xml 16 | .rakeTasks 17 | .generators 18 | 19 | # Gem 20 | *.gem 21 | 22 | # Logs 23 | spec/dummy/log/*.log 24 | spec/dummy/tmp/ 25 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | --backtrace -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Documentation: 2 | Enabled: false 3 | Metrics/LineLength: 4 | IgnoredPatterns: ['(\A|\s)#'] 5 | Max: 100 6 | Layout/EmptyLineAfterGuardClause: 7 | Enabled: false 8 | Layout/EmptyLinesAroundBlockBody: 9 | Enabled: false 10 | Style/DoubleNegation: 11 | Enabled: false 12 | Metrics/MethodLength: 13 | Max: 20 14 | Metrics/BlockLength: 15 | Max: 30 16 | Metrics/AbcSize: 17 | Max: 20 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | os: linux 3 | language: ruby 4 | cache: bundler 5 | services: 6 | - redis 7 | rvm: 8 | - 2.5.5 9 | - 2.6.3 10 | - 2.7.0 11 | - 3.0.1 12 | gemfile: 13 | - gemfiles/Gemfile-5-2 14 | - gemfiles/Gemfile-6-0 15 | - gemfiles/Gemfile-7-0 16 | before_install: gem install bundler -v 2.1.0 17 | before_script: bundle install 18 | script: RAILS_ENV=test bundle exec rspec 19 | jobs: 20 | exclude: 21 | - rvm: 2.5.5 22 | gemfile: gemfiles/Gemfile-7-0 23 | - rvm: 2.6.3 24 | gemfile: gemfiles/Gemfile-7-0 25 | - rvm: 3.0.1 26 | gemfile: gemfiles/Gemfile-5-2 27 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | # Declare your gem's dependencies in redis_web_manager.gemspec. 7 | # Bundler will treat runtime dependencies like base dependencies, and 8 | # development dependencies will be added by default to the :development group. 9 | gemspec 10 | 11 | # Declare any dependencies that are still in development here instead of in 12 | # your gemspec. These might include edge Rails or gems from your path or 13 | # Git. Remember to move these dependencies to your gemspec before releasing 14 | # your gem to rubygems.org. 15 | 16 | # To use a debugger 17 | # gem 'byebug', group: [:development, :test] 18 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | redis_web_manager (0.5.0) 5 | pagy (>= 5.0, < 6) 6 | rails (>= 5.2, < 8) 7 | redis (>= 4.1.0, < 5) 8 | sprockets-rails (~> 3.4.2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | actioncable (6.1.4.4) 14 | actionpack (= 6.1.4.4) 15 | activesupport (= 6.1.4.4) 16 | nio4r (~> 2.0) 17 | websocket-driver (>= 0.6.1) 18 | actionmailbox (6.1.4.4) 19 | actionpack (= 6.1.4.4) 20 | activejob (= 6.1.4.4) 21 | activerecord (= 6.1.4.4) 22 | activestorage (= 6.1.4.4) 23 | activesupport (= 6.1.4.4) 24 | mail (>= 2.7.1) 25 | actionmailer (6.1.4.4) 26 | actionpack (= 6.1.4.4) 27 | actionview (= 6.1.4.4) 28 | activejob (= 6.1.4.4) 29 | activesupport (= 6.1.4.4) 30 | mail (~> 2.5, >= 2.5.4) 31 | rails-dom-testing (~> 2.0) 32 | actionpack (6.1.4.4) 33 | actionview (= 6.1.4.4) 34 | activesupport (= 6.1.4.4) 35 | rack (~> 2.0, >= 2.0.9) 36 | rack-test (>= 0.6.3) 37 | rails-dom-testing (~> 2.0) 38 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 39 | actiontext (6.1.4.4) 40 | actionpack (= 6.1.4.4) 41 | activerecord (= 6.1.4.4) 42 | activestorage (= 6.1.4.4) 43 | activesupport (= 6.1.4.4) 44 | nokogiri (>= 1.8.5) 45 | actionview (6.1.4.4) 46 | activesupport (= 6.1.4.4) 47 | builder (~> 3.1) 48 | erubi (~> 1.4) 49 | rails-dom-testing (~> 2.0) 50 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 51 | activejob (6.1.4.4) 52 | activesupport (= 6.1.4.4) 53 | globalid (>= 0.3.6) 54 | activemodel (6.1.4.4) 55 | activesupport (= 6.1.4.4) 56 | activerecord (6.1.4.4) 57 | activemodel (= 6.1.4.4) 58 | activesupport (= 6.1.4.4) 59 | activestorage (6.1.4.4) 60 | actionpack (= 6.1.4.4) 61 | activejob (= 6.1.4.4) 62 | activerecord (= 6.1.4.4) 63 | activesupport (= 6.1.4.4) 64 | marcel (~> 1.0.0) 65 | mini_mime (>= 1.1.0) 66 | activesupport (6.1.4.4) 67 | concurrent-ruby (~> 1.0, >= 1.0.2) 68 | i18n (>= 1.6, < 2) 69 | minitest (>= 5.1) 70 | tzinfo (~> 2.0) 71 | zeitwerk (~> 2.3) 72 | builder (3.2.4) 73 | concurrent-ruby (1.1.9) 74 | coveralls (0.8.23) 75 | json (>= 1.8, < 3) 76 | simplecov (~> 0.16.1) 77 | term-ansicolor (~> 1.3) 78 | thor (>= 0.19.4, < 2.0) 79 | tins (~> 1.6) 80 | crass (1.0.6) 81 | diff-lcs (1.5.0) 82 | docile (1.4.0) 83 | erubi (1.10.0) 84 | globalid (1.0.0) 85 | activesupport (>= 5.0) 86 | i18n (1.8.11) 87 | concurrent-ruby (~> 1.0) 88 | json (2.6.1) 89 | loofah (2.13.0) 90 | crass (~> 1.0.2) 91 | nokogiri (>= 1.5.9) 92 | mail (2.7.1) 93 | mini_mime (>= 0.1.1) 94 | marcel (1.0.2) 95 | method_source (1.0.0) 96 | mini_mime (1.1.2) 97 | minitest (5.15.0) 98 | nio4r (2.5.8) 99 | nokogiri (1.13.0-x86_64-darwin) 100 | racc (~> 1.4) 101 | pagy (5.7.1) 102 | racc (1.6.0) 103 | rack (2.2.3) 104 | rack-test (1.1.0) 105 | rack (>= 1.0, < 3) 106 | rails (6.1.4.4) 107 | actioncable (= 6.1.4.4) 108 | actionmailbox (= 6.1.4.4) 109 | actionmailer (= 6.1.4.4) 110 | actionpack (= 6.1.4.4) 111 | actiontext (= 6.1.4.4) 112 | actionview (= 6.1.4.4) 113 | activejob (= 6.1.4.4) 114 | activemodel (= 6.1.4.4) 115 | activerecord (= 6.1.4.4) 116 | activestorage (= 6.1.4.4) 117 | activesupport (= 6.1.4.4) 118 | bundler (>= 1.15.0) 119 | railties (= 6.1.4.4) 120 | sprockets-rails (>= 2.0.0) 121 | rails-dom-testing (2.0.3) 122 | activesupport (>= 4.2.0) 123 | nokogiri (>= 1.6) 124 | rails-html-sanitizer (1.4.2) 125 | loofah (~> 2.3) 126 | railties (6.1.4.4) 127 | actionpack (= 6.1.4.4) 128 | activesupport (= 6.1.4.4) 129 | method_source 130 | rake (>= 0.13) 131 | thor (~> 1.0) 132 | rake (13.0.6) 133 | redis (4.5.1) 134 | rspec-core (3.10.1) 135 | rspec-support (~> 3.10.0) 136 | rspec-expectations (3.10.1) 137 | diff-lcs (>= 1.2.0, < 2.0) 138 | rspec-support (~> 3.10.0) 139 | rspec-mocks (3.10.2) 140 | diff-lcs (>= 1.2.0, < 2.0) 141 | rspec-support (~> 3.10.0) 142 | rspec-rails (5.0.2) 143 | actionpack (>= 5.2) 144 | activesupport (>= 5.2) 145 | railties (>= 5.2) 146 | rspec-core (~> 3.10) 147 | rspec-expectations (~> 3.10) 148 | rspec-mocks (~> 3.10) 149 | rspec-support (~> 3.10) 150 | rspec-support (3.10.3) 151 | simplecov (0.16.1) 152 | docile (~> 1.1) 153 | json (>= 1.8, < 3) 154 | simplecov-html (~> 0.10.0) 155 | simplecov-html (0.10.2) 156 | sprockets (4.0.2) 157 | concurrent-ruby (~> 1.0) 158 | rack (> 1, < 3) 159 | sprockets-rails (3.4.2) 160 | actionpack (>= 5.2) 161 | activesupport (>= 5.2) 162 | sprockets (>= 3.0.0) 163 | sync (0.5.0) 164 | term-ansicolor (1.7.1) 165 | tins (~> 1.0) 166 | thor (1.2.1) 167 | tins (1.31.0) 168 | sync 169 | tzinfo (2.0.4) 170 | concurrent-ruby (~> 1.0) 171 | websocket-driver (0.7.5) 172 | websocket-extensions (>= 0.1.0) 173 | websocket-extensions (0.1.5) 174 | zeitwerk (2.5.3) 175 | 176 | PLATFORMS 177 | x86_64-darwin-20 178 | x86_64-darwin-21 179 | 180 | DEPENDENCIES 181 | coveralls (~> 0.8) 182 | redis_web_manager! 183 | rspec-rails (~> 5.0.0) 184 | simplecov (~> 0.16) 185 | 186 | BUNDLED WITH 187 | 2.2.15 188 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 OpenGems 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ Gem status 2 | Word of caution if you intend to use this project in production. This gem has not been properly maintained in the past years. Maintainers are trying their best to revive it but it's not their main priority. Thank you for your comprehension. 3 | 4 | # RedisWebManager 5 | 6 | [![Gem Version](https://badge.fury.io/rb/redis_web_manager.svg)](https://badge.fury.io/rb/redis_web_manager) 7 | [![Maintainability](https://api.codeclimate.com/v1/badges/55600fe789679fe62d8b/maintainability)](https://codeclimate.com/github/OpenGems/redis_web_manager/maintainability) 8 | [![Build Status](https://travis-ci.org/OpenGems/redis_web_manager.svg?branch=master)](https://travis-ci.org/OpenGems/redis_web_manager) 9 | [![security](https://hakiri.io/github/OpenGems/redis_web_manager/master.svg)](https://hakiri.io/github/OpenGems/redis_web_manager/master) 10 | ![Gem](https://img.shields.io/gem/dt/redis_web_manager) 11 | [![Coverage Status](https://coveralls.io/repos/github/OpenGems/redis_web_manager/badge.svg?branch=master)](https://coveralls.io/github/OpenGems/redis_web_manager?branch=master) 12 | 13 | Web interface that allows you to manage easily your Redis instance (see keys, memory used, connected client, etc...). 14 | 15 | ### Check your stats 16 | The Dashboard allows you to check the Memory usage, CPU and Redis clients. 17 | 18 | ![RedisWebManager Dashboard](images/images_dashboard.png) 19 | 20 | ### Manage your redis keys 21 | You can easily edit and delete any keys stored in your redis database. 22 | 23 | ![RedisWebManager Keys](images/images_keys.png) 24 | 25 | ### Keep an eye on your redis clients 26 | Check how many clients are connected and their infos. 27 | 28 | ![RedisWebManager Clients](images/images_clients.png) 29 | 30 | ## Installation 31 | Add this line to your application's Gemfile: 32 | 33 | ```ruby 34 | gem 'redis_web_manager' 35 | ``` 36 | 37 | And then execute: 38 | ```bash 39 | $ bundle 40 | ``` 41 | 42 | Or install it yourself as: 43 | ```bash 44 | $ gem install redis_web_manager 45 | ``` 46 | 47 | Add the custom route in your `routes.rb`: 48 | ``` 49 | mount RedisWebManager::Engine => '/redis_web_manager' 50 | ``` 51 | 52 | Access to RedisWebManager at `/redis_web_manager` 53 | 54 | ## Configuration 55 | 56 | You can configure RedisWebManager: 57 | 58 | ```ruby 59 | # initializers/redis_web_manager.rb 60 | 61 | RedisWebManager.configure do |config| 62 | config.redises = { 63 | instance_1: Redis.new(db: 1), 64 | instance_2: Redis.new(url: 'XXX') 65 | } # Default { default: Redis.new } (Hash with instance(s) of Redis) 66 | config.lifespan = 2.days # Default 15.days (Lifespan of each keys for dashboard) 67 | config.authenticate = proc { 68 | authenticate_or_request_with_http_basic do |username, password| 69 | username == 'TEST' && password == 'TEST' 70 | end 71 | } # Default nil (Authenticate method to secure tools) 72 | end 73 | ``` 74 | 75 | ## Collect data for dashboard 76 | 77 | In order to have data on your dashboard you must collect the data like this: 78 | ```ruby 79 | data = RedisWebManager::Data.new(:instance_1) 80 | data.perform 81 | ``` 82 | 83 | or 84 | 85 | ```ruby 86 | RedisWebManager.redises.keys.each do |instance| 87 | data = RedisWebManager::Data.new(instance) 88 | data.perform 89 | end 90 | ``` 91 | 92 | If you are using a system to run background tasks in your application (like Sidekiq, Sucker Punch or ActiveJob), you can write your own background task to update the dashboard statistics. 93 | 94 | Sidekiq exemple: 95 | ```ruby 96 | class DashboardWorker 97 | include Sidekiq::Worker 98 | 99 | def perform 100 | data = RedisWebManager::Data.new(:instance_1) 101 | data.perform 102 | end 103 | end 104 | ``` 105 | 106 | or 107 | 108 | ```ruby 109 | class DashboardWorker 110 | include Sidekiq::Worker 111 | 112 | def perform 113 | RedisWebManager.redises.keys.each do |instance| 114 | data = RedisWebManager::Data.new(instance) 115 | data.perform 116 | end 117 | end 118 | end 119 | ``` 120 | 121 | Sucker Punch exemple: 122 | ```ruby 123 | class DashboardJob 124 | include SuckerPunch::Job 125 | 126 | def perform 127 | data = RedisWebManager::Data.new(:instance_1) 128 | data.perform 129 | end 130 | end 131 | ``` 132 | 133 | or 134 | 135 | ```ruby 136 | class DashboardJob 137 | include SuckerPunch::Job 138 | 139 | def perform 140 | RedisWebManager.redises.keys.each do |instance| 141 | data = RedisWebManager::Data.new(instance) 142 | data.perform 143 | end 144 | end 145 | end 146 | ``` 147 | 148 | 149 | 150 | 151 | ## Todo 152 | * [ ] Add graph for most used commands 153 | * [ ] Real time chart update 154 | * [ ] Alert system (ex: triggered when memory is peaking) 155 | * [ ] Command line interface to manage your redis database 156 | * [ ] Logs interface 157 | 158 | 159 | ## Contributing 160 | Bug reports and pull requests are welcome on GitHub at https://github.com/OpenGems/redis_web_manager. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 161 | 162 | ## License 163 | 164 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 165 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require 'bundler/setup' 5 | rescue LoadError 6 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 7 | end 8 | 9 | require 'rdoc/task' 10 | 11 | RDoc::Task.new(:rdoc) do |rdoc| 12 | rdoc.rdoc_dir = 'rdoc' 13 | rdoc.title = 'RedisWebManager' 14 | rdoc.options << '--line-numbers' 15 | rdoc.rdoc_files.include('README.md') 16 | rdoc.rdoc_files.include('lib/**/*.rb') 17 | end 18 | 19 | APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__) 20 | load 'rails/tasks/engine.rake' 21 | load 'rails/tasks/statistics.rake' 22 | 23 | require 'bundler/gem_tasks' 24 | require 'rspec/core/rake_task' 25 | 26 | RSpec::Core::RakeTask.new(:spec) 27 | 28 | task default: :spec 29 | -------------------------------------------------------------------------------- /app/assets/config/redis_web_manager_manifest.js: -------------------------------------------------------------------------------- 1 | //= link_directory ../javascripts/redis_web_manager .js 2 | //= link_directory ../stylesheets/redis_web_manager .css 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/redis_web_manager/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require ./jquery.js 15 | //= require ./popper.js 16 | //= require ./bootstrap.js 17 | //= require ./keys.js 18 | //= require ./chartjs.js 19 | //= require ./dashboard.js -------------------------------------------------------------------------------- /app/assets/javascripts/redis_web_manager/bootstrap.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t 0) { 9 | if (value === 'memory') { 10 | renderMemory(node, data); 11 | } else if (value === 'cpu') { 12 | renderCpu(node, data); 13 | } else if (value === 'client') { 14 | renderClient(node, data); 15 | } 16 | } 17 | } 18 | } 19 | }); 20 | 21 | function renderMemory(node, data) { 22 | const labels = data.map(d => humanizeDate(d['date'])); 23 | const memories = data.map(d => d['memory']); 24 | const used_memory = memories.map(x => parseInt(x['used_memory'], 10)); 25 | const used_memory_rss = memories.map(x => parseInt(x['used_memory_rss'], 10)); 26 | const used_memory_peak = memories.map(x => parseInt(x['used_memory_peak'], 10)); 27 | const used_memory_overhead = memories.map(x => parseInt(x['used_memory_overhead'], 10)); 28 | const used_memory_startup = memories.map(x => parseInt(x['used_memory_startup'], 10)); 29 | const used_memory_dataset = memories.map(x => parseInt(x['used_memory_dataset'], 10)); 30 | return new Chart(node.getContext('2d'), { 31 | type: 'line', 32 | responsive: true, 33 | data: { 34 | labels: labels, 35 | datasets: [ 36 | { 37 | label: 'Used memory', 38 | data: used_memory, 39 | borderWidth: 2, 40 | borderColor: '#3e95cd', 41 | fill: false 42 | }, 43 | { 44 | label: 'Used memory rss', 45 | data: used_memory_rss, 46 | borderWidth: 2, 47 | borderColor: '#8e5ea2', 48 | fill: false 49 | }, 50 | { 51 | label: 'Used memory peak', 52 | data: used_memory_peak, 53 | borderWidth: 2, 54 | borderColor: '#3cba9f', 55 | fill: false 56 | }, 57 | { 58 | label: 'Used memory overhead', 59 | data: used_memory_overhead, 60 | borderWidth: 2, 61 | borderColor: '#4bc0c0', 62 | fill: false 63 | }, 64 | { 65 | label: 'Used memory overhead', 66 | data: used_memory_startup, 67 | borderWidth: 2, 68 | borderColor: '#e8c3b9', 69 | fill: false 70 | }, 71 | { 72 | label: 'Used memory overhead', 73 | data: used_memory_dataset, 74 | borderWidth: 2, 75 | borderColor: '#8e5ea2', 76 | fill: false 77 | } 78 | ] 79 | }, 80 | options: { 81 | legend: { 82 | position: 'bottom' 83 | }, 84 | scales: { 85 | yAxes: [ 86 | { 87 | ticks: { 88 | callback: function (value) { 89 | return humanSize(value); 90 | } 91 | } 92 | } 93 | ] 94 | }, 95 | tooltips: { 96 | callbacks: { 97 | label: function (props) { 98 | return humanSize(props['value']); 99 | } 100 | } 101 | } 102 | } 103 | }); 104 | } 105 | 106 | function renderCpu(node, data) { 107 | const labels = data.map(d => humanizeDate(d['date'])); 108 | const cpus = data.map(d => d['cpu']); 109 | const used_cpu_sys = cpus.map(x => parseInt(x['used_cpu_sys'], 10)); 110 | const used_cpu_user = cpus.map(x => parseInt(x['used_cpu_user'])); 111 | const used_cpu_sys_children = cpus.map(x => parseInt(x['used_cpu_sys_children'], 10)); 112 | const used_cpu_user_children = cpus.map(x => parseInt(x['used_cpu_user_children'], 10)); 113 | return new Chart(node.getContext('2d'), { 114 | type: 'line', 115 | responsive: true, 116 | data: { 117 | labels: labels, 118 | datasets: [ 119 | { 120 | label: 'Used cpu sys', 121 | data: used_cpu_sys, 122 | borderWidth: 2, 123 | borderColor: '#3e95cd', 124 | fill: false 125 | }, 126 | { 127 | label: 'Used cpu user', 128 | data: used_cpu_user, 129 | borderWidth: 2, 130 | borderColor: '#8e5ea2', 131 | fill: false 132 | }, 133 | { 134 | label: 'Used cpu sys children', 135 | data: used_cpu_sys_children, 136 | borderWidth: 2, 137 | borderColor: '#3cba9f', 138 | fill: false 139 | }, 140 | { 141 | label: 'Used cpu user children', 142 | data: used_cpu_user_children, 143 | borderWidth: 2, 144 | borderColor: '#c45850', 145 | fill: false 146 | } 147 | ] 148 | }, 149 | options: { 150 | legend: { 151 | position: 'bottom' 152 | }, 153 | scales: { 154 | yAxes: [ 155 | { 156 | ticks: { 157 | callback: function (value) { 158 | return humanSecond(parseInt(value, 10)); 159 | } 160 | } 161 | } 162 | ] 163 | }, 164 | tooltips: { 165 | callbacks: { 166 | label: function (props) { 167 | return humanSecond(parseInt(props['value'], 10)); 168 | } 169 | } 170 | } 171 | } 172 | }); 173 | } 174 | 175 | function renderClient(node, data) { 176 | const labels = data.map(d => humanizeDate(d['date'])); 177 | const clients = data.map(d => d['client']); 178 | const connected_clients = clients.map(x => parseInt(x['connected_clients'], 10)); 179 | const blocked_clients = clients.map(x => parseInt(x['blocked_clients'], 10)); 180 | return new Chart(node.getContext('2d'), { 181 | type: 'line', 182 | responsive: true, 183 | data: { 184 | labels: labels, 185 | datasets: [ 186 | { 187 | label: 'Connected clients', 188 | data: connected_clients, 189 | borderWidth: 2, 190 | borderColor: '#3e95cd', 191 | fill: false 192 | }, 193 | { 194 | label: 'Blocked clients', 195 | data: blocked_clients, 196 | borderWidth: 2, 197 | borderColor: '#8e5ea2', 198 | fill: false 199 | } 200 | ] 201 | }, 202 | options: { 203 | legend: { 204 | position: 'bottom' 205 | } 206 | } 207 | }); 208 | } 209 | 210 | function humanizeDate(value) { 211 | const date = new Date(value); 212 | const year = date.getFullYear(); 213 | let month = date.getMonth()+1; 214 | let day = date.getDate(); 215 | let hour = date.getHours(); 216 | let minute = date.getMinutes(); 217 | if (day < 10) { 218 | day = `0${day}`; 219 | } 220 | if (month < 10) { 221 | month = `0${month}`; 222 | } 223 | if (hour < 10) { 224 | hour = `0${hour}`; 225 | } 226 | if (minute < 10) { 227 | minute = `0${minute}`; 228 | } 229 | return `${day}/${month}/${year} ${hour}:${minute}`; 230 | } 231 | 232 | function humanSize(size) { 233 | if (size < 1024) { 234 | return `${size} B`; 235 | } 236 | const i = Math.floor(Math.log(size) / Math.log(1024)); 237 | const num = (size / Math.pow(1024, i)); 238 | const round = Math.round(num); 239 | const value = round < 10 ? num.toFixed(2) : round < 100 ? num.toFixed(1) : round; 240 | return `${value} ${'KMGTPEZY'[i - 1]}B` 241 | } 242 | 243 | function humanSecond(value) { 244 | return `${value} sec`; 245 | } 246 | }); -------------------------------------------------------------------------------- /app/assets/javascripts/redis_web_manager/keys.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | const url = document.location.toString(); 3 | if (url.match('#')) { 4 | $('.nav-tabs a[href="#' + url.split('#')[1] + '"]').tab('show'); 5 | } 6 | $('.nav-tabs a').on('click', function () { 7 | window.location.hash = this.target.hash; 8 | $(this).tab('show'); 9 | }); 10 | 11 | $('#sort').on('click', function () { 12 | console.log('e') 13 | }) 14 | }); -------------------------------------------------------------------------------- /app/assets/javascripts/redis_web_manager/popper.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2019 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/redis_web_manager/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require ./bootstrap.css 14 | *= require ./dashboard.css 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/redis_web_manager/dashboard.css: -------------------------------------------------------------------------------- 1 | .bounce { 2 | animation: bounce .8s ease-out infinite; 3 | -webkit-animation: bounce .8s ease-out infinite; 4 | } 5 | 6 | @keyframes bounce { 7 | from { 8 | box-shadow: 0 0 15px #6c757d; 9 | } 10 | 50% { 11 | box-shadow: 0 0 30px #6c757d; 12 | } 13 | to { 14 | box-shadow: 0 0 15px #6c757d; 15 | } 16 | } 17 | 18 | @-webkit-keyframes bounce { 19 | from { 20 | -webkit-box-shadow: 0 0 15px #6c757d; 21 | } 22 | 50% { 23 | -webkit-box-shadow: 0 0 30px #6c757d; 24 | } 25 | to { 26 | -webkit-box-shadow: 0 0 15px #6c757d; 27 | } 28 | } -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/actions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class ActionsController < ApplicationController 5 | # DELETE /reset 6 | def reset 7 | data.flush 8 | redirect_to root_url 9 | end 10 | 11 | # DELETE /flushdb 12 | def flushdb 13 | action.flushdb 14 | redirect_to root_url 15 | end 16 | 17 | # DELETE /flushall 18 | def flushall 19 | action.flushall 20 | redirect_to root_url 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pagy' 4 | require 'pagy/extras/array' 5 | require 'pagy/extras/bootstrap' 6 | 7 | module RedisWebManager 8 | class ApplicationController < ActionController::Base 9 | include ::Pagy::Backend 10 | 11 | protect_from_forgery with: :exception 12 | 13 | before_action :authenticated?, if: :authenticate 14 | before_action :valid_instance? 15 | helper_method :redises 16 | helper_method :instance 17 | 18 | private 19 | 20 | def authenticated? 21 | instance_exec(&authenticate) 22 | end 23 | 24 | def authenticate 25 | RedisWebManager.authenticate 26 | end 27 | 28 | def info 29 | @info ||= RedisWebManager::Info.new(instance) 30 | end 31 | 32 | def connection 33 | @connection ||= RedisWebManager::Connection.new(instance) 34 | end 35 | 36 | def action 37 | @action ||= RedisWebManager::Action.new(instance) 38 | end 39 | 40 | def data 41 | @data ||= RedisWebManager::Data.new(instance) 42 | end 43 | 44 | def default_url_options(options = {}) 45 | options.merge(instance: instance) 46 | end 47 | 48 | def valid_instance? 49 | default_instance = redises.keys[0] 50 | redirect_to dashboard_url(instance: default_instance) if instance.nil? 51 | end 52 | 53 | def instance 54 | @instance ||= params[:instance].presence 55 | end 56 | 57 | def redises 58 | @redises ||= RedisWebManager.redises 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/clients_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class ClientsController < ApplicationController 5 | # GET /clients 6 | def index 7 | @status = info.status 8 | @url = connection.id 9 | @clients = info.clients.map(&:symbolize_keys) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/configuration_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class ConfigurationController < ApplicationController 5 | # GET /configuration 6 | def index 7 | @configurations = info.configuration 8 | @status = info.status 9 | @url = connection.id 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class DashboardController < ApplicationController 5 | # GET /dashboard 6 | def index 7 | @information = stats.map { |k, v| { name: k.to_s.humanize, value: v } } 8 | @status = info.status 9 | @url = connection.id 10 | @memory = keys_by_type(data.keys, :memory) 11 | @cpu = keys_by_type(data.keys, :cpu) 12 | @client = keys_by_type(data.keys, :client) 13 | end 14 | 15 | private 16 | 17 | def stats 18 | @stats ||= info.stats.symbolize_keys.slice(:redis_version, 19 | :redis_mode, 20 | :arch_bits, 21 | :process_id, 22 | :os, 23 | :role, 24 | :connected_clients, 25 | :blocked_clients, 26 | :uptime_in_days, 27 | :used_memory_human) 28 | end 29 | 30 | def keys_by_type(keys, value) 31 | keys.map { |key| key.slice(:date, value) }.sort_by { |key| key[:date] } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/information_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class InformationController < ApplicationController 5 | # GET /information 6 | def index 7 | @information = stats.map { |k, v| { name: k.to_s.humanize, value: v } } 8 | @status = info.status 9 | @url = connection.id 10 | end 11 | 12 | private 13 | 14 | def stats 15 | @stats ||= info.stats.symbolize_keys 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/redis_web_manager/keys_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class KeysController < ApplicationController 5 | 6 | # GET /keys 7 | def index 8 | @status = info.status 9 | @url = connection.id 10 | @pagy, @keys = pagy_array(keys) 11 | end 12 | 13 | # GET /key/:key 14 | def show 15 | key = params[:key].presence 16 | redirect_to keys_url if key.nil? 17 | @key = format_key(key) 18 | end 19 | 20 | # GET /key/:key 21 | def edit 22 | key = params[:key].presence 23 | redirect_to keys_url if key.nil? 24 | @key = format_key(key) 25 | end 26 | 27 | # PUT /key/:key 28 | def update 29 | old_key = params[:old_name].presence 30 | new_name = params[:new_name].presence 31 | redirect_to keys_url if old_key.nil? || new_name.nil? 32 | action.rename(old_key, new_name) 33 | redirect_to keys_url 34 | end 35 | 36 | # DELETE /key/:key 37 | def destroy 38 | key = params[:key].presence 39 | redirect_to keys_url if key.nil? 40 | action.del(key) 41 | redirect_to keys_url 42 | end 43 | 44 | private 45 | 46 | # FIXME: - Refactoring 47 | # - Move this part 48 | def item_type(value) 49 | ['json', JSON.parse(value)] 50 | rescue JSON::ParserError 51 | ['string', value] 52 | end 53 | 54 | def get_list(key) 55 | start = 0 56 | stop = 99 57 | 58 | length = info.llen(key) 59 | values = info.lrange(key, start, stop).map.with_index do |e, i| 60 | type, value = item_type(e) 61 | { type: type, value: value, index: start + i } 62 | end 63 | 64 | { length: length, values: values } 65 | end 66 | 67 | def get_set(key) 68 | values = info.smembers(key).map do |e| 69 | type, value = item_type(e) 70 | { type: type, value: value } 71 | end 72 | 73 | { values: values } 74 | end 75 | 76 | def get_zset(key) 77 | values = info.zrange(key, 0, -1, withscores: true).map do |e, score| 78 | type, value = item_type(e) 79 | { type: type, value: value, score: score } 80 | end 81 | 82 | { values: values } 83 | end 84 | 85 | def get_hash(key) 86 | value = Hash[info.hgetall(key).map do |k, v| 87 | type, value = item_type(v) 88 | [k, { type: type, value: value }] 89 | end] 90 | 91 | { value: value } 92 | end 93 | 94 | def get_value(key) 95 | case info.type(key) 96 | when 'string' 97 | { value: info.get(key) } 98 | when 'list' 99 | get_list(key) 100 | when 'set' 101 | get_set(key) 102 | when 'zset' 103 | get_zset(key) 104 | when 'hash' 105 | get_hash(key) 106 | else 107 | { value: 'Not found' } 108 | end 109 | end 110 | 111 | def format_key(key) 112 | { 113 | key: key, 114 | expiry: info.expiry(key), 115 | node: get_value(key), 116 | type: info.type(key), 117 | memory: info.memory_usage(key) 118 | } 119 | end 120 | 121 | def keys 122 | keys = info.search(params[:query].presence).map { |key| format_key(key) } 123 | keys = filter_by_type(keys, params[:type].presence) 124 | keys = filter_by_expiry(keys, params[:expiry].presence) 125 | filter_by_memory(keys, params[:expiry].presence) 126 | end 127 | 128 | def filter_by_type(keys, type) 129 | return keys if invalid_option(type) 130 | keys.select { |key| key[:type] == type } 131 | end 132 | 133 | def filter_by_expiry(keys, expiry) 134 | return keys if invalid_option(expiry) 135 | duration = expiry.to_i 136 | return keys.select { |key| key[:expiry] == -1 } if duration == -1 137 | keys.select { |key| key[:expiry] != -1 && key[:expiry] < duration } 138 | end 139 | 140 | def filter_by_memory(keys, memory) 141 | return keys if invalid_option(memory) 142 | keys.select { |key| key[:memory] < memory.to_i } 143 | end 144 | 145 | def invalid_option(option) 146 | option.nil? || option == 'all' 147 | end 148 | end 149 | end 150 | -------------------------------------------------------------------------------- /app/helpers/redis_web_manager/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | module ApplicationHelper 5 | require 'pagy' 6 | 7 | include ::Pagy::Frontend 8 | 9 | def status(value) 10 | if value 11 | content_tag(:kbd, 'ON', class: 'bg-success bounce') 12 | else 13 | content_tag(:kbd, 'OFF', class: 'bg-danger bounce') 14 | end 15 | end 16 | 17 | def url(value) 18 | content_tag(:kbd, value, class: 'bg-dark') 19 | end 20 | 21 | def expiry(value) 22 | if value == -1 23 | 'No expiration date' 24 | else 25 | distance_of_time_in_words(value) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/helpers/redis_web_manager/clients_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | module ClientsHelper 5 | def age(value) 6 | time_ago_in_words(Time.now - value.seconds).humanize 7 | end 8 | 9 | def flags(value) 10 | { 11 | A: 'Connection to be closed ASAP', 12 | b: 'The client is waiting in a blocking operation', 13 | c: 'Connection to be closed after writing entire reply', 14 | d: 'A watched keys has been modified - EXEC will fail', 15 | i: 'The client is waiting for a VM I/O (deprecated)', 16 | M: 'The client is a master', 17 | N: 'No specific flag set', 18 | O: 'The client is a client in MONITOR mode', 19 | P: 'The client is a Pub/Sub subscriber', 20 | r: 'The client is in readonly mode against a cluster node', 21 | S: 'The client is a replica node connection to this instance', 22 | u: 'The client is unblocked', 23 | U: 'The client is connected via a Unix domain socket', 24 | X: 'The client is in a MULTI/EXEC context' 25 | }[value.to_sym] 26 | end 27 | 28 | def events(value) 29 | { 30 | r: 'The client socket is readable', 31 | w: 'The client socket is writable' 32 | }[value.to_sym] 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/redis_web_manager/dashboard_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | module DashboardHelper 5 | def graph_canvas(data, id) 6 | if data.nil? || data.empty? 7 | content_tag(:div, 8 | 'You don\'t have any RedisWebManager keys yet into your redis database', 9 | class: 'm-5 text-center') 10 | else 11 | content_tag(:canvas, 12 | nil, 13 | id: id, 14 | width: 800, 15 | height: 400, 16 | data: { canvas: data.to_json }) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/helpers/redis_web_manager/keys_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | module KeysHelper 5 | def types_filters 6 | [%w[All all], 7 | %w[String string], 8 | %w[Hash hash], 9 | %w[Set set], 10 | %w[Zset zset], 11 | %w[List list]] 12 | end 13 | 14 | def expiry_filters 15 | [%w[All all], 16 | ['No expiry', -1], 17 | ['Less than 1 hour', 3600], 18 | ['Less than 1 week', 604_800], 19 | ['Less than 1 month', 2_592_000], 20 | ['Less than 6 months', 15_552_000]] 21 | end 22 | 23 | def memory_filters 24 | [%w[All all], 25 | ['Less than 1 KB', 1000], 26 | ['Less than 10 KB', 10_000], 27 | ['Less than 100 KB', 100_000], 28 | ['Less than 1 MB', 1_000_000], 29 | ['Less than 10 MB', 10_000_000], 30 | ['Less than 100 MB', 100_000_000]] 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/views/layouts/redis_web_manager/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redis Web Manager 6 | 7 | 8 | <%= csrf_meta_tags %> 9 | <%= csp_meta_tag %> 10 | <%= stylesheet_link_tag 'redis_web_manager/application', media: 'all' %> 11 | <%= javascript_include_tag 'redis_web_manager/application' %> 12 | 13 | 14 | <%= render partial: 'redis_web_manager/shared/header' %> 15 | <%= yield %> 16 | 17 | -------------------------------------------------------------------------------- /app/views/redis_web_manager/clients/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'redis_web_manager/shared/status' %> 3 |
4 |
5 |
6 |
7 |
8 | Clients 9 |
10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% @clients.each do |c| %> 27 | 28 | 31 | 34 | 37 | 40 | 43 | 46 | 49 | 50 | <% end %> 51 | 52 |
IDNameAddressAgeFlagEventLast command
29 | <%= c[:id] %> 30 | 32 | <%= c[:name] %> 33 | 35 | <%= c[:addr] %> 36 | 38 | <%= age(c[:age].to_i) %> 39 | 41 | <%= flags(c[:flags]) %> 42 | 44 | <%= events(c[:events]) %> 45 | 47 | <%= c[:cmd] %> 48 |
53 |
54 |
55 |
56 |
57 |
58 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/configuration/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'redis_web_manager/shared/status' %> 3 |
4 |
5 |
6 |
7 |
8 | Configuration 9 |
10 |
11 |
12 |
    13 | <% @configurations.each do |k, v| %> 14 |
  • 15 |
    <%= k %>
    16 |
    <%= v %>
    17 |
  • 18 | <% end %> 19 |
20 |
21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/dashboard/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'redis_web_manager/shared/status' %> 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Memory usage 11 |
12 |
13 |
14 | <%= graph_canvas(@memory, :memory) %> 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | CPU 25 |
26 |
27 |
28 | <%= graph_canvas(@cpu, :cpu) %> 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Clients 39 |
40 |
41 |
42 | <%= graph_canvas(@client, :client) %> 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Information 53 |
54 |
55 |
56 |
    57 | <% @information.each do |data| %> 58 |
  • 59 |
    <%= data[:name] %>
    60 |
    <%= data[:value] %>
    61 |
  • 62 | <% end %> 63 |
64 |
65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/information/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'redis_web_manager/shared/status' %> 3 |
4 |
5 |
6 |
7 |
8 | Information 9 |
10 |
11 |
12 |
    13 | <% @information.each do |data| %> 14 |
  • 15 |
    <%= data[:name] %>
    16 |
    <%= data[:value] %>
    17 |
  • 18 | <% end %> 19 |
20 |
21 |
22 |
23 |
24 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/keys/_search.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_tag(keys_path, method: :get, class: 'p-4', enforce_utf8: false) do %> 2 |
3 |
4 |
5 | <%= label_tag :query, 'Name', class: 'font-weight-bolder' %> 6 | <%= text_field_tag :query, params[:query], class: 'form-control', placeholder: 'Search key(s)' %> 7 |
8 |
9 |
10 |
11 |
12 |
13 | <%= label_tag :type, 'Type', class: 'font-weight-bolder' %> 14 | <%= select_tag :type, options_for_select(types_filters, params[:type]), class: 'form-control custom-select' %> 15 |
16 |
17 |
18 |
19 | <%= label_tag :expiry, 'Expiry', class: 'font-weight-bolder' %> 20 | <%= select_tag :expiry, options_for_select(expiry_filters, params[:expiry]), class: 'form-control custom-select' %> 21 |
22 |
23 |
24 |
25 | <%= label_tag :memory, 'Memory', class: 'font-weight-bolder' %> 26 | <%= select_tag :memory, options_for_select(memory_filters, params[:memory]), class: 'form-control custom-select' %> 27 |
28 |
29 |
30 | <%= submit_tag 'Search', class: 'btn btn-primary btn-block', name: nil %> 31 | <% end %> -------------------------------------------------------------------------------- /app/views/redis_web_manager/keys/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= form_tag update_key_path, method: :put, class: "form-horizontal center" do %> 5 |
6 | <%= label_tag :key, "Name key:", class: "control-label" %> 7 | <%= text_field_tag :key, @key[:key], class: "form-control" %> 8 | <%= hidden_field_tag :old_key, @key[:key] %> 9 |
10 | <%= submit_tag "Submit", class: "btn btn-default btn-primary" %> 11 | <% end %> 12 |
13 |
14 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/keys/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'redis_web_manager/shared/status' %> 3 |
4 |
5 |
6 |
7 |
8 | Keys 9 |
10 |
11 |
12 | 15 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | <% @keys.each do |key| %> 31 | 32 | 33 | 34 | 35 | 36 | 40 | 41 | <% end %> 42 | 43 |
NameExpiration DateSizeTypeOptions
<%= link_to (key[:key]).truncate(45), key_path(key: key[:key]), title: "Show #{key[:key]}" %><%= expiry(key[:expiry]) %><%= number_to_human_size(key[:memory]) %><%= key[:type] %> 37 | <%= link_to 'Edit', edit_key_path(key: key[:key]), title: "Edit #{key[:key]}", class: 'btn btn-sm btn-primary' %> 38 | <%= link_to 'Delete', destroy_key_path(key: key[:key]), method: :delete, data: {confirm: 'Are you sure ?'}, title: "Delete #{key[:key]}", class: 'btn btn-sm btn-danger ml-2' %> 39 |
44 |
45 |
46 |
47 |
48 | <%= pagy_bootstrap_nav(@pagy).html_safe %> 49 |
50 |
51 |
52 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/keys/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

6 | <%= @key[:key] %> 7 |

8 | 9 |

10 | <%= @key[:node][:value] %> 11 |

12 | 13 |

14 | <%= expiry(@key[:expiry]) %> 15 |

16 | 17 |

18 | <%= number_to_human_size(@key[:memory]) %> 19 |

20 | 21 |

22 | <%= @key[:type] %> 23 |

24 |

25 | <%= link_to 'Delete', destroy_key_path(key: @key[:key]), method: :delete, data: {confirm: 'Are you sure ?'}, title: "Delete #{@key}", class: 'btn btn-sm btn-danger w-100 mb-2' %> 26 | <%= link_to 'Back', keys_path, title: 'View keys', class: 'btn btn-sm btn-primary w-100' %> 27 |

28 |
29 |
30 |
-------------------------------------------------------------------------------- /app/views/redis_web_manager/shared/_header.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/redis_web_manager/shared/_status.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= url(@url) %> 4 | <%= status(@status) %> 5 |
6 |
-------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # This command will automatically be run when you run "rails" with Rails gems 5 | # installed from the root of your application. 6 | 7 | ENGINE_ROOT = File.expand_path('..', __dir__) 8 | ENGINE_PATH = File.expand_path('../lib/redis_web_manager/engine', __dir__) 9 | APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__) 10 | 11 | # Set up gems listed in the Gemfile. 12 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 13 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 14 | 15 | require 'rails' 16 | # Pick the frameworks you want: 17 | require 'action_controller/railtie' 18 | require 'action_view/railtie' 19 | require 'sprockets/railtie' 20 | require 'rails/engine/commands' 21 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RedisWebManager::Engine.routes.draw do 4 | redises_keys = RedisWebManager.redises.keys 5 | 6 | scope ':instance', instance: /#{redises_keys.join('|')}/ do 7 | # Configuration 8 | get :configuration, to: 'configuration#index' 9 | 10 | # Information 11 | get :information, to: 'information#index' 12 | 13 | # Dashboard 14 | get :dashboard, to: 'dashboard#index' 15 | 16 | # Keys 17 | get 'keys' => 'keys#index' 18 | get 'key' => 'keys#show' 19 | get 'key/edit' => 'keys#edit', as: :edit_key 20 | put 'keys' => 'keys#update', as: :update_key 21 | delete 'keys' => 'keys#destroy', as: :destroy_key 22 | 23 | # Clients 24 | get :clients, to: 'clients#index' 25 | 26 | # Actions 27 | delete :reset, to: 'actions#reset' 28 | delete :flushdb, to: 'actions#flushdb' 29 | delete :flushall, to: 'actions#flushall' 30 | end 31 | 32 | # Root 33 | root 'dashboard#index' 34 | end 35 | -------------------------------------------------------------------------------- /gemfiles/Gemfile-5-2: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '..' 4 | 5 | gem 'rails', '~> 5.2' -------------------------------------------------------------------------------- /gemfiles/Gemfile-6-0: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '..' 4 | 5 | gem 'rails', '~> 6.0' -------------------------------------------------------------------------------- /gemfiles/Gemfile-7-0: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: '..' 4 | 5 | gem 'rails', '~> 7.0' -------------------------------------------------------------------------------- /images/images_clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/images/images_clients.png -------------------------------------------------------------------------------- /images/images_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/images/images_dashboard.png -------------------------------------------------------------------------------- /images/images_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/images/images_keys.png -------------------------------------------------------------------------------- /lib/redis_web_manager.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'redis_web_manager/engine' 4 | require 'redis_web_manager/base' 5 | require 'redis_web_manager/action' 6 | require 'redis_web_manager/connection' 7 | require 'redis_web_manager/info' 8 | require 'redis_web_manager/data' 9 | require 'active_support/time' 10 | require 'redis' 11 | 12 | module RedisWebManager 13 | mattr_accessor :redises, default: { default: Redis.new } 14 | mattr_accessor :lifespan, default: 15.days 15 | mattr_accessor :authenticate, default: nil 16 | 17 | class << self 18 | def configure 19 | yield self if block_given? 20 | check_attrs 21 | end 22 | 23 | private 24 | 25 | def check_attrs 26 | unless redises.is_a?(::Hash) 27 | raise(ArgumentError, 'Invalid redises hash, use like that { test: Redis.new }') 28 | end 29 | redises.each do |k, v| 30 | unless v.is_a?(Redis) 31 | raise(ArgumentError, "Invalid Redis instance for #{k}, use like that Redis.new") 32 | end 33 | end 34 | unless lifespan.is_a?(::ActiveSupport::Duration) 35 | raise(ArgumentError, 'Invalid lifespan, use like that 15.days, 15.minutes etc') 36 | end 37 | valid = lifespan.to_i.positive? 38 | raise(ArgumentError, 'Invalid lifespan, value must be greater than 0') unless valid 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/redis_web_manager/action.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Action < Base 5 | def flushall 6 | redis.flushall 7 | end 8 | 9 | def flushdb 10 | redis.flushdb 11 | end 12 | 13 | def del(key) 14 | redis.del(key) 15 | end 16 | 17 | def rename(old_name, new_name) 18 | redis.rename(old_name, new_name) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/redis_web_manager/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Base 5 | attr_accessor :instance 6 | 7 | def initialize(instance) 8 | @instance = instance || redises.keys[0] 9 | end 10 | 11 | private 12 | 13 | def redis 14 | @redis ||= redises[instance.to_sym] 15 | end 16 | 17 | def redises 18 | @redises ||= RedisWebManager.redises 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/redis_web_manager/connection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Connection < Base 5 | def host 6 | @host ||= connection[:host] 7 | end 8 | 9 | def port 10 | @port ||= connection[:port] 11 | end 12 | 13 | def db 14 | @db ||= connection[:db] 15 | end 16 | 17 | def id 18 | @id ||= connection[:id] 19 | end 20 | 21 | def location 22 | @location ||= connection[:location] 23 | end 24 | 25 | private 26 | 27 | def connection 28 | @connection ||= redis.connection 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/redis_web_manager/data.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Data < Base 5 | BASE = 'RedisWebManager' 6 | 7 | def keys 8 | data.map { |key| JSON.parse(redis.get(key), symbolize_names: true) } 9 | end 10 | 11 | def perform 12 | now = Time.now.to_i 13 | seconds = (now + lifespan.to_i) - now 14 | redis.setex("#{BASE}_#{instance}_#{now}", seconds, serialize.to_json) 15 | end 16 | 17 | def flush 18 | data.map { |key| redis.del(key) } 19 | end 20 | 21 | private 22 | 23 | def data 24 | @data ||= redis.scan_each(match: "#{BASE}_#{instance}_*").to_a 25 | end 26 | 27 | def lifespan 28 | @lifespan ||= RedisWebManager.lifespan 29 | end 30 | 31 | def serialize 32 | { 33 | date: Time.now, 34 | memory: memory, 35 | client: client, 36 | cpu: cpu 37 | } 38 | end 39 | 40 | def memory 41 | { 42 | used_memory: stats[:used_memory], 43 | used_memory_rss: stats[:used_memory_rss], 44 | used_memory_peak: stats[:used_memory_peak], 45 | used_memory_overhead: stats[:used_memory_overhead], 46 | used_memory_startup: stats[:used_memory_startup], 47 | used_memory_dataset: stats[:used_memory_dataset] 48 | } 49 | end 50 | 51 | def client 52 | { 53 | connected_clients: stats[:connected_clients], 54 | blocked_clients: stats[:blocked_clients] 55 | } 56 | end 57 | 58 | def cpu 59 | { 60 | used_cpu_sys: stats[:used_cpu_sys], 61 | used_cpu_user: stats[:used_cpu_user], 62 | used_cpu_sys_children: stats[:used_cpu_sys_children], 63 | used_cpu_user_children: stats[:used_cpu_user_children] 64 | } 65 | end 66 | 67 | def stats 68 | @stats ||= redis.info.symbolize_keys 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/redis_web_manager/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Engine < ::Rails::Engine 5 | isolate_namespace RedisWebManager 6 | 7 | initializer 'redis_web_manager.assets.precompile' do |app| 8 | # check if Rails api mode 9 | if app.config.respond_to?(:assets) 10 | if defined?(Sprockets) && Sprockets::VERSION >= '4' 11 | app.config.assets.precompile << 'redis_web_manager/application.js' 12 | app.config.assets.precompile << 'redis_web_manager/application.css' 13 | else 14 | # use a proc instead of a string 15 | app.config.assets.precompile << proc { |path| path == 'redis_web_manager/application.js' } 16 | app.config.assets.precompile << proc { |path| path == 'redis_web_manager/application.css' } 17 | end 18 | end 19 | end 20 | 21 | config.generators do |generate| 22 | # Don't generate assets 23 | generate.assets false 24 | 25 | # Don't generate helper 26 | generate.helper false 27 | 28 | # Rspec 29 | generate.test_framework :rspec 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/redis_web_manager/info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | class Info < Base 5 | def status 6 | @status ||= redis.ping == 'PONG' 7 | end 8 | 9 | def stats 10 | @stats ||= redis.info 11 | end 12 | 13 | def search(query) 14 | query.blank? ? [] : redis.scan_each(match: "*#{query}*").to_a 15 | end 16 | 17 | def type(key) 18 | redis.type(key) 19 | end 20 | 21 | def expiry(key) 22 | redis.ttl(key) 23 | end 24 | 25 | def memory_usage(key) 26 | redis.memory(:usage, key) 27 | end 28 | 29 | def get(key) 30 | redis.get(key) 31 | end 32 | 33 | def llen(key) 34 | redis.llen(key) 35 | end 36 | 37 | def lrange(key, start, stop) 38 | redis.lrange(key, start, stop) 39 | end 40 | 41 | def smembers(key) 42 | redis.smembers(key) 43 | end 44 | 45 | def zrange(key, start, stop, options = {}) 46 | if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') 47 | return redis.zrange(key, start, stop, **options) 48 | end 49 | 50 | redis.zrange(key, start, stop, options) 51 | end 52 | 53 | def hgetall(key) 54 | redis.hgetall(key) 55 | end 56 | 57 | def dbsize 58 | @dbsize ||= redis.dbsize 59 | end 60 | 61 | def configuration 62 | @configuration ||= redis.config(:get, '*') 63 | end 64 | 65 | def clients 66 | @clients ||= redis.client(:list) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/redis_web_manager/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RedisWebManager 4 | VERSION = '0.5.0' 5 | end 6 | -------------------------------------------------------------------------------- /redis_web_manager.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path('lib', __dir__) 4 | 5 | # Maintain your gem's version: 6 | require 'redis_web_manager/version' 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |spec| 10 | spec.name = 'redis_web_manager' 11 | spec.version = RedisWebManager::VERSION 12 | 13 | spec.authors = ['Boris BRESCIANI', 'Benjamin DARCET', 'Olivier DUMAS'] 14 | spec.email = %w[boris2bresciani@gmail.com 15 | b.darcet@gmail.com 16 | dumas.olivier@outlook.fr] 17 | 18 | spec.summary = 'Manage your Redis instance (See keys, memory used, connected client, etc...)' 19 | spec.description = 'Manage your Redis instance (See keys, memory used, connected client, configuration, information)' 20 | spec.homepage = 'https://github.com/OpenGems/redis_web_manager' 21 | spec.license = 'MIT' 22 | 23 | spec.metadata['homepage_uri'] = spec.homepage 24 | spec.metadata['source_code_uri'] = spec.homepage 25 | 26 | # Specify which files should be added to the gem when it is released. 27 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 28 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 29 | `git ls-files -z`.split("\x0") 30 | .reject { |f| f.match(%r{^(test|spec|features|images)/}) } 31 | end 32 | spec.bindir = 'exe' 33 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 34 | spec.require_paths = ['lib'] 35 | spec.test_files = Dir['spec/**/*'] 36 | 37 | spec.add_development_dependency 'coveralls', '~> 0.8' 38 | spec.add_development_dependency 'rspec-rails', '~> 5.0.0' 39 | spec.add_development_dependency 'simplecov', '~> 0.16' 40 | 41 | spec.add_dependency 'pagy', '>= 5.0', '< 6' 42 | spec.add_dependency 'rails', '>= 5.2', '< 9' 43 | spec.add_dependency 'redis', '>= 4.1.0', '< 5' 44 | spec.add_dependency 'sprockets-rails', '~> 3.4.2' 45 | end 46 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/actions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ActionsController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | 8 | let(:default) do 9 | RedisWebManager.redises.keys[0] 10 | end 11 | 12 | describe 'DELETE #reset' do 13 | it 'returns a success response' do 14 | delete :reset, params: { instance: default.to_s } 15 | expect(response).to be_redirect 16 | end 17 | end 18 | 19 | describe 'DELETE #flushdb' do 20 | it 'returns a success response' do 21 | delete :flushdb, params: { instance: default.to_s } 22 | expect(response).to be_redirect 23 | end 24 | end 25 | 26 | describe 'DELETE #flushall' do 27 | it 'returns a success response' do 28 | delete :flushall, params: { instance: default.to_s } 29 | expect(response).to be_redirect 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/application_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ApplicationController, type: :controller do 6 | describe 'Methods' do 7 | it 'returns a raise value (authenticated?)' do 8 | expect do 9 | controller.send(:authenticated?) 10 | end.to raise_error(LocalJumpError) 11 | end 12 | 13 | it 'returns a nil value (authenticate)' do 14 | expect(controller.send(:authenticate)).to eql(nil) 15 | end 16 | 17 | it 'returns a Info class (info)' do 18 | expect(controller.send(:info)).to be_a_kind_of(RedisWebManager::Info) 19 | end 20 | 21 | it 'returns a Connection class (connection)' do 22 | expect(controller.send(:connection)).to be_a_kind_of(RedisWebManager::Connection) 23 | end 24 | 25 | it 'returns a Action class (action)' do 26 | expect(controller.send(:action)).to be_a_kind_of(RedisWebManager::Action) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/clients_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ClientsController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | 8 | let(:default) do 9 | RedisWebManager.redises.keys[0] 10 | end 11 | 12 | describe 'GET #index' do 13 | it 'returns a success response' do 14 | get :index, params: { instance: default.to_s } 15 | expect(response).to be_successful 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/configuration_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ConfigurationController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | 8 | let(:default) do 9 | RedisWebManager.redises.keys[0] 10 | end 11 | 12 | describe 'GET #index' do 13 | it 'returns a success response' do 14 | get :index, params: { instance: default.to_s } 15 | expect(response).to be_successful 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/dashboard_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::DashboardController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | 8 | let(:default) do 9 | RedisWebManager.redises.keys[0] 10 | end 11 | 12 | describe 'GET #index' do 13 | it 'returns a success response' do 14 | get :index, params: { instance: default.to_s } 15 | expect(response).to be_successful 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/information_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::InformationController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | 8 | let(:default) do 9 | RedisWebManager.redises.keys[0] 10 | end 11 | 12 | describe 'GET #index' do 13 | it 'returns a success response' do 14 | get :index, params: { instance: default.to_s } 15 | expect(response).to be_successful 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/redis_web_manager/keys_controller_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::KeysController, type: :controller do 6 | routes { RedisWebManager::Engine.routes } 7 | let(:redis) do 8 | Redis.new 9 | end 10 | 11 | let(:default) do 12 | RedisWebManager.redises.keys[0] 13 | end 14 | 15 | describe 'GET #index' do 16 | it 'returns a success response' do 17 | get :index, params: { instance: default.to_s } 18 | expect(response).to be_successful 19 | get :index, params: { query: 'test', instance: default.to_s } 20 | expect(response).to be_successful 21 | get :index, params: { type: 'string', instance: default.to_s } 22 | expect(response).to be_successful 23 | get :index, params: { expiry: '-1', instance: default.to_s } 24 | expect(response).to be_successful 25 | get :index, params: { expiry: '3600', instance: default.to_s } 26 | expect(response).to be_successful 27 | get :index, params: { memory: '1000', instance: default.to_s } 28 | expect(response).to be_successful 29 | end 30 | end 31 | 32 | describe 'GET #show' do 33 | it 'returns a success response' do 34 | redis.set('test', 'test') 35 | get :show, params: { key: 'test', instance: default.to_s } 36 | expect(response).to be_successful 37 | end 38 | end 39 | 40 | describe 'GET #edit' do 41 | it 'returns a success response' do 42 | redis.set('test', 'test') 43 | get :edit, params: { key: 'test', instance: default.to_s } 44 | expect(response).to be_successful 45 | end 46 | end 47 | 48 | describe 'GET #update' do 49 | it 'returns a success response' do 50 | redis.set('test', 'test') 51 | put :update, params: { old_name: 'test', new_name: 'testtest', instance: default.to_s } 52 | expect(response).to be_redirect 53 | end 54 | end 55 | 56 | describe 'GET #destroy' do 57 | it 'returns a success response' do 58 | redis.set('test', 'test') 59 | delete :destroy, params: { key: 'test', instance: default.to_s } 60 | expect(response).to be_redirect 61 | end 62 | end 63 | 64 | describe 'Methods' do 65 | it 'returns a hash value (get_value - string)' do 66 | redis.set('test', 'test') 67 | eql = { 68 | value: 'test' 69 | } 70 | expect(controller.send(:get_value, 'test')).to eql(eql) 71 | end 72 | 73 | it 'returns a hash value (get_value - hgetall)' do 74 | eql = { 75 | value: { 76 | 'name' => { 77 | type: 'string', 78 | value: 'hgetall' 79 | } 80 | } 81 | } 82 | redis.hset('hgetall', 'name', 'hgetall') 83 | expect(controller.send(:get_value, 'hgetall')).to eql(eql) 84 | end 85 | 86 | it 'returns a hash value (get_value - lrange)' do 87 | redis.lpush('lrange', '1') 88 | redis.lpush('lrange', '2') 89 | eql = { 90 | length: 2, 91 | values: [ 92 | { 93 | index: 0, 94 | type: 'json', 95 | value: 2 96 | }, 97 | { 98 | index: 1, 99 | type: 'json', 100 | value: 1 101 | } 102 | ] 103 | } 104 | expect(controller.send(:get_value, 'lrange')).to eql(eql) 105 | end 106 | 107 | it 'returns a hash value (get_value - set)' do 108 | redis.sadd('smembers', 'smembers') 109 | eql = { 110 | values: [{ type: 'string', value: 'smembers' }] 111 | } 112 | expect(controller.send(:get_value, 'smembers')).to eql(eql) 113 | end 114 | 115 | it 'returns a hash value (get_value - zset)' do 116 | redis.zadd('zrange', 10, '1') 117 | redis.zadd('zrange', 20, '2') 118 | redis.zadd('zrange', 30, '3') 119 | eql = { 120 | values: [ 121 | { 122 | score: 10.0, 123 | type: 'json', 124 | value: 1 125 | }, 126 | { 127 | score: 20.0, 128 | type: 'json', 129 | value: 2 130 | }, 131 | { 132 | score: 30.0, 133 | type: 'json', 134 | value: 3 135 | } 136 | ] 137 | } 138 | expect(controller.send(:get_value, 'zrange')).to eql(eql) 139 | end 140 | 141 | it 'returns a hash value (get_value - not found)' do 142 | redis.zadd('zrange', 10, '1') 143 | redis.zadd('zrange', 20, '2') 144 | redis.zadd('zrange', 30, '3') 145 | eql = { 146 | value: 'Not found' 147 | } 148 | expect(controller.send(:get_value, 'testtesttesttesttest')).to eql(eql) 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /spec/dummy/.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.6.3 -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 5 | 6 | require_relative 'config/application' 7 | 8 | Rails.application.load_tasks 9 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | //= link redis_web_manager_manifest.js 5 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require_tree . 15 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationHelper 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | <%= stylesheet_link_tag 'application', media: 'all' %> 9 | <%= javascript_include_tag 'application' %> 10 | 11 | 12 | 13 | <%= yield %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /spec/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 5 | load Gem.bin_path('bundler', 'bundle') 6 | -------------------------------------------------------------------------------- /spec/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | APP_PATH = File.expand_path('../config/application', __dir__) 5 | require_relative '../config/boot' 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require_relative '../config/boot' 5 | require 'rake' 6 | Rake.application.run 7 | -------------------------------------------------------------------------------- /spec/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'fileutils' 5 | include FileUtils 6 | 7 | # path to your application root. 8 | APP_ROOT = File.expand_path('..', __dir__) 9 | 10 | def system!(*args) 11 | system(*args) || abort("\n== Command #{args} failed ==") 12 | end 13 | 14 | chdir APP_ROOT do 15 | # This script is a starting point to setup your application. 16 | # Add necessary setup steps to this file. 17 | 18 | puts '== Installing dependencies ==' 19 | system! 'gem install bundler --conservative' 20 | system('bundle check') || system!('bundle install') 21 | 22 | puts "\n== Removing old logs and tempfiles ==" 23 | system! 'bin/rails log:clear tmp:clear' 24 | 25 | puts "\n== Restarting application server ==" 26 | system! 'bin/rails restart' 27 | end 28 | -------------------------------------------------------------------------------- /spec/dummy/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'fileutils' 5 | include FileUtils 6 | 7 | # path to your application root. 8 | APP_ROOT = File.expand_path('..', __dir__) 9 | 10 | def system!(*args) 11 | system(*args) || abort("\n== Command #{args} failed ==") 12 | end 13 | 14 | chdir APP_ROOT do 15 | # This script is a way to update your development environment automatically. 16 | # Add necessary update steps to this file. 17 | 18 | puts '== Installing dependencies ==' 19 | system! 'gem install bundler --conservative' 20 | system('bundle check') || system!('bundle install') 21 | 22 | puts "\n== Removing old logs and tempfiles ==" 23 | system! 'bin/rails log:clear tmp:clear' 24 | 25 | puts "\n== Restarting application server ==" 26 | system! 'bin/rails restart' 27 | end 28 | -------------------------------------------------------------------------------- /spec/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require_relative 'config/environment' 6 | 7 | run Rails.application 8 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'boot' 4 | 5 | require 'rails' 6 | require 'action_controller/railtie' 7 | require 'action_view/railtie' 8 | require 'sprockets/railtie' 9 | 10 | Bundler.require(*Rails.groups) 11 | require 'redis_web_manager' 12 | 13 | module Dummy 14 | class Application < Rails::Application 15 | # Initialize configuration defaults for originally generated Rails version. 16 | config.load_defaults 5.2 17 | 18 | # Settings in config/environments/* take precedence over those specified here. 19 | # Application configuration can go into files in config/initializers 20 | # -- all .rb files in that directory are automatically loaded after loading 21 | # the framework and any gems in your application. 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) 5 | 6 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 7 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) 8 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative 'application' 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded on 7 | # every request. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable/disable caching. By default caching is disabled. 18 | # Run rails dev:cache to toggle caching. 19 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 20 | config.action_controller.perform_caching = true 21 | 22 | config.cache_store = :memory_store 23 | config.public_file_server.headers = { 24 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 25 | } 26 | else 27 | config.action_controller.perform_caching = false 28 | 29 | config.cache_store = :null_store 30 | end 31 | 32 | # Print deprecation notices to the Rails logger. 33 | config.active_support.deprecation = :log 34 | 35 | # Debug mode disables concatenation and preprocessing of assets. 36 | # This option may cause significant delays in view rendering with a large 37 | # number of complex assets. 38 | config.assets.debug = true 39 | 40 | # Suppress logger output for asset requests. 41 | config.assets.quiet = true 42 | 43 | # Raises error for missing translations 44 | # config.action_view.raise_on_missing_translations = true 45 | 46 | # Use an evented file watcher to asynchronously detect changes in source code, 47 | # routes, locales, etc. This feature depends on the listen gem. 48 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 49 | end 50 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 35 | 36 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 37 | # config.action_controller.asset_host = 'http://assets.example.com' 38 | 39 | # Specifies the header that your server uses for sending files. 40 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 41 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 42 | 43 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 44 | # config.force_ssl = true 45 | 46 | # Use the lowest log level to ensure availability of diagnostic information 47 | # when problems arise. 48 | config.log_level = :debug 49 | 50 | # Prepend all log lines with the following tags. 51 | config.log_tags = [:request_id] 52 | 53 | # Use a different cache store in production. 54 | # config.cache_store = :mem_cache_store 55 | 56 | # Use a real queuing backend for Active Job (and separate queues per environment) 57 | # config.active_job.queue_adapter = :resque 58 | # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" 59 | 60 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 61 | # the I18n.default_locale when a translation cannot be found). 62 | config.i18n.fallbacks = true 63 | 64 | # Send deprecation notices to registered listeners. 65 | config.active_support.deprecation = :notify 66 | 67 | # Use default logging formatter so that PID and timestamp are not suppressed. 68 | config.log_formatter = ::Logger::Formatter.new 69 | 70 | # Use a different logger for distributed setups. 71 | # require 'syslog/logger' 72 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 73 | 74 | if ENV['RAILS_LOG_TO_STDOUT'].present? 75 | logger = ActiveSupport::Logger.new(STDOUT) 76 | logger.formatter = config.log_formatter 77 | config.logger = ActiveSupport::TaggedLogging.new(logger) 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # The test environment is used exclusively to run your application's 7 | # test suite. You never need to work with it otherwise. Remember that 8 | # your test database is "scratch space" for the test suite and is wiped 9 | # and recreated between test runs. Don't rely on the data there! 10 | config.cache_classes = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Print deprecation notices to the stderr. 34 | config.active_support.deprecation = :stderr 35 | 36 | # Raises error for missing translations 37 | # config.action_view.raise_on_missing_translations = true 38 | end 39 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # ActiveSupport::Reloader.to_prepare do 5 | # ApplicationController.renderer.defaults.merge!( 6 | # http_host: 'example.org', 7 | # https: false 8 | # ) 9 | # end 10 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Version of your assets, change this if you want to expire all your assets. 6 | Rails.application.config.assets.version = '1.0' 7 | 8 | # Add additional assets to the asset load path. 9 | # Rails.application.config.assets.paths << Emoji.images_path 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 6 | 7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 8 | # Rails.backtrace_cleaner.remove_silencers! 9 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Define an application-wide content security policy 5 | # For further information see the following documentation 6 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 7 | 8 | # Rails.application.config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | 16 | # # Specify URI for violation reports 17 | # # policy.report_uri "/csp-violation-report-endpoint" 18 | # end 19 | 20 | # If you are using UJS then enable automatic nonce generation 21 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 22 | 23 | # Report CSP violations to a specified URI 24 | # For further information see the following documentation: 25 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 26 | # Rails.application.config.content_security_policy_report_only = true 27 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Specify a serializer for the signed and encrypted cookie jars. 6 | # Valid options are :json, :marshal, and :hybrid. 7 | Rails.application.config.action_dispatch.cookies_serializer = :json 8 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure sensitive parameters which will be filtered from the log file. 6 | Rails.application.config.filter_parameters += [:password] 7 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Add new inflection rules using the following format. Inflections 5 | # are locale specific, and you may define rules for as many different 6 | # locales as you wish. All of these examples are active by default: 7 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 8 | # inflect.plural /^(ox)$/i, '\1en' 9 | # inflect.singular /^(ox)en/i, '\1' 10 | # inflect.irregular 'person', 'people' 11 | # inflect.uncountable %w( fish sheep ) 12 | # end 13 | 14 | # These inflection rules are supported but not enabled by default: 15 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 16 | # inflect.acronym 'RESTful' 17 | # end 18 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Be sure to restart your server when you modify this file. 3 | 4 | # Add new mime types for use in respond_to blocks: 5 | # Mime::Type.register "text/richtext", :rtf 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] 11 | end 12 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /spec/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Puma can serve each request in a thread from an internal thread pool. 4 | # The `threads` method setting takes two numbers: a minimum and maximum. 5 | # Any libraries that use thread pools should be configured to match 6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 7 | # and maximum; this matches the default thread size of Active Record. 8 | # 9 | threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } 10 | threads threads_count, threads_count 11 | 12 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 13 | # 14 | port ENV.fetch('PORT') { 3000 } 15 | 16 | # Specifies the `environment` that Puma will run in. 17 | # 18 | environment ENV.fetch('RAILS_ENV') { 'development' } 19 | 20 | # Specifies the `pidfile` that Puma will use. 21 | pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } 22 | 23 | # Specifies the number of `workers` to boot in clustered mode. 24 | # Workers are forked webserver processes. If using threads and workers together 25 | # the concurrency of the application would be max `threads` * `workers`. 26 | # Workers do not work on JRuby or Windows (both of which do not support 27 | # processes). 28 | # 29 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 30 | 31 | # Use the `preload_app!` method when specifying a `workers` number. 32 | # This directive tells Puma to first boot the application and load code 33 | # before forking the application. This takes advantage of Copy On Write 34 | # process behavior so workers use less memory. 35 | # 36 | # preload_app! 37 | 38 | # Allow puma to be restarted by `rails restart` command. 39 | plugin :tmp_restart 40 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.routes.draw do 4 | mount RedisWebManager::Engine => '/redis_web_manager' 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | %w[ 4 | .ruby-version 5 | .rbenv-vars 6 | tmp/restart.txt 7 | tmp/caching-dev.txt 8 | ].each { |path| Spring.watch(path) } 9 | -------------------------------------------------------------------------------- /spec/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /spec/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/log/.keep -------------------------------------------------------------------------------- /spec/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /spec/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /spec/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /spec/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenGems/redis_web_manager/4bc562f833cd0d51f0249b5fc56b2bfccd8da436/spec/dummy/public/favicon.ico -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ApplicationHelper, type: :helper do 6 | describe 'helper' do 7 | it 'returns status tag (true)' do 8 | expect(helper.status(true)).to match(/ON/) 9 | end 10 | 11 | it 'returns status tag (true)' do 12 | expect(helper.status(false)).to match(/OFF/) 13 | end 14 | 15 | it 'returns url tag' do 16 | expect(helper.url('test.com')).to match(/kbd/) 17 | end 18 | 19 | it 'returns a no expiration' do 20 | expect(helper.expiry(-1)).to eql('No expiration date') 21 | end 22 | 23 | it 'returns a expiration' do 24 | expect(helper.expiry(86_400)).to eql('1 day') 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/helpers/clients_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::ClientsHelper, type: :helper do 6 | describe 'helper' do 7 | it 'returns age value' do 8 | expect(helper.age(86_400)).to eql('1 day') 9 | end 10 | 11 | it 'returns flags value' do 12 | expect(helper.flags(:A)).to eql('Connection to be closed ASAP') 13 | end 14 | 15 | it 'returns events value' do 16 | expect(helper.events(:r)).to eql('The client socket is readable') 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/helpers/dashboard_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::DashboardHelper, type: :helper do 6 | describe 'helper' do 7 | it 'returns graph_canvas tag (div)' do 8 | expect(helper.graph_canvas(nil, :test)).to match(/div/) 9 | end 10 | 11 | it 'returns graph_canvas tag (canvas)' do 12 | expect(helper.graph_canvas([1], :test)).to match(/canvas/) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/helpers/keys_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::KeysHelper, type: :helper do 6 | describe 'helper' do 7 | it 'returns Array of types' do 8 | expect(helper.types_filters).to eql([%w[All all], 9 | %w[String string], 10 | %w[Hash hash], 11 | %w[Set set], 12 | %w[Zset zset], 13 | %w[List list]]) 14 | end 15 | 16 | it 'returns Array of expiration' do 17 | expect(helper.expiry_filters).to eq( 18 | [%w[All all], 19 | ['No expiry', -1], 20 | ['Less than 1 hour', 3600], 21 | ['Less than 1 week', 604_800], 22 | ['Less than 1 month', 2_592_000], 23 | ['Less than 6 months', 15_552_000]] 24 | ) 25 | end 26 | 27 | it 'returns Array of memories' do 28 | expect(helper.memory_filters).to eq( 29 | [%w[All all], 30 | ['Less than 1 KB', 1000], 31 | ['Less than 10 KB', 10_000], 32 | ['Less than 100 KB', 100_000], 33 | ['Less than 1 MB', 1_000_000], 34 | ['Less than 10 MB', 10_000_000], 35 | ['Less than 100 MB', 100_000_000]] 36 | ) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is copied to spec/ when you run 'rails generate rspec:install' 4 | require 'spec_helper' 5 | ENV['RAILS_ENV'] ||= 'test' 6 | 7 | require File.expand_path('dummy/config/environment.rb', __dir__) 8 | 9 | # Prevent database truncation if the environment is production 10 | abort('The Rails environment is running in production mode!') if Rails.env.production? 11 | 12 | require 'rspec/rails' 13 | # Add additional requires below this line. Rails is not loaded until this point! 14 | 15 | # Requires supporting ruby files with custom matchers and macros, etc, in 16 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 17 | # run as spec files by default. This means that files in spec/support that end 18 | # in _spec.rb will both be required and run as specs, causing the specs to be 19 | # run twice. It is recommended that you do not name files matching this glob to 20 | # end with _spec.rb. You can configure this pattern with the --pattern 21 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 22 | # 23 | # The following line is provided for convenience purposes. It has the downside 24 | # of increasing the boot-up time by auto-requiring all files in the support 25 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 26 | # require only the support files necessary. 27 | # 28 | # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } 29 | 30 | RSpec.configure do |config| 31 | # RSpec Rails can automatically mix in different behaviours to your tests 32 | # based on their file location, for example enabling you to call `get` and 33 | # `post` in specs under `spec/controllers`. 34 | # 35 | # You can disable this behaviour by removing the line below, and instead 36 | # explicitly tag your specs with their type, e.g.: 37 | # 38 | # RSpec.describe UsersController, :type => :controller do 39 | # # ... 40 | # end 41 | # 42 | # The different available types are documented in the features, such as in 43 | # https://relishapp.com/rspec/rspec-rails/docs 44 | config.infer_spec_type_from_file_location! 45 | 46 | # Filter lines from Rails gems in backtraces. 47 | config.filter_rails_from_backtrace! 48 | # arbitrary gems may also be filtered via: 49 | # config.filter_gems_from_backtrace("gem name") 50 | end 51 | -------------------------------------------------------------------------------- /spec/redis_web_manager_action_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::Action do 6 | let(:action) do 7 | RedisWebManager::Action.new(RedisWebManager.redises.keys[0]) 8 | end 9 | 10 | let(:redis) do 11 | Redis.new 12 | end 13 | 14 | describe 'action' do 15 | it 'returns a OK (flushall)' do 16 | expect(action.flushall).to eql('OK') 17 | end 18 | 19 | it 'returns a OK (flushdb)' do 20 | expect(action.flushdb).to eql('OK') 21 | end 22 | 23 | it 'returns a 1 (del)' do 24 | redis.set('test', 'test') 25 | expect(action.del('test')).to eql(1) 26 | end 27 | 28 | it 'returns a OK (rename)' do 29 | redis.set('test', 'test') 30 | expect(action.rename('test', 'test2')).to eql('OK') 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/redis_web_manager_connection_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::Connection do 6 | let(:connection) do 7 | RedisWebManager::Connection.new(RedisWebManager.redises.keys[0]) 8 | end 9 | 10 | describe 'connection' do 11 | it 'returns a host' do 12 | expect(connection.host).to eql('127.0.0.1') 13 | end 14 | 15 | it 'returns a port' do 16 | expect(connection.port).to eql(6379) 17 | end 18 | 19 | it 'returns a db' do 20 | expect(connection.db).to eql(0) 21 | end 22 | 23 | it 'returns an id' do 24 | expect(connection.id).to eql('redis://127.0.0.1:6379/0') 25 | end 26 | 27 | it 'returns a location' do 28 | expect(connection.location).to eql('127.0.0.1:6379') 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/redis_web_manager_data_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::Data do 6 | let(:data) do 7 | RedisWebManager::Data.new(RedisWebManager.redises.keys[0]) 8 | end 9 | 10 | describe 'data' do 11 | it 'returns a OK (perform)' do 12 | expect(data.perform).to eql('OK') 13 | end 14 | 15 | it 'returns a Array of keys' do 16 | expect(data.keys).to be_a_kind_of(Array) 17 | end 18 | 19 | it 'returns a Array of keys deleted' do 20 | expect(data.flush).to be_a_kind_of(Array) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/redis_web_manager_info_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager::Info do 6 | let(:info) do 7 | RedisWebManager::Info.new(RedisWebManager.redises.keys[0]) 8 | end 9 | 10 | let(:redis) do 11 | Redis.new 12 | end 13 | 14 | describe 'info' do 15 | it 'returns a true (status)' do 16 | expect(info.status).to eql(true) 17 | end 18 | 19 | it 'returns a Hash class (stats)' do 20 | expect(info.stats).to be_a_kind_of(Hash) 21 | end 22 | 23 | 24 | it 'returns a string value (type)' do 25 | redis.set('test', 'test', ex: 20.seconds) 26 | expect(info.type('test')).to eql('string') 27 | end 28 | 29 | it 'returns a Arary of keys (search)' do 30 | redis.set('testtesttest', 'testtesttest') 31 | expect(info.search('testtesttest')).to eql(['testtesttest']) 32 | end 33 | 34 | it 'returns an empty array if query string is empty' do 35 | expect(info.search(nil)).to eql([]) 36 | expect(info.search('')).to eql([]) 37 | end 38 | 39 | it 'returns a ttl value (expire)' do 40 | expect(info.expiry('test')).to eql(20) 41 | end 42 | 43 | it 'returns a memory usage value (memory_usage)' do 44 | expect(info.memory_usage('test')).to be_between(52, 62) 45 | end 46 | 47 | it 'returns a test value (get)' do 48 | expect(info.get('test')).to eql('test') 49 | end 50 | 51 | it 'returns a Integer value (llen)' do 52 | redis.lpush('llen', '1') 53 | redis.lpush('llen', '2') 54 | expect(info.llen('llen')).to eql(2) 55 | end 56 | 57 | it 'returns a Array value (lrange)' do 58 | redis.lpush('lrange', '1') 59 | redis.lpush('lrange', '2') 60 | expect(info.lrange('lrange', 0, -1)).to eql(%w[2 1]) 61 | end 62 | 63 | it 'returns a Array value (smembers)' do 64 | redis.sadd('smembers', 'smembers') 65 | expect(info.smembers('smembers')).to eql(%w[smembers]) 66 | end 67 | 68 | it 'returns a Array value (zrange)' do 69 | redis.zadd('zrange', 10, '1') 70 | redis.zadd('zrange', 20, '2') 71 | redis.zadd('zrange', 30, '3') 72 | expect(info.zrange('zrange', 0, -1)).to eql(%w[1 2 3]) 73 | end 74 | 75 | it 'returns a Hash value (hgetall)' do 76 | redis.hset('hgetall', 'name', 'hgetall') 77 | expect(info.hgetall('hgetall')).to eql('name' => 'hgetall') 78 | end 79 | 80 | it 'returns a Integer class (dbsize)' do 81 | expect(info.dbsize).to be_a_kind_of(Integer) 82 | end 83 | 84 | it 'returns a Hash class (configuration)' do 85 | expect(info.configuration).to be_a_kind_of(Hash) 86 | end 87 | 88 | it 'returns a Array class (clients)' do 89 | expect(info.clients).to be_a_kind_of(Array) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /spec/redis_web_manager_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | RSpec.describe RedisWebManager do 6 | describe 'Test default configuration' do 7 | it 'returns a Redis class' do 8 | expect(RedisWebManager.redises).to be_a_kind_of(Hash) 9 | end 10 | 11 | it 'returns a nil class' do 12 | expect(RedisWebManager.authenticate).to eql(nil) 13 | end 14 | 15 | it 'returns a ActiveSupport::Duration class' do 16 | expect(RedisWebManager.lifespan).to be_a_kind_of(ActiveSupport::Duration) 17 | end 18 | end 19 | 20 | describe 'Test configuration' do 21 | it 'returns a raise error (redises)' do 22 | expect do 23 | RedisWebManager.configure do |c| 24 | c.redises = 1 25 | end 26 | end.to raise_error(ArgumentError, 'Invalid redises hash, use like that { test: Redis.new }') 27 | end 28 | 29 | it 'returns a raise error (value of redises)' do 30 | expect do 31 | RedisWebManager.configure do |c| 32 | c.redises = { 33 | default: 1 34 | } 35 | end 36 | end.to raise_error(ArgumentError, 'Invalid Redis instance for default, use like that Redis.new') 37 | end 38 | 39 | it 'returns a raise error (lifespan)' do 40 | expect do 41 | RedisWebManager.configure do |c| 42 | c.redises = { 43 | default: Redis.new 44 | } 45 | c.lifespan = 1 46 | end 47 | end.to raise_error(ArgumentError, 'Invalid lifespan, use like that 15.days, 15.minutes etc') 48 | end 49 | 50 | it 'returns a raise error (lifespan)' do 51 | expect do 52 | RedisWebManager.configure do |c| 53 | c.redises = { 54 | default: Redis.new 55 | } 56 | c.lifespan = -1.days 57 | end 58 | end.to raise_error(ArgumentError, 'Invalid lifespan, value must be greater than 0') 59 | end 60 | 61 | it 'returns instances' do 62 | RedisWebManager.configure do |c| 63 | c.redises = { 64 | foo: Redis.new, 65 | bar: Redis.new 66 | } 67 | c.lifespan = 12.days 68 | end 69 | 70 | expect(RedisWebManager.redises.keys).to eql(%i[foo bar]) 71 | expect(RedisWebManager.redises.values.map(&:class)).to eql([Redis, Redis]) 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 5 | # The generated `.rspec` file contains `--require spec_helper` which will cause 6 | # this file to always be loaded, without a need to explicitly require it in any 7 | # files. 8 | 9 | require 'simplecov' 10 | require 'coveralls' 11 | 12 | SimpleCov.start 13 | Coveralls.wear! 14 | 15 | # Given that it is always loaded, you are encouraged to keep this file as 16 | # light-weight as possible. Requiring heavyweight dependencies from this file 17 | # will add to the boot time of your test suite on EVERY test run, even for an 18 | # individual file that may not need all of that loaded. Instead, consider making 19 | # a separate helper file that requires the additional dependencies and performs 20 | # the additional setup, and require it from the spec files that actually need 21 | # it. 22 | # 23 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 24 | RSpec.configure do |config| 25 | # rspec-expectations config goes here. You can use an alternate 26 | # assertion/expectation library such as wrong or the stdlib/minitest 27 | # assertions if you prefer. 28 | config.expect_with :rspec do |expectations| 29 | # This option will default to `true` in RSpec 4. It makes the `description` 30 | # and `failure_message` of custom matchers include text for helper methods 31 | # defined using `chain`, e.g.: 32 | # be_bigger_than(2).and_smaller_than(4).description 33 | # # => "be bigger than 2 and smaller than 4" 34 | # ...rather than: 35 | # # => "be bigger than 2" 36 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 37 | end 38 | 39 | # rspec-mocks config goes here. You can use an alternate test double 40 | # library (such as bogus or mocha) by changing the `mock_with` option here. 41 | config.mock_with :rspec do |mocks| 42 | # Prevents you from mocking or stubbing a method that does not exist on 43 | # a real object. This is generally recommended, and will default to 44 | # `true` in RSpec 4. 45 | mocks.verify_partial_doubles = true 46 | end 47 | 48 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 49 | # have no way to turn it off -- the option exists only for backwards 50 | # compatibility in RSpec 3). It causes shared context metadata to be 51 | # inherited by the metadata hash of host groups and examples, rather than 52 | # triggering implicit auto-inclusion in groups with matching metadata. 53 | config.shared_context_metadata_behavior = :apply_to_host_groups 54 | 55 | # The settings below are suggested to provide a good initial experience 56 | # with RSpec, but feel free to customize to your heart's content. 57 | # # This allows you to limit a spec run to individual examples or groups 58 | # # you care about by tagging them with `:focus` metadata. When nothing 59 | # # is tagged with `:focus`, all examples get run. RSpec also provides 60 | # # aliases for `it`, `describe`, and `context` that include `:focus` 61 | # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 62 | # config.filter_run_when_matching :focus 63 | # 64 | # # Allows RSpec to persist some state between runs in order to support 65 | # # the `--only-failures` and `--next-failure` CLI options. We recommend 66 | # # you configure your source control system to ignore this file. 67 | # config.example_status_persistence_file_path = "spec/examples.txt" 68 | # 69 | # # Limits the available syntax to the non-monkey patched syntax that is 70 | # # recommended. For more details, see: 71 | # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 72 | # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 73 | # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 74 | # config.disable_monkey_patching! 75 | # 76 | # # Many RSpec users commonly either run the entire suite or an individual 77 | # # file, and it's useful to allow more verbose output when running an 78 | # # individual spec file. 79 | # if config.files_to_run.one? 80 | # # Use the documentation formatter for detailed output, 81 | # # unless a formatter has already been configured 82 | # # (e.g. via a command-line flag). 83 | # config.default_formatter = "doc" 84 | # end 85 | # 86 | # # Print the 10 slowest examples and example groups at the 87 | # # end of the spec run, to help surface which specs are running 88 | # # particularly slow. 89 | # config.profile_examples = 10 90 | # 91 | # # Run specs in random order to surface order dependencies. If you find an 92 | # # order dependency and want to debug it, you can fix the order by providing 93 | # # the seed, which is printed after each run. 94 | # # --seed 1234 95 | # config.order = :random 96 | # 97 | # # Seed global randomization in this process using the `--seed` CLI option. 98 | # # Setting this allows you to use `--seed` to deterministically reproduce 99 | # # test failures related to randomization by passing the same `--seed` value 100 | # # as the one that triggered the failure. 101 | # Kernel.srand config.seed 102 | end 103 | --------------------------------------------------------------------------------