oss-gate/workshop

OSS Gate Workshop: Techouse: 2024-04-18: kai-matsudate: faraday: Work log

Closed this issue · 52 comments

This is a work log of a "OSS Gate workshop".
"OSS Gate workshop" is an activity to increase OSS developers.
Here's been discussed in Japanese. Thanks.

作業ログ作成時の説明

以下のテンプレートを埋めてタイトルに設定します。埋め方例はスクロールすると見えてきます。

OSS Gate Workshop: ${LOCATION}: ${YEAR}-${MONTH}-${DAY}: ${ACCOUNT_NAME}: ${OSS_NAME}: Work log

タイトル例↓:

OSS Gate Workshop: Tokyo: 2017-01-16: kou: Rabbit: Work log

OSS Gateワークショップ関連情報

  • スライド:ワークショップの進行に使っているスライドがあります。
  • チャット:OSS開発に関することならなんでも相談できます。ワークショップが終わった後もオンラインで相談しながら継続的にOSSの開発に参加しましょう!
  • シナリオ:ワークショップの目的・内容・進め方の詳細が書いています。
  • 過去のビギナーの作業ログ:他の人の作業ログから学べることがいろいろあるはずです。

faradayを選んでみた,faradayで検索してこちらを見つけた.
quick guideがあったので読んでみる.

ガイドの通り,ローカルでgem install faradayを実行してみた.
以下のログが出た.

Successfully installed faraday-2.9.0
Parsing documentation for faraday-2.9.0
Installing ri documentation for faraday-2.9.0
Done installing documentation for faraday after 0 seconds
1 gem installed

A new release of RubyGems is available: 3.4.10 → 3.5.9!
Run `gem update --system 3.5.9` to update your installation.

readmeを読んで,以下のコードを書いた.

require 'faraday'

response = Faraday.get('http://httpbingo.org')

puts response.status
puts response.headers
puts response.body

こんなログが出た.

