├── .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 [![Gem Version](https://badge.fury.io/rb/runtime_config.svg)](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 | ![screenshot](screenshot.png) 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 | ![Nebulab](http://nebulab.it/assets/images/public/logo.svg) 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 |
16 |
17 |

Runtime Config

18 |
19 |
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 |
38 | Verbose query logs:
39 | ON (*) 40 | OFF (*) 41 |
42 |
43 |
44 |
45 | Filter logs (using a Regexp, ex. app/views) (*):
46 |
47 | 48 |
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 --------------------------------------------------------------------------------