twirp-on-rails 1.2.0 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 308f1b4a611dd3496ffa026dae81410bffedffb0a2330ef009c4090b2ce71c24
4
- data.tar.gz: dcfba3e14253882f6b7ba0943a89876789fb0d8e68015e397c6143e5417f7d35
3
+ metadata.gz: 4525c9180d9b5ee9316453fefadc8982793f5aee3ebe55db5136fb2970e3b764
4
+ data.tar.gz: 68cb9924021caeefa024616b0d68d28a4a20ec77bf663b658be973fbb9fea86b
5
5
  SHA512:
6
- metadata.gz: 140d92258a215e8de86f641a39aeb7cf6e07034ee3c15e9ae187ed9a8f7d1d45d360a7240ecdc0599069dbe63558de7f2ba9ce15ecf7d7c538968bd96f784596
7
- data.tar.gz: 3dcfdf03ddab84f16a8b8a052439b22e982456a7076c2ac54d2580244c16846b17cf70709a72bfa4a0be42743f139624bf338a97ff454d9fba83455be77eef90
6
+ metadata.gz: 545989b2edaa3b4e78291f6847042e0728053e677b2e144e5b0463bdc5e00acd86fd0ea95ff8de6ae3ff19a12ac99d3941e325a27188a2f2fb0e4b23e787d9f4
7
+ data.tar.gz: 9075fd125690021fa6c1f192f0474fcc53639677cfa4c08904a20e7d31c1fbd487773ac227d42200c3e7e894b4ba9605486b017400d64c6c12bdd8cc455e96fc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.4.0] - 2025-01-28
4
+
5
+ - Fix middleware to not apply where it isn't expected.
6
+
7
+ ## [1.3.0] - 2025-01-20
8
+
9
+ - Add some logging.
3
10
 
4
11
  ## [1.2.0] - 2024-10-04
5
12
 
data/Gemfile CHANGED
@@ -9,7 +9,11 @@ gem "rake"
9
9
 
10
10
  gem "debug"
11
11
  gem "rspec-rails"
12
- gem "sqlite3", "~> 1.4"
13
12
  gem "standard", ">= 1.35.1"
14
13
  gem "standard-performance"
15
14
  gem "standard-rails"
15
+
16
+ # These standard library gems need to be here for with certain Rails 7/Ruby 3.4 combinations. Delete eventually.
17
+ gem "mutex_m"
18
+ gem "bigdecimal"
19
+ gem "drb"
data/README.md CHANGED
@@ -151,6 +151,22 @@ As an Engine, we avoid all the standard Rails middleware. That's nice for simpli
151
151
  Rails.application.config.twirp.middleware = [Rack::Deflater]
152
152
  ```
153
153
 
154
+ ### Logging
155
+
156
+ Our built-in logging outputs the result of each request.
157
+
158
+ You could replace our logger if you want different output:
159
+
160
+ ```ruby
161
+ Rails.application.config.twirp.logger = Rack::CommonLogger
162
+ ```
163
+
164
+ Additionally, you can log the full Twirp response object to help with debugging:
165
+
166
+ ```ruby
167
+ Rails.application.config.twirp.verbose_logging = true
168
+ ```
169
+
154
170
  ## Bonus Features
155
171
 
156
172
  Outside the [Twirp spec](https://twitchtv.github.io/twirp/docs/spec_v7.html), we have some (optional) extra magic. They might be useful to you, but you can easily ignore them too.
@@ -170,6 +186,8 @@ Rails.application.config.twirp.middleware = [
170
186
  ]
171
187
  ```
172
188
 
189
+ Note: The Handler will still be run, but you won't need to send back the response. Make sure your RPC is idempotent! Future versions hope to make it easier to short-circuit expensive parts of the handler.
190
+
173
191
  ## TODO
174
192
 
175
193
  * More docs!
@@ -9,6 +9,15 @@ module Twirp
9
9
  # Where to mount twirp routes. Defaults to /twirp
10
10
  attr_accessor :endpoint
11
11
 
12
+ # Logger to use for Twirp requests. Defaults to Rails.logger
13
+ attr_accessor :logger
14
+
15
+ # Whether to log full Twirp responses. Can be useful for debugging, but can expose sensitive data.
16
+ # Defauts to false
17
+ # Example:
18
+ # Twirp Response: <Twirp::Example::Haberdasher::Hat: inches: 24, color: "Tan", name: "Pork Pie">
19
+ attr_accessor :verbose_logging
20
+
12
21
  # An array of directories to search for *_twirp.rb files
