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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +145 -0
  3. data/bin/gemfather +6 -0
  4. data/gem_template/%app_name%.gemspec.tt +32 -0
  5. data/gem_template/.gitignore.tt +26 -0
  6. data/gem_template/.rubocop-rspec.yml.tt +38 -0
  7. data/gem_template/.rubocop-ruby.yml.tt +54 -0
  8. data/gem_template/.rubocop.yml.tt +11 -0
  9. data/gem_template/Gemfile.tt +17 -0
  10. data/gem_template/Makefile.tt +21 -0
  11. data/gem_template/README.md.tt +180 -0
  12. data/gem_template/Rakefile.tt +6 -0
  13. data/gem_template/bin/console.tt +21 -0
  14. data/gem_template/bin/generate.tt +6 -0
  15. data/gem_template/bin/rubocop.tt +29 -0
  16. data/gem_template/lib/%app_name%.rb.tt +25 -0
  17. data/gem_template/lib/%app_short_name%/client.rb.tt +18 -0
  18. data/gem_template/lib/%app_short_name%/http_errors.rb.tt +20 -0
  19. data/gem_template/lib/%app_short_name%/railtie.rb.tt +5 -0
  20. data/gem_template/lib/%app_short_name%/version.rb.tt +5 -0
  21. data/gem_template/log/.keep.tt +0 -0
  22. data/gem_template/spec/spec_helper.rb.tt +35 -0
  23. data/lib/api_generator/client/config.rb +57 -0
  24. data/lib/api_generator/client/connection.rb +149 -0
  25. data/lib/api_generator/commands/generate.rb +56 -0
  26. data/lib/api_generator/helpers/utils.rb +56 -0
  27. data/lib/api_generator/middleware/error_code_middleware.rb +43 -0
  28. data/lib/api_generator/middleware/error_handler_middleware.rb +26 -0
  29. data/lib/api_generator/middleware/handle_unsuccessful_request_middleware.rb +36 -0
  30. data/lib/api_generator/middleware/http_errors.rb +21 -0
  31. data/lib/api_generator/middleware/raise_error_base.rb +11 -0
  32. data/lib/api_generator/middleware/raise_error_dsl.rb +36 -0
  33. data/lib/api_generator/middleware.rb +4 -0
  34. data/lib/api_generator/models/base.rb +15 -0
  35. data/lib/api_generator/models/data.rb +16 -0
  36. data/lib/api_generator/pagination/dsl.rb +32 -0
  37. data/lib/api_generator/pagination/limit_offset_relation.rb +24 -0
  38. data/lib/api_generator/pagination/page_relation.rb +21 -0
  39. data/lib/api_generator/pagination/relation.rb +114 -0
  40. data/lib/api_generator/pagination.rb +2 -0
  41. data/lib/api_generator/railtie.rb +24 -0
  42. data/lib/api_generator/services/base_create.rb +56 -0
  43. data/lib/api_generator/services/create_api.rb +87 -0
  44. data/lib/api_generator/services/create_model.rb +69 -0
  45. data/lib/api_generator/services/create_scaffold.rb +53 -0
  46. data/lib/api_generator/services/gemfather.rb +81 -0
  47. data/lib/api_generator/version.rb +3 -0
  48. data/lib/api_generator.rb +18 -0
  49. data/templates/api/action.erb +10 -0
  50. data/templates/api/action_paginate.erb +10 -0
  51. data/templates/api/api_module.erb +11 -0
  52. data/templates/api/include_helpers.erb +9 -0
  53. data/templates/api/include_namespace.erb +1 -0
  54. data/templates/api/path.erb +1 -0
  55. data/templates/models/request.erb +11 -0
  56. data/templates/models/response.erb +11 -0
  57. data/templates/specs/api_spec.erb +15 -0
  58. data/templates/specs/request_spec.erb +12 -0
  59. data/templates/specs/response_spec.erb +12 -0
  60. metadata +229 -0