200
{"access-control-allow-credentials"=>"true", "access-control-allow-origin"=>"*", "content-security-policy"=>"default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' camo.githubusercontent.com", "content-type"=>"text/html; charset=utf-8", "date"=>"Thu, 18 Apr 2024 02:35:00 GMT", "transfer-encoding"=>"chunked", "content-encoding"=>"gzip", "server"=>"Fly/f28a2f4b (2024-04-16)", "via"=>"1.1 fly.io", "fly-request-id"=>"01HVQGXAZ8WA7WPT14WR9K50P9-nrt"}
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='content-type' value='text/html;charset=utf8'>
  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
  <title>go-httpbin(1): HTTP Client Testing Service</title>
  <style type='text/css' media='all'>
  /* style: man */
  body#manpage {margin:0;background:#fff;}
  .mp {max-width:100ex;padding:0 9ex 1ex 4ex}
  .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
  .mp h2 {margin:10px 0 0 0}
  .mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
  .mp h3 {margin:0 0 0 4ex}
  .mp dt {margin:0;clear:left}
  .mp dt.flush {float:left;width:8ex}
  .mp dd {margin:0 0 0 9ex}
  .mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
  .mp pre {margin-bottom:20px}
  .mp pre+h2,.mp pre+h3 {margin-top:22px}
  .mp h2+pre,.mp h3+pre {margin-top:5px}
  .mp img {display:block;margin:auto}
  .mp h1.man-title {display:none}
  .mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
  .mp h2 {font-size:16px;line-height:1.25}
  .mp h1 {font-size:20px;line-height:2}
  .mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
  .mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
  .mp u {text-decoration:underline}
  .mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
  .mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
  .mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
  .mp b.man-ref {font-weight:normal;color:#434241}
  .mp pre {padding:0 4ex}
  .mp pre code {font-weight:normal;color:#434241}
  .mp h2+pre,h3+pre {padding-left:0}
  ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
  ol.man-decor {width:100%}
  ol.man-decor li.tl {text-align:left}
  ol.man-decor li.tc {text-align:center;letter-spacing:4px}
  ol.man-decor li.tr {text-align:right;float:right}
  </style>
  <style type='text/css' media='all'>
  /* style: 80c */
  .mp {max-width:86ex}
  ul {list-style: None; margin-left: 1em!important}
  .man-navigation {left:101ex}
  </style>
</head>

<body id='manpage'>


<div class='mp'>
<h1>go-httpbin(1)</h1>
<p>A golang port of the venerable <a href="https://httpbin.org/">httpbin.org</a> HTTP request &amp; response testing service.</p>

<h2 id="ENDPOINTS">ENDPOINTS</h2>

<ul>
<li><a href="/"><code>/</code></a> This page.</li>
<li><a href="/absolute-redirect/6"><code>/absolute-redirect/:n</code></a> 302 Absolute redirects <em>n</em> times.</li>
<li><a href="/anything"><code>/anything/:anything</code></a> Returns anything that is passed to request.</li>
<li><a href="/base64/aHR0cGJpbmdvLm9yZw=="><code>/base64/:value</code></a> Decodes a Base64-encoded string.</li>
<li><a href="/base64/decode/aHR0cGJpbmdvLm9yZw=="><code>/base64/decode/:value</code></a> Explicit URL for decoding a Base64 encoded string.</li>
<li><a href="/base64/encode/httpbingo.org"><code>/base64/encode/:value</code></a> Encodes a string into URL-safe Base64.</li>
<li><a href="/basic-auth/user/passwd"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>
<li><a href="/bearer"><code>/bearer</code></a> Checks Bearer token header - returns 401 if not set.</li>
<li><a href="/brotli"><code><del>/brotli</del></code></a> Returns brotli-encoded data.</del> <i>Not implemented!</i></li>
<li><a href="/bytes/1024"><code>/bytes/:n</code></a> Generates <em>n</em> random bytes of binary data, accepts optional <em>seed</em> integer parameter.</li>
<li><a href="/cache"><code>/cache</code></a> Returns 200 unless an If-Modified-Since or If-None-Match header is provided, when it returns a 304.</li>
<li><a href="/cache/60"><code>/cache/:n</code></a> Sets a Cache-Control header for <em>n</em> seconds.</li>
<li><a href="/cookies"><code>/cookies</code></a> Returns cookie data.</li>
<li><a href="/cookies/delete?k1=&amp;k2="><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>
<li><a href="/cookies/set?k1=v1&amp;k2=v2"><code>/cookies/set?name=value</code></a> Sets one or more simple cookies.</li>
<li><a href="/deflate"><code>/deflate</code></a> Returns deflate-encoded data.</li>
<li><a href="/delay/3"><code>/delay/:n</code></a> Delays responding for <em>min(n, 10)</em> seconds.</li>
<li><code>/delete</code> Returns request data.  Allows only <code>DELETE</code> requests.</li>
<li><a href="/deny"><code>/deny</code></a> Denied by robots.txt file.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5"><code>/digest-auth/:qop/:user/:passwd/:algorithm</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5"><code>/digest-auth/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/drip?code=200&amp;numbytes=5&amp;duration=5"><code>/drip?numbytes=n&amp;duration=s&amp;delay=s&amp;code=code</code></a> Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code.</li>
<li><a href="/dump/request"><code>/dump/request</code></a> Returns the given request in its HTTP/1.x wire approximate representation.</li>
<li><a href="/encoding/utf8"><code>/encoding/utf8</code></a> Returns page containing UTF-8 data.</li>
<li><a href="/etag/etag"><code>/etag/:etag</code></a> Assumes the resource has the given etag and responds to If-None-Match header with a 200 or 304 and If-Match with a 200 or 412 as appropriate.</li>
<li><a href="/forms/post"><code>/forms/post</code></a> HTML form that submits to <em>/post</em></li>
<li><a href="/get"><code>/get</code></a> Returns GET data.</li>
<li><a href="/gzip"><code>/gzip</code></a> Returns gzip-encoded data.</li>
<li><code>/head</code> Returns response headers.  Allows only <code>HEAD</code> requests.</li>
<li><a href="/headers"><code>/headers</code></a> Returns request header dict.</li>
<li><a href="/hidden-basic-auth/user/passwd"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>
<li><a href="/html"><code>/html</code></a> Renders an HTML Page.</li>
<li><a href="/hostname"><code>/hostname</code></a> Returns the name of the host serving the request.</li>
<li><a href="/image"><code>/image</code></a> Returns page containing an image based on sent Accept header.</li>
<li><a href="/image/jpeg"><code>/image/jpeg</code></a> Returns a JPEG image.</li>
<li><a href="/image/png"><code>/image/png</code></a> Returns a PNG image.</li>
<li><a href="/image/svg"><code>/image/svg</code></a> Returns a SVG image.</li>
<li><a href="/image/webp"><code>/image/webp</code></a> Returns a WEBP image.</li>
<li><a href="/ip"><code>/ip</code></a> Returns Origin IP.</li>
<li><a href="/json"><code>/json</code></a> Returns JSON.</li>
<li><a href="/links/10"><code>/links/:n</code></a> Returns page containing <em>n</em> HTML links.</li>
<li><code>/patch</code> Returns request data.  Allows only <code>PATCH</code> requests.</li>
<li><code>/post</code> Returns request data.  Allows only <code>POST</code> requests.</li>
<li><code>/put</code> Returns request data.  Allows only <code>PUT</code> requests.</li>
<li><a href="/range/1024"><code>/range/1024?duration=s&amp;chunk_size=code</code></a> Streams <em>n</em> bytes, and allows specifying a <em>Range</em> header to select a subset of the data. Accepts a <em>chunk_size</em> and request <em>duration</em> parameter.</li>
<li><a href="/redirect-to?status_code=307&amp;url=http%3A%2F%2Fexample.com%2F"><code>/redirect-to?url=foo&status_code=307</code></a> 307 Redirects to the <em>foo</em> URL.</li>
<li><a href="/redirect-to?url=http%3A%2F%2Fexample.com%2F"><code>/redirect-to?url=foo</code></a> 302 Redirects to the <em>foo</em> URL.</li>
<li><a href="/redirect/6"><code>/redirect/:n</code></a> 302 Redirects <em>n</em> times.</li>
<li><a href="/relative-redirect/6"><code>/relative-redirect/:n</code></a> 302 Relative redirects <em>n</em> times.</li>
<li><a href="/response-headers?Server=httpbin&amp;Content-Type=text%2Fplain%3B+charset%3DUTF-8"><code>/response-headers?key=val</code></a> Returns given response headers.</li>
<li><a href="/robots.txt"><code>/robots.txt</code></a> Returns some robots.txt rules.</li>
<li><a href="/sse?delay=1s&amp;duration=5s&count=10"><code>/sse?delay=1s&amp;duration=5s&count=10</code></a> a stream of server-sent events.</li>
<li><a href="/status/418"><code>/status/:code</code></a> Returns given HTTP Status code.</li>
<li><a href="/stream-bytes/1024"><code>/stream-bytes/:n</code></a> Streams <em>n</em> random bytes of binary data, accepts optional <em>seed</em> and <em>chunk_size</em> integer parameters.</li>
<li><a href="/stream/20"><code>/stream/:n</code></a> Streams <em>min(n, 100)</em> lines.</li>
<li><a href="/unstable"><code>/unstable</code></a> Fails half the time, accepts optional <em>failure_rate</em> float and <em>seed</em> integer parameters.</li>
<li><a href="/user-agent"><code>/user-agent</code></a> Returns user-agent.</li>
<li><a href="/uuid"><code>/uuid</code></a> Generates a <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUIDv4</a> value.</li>
<li><a href="/websocket/echo?max_fragment_size=2048&amp;max_message_size=10240"><code>/websocket/echo?max_fragment_size=2048&amp;max_message_size=10240</code></a> A WebSocket echo service.</li>
<li><a href="/xml"><code>/xml</code></a> Returns some XML</li>
</ul>

<h2 id="DESCRIPTION">DESCRIPTION</h2>

<p>Testing an HTTP Library can become difficult sometimes. <a href="http://requestb.in">RequestBin</a> is fantastic for testing POST requests, but doesn't let you control the response. This exists to cover all kinds of HTTP scenarios. Additional endpoints are being considered.</p>

<p>All endpoint responses are JSON-encoded.</p>

<h2 id="EXAMPLES">EXAMPLES</h2>

<h3 id="-curl-http-httpbin-org-ip">$ curl https://httpbingo.org/ip</h3>

<pre><code>{"origin":"73.238.9.52, 77.83.142.42"}
</code></pre>

<h3 id="-curl-http-httpbin-org-user-agent">$ curl https://httpbingo.org/user-agent</h3>

<pre><code>{"user-agent":"curl/7.64.1"}</code></pre>

<h3 id="-curl-http-httpbin-org-get">$ curl https://httpbingo.org/get?foo=bar</h3>

<pre><code>{
  "args": {
    "foo": [
      "bar"
    ]
  },
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Host": [
      "httpbingo.org"
    ],
    "User-Agent": [
      "curl/7.64.1"
    ]
  },
  "origin": "73.238.9.52, 77.83.142.42",
  "url": "https://httpbingo.org/get?foo=bar"
}
</code></pre>

