dial 0.2.2 → 0.2.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0cde97fba38cb1cf2eb253b525513f6e531f1e5b917eda64ebcae004e608fb6
4
- data.tar.gz: 97e745b90b4bb7bfaae7ac64c8bb26325fe7927182877d316bf503b46e9712b8
3
+ metadata.gz: c21506e3c567f79104e394c2ab2fc1c29d433ef9e9e27f201d8fe3e6bdc2227f
4
+ data.tar.gz: 5a8a65aa3fcdd52d59508ebac6bb12bfa156d6f577793f7c05ac8eb14d5409b3
5
5
  SHA512:
6
- metadata.gz: b5efaaef24b3fa42ddbb732c70f0af3ba9d4240ddc1a4b520304a68b020ec88c5b1751e92c79b0d8c02fe20b521e73bf27adeefd9af5293a03bd33aeb8dc17e7
7
- data.tar.gz: b9b65a06f97a67fd003e73b6b8337d0a9800fb78dc02b095f4878e4b3062a69915e4022e270669c870df3514d45d57c15132f45bf70c4b559eeacd9b3d050d26
6
+ metadata.gz: c20d37444e2d186490ea5e75d30c2a6940d56adbff21c91d01a448d96b4b7b6baa0ead5da5305a1398dcd8e916f5a9dab60c2d0d101404a1377a998fe2d2d91c
7
+ data.tar.gz: 525eef47d8136c0c34ddc0c8460637370e87ea13d43c8d83e1c3b4c9dc992b192414f570a0258a9ffd16206a235611a5450982c3a9cf474f12552c16ef646613
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.4] - 2025-03-03
4
+
5
+ - Add configuration option for setting script CSP nonce (thanks @matthaigh27)
6
+
7
+ ## [0.2.3] - 2025-02-28
8
+
9
+ - Add configuration API
10
+ - Make default prosopite ignore queries case insensitive
11
+
3
12
  ## [0.2.2] - 2025-02-27
4
13
 
5
14
  - Increase default vernier allocation interval from 200 to 20k
data/README.md CHANGED
@@ -33,6 +33,27 @@ bundle install
33
33
  mount Dial::Engine, at: "/" if Rails.env.development?