13
22
  # Defaults to ["lib"]
14
23
  attr_accessor :load_paths
@@ -25,6 +34,8 @@ module Twirp
25
34
  @auto_mount = false
26
35
  @endpoint = "/twirp"
27
36
  @load_paths = ["lib"]
37
+ @logger = Logger
38
+ @verbose_logging = false
28
39
  @middleware = []
29
40
  @service_hooks = {}
30
41
  end
@@ -26,8 +26,15 @@ module Twirp
26
26
  app.config.twirp.send(key)
27
27
  end
28
28
 
29
- app.config.twirp.middleware.each do |middleware|
30
- app.config.middleware.use middleware
29
+ # Set up logging
30
+ middleware.use app.config.twirp.logger, ::Rails.logger
31
+ app.config.twirp.middleware.each do |user_middleware|
32
+ middleware.use user_middleware
33
+ end
34
+
35
+ # Load all Twirp files
36
+ app.config.twirp.load_paths.each do |directory|
37
+ ::Rails.root.glob("#{directory}/**/*_twirp.rb").sort.each { |file| require file }
31
38
  end
32
39
  end
33
40
  end
@@ -35,17 +42,29 @@ module Twirp
35
42
  class << self
36
43
  def services
37
44
  if @services.nil?
38
- ::Rails.application.config.twirp.load_paths.each do |directory|
39
- ::Rails.root.glob("#{directory}/**/*_twirp.rb").sort.each { |file| require file }
40
- end
41
-
42
45
  @services = Twirp::Service.subclasses.map(&:new)
43
46
 
44
47
  # Install hooks that may be defined in the config
45
48
  @services.each do |service|
49
+ # Add user-defined hooks
46
50
  ::Rails.application.config.twirp.service_hooks.each do |hook_name, hook|
47
51
  service.send(hook_name, &hook)
48
52
  end
53
+
54
+ # Add our own logging hooks
55
+ service.on_success do |env|
56
+ if ::Rails.application.config.twirp.verbose_logging
57
+ ::Rails.logger.debug { "Twirp Response: #{env[:output].inspect}" }
58
+ end
59
+ end
60
+
61
+ service.on_error do |error, _env|
62
+ ::Rails.logger.debug { "Twirp Response: #{error.inspect}" }
63
+ end
64
+
65
+ service.exception_raised do |exception, _env|
66
+ ::Rails.logger.error { "Twirp Exception (#{exception.class}: #{exception.message})\n#{exception.backtrace.join("\n")}" }
67
+ end
49
68
  end
50
69
  end
51
70
 
@@ -54,11 +73,3 @@ module Twirp
54
73
  end
55
74
  end
56
75
  end
57
-
58
- class Twirp::Service
59
- # Override inspect to show all available RPCs
60
- # This is used when displaying routes.
61
- def inspect
62
- self.class.rpcs.map { |rpc| "#{self.class.name.demodulize.underscore}_handler##{rpc[1][:ruby_method]}" }.join("\n")
63
- end
64
- end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twirp
4
+ module Rails
5
+ class Error < StandardError
6
+ end
7
+ end
8
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "rescuable" # Ruby 2.7 fails without this. Remove eventually.
4
+
3
5
  module Twirp
4
6
  module Rails
