From ea59a8571b0235d204544aa80c707a2e6a26910e Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 11 May 2024 08:11:24 +0900 Subject: [PATCH] Modernize gem. (#3) --- .editorconfig | 9 ++ .github/workflows/coverage.yaml | 57 +++++++++++ .github/workflows/documentation.yaml | 58 ++++++++++++ .github/workflows/test-external.yaml | 36 +++++++ .github/workflows/test.yaml | 50 ++++++++++ .gitignore | 7 +- .mailmap | 1 + CHANGELOG.md | 5 - Gemfile | 5 - LICENSE | 18 ---- Rakefile | 14 --- config/sus.rb | 8 ++ examples/echo_server/client.rb | 34 ++++--- examples/echo_server/config.ru | 36 +++---- examples/interactive_browser/client.rb | 60 ++++++------ examples/interactive_browser/config.ru | 126 ++++++++++++------------- gems.rb | 26 +++++ lib/roda/plugins/websockets.rb | 89 ++++++----------- lib/roda/websockets.rb | 6 ++ lib/roda/websockets/version.rb | 10 ++ license.md | 23 +++++ pkg/roda-websockets-0.1.0.gem | Bin 6144 -> 0 bytes README.md => readme.md | 10 +- release.cert | 28 ++++++ roda-websockets.gemspec | 43 +++++---- spec/roda-websockets_spec.rb | 85 ----------------- test/roda/websockets.rb | 55 +++++++++++ 27 files changed, 568 insertions(+), 331 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/coverage.yaml create mode 100644 .github/workflows/documentation.yaml create mode 100644 .github/workflows/test-external.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 .mailmap delete mode 100644 CHANGELOG.md delete mode 100644 Gemfile delete mode 100644 LICENSE delete mode 100644 Rakefile create mode 100644 config/sus.rb create mode 100644 gems.rb create mode 100644 lib/roda/websockets.rb create mode 100644 lib/roda/websockets/version.rb create mode 100644 license.md delete mode 100644 pkg/roda-websockets-0.1.0.gem rename README.md => readme.md (82%) create mode 100644 release.cert delete mode 100644 spec/roda-websockets_spec.rb create mode 100644 test/roda/websockets.rb diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a6e7d26 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = tab +indent_size = 2 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 0000000..68adbf2 --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,57 @@ +name: Coverage + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + COVERAGE: PartialSummary + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.3" + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 5 + run: bundle exec bake test + + - uses: actions/upload-artifact@v3 + with: + name: coverage-${{matrix.os}}-${{matrix.ruby}} + path: .covered.db + + validate: + needs: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - uses: actions/download-artifact@v3 + + - name: Validate coverage + timeout-minutes: 5 + run: bundle exec bake covered:validate --paths */.covered.db \; diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 0000000..f5f553a --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,58 @@ +name: Documentation + +on: + push: + branches: + - main + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages: +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment: +concurrency: + group: "pages" + cancel-in-progress: true + +env: + CONSOLE_OUTPUT: XTerm + BUNDLE_WITH: maintenance + +jobs: + generate: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Installing packages + run: sudo apt-get install wget + + - name: Generate documentation + timeout-minutes: 5 + run: bundle exec bake utopia:project:static --force no + + - name: Upload documentation artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs + + deploy: + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{steps.deployment.outputs.page_url}} + + needs: generate + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test-external.yaml b/.github/workflows/test-external.yaml new file mode 100644 index 0000000..21898f5 --- /dev/null +++ b/.github/workflows/test-external.yaml @@ -0,0 +1,36 @@ +name: Test External + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.1" + - "3.2" + - "3.3" + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 10 + run: bundle exec bake test:external diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..0769a98 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,50 @@ +name: Test + +on: [push, pull_request] + +permissions: + contents: read + +env: + CONSOLE_OUTPUT: XTerm + +jobs: + test: + name: ${{matrix.ruby}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + continue-on-error: ${{matrix.experimental}} + + strategy: + matrix: + os: + - ubuntu + - macos + + ruby: + - "3.1" + - "3.2" + - "3.3" + + experimental: [false] + + include: + - os: ubuntu + ruby: truffleruby + experimental: true + - os: ubuntu + ruby: jruby + experimental: true + - os: ubuntu + ruby: head + experimental: true + + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby}} + bundler-cache: true + + - name: Run tests + timeout-minutes: 10 + run: bundle exec bake test diff --git a/.gitignore b/.gitignore index 080e11b..09a72e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -/Gemfile.lock -/roda-websockets-*.gem +/.bundle/ +/pkg/ +/gems.locked +/.covered.db +/external diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..6ba2626 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Jeffrey Lim \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index b5eb0fd..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## 0.1.0 (2019-06-26) - -* Initial Public Release diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 7f4f5e9..0000000 --- a/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f37eb9b..0000000 --- a/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2019 Shannon Skipper - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 4f78b51..0000000 --- a/Rakefile +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/gem_tasks' -require 'rake/clean' -require 'rake/testtask' - -CLEAN.include %w[pkg/roda-websockets-*.gem].freeze - -task default: :test - -Rake::TestTask.new do |test| - test.pattern = 'spec/**/*_spec.rb' - test.warning = false -end diff --git a/config/sus.rb b/config/sus.rb new file mode 100644 index 0000000..ca3734e --- /dev/null +++ b/config/sus.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Shannon Skipper. +# Copyright, 2024, by Samuel Williams. + +require 'covered/sus' +include Covered::Sus diff --git a/examples/echo_server/client.rb b/examples/echo_server/client.rb index 0045932..063b050 100644 --- a/examples/echo_server/client.rb +++ b/examples/echo_server/client.rb @@ -1,28 +1,32 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2019, by Shannon Skipper. +# Copyright, 2024, by Samuel Williams. + require 'async' require 'async/http/endpoint' require 'async/websocket/client' module Client - URL = 'https://localhost:9292' - ENDPOINT = Async::HTTP::Endpoint.parse URL - MESSAGE = ARGV.first || 'ping' + URL = 'https://localhost:9292' + ENDPOINT = Async::HTTP::Endpoint.parse URL + MESSAGE = ARGV.first || 'ping' - module_function + module_function - def call - Async do - Async::WebSocket::Client.connect ENDPOINT do |connection| - puts "Sending message: #{MESSAGE}" - connection.write MESSAGE - connection.flush + def call + Async do + Async::WebSocket::Client.connect ENDPOINT do |connection| + puts "Sending message: #{MESSAGE}" + connection.write MESSAGE + connection.flush - message = connection.read - puts "Receiving message: #{message}" - end - end - end + message = connection.read + puts "Receiving message: #{message}" + end + end + end end Client.call diff --git a/examples/echo_server/config.ru b/examples/echo_server/config.ru index 6c51083..8e45ee6 100755 --- a/examples/echo_server/config.ru +++ b/examples/echo_server/config.ru @@ -4,25 +4,27 @@ require 'roda' class App < Roda - plugin :websockets + plugin :websockets - def on_message(connection) - Async do |task| - message = connection.read - task.sleep(3) # Async I/O here - connection.write(message) - connection.flush - connection.close - end - end + def on_message(connection) + Async do |task| + message = connection.read + + sleep(3) # Async I/O here + + connection.write(message) + connection.flush + connection.close + end + end - route do |r| - r.is '' do - r.websocket do |connection| - on_message(connection).wait - end - end - end + route do |r| + r.is '' do + r.websocket do |connection| + on_message(connection).wait + end + end + end end run App.freeze.app diff --git a/examples/interactive_browser/client.rb b/examples/interactive_browser/client.rb index fd93ff5..a0475db 100644 --- a/examples/interactive_browser/client.rb +++ b/examples/interactive_browser/client.rb @@ -1,41 +1,45 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2019, by Shannon Skipper. +# Copyright, 2024, by Samuel Williams. + require 'async' require 'async/http/endpoint' require 'async/websocket/client' module Client - URL = 'https://localhost:9292' - ENDPOINT = Async::HTTP::Endpoint.parse URL - TIMEOUT = 15 - REPEAT = 3 + URL = 'https://localhost:9292' + ENDPOINT = Async::HTTP::Endpoint.parse URL + TIMEOUT = 15 + REPEAT = 3 - module_function + module_function - def call - Async do |task| - task.with_timeout TIMEOUT do - Async::WebSocket::Client.connect ENDPOINT do |connection| - tasks = REPEAT.times.map do - task.async do - pinapple_count = rand 2..12 - puts "Sending message: #{pinapple_count}" - connection.write pinapple_count - connection.flush - end - end + def call + Async do |task| + task.with_timeout TIMEOUT do + Async::WebSocket::Client.connect ENDPOINT do |connection| + tasks = REPEAT.times.map do + task.async do + pinapple_count = rand 2..12 + puts "Sending message: #{pinapple_count}" + connection.write pinapple_count + connection.flush + end + end - while (message = connection.read) - puts "Receiving message: #{message}" - end - rescue Async::TimeoutError - connection.close - ensure - tasks.each(&:stop) - end - end - end - end + while (message = connection.read) + puts "Receiving message: #{message}" + end + rescue Async::TimeoutError + connection.close + ensure + tasks.each(&:stop) + end + end + end + end end Client.call diff --git a/examples/interactive_browser/config.ru b/examples/interactive_browser/config.ru index 271be27..6ae604f 100755 --- a/examples/interactive_browser/config.ru +++ b/examples/interactive_browser/config.ru @@ -6,80 +6,80 @@ require 'roda' ## # Run this example with `ruby client.rb` or browse to https://localhost:9292. class App < Roda - # Roda usually extracts HTML to separate files, but we'll inline it here. - BODY = <<~HTML - - - - - WebSockets Example - - - - - - HTML + socket.onmessage = function(event) { + status.innerHTML = event.data; + }; + + + + HTML - plugin :websockets + plugin :websockets - def on_message(connection, pineapple_count:) - Async do |task| - connection.write "Eating #{pineapple_count.buffer} pineapples." - connection.flush + def on_message(connection, pineapple_count:) + Async do |task| + connection.write "Eating #{pineapple_count.buffer} pineapples." + connection.flush - pineapple_count.buffer.to_i.downto(1) do |n| - task.sleep 1 - connection.write '🍍' * n - connection.flush - end - task.sleep 1 + pineapple_count.buffer.to_i.downto(1) do |n| + task.sleep 1 + connection.write '🍍' * n + connection.flush + end + task.sleep 1 - connection.write "Ate #{pineapple_count.buffer} pineapples." - connection.flush - end - end + connection.write "Ate #{pineapple_count.buffer} pineapples." + connection.flush + end + end - def messages(connection) - Enumerator.new do |yielder| - loop do - message = connection.read - break unless message + def messages(connection) + Enumerator.new do |yielder| + loop do + message = connection.read + break unless message - yielder << message - end - end - end + yielder << message + end + end + end - route do |r| - r.is '' do - r.websocket do |connection| - messages(connection).each do |message| - on_message(connection, pineapple_count: message) - end - end + route do |r| + r.is '' do + r.websocket do |connection| + messages(connection).each do |message| + on_message(connection, pineapple_count: message) + end + end - r.get do - BODY - end - end - end + r.get do + BODY + end + end + end end run App.freeze.app diff --git a/gems.rb b/gems.rb new file mode 100644 index 0000000..36ef4c0 --- /dev/null +++ b/gems.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +source 'https://rubygems.org' + +gemspec + +group :maintenance, optional: true do + gem "bake-gem" + gem "bake-modernize" + + gem "utopia-project" +end + +group :test do + gem "sus" + gem "covered" + + gem "sus-fixtures-async" + gem "sus-fixtures-async-http" + + gem "bake-test" + gem "bake-test-external" +end diff --git a/lib/roda/plugins/websockets.rb b/lib/roda/plugins/websockets.rb index a33431c..e3ee96f 100644 --- a/lib/roda/plugins/websockets.rb +++ b/lib/roda/plugins/websockets.rb @@ -1,64 +1,35 @@ -# frozen-string-literal: true +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Shannon Skipper. +# Copyright, 2024, by Samuel Williams. require 'async/websocket/adapters/rack' class Roda - module RodaPlugins - # The websockets plugin integrates the async-websocket gem into Roda's - # routing tree. See the - # {async-websocket docs}[https://github.com/socketry/async-websocket] - # for usage details. - # - # The following example is an echo server that sleeps one second after - # receiving a message before echoing that same message back to the client - # and closing the connection: - # - # plugin :websockets - # - # def messages(connection) - # Enumerator.new do |yielder| - # while (message = connection.read) - # yielder << message - # end - # end - # end - # - # def on_message(connection, message:) - # Async do |task| - # task.sleep(1) - # connection.write(message) - # connection.flush - # connection.close - # end - # end - # - # route do |r| - # r.root do - # r.websocket do |connection| - # messages(connection).each do |message| - # on_message(connection, message: message) - # end - # end - # end - # end - module WebSockets - module RequestMethods - ARGS = {}.freeze - - def websocket? - Async::WebSocket::Adapters::Rack.websocket?(env) - end - - def websocket(args = ARGS, &block) - return unless websocket? - - always do - halt Async::WebSocket::Adapters::Rack.open(env, *args, &block) - end - end - end - end - - register_plugin(:websockets, WebSockets) - end + module RodaPlugins + # The websockets plugin integrates the async-websocket gem into Roda's + # routing tree. See the + # (async-websocket docs)[https://github.com/socketry/async-websocket] + # for usage details. + module WebSockets + module RequestMethods + ARGS = {}.freeze + + def websocket? + ::Async::WebSocket::Adapters::Rack.websocket?(env) + end + + def websocket(args = ARGS, &block) + return unless websocket? + + always do + halt ::Async::WebSocket::Adapters::Rack.open(env, *args, &block) + end + end + end + end + + register_plugin(:websockets, WebSockets) + end end diff --git a/lib/roda/websockets.rb b/lib/roda/websockets.rb new file mode 100644 index 0000000..80c5c05 --- /dev/null +++ b/lib/roda/websockets.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +require_relative 'plugins/websockets' diff --git a/lib/roda/websockets/version.rb b/lib/roda/websockets/version.rb new file mode 100644 index 0000000..b0299ac --- /dev/null +++ b/lib/roda/websockets/version.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2024, by Samuel Williams. + +class Roda + module WebSockets + VERSION = '0.1.0' + end +end diff --git a/license.md b/license.md new file mode 100644 index 0000000..ae2bbc7 --- /dev/null +++ b/license.md @@ -0,0 +1,23 @@ +# MIT License + +Copyright, 2019, by Shannon Skipper. +Copyright, 2024, by Jeffrey Lim. +Copyright, 2024, by Samuel Williams. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg/roda-websockets-0.1.0.gem b/pkg/roda-websockets-0.1.0.gem deleted file mode 100644 index a001bdf55df2c26bfedf9b03561a518896ce1983..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6144 zcmeHKS6CCs7LLF!y@y@|r9_QL2@sGbJro7$O^g^r3qmN7A}A8+Xy^h05)lN!fCz#@ zloE;{5l8}pARqyOT||1#y7xZpem>lN+1d?A5rwwfN`or zAaa)C=4ZQZ3cDuZW?bAn7S6&>*T$`no~+#{jt@lSz(wxMo@DpPZ z{p&l6Oc%3LxdT=B3&QUS*;*NzRw=ox0#>1_xUP-8u(2G$w&R+eJpRb_gIhI zF>8lwM;$C(Drxh|d*HHhYp}zJh!fx{7XXTok8c#LY%!@Ay}R1p+XwW-rmSlnX73>a zH-73#{lE>a76Hqu9vf9OHc1Vp0pwzmwD(qypIKKsqwYZptp%NLSM7W}3-6#U0E$=A|hzDW7&DLf;BH!k6UFtIa8)7g0Jis874X3kaBa%LE`h5 z+1wiNgDVTR;Me3!cDc;rwsP0~F67k<^vGA9zu+eGaPc5J$p*}2~FC)fjIT+(ur1P?iX;HKw{%$`m5ow)a8CacP6cV%$ z;J4j1%YIvez7U|mg@A)s2y0u%az-v`qZI9OEEEs&bOj{VIf}C81C>3;&iVBA+mRQ< z?&1R@JFkG^+Y>F=9cJuu?@uZjbY^ zVRz>(wtwnZ`M>n*U)=wd0so8quco4^`7QsesYAZ=|KIW8e&hc|XVjTdb3Xc$n2v<` zv}>^|hT8C$lH%8yfvR1(fr=`ryVq|5mHC{BBmSKB6bKPW3)PE!j#QTaICFHn&Cmol-uk> zVfCvqCBX$}TG@&yB)heV@&?TXqQ_;_0z-aI$m+@${%JqiaM3f5@ivE$wybjfHiO3L zK?mWN6%a?YpNjXBFm?r;x|4&t)^@j!Y>ygE#+SBG)KRqE!nKJRJpvnps0z3k$LoN zP4$b{h|DZ|d3zGEBgv+Yn1v4C1Ru zFg9Z*>s+V}LMMkqIygEL<&d0;Ljw0JLp^HSQ*awz?hjJH@iinW(^Suaid^Vr?!mZO zVNGUAH#2&&l?8K!?je@Op3Qj}xk;VBqdTnb9x+kXnlaDS3~_|mgL-*iEe&jh$Go$4leu$xS!H5=8eKP#7sosSsVOM+CN!xXp zakFO=z2{D$&nhgZvcye<+R(6d#yHJq^~VQ#ye*3RWwX}169N893=x-@up9l{fFf6h z<22_kW-K6p#?ZgUfbM5uhf)_)pFk6C)7X|hEENLU$CEmJcqzMVE-$n|54r6IScINm z9VDzFJP#1T<7JwI;%At`p1o8FjnM>kOkO{|tv^>$x@%6Q*s}$;)|!VEN0&}Vw$vEzwcSM&zrohzuT~loKjkM%EyzKveWOE z2hl6PtRHR-{6Ick^5xK6rb#X_H3?M;IPiiAYvCxtm85GXYINuo>}Zdq$;StdWlUsT zv2nWl#JPt_f@yY?W1k+8U&(V-+;)GpQ}-~|pmcM$Wj8@x9+;aNs(**Zp+~Gl^==Y| zAhPWl>>4K8H`%&O4Nr*rEkBYG-S4CO+%J}ZgAHO#Z*y-QN260=eEb(IoR26JU^%J} z9u@O;$s$5oxHp=&~11{qgtq%9|HVusNl#~L& zXM3`TP6-A@h_h)DF1kdVK~NUfBBB`ULe6^9{j>eiW*sM6b~pEyjf<`0=AYHeV#1zG zNSv7P5SXh(LyxhPkXjp_OOf7s`!u)2RAY9xm;BDT0jUoMnugyKl2T46=G#9i(*?EQmR|IhZy=>y=ECwXe{1g;_E8sL(fydYlhF4i+W))mq(P83yeDuw})_(oM(0 zRuf^#46UZJ1kL%ocJozoSbw~^YT-xZW1ODVd3qsFu5OO{aIntW`jaW9u3_I>`d@r- Xl0ygn0K@O 0.12') - s.add_dependency('falcon', '~> 0.33') - s.add_dependency('roda', '~> 3.0') - s.add_development_dependency('minitest', '~> 5.11') - s.add_development_dependency('minitest-proveit', '~> 1.0') +require_relative "lib/roda/websockets/version" + +Gem::Specification.new do |spec| + spec.name = "roda-websockets" + spec.version = Roda::WebSockets::VERSION + + spec.summary = "WebSocket integration for Roda" + spec.authors = ["Shannon Skipper", "Jeffrey Lim", "Samuel Williams"] + spec.license = "MIT" + + spec.cert_chain = ['release.cert'] + spec.signing_key = File.expand_path('~/.gem/release.pem') + + spec.homepage = "https://github.com/socketry/roda-websockets" + + spec.metadata = { + "documentation_uri" => "https://socketry.github.io/roda-websockets/", + "source_code_uri" => "https://github.com/socketry/roda-websockets.git", + } + + spec.files = Dir['{lib}/**/*', '*.md', base: __dir__] + + spec.required_ruby_version = ">= 3.1" + + spec.add_dependency "async-websocket", "~> 0.12" + spec.add_dependency "falcon", "~> 0.33" + spec.add_dependency "roda", "~> 3.0" end diff --git a/spec/roda-websockets_spec.rb b/spec/roda-websockets_spec.rb deleted file mode 100644 index 86a6bef..0000000 --- a/spec/roda-websockets_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -require 'async' -require 'async/debug/selector' -require 'async/http/endpoint' -require 'async/websocket/client' -require 'falcon/adapters/rack' -require 'falcon/server' -require 'minitest/autorun' -require 'minitest/pride' -require 'minitest/proveit' -require 'roda' - -describe 'roda-websockets plugin' do - prove_it! - - let :reactor do - Async::Reactor.new(selector: Async::Debug::Selector.new) - end - - let :endpoint do - Async::HTTP::Endpoint.parse('http://localhost:7050') - end - - let :app do - app = Class.new(Roda) - app.plugin :websockets - app.route do |r| - r.root do - r.websocket do |connection| - %w[zxc spqr wombat].each do |message| - connection.write(message) - connection.flush - end - - connection.close - end - - 'bar' - end - end - - app - end - - let :server do - Falcon::Server.new(Falcon::Server.middleware(app), endpoint) - end - - let :client do - Async::HTTP::Client.new(endpoint) - end - - before do - @server_task = reactor.async do - server.run - end - end - - after do - @server_task.stop - end - - it 'supports regular requests' do - reactor.run do - response = client.get('/') - assert_equal response.read, 'bar' - - client.close - reactor.stop - end - end - - it 'supports websocket requests' do - reactor.run do - Async::WebSocket::Client.connect(endpoint) do |connection| - assert_equal(connection.read, 'zxc') - assert_equal(connection.read, 'spqr') - assert_equal(connection.read, 'wombat') - end - - reactor.stop - end - end -end diff --git a/test/roda/websockets.rb b/test/roda/websockets.rb new file mode 100644 index 0000000..25ec455 --- /dev/null +++ b/test/roda/websockets.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2019, by Shannon Skipper. +# Copyright, 2024, by Samuel Williams. + +require 'async/websocket/client' +require 'sus/fixtures/async/reactor_context' +require 'sus/fixtures/async/http/server_context' + +require 'roda' +require 'roda/websockets' + +describe Roda::WebSockets do + include Sus::Fixtures::Async::ReactorContext + include Sus::Fixtures::Async::HTTP::ServerContext + + let(:roda) do + Class.new(::Roda).tap do |roda| + roda.plugin :websockets + + roda.route do |r| + r.root do + r.websocket do |connection| + %w[zxc spqr wombat].each do |message| + connection.write(message) + connection.flush + end + + connection.close + end + + 'bar' + end + end + end + end + + let(:app) do + Protocol::Rack::Adapter.new(roda) + end + + it 'supports regular requests' do + response = client.get('/') + expect(response.read).to be == 'bar' + end + + it 'supports websocket requests' do + Async::WebSocket::Client.connect(client_endpoint) do |connection| + expect(connection.read).to be == 'zxc' + expect(connection.read).to be == 'spqr' + expect(connection.read).to be == 'wombat' + end + end +end