horn_of_plenty 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE.txt +19 -0
- data/README.md +36 -0
- data/lib/horn_of_plenty.rb +22 -0
- data/lib/horn_of_plenty/adapters/github/client.rb +82 -0
- data/lib/horn_of_plenty/adapters/github/collections/card.rb +46 -0
- data/lib/horn_of_plenty/adapters/github/configuration.rb +38 -0
- data/lib/horn_of_plenty/adapters/github/error.rb +44 -0
- data/lib/horn_of_plenty/adapters/github/parsers/board.rb +52 -0
- data/lib/horn_of_plenty/adapters/github/parsers/card.rb +91 -0
- data/lib/horn_of_plenty/adapters/github/parsers/issue.rb +95 -0
- data/lib/horn_of_plenty/adapters/github/parsers/lane.rb +50 -0
- data/lib/horn_of_plenty/adapters/github/parsers/pull_request.rb +38 -0
- data/lib/horn_of_plenty/adapters/github/queries/list_boards.rb +18 -0
- data/lib/horn_of_plenty/adapters/github/queries/list_cards.rb +19 -0
- data/lib/horn_of_plenty/adapters/github/queries/list_issues.rb +22 -0
- data/lib/horn_of_plenty/adapters/github/queries/list_lanes.rb +19 -0
- data/lib/horn_of_plenty/adapters/github/queries/list_pull_requests.rb +22 -0
- data/lib/horn_of_plenty/adapters/github/repositories/board.rb +24 -0
- data/lib/horn_of_plenty/adapters/github/repositories/card.rb +24 -0
- data/lib/horn_of_plenty/adapters/github/repositories/issue.rb +24 -0
- data/lib/horn_of_plenty/adapters/github/repositories/lane.rb +24 -0
- data/lib/horn_of_plenty/adapters/github/repositories/pull_request.rb +24 -0
- data/lib/horn_of_plenty/adapters/github/requests/list_boards.rb +50 -0
- data/lib/horn_of_plenty/adapters/github/requests/list_cards.rb +52 -0
- data/lib/horn_of_plenty/adapters/github/requests/list_issues.rb +50 -0
- data/lib/horn_of_plenty/adapters/github/requests/list_lanes.rb +52 -0
- data/lib/horn_of_plenty/adapters/github/requests/list_pull_requests.rb +50 -0
- data/lib/horn_of_plenty/adapters/github/response.rb +16 -0
- data/lib/horn_of_plenty/adapters/github/responses/list_boards.rb +17 -0
- data/lib/horn_of_plenty/adapters/github/responses/list_cards.rb +23 -0
- data/lib/horn_of_plenty/adapters/github/responses/list_issues.rb +18 -0
- data/lib/horn_of_plenty/adapters/github/responses/list_lanes.rb +17 -0
- data/lib/horn_of_plenty/adapters/github/responses/list_pull_requests.rb +17 -0
- data/lib/horn_of_plenty/collection.rb +60 -0
- data/lib/horn_of_plenty/core_ext/string.rb +95 -0
- data/lib/horn_of_plenty/models/board.rb +32 -0
- data/lib/horn_of_plenty/models/card.rb +51 -0
- data/lib/horn_of_plenty/models/issue.rb +46 -0
- data/lib/horn_of_plenty/models/lane.rb +30 -0
- data/lib/horn_of_plenty/models/parsable.rb +44 -0
- data/lib/horn_of_plenty/models/pull_request.rb +12 -0
- data/lib/horn_of_plenty/null_objects/board.rb +10 -0
- data/lib/horn_of_plenty/null_objects/card.rb +10 -0
- data/lib/horn_of_plenty/null_objects/issue.rb +10 -0
- data/lib/horn_of_plenty/null_objects/lane.rb +10 -0
- data/lib/horn_of_plenty/null_objects/pull_request.rb +10 -0
- data/lib/horn_of_plenty/paginator.rb +109 -0
- data/lib/horn_of_plenty/parsers/hash.rb +95 -0
- data/lib/horn_of_plenty/queries/fetch.rb +36 -0
- data/lib/horn_of_plenty/repositories/board.rb +26 -0
- data/lib/horn_of_plenty/repositories/card.rb +26 -0
- data/lib/horn_of_plenty/repositories/issue.rb +26 -0
- data/lib/horn_of_plenty/repositories/lane.rb +26 -0
- data/lib/horn_of_plenty/repositories/pull_request.rb +26 -0
- data/lib/horn_of_plenty/repository.rb +29 -0
- data/lib/horn_of_plenty/request.rb +32 -0
- data/lib/horn_of_plenty/response.rb +44 -0
- data/lib/horn_of_plenty/version.rb +4 -0
- data/spec/fixtures/responses/board_github.json +29 -0
- data/spec/fixtures/responses/card_github.json +7 -0
- data/spec/fixtures/responses/issue_github.json +163 -0
- data/spec/fixtures/responses/lane_github.json +7 -0
- data/spec/fixtures/responses/pull_request_github.json +361 -0
- data/spec/lib/horn_of_plenty/adapters/github/client_spec.rb +12 -0
- data/spec/lib/horn_of_plenty/adapters/github/collections/card_spec.rb +14 -0
- data/spec/lib/horn_of_plenty/adapters/github/configuration_spec.rb +12 -0
- data/spec/lib/horn_of_plenty/adapters/github/error_spec.rb +35 -0
- data/spec/lib/horn_of_plenty/adapters/github/parsers/board_spec.rb +39 -0
- data/spec/lib/horn_of_plenty/adapters/github/parsers/card_spec.rb +64 -0
- data/spec/lib/horn_of_plenty/adapters/github/parsers/issue_spec.rb +47 -0
- data/spec/lib/horn_of_plenty/adapters/github/parsers/lane_spec.rb +38 -0
- data/spec/lib/horn_of_plenty/adapters/github/parsers/pull_request_spec.rb +50 -0
- data/spec/lib/horn_of_plenty/adapters/github/queries/list_boards_spec.rb +19 -0
- data/spec/lib/horn_of_plenty/adapters/github/queries/list_cards_spec.rb +19 -0
- data/spec/lib/horn_of_plenty/adapters/github/queries/list_issues_spec.rb +19 -0
- data/spec/lib/horn_of_plenty/adapters/github/queries/list_lanes_spec.rb +19 -0
- data/spec/lib/horn_of_plenty/adapters/github/queries/list_pull_requests_spec.rb +19 -0
- data/spec/lib/horn_of_plenty/adapters/github/repositories/board_spec.rb +22 -0
- data/spec/lib/horn_of_plenty/adapters/github/repositories/card_spec.rb +67 -0
- data/spec/lib/horn_of_plenty/adapters/github/repositories/issue_spec.rb +22 -0
- data/spec/lib/horn_of_plenty/adapters/github/repositories/lane_spec.rb +25 -0
- data/spec/lib/horn_of_plenty/adapters/github/repositories/pull_request_spec.rb +24 -0
- data/spec/lib/horn_of_plenty/adapters/github/requests/list_boards_spec.rb +42 -0
- data/spec/lib/horn_of_plenty/adapters/github/requests/list_cards_spec.rb +51 -0
- data/spec/lib/horn_of_plenty/adapters/github/requests/list_issues_spec.rb +42 -0
- data/spec/lib/horn_of_plenty/adapters/github/requests/list_lanes_spec.rb +51 -0
- data/spec/lib/horn_of_plenty/adapters/github/requests/list_pull_requests_spec.rb +42 -0
- data/spec/lib/horn_of_plenty/adapters/github/responses/list_boards_spec.rb +78 -0
- data/spec/lib/horn_of_plenty/adapters/github/responses/list_cards_spec.rb +86 -0
- data/spec/lib/horn_of_plenty/adapters/github/responses/list_issues_spec.rb +104 -0
- data/spec/lib/horn_of_plenty/adapters/github/responses/list_lanes_spec.rb +86 -0
- data/spec/lib/horn_of_plenty/adapters/github/responses/list_pull_requests_spec.rb +109 -0
- data/spec/lib/horn_of_plenty/models/board_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/models/card_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/models/issue_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/models/lane_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/models/pull_request_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/null_objects/board_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/null_objects/card_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/null_objects/issue_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/null_objects/lane_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/null_objects/pull_request_spec.rb +10 -0
- data/spec/lib/horn_of_plenty/queries/fetch_spec.rb +15 -0
- data/spec/lib/horn_of_plenty/repositories/board_spec.rb +48 -0
- data/spec/lib/horn_of_plenty/repositories/card_spec.rb +51 -0
- data/spec/lib/horn_of_plenty/repositories/issue_spec.rb +48 -0
- data/spec/lib/horn_of_plenty/repositories/lane_spec.rb +51 -0
- data/spec/lib/horn_of_plenty/repositories/pull_request_spec.rb +50 -0
- data/spec/support/patterns.rb +1 -0
- metadata +396 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'digest'
|
3
|
+
require 'horn_of_plenty/models/parsable'
|
4
|
+
|
5
|
+
module HornOfPlenty
|
6
|
+
module Models
|
7
|
+
class Board
|
8
|
+
include Parsable
|
9
|
+
|
10
|
+
parsable_attributes :id,
|
11
|
+
:author_id,
|
12
|
+
:title,
|
13
|
+
:description,
|
14
|
+
:created_at,
|
15
|
+
:updated_at
|
16
|
+
def to_h
|
17
|
+
{
|
18
|
+
id: id,
|
19
|
+
author_id: author_id,
|
20
|
+
title: title,
|
21
|
+
description: description,
|
22
|
+
created_at: created_at,
|
23
|
+
updated_at: updated_at,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_sha
|
28
|
+
@sha ||= Digest::SHA2.hexdigest(to_h.to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'digest'
|
3
|
+
require 'horn_of_plenty/models/parsable'
|
4
|
+
|
5
|
+
module HornOfPlenty
|
6
|
+
module Models
|
7
|
+
class Card
|
8
|
+
include Parsable
|
9
|
+
|
10
|
+
parsable_attributes :id,
|
11
|
+
:lane_id,
|
12
|
+
:repository,
|
13
|
+
:status,
|
14
|
+
:content_type,
|
15
|
+
:content_id,
|
16
|
+
:title,
|
17
|
+
:description,
|
18
|
+
:labels,
|
19
|
+
:created_at,
|
20
|
+
:updated_at
|
21
|
+
|
22
|
+
def labels
|
23
|
+
@labels || []
|
24
|
+
end
|
25
|
+
|
26
|
+
def status
|
27
|
+
@status || nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
{
|
32
|
+
id: id,
|
33
|
+
lane_id: lane_id,
|
34
|
+
repository: repository,
|
35
|
+
status: status,
|
36
|
+
content_type: content_type,
|
37
|
+
content_id: content_id,
|
38
|
+
title: title,
|
39
|
+
description: description,
|
40
|
+
labels: labels,
|
41
|
+
created_at: created_at,
|
42
|
+
updated_at: updated_at,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_sha
|
47
|
+
@sha ||= Digest::SHA2.hexdigest(to_h.to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'digest'
|
3
|
+
require 'horn_of_plenty/models/parsable'
|
4
|
+
|
5
|
+
module HornOfPlenty
|
6
|
+
module Models
|
7
|
+
class Issue
|
8
|
+
include Parsable
|
9
|
+
|
10
|
+
parsable_attributes :id,
|
11
|
+
:author_id,
|
12
|
+
:assignee_ids,
|
13
|
+
:closer_id,
|
14
|
+
:repository,
|
15
|
+
:status,
|
16
|
+
:title,
|
17
|
+
:description,
|
18
|
+
:labels,
|
19
|
+
:closed_at,
|
20
|
+
:has_pull_request?,
|
21
|
+
:created_at,
|
22
|
+
:updated_at
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
id: id,
|
26
|
+
author_id: author_id,
|
27
|
+
assignee_ids: assignee_ids,
|
28
|
+
closer_id: closer_id,
|
29
|
+
repository: repository,
|
30
|
+
status: status,
|
31
|
+
title: title,
|
32
|
+
description: description,
|
33
|
+
labels: labels,
|
34
|
+
closed_at: closed_at,
|
35
|
+
has_pull_request: has_pull_request?,
|
36
|
+
created_at: created_at,
|
37
|
+
updated_at: updated_at,
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_sha
|
42
|
+
@sha ||= Digest::SHA2.hexdigest(to_h.to_s)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'digest'
|
3
|
+
require 'horn_of_plenty/models/parsable'
|
4
|
+
|
5
|
+
module HornOfPlenty
|
6
|
+
module Models
|
7
|
+
class Lane
|
8
|
+
include Parsable
|
9
|
+
|
10
|
+
parsable_attributes :id,
|
11
|
+
:board_id,
|
12
|
+
:title,
|
13
|
+
:created_at,
|
14
|
+
:updated_at
|
15
|
+
def to_h
|
16
|
+
{
|
17
|
+
id: id,
|
18
|
+
board_id: board_id,
|
19
|
+
title: title,
|
20
|
+
created_at: created_at,
|
21
|
+
updated_at: updated_at,
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_sha
|
26
|
+
@sha ||= Digest::SHA2.hexdigest(to_h.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module HornOfPlenty
|
3
|
+
module Parsable
|
4
|
+
module ClassMethods
|
5
|
+
def from_parser(parser:)
|
6
|
+
new(parser: parser)
|
7
|
+
end
|
8
|
+
|
9
|
+
def parsable_attributes(*attribute_list)
|
10
|
+
attribute_list.each do |attribute|
|
11
|
+
instance_variable_name = "@#{attribute.to_s.gsub(/[\?]/, '')}".to_sym
|
12
|
+
|
13
|
+
define_method attribute do
|
14
|
+
if instance_variable_get(instance_variable_name).nil?
|
15
|
+
parser.__send__(attribute)
|
16
|
+
else
|
17
|
+
instance_variable_get(instance_variable_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
define_method "#{attribute}=" do |other|
|
22
|
+
instance_variable_set(instance_variable_name, other)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :parser
|
29
|
+
|
30
|
+
def initialize(parser: nil, **args)
|
31
|
+
self.parser = parser
|
32
|
+
|
33
|
+
if defined?(super) && args.empty?
|
34
|
+
super()
|
35
|
+
elsif defined?(super)
|
36
|
+
super(**args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.included(base)
|
41
|
+
base.extend ClassMethods
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module HornOfPlenty
|
5
|
+
class Paginator
|
6
|
+
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :items,
|
10
|
+
:length,
|
11
|
+
:size,
|
12
|
+
:count,
|
13
|
+
:empty?,
|
14
|
+
:[],
|
15
|
+
:first
|
16
|
+
|
17
|
+
def initialize(client:,
|
18
|
+
request:,
|
19
|
+
items_per_page: nil,
|
20
|
+
current_page: nil,
|
21
|
+
autopage: true)
|
22
|
+
|
23
|
+
self.client = client
|
24
|
+
self.request = request
|
25
|
+
|
26
|
+
self.items_per_page = items_per_page || 500
|
27
|
+
self.current_page = current_page || 1
|
28
|
+
self.autopage = autopage
|
29
|
+
end
|
30
|
+
|
31
|
+
def all
|
32
|
+
items
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(_options = {})
|
36
|
+
loop do
|
37
|
+
items.each do |item|
|
38
|
+
yield item
|
39
|
+
end
|
40
|
+
|
41
|
+
break unless autopage && go_to_next_page
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def each_page
|
46
|
+
loop do
|
47
|
+
yield items
|
48
|
+
|
49
|
+
break unless go_to_next_page
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def go_to_next_page
|
54
|
+
return false if items.count < items_per_page
|
55
|
+
|
56
|
+
go_to_page(current_page + 1)
|
57
|
+
end
|
58
|
+
|
59
|
+
def go_to_previous_page
|
60
|
+
go_to_page(current_page - 1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def go_to_page(page_number)
|
64
|
+
page_number = 1 if page_number < 1
|
65
|
+
|
66
|
+
self.current_page = page_number
|
67
|
+
end
|
68
|
+
|
69
|
+
def items
|
70
|
+
@items ||= retrieval_result.result
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
attr_accessor :client,
|
76
|
+
:request,
|
77
|
+
:autopage
|
78
|
+
attr_reader :items_per_page,
|
79
|
+
:current_page,
|
80
|
+
:current_offset
|
81
|
+
|
82
|
+
def items_per_page=(value)
|
83
|
+
@items_per_page = value.to_i
|
84
|
+
@retrieval_result = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def current_page=(value)
|
88
|
+
@current_page = value.to_i
|
89
|
+
@current_offset = (@current_page - 1) * items_per_page
|
90
|
+
@retrieval_result = nil
|
91
|
+
@items = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def retrieval_result
|
97
|
+
request.pagination = pagination_parameters
|
98
|
+
|
99
|
+
client.request(request)
|
100
|
+
end
|
101
|
+
|
102
|
+
def pagination_parameters
|
103
|
+
{
|
104
|
+
limit: items_per_page,
|
105
|
+
offset: current_offset,
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# rubocop:disable Style/OptionHash
|
3
|
+
module HornOfPlenty
|
4
|
+
module Parsers
|
5
|
+
module Hash
|
6
|
+
private
|
7
|
+
|
8
|
+
def parse_text(hash, key, options = {}, &user_block)
|
9
|
+
parse_key(hash, key, ->(value) { value.to_s }, options, &user_block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_html(hash, key, options = {}, &user_block)
|
13
|
+
converter = ->(value) { CGI.unescape_html(value.to_s) }
|
14
|
+
|
15
|
+
parse_key(hash, key, converter, options, &user_block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse_integer(hash, key, options = {}, &user_block)
|
19
|
+
parse_key(hash, key, ->(value) { value.to_i }, options, &user_block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_float(hash, key, options = {}, &user_block)
|
23
|
+
parse_key(hash, key, ->(value) { value.to_f }, options, &user_block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_time(hash, key, options = {}, &user_block)
|
27
|
+
parse_key(hash, key, ->(value) { Time.iso8601(value).round }, options, &user_block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_url(hash, key, options = {}, &user_block)
|
31
|
+
parse_key(hash, key, ->(value) { URI.parse(value) }, options, &user_block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_phone(hash, key, options = {}, &user_block)
|
35
|
+
parse_key(hash, key, ->(value) { value.gsub(/[^x\d]/, '') }, options, &user_block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_existance(hash, key, options = {}, &user_block)
|
39
|
+
parse_key(hash, key, ->(value) { value }, options, &user_block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_boolean(hash, key, options = {}, &user_block)
|
43
|
+
converter = lambda do |value|
|
44
|
+
if value
|
45
|
+
%w{true TRUE True t T yes YES Yes y Y 1}.include?(value) ? true : false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
parse_key(hash, key, converter, options, &user_block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_percentage(hash, key, options = {}, &user_block)
|
53
|
+
parse_key(hash, key, ->(value) { (value.to_f / 100).round(4) }, options, &user_block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
57
|
+
def parse_key(hash, key, converter = nil, options = {})
|
58
|
+
default_value = if options[:default_value]
|
59
|
+
options[:default_value]
|
60
|
+
elsif options[:array]
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
|
64
|
+
value = hash.dig(*key.split('/:/'))
|
65
|
+
value = if value.to_s.empty?
|
66
|
+
default_value
|
67
|
+
else
|
68
|
+
value
|
69
|
+
end
|
70
|
+
value = if options[:array] && value.respond_to?(:split)
|
71
|
+
value.split(',')
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
value = if value.respond_to?(:map) && converter
|
76
|
+
value.map do |item|
|
77
|
+
converter.call(item)
|
78
|
+
end
|
79
|
+
elsif value && converter
|
80
|
+
converter.call(value)
|
81
|
+
else
|
82
|
+
value
|
83
|
+
end
|
84
|
+
|
85
|
+
if block_given? && (value || options[:yield_if_nil])
|
86
|
+
yield value
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
# rubocop:enable Style/OptionHash
|