From c58471b6f386d494a8bc19850b470f86d87d018a Mon Sep 17 00:00:00 2001 From: Daniel Morrison Date: Thu, 12 Sep 2024 09:38:07 -0400 Subject: [PATCH 1/5] Look for handlers in both the fully-qualified namespace, or root namespace. Many projects only ever use services in one namespace, but we'll respect them if you want them. --- README.md | 21 ++++++++++++++++- lib/twirp/rails/dispatcher.rb | 8 ++++++- .../twirp/example/cobbler/cobbler_handler.rb | 21 +++++++++++++++++ .../lib/twirp/example/cobbler/cobbler_pb.rb | 20 ++++++++++++++++ .../twirp/example/cobbler/cobbler_twirp.rb | 23 +++++++++++++++++++ .../proto/twirp/example/cobbler/cobbler.proto | 23 +++++++++++++++++++ 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 spec/rails_app/app/handlers/twirp/example/cobbler/cobbler_handler.rb create mode 100644 spec/rails_app/lib/twirp/example/cobbler/cobbler_pb.rb create mode 100644 spec/rails_app/lib/twirp/example/cobbler/cobbler_twirp.rb create mode 100644 spec/rails_app/proto/twirp/example/cobbler/cobbler.proto diff --git a/README.md b/README.md index 54d2d60..6d42ec9 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,26 @@ end Each handler method should return the appropriate Protobuf, or a `Twirp::Error`. -TODO: Give more examples of both +#### Packages and Namespacing + +Handlers can live in directories that reflect the service's package. For example, `haberdasher.proto` defines: + +```protobuf +package twirp.example.haberdasher; +``` + +You can use the full path, or because many projects have only one namespace, we also let you skip the namespace for simplicity: + +We look for the handler in either location: + +`app/handlers/twirp/example/haberdasher/haberdasher_service_handler.rb` defines `Twirp::Example::Haberdasher::HaberdasherServiceHandler` + +or + +`app/handlers/haberdasher_service_handler.rb` defines `HaberdasherServiceHandler` + + +TODO: Give more examples of handlers ### Familiar Callbacks diff --git a/lib/twirp/rails/dispatcher.rb b/lib/twirp/rails/dispatcher.rb index 514114e..f51425b 100644 --- a/lib/twirp/rails/dispatcher.rb +++ b/lib/twirp/rails/dispatcher.rb @@ -4,7 +4,13 @@ module Twirp module Rails class Dispatcher def initialize(service_class) - @service_handler = "#{service_class.service_name}Handler".constantize + # Check for a handler in the service's namespace, or in the root namespace + # e.g. Twirp::Example::Cobbler::CobblerHandler or ::CobblerHandler + @service_handler = if Object.const_defined?("#{service_class.module_parent}::#{service_class.service_name}Handler") + "#{service_class.module_parent}::#{service_class.service_name}Handler".constantize + else + "#{service_class.service_name}Handler".constantize + end end def respond_to_missing?(method, *) diff --git a/spec/rails_app/app/handlers/twirp/example/cobbler/cobbler_handler.rb b/spec/rails_app/app/handlers/twirp/example/cobbler/cobbler_handler.rb new file mode 100644 index 0000000..69581bf --- /dev/null +++ b/spec/rails_app/app/handlers/twirp/example/cobbler/cobbler_handler.rb @@ -0,0 +1,21 @@ +module Twirp + module Example + module Cobbler + class CobblerHandler < Twirp::Rails::Handler + def make_shoe + # We can return a Twirp::Error when appropriate + if request.inches < 12 + return Twirp::Error.invalid_argument("is too small", argument: "inches") + end + + # Build the reponse + Twirp::Example::Cobbler::Shoe.new( + inches: request.inches, + name: "Pork Pie", + color: "Tan" + ) + end + end + end + end +end diff --git a/spec/rails_app/lib/twirp/example/cobbler/cobbler_pb.rb b/spec/rails_app/lib/twirp/example/cobbler/cobbler_pb.rb new file mode 100644 index 0000000..9e0b3d7 --- /dev/null +++ b/spec/rails_app/lib/twirp/example/cobbler/cobbler_pb.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: twirp/example/cobbler/cobbler.proto + +require 'google/protobuf' + + +descriptor_data = "\n#twirp/example/cobbler/cobbler.proto\x12\x15twirp.example.cobbler\"\x16\n\x04Size\x12\x0e\n\x06inches\x18\x01 \x01(\x05\"3\n\x04Shoe\x12\x0e\n\x06inches\x18\x01 \x01(\x05\x12\r\n\x05\x63olor\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t2O\n\x07\x43obbler\x12\x44\n\x08MakeShoe\x12\x1b.twirp.example.cobbler.Size\x1a\x1b.twirp.example.cobbler.Shoeb\x06proto3" + +pool = Google::Protobuf::DescriptorPool.generated_pool +pool.add_serialized_file(descriptor_data) + +module Twirp + module Example + module Cobbler + Size = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("twirp.example.cobbler.Size").msgclass + Shoe = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("twirp.example.cobbler.Shoe").msgclass + end + end +end diff --git a/spec/rails_app/lib/twirp/example/cobbler/cobbler_twirp.rb b/spec/rails_app/lib/twirp/example/cobbler/cobbler_twirp.rb new file mode 100644 index 0000000..c05e46c --- /dev/null +++ b/spec/rails_app/lib/twirp/example/cobbler/cobbler_twirp.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Generated by the protoc-gen-twirp_ruby gem v1.1.1. DO NOT EDIT! +# source: twirp/example/cobbler/cobbler.proto + +require "twirp" +require_relative "cobbler_pb" + +module Twirp + module Example + module Cobbler + class CobblerService < ::Twirp::Service + package "twirp.example.cobbler" + service "Cobbler" + rpc :MakeShoe, Size, Shoe, ruby_method: :make_shoe + end + + class CobblerClient < ::Twirp::Client + client_for CobblerService + end + end + end +end diff --git a/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto b/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto new file mode 100644 index 0000000..36b2048 --- /dev/null +++ b/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto @@ -0,0 +1,23 @@ +// A close copy of haberdasher.proto +// We're putting this in /proto/twirp/example/ so that we can test namespace handling +syntax = "proto3"; + +package twirp.example.cobbler; + +// Cobbler service makes shoes for clients. +service Cobbler { + // MakeShoe produces a shoe of mysterious, randomly-selected color! + rpc MakeShoe(Size) returns (Shoe); +} + +// Size of a Shoe, in inches. +message Size { + int32 inches = 1; // must be > 0 +} + +// A Shoe is a piece of footwear made by a Cobbler. +message Shoe { + int32 inches = 1; + string color = 2; // anything but "invisible" + string name = 3; // i.e. "oxford" +} From 0e1157bf9675f1e7e80858c0dbb64b176c2129ca Mon Sep 17 00:00:00 2001 From: Daniel Morrison Date: Thu, 12 Sep 2024 09:43:25 -0400 Subject: [PATCH 2/5] Have standard ignore generated files --- .standard.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.standard.yml b/.standard.yml index 8de184f..efc21a1 100644 --- a/.standard.yml +++ b/.standard.yml @@ -2,3 +2,6 @@ ruby_version: 2.7 plugins: - standard-performance - standard-rails +ignore: + - '**/*_pb.rb' + - '**/*_twirp.rb' From 1adcf0fb390ed4b18c54a85ccb7e9f9ed78b1ac6 Mon Sep 17 00:00:00 2001 From: Daniel Morrison Date: Thu, 12 Sep 2024 09:45:57 -0400 Subject: [PATCH 3/5] Regenerate haberdasher files --- spec/rails_app/lib/haberdasher_pb.rb | 22 ++++++++-------------- spec/rails_app/lib/haberdasher_twirp.rb | 6 +++++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/spec/rails_app/lib/haberdasher_pb.rb b/spec/rails_app/lib/haberdasher_pb.rb index 37d5652..ec73d3e 100644 --- a/spec/rails_app/lib/haberdasher_pb.rb +++ b/spec/rails_app/lib/haberdasher_pb.rb @@ -1,20 +1,14 @@ +# frozen_string_literal: true # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: lib/haberdasher.proto +# source: haberdasher.proto -require "google/protobuf" +require 'google/protobuf' -Google::Protobuf::DescriptorPool.generated_pool.build do - add_file("lib/haberdasher.proto", syntax: :proto3) do - add_message "twirp.example.haberdasher.Size" do - optional :inches, :int32, 1 - end - add_message "twirp.example.haberdasher.Hat" do - optional :inches, :int32, 1 - optional :color, :string, 2 - optional :name, :string, 3 - end - end -end + +descriptor_data = "\n\x11haberdasher.proto\x12\x19twirp.example.haberdasher\"\x16\n\x04Size\x12\x0e\n\x06inches\x18\x01 \x01(\x05\"2\n\x03Hat\x12\x0e\n\x06inches\x18\x01 \x01(\x05\x12\r\n\x05\x63olor\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t2Y\n\x0bHaberdasher\x12J\n\x07MakeHat\x12\x1f.twirp.example.haberdasher.Size\x1a\x1e.twirp.example.haberdasher.Hatb\x06proto3" + +pool = Google::Protobuf::DescriptorPool.generated_pool +pool.add_serialized_file(descriptor_data) module Twirp module Example diff --git a/spec/rails_app/lib/haberdasher_twirp.rb b/spec/rails_app/lib/haberdasher_twirp.rb index 442a036..588d5aa 100644 --- a/spec/rails_app/lib/haberdasher_twirp.rb +++ b/spec/rails_app/lib/haberdasher_twirp.rb @@ -1,4 +1,8 @@ -# Code generated by protoc-gen-twirp_ruby 1.10.0, DO NOT EDIT. +# frozen_string_literal: true + +# Generated by the protoc-gen-twirp_ruby gem v1.1.1. DO NOT EDIT! +# source: haberdasher.proto + require "twirp" require_relative "haberdasher_pb" From 97ae01b3cf50717165b206a7d737279c76a1a841 Mon Sep 17 00:00:00 2001 From: Daniel Morrison Date: Thu, 12 Sep 2024 09:59:58 -0400 Subject: [PATCH 4/5] Fix comment --- spec/rails_app/proto/twirp/example/cobbler/cobbler.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto b/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto index 36b2048..c820e2d 100644 --- a/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto +++ b/spec/rails_app/proto/twirp/example/cobbler/cobbler.proto @@ -1,5 +1,5 @@ // A close copy of haberdasher.proto -// We're putting this in /proto/twirp/example/ so that we can test namespace handling +// We're putting this in /proto/twirp/example/cobbler/ so that we can test namespace handling syntax = "proto3"; package twirp.example.cobbler; From 8c387b0d4ef1ffc902412b86cc804552c7950ff7 Mon Sep 17 00:00:00 2001 From: Daniel Morrison Date: Thu, 12 Sep 2024 10:09:17 -0400 Subject: [PATCH 5/5] Specify minimum standard version to fix Ruby 2.7 builds --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c4bf4bc..e15d092 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,6 @@ gem "rake" gem "debug" gem "rspec-rails" gem "sqlite3", "~> 1.4" -gem "standard" +gem "standard", ">= 1.35.1" gem "standard-performance" gem "standard-rails"