Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
787 changes: 36 additions & 751 deletions README.jp.md

Large diffs are not rendered by default.

433 changes: 27 additions & 406 deletions README.md

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions docs/en/01-getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Getting Started

## Installation

```ruby
gem 'my_api_client'
```

## Rails Generator

```sh
rails g api_client path/to/resource get:path/to/resource --endpoint https://example.com
```

This generates:

- `app/api_clients/application_api_client.rb`
- `app/api_clients/path/to/resource_api_client.rb`
- `spec/api_clients/path/to/resource_api_client_spec.rb`

## Minimal Class

```ruby
class ApplicationApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'
end
```

Define shared endpoint, logger, and common error handling in this base class.
47 changes: 47 additions & 0 deletions docs/en/02-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Basic Usage and Pagination

## Basic Request DSL

```ruby
class ExampleApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'

def get_users(condition:)
get 'users', query: { search: condition }
end

def post_user(name:)
post 'users', body: { name: name }
end
end
```

Available methods:

- `get`
- `post`
- `put`
- `patch`
- `delete`

Each method returns `response.data` (`Sawyer::Resource`) unless you pass a block.

## Pagination (`pageable_get` / `pget`)

```ruby
class MyPaginationApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'

def users
pageable_get 'users', paging: '$.links.next', query: { page: 1 }
end
end

api_client = MyPaginationApiClient.new
api_client.users.take(3).each { |page| p page }
```

`paging` can be:

- JSONPath string (for response body)
- `Proc` (custom next-page extraction)
57 changes: 57 additions & 0 deletions docs/en/03-error-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Error Handling

## Basic Rules

```ruby
class ExampleApiClient < MyApiClient::Base
endpoint 'https://example.com'

error_handling status_code: 400..499, raise: MyApiClient::ClientError
error_handling status_code: 500..599, raise: MyApiClient::ServerError
end
```

Matchers support:

- `status_code`: `Integer`, `Range`, `Regexp`, `Symbol`
- `headers`: `Hash` values with `String` or `Regexp`
- `json`: `Hash` values with `String`, `Integer`, `Range`, `Regexp`, `Symbol`

## Custom Processing

Use a block:

```ruby
error_handling status_code: 500..599, raise: MyApiClient::ServerError do |params, logger|
logger.warn "Response Body: #{params.response&.body.inspect}"
end
```

Or use `with:` to call an instance method:

```ruby
error_handling json: { '$.errors.code': 10..19 }, with: :log_error
```

## `forbid_nil`

```ruby
error_handling status_code: 200, json: :forbid_nil
```

Useful when an empty response body should be treated as an error.

## Error Object (`MyApiClient::Error`)

All gem-specific errors inherit `MyApiClient::Error` and expose `#params`.

```ruby
begin
api_client.get_users(condition: 'john')
rescue MyApiClient::Error => e
e.params.request
e.params.response
end
```

`e.params.metadata` (or `e.metadata`) is useful for external logging tools.
58 changes: 58 additions & 0 deletions docs/en/04-retry-timeout-logger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Retry, Timeout, Logger

## Retry

```ruby
class ExampleApiClient < MyApiClient::Base
endpoint 'https://example.com'

error_handling json: { '$.errors.code': 20 },
raise: MyApiClient::ApiLimitError,
retry: { wait: 30.seconds, attempts: 3 }
end
```

Notes:

- `retry: true` uses default retry options.
- `retry` requires `raise`.
- `retry` cannot be combined with block-style `error_handling`.

`MyApiClient::NetworkError` is retried by default.

## Network Error

`MyApiClient::NetworkError` wraps low-level errors and exposes `#original_error`.

```ruby
begin
api_client.get_users(condition: 'john')
rescue MyApiClient::NetworkError => e
e.original_error
e.params.response # nil
end
```

## Timeout

```ruby
class ExampleApiClient < MyApiClient::Base
endpoint 'https://example.com'
http_read_timeout 10
http_open_timeout 5
end
```

## Logger

```ruby
class ExampleApiClient < MyApiClient::Base
self.logger = Rails.logger
end
```

Request logs are emitted like:

- `Start`
- `Duration ... msec`
- `Success (...)` or `Failure (...)`
50 changes: 50 additions & 0 deletions docs/en/05-rspec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# RSpec Helpers and Matchers

## Setup

Add to `spec/spec_helper.rb` or `spec/rails_helper.rb`:

```ruby
require 'my_api_client/rspec'
```

## Request Matcher (`request_to`)

```ruby
expect { api_client.get_users(condition: 'john') }
.to request_to(:get, 'https://example.com/v1/users')
.with(query: { search: 'john' })
```

## Error Matcher (`be_handled_as_an_error`)

```ruby
expect { api_client.get_users(condition: 'john') }
.to be_handled_as_an_error(MyApiClient::ClientError)
.when_receive(status_code: 200, body: { errors: { code: 10 } }.to_json)
```

Retry assertion:

```ruby
expect { api_client.get_users(condition: 'john') }
.to be_handled_as_an_error(MyApiClient::ApiLimitError)
.after_retry(3).times
.when_receive(status_code: 200, body: { errors: { code: 20 } }.to_json)
```

## Stubbing (`stub_api_client`, `stub_api_client_all`)

```ruby
stub_api_client_all(ExampleApiClient, request: { response: { id: 1 } })
ExampleApiClient.new.request(user_id: 10).id # => 1
```

Supported options per action:

- plain hash response
- `response:`
- `raise:` (error class or instance)
- `status_code:` (with `raise:`)
- `pageable:` (Enumerable for pagination)
- `Proc` for dynamic response
23 changes: 23 additions & 0 deletions docs/en/06-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Development and Release

## Local Development

```sh
bin/setup
rake spec
bin/console
```

## Install Gem Locally

```sh
bundle exec rake install
```

## Release

```sh
bundle exec rake release
```

This creates a tag, pushes commits/tags, and publishes to RubyGems.
29 changes: 29 additions & 0 deletions docs/ja/01-getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# はじめに

## インストール

```ruby
gem 'my_api_client'
```

## Rails Generator

```sh
rails g api_client path/to/resource get:path/to/resource --endpoint https://example.com
```

以下のようなファイルが生成されます。

- `app/api_clients/application_api_client.rb`
- `app/api_clients/path/to/resource_api_client.rb`
- `spec/api_clients/path/to/resource_api_client_spec.rb`

## 最小構成

```ruby
class ApplicationApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'
end
```

共通の `endpoint`、`logger`、エラーハンドリングは親クラスで定義すると運用しやすいです。
47 changes: 47 additions & 0 deletions docs/ja/02-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 基本的な使い方とページネーション

## 基本リクエスト DSL

```ruby
class ExampleApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'

def get_users(condition:)
get 'users', query: { search: condition }
end

def post_user(name:)
post 'users', body: { name: name }
end
end
```

利用できるメソッド:

- `get`
- `post`
- `put`
- `patch`
- `delete`

ブロックを渡さない場合、戻り値は `response.data` (`Sawyer::Resource`) です。

## ページネーション (`pageable_get` / `pget`)

```ruby
class MyPaginationApiClient < MyApiClient::Base
endpoint 'https://example.com/v1'

def users
pageable_get 'users', paging: '$.links.next', query: { page: 1 }
end
end

api_client = MyPaginationApiClient.new
api_client.users.take(3).each { |page| p page }
```

`paging` には次を指定できます。

- JSONPath 文字列
- `Proc`(次ページ URL の独自抽出)
Loading