<h3 id="-curl-http-httpbin-org-dump-request">$ curl https://httpbingo.org/dump/request?foo=bar</h3>

<pre><code>GET /dump/request?foo=bar HTTP/1.1
Host: httpbingo.org
Accept: */*
User-Agent: curl/7.64.1
</code></pre>

<h3 id="-curl-I-http-httpbin-org-status-418">$ curl -I https://httpbingo.org/status/418</h3>

<pre><code>HTTP/1.1 418 I'm a teapot
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
X-More-Info: http://tools.ietf.org/html/rfc2324
Date: Tue, 13 Jul 2021 13:12:37 GMT
Content-Length: 0
</code></pre>


<h2 id="AUTHOR">AUTHOR</h2>

<p>Ported to Go by <a href="https://github.com/mccutchen">Will McCutchen</a>.</p>
<p>From <a href="https://httpbin.org/">the original</a> <a href="https://kennethreitz.org/">Kenneth Reitz</a> project.</p>

<h2 id="SEE-ALSO">SEE ALSO</h2>

<p><a href="https://httpbin.org/">httpbin.org</a> &mdash; the original httpbin</p>

</div>

<a href="https://github.com/mccutchen/go-httpbin"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/38ef81f8aca64bb9a64448d0d70f1308ef5341ab/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6461726b626c75655f3132313632312e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>

</body>
</html>

普通.

readmeを読み進めた.
3rd partyのAPIを利用する際は,connectionクラスを利用すると便利と書いてある.
quick guide記載の以下のコードを実行してみることにした.

conn = Faraday.new(
  url: 'http://httpbingo.org',
  params: {param: '1'},
  headers: {'Content-Type' => 'application/json'}
)

response = conn.post('/post') do |req|
  req.params['limit'] = 100
  req.body = {query: 'chunky bacon'}.to_json
end
# => POST http://httpbingo.org/post?param=1&limit=100

実行したら,以下のように標準エラー出力が出た.

connection_init.rb:1:in `<main>': uninitialized constant Faraday (NameError)

conn = Faraday.new(
       ^^^^^^^

先ほどは自前で require 'faraday'を追加していたが,今回はうっかり忘れてしまったので,こけている.

上記はサンプルコードにrequire 'faraday'がないため起きている事象なので,readmeに注意書きがあると初学者にはわかりやすいかもしれないと感じた.

同様のissueを探したが,見つからなかった.
https://github.com/lostisland/faraday/issues?page=7&q=is%3Aissue+require

引き続きreadmeを読んでいるが,全部叩くのは面倒なのでツラっと読んでみる.

response = conn.get('get', { boom: 'zap' }, { 'User-Agent' => 'myapp' })
# => GET http://httpbingo.org/get?boom=zap

上記のコードが貼られていたが,これはconn(Faraday::Connectionのインスタンス)の初期化を前提としている.
しかし,ドキュメント構成は以下の画像のようになっており,

スクリーンショット 2024-04-18 11 45 58

どちらもh3要素なので同じ区分だと分かりずらい.

faradayの強みはmiddlewareらしい.
以下のスクリプトを書いて実行した.

require 'faraday'

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  # Calls MyAuthStorage.get_auth_token on each request to get the auth token
  # and sets it in the Authorization header with Bearer scheme.
  builder.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }

  # Sets the Content-Type header to application/json on each request.
  # Also, if the request body is a Hash, it will automatically be encoded as JSON.
  builder.request :json

  # Parses JSON response bodies.
  # If the response body is not valid JSON, it will raise a Faraday::ParsingError.
  builder.response :json

  # Raises an error on 4xx and 5xx responses.
  builder.response :raise_error

  # Logs requests and responses.
  # By default, it only logs the request method and URL, and the request/response headers.
  builder.response :logger
end

# A simple example implementation for MyAuthStorage
class MyAuthStorage
  def self.get_auth_token
    rand(36 ** 8).to_s(36)
  end
end


begin
  response = conn.post('post', { payload: 'this ruby hash will become JSON' })
rescue Faraday::Error => e
  # You can handle errors here (4xx/5xx responses, timeouts, etc.)
  puts e.response[:status]
  puts e.response[:body]
end

# At this point, you can assume the request was successful
puts response.body

# I, [2023-06-30T14:27:11.776511 #35368]  INFO -- request: POST http://httpbingo.org/post
# I, [2023-06-30T14:27:11.776646 #35368]  INFO -- request: User-Agent: "Faraday v2.7.8"
# Authorization: "Bearer wibzjgyh"
# Content-Type: "application/json"
# I, [2023-06-30T14:27:12.063897 #35368]  INFO -- response: Status 200
# I, [2023-06-30T14:27:12.064260 #35368]  INFO -- response: access-control-allow-credentials: "true"
# access-control-allow-origin: "*"
# content-type: "application/json; encoding=utf-8"
# date: "Fri, 30 Jun 2023 13:27:12 GMT"
# content-encoding: "gzip"
# transfer-encoding: "chunked"
# server: "Fly/a0b91024 (2023-06-13)"
# via: "1.1 fly.io"
# fly-request-id: "01H467RYRHA0YK4TQSZ7HS8ZFT-lhr"
# cf-team: "19ae1592b8000003bbaedcf400000001"

予定通りのログが出た.

I, [2024-04-18T11:52:56.235720 #23297]  INFO -- request: POST http://httpbingo.org/post
I, [2024-04-18T11:52:56.235748 #23297]  INFO -- request: User-Agent: "Faraday v2.9.0"
Authorization: "Bearer fdbn3t77"
Content-Type: "application/json"
I, [2024-04-18T11:52:56.667742 #23297]  INFO -- response: Status 200
I, [2024-04-18T11:52:56.667942 #23297]  INFO -- response: access-control-allow-credentials: "true"
access-control-allow-origin: "*"
content-type: "application/json; charset=utf-8"
date: "Thu, 18 Apr 2024 02:52:56 GMT"
content-encoding: "gzip"
transfer-encoding: "chunked"
server: "Fly/f28a2f4b (2024-04-16)"
via: "1.1 fly.io"
fly-request-id: "01HVQHY64GD8CG74B6GSGREMAV-nrt"
{"args"=>{}, "headers"=>{"Accept"=>["*/*"], "Accept-Encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"], "Authorization"=>["Bearer fdbn3t77"], "Content-Length"=>["45"], "Content-Type"=>["application/json"], "Host"=>["httpbingo.org"], "User-Agent"=>["Faraday v2.9.0"], "Via"=>["1.1 fly.io"], "X-Forwarded-For"=>["240d:1b:9a:c908:e9e8:5f13:d15c:e662, 2a09:8280:1:7fcb:9efa:a365:aa2a:d036"], "X-Forwarded-Port"=>["80"], "X-Forwarded-Proto"=>["http"], "X-Forwarded-Ssl"=>["off"], "X-Request-Start"=>["t=1713408776336849"]}, "method"=>"POST", "origin"=>"240d:1b:9a:c908:e9e8:5f13:d15c:e662", "url"=>"http://httpbingo.org/post", "data"=>"{\"payload\":\"this ruby hash will become JSON\"}", "files"=>{}, "form"=>{}, "json"=>{"payload"=>"this ruby hash will become JSON"}}

