diff --git a/.toys/cucumber.rb b/.toys/cucumber.rb index 5d50066..a477b82 100644 --- a/.toys/cucumber.rb +++ b/.toys/cucumber.rb @@ -20,8 +20,8 @@ def run end def setup_features - remote_features = git_cache.find "https://github.com/cloudevents/conformance", path: "features" - local_features = File.join context_directory, "features", "conformance" - rm_rf local_features - cp_r remote_features, local_features + remote_features = git_cache.find("https://github.com/cloudevents/conformance", path: "features") + local_features = File.join(context_directory, "features", "conformance") + rm_rf(local_features) + cp_r(remote_features, local_features) end diff --git a/Gemfile b/Gemfile index 974f566..a1f3401 100644 --- a/Gemfile +++ b/Gemfile @@ -4,11 +4,13 @@ source "https://rubygems.org" gemspec gem "cucumber", "~> 9.2" +gem "logger", "~> 1.4" gem "minitest", "~> 5.25" gem "minitest-focus", "~> 1.4" gem "minitest-rg", "~> 5.3" gem "rack", "~> 3.2" gem "redcarpet", "~> 3.6" unless ::RUBY_PLATFORM == "java" gem "rubocop", "~> 1.81" +gem "toys-core", "~> 0.17" gem "webrick", "~> 1.9" gem "yard", "~> 0.9.37" diff --git a/README.md b/README.md index 8aa31c4..2217b2b 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ Features: JSON Batch Format. * Support for sending and receiving CloudEvents via HTTP Bindings. * Supports the [CloudEvent 0.3](https://github.com/cloudevents/spec/tree/v0.3) - and [CloudEvents 1.0](https://github.com/cloudevents/spec/tree/v1.0.1) + and [CloudEvents 1.0](https://github.com/cloudevents/spec/tree/v1.0.2) specifications. * Extensible to additional formats and protocol bindings, and future specification versions. - * Compatible with Ruby 2.5 or later, or JRuby 9.2.x or later. No runtime gem + * Compatible with Ruby 2.7 or later, or JRuby 9.2.x or later. No runtime gem dependencies. ## Quickstart @@ -35,8 +35,10 @@ A simple [Sinatra](https://sinatrarb.com) app that receives CloudEvents: ```ruby # examples/server/Gemfile source "https://rubygems.org" -gem "cloud_events", "~> 0.6" -gem "sinatra", "~> 2.0" +gem "cloud_events", "~> 0.8" +gem "sinatra", "~> 4.0" +gem "rackup" +gem "puma" ``` ```ruby @@ -46,9 +48,9 @@ require "cloud_events" cloud_events_http = CloudEvents::HttpBinding.default -post "/" do - event = cloud_events_http.decode_event request.env - logger.info "Received CloudEvent: #{event.to_h}" +post("/") do + event = cloud_events_http.decode_event(request.env) + logger.info("Received CloudEvent: #{event.to_h}") end ``` @@ -59,7 +61,7 @@ A simple Ruby script that sends a CloudEvent: ```ruby # examples/client/Gemfile source "https://rubygems.org" -gem "cloud_events", "~> 0.6" +gem "cloud_events", "~> 0.8" ``` ```ruby @@ -69,17 +71,18 @@ require "net/http" require "uri" data = { message: "Hello, CloudEvents!" } -event = CloudEvents::Event.create \ +event = CloudEvents::Event.create( spec_version: "1.0", id: "1234-1234-1234", source: "/mycontext", type: "com.example.someevent", data_content_type: "application/json", data: data +) cloud_events_http = CloudEvents::HttpBinding.default -headers, body = cloud_events_http.encode_event event -Net::HTTP.post URI("http://localhost:4567"), body, headers +headers, body = cloud_events_http.encode_event(event) +Net::HTTP.post(URI("http://localhost:4567"), body, headers) ``` ### Putting it together @@ -108,7 +111,8 @@ Hit `CTRL+C` to stop the server. ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/cloudevents/sdk-ruby. +Bug reports and pull requests are welcome on GitHub at +https://github.com/cloudevents/sdk-ruby. ### Development @@ -145,11 +149,15 @@ toys test --help ### Code style -Ruby code in this library generally follows the -[Google Ruby Style Guide](https://github.com/googleapis/ruby-style), which is -based on "Seattle Style" Ruby. +Ruby code style is enforced by Rubocop rules. We've left the configuration +largely on the Rubocop defaults, with a few exceptions, notably: -Style is enforced by Rubocop rules. You can run rubocop directly using the +* We prefer double-quoted strings rather than single-quoted strings. +* We prefer trailing commas in multi-line array and hash literals. +* Line length limit is 120 +* We've loosened a few additional checks that we felt were not helpful. + +You can run rubocop directly using the `rubocop` binary: ```sh diff --git a/RELEASING.md b/RELEASING.md index de182ae..d2902b8 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -22,4 +22,3 @@ locally. See `toys release request --help` and `toys release perform --help` for more information. If a release fails, you may need to delete the release tag before retrying. - diff --git a/examples/client/Gemfile b/examples/client/Gemfile index d65f423..46a4ebf 100644 --- a/examples/client/Gemfile +++ b/examples/client/Gemfile @@ -1,4 +1,5 @@ # frozen_string_literal: true source "https://rubygems.org" + gem "cloud_events", path: "../.." diff --git a/examples/server/Gemfile b/examples/server/Gemfile index a700603..f3f9867 100644 --- a/examples/server/Gemfile +++ b/examples/server/Gemfile @@ -1,6 +1,8 @@ # frozen_string_literal: true source "https://rubygems.org" + gem "cloud_events", path: "../.." -gem "sinatra", "~> 2.0" -gem "webrick" +gem "puma" +gem "rackup" +gem "sinatra", "~> 4.0" diff --git a/examples/server/app.rb b/examples/server/app.rb index 33a5f8b..1075c0d 100644 --- a/examples/server/app.rb +++ b/examples/server/app.rb @@ -5,7 +5,7 @@ cloud_events_http = CloudEvents::HttpBinding.default -post "/" do +post("/") do event = cloud_events_http.decode_event(request.env) logger.info("Received CloudEvent: #{event.to_h}") end diff --git a/test/test_examples.rb b/test/test_examples.rb new file mode 100644 index 0000000..e2f10f5 --- /dev/null +++ b/test/test_examples.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require_relative "helper" + +require "io/wait" + +require "toys/compat" +require "toys/utils/exec" + +describe "examples" do + let(:exec_util) { Toys::Utils::Exec.new } + let(:examples_dir) { File.join(File.dirname(__dir__), "examples") } + let(:client_dir) { File.join(examples_dir, "client") } + let(:server_dir) { File.join(examples_dir, "server") } + + def expect_read(io, content, timeout) + deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout + received = String.new + loop do + time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + assert(time < deadline, "Did not receive content in time: #{content}") + next unless io.wait_readable((deadline - time).ceil) + received.concat(io.readpartial(1024)) + return if received.include?(content) + end + end + + it "sends and receives an event" do + skip if Toys::Compat.jruby? + skip if Toys::Compat.truffleruby? + skip if Toys::Compat.windows? + Bundler.with_unbundled_env do + assert(exec_util.exec(["bundle", "install"], out: :null, chdir: server_dir).success?, "server bundle failed") + assert(exec_util.exec(["bundle", "install"], out: :null, chdir: client_dir).success?, "client bundle failed") + exec_util.exec(["bundle", "exec", "ruby", "app.rb"], chdir: server_dir, + in: :controller, out: :controller, err: :controller) do |server_control| + expect_read(server_control.out, "* Listening on http", 5) + client_result = exec_util.exec(["bundle", "exec", "ruby", "send.rb"], chdir: client_dir) + assert(client_result.success?) + expect_read(server_control.err, "Hello, CloudEvents!", 5) + ensure + server_control.kill("TERM") + end + end + end +end