rails_web_cache 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +11 -0
  6. data/Gemfile +15 -0
  7. data/Gemfile.lock +178 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +43 -0
  10. data/Rakefile +28 -0
  11. data/app/assets/config/rails_web_cache_manifest.js +2 -0
  12. data/app/assets/images/rails_web_cache/.keep +0 -0
  13. data/app/assets/javascripts/rails_web_cache/application.js +17 -0
  14. data/app/assets/javascripts/rails_web_cache/bootstrap.js +6 -0
  15. data/app/assets/javascripts/rails_web_cache/jquery.js +2 -0
  16. data/app/assets/javascripts/rails_web_cache/keys.js +22 -0
  17. data/app/assets/javascripts/rails_web_cache/popper.js +5 -0
  18. data/app/assets/stylesheets/rails_web_cache/application.css +14 -0
  19. data/app/assets/stylesheets/rails_web_cache/bootstrap.css +7 -0
  20. data/app/controllers/rails_web_cache/application_controller.rb +7 -0
  21. data/app/controllers/rails_web_cache/keys_controller.rb +84 -0
  22. data/app/helpers/rails_web_cache/keys_helper.rb +18 -0
  23. data/app/views/layouts/rails_web_cache/application.html.erb +16 -0
  24. data/app/views/rails_web_cache/keys/index.html.erb +95 -0
  25. data/app/views/rails_web_cache/keys/show.html.erb +26 -0
  26. data/app/views/rails_web_cache/shared/_header.html.erb +17 -0
  27. data/bin/rails +20 -0
  28. data/config/routes.rb +11 -0
  29. data/lib/rails_web_cache/base.rb +44 -0
  30. data/lib/rails_web_cache/engine.rb +21 -0
  31. data/lib/rails_web_cache/entry.rb +19 -0
  32. data/lib/rails_web_cache/file_store.rb +9 -0
  33. data/lib/rails_web_cache/memory_store.rb +9 -0
  34. data/lib/rails_web_cache/redis_cache_store.rb +23 -0
  35. data/lib/rails_web_cache/version.rb +5 -0
  36. data/lib/rails_web_cache.rb +46 -0
  37. data/rails_web_cache.gemspec +39 -0
  38. data/spec/controllers/keys_controller_spec.rb +41 -0
  39. data/spec/dummy/Rakefile +6 -0
  40. data/spec/dummy/app/assets/config/manifest.js +3 -0
  41. data/spec/dummy/app/assets/javascripts/application.js +14 -0
  42. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy/bin/bundle +5 -0
  46. data/spec/dummy/bin/rails +6 -0
  47. data/spec/dummy/bin/rake +6 -0
  48. data/spec/dummy/bin/setup +38 -0
  49. data/spec/dummy/bin/update +33 -0
  50. data/spec/dummy/bin/yarn +13 -0
  51. data/spec/dummy/config/application.rb +23 -0
  52. data/spec/dummy/config/boot.rb +7 -0
  53. data/spec/dummy/config/environment.rb +7 -0
  54. data/spec/dummy/config/environments/development.rb +47 -0
  55. data/spec/dummy/config/environments/production.rb +74 -0
  56. data/spec/dummy/config/environments/test.rb +40 -0
  57. data/spec/dummy/config/initializers/application_controller_renderer.rb +10 -0
  58. data/spec/dummy/config/initializers/assets.rb +14 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +8 -0
  60. data/spec/dummy/config/initializers/content_security_policy.rb +26 -0
  61. data/spec/dummy/config/initializers/cookies_serializer.rb +7 -0
  62. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  63. data/spec/dummy/config/initializers/inflections.rb +18 -0
  64. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  65. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  66. data/spec/dummy/config/locales/en.yml +33 -0
  67. data/spec/dummy/config/puma.rb +39 -0
  68. data/spec/dummy/config/routes.rb +5 -0
  69. data/spec/dummy/config/spring.rb +8 -0
  70. data/spec/dummy/config.ru +7 -0
  71. data/spec/dummy/log/development.log +630 -0
  72. data/spec/dummy/log/test.log +1791 -0
  73. data/spec/dummy/public/404.html +67 -0
  74. data/spec/dummy/public/422.html +67 -0
  75. data/spec/dummy/public/500.html +66 -0
  76. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  77. data/spec/dummy/public/apple-touch-icon.png +0 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/dummy/tmp/development_secret.txt +1 -0
  80. data/spec/helpers/keys_helper_spec.rb +28 -0
  81. data/spec/rails_helper.rb +50 -0
  82. data/spec/rails_web_cache_spec.rb +37 -0
  83. data/spec/routing/keys_controller_spec.rb +31 -0
  84. data/spec/spec_helper.rb +101 -0
  85. metadata +237 -0
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class KeysController < RailsWebCache::ApplicationController
5
+ before_action :set_key, only: %i[show destroy]
6
+ before_action :set_page, only: :index
7
+ before_action :set_per_page, only: :index
8
+ before_action :set_offset, only: :index
9
+
10
+ # GET /keys
11
+ def index
12
+ @keys = keys
13
+ @pages = (@keys.size / @per_page.to_f).ceil
14
+ @keys = (@keys[@offset, @per_page] || []).map.with_index do |key, idx|
15
+ format_key(key, idx)
16
+ end
17
+ end
18
+
19
+ # GET /keys/:key
20
+ def show
21
+ @value = cache.read(@key)
22
+ @entry = cache.entry(@key)
23
+ end
24
+
25
+ # DELETE /keys/:key
26
+ def destroy
27
+ cache.delete(@key)
28
+ redirect_to root_path
29
+ end
30
+
31
+ # DELETE /keys/all/:keys
32
+ def destroy_all
33
+ keys = params[:keys].presence || []
34
+ keys.each do |key|
35
+ cache.delete(key)
36
+ end
37
+ redirect_to root_path
38
+ end
39
+
40
+ private
41
+
42
+ def cache
43
+ @cache ||= RailsWebCache.cache
44
+ end
45
+
46
+ def format_key(key, idx)
47
+ entry = cache.entry(key)
48
+ {
49
+ id: idx,
50
+ key: key,
51
+ value: cache.read(key),
52
+ expires_at: entry&.expires_at,
53
+ size: entry&.size
54
+ }
55
+ end
56
+
57
+ def set_key
58
+ @key = params[:key]
59
+ end
60
+
61
+ def set_page
62
+ page = (params[:page].presence || 1).to_i
63
+ page = 1 if page.negative? || page > (2**32)
64
+ @page = page
65
+ end
66
+
67
+ def set_offset
68
+ @offset = (@page - 1) * @per_page
69
+ end
70
+
71
+ def set_per_page
72
+ @per_page = 20
73
+ end
74
+
75
+ def keys
76
+ query = params[:query].presence
77
+ if query.present?
78
+ cache.search(query)
79
+ else
80
+ cache.keys
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ module KeysHelper
5
+ def expired_at(value)
6
+ return 'n/a' unless value.present?
7
+ content_tag(:time,
8
+ value.strftime('%d %b %Y - %H:%M'),
9
+ datetime: value.iso8601)
10
+ end
11
+
12
+ def check_box_key(value, id)
13
+ (label_tag "key-#{id}", value, class: 'd-none').concat(
14
+ check_box_tag('keys[]', value, nil, id: "key-#{id}")
15
+ )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>
5
+ Rails web cache
6
+ </title>
7
+ <%= csrf_meta_tags %>
8
+ <%= csp_meta_tag %>
9
+ <%= stylesheet_link_tag 'rails_web_cache/application', media: 'all' %>
10
+ <%= javascript_include_tag 'rails_web_cache/application' %>
11
+ </head>
12
+ <body>
13
+ <%= render partial: 'rails_web_cache/shared/header' %>
14
+ <%= yield %>
15
+ </body>
16
+ </html>
@@ -0,0 +1,95 @@
1
+ <div class="container mt-4">
2
+ <div class="row">
3
+ <div class="col-12">
4
+ <%= form_tag(root_path, method: :get, class: 'mb-4') do %>
5
+ <div class="input-group">
6
+ <%= text_field_tag :query, params[:query], class: 'form-control', placeholder: 'Search key(s)' %>
7
+ <div class="input-group-append">
8
+ <%= submit_tag 'Search', class: 'btn btn-primary', name: nil %>
9
+ </div>
10
+ </div>
11
+ <% end %>
12
+ </div>
13
+ </div>
14
+ <div class="row">
15
+ <div class="col-12">
16
+ <%= form_tag(destroy_all_keys_path, method: :delete) do %>
17
+ <div class="table-responsive">
18
+ <table class="table table-striped table-bordered">
19
+ <thead>
20
+ <tr>
21
+ <th>
22
+ <%= label_tag 'select-all-keys', 'Select all keys', class: 'd-none' %>
23
+ <%= check_box_tag 'select-all-keys', nil %>
24
+ </th>
25
+ <th>Key</th>
26
+ <th>Value</th>
27
+ <th>Expires at</th>
28
+ <th>Size</th>
29
+ <th>Actions</th>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ <% @keys.each do |key| %>
34
+ <tr>
35
+ <td class="align-middle">
36
+ <%= check_box_key(key[:key], key[:id]) %>
37
+ </td>
38
+ <td class="align-middle">
39
+ <%= truncate(key[:key], length: 40) %>
40
+ </td>
41
+ <td class="align-middle">
42
+ <%= truncate(key[:value], length: 40) %>
43
+ </td>
44
+ <td class="align-middle">
45
+ <%= expired_at(key[:expires_at]) %>
46
+ </td>
47
+ <td class="align-middle text-center">
48
+ <%= number_to_human_size(key[:size]) %>
49
+ </td>
50
+ <td class="align-middle">
51
+ <div class="d-flex flex-row">
52
+ <%= link_to 'Show', key_path(key[:key]), title: "Show #{key[:key]}", class: 'btn btn-sm btn-primary w-100' %>
53
+ <%= link_to 'Delete', key_path(key[:key]), method: :delete, data: {confirm: 'Are you sure ?'}, title: "Delete #{key[:key]}", class: 'btn btn-sm btn-danger w-100 ml-2' %>
54
+ </div>
55
+ </td>
56
+ </tr>
57
+ <% end %>
58
+ </tbody>
59
+ </table>
60
+ </div>
61
+ <%= submit_tag 'Delete selected keys', class: 'btn btn-danger w-100 mb-4 disabled', id: 'delete-all-keys' %>
62
+ <% end %>
63
+ </div>
64
+ <div class="col-12">
65
+ <div class="d-flex justify-content-center">
66
+ <nav aria-label="Rails Web Cache">
67
+ <ul class="pagination">
68
+ <li class="page-item <%= @page <= 1 ? 'disabled' : '' %>">
69
+ <%= link_to 'Previous', root_path(params.merge(page: @page - 1 <= 0 ? 1 : @page - 1).permit(:query, :page)), title: "Pages - #{@page - 1 <= 0 ? 1 : @page - 1}", class: 'page-link' %>
70
+ </li>
71
+ <% if @pages > 2 %>
72
+ <li class="page-item">
73
+ <%= link_to (@pages - @pages) + 1, root_path(params.merge(page: 1).permit(:query, :page)), title: "Pages - #{(@pages - @pages) + 1}", class: 'page-link' %>
74
+ </li>
75
+ <% if @page > 2 %>
76
+ <li class="page-item">
77
+ <%= link_to @page - 1, root_path(params.merge(page: @page - 1).permit(:query, :page)), title: "Pages - #{@page - 1}", class: 'page-link' %>
78
+ </li>
79
+ <% end %>
80
+ <li class="page-item">
81
+ <%= link_to @page + 1, root_path(params.merge(page: @page + 1).permit(:query, :page)), title: "Pages - #{@page + 1}", class: 'page-link' %>
82
+ </li>
83
+ <li class="page-item">
84
+ <%= link_to @pages - 1, root_path(params.merge(page: @pages - 1).permit(:query, :page)), title: "Pages - #{@pages - 1}", class: 'page-link' %>
85
+ </li>
86
+ <% end %>
87
+ <li class="page-item <%= @page >= @pages ? 'disabled' : '' %>">
88
+ <%= link_to 'Next', root_path(params.merge(page: @page + 1 > @pages ? @pages : @page + 1).permit(:query, :page)), title: "Pages - #{@page + 1 > @pages ? @pages : @page + 1}", class: 'page-link' %>
89
+ </li>
90
+ </ul>
91
+ </nav>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
@@ -0,0 +1,26 @@
1
+ <div class="container mt-4">
2
+ <div class="row">
3
+ <div class="col-12">
4
+ <label>Key:</label>
5
+ <p>
6
+ <code><%= @key %></code>
7
+ </p>
8
+ <label>Value:</label>
9
+ <p>
10
+ <code><%= @value %></code>
11
+ </p>
12
+ <label>Expires at:</label>
13
+ <p>
14
+ <code><%= expired_at(@entry.expires_at) %></code>
15
+ </p>
16
+ <label>Size:</label>
17
+ <p>
18
+ <code><%= number_to_human_size(@entry.size, locale: :en) %></code>
19
+ </p>
20
+ <p>
21
+ <%= link_to 'Delete', key_path(@key), method: :delete, data: {confirm: 'Are you sure ?'}, title: "Delete #{@key}", class: 'btn btn-sm btn-danger w-100 mb-2' %>
22
+ <%= link_to 'Back', keys_path, title: 'View keys', class: 'btn btn-sm btn-primary w-100' %>
23
+ </p>
24
+ </div>
25
+ </div>
26
+ </div>
@@ -0,0 +1,17 @@
1
+ <nav class="navbar navbar-expand-lg navbar-light bg-light">
2
+ <div class="container">
3
+ <%= link_to 'Rails Web Cache', root_path, title: 'Rails Web Cache', class: 'navbar-brand' %>
4
+ <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Rails Web Cache">
5
+ <span class="navbar-toggler-icon"></span>
6
+ </button>
7
+ <div class="collapse navbar-collapse" id="navbarSupportedContent">
8
+ <ul class="navbar-nav mr-auto">
9
+ <li class="nav-item">
10
+ <%= link_to keys_path, title: 'View keys', class: 'nav-link' do %>
11
+ Keys <span class="badge badge-info badge-pill"><%= RailsWebCache.cache.keys_size %></span>
12
+ <% end %>
13
+ </li>
14
+ </ul>
15
+ </div>
16
+ </div>
17
+ </nav>
data/bin/rails ADDED
@@ -0,0 +1,20 @@
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/rails_web_cache/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'
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RailsWebCache::Engine.routes.draw do
4
+ resources :keys, only: %i[index show destroy], param: :key do
5
+ collection do
6
+ delete :destroy_all
7
+ end
8
+ end
9
+
10
+ root to: 'keys#index'
11
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class Base
5
+ attr_reader :cache
6
+
7
+ def initialize(cache)
8
+ @cache = cache
9
+ end
10
+
11
+ def keys_size
12
+ keys.size
13
+ end
14
+
15
+ def clear
16
+ cache.clear
17
+ end
18
+
19
+ def read(key)
20
+ cache.read(key) if key
21
+ end
22
+
23
+ def delete(key)
24
+ cache.delete(key) if key
25
+ end
26
+
27
+ def entry(key, options = {})
28
+ return nil unless key
29
+ entry = read_entry(key, options)
30
+ return nil unless entry
31
+ RailsWebCache::Entry.new(entry)
32
+ end
33
+
34
+ def search(query = '')
35
+ keys.select { |key| key.downcase.include?(query.downcase) } if query
36
+ end
37
+
38
+ private
39
+
40
+ def read_entry(key, options = {})
41
+ cache.send(:read_entry, key, options)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace RailsWebCache
6
+
7
+ path = Engine.root.join('app', 'assets', 'config')
8
+ config.assets.precompile << "#{path}/rails_web_cache_manifest.js"
9
+
10
+ config.generators do |generate|
11
+ # Don't generate assets
12
+ generate.assets false
13
+
14
+ # Don't generate helper
15
+ generate.helper false
16
+
17
+ # Rspec
18
+ generate.test_framework :rspec
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class Entry
5
+ attr_reader :entry
6
+
7
+ def initialize(entry)
8
+ @entry = entry
9
+ end
10
+
11
+ def expires_at
12
+ entry.expires_at ? Time.at(entry.expires_at) : nil
13
+ end
14
+
15
+ def size
16
+ entry.size
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class FileStore < Base
5
+ def keys
6
+ cache.instance_variable_get(:@data).keys
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class MemoryStore < Base
5
+ def keys
6
+ cache.instance_variable_get(:@data).keys
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ class RedisCacheStore < Base
5
+ def keys_size
6
+ redis.dbsize
7
+ end
8
+
9
+ def keys
10
+ redis.keys
11
+ end
12
+
13
+ def search(query = '')
14
+ redis.scan_each(match: "*#{query.downcase}*").to_a if query
15
+ end
16
+
17
+ private
18
+
19
+ def redis
20
+ @redis ||= cache.redis
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWebCache
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_web_cache/engine'
4
+ require 'rails_web_cache/base'
5
+ require 'rails_web_cache/entry'
6
+ require 'rails_web_cache/file_store'
7
+ require 'rails_web_cache/memory_store'
8
+ require 'rails_web_cache/redis_cache_store'
9
+
10
+ module RailsWebCache
11
+ class << self
12
+ def cache
13
+ @cache ||= driver(Rails.cache)
14
+ end
15
+
16
+ private
17
+
18
+ def driver(cache)
19
+ if redis_cache_store?(cache)
20
+ RedisCacheStore.new(cache)
21
+ elsif memory_store?(cache)
22
+ MemoryStore.new(cache)
23
+ elsif file_store?(cache)
24
+ FileStore.new(cache)
25
+ else
26
+ raise ArgumentError, "RailsWebCache doesn't support #{cache.class}"
27
+ end
28
+ end
29
+
30
+ def redis_cache_store?(driver)
31
+ defined?(::Redis) &&
32
+ defined?(::ActiveSupport::Cache::RedisCacheStore) &&
33
+ driver.is_a?(::ActiveSupport::Cache::RedisCacheStore)
34
+ end
35
+
36
+ def memory_store?(driver)
37
+ defined?(::ActiveSupport::Cache::MemoryStore) &&
38
+ driver.is_a?(::ActiveSupport::Cache::MemoryStore)
39
+ end
40
+
41
+ def file_store?(driver)
42
+ defined?(::ActiveSupport::Cache::FileStore) &&
43
+ driver.is_a?(::ActiveSupport::Cache::FileStore)
44
+ end
45
+ end
46
+ end