faraday自身はHTTPリクエストを送信しておらず,adapterに任せているらしい.
デフォルトはNet::HTTPであり,自由に変更ができるとのこと.
以下の例が記載されていた.

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.adapter :async_http
end

入れれた

$ gem install async-http
Fetching async-http-0.64.1.gem
Successfully installed async-http-0.64.1
Parsing documentation for async-http-0.64.1
Installing ri documentation for async-http-0.64.1
Done installing documentation for async-http after 0 seconds
1 gem installed

以下のスクリプトを実行して,こけた

require 'faraday'
require 'async_http'

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.adapter :async_http
end

puts conn.get
---
<internal:/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- async_http (LoadError)
        from <internal:/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
        from change_adapter_init.rb:2:in `<main>'

async-httpの呼び方がわかっていなかった.
https://github.com/socketry/async-http
こっちに書いてた.

readme通りやったが,こけた

require 'faraday'
require 'json'
require 'async'
require 'async/http/internet'

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.adapter :async_http
end

puts conn.get

---
/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/io-event-1.5.1/lib/io/event/support.rb:27: warning: IO::Buffer is experimental and both the Ruby and C interface may change in the future!
/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday/middleware_registry.rb:57:in `lookup_middleware': :async_http is not registered on Faraday::Adapter (Faraday::Error)
        from /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday/rack_builder.rb:112:in `adapter'
        from /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/forwardable.rb:240:in `adapter'
        from change_adapter_init.rb:7:in `block in <main>'
        from /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday/connection.rb:91:in `initialize'
        from /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday.rb:98:in `new'
        from /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday.rb:98:in `new'
        from change_adapter_init.rb:6:in `<main>'

あーなるほど

こけた

require 'faraday'
require 'faraday/httpclient'

Faraday.default_adapter = :httpclient

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.adapter :httpclient
end

puts conn.get

入れていないからそれはそう
gemを入れた

gem install httpclient
Fetching httpclient-2.8.3.gem
Successfully installed httpclient-2.8.3
Parsing documentation for httpclient-2.8.3
Installing ri documentation for httpclient-2.8.3
Done installing documentation for httpclient after 0 seconds
1 gem installed

いや,こけるんかい

require 'httpclient'
require 'faraday'
require 'faraday/httpclient'

Faraday.default_adapter = :httpclient

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.adapter :httpclient
end

puts conn.get
---
<internal:/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- faraday/httpclient (LoadError)
        from <internal:/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
        from change_adapter_init.rb:3:in `<main>'