34
34
  ```
35
35
 
36
+ 4. (Optional) Configure the gem in an initializer:
37
+
38
+ ```ruby
39
+ # config/initializers/dial.rb
40
+
41
+ Dial.configure do |config|
42
+ config.vernier_interval = 100
43
+ config.vernier_allocation_interval = 10_000
44
+ config.prosopite_ignore_queries += [/pg_sleep/i]
45
+ end
46
+ ```
47
+
48
+ ### Options
49
+
50
+ Option | Description | Default
51
+ - | - | -
52
+ `vernier_interval` | Sets the `interval` option for vernier. | `200`
53
+ `vernier_allocation_interval` | Sets the `allocation_interval` option for vernier. | `20_000`
54
+ `prosopite_ignore_queries` | Sets the `ignore_queries` option for prosopite. | `[/schema_migrations/i]`
55
+ `content_security_policy_nonce` | Sets the content security policy nonce to use when inserting Dial's script. Can be a string, or a Proc which receives `env` and response `headers` as arguments and returns the nonce. | Rails generated nonce or `nil`
56
+
36
57
  ## Development
37
58
 
38
59
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dial
4
+ def self.configure
5
+ yield _configuration
6
+ end
7
+
8
+ def self._configuration
9
+ @_configuration ||= Configuration.new
10
+ end
11
+
12
+ class Configuration
13
+ def initialize
14
+ @options = {
15
+ vernier_interval: VERNIER_INTERVAL,
16
+ vernier_allocation_interval: VERNIER_ALLOCATION_INTERVAL,
17
+ prosopite_ignore_queries: PROSOPITE_IGNORE_QUERIES,
18
+ content_security_policy_nonce: -> (env, _headers) { env[NONCE] || "" },
19
+ }
20
+
21
+ @options.keys.each do |key|
22
+ define_singleton_method key do
23
+ @options[key]
24
+ end
25
+
26
+ define_singleton_method "#{key}=" do |value|
27
+ @options[key] = value
28
+ end
29
+ end
30
+ end
31
+
32
+ def freeze
33
+ @options.freeze
34
+ super
35
+ end
36
+ end
37
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rack"
4
+ require "action_dispatch"
4
5
 
5
6
  require_relative "version"
6
7
 
@@ -10,6 +11,7 @@ module Dial
10
11
  HTTP_ACCEPT = "HTTP_ACCEPT"
11
12
  CONTENT_TYPE = ::Rack::CONTENT_TYPE
12
13
  CONTENT_LENGTH = ::Rack::CONTENT_LENGTH
14
+ NONCE = ::ActionDispatch::ContentSecurityPolicy::Request::NONCE
13
15
  REQUEST_TIMING = "dial_request_timing"
14
16
 
15
17
  FILE_STALE_SECONDS = 60 * 60
@@ -18,7 +20,7 @@ module Dial
18
20
  VERNIER_ALLOCATION_INTERVAL = 20_000
19
21
  VERNIER_PROFILE_OUT_RELATIVE_DIRNAME = "tmp/dial/profiles"
20
22
 
21
- PROSOPITE_IGNORE_QUERIES = [/schema_migrations/].freeze
23
+ PROSOPITE_IGNORE_QUERIES = [/schema_migrations/i].freeze
22
24
  PROSOPITE_LOG_RELATIVE_DIRNAME = "log/dial"
23
25
  PROSOPITE_LOG_FILENAME = "#{Util.uuid}_prosopite_#{PROGRAM_ID}.log".freeze
24
26
  end
@@ -5,7 +5,7 @@ require "uri"
5
5
  module Dial
6
6
  class Panel
7
7
  class << self
8
- def html env, profile_out_filename, query_logs, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing
8
+ def html env, headers, profile_out_filename, query_logs, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing
9
9
  <<~HTML
10
10
  <style>#{style}</style>
11
11
 
@@ -69,7 +69,9 @@ module Dial
69
69
  </div>
70
70
  </div>
71
71
 
72
- <script>#{script}</script>
72
+ <script nonce="#{configured_nonce env, headers}">
73
+ #{script}
74
+ </script>
73
75
  HTML
74
76
  end
75
77
 
@@ -171,7 +173,7 @@ module Dial
171
173
  end
172
174
 
173
175
  def formatted_rails_route_info env
174
- rails_route_info = begin
176
+ begin
175
177
  ::Rails.application.routes.recognize_path env[::Rack::PATH_INFO], method: env[::Rack::REQUEST_METHOD]
176
178
  rescue ::ActionController::RoutingError
177
179
  {}
@@ -253,6 +255,15 @@ module Dial
253
255
  HTML
254
256
  end.join
255
257
  end
258
+
259
+ def configured_nonce env, headers
260
+ config_nonce = Dial._configuration.content_security_policy_nonce
261
+ if config_nonce.instance_of? Proc
262
+ config_nonce.call env, headers
263
+ else
264
+ config_nonce
265
+ end
266
+ end
256
267
  end
257
268
  end
258
269
  end
@@ -27,7 +27,7 @@ module Dial
27
27
  def stat_diff before, after, no_diff: []
28
28
  after.except(*no_diff).each_with_object({}) do |(key, value), diff|
29
29
  diff[key] = value - before[key]
30
- end.merge after.slice *no_diff
30
+ end.merge after.slice(*no_diff)
31
31
  end
32
32
  end
33
33
  end
@@ -28,7 +28,9 @@ module Dial
28
28
 
29
29
  status, headers, rack_body, ruby_vm_stat, gc_stat, gc_stat_heap, vernier_result = nil
30
30
  ::Prosopite.scan do
31
- vernier_result = ::Vernier.profile interval: VERNIER_INTERVAL, allocation_interval: VERNIER_ALLOCATION_INTERVAL, hooks: [:memory_usage, :rails] do
31
+ vernier_result = ::Vernier.profile interval: Dial._configuration.vernier_interval, \
32
+ allocation_interval: Dial._configuration.vernier_allocation_interval, \
33
+ hooks: [:memory_usage, :rails] do
32
34
  ruby_vm_stat, gc_stat, gc_stat_heap = with_diffed_ruby_stats do
33
35
  status, headers, rack_body = @app.call env
34
36
  end
@@ -49,10 +51,12 @@ module Dial
49
51
  body = String.new.tap do |str|
50
52
  rack_body.each { |chunk| str << chunk }
51
53
  rack_body.close if rack_body.respond_to? :close
52
- end.sub "</body>", <<~HTML
53
- #{Panel.html env, profile_out_filename, query_logs, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing}
54
- </body>
55
- HTML
54
+
55
+ str.sub! "</body>", <<~HTML
56
+ #{Panel.html env, headers, profile_out_filename, query_logs, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing}
57
+ </body>
58
+ HTML
59
+ end
56
60
 
57
61
  headers[CONTENT_LENGTH] = body.bytesize.to_s
58
62
 
@@ -84,7 +88,7 @@ module Dial
84
88
 
85
89
  def clear_query_logs!
86
90
  [].tap do |query_logs|
87
- File.open("#{query_log_dir_pathname}/#{PROSOPITE_LOG_FILENAME}", "r+") do |file|
91
+ File.open "#{query_log_dir_pathname}/#{PROSOPITE_LOG_FILENAME}", "r+" do |file|
88
92
  entry = section = count = nil
89
93
  file.each_line do |line|
90
94
  entry, section, count = process_query_log_line line, entry, section, count
data/lib/dial/railtie.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
+ require "active_record"
4
5
  require "prosopite"
5
6
 
6
7
  require_relative "middleware"
@@ -8,25 +9,25 @@ require_relative "prosopite_logger"
8
9
 
9
10
  module Dial
10
11
  class Railtie < ::Rails::Railtie
11
- initializer "dial.use_middleware" do |app|
12
+ initializer "dial.use_middleware", after: :load_config_initializers do |app|
12
13
  app.middleware.insert_before 0, Middleware
13
14
  end
14
15
 
15
- initializer "dial.set_up_vernier" do |app|
16
+ initializer "dial.set_up_vernier", after: :load_config_initializers do |app|
16
17
  app.config.after_initialize do
17
18
  FileUtils.mkdir_p ::Rails.root.join VERNIER_PROFILE_OUT_RELATIVE_DIRNAME
18
19
  end
19
20
  end
20
21
 
21
- initializer "dial.clean_up_vernier_profile_out_files" do |app|
22
+ initializer "dial.clean_up_vernier_profile_out_files", after: :load_config_initializers do |app|
22
23
  stale_files("#{profile_out_dir_pathname}/*.json.gz").each do |profile_out_file|
23
24
  File.delete profile_out_file rescue nil
24
25
  end
25
26
  end
26
27
 
27
- initializer "dial.set_up_prosopite" do |app|
28
+ initializer "dial.set_up_prosopite", after: :load_config_initializers do |app|
28
29
  app.config.after_initialize do
29
- if ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
30
+ if ::ActiveRecord::Base.configurations.configurations.any? { |config| config.adapter == "postgresql" }
30
31
  require "pg_query"
31
32
  end
32
33
 
@@ -34,17 +35,23 @@ module Dial
34
35
  FileUtils.mkdir_p File.dirname prosopite_log_pathname
35
36
  FileUtils.touch prosopite_log_pathname
36
37
  ::Prosopite.custom_logger = ProsopiteLogger.new prosopite_log_pathname
37
-
38
- ::Prosopite.ignore_queries = PROSOPITE_IGNORE_QUERIES
39
38
  end
40
39
  end
41
40
 
42
- initializer "dial.clean_up_prosopite_log_files" do |app|
41
+ initializer "dial.clean_up_prosopite_log_files", after: :load_config_initializers do |app|
43
42
  stale_files("#{query_log_dir_pathname}/*.log").each do |query_log_file|
44
43
  File.delete query_log_file rescue nil
45
44
  end
46
45
  end
47
46
 
47
+ initializer "dial.setup", after: :load_config_initializers do |app|
48
+ app.config.after_initialize do
49
+ Dial._configuration.freeze
50
+
51
+ ::Prosopite.ignore_queries = Dial._configuration.prosopite_ignore_queries
52
+ end
53
+ end
54
+
48
55
  private
49
56
 
50
57
  def stale_files glob_pattern
data/lib/dial/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dial
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.4"
5
5
  end
data/lib/dial.rb CHANGED
@@ -3,5 +3,7 @@
3
3
  require_relative "dial/util"
4
4
  require_relative "dial/constants"
5
5
 
6
+ require_relative "dial/configuration"
7
+
6
8
  require_relative "dial/railtie"
7
9
  require_relative "dial/engine"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dial
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-27 00:00:00.000000000 Z
10
+ date: 2025-03-03 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: railties
@@ -123,6 +123,7 @@ files:
123
123
  - README.md
124
124
  - dial.gemspec
125
125
  - lib/dial.rb
126
+ - lib/dial/configuration.rb
126
127
  - lib/dial/constants.rb
127
128
  - lib/dial/engine.rb
128
129
  - lib/dial/engine/routes.rb