5
7
  class Handler
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rack::CommonLogger is nice but we can do better.
4
+ # Rails doesn't use it, but we need to log Twirp requests.
5
+ # Here's an example from Rack::CommonLogger:
6
+ # 127.0.0.1 - - [12/Jan/2025:17:09:49 -0500] "POST /twirp/twirp.example.haberdasher.Haberdasher/MakeHat HTTP/1.0" 200 - 439.0060
7
+ #
8
+ # Rails gives us this:
9
+ # Started POST "/twirp/twirp.example.haberdasher.Haberdasher/MakeHat" for 127.0.0.1 at 2025-01-12 22:48:00 -0500
10
+ # but we also want to know the result of the Twirp call.
11
+ # Here's what this Logger adds:
12
+ # Twirp 200 in 2ms as application/protobuf
13
+ #
14
+
15
+ module Twirp
16
+ module Rails
17
+ class Logger < ::Rack::CommonLogger
18
+ private
19
+
20
+ def log(env, status, response_headers, began_at)
21
+ content_type = response_headers["content-type"].presence
22
+ content_encoding = response_headers["content-encoding"].presence
23
+ @logger.info { "Twirp #{status} in #{duration_in_ms(began_at)}ms#{" as #{content_type}" if content_type}#{" with content-encoding: #{content_encoding}" if content_encoding}" }
24
+ end
25
+
26
+ def duration_in_ms(time)
27
+ ((::Rack::Utils.clock_time - time) * 1000).to_i
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Twirp
4
4
  module Rails
5
- VERSION = "1.2.0"
5
+ VERSION = "1.4.0"
6
6
  end
7
7
  end
data/lib/twirp/rails.rb CHANGED
@@ -1,31 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rails/version"
3
+ require "twirp"
4
+ require "active_support/notifications"
4
5
 
5
- module Twirp
6
- module Rails
7
- class Error < StandardError; end
8
- # Your code goes here...
9
- end
6
+ require "zeitwerk"
7
+ loader = Zeitwerk::Loader.for_gem_extension(Twirp)
8
+ loader.ignore("#{__dir__}/on/rails.rb")
9
+ loader.setup
10
+
11
+ module Twirp::Rails
10
12
  end
11
13
 
12
- require "twirp"
13
- require "active_support/notifications"
14
- require_relative "rails/callbacks"
15
- require_relative "rails/configuration"
16
- require_relative "rails/dispatcher"
17
- require_relative "rails/engine"
18
- require_relative "rails/rescuable"
19
- require_relative "rails/handler"
14
+ loader.eager_load
15
+
16
+ Twirp::Service.class_eval do
17
+ # Override initialize to make handler argument optional.
18
+ # When left nil, we will use our dispatcher.
19
+ alias_method :original_initialize, :initialize
20
+ def initialize(handler = nil)
21
+ handler ||= Twirp::Rails::Dispatcher.new(self.class)
22
+ original_initialize(handler)
23
+ end
20
24
 
21
- module Twirp
22
- class Service
23
- # Override initialize to make handler argument optional.
24
- # When left nil, we will use our dispatcher.
25
- alias_method :original_initialize, :initialize
26
- def initialize(handler = nil)
27
- handler ||= Twirp::Rails::Dispatcher.new(self.class)
28
- original_initialize(handler)
29
- end
25
+ # Override inspect to show all available RPCs
26
+ # This is used when displaying routes.
27
+ def inspect
28
+ self.class.rpcs.map { |rpc| "#{self.class.name.demodulize.underscore}_handler##{rpc[1][:ruby_method]}" }.join("\n")
30
29
  end
31
30
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twirp-on-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Morrison
8
8
  - Darron Schall
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2024-10-04 00:00:00.000000000 Z
11
+ date: 2025-01-28 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rails
@@ -61,7 +60,9 @@ files:
61
60
  - lib/twirp/rails/configuration.rb
62
61
  - lib/twirp/rails/dispatcher.rb
63
62
  - lib/twirp/rails/engine.rb
63
+ - lib/twirp/rails/error.rb
64
64
  - lib/twirp/rails/handler.rb
65
+ - lib/twirp/rails/logger.rb
65
66
  - lib/twirp/rails/rack/conditional_post.rb
66
67
  - lib/twirp/rails/rescuable.rb
67
68
  - lib/twirp/rails/version.rb
@@ -74,7 +75,6 @@ metadata:
74
75
  homepage_uri: https://github.com/collectiveidea/twirp-rails
75
76
  source_code_uri: https://github.com/collectiveidea/twirp-rails
76
77
  changelog_uri: https://github.com/collectiveidea/twirp-rails/blob/main/CHANGELOG.md
77
- post_install_message:
78
78
  rdoc_options: []
79
79
  require_paths:
80
80
  - lib
@@ -89,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
89
  - !ruby/object:Gem::Version
90
90
  version: '0'
91
91
  requirements: []
92
- rubygems_version: 3.5.16
93
- signing_key:
92
+ rubygems_version: 3.6.2
94
93
  specification_version: 4
95
94
  summary: Use Twirp RPC with Rails
96
95
  test_files: []