まあそんなわけないか

$ gem install faraday/httpclient
ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    bad response Not Found 404 (Gem::RemoteFetcher::FetchError)
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/remote_fetcher.rb:232:in `fetch_http'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/remote_fetcher.rb:248:in `fetch_path'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/api_set.rb:105:in `versions'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/api_set.rb:56:in `find_all'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/composed_set.rb:54:in `block in find_all'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/composed_set.rb:53:in `map'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/composed_set.rb:53:in `find_all'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/best_set.rb:30:in `find_all'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/installer_set.rb:170:in `find_all'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/resolver/installer_set.rb:61:in `add_always_install'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/dependency_installer.rb:322:in `resolve_dependencies'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/commands/install_command.rb:205:in `install_gem'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/commands/install_command.rb:230:in `block in install_gems'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/commands/install_command.rb:223:in `each'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/commands/install_command.rb:223:in `install_gems'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/commands/install_command.rb:169:in `execute'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/command.rb:327:in `invoke_with_build_args'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/command_manager.rb:252:in `invoke_command'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/command_manager.rb:192:in `process_args'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/command_manager.rb:150:in `run'
        /Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/3.2.0/rubygems/gem_runner.rb:51:in `run'
        /Users/matsudatekai/.rbenv/versions/3.2.2/bin/gem:10:in `<main>'

あーこれか

$ gem install faraday-httpclient
Fetching faraday-httpclient-2.0.1.gem
Successfully installed faraday-httpclient-2.0.1
Parsing documentation for faraday-httpclient-2.0.1
Installing ri documentation for faraday-httpclient-2.0.1
Done installing documentation for faraday-httpclient after 0 seconds
1 gem installed

普通に書いてた,くそ
スクリーンショット 2024-04-18 12 13 51