@@ -0,0 +1,5 @@
1
+ module <%= app_short_name_class %>
2
+ class Railtie < Rails::Railtie
3
+ extend ApiGenerator::Railtie
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module <%= app_short_name_class %>
2
+ module Version
3
+ VERSION = '0.0.1'.freeze
4
+ end
5
+ end
File without changes
@@ -0,0 +1,35 @@
1
+ require 'bundler/setup'
2
+ require '<%= app_name %>'
3
+ require 'dotenv/load'
4
+ require 'vcr'
5
+
6
+ VCR.configure do |config|
7
+ config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
8
+
9
+ config.hook_into :faraday
10
+
11
+ config.before_record do |vcr|
12
+ vcr.response.body.force_encoding('UTF-8')
13
+ end
14
+
15
+ config.configure_rspec_metadata!
16
+ end
17
+
18
+ RSpec.configure do |config|
19
+ config.expect_with :rspec do |c|
20
+ c.syntax = :expect
21
+ end
22
+
23
+ config.order = :random
24
+
25
+ Kernel.srand config.seed
26
+
27
+ config.before do
28
+ <%= app_name_class %>::Client.configure do |configuration|
29
+ configuration.api_endpoint = 'http://<%= app_name %>.dev'
30
+ configuration.api_header = 'X-Source-Token'
31
+ configuration.api_token = 'TEST_TOKEN'
32
+ configuration.logger = Logger.new(Pathname.new(__dir__).join('..', 'log', 'test.log'))
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ module ApiGenerator
2
+ module Client
3
+ module Config
4
+ class ConfigurationError < StandardError; end
5
+
6
+ USER_AGENT = 'Ruby API Client'.freeze
7
+ OPEN_TIMEOUT = 3
8
+ READ_TIMEOUT = 3
9
+ MAX_RETRIES = 3
10
+ RETRY_INTERVAL = 1
11
+ RETRY_BACKOFF_FACTOR = 1
12
+ CONNECTION_ERROR = ApiGenerator::Middleware::HttpErrors::ConnectionError
13
+
14
+ # rubocop:disable Metrics/MethodLength
15
+ def self.extended(klass)
16
+ klass.extend Dry::Configurable
17
+
18
+ klass.class_eval do
19
+ setting :api_endpoint, reader: true
20
+ setting :user_agent, default: USER_AGENT, reader: true
21
+
22
+ setting :api_header, reader: true
23
+ setting :api_token, reader: true
24
+
25
+ setting :api_user, reader: true
26
+ setting :api_password, reader: true
27
+
28
+ setting :ssl_verify, reader: true
29
+ setting :ca_file, reader: true
30
+
31
+ setting :open_timeout, default: OPEN_TIMEOUT, reader: true
32
+ setting :read_timeout, default: READ_TIMEOUT, reader: true
33
+ setting :max_retries, default: MAX_RETRIES, reader: true
34
+ setting :retry_interval, default: RETRY_INTERVAL, reader: true
35
+ setting :retry_backoff_factor, default: RETRY_BACKOFF_FACTOR, reader: true
36
+ setting :logger, reader: true
37
+
38
+ setting :retriable_errors, default: [CONNECTION_ERROR], reader: true
39
+ end
40
+ end
41
+ # rubocop:enable Metrics/MethodLength
42
+
43
+ def check_config
44
+ raise ConfigurationError, 'API endpoint is not configured.' if config.api_endpoint.nil?
45
+ end
46
+
47
+ def inherit_config!(other_config)
48
+ config.to_h.each_key { |setting| config[setting] = other_config[setting] }
49
+ config
50
+ end
51
+
52
+ def same_config?(other_config)
53
+ config.to_h == other_config.to_h
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,149 @@
1
+ module ApiGenerator
2
+ module Client
3
+ # rubocop:disable Metrics/ClassLength
4
+ class Connection < SimpleDelegator
5
+ extend Config
6
+
7
+ class << self
8
+ def inherited(subclass)
9
+ subclass.extend Middleware::RaiseErrorDsl
10
+
11
+ error_namespace = begin
12
+ subclass.module_parent::HttpErrors
13
+ rescue NameError
14
+ ApiGenerator::Middleware::HttpErrors
15
+ end
16
+
17
+ subclass.setup_middleware(error_namespace)
18
+
19
+ super
20
+ end
21
+
22
+ def setup_middleware(error_namespace)
23
+ unsuccessful_request_middleware = Class.new(
24
+ Middleware::HandleUnsuccessfulRequestMiddleware,
25
+ )
26
+ unsuccessful_request_middleware.error_namespace = error_namespace if error_namespace
27
+ const_set(:HandleUnsuccessfulRequestMiddleware, unsuccessful_request_middleware)
28
+
29
+ error_handler_middleware = Class.new(Middleware::ErrorHandlerMiddleware)
30
+ error_handler_middleware.error_namespace = error_namespace if error_namespace
31
+ const_set(:ErrorHandlerMiddleware, error_handler_middleware)
32
+ end
33
+ end
34
+
35
+ ACCEPT_HEADER = 'application/json'.freeze
36
+
37
+ attr_accessor :customization_block
38
+
39
+ def initialize(**settings, &blk)
40
+ if instance_of?(Connection)
41
+ raise LoadError, 'This class is abstract and is not intended to be initialized directly'
42
+ end
43
+
44
+ self.customization_block = blk if blk
45
+
46
+ config = self.class.inherit_config!(self.class.config)
47
+ settings.each { |setting, value| config[setting] = value }
48
+ self.class.check_config
49
+
50
+ super(connection)
51
+ end
52
+
53
+ private
54
+
55
+ # rubocop:disable Metrics/MethodLength
56
+ def connection
57
+ @connection ||= Faraday.new(url: self.class.api_endpoint) do |connection|
58
+ setup_auth!(connection)
59
+
60
+ setup_failed_req_handling!(connection)
61
+ setup_timeouts!(connection)
62
+ setup_user_agent!(connection)
63
+ setup_logger!(connection)
64
+ setup_retries!(connection)
65
+ setup_error_handling!(connection)
66
+ setup_error_code_middleware!(connection)
67
+ setup_ssl(connection)
68
+
69
+ customize_connection!(connection)
70
+
71
+ # When set last - req/res parsed as json in middleware
72
+ setup_json!(connection)
73
+
74
+ connection.adapter(Faraday.default_adapter)
75
+ end
76
+ end
77
+ # rubocop:enable Metrics/MethodLength
78
+
79
+ # rubocop:disable Metrics/AbcSize
80
+ def setup_auth!(connection)
81
+ if self.class.api_token && self.class.api_header
82
+ connection.headers[self.class.api_header] = self.class.api_token
83
+ elsif self.class.api_user && self.class.api_password
84
+ connection.use(
85
+ Faraday::Request::BasicAuthentication,
86
+ self.class.api_user,
87
+ self.class.api_password,
88
+ )
89
+ end
90
+ end
91
+ # rubocop:enable Metrics/AbcSize
92
+
93
+ def setup_json!(connection)
94
+ connection.headers[:accept] = ACCEPT_HEADER
95
+ connection.request(:json)
96
+ connection.response(:json)
97
+ end
98
+
99
+ def setup_timeouts!(connection)
100
+ connection.options.open_timeout = self.class.open_timeout
101
+ connection.options.timeout = self.class.read_timeout
102
+ end
103
+
104
+ def setup_user_agent!(connection)
105
+ connection.headers[:user_agent] = self.class.user_agent
106
+ end
107
+
108
+ def setup_logger!(connection)
109
+ connection.response(:logger, self.class.logger) if self.class.logger
110
+ end
111
+
112
+ def setup_ssl(connection)
113
+ connection.ssl.verify = self.class.ssl_verify
114
+ connection.ssl.ca_file = self.class.ca_file
115
+ end
116
+
117
+ def setup_retries!(connection)
118
+ return if self.class.max_retries <= 0
119
+
120
+ connection.request(
121
+ :retry,
122
+ max: self.class.max_retries,
123
+ interval: self.class.retry_interval,
124
+ backoff_factor: self.class.retry_backoff_factor,
125
+ exceptions: self.class.retriable_errors,
126
+ )
127
+ end
128
+
129
+ def setup_failed_req_handling!(connection)
130
+ connection.use(self.class::HandleUnsuccessfulRequestMiddleware)
131
+ end
132
+
133
+ def setup_error_handling!(connection)
134
+ connection.use(self.class::ErrorHandlerMiddleware)
135
+ end
136
+
137
+ def setup_error_code_middleware!(connection)
138
+ connection.use(self.class::ErrorCodeMiddleware)
139
+ end
140
+
141
+ def customize_connection!(connection)
142
+ return unless customization_block
143
+
144
+ customization_block.call(connection)
145
+ end
146
+ end
147
+ # rubocop:enable Metrics/ClassLength
148
+ end
149
+ end
@@ -0,0 +1,56 @@
1
+ module ApiGenerator
2
+ module Commands
3
+ class Generate
4
+ # rubocop:disable Layout/LineLength
5
+ ARGS_REGEX = /(?<namespace>[\w\-_]+):(?<action>[\w\-_]+):(?<http_method>get|post|patch|put)/i.freeze
6
+ # rubocop:enable Layout/LineLength
7
+
8
+ def initialize(argv)
9
+ @argv = argv[0..1]
10
+ end
11
+
12
+ def call
13
+ service_args = [*parse_parameters(@argv), paginate(@argv)]
14
+
15
+ shutdown unless service_args[0..2].all?(&:present?)
16
+
17
+ Services::CreateScaffold.new(*service_args).call
18
+ end
19
+
20
+ private
21
+
22
+ def usage
23
+ <<~USAGE
24
+ Generate API scaffold (api_namespace, models, specs)
25
+ Usage: bin/generate NAMESPACE:ACTION:HTTP_METHOD [--paginate]
26
+ Examples:
27
+ bin/generate deals:search:post --paginate
28
+ bin/generate persons:create:post
29
+ bin/generate deals:update:put
30
+ USAGE
31
+ end
32
+
33
+ def shutdown
34
+ puts(usage) || exit
35
+ end
36
+
37
+ def parse_parameters(args)
38
+ params = args.find { |arg| arg =~ ARGS_REGEX }
39
+
40
+ shutdown unless params
41
+
42
+ match_data = params.match(ARGS_REGEX)
43
+
44
+ [
45
+ match_data['namespace'],
46
+ match_data['action'],
47
+ match_data['http_method'],
48
+ ]
49
+ end
50
+
51
+ def paginate(args)
52
+ args.find { |arg| arg == '--paginate' }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ module ApiGenerator
2
+ module Helpers
3
+ module Utils
4
+ INDENTATION_REGEX = /^([ \t]*)(?=\S)/.freeze
5
+ WRITE_MODE = 'w'.freeze
6
+
7
+ def create_dir(dir)
8
+ FileUtils.mkdir_p(dir) && log(dir)
9
+ end
10
+
11
+ def log(path)
12
+ path.gsub!(root, '.') if path.match?(Regexp.new(root))
13
+ shell.say_status('create', path, :green)
14
+ end
15
+
16
+ def shell
17
+ Thor::Shell::Color.new
18
+ end
19
+
20
+ def copy_template(template, destination, data_binding)
21
+ content = render(template, data_binding)
22
+ destination.gsub!(/\.erb$/, '.rb')
23
+
24
+ File.write(destination, content, mode: WRITE_MODE) && log(destination)
25
+ end
26
+
27
+ def render(path, data_binding)
28
+ ERB.new(File.read(path)).result(data_binding)
29
+ end
30
+
31
+ def inject_after_str(file, str, insert)
32
+ text = File.read(file)
33
+ result = inject_after(text, str, insert)
34
+ File.write(file, result, mode: WRITE_MODE)
35
+ end
36
+
37
+ def inject_after(text, str, insert)
38
+ lines = text.each_line.to_a
39
+ regex = Regexp.new(str)
40
+ match_line_idx = lines.index { |line| line =~ regex }
41
+
42
+ return unless match_line_idx
43
+
44
+ line = lines[match_line_idx]
45
+ indented_insert = insert.each_line.map { |insert_line| indent_as(line, insert_line) }
46
+ lines.insert(match_line_idx + 1, *indented_insert)
47
+ lines.map(&:chomp).join("\n")
48
+ end
49
+
50
+ def indent_as(source, target)
51
+ indentation = INDENTATION_REGEX.match(source).captures.first.to_s
52
+ indentation + target
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,43 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ class ErrorCodeMiddleware < Faraday::Response::Middleware
4
+ def self.inherited(subclass)
5
+ class << subclass
6
+ attr_accessor :error_mapping, :error_namespace
7
+
8
+ def add_error(code, exception)
9
+ error_mapping[code] = exception
10
+ end
11
+ end
12
+
13
+ subclass.error_mapping ||= {}
14
+ subclass.error_namespace ||= ApiGenerator::Middleware::HttpErrors
15
+
16
+ super
17
+ end
18
+
19
+ def on_complete(env)
20
+ error_class = error_class(env.status)
21
+ return unless error_class
22
+
23
+ raise(error_class.new(message(env), env.body))
24
+ end
25
+
26
+ private
27
+
28
+ def message(env)
29
+ "Server returned #{env.status}: #{env.body}. Headers: #{env.response_headers.inspect}"
30
+ end
31
+
32
+ def error_class(code)
33
+ self.class.error_mapping.fetch(code) do
34
+ if code >= 500
35
+ self.class.error_namespace::ServerError
36
+ elsif code >= 400
37
+ self.class.error_namespace::ClientError
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ class ErrorHandlerMiddleware < Faraday::Middleware
4
+ def self.inherited(subclass)
5
+ class << subclass
6
+ attr_accessor :error_namespace
7
+ end
8
+
9
+ subclass.error_namespace ||= ApiGenerator::Middleware::HttpErrors
10
+ super
11
+ end
12
+
13
+ FARADAY_CONNECTION_ERRORS = [
14
+ Faraday::TimeoutError,
15
+ Faraday::ConnectionFailed,
16
+ Faraday::SSLError,
17
+ ].freeze
18
+
19
+ def call(env)
20
+ @app.call(env)
21
+ rescue *FARADAY_CONNECTION_ERRORS => e
22
+ raise self.class.error_namespace::ConnectionError, e
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ class HandleUnsuccessfulRequestMiddleware < Faraday::Response::Middleware
4
+ def self.inherited(subclass)
5
+ class << subclass
6
+ attr_accessor :error_namespace
7
+ end
8
+
9
+ subclass.error_namespace ||= ApiGenerator::Middleware::HttpErrors
10
+ super
11
+ end
12
+
13
+ def on_complete(env)
14
+ response_body = env.body
15
+
16
+ return unless response_body.is_a?(Hash) && response_body['success'] == false
17
+
18
+ raise(
19
+ self.class.error_namespace::UnsuccessfulRequestError.new(
20
+ message(env), env.body,
21
+ ),
22
+ )
23
+ end
24
+
25
+ private
26
+
27
+ def message(env)
28
+ <<~MSG
29
+ Server is unable to process the request: #{env.request_body}.
30
+ Headers: #{env.request_headers.inspect}
31
+ Server returned: #{env.body}. Headers: #{env.response_headers.inspect}
32
+ MSG
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ module HttpErrors
4
+ class ConnectionError < StandardError; end
5
+
6
+ class BaseError < StandardError
7
+ attr_reader :body
8
+
9
+ def initialize(msg = nil, body = nil)
10
+ @body = body if body
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ class ServerError < BaseError; end
16
+ class ClientError < BaseError; end
17
+
18
+ class UnsuccessfulRequestError < ClientError; end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ class RaiseErrorBase < Faraday::Response::Middleware
4
+ private
5
+
6
+ def message(env)
7
+ "Server returned #{env.status}: #{env.body}. Headers: #{env.response_headers.inspect}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ module RaiseErrorDsl
4
+ def self.extended(klass)
5
+ klass.singleton_class.class_eval { attr_accessor :error_namespace }
6
+ klass.error_namespace ||= if klass.module_parent.const_defined?(:HttpErrors)
7
+ klass.module_parent::HttpErrors
8
+ else
9
+ ApiGenerator::Middleware::HttpErrors
10
+ end
11
+ error_code_middleware = Class.new(ErrorCodeMiddleware)
12
+ error_code_middleware.error_namespace = klass.error_namespace
13
+ klass.const_set(:ErrorCodeMiddleware, error_code_middleware)
14
+
15
+ super
16
+ end
17
+
18
+ def on_status(code, error:)
19
+ self::ErrorCodeMiddleware.add_error(code, error_const(code, error))
20
+ end
21
+
22
+ private
23
+
24
+ def error_const(code, error_name)
25
+ const_name = error_name.to_s.camelize
26
+
27
+ return error_namespace.const_get(const_name) if error_namespace.const_defined?(const_name)
28
+
29
+ error_namespace.const_set(
30
+ const_name,
31
+ Class.new(code >= 500 ? error_namespace::ServerError : error_namespace::ClientError),
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ module ApiGenerator
2
+ module Middleware
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ module ApiGenerator
2
+ module Models
3
+ class Base < Hashie::Trash
4
+ include Hashie::Extensions::Dash::Coercion
5
+ include Hashie::Extensions::Dash::IndifferentAccess
6
+ include Hashie::Extensions::IgnoreUndeclared
7
+
8
+ def self.parse_time
9
+ ->(time) { Time.parse(time) if time.present? }
10
+ end
11
+
12
+ private_class_method :parse_time
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module ApiGenerator
2
+ module Models
3
+ class Data < Base
4
+ property :namespace
5
+ property :action
6
+ property :http_method
7
+ property :paginate
8
+ property :app_name
9
+ property :app_short_name
10
+
11
+ def for_template
12
+ binding
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module ApiGenerator::Pagination
2
+ module Dsl
3
+ STRATEGIES = {
4
+ limit_offset: LimitOffsetRelation,
5
+ page: PageRelation,
6
+ }.freeze
7
+
8
+ # rubocop:disable Metrics/MethodLength
9
+ # @param [Array<Symbol] methods список методов для применения пагинации
10
+ # @param [Symbol, Class] with стратегия пагинации (например, :limit_offset, :page)
11
+ # @param data_key ключ, по которому можно получить массив данных в ответе
12
+ # @param [Proc] on_request
13
+ # коллбэк, вызываемый при запросе и принимающий как аргументы запрос и параметры
14
+ # @param [Hash] config дополнительная конфигурация для стратегии пагинации
15
+ def paginate(*methods, with:, data_key:, on_request:, **config)
16
+ relation_class = STRATEGIES.fetch(with, with)
17
+
18
+ prepend(
19
+ Module.new do
20
+ methods.each do |method|
21
+ define_method(method) do |*args|
22
+ relation_class.new(config.merge(data_key: data_key)) do |relation|
23
+ super(*args) { |req| on_request.call(req, **relation.request_options) }
24
+ end
25
+ end
26
+ end
27
+ end,
28
+ )
29
+ end
30
+ # rubocop:enable Metrics/MethodLength
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ class ApiGenerator::Pagination::LimitOffsetRelation < ApiGenerator::Pagination::Relation
2
+ def limit(limit)
3
+ expand(limit: limit)
4
+ end
5
+
6
+ def offset(offset)
7
+ expand(offset: offset)
8
+ end
9
+
10
+ private
11
+
12
+ def next_relation
13
+ limit = config[:limit]&.call(response) || size
14
+ offset = config[:offset]&.call(response) || options[:offset].to_i
15
+
16
+ expand(offset: offset + limit)
17
+ end
18
+
19
+ def last_page?
20
+ return size < config[:limit].call(response) if config[:limit]
21
+
22
+ data.empty?
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ class ApiGenerator::Pagination::PageRelation < ApiGenerator::Pagination::Relation
2
+ def page(page)
3
+ expand(page: page)
4
+ end
5
+
6
+ def per(size)
7
+ expand(per_page: size)
8
+ end
9
+
10
+ private
11
+
12
+ def next_relation
13
+ expand(page: [options[:page].to_i, 1].max + 1)
14
+ end
15
+
16
+ def last_page?
17
+ return data.size < config[:per_page].call(response) if config[:per_page]
18
+
19
+ data.empty?
20
+ end
21
+ end