gemfather-stable 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +145 -0
- data/bin/gemfather +6 -0
- data/gem_template/%app_name%.gemspec.tt +32 -0
- data/gem_template/.gitignore.tt +26 -0
- data/gem_template/.rubocop-rspec.yml.tt +38 -0
- data/gem_template/.rubocop-ruby.yml.tt +54 -0
- data/gem_template/.rubocop.yml.tt +11 -0
- data/gem_template/Gemfile.tt +17 -0
- data/gem_template/Makefile.tt +21 -0
- data/gem_template/README.md.tt +180 -0
- data/gem_template/Rakefile.tt +6 -0
- data/gem_template/bin/console.tt +21 -0
- data/gem_template/bin/generate.tt +6 -0
- data/gem_template/bin/rubocop.tt +29 -0
- data/gem_template/lib/%app_name%.rb.tt +25 -0
- data/gem_template/lib/%app_short_name%/client.rb.tt +18 -0
- data/gem_template/lib/%app_short_name%/http_errors.rb.tt +20 -0
- data/gem_template/lib/%app_short_name%/railtie.rb.tt +5 -0
- data/gem_template/lib/%app_short_name%/version.rb.tt +5 -0
- data/gem_template/log/.keep.tt +0 -0
- data/gem_template/spec/spec_helper.rb.tt +35 -0
- data/lib/api_generator/client/config.rb +57 -0
- data/lib/api_generator/client/connection.rb +149 -0
- data/lib/api_generator/commands/generate.rb +56 -0
- data/lib/api_generator/helpers/utils.rb +56 -0
- data/lib/api_generator/middleware/error_code_middleware.rb +43 -0
- data/lib/api_generator/middleware/error_handler_middleware.rb +26 -0
- data/lib/api_generator/middleware/handle_unsuccessful_request_middleware.rb +36 -0
- data/lib/api_generator/middleware/http_errors.rb +21 -0
- data/lib/api_generator/middleware/raise_error_base.rb +11 -0
- data/lib/api_generator/middleware/raise_error_dsl.rb +36 -0
- data/lib/api_generator/middleware.rb +4 -0
- data/lib/api_generator/models/base.rb +15 -0
- data/lib/api_generator/models/data.rb +16 -0
- data/lib/api_generator/pagination/dsl.rb +32 -0
- data/lib/api_generator/pagination/limit_offset_relation.rb +24 -0
- data/lib/api_generator/pagination/page_relation.rb +21 -0
- data/lib/api_generator/pagination/relation.rb +114 -0
- data/lib/api_generator/pagination.rb +2 -0
- data/lib/api_generator/railtie.rb +24 -0
- data/lib/api_generator/services/base_create.rb +56 -0
- data/lib/api_generator/services/create_api.rb +87 -0
- data/lib/api_generator/services/create_model.rb +69 -0
- data/lib/api_generator/services/create_scaffold.rb +53 -0
- data/lib/api_generator/services/gemfather.rb +81 -0
- data/lib/api_generator/version.rb +3 -0
- data/lib/api_generator.rb +18 -0
- data/templates/api/action.erb +10 -0
- data/templates/api/action_paginate.erb +10 -0
- data/templates/api/api_module.erb +11 -0
- data/templates/api/include_helpers.erb +9 -0
- data/templates/api/include_namespace.erb +1 -0
- data/templates/api/path.erb +1 -0
- data/templates/models/request.erb +11 -0
- data/templates/models/response.erb +11 -0
- data/templates/specs/api_spec.erb +15 -0
- data/templates/specs/request_spec.erb +12 -0
- data/templates/specs/response_spec.erb +12 -0
- metadata +229 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 73b2704a7a26751dddda94278a37d81fd0ed4a411c622e3b189212d25f7357f0
|
4
|
+
data.tar.gz: f2513f818f206125c3752aeea49f308efb33aabbe3a17cc33d054ffc946ce6f5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 83ee29716ac671380b04d80903f6bf85c29c5af4c9922f746de54faee854dc421195e7a68076028d12ec53bdc978cebe6a3f60b77fbda17f16096fc9311bd8cd
|
7
|
+
data.tar.gz: f5309abbd86c427b9d573f9a1d7f0d48d75b3585cc2b8138de0d371c868f54a824195edd5029645d54685330232891e74b7a9fbcbd81ea1744e5dc2610c739e3
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# Gemfather
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
### Гем для генерации API-гемов
|
6
|
+
|
7
|
+
Установка
|
8
|
+
|
9
|
+
`gem install gemfather`
|
10
|
+
|
11
|
+
После установки гем добавляет в путь `$PATH` исполняемый скрипт `gemfather`, который позволит запускать `gemfather` из командной строки
|
12
|
+
|
13
|
+
`gemfather GEM_NAME` создает в текущем каталоге папку GEM_NAME c базовой структурой для гема API
|
14
|
+
|
15
|
+
Описание нового проекта и документация по его конфигурированию и работе также будет сгененрирована в файле `GEM_NAME/README.md`
|
16
|
+
|
17
|
+
Пример:
|
18
|
+
```
|
19
|
+
./gemfather domclick_new_api
|
20
|
+
|
21
|
+
create domclick_new_api
|
22
|
+
create domclick_new_api/domclick_new_api.gemspec
|
23
|
+
create domclick_new_api/Dockerfile.test
|
24
|
+
create domclick_new_api/Gemfile
|
25
|
+
create domclick_new_api/Makefile
|
26
|
+
create domclick_new_api/README.md
|
27
|
+
create domclick_new_api/Rakefile
|
28
|
+
create domclick_new_api/bin/console
|
29
|
+
create domclick_new_api/bin/dc
|
30
|
+
create domclick_new_api/bin/generate
|
31
|
+
create domclick_new_api/lib/domclick_new_api.rb
|
32
|
+
create domclick_new_api/lib/domclick_new_api/client.rb
|
33
|
+
create domclick_new_api/lib/domclick_new_api/railtie.rb
|
34
|
+
create domclick_new_api/lib/domclick_new_api/version.rb
|
35
|
+
create domclick_new_api/spec/spec_helper.rb
|
36
|
+
create domclick_new_api/log
|
37
|
+
chmod domclick_new_api/bin/dc
|
38
|
+
chmod domclick_new_api/bin/generate
|
39
|
+
chmod domclick_new_api/bin/console
|
40
|
+
run bundle binstubs rspec-core from "./domclick_new_api"
|
41
|
+
run bin/rspec from "./domclick_new_api"
|
42
|
+
```
|
43
|
+
|
44
|
+
Внутри каталога есть исполняемый скрипт generate, который генерирует в проекте скаффолд для ручки сервиса
|
45
|
+
|
46
|
+
Генерация скаффолда (api, model, specs) происходит следующим образом:
|
47
|
+
`bin/generate namespace:action:post`
|
48
|
+
|
49
|
+
Пример: `bin/generate deals:create:post`
|
50
|
+
|
51
|
+
Будет добавлена следующая структура каталогов и файлов
|
52
|
+
```
|
53
|
+
create ./lib/domclick_api/api
|
54
|
+
create ./lib/domclick_api/api/deals.rb
|
55
|
+
create ./lib/domclick_api/model/deals/create
|
56
|
+
create ./lib/domclick_api/model/deals/create/request.rb
|
57
|
+
create ./lib/domclick_api/model/deals/create/response.rb
|
58
|
+
create ./spec/api
|
59
|
+
create ./spec/api/deals_spec.rb
|
60
|
+
create ./spec/model/deals/create
|
61
|
+
create ./spec/model/deals/create/request_spec.rb
|
62
|
+
create ./spec/model/deals/create/response_spec.rb
|
63
|
+
```
|
64
|
+
Таким образом, будет создана ручка, вызывающая путь `/create`, модели `request` и `response`, а также тесты для этой ручки
|
65
|
+
|
66
|
+
## Пагинация
|
67
|
+
|
68
|
+
Опционально, если добавить параметр `--paginate` - то ручка будет поддерживать пагинацию. Для конфигурации пагинации используется DSL:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
paginate :find_offers,
|
72
|
+
with: :limit_offset,
|
73
|
+
on_request: lambda { |req, limit: nil, offset: nil, sort: nil|
|
74
|
+
req.params[:limit] = limit if limit.present?
|
75
|
+
req.params[:offset] = offset if offset.present?
|
76
|
+
req.params[:sort] = sort if sort.present?
|
77
|
+
},
|
78
|
+
data_key: :offers,
|
79
|
+
limit: :limit.to_proc,
|
80
|
+
offset: :offset.to_proc,
|
81
|
+
total: :total.to_proc
|
82
|
+
```
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
paginate :find_offers,
|
86
|
+
with: :page,
|
87
|
+
on_request: lambda { |req, page: nil, per_page: nil, sort: nil|
|
88
|
+
req.params[:page] = page if page.present?
|
89
|
+
req.params[:per_page] = per_page if per_page.present?
|
90
|
+
req.params[:sort] = sort if sort.present?
|
91
|
+
},
|
92
|
+
data_key: :offers,
|
93
|
+
per_page: :per_page.to_proc,
|
94
|
+
total: :total.to_proc
|
95
|
+
```
|
96
|
+
|
97
|
+
Таким образом, метод `find_offers` будет возвращать объект relation, реализующий `Enumerable`.
|
98
|
+
Данные при этом не будут загружены до тех пор, пока доступ к ним не понадобится (например, метод `#to_a`).
|
99
|
+
|
100
|
+
## Опции DSL
|
101
|
+
|
102
|
+
`with` - стратегия пагинации, стандартно поддерживаются `:page` и `:limit_offset`.
|
103
|
+
`on_request` - коллбэк вызываемый при инициализации запроса. Позволяет нужным образом передать параметры (например, в теле или в хедерах).
|
104
|
+
`data_key` - ключ, по которому можно получить массив данных из ответа.
|
105
|
+
Может быть пустым, если данные лежат в корне, или массивом, если данные находятся в многоуровневой структуре (например, `[:data, :offers]`).
|
106
|
+
`total` - коллбэк, по которому можно получить из ответа общее количество элементов на всех страницах (не обязательная опция).
|
107
|
+
|
108
|
+
Опции, индивидуальные для стратегий пагинации:
|
109
|
+
|
110
|
+
### Стратегия :page
|
111
|
+
|
112
|
+
`per_page` - коллбэк, по которому можно получить из ответа количество элементов на одной странице (не обязательная опция).
|
113
|
+
|
114
|
+
### Стратегия :limit_offset
|
115
|
+
|
116
|
+
`limit` - коллбэк, по которому можно получить из ответа количество элементов на одной странице (не обязательная опция).
|
117
|
+
`offset` - коллбэк, по которому можно получить из ответа текущее смещение (не обязательная опция).
|
118
|
+
|
119
|
+
## Использование метода с пагинацией
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
# Получение 100 записей, упорядоченных по id, начиная с 50-го элемента
|
123
|
+
client.find_offers(company_id: 1).limit(100).offset(50).order(id: 'desc').to_a
|
124
|
+
|
125
|
+
# Получение всех записей начиная с 50-го элемента
|
126
|
+
client.find_offers(company_id: 1).limit(100).offset(50).all_remaining.to_a
|
127
|
+
|
128
|
+
# Получение 10 записей на второй странице
|
129
|
+
client.find_offers(company_id: 1).page(2).per_page(10).to_a
|
130
|
+
|
131
|
+
# Получение всех записей начиная со второй страницы
|
132
|
+
client.find_offers(company_id: 1).page(2).all_remaining.to_a
|
133
|
+
|
134
|
+
# Получение первого элемента со второй страницы
|
135
|
+
client.find_offers(company_id: 1).page(2).first
|
136
|
+
|
137
|
+
# Получение полного ответа со второй страницы
|
138
|
+
client.find_offers(company_id: 1).page(2).response
|
139
|
+
|
140
|
+
# Получение общего количество элементов на всех страницах в сумме
|
141
|
+
client.find_offers(company_id: 1).page(2).total
|
142
|
+
|
143
|
+
# Получение общего количество элементов на всех страницах в сумме (сначала загрузит все страницы)
|
144
|
+
client.find_offers(company_id: 1).page(2).total!
|
145
|
+
```
|
data/bin/gemfather
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative './lib/<%= app_short_name %>/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = '<%= app_name %>'
|
5
|
+
spec.version = <%= app_name_class %>::Version::VERSION
|
6
|
+
spec.authors = ['<%= author %>']
|
7
|
+
spec.email = ['<%= email %>']
|
8
|
+
|
9
|
+
spec.summary = 'API client for <%= app_name %> service'
|
10
|
+
spec.homepage = 'https://TODO.INSERT/YOUR_REPO/HERE/'
|
11
|
+
spec.required_ruby_version = '>= 2.7'
|
12
|
+
|
13
|
+
spec.metadata = {
|
14
|
+
'bug_tracker_uri' => "#{spec.homepage}/issues",
|
15
|
+
'changelog_uri' => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
16
|
+
'documentation_uri' => "#{spec.homepage}/blob/main/README.md",
|
17
|
+
'homepage_uri' => spec.homepage,
|
18
|
+
'source_code_uri' => spec.homepage,
|
19
|
+
'rubygems_mfa_required' => 'true'
|
20
|
+
}
|
21
|
+
|
22
|
+
spec.files = Dir['lib/**/*', 'README.md']
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'addressable', '~> 2.3'
|
26
|
+
spec.add_dependency 'dry-configurable', '~> 0.11'
|
27
|
+
spec.add_dependency 'gemfather', '~> <%= version %>'
|
28
|
+
spec.add_dependency 'faraday', '>= 0.17', '< 2'
|
29
|
+
spec.add_dependency 'faraday_middleware', '>= 0.13', '< 2'
|
30
|
+
spec.add_dependency 'hashie', '>= 3.0', '< 5'
|
31
|
+
spec.add_dependency 'zeitwerk', '>= 2.2.2'
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
6
|
+
|
7
|
+
# Ignore bundler config.
|
8
|
+
/.bundle
|
9
|
+
|
10
|
+
# Ignore all logfiles and tempfiles.
|
11
|
+
/log/*
|
12
|
+
!/log/.keep
|
13
|
+
|
14
|
+
# Local environments file by gem 'dotenv'
|
15
|
+
.env
|
16
|
+
|
17
|
+
# Ingore all builded gems
|
18
|
+
*.gem
|
19
|
+
|
20
|
+
# Ignore local lock for gem developing
|
21
|
+
Gemfile.lock
|
22
|
+
|
23
|
+
coverage
|
24
|
+
.rspec_status
|
25
|
+
.rspec
|
26
|
+
.yardoc
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
Metrics/BlockLength:
|
4
|
+
Exclude:
|
5
|
+
- app/admin/**/*.rb
|
6
|
+
- spec/**/*.rb
|
7
|
+
|
8
|
+
Metrics/ModuleLength:
|
9
|
+
Exclude:
|
10
|
+
- spec/**/*.rb
|
11
|
+
|
12
|
+
Style/SymbolProc:
|
13
|
+
Exclude:
|
14
|
+
- spec/factories/**/*.rb
|
15
|
+
|
16
|
+
RSpec/ExampleLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
RSpec/HookArgument:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
RSpec/MultipleExpectations:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
RSpec/NestedGroups:
|
26
|
+
Max: 4
|
27
|
+
|
28
|
+
RSpec/FilePath:
|
29
|
+
Exclude:
|
30
|
+
- spec/requests/**/*.rb
|
31
|
+
|
32
|
+
RSpec/DescribeClass:
|
33
|
+
Exclude:
|
34
|
+
- spec/lib/tasks/**/*.rb
|
35
|
+
- spec/config/*.rb
|
36
|
+
|
37
|
+
RSpec/MultipleMemoizedHelpers:
|
38
|
+
Enabled: false
|
@@ -0,0 +1,54 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.7
|
3
|
+
Exclude:
|
4
|
+
- 'bin/**/*'
|
5
|
+
NewCops: enable
|
6
|
+
|
7
|
+
Layout/LineLength:
|
8
|
+
Max: 100
|
9
|
+
|
10
|
+
Bundler/OrderedGems:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Layout/FirstHashElementIndentation:
|
14
|
+
EnforcedStyle: consistent
|
15
|
+
|
16
|
+
Layout/ClassStructure:
|
17
|
+
Enabled: true
|
18
|
+
|
19
|
+
Style/ClassAndModuleChildren:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/Documentation:
|
23
|
+
Enabled: true
|
24
|
+
Include:
|
25
|
+
- 'app/services/**/*'
|
26
|
+
|
27
|
+
Style/FrozenStringLiteralComment:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/AsciiComments:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/Send:
|
34
|
+
Enabled: true
|
35
|
+
|
36
|
+
Style/HashEachMethods:
|
37
|
+
Enabled: true
|
38
|
+
|
39
|
+
Style/HashTransformKeys:
|
40
|
+
Enabled: true
|
41
|
+
|
42
|
+
Style/HashTransformValues:
|
43
|
+
Enabled: true
|
44
|
+
|
45
|
+
Style/TrailingCommaInArguments:
|
46
|
+
EnforcedStyleForMultiline: consistent_comma
|
47
|
+
|
48
|
+
Style/TrailingCommaInArrayLiteral:
|
49
|
+
EnforcedStyleForMultiline: consistent_comma
|
50
|
+
|
51
|
+
Style/TrailingCommaInHashLiteral:
|
52
|
+
EnforcedStyleForMultiline: consistent_comma
|
53
|
+
|
54
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://repo.sberned.ru/repository/rubygems-public/'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in <%= app_name %>.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'bundler', '~> 2.0'
|
9
|
+
gem 'rubocop', '~> 1.8'
|
10
|
+
gem 'rubocop-performance'
|
11
|
+
gem 'rubocop-rspec', '~> 2.1'
|
12
|
+
gem 'rubocop-rake'
|
13
|
+
gem 'dotenv', '~> 2.7'
|
14
|
+
gem 'pry', '~> 0.13'
|
15
|
+
gem 'rake', '~> 12.0'
|
16
|
+
gem 'rspec', '~> 3.9'
|
17
|
+
gem 'vcr', '~> 4.0'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
default: lint test
|
2
|
+
|
3
|
+
setup:
|
4
|
+
bundle install
|
5
|
+
|
6
|
+
test:
|
7
|
+
bin/rspec
|
8
|
+
|
9
|
+
lint:
|
10
|
+
bin/rubocop
|
11
|
+
|
12
|
+
cleanup:
|
13
|
+
rm -f <%= app_name %>-*.gem
|
14
|
+
|
15
|
+
build:
|
16
|
+
gem build <%= app_name %>.gemspec
|
17
|
+
|
18
|
+
publish:
|
19
|
+
gem push <%= app_name %>-*.gem
|
20
|
+
|
21
|
+
deploy: build publish cleanup
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# <%= app_name_class %>
|
2
|
+
|
3
|
+
Ruby клиент для взаимодейтсвия с API сервиса [<%= app_name_class %> API](https://<%= app_name %>.dev/)
|
4
|
+
|
5
|
+
## Установка
|
6
|
+
|
7
|
+
Добавьте эту строку в Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem '<%= app_name %>'
|
11
|
+
```
|
12
|
+
|
13
|
+
Выполните:
|
14
|
+
|
15
|
+
```console
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Или установите из командной строки:
|
20
|
+
|
21
|
+
```console
|
22
|
+
$ gem install <%= app_name %>
|
23
|
+
```
|
24
|
+
|
25
|
+
## Конфигурация
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
<%= app_name_class %>::Client.configure do |config|
|
29
|
+
config.api_endpoint = 'https://<%= app_name %>.dev/'
|
30
|
+
config.api_user = 'user'
|
31
|
+
config.api_password = 'password'
|
32
|
+
config.open_timeout = 5
|
33
|
+
config.read_timeout = 5
|
34
|
+
config.user_agent = 'info for debugging' # default is `Ruby <%= app_name_class %> API Client`
|
35
|
+
config.logger = Logger.new(STDERR) # по дефолту `Rails.logger` в development окружении, в других - nil
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
## Авторизация
|
40
|
+
|
41
|
+
Из коробки есть:
|
42
|
+
- По токену
|
43
|
+
|
44
|
+
Для конфигурации в config нужно передать заголовок и сам токен
|
45
|
+
```ruby
|
46
|
+
config.api_token
|
47
|
+
config.api_header
|
48
|
+
```
|
49
|
+
- Basic auth
|
50
|
+
|
51
|
+
Для конфигурации в config нужно передать логин и пароль
|
52
|
+
```ruby
|
53
|
+
config.api_user
|
54
|
+
config.api_password
|
55
|
+
```
|
56
|
+
- Для прочих кастомных настроек соединения при инициализации клиента передается блок
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
<%= app_name_class %>::Client.new do |connection|
|
60
|
+
# кастомные настройки
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
## Обработка ошибок
|
65
|
+
Обработка ошибок реализована через DSL в клиенте вида
|
66
|
+
|
67
|
+
`on_status 4XX, error: :error_name`
|
68
|
+
|
69
|
+
При появлении статуса `4ХХ`, будет выброшено исключение `ErrorName`
|
70
|
+
|
71
|
+
|
72
|
+
## Использование
|
73
|
+
|
74
|
+
В папке `bin` существует скрипт `generate`, который генерирует в проекте скаффолд для ручки сервиса
|
75
|
+
|
76
|
+
Генерация скаффолда (api, model, specs) происходит следующим образом:
|
77
|
+
`bin/generate namespace:action:post`
|
78
|
+
|
79
|
+
Пример: `bin/generate deals:create:post`
|
80
|
+
|
81
|
+
Будет добавлена следующая структура каталогов и файлов
|
82
|
+
```console
|
83
|
+
create ./lib/domclick_api/api
|
84
|
+
create ./lib/domclick_api/api/deals.rb
|
85
|
+
create ./lib/domclick_api/model/deals/create
|
86
|
+
create ./lib/domclick_api/model/deals/create/request.rb
|
87
|
+
create ./lib/domclick_api/model/deals/create/response.rb
|
88
|
+
create ./spec/api
|
89
|
+
create ./spec/api/deals_spec.rb
|
90
|
+
create ./spec/model/deals/create
|
91
|
+
create ./spec/model/deals/create/request_spec.rb
|
92
|
+
create ./spec/model/deals/create/response_spec.rb
|
93
|
+
```
|
94
|
+
Таким образом, будет создана ручка, вызывающая путь `/create`, модели `request` и `response`, а также тесты для этой ручки
|
95
|
+
|
96
|
+
Вызов ручки:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
client = <%= app_name_class %>.client
|
100
|
+
client.create(params)
|
101
|
+
```
|
102
|
+
Опционально, если добавить параметр `--paginate` - то ручка будет поддерживать пагинацию. Для конфигурации пагинации используется DSL:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
paginate :find_offers,
|
106
|
+
with: :limit_offset,
|
107
|
+
on_request: lambda { |req, limit: nil, offset: nil, sort: nil|
|
108
|
+
req.params[:limit] = limit if limit.present?
|
109
|
+
req.params[:offset] = offset if offset.present?
|
110
|
+
req.params[:sort] = sort if sort.present?
|
111
|
+
},
|
112
|
+
data_key: :offers,
|
113
|
+
limit: :limit.to_proc,
|
114
|
+
offset: :offset.to_proc,
|
115
|
+
total: :total.to_proc
|
116
|
+
```
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
paginate :find_offers,
|
120
|
+
with: :page,
|
121
|
+
on_request: lambda { |req, page: nil, per_page: nil, sort: nil|
|
122
|
+
req.params[:page] = page if page.present?
|
123
|
+
req.params[:per_page] = per_page if per_page.present?
|
124
|
+
req.params[:sort] = sort if sort.present?
|
125
|
+
},
|
126
|
+
data_key: :offers,
|
127
|
+
per_page: :per_page.to_proc,
|
128
|
+
total: :total.to_proc
|
129
|
+
```
|
130
|
+
|
131
|
+
Таким образом, метод `find_offers` будет возвращать объект relation, реализующий `Enumerable`.
|
132
|
+
Данные при этом не будут загружены до тех пор, пока доступ к ним не понадобится (например, метод `#to_a`).
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
client.find_offers(company_id: 1).limit(100).offset(50).order(id: 'desc').map(&:id)
|
136
|
+
```
|
137
|
+
|
138
|
+
## Разработка
|
139
|
+
|
140
|
+
Установка:
|
141
|
+
|
142
|
+
```console
|
143
|
+
make setup
|
144
|
+
```
|
145
|
+
|
146
|
+
Тесты:
|
147
|
+
|
148
|
+
```console
|
149
|
+
make test
|
150
|
+
```
|
151
|
+
|
152
|
+
Рубокоп:
|
153
|
+
|
154
|
+
```console
|
155
|
+
make lint
|
156
|
+
```
|
157
|
+
|
158
|
+
Очистка:
|
159
|
+
|
160
|
+
```console
|
161
|
+
make cleanup
|
162
|
+
```
|
163
|
+
|
164
|
+
Создать docker образ:
|
165
|
+
|
166
|
+
```console
|
167
|
+
make build
|
168
|
+
```
|
169
|
+
|
170
|
+
Поставка гема в RubyGems:
|
171
|
+
|
172
|
+
```console
|
173
|
+
make publish
|
174
|
+
```
|
175
|
+
|
176
|
+
Деплой:
|
177
|
+
|
178
|
+
```console
|
179
|
+
make deploy
|
180
|
+
```
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'dotenv/load'
|
5
|
+
require 'irb'
|
6
|
+
require_relative '../lib/<%= app_name %>'
|
7
|
+
|
8
|
+
<%= app_name_class %>::Client.configure do |configuration|
|
9
|
+
configuration.api_endpoint = 'http://server.dev'
|
10
|
+
configuration.api_token = 'TOKEN'
|
11
|
+
|
12
|
+
configuration.logger = Logger.new(STDOUT)
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
# Put your test code here
|
17
|
+
rescue StandardError => e
|
18
|
+
pp e
|
19
|
+
end
|
20
|
+
|
21
|
+
binding.irb
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'addressable'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'api_generator'
|
4
|
+
require 'dry-configurable'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
require 'hashie'
|
8
|
+
require 'time'
|
9
|
+
require 'zeitwerk'
|
10
|
+
|
11
|
+
require '<%= app_short_name %>/railtie' if defined?(Rails)
|
12
|
+
|
13
|
+
module <%= app_short_name_class %>
|
14
|
+
def self.client
|
15
|
+
return @client if defined?(@client) && @client.class.same_config?(Client.config)
|
16
|
+
|
17
|
+
@client = Client.new(**Client.config.to_h)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
loader = Zeitwerk::Loader.new
|
22
|
+
loader.tag = File.basename(__FILE__, '.rb')
|
23
|
+
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
24
|
+
loader.push_dir("#{__dir__}/<%= app_short_name %>", namespace: <%= app_name_class %>)
|
25
|
+
loader.setup
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module <%= app_short_name_class %>
|
2
|
+
class Client < ApiGenerator::Client::Connection
|
3
|
+
# INCLUDES
|
4
|
+
|
5
|
+
# ERROR_HANDLING
|
6
|
+
on_status 400, error: :invalid_request_error
|
7
|
+
on_status 401, error: :unauthorized_error
|
8
|
+
on_status 403, error: :forbidden_error
|
9
|
+
on_status 404, error: :resource_not_found_error
|
10
|
+
on_status 405, error: :method_not_allowed_error
|
11
|
+
on_status 413, error: :payload_too_large_error
|
12
|
+
on_status 422, error: :unprocessable_entity
|
13
|
+
|
14
|
+
def customize_connection!(connection)
|
15
|
+
# set custom authorization here if needed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module <%= app_short_name_class %>
|
2
|
+
module HttpErrors
|
3
|
+
class ConnectionError < StandardError; end
|
4
|
+
|
5
|
+
class BaseError < StandardError
|
6
|
+
attr_reader :body
|
7
|
+
|
8
|
+
def initialize(msg = nil, body = nil)
|
9
|
+
@body = body if body
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ServerError < BaseError; end
|
15
|
+
|
16
|
+
class ClientError < BaseError; end
|
17
|
+
|
18
|
+
class UnsuccessfulRequestError < ClientError; end
|
19
|
+
end
|
20
|
+
end
|