いけた

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='content-type' value='text/html;charset=utf8'>
  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
  <title>go-httpbin(1): HTTP Client Testing Service</title>
  <style type='text/css' media='all'>
  /* style: man */
  body#manpage {margin:0;background:#fff;}
  .mp {max-width:100ex;padding:0 9ex 1ex 4ex}
  .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
  .mp h2 {margin:10px 0 0 0}
  .mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
  .mp h3 {margin:0 0 0 4ex}
  .mp dt {margin:0;clear:left}
  .mp dt.flush {float:left;width:8ex}
  .mp dd {margin:0 0 0 9ex}
  .mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
  .mp pre {margin-bottom:20px}
  .mp pre+h2,.mp pre+h3 {margin-top:22px}
  .mp h2+pre,.mp h3+pre {margin-top:5px}
  .mp img {display:block;margin:auto}
  .mp h1.man-title {display:none}
  .mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
  .mp h2 {font-size:16px;line-height:1.25}
  .mp h1 {font-size:20px;line-height:2}
  .mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
  .mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
  .mp u {text-decoration:underline}
  .mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
  .mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
  .mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
  .mp b.man-ref {font-weight:normal;color:#434241}
  .mp pre {padding:0 4ex}
  .mp pre code {font-weight:normal;color:#434241}
  .mp h2+pre,h3+pre {padding-left:0}
  ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
  ol.man-decor {width:100%}
  ol.man-decor li.tl {text-align:left}
  ol.man-decor li.tc {text-align:center;letter-spacing:4px}
  ol.man-decor li.tr {text-align:right;float:right}
  </style>
  <style type='text/css' media='all'>
  /* style: 80c */
  .mp {max-width:86ex}
  ul {list-style: None; margin-left: 1em!important}
  .man-navigation {left:101ex}
  </style>
</head>

<body id='manpage'>


<div class='mp'>
<h1>go-httpbin(1)</h1>
<p>A golang port of the venerable <a href="https://httpbin.org/">httpbin.org</a> HTTP request &amp; response testing service.</p>

<h2 id="ENDPOINTS">ENDPOINTS</h2>

<ul>
<li><a href="/"><code>/</code></a> This page.</li>
<li><a href="/absolute-redirect/6"><code>/absolute-redirect/:n</code></a> 302 Absolute redirects <em>n</em> times.</li>
<li><a href="/anything"><code>/anything/:anything</code></a> Returns anything that is passed to request.</li>
<li><a href="/base64/aHR0cGJpbmdvLm9yZw=="><code>/base64/:value</code></a> Decodes a Base64-encoded string.</li>
<li><a href="/base64/decode/aHR0cGJpbmdvLm9yZw=="><code>/base64/decode/:value</code></a> Explicit URL for decoding a Base64 encoded string.</li>
<li><a href="/base64/encode/httpbingo.org"><code>/base64/encode/:value</code></a> Encodes a string into URL-safe Base64.</li>
<li><a href="/basic-auth/user/passwd"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>
<li><a href="/bearer"><code>/bearer</code></a> Checks Bearer token header - returns 401 if not set.</li>
<li><a href="/brotli"><code><del>/brotli</del></code></a> Returns brotli-encoded data.</del> <i>Not implemented!</i></li>
<li><a href="/bytes/1024"><code>/bytes/:n</code></a> Generates <em>n</em> random bytes of binary data, accepts optional <em>seed</em> integer parameter.</li>
<li><a href="/cache"><code>/cache</code></a> Returns 200 unless an If-Modified-Since or If-None-Match header is provided, when it returns a 304.</li>
<li><a href="/cache/60"><code>/cache/:n</code></a> Sets a Cache-Control header for <em>n</em> seconds.</li>
<li><a href="/cookies"><code>/cookies</code></a> Returns cookie data.</li>
<li><a href="/cookies/delete?k1=&amp;k2="><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>
<li><a href="/cookies/set?k1=v1&amp;k2=v2"><code>/cookies/set?name=value</code></a> Sets one or more simple cookies.</li>
<li><a href="/deflate"><code>/deflate</code></a> Returns deflate-encoded data.</li>
<li><a href="/delay/3"><code>/delay/:n</code></a> Delays responding for <em>min(n, 10)</em> seconds.</li>
<li><code>/delete</code> Returns request data.  Allows only <code>DELETE</code> requests.</li>
<li><a href="/deny"><code>/deny</code></a> Denied by robots.txt file.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5"><code>/digest-auth/:qop/:user/:passwd/:algorithm</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5"><code>/digest-auth/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/drip?code=200&amp;numbytes=5&amp;duration=5"><code>/drip?numbytes=n&amp;duration=s&amp;delay=s&amp;code=code</code></a> Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code.</li>
<li><a href="/dump/request"><code>/dump/request</code></a> Returns the given request in its HTTP/1.x wire approximate representation.</li>
<li><a href="/encoding/utf8"><code>/encoding/utf8</code></a> Returns page containing UTF-8 data.</li>
<li><a href="/etag/etag"><code>/etag/:etag</code></a> Assumes the resource has the given etag and responds to If-None-Match header with a 200 or 304 and If-Match with a 200 or 412 as appropriate.</li>
<li><a href="/forms/post"><code>/forms/post</code></a> HTML form that submits to <em>/post</em></li>
<li><a href="/get"><code>/get</code></a> Returns GET data.</li>
<li><a href="/gzip"><code>/gzip</code></a> Returns gzip-encoded data.</li>
<li><code>/head</code> Returns response headers.  Allows only <code>HEAD</code> requests.</li>
<li><a href="/headers"><code>/headers</code></a> Returns request header dict.</li>
<li><a href="/hidden-basic-auth/user/passwd"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>
<li><a href="/html"><code>/html</code></a> Renders an HTML Page.</li>
<li><a href="/hostname"><code>/hostname</code></a> Returns the name of the host serving the request.</li>
<li><a href="/image"><code>/image</code></a> Returns page containing an image based on sent Accept header.</li>
<li><a href="/image/jpeg"><code>/image/jpeg</code></a> Returns a JPEG image.</li>
<li><a href="/image/png"><code>/image/png</code></a> Returns a PNG image.</li>
<li><a href="/image/svg"><code>/image/svg</code></a> Returns a SVG image.</li>
<li><a href="/image/webp"><code>/image/webp</code></a> Returns a WEBP image.</li>
<li><a href="/ip"><code>/ip</code></a> Returns Origin IP.</li>
<li><a href="/json"><code>/json</code></a> Returns JSON.</li>
<li><a href="/links/10"><code>/links/:n</code></a> Returns page containing <em>n</em> HTML links.</li>
<li><code>/patch</code> Returns request data.  Allows only <code>PATCH</code> requests.</li>
<li><code>/post</code> Returns request data.  Allows only <code>POST</code> requests.</li>
<li><code>/put</code> Returns request data.  Allows only <code>PUT</code> requests.</li>
<li><a href="/range/1024"><code>/range/1024?duration=s&amp;chunk_size=code</code></a> Streams <em>n</em> bytes, and allows specifying a <em>Range</em> header to select a subset of the data. Accepts a <em>chunk_size</em> and request <em>duration</em> parameter.</li>
<li><a href="/redirect-to?status_code=307&amp;url=http%3A%2F%2Fexample.com%2F"><code>/redirect-to?url=foo&status_code=307</code></a> 307 Redirects to the <em>foo</em> URL.</li>
<li><a href="/redirect-to?url=http%3A%2F%2Fexample.com%2F"><code>/redirect-to?url=foo</code></a> 302 Redirects to the <em>foo</em> URL.</li>
<li><a href="/redirect/6"><code>/redirect/:n</code></a> 302 Redirects <em>n</em> times.</li>
<li><a href="/relative-redirect/6"><code>/relative-redirect/:n</code></a> 302 Relative redirects <em>n</em> times.</li>
<li><a href="/response-headers?Server=httpbin&amp;Content-Type=text%2Fplain%3B+charset%3DUTF-8"><code>/response-headers?key=val</code></a> Returns given response headers.</li>
<li><a href="/robots.txt"><code>/robots.txt</code></a> Returns some robots.txt rules.</li>
<li><a href="/sse?delay=1s&amp;duration=5s&count=10"><code>/sse?delay=1s&amp;duration=5s&count=10</code></a> a stream of server-sent events.</li>
<li><a href="/status/418"><code>/status/:code</code></a> Returns given HTTP Status code.</li>
<li><a href="/stream-bytes/1024"><code>/stream-bytes/:n</code></a> Streams <em>n</em> random bytes of binary data, accepts optional <em>seed</em> and <em>chunk_size</em> integer parameters.</li>
<li><a href="/stream/20"><code>/stream/:n</code></a> Streams <em>min(n, 100)</em> lines.</li>
<li><a href="/unstable"><code>/unstable</code></a> Fails half the time, accepts optional <em>failure_rate</em> float and <em>seed</em> integer parameters.</li>
<li><a href="/user-agent"><code>/user-agent</code></a> Returns user-agent.</li>
<li><a href="/uuid"><code>/uuid</code></a> Generates a <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUIDv4</a> value.</li>
<li><a href="/websocket/echo?max_fragment_size=2048&amp;max_message_size=10240"><code>/websocket/echo?max_fragment_size=2048&amp;max_message_size=10240</code></a> A WebSocket echo service.</li>
<li><a href="/xml"><code>/xml</code></a> Returns some XML</li>
</ul>

<h2 id="DESCRIPTION">DESCRIPTION</h2>

<p>Testing an HTTP Library can become difficult sometimes. <a href="http://requestb.in">RequestBin</a> is fantastic for testing POST requests, but doesn't let you control the response. This exists to cover all kinds of HTTP scenarios. Additional endpoints are being considered.</p>

<p>All endpoint responses are JSON-encoded.</p>

<h2 id="EXAMPLES">EXAMPLES</h2>

<h3 id="-curl-http-httpbin-org-ip">$ curl https://httpbingo.org/ip</h3>

<pre><code>{"origin":"73.238.9.52, 77.83.142.42"}
</code></pre>

<h3 id="-curl-http-httpbin-org-user-agent">$ curl https://httpbingo.org/user-agent</h3>

<pre><code>{"user-agent":"curl/7.64.1"}</code></pre>

<h3 id="-curl-http-httpbin-org-get">$ curl https://httpbingo.org/get?foo=bar</h3>

<pre><code>{
  "args": {
    "foo": [
      "bar"
    ]
  },
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Host": [
      "httpbingo.org"
    ],
    "User-Agent": [
      "curl/7.64.1"
    ]
  },
  "origin": "73.238.9.52, 77.83.142.42",
  "url": "https://httpbingo.org/get?foo=bar"
}
</code></pre>

