├── .gitignore
├── .rspec
├── .rubocop.yml
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── lib
├── runtime_config.rb
└── runtime_config
│ ├── conf.html.erb
│ ├── conf_param.rb
│ ├── middleware.rb
│ └── version.rb
├── runtime_config.gemspec
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | _misc/
2 | Gemfile.lock
3 | *.orig
4 | .rubocop-*
5 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --order random
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from:
2 | - https://relaxed.ruby.style/rubocop.yml
3 |
4 | AllCops:
5 | TargetRubyVersion: 2.2.2
6 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Nebulab
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 | # Runtime Config for Rails [](https://badge.fury.io/rb/runtime_config)
2 |
3 | A middleware to change configuration parameters at runtime for Rails 5.
4 |
5 | ## Installation and Usage
6 |
7 | - Add to `Gemfile`: `gem 'runtime_config'`
8 | - Add to `config/environments/development.rb`: `config.middleware.use RuntimeConfig::Middleware`
9 | - Optionally specify a path, ex. `config.middleware.use RuntimeConfig::Middleware, path: '/some_path'`
10 | - Open the path (or the one set in the option): **/dev**
11 |
12 | ## Features
13 |
14 | - Minimal interface using [milligram.io](https://milligram.io)
15 | - Change log level
16 | - Filter log lines using a RegExp
17 | - Enable/disable catching errors
18 | - Eneble/disable verbose query logs
19 | - Toggle cache
20 | - Clear cache
21 | - Restart server
22 |
23 | ## Preview
24 |
25 | 
26 |
27 | ## License
28 |
29 | This project is copyright © 2019 [Nebulab](http://nebulab.it/). It is free software, and may be redistributed under the terms specified in the [MIT](LICENSE.txt) license.
30 |
31 | ## About
32 |
33 | 
34 |
35 | DRU is funded and maintained by the [Nebulab](http://nebulab.it/) team.
36 |
37 | We firmly believe in the power of open-source. [Contact us](http://nebulab.it/contact-us/) if you like our work and you need help with your project design or development.
38 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 |
--------------------------------------------------------------------------------
/lib/runtime_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'runtime_config/conf_param'
4 | require 'runtime_config/middleware'
5 |
--------------------------------------------------------------------------------
/lib/runtime_config/conf.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Runtime Config
5 |
6 |
12 |
13 |
14 |
15 |
20 |
21 |
22 | Set log level to:
23 | <%= RuntimeConfig::LOGGER_SEVERITY.map{ |level| '
' + level + '' }.join(' ') %>
24 |
25 |
26 |
27 |
32 |
33 | Catch errors (404, 500, etc.):
34 |
ON (*)
35 |
OFF (*)
36 |
37 |
42 |
43 |
44 |
45 | Filter logs (using a Regexp, ex. app/views) (*):
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
(*) restarts the server
57 |
<% if @actions && @actions.any? %>
58 | <%= @actions.join("\n") %>
59 | <% else %>
60 | Rails.logger.level: <%= RuntimeConfig::LOGGER_SEVERITY[Rails.logger.level] %>
61 | Rails.application.config.active_record.verbose_query_logs: <%= Rails.application.config.active_record.verbose_query_logs %>
62 | Rails.application.config.consider_all_requests_local: <%= Rails.application.config.consider_all_requests_local %>
63 |
64 | Rails.cache: <%= Rails.application.config.cache_store == :null_store ? '-' : Rails.cache.inspect %>
65 | Rails.application.config.cache_store: <%= Rails.application.config.cache_store %>
66 | Rails.application.config.action_controller.perform_caching: <%= Rails.application.config.action_controller.perform_caching %>
67 | <% end %>
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/lib/runtime_config/conf_param.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RuntimeConfig
4 | class ConfParam
5 | attr_reader :conf, :exec, :opt, :parent
6 |
7 | def initialize(opt, parent, conf, exec = nil)
8 | @opt = opt
9 | @parent = parent
10 | @conf = conf
11 | @exec = exec
12 | end
13 |
14 | def set(value)
15 | @parent.send("#{@conf}=", value) if @parent
16 | @exec.call(value) if @exec
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/runtime_config/middleware.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RuntimeConfig
4 | LOGGER_SEVERITY = %w[debug info warn error fatal unknown].freeze # https://github.com/ruby/ruby/blob/trunk/lib/logger.rb
5 |
6 | class Middleware
7 | def initialize(app, options = {})
8 | @app = app
9 | @params = {
10 | assets_logs: ConfParam.new(:assets_logs, Rails.configuration.assets, :quiet),
11 | catch_errors: ConfParam.new(:catch_errors, Rails.configuration, :consider_all_requests_local),
12 | filter_logs: ConfParam.new(:filter_logs, nil, :filter_logs, ->(value) {
13 | regexp = Regexp.new value, Regexp::IGNORECASE
14 | Rails.logger = ActiveSupport::Logger.new STDOUT
15 | Rails.logger.formatter = proc do |_severity, _time, _progname, msg|
16 | "#{msg}\n" if msg =~ regexp
17 | end
18 | }),
19 | verbose_query_logs: ConfParam.new(:verbose_query_logs, Rails.configuration.active_record, :verbose_query_logs),
20 | }
21 | @options = (options || {}).symbolize_keys
22 | @options[:path] ||= '/dev'
23 |
24 | Rails.configuration.after_initialize do
25 | params_load.each do |k, v|
26 | @params[k.to_sym].set v
27 | end
28 | params_reset
29 | end
30 | end
31 |
32 | def call(env)
33 | req = Rack::Request.new(env)
34 | return @app.call(env) unless req.path == @options[:path]
35 |
36 | restart = false
37 | @actions = []
38 | if LOGGER_SEVERITY.include?((req.params['log'] || '').downcase)
39 | level = "Logger::#{req.params['log'].upcase}"
40 | Rails.logger.level = level.constantize
41 | @actions.push "Logger level set to: #{level}"
42 | end
43 | if req.params.include? 'cache'
44 | Rails.application.load_tasks
45 | Rake::Task['dev:cache'].invoke
46 | @actions.push 'Invoked dev:cache'
47 | end
48 | if req.params.include? 'catch_errors'
49 | params_save(@params[:catch_errors], req.params['catch_errors'] == '1')
50 | @actions.push "catch_errors: #{req.params['catch_errors'] == '1'}"
51 | restart = true
52 | end
53 | if req.params.include? 'cache_clear'
54 | Rails.cache.clear
55 | @actions.push "cache_clear: #{req.params['cache_clear'] == '1'}"
56 | restart = true
57 | end
58 | if req.params.include? 'filter_logs'
59 | params_save(@params[:filter_logs], req.params['filter_logs'].strip)
60 | @actions.push "filter logs: #{req.params['filter_logs'].strip}"
61 | restart = true
62 | end
63 | if req.params.include? 'verbose_query_logs'
64 | params_save(@params[:verbose_query_logs], req.params['verbose_query_logs'] == '1')
65 | @actions.push "verbose_query_logs: #{req.params['verbose_query_logs'] == '1'}"
66 | restart = true
67 | end
68 | restart = true if req.params.include? 'restart'
69 | if restart
70 | restart_server
71 | @actions.push 'Restarting server'
72 | end
73 |
74 | [
75 | 200,
76 | { 'Content-Type' => 'text/html' },
77 | [ERB.new(
78 | File.read(
79 | File.join(File.dirname(__FILE__), 'conf.html.erb')
80 | )
81 | ).result(binding)]
82 | ]
83 | end
84 |
85 | private
86 |
87 | def params_load
88 | JSON.parse Rails.root.join('tmp', '_runtime_config.txt').read
89 | rescue StandardError
90 | {}
91 | end
92 |
93 | def params_reset
94 | Rails.root.join('tmp', '_runtime_config.txt').unlink
95 | rescue StandardError
96 | nil
97 | end
98 |
99 | def params_save(param, value)
100 | Rails.root.join('tmp', '_runtime_config.txt').open('w') do |f|
101 | f.puts({ param.opt => value }.to_json)
102 | end
103 | end
104 |
105 | def restart_server
106 | Rails.application.load_tasks
107 | Rake::Task['restart'].invoke
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/lib/runtime_config/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RuntimeConfig
4 | VERSION = '0.2.2'.freeze
5 | end
6 |
--------------------------------------------------------------------------------
/runtime_config.gemspec:
--------------------------------------------------------------------------------
1 | lib = File.expand_path('lib', __dir__)
2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3 | require 'runtime_config/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.platform = Gem::Platform::RUBY
7 | spec.version = RuntimeConfig::VERSION
8 | spec.name = 'runtime_config'
9 | spec.summary = 'A middleware to change configuration parameters at runtime for Rails 5'
10 |
11 | spec.required_ruby_version = '>= 2.2.2'
12 | spec.add_dependency 'rails', ['>= 5.0', '< 6.0']
13 |
14 | spec.files = Dir['README.md', 'lib/**/*']
15 | spec.require_path = 'lib'
16 | spec.requirements << 'none'
17 |
18 | spec.authors = ['Mattia Roccoberton']
19 | spec.email = ['mat@blocknot.es']
20 | spec.homepage = 'https://github.com/blocknotes/runtime_config'
21 | spec.licenses = ['MIT']
22 | end
23 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nebulab/runtime_config/60ece495798f2ba78f7c9cb8e9f6954de9301ca3/screenshot.png
--------------------------------------------------------------------------------