<h3 id="-curl-http-httpbin-org-dump-request">$ curl https://httpbingo.org/dump/request?foo=bar</h3>

<pre><code>GET /dump/request?foo=bar HTTP/1.1
Host: httpbingo.org
Accept: */*
User-Agent: curl/7.64.1
</code></pre>

<h3 id="-curl-I-http-httpbin-org-status-418">$ curl -I https://httpbingo.org/status/418</h3>

<pre><code>HTTP/1.1 418 I'm a teapot
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
X-More-Info: http://tools.ietf.org/html/rfc2324
Date: Tue, 13 Jul 2021 13:12:37 GMT
Content-Length: 0
</code></pre>


<h2 id="AUTHOR">AUTHOR</h2>

<p>Ported to Go by <a href="https://github.com/mccutchen">Will McCutchen</a>.</p>
<p>From <a href="https://httpbin.org/">the original</a> <a href="https://kennethreitz.org/">Kenneth Reitz</a> project.</p>

<h2 id="SEE-ALSO">SEE ALSO</h2>

<p><a href="https://httpbin.org/">httpbin.org</a> &mdash; the original httpbin</p>

</div>

<a href="https://github.com/mccutchen/go-httpbin"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/38ef81f8aca64bb9a64448d0d70f1308ef5341ab/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6461726b626c75655f3132313632312e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>

</body>
</html>

build your ownと書いてあるので,書いてみるか?
スクリーンショット 2024-04-18 12 16 29

アドバイスにしたがって git cloneして,CIを実行した.

# Run unit tests and check code coverage
$ bundle exec rspec

# Check code style
$ bundle exec rubocop

どっちも通った

ruby async_parallel.rb
/Users/matsudatekai/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/io-event-1.5.1/lib/io/event/support.rb:27: warning: IO::Buffer is experimental and both the Ruby and C interface may change in the future!
Time taken: 3.448315

ドキュメントからスクリプトをこぴぺして実行したが,上記のwarningが出た

asyncの開発者の別のリポジトリのissueが解決されていたので読んでみる
socketry/io-event#82

よくみたら同じGemを内部で使っていて同じ事象が起きていた.

何したらいいかわからなくなってきた

業務で使ったautocomplete.jsというvanilla jsのライブラリに一旦変えてみる.
https://github.com/TarekRaafat/autoComplete.js
Apache-2.0 licenseであることを確認.

リポジトリのreadmeを参照.

以下のwarningを確認.

npm i @tarekraafat/autocomplete.js
npm WARN deprecated rollup-plugin-terser@7.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated acorn-dynamic-import@3.0.0: This is probably built in to whatever tool you're using. If you still need it... idk

added 229 packages, and audited 230 packages in 3s

12 packages are looking for funding
  run `npm fund` for details

2 vulnerabilities (1 moderate, 1 critical)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

しょうがないので,業務上ででくわした以下のバグを報告する.
スクリーンショット 2024-04-18 14 27 17

autocompleteの候補が変な場所に出てくる.

上記の再現手順を確認して,可能であればPRとしてあげる.
時間内で厳しければIssueとしてあげることにした.

別環境で再現できなさそうで辛い.

いうて再現状況が結構限定的な気もするので,報告する必要性もない気がしてきた.

別にバグではなかった.非常に残念.

faradayのやつだけ報告して終わるか.

もうちょっとマシなFBできないか探索だけしてみる.

画像が死んでいたので報告する.
https://www.rubydoc.info/github/lostisland/faraday
スクリーンショット 2024-04-18 15 36 16

画像のパスは合っている気がするので,ローカルで自動生成したRdocでは表示されているが,https://www.rubydoc.info/github/lostisland/faraday
のドメインでは参照できない,というだけな気がする.

faraday側でテンプレがあったので,下に書いていく.

Basic Info

  • Faraday Version: 2.9.0
  • Ruby Version: 3.2.2

Issue description

When you access to this page, could not see home-logo image, like following.

スクリーンショット 2024-04-18 15 36 16

Seems to be it is local relative path, so you have to change image path like below.

https://github.com/lostisland/faraday/raw/main/docs/_media/home-logo.svg

Steps to reproduce

Just Access to following page.
https://www.rubydoc.info/github/lostisland/faraday

上記でいいか確認して,Issueを出そう!

TODO: 期待値を記載する,出すべき画像のsnapshotで良い.

おつかれさまでした!

ワークショップの終了にともないissueを閉じますが、このまま作業メモとして使っても構いません 👌

ワークショップの感想を集めています!

ブログなどに書かれた際は、このページへリンクの追加をお願いします 🙏

またの参加をお待ちしています!