grape-path-helpers 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40792c146fc9c26dd609bc8dd99adce92b010820e3c2ca9644144f3493dd06ec
4
- data.tar.gz: aed680f63272f1e61467787e0d374bb8a8145b61979c18b5c61e332763472cd5
3
+ metadata.gz: 4a9b0351af6ba5b42ba8b521c7bc83a832d4a20a445c30c4c346874f161ddfba
4
+ data.tar.gz: abe212bb7ab94afa5d12f738a68b2c7c84b93ec807825d5622022ccacb99c5bd
5
5
  SHA512:
6
- metadata.gz: ff06e53d14f8d46a398cc14754fbc61e7e1a1e28179aa83a27bbca47698c117daaf064f62d3e03e1a385112fea2c0dd076b102e64e4c2f1ffa3c4d4fae7d3279
7
- data.tar.gz: 82fde6483c7ad996189c59ab9c79969495e642fb0274d9e209228b6633e6c6c731066a4bf24effa6e00e990f3fc82b94eca8084b469f64aad15e8f9a5f486728
6
+ metadata.gz: 1c4341eed5f6e6cbbc0f3fa7c35bca36e0f10129948556f6549a4f384e699aa6e6981f61e4c8657e660a7db6d2acfd4adb0b42558bfa58c9e7f8b337014f4f96
7
+ data.tar.gz: c996a522cfcfb00b275ab276a9ee3e24280a624fa859e4c434937903de0ad3dc004c50231cf37c5abca9ad467b30d29ec0aff131e6e33a58d88b5d1bed5d3499
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.0
4
+
5
+ * [Further improve performance of route matching](https://gitlab.com/gitlab-org/grape-path-helpers/-/merge_requests/38)
6
+
7
+ ## 1.6.3
8
+
9
+ * [Fix route matcher when method ends in path and arg isn't a Hash](https://gitlab.com/gitlab-org/grape-path-helpers/-/merge_requests/35)
10
+
11
+ ## 1.6.2
12
+
13
+ * [Improve performance of route matching](https://gitlab.com/gitlab-org/grape-path-helpers/-/merge_requests/33)
14
+
15
+ ## 1.6.1
16
+
17
+ * [Use ruby2_keywords to fix 2.7 warning](https://gitlab.com/gitlab-org/grape-path-helpers/-/merge_requests/31)
18
+
3
19
  ## 1.6.0
4
20
 
5
21
  * [Extract kwargs to fix 2.7 warnings](https://gitlab.com/gitlab-org/grape-path-helpers/-/merge_requests/29)
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,40 @@
1
+ ## Developer Certificate of Origin and License
2
+
3
+ By contributing to GitLab B.V., you accept and agree to the following terms and
4
+ conditions for your present and future contributions submitted to GitLab B.V.
5
+ Except for the license granted herein to GitLab B.V. and recipients of software
6
+ distributed by GitLab B.V., you reserve all right, title, and interest in and to
7
+ your Contributions.
8
+
9
+ All contributions are subject to the Developer Certificate of Origin and license set out at [docs.gitlab.com/ce/legal/developer_certificate_of_origin](https://docs.gitlab.com/ce/legal/developer_certificate_of_origin).
10
+
11
+ _This notice should stay as the first item in the CONTRIBUTING.md file._
12
+
13
+ ## Code of conduct
14
+
15
+ As contributors and maintainers of this project, we pledge to respect all people
16
+ who contribute through reporting issues, posting feature requests, updating
17
+ documentation, submitting pull requests or patches, and other activities.
18
+
19
+ We are committed to making participation in this project a harassment-free
20
+ experience for everyone, regardless of level of experience, gender, gender
21
+ identity and expression, sexual orientation, disability, personal appearance,
22
+ body size, race, ethnicity, age, or religion.
23
+
24
+ Examples of unacceptable behavior by participants include the use of sexual
25
+ language or imagery, derogatory comments or personal attacks, trolling, public
26
+ or private harassment, insults, or other unprofessional conduct.
27
+
28
+ Project maintainers have the right and responsibility to remove, edit, or reject
29
+ comments, commits, code, wiki edits, issues, and other contributions that are
30
+ not aligned to this Code of Conduct. Project maintainers who do not follow the
31
+ Code of Conduct may be removed from the project team.
32
+
33
+ This code of conduct applies both within project spaces and in public spaces
34
+ when an individual is representing the project or its community.
35
+
36
+ Instances of abusive, harassing, or otherwise unacceptable behavior can be
37
+ reported by emailing contact@gitlab.com.
38
+
39
+ This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org), version 1.1.0,
40
+ available at [https://contributor-covenant.org/version/1/1/0/](https://contributor-covenant.org/version/1/1/0/).
data/Gemfile.lock CHANGED
@@ -1,15 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grape-path-helpers (1.6.0)
4
+ grape-path-helpers (1.7.0)
5
5
  activesupport
6
6
  grape (~> 1.3)
7
7
  rake (> 12)
8
+ ruby2_keywords (~> 0.0.2)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
- activesupport (6.1.1)
13
+ activesupport (6.1.3.2)
13
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
14
15
  i18n (>= 1.6, < 2)
15
16
  minitest (>= 5.1)
@@ -18,9 +19,9 @@ GEM
18
19
  ast (2.4.0)
19
20
  builder (3.2.4)
20
21
  coderay (1.1.2)
21
- concurrent-ruby (1.1.7)
22
+ concurrent-ruby (1.1.8)
22
23
  diff-lcs (1.3)
23
- dry-configurable (0.12.0)
24
+ dry-configurable (0.12.1)
24
25
  concurrent-ruby (~> 1.0)
25
26
  dry-core (~> 0.5, >= 0.5.0)
26
27
  dry-container (0.7.2)
@@ -28,29 +29,27 @@ GEM
28
29
  dry-configurable (~> 0.1, >= 0.1.3)
29
30
  dry-core (0.5.0)
30
31
  concurrent-ruby (~> 1.0)
31
- dry-equalizer (0.3.0)
32
32
  dry-inflector (0.2.0)
33
- dry-logic (1.1.0)
33
+ dry-logic (1.2.0)
34
34
  concurrent-ruby (~> 1.0)
35
35
  dry-core (~> 0.5, >= 0.5)
36
- dry-types (1.4.0)
36
+ dry-types (1.5.1)
37
37
  concurrent-ruby (~> 1.0)
38
38
  dry-container (~> 0.3)
39
- dry-core (~> 0.4, >= 0.4.4)
40
- dry-equalizer (~> 0.3)
39
+ dry-core (~> 0.5, >= 0.5)
41
40
  dry-inflector (~> 0.1, >= 0.1.2)
42
41
  dry-logic (~> 1.0, >= 1.0.2)
43
- grape (1.5.1)
42
+ grape (1.5.3)
44
43
  activesupport
45
44
  builder
46
45
  dry-types (>= 1.1)
47
46
  mustermann-grape (~> 1.0.0)
48
47
  rack (>= 1.3.0)
49
48
  rack-accept
50
- i18n (1.8.7)
49
+ i18n (1.8.10)
51
50
  concurrent-ruby (~> 1.0)
52
51
  method_source (0.9.0)
53
- minitest (5.14.3)
52
+ minitest (5.14.4)
54
53
  mustermann (1.1.1)
55
54
  ruby2_keywords (~> 0.0.1)
56
55
  mustermann-grape (1.0.1)
@@ -88,7 +87,7 @@ GEM
88
87
  ruby-progressbar (~> 1.7)
89
88
  unicode-display_width (~> 1.0, >= 1.0.1)
90
89
  ruby-progressbar (1.9.0)
91
- ruby2_keywords (0.0.2)
90
+ ruby2_keywords (0.0.4)
92
91
  tzinfo (2.0.4)
93
92
  concurrent-ruby (~> 1.0)
94
93
  unicode-display_width (1.3.3)
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.add_runtime_dependency 'activesupport'
15
15
  gem.add_runtime_dependency 'grape', '~> 1.3'
16
16
  gem.add_runtime_dependency 'rake', '> 12'
17
+ gem.add_runtime_dependency 'ruby2_keywords', '~> 0.0.2'
17
18
 
18
19
  gem.add_development_dependency 'pry', '~> 0.11'
19
20
  gem.add_development_dependency 'rspec', '~> 3.7'
@@ -3,11 +3,24 @@ module GrapePathHelpers
3
3
  # list of routes from all APIs and decorate them with
4
4
  # the DecoratedRoute class
5
5
  module AllRoutes
6
- def decorated_routes
7
- # memoize so that construction of decorated routes happens once
8
- @decorated_routes ||= all_routes
9
- .map { |r| DecoratedRoute.new(r) }
10
- .sort_by { |r| -r.dynamic_path_segments.count }
6
+ def decorated_routes_by_helper_name
7
+ return @decorated_routes_by_helper_name if @decorated_routes_by_helper_name # rubocop:disable Metrics/LineLength
8
+
9
+ routes = {}
10
+
11
+ all_routes
12
+ .map { |r| DecoratedRoute.new(r) }
13
+ .sort_by { |r| -r.dynamic_path_segments.count }
14
+ .each do |route|
15
+ route.helper_names.each do |helper_name|
16
+ key = helper_name.to_sym
17
+
18
+ routes[key] ||= []
19
+ routes[key] << route
20
+ end
21
+ end
22
+
23
+ @decorated_routes_by_helper_name = routes
11
24
  end
12
25
 
13
26
  def all_routes
@@ -147,10 +147,10 @@ module GrapePathHelpers
147
147
  def uses_segments_in_path_helper?(segments)
148
148
  segments = segments.reject { |x| special_keys.include?(x) }
149
149
 
150
- if required_helper_segments.empty? && segments.any?
150
+ if helper_arguments.empty? && segments.any?
151
151
  false
152
152
  else
153
- required_helper_segments.all? { |x| segments.include?(x) }
153
+ helper_arguments.all? { |x| segments.include?(x) }
154
154
  end
155
155
  end
156
156
 
@@ -3,47 +3,31 @@ module GrapePathHelpers
3
3
  # to unknown methods will look for a route with a matching
4
4
  # helper function name
5
5
  module NamedRouteMatcher
6
- def method_missing(method_id, *args, **kwargs)
7
- return super unless method_id.to_s =~ /_path$/
6
+ def method_missing(method_name, *args)
7
+ possible_routes = Grape::API::Instance
8
+ .decorated_routes_by_helper_name[method_name]
9
+ return super unless possible_routes
8
10
 
9
- success, result = call_path_method(method_id, *args, **kwargs)
11
+ segments = args.first || {}
12
+ return super unless segments.is_a?(Hash)
10
13
 
11
- if success
12
- result
13
- else
14
- super
15
- end
16
- end
17
-
18
- def call_path_method(method_id, *arguments)
19
- segments = arguments.first || {}
14
+ requested_segments = segments.keys.map(&:to_s)
20
15
 
21
- route = Grape::API::Instance.decorated_routes.detect do |r|
22
- route_match?(r, method_id, segments)
16
+ route = possible_routes.detect do |r|
17
+ r.uses_segments_in_path_helper?(requested_segments)
23
18
  end
24
19
 
25
- return false unless route
26
-
27
- [true, route.send(method_id, *arguments)]
28
- end
29
-
30
- def respond_to_missing?(method_name, _include_private = false)
31
- return super unless method_name =~ /_path$/
32
-
33
- Grape::API::Instance.decorated_routes.detect do |route|
34
- return true if route.respond_to?(method_name)
20
+ if route
21
+ route.send(method_name, *args)
22
+ else
23
+ super
35
24
  end
36
-
37
- super
38
25
  end
26
+ ruby2_keywords(:method_missing)
39
27
 
40
- def route_match?(route, method_name, segments)
41
- return false unless route.respond_to?(method_name)
42
- # rubocop:disable Metrics/LineLength
43
- raise ArgumentError, 'Helper options must be a hash' unless segments.is_a?(Hash)
44
- # rubocop:enable Metrics/LineLength
45
- requested_segments = segments.keys.map(&:to_s)
46
- route.uses_segments_in_path_helper?(requested_segments)
28
+ def respond_to_missing?(method_name, _include_private = false)
29
+ !Grape::API::Instance.decorated_routes_by_helper_name[method_name].nil? ||
30
+ super
47
31
  end
48
32
  end
49
33
  end
@@ -3,7 +3,7 @@ module GrapePathHelpers
3
3
  # and required arguments for every Grape::Route.
4
4
  class RouteDisplayer
5
5
  def route_attributes
6
- Grape::API::Instance.decorated_routes.map do |route|
6
+ Grape::API::Instance.decorated_routes.values.flatten.uniq.map do |route|
7
7
  {
8
8
  route_path: route.route_path,
9
9
  route_method: route.route_method,
@@ -1,4 +1,4 @@
1
1
  # Gem version
2
2
  module GrapePathHelpers
3
- VERSION = '1.6.0'.freeze
3
+ VERSION = '1.7.0'.freeze
4
4
  end
@@ -4,28 +4,6 @@ require 'spec_helper'
4
4
  describe GrapePathHelpers::NamedRouteMatcher do
5
5
  include described_class
6
6
 
7
- let(:routes) do
8
- Grape::API::Instance.decorated_routes
9
- end
10
-
11
- let(:ping_route) do
12
- routes.detect do |route|
13
- route.route_path =~ /ping/ && route.route_version == 'v1'
14
- end
15
- end
16
-
17
- let(:index_route) do
18
- routes.detect do |route|
19
- route.route_namespace =~ /cats$/
20
- end
21
- end
22
-
23
- let(:show_route) do
24
- routes.detect do |route|
25
- route.route_namespace =~ %r{cats/:id}
26
- end
27
- end
28
-
29
7
  let(:helper_class) do
30
8
  fake_class = Class.new do
31
9
  prepend GrapePathHelpers::NamedRouteMatcher
@@ -43,123 +21,46 @@ describe GrapePathHelpers::NamedRouteMatcher do
43
21
  end
44
22
 
45
23
  describe '#method_missing' do
46
- it 'returns super method_missing if the method does not end with path' do
47
- expect(Grape::API::Instance).not_to receive(:decorated_routes)
48
-
49
- expect(helper_class.test_method(:arg1, kwarg1: :kwarg1))
50
- .to eq([:test_method, [:arg1], { kwarg1: :kwarg1 }])
24
+ it 'returns super method_missing if the route does not exist' do
25
+ expect(helper_class.test_method(id: 1))
26
+ .to eq([:test_method, [], { id: 1 }])
51
27
  end
52
28
 
53
- it 'search for the route if the method ends with path' do
54
- expect(Grape::API::Instance).to receive(:decorated_routes).and_call_original # rubocop:disable Metrics/LineLength
55
-
56
- helper_class.test_method_path
29
+ it 'returns super method_missing if first arg is not a hash' do
30
+ expect(helper_class.api_v1_cats_path(:arg1, kwarg1: :kwarg1))
31
+ .to eq([:api_v1_cats_path, [:arg1], { kwarg1: :kwarg1 }])
57
32
  end
58
- end
59
33
 
60
- describe '#route_match?' do
61
- context 'when route responds to a method name' do
62
- let(:route) { ping_route }
63
- let(:method_name) { :api_v1_ping_path }
64
- let(:segments) { {} }
65
-
66
- context 'when segments is not a hash' do
67
- it 'raises an ArgumentError' do
68
- expect do
69
- route_match?(route, method_name, 1234)
70
- end.to raise_error(ArgumentError)
71
- end
72
- end
73
-
74
- it 'returns true' do
75
- is_match = route_match?(route, method_name, segments)
76
- expect(is_match).to eq(true)
34
+ context 'when method name matches a Grape::Route path helper name' do
35
+ it 'returns the path for that route object' do
36
+ expect(helper_class.api_v1_ping_path).to eq('/api/v1/ping.json')
77
37
  end
78
38
 
79
- context 'when requested segments contains expected options' do
80
- let(:segments) { { 'format' => 'xml' } }
81
-
82
- it 'returns true' do
83
- is_match = route_match?(route, method_name, segments)
84
- expect(is_match).to eq(true)
85
- end
86
-
87
- context 'when no dynamic segments are requested' do
88
- context 'when the route requires dynamic segments' do
89
- let(:route) { show_route }
90
- let(:method_name) { :ap1_v1_cats_path }
91
-
92
- it 'returns false' do
93
- is_match = route_match?(route, method_name, segments)
94
- expect(is_match).to eq(false)
95
- end
96
- end
97
-
98
- context 'when the route does not require dynamic segments' do
99
- it 'returns true' do
100
- is_match = route_match?(route, method_name, segments)
101
- expect(is_match).to eq(true)
102
- end
103
- end
104
- end
105
-
106
- context 'when route requires the requested segments' do
107
- let(:route) { show_route }
108
- let(:method_name) { :api_v1_cats_path }
109
- let(:segments) { { id: 1 } }
110
-
111
- it 'returns true' do
112
- is_match = route_match?(route, method_name, segments)
113
- expect(is_match).to eq(true)
114
- end
115
- end
116
-
117
- context 'when route does not require the requested segments' do
118
- let(:segments) { { some_option: 'some value' } }
119
-
120
- it 'returns false' do
121
- is_match = route_match?(route, method_name, segments)
122
- expect(is_match).to eq(false)
123
- end
39
+ context 'when route contains dynamic segments' do
40
+ it 'returns the path for that route object' do
41
+ expect(helper_class.api_v1_cats_path(id: 5))
42
+ .to eq('/api/v1/cats/5.json')
124
43
  end
125
44
  end
126
45
 
127
- context 'when segments contains unexpected options' do
128
- let(:segments) { { some_option: 'some value' } }
129
-
130
- it 'returns false' do
131
- is_match = route_match?(route, method_name, segments)
132
- expect(is_match).to eq(false)
46
+ context 'when route requires dynamic segments but none are passed in' do
47
+ it 'returns super method_missing' do
48
+ expect(helper_class.api_v1_cats_owners_path)
49
+ .to eq([:api_v1_cats_owners_path, [], {}])
133
50
  end
134
51
  end
135
- end
136
52
 
137
- context 'when route does not respond to a method name' do
138
- let(:method_name) { :some_other_path }
139
- let(:route) { ping_route }
140
- let(:segments) { {} }
141
-
142
- it 'returns false' do
143
- is_match = route_match?(route, method_name, segments)
144
- expect(is_match).to eq(false)
53
+ context 'when route has no dynamic segments but some are passed in' do
54
+ it 'returns super method_missing' do
55
+ expect(helper_class.api_v1_ping_path(invalid: 'test'))
56
+ .to eq([:api_v1_ping_path, [], { invalid: 'test' }])
57
+ end
145
58
  end
146
59
  end
147
60
  end
148
61
 
149
62
  describe '#respond_to_missing?' do
150
- it 'returns super if the method does not end with path' do
151
- expect(Grape::API::Instance).not_to receive(:decorated_routes) # rubocop:disable Metrics/LineLength
152
-
153
- expect(helper_class.send(:respond_to_missing?, :test)).to eq(false)
154
- end
155
-
156
- it 'search for the route if the method ends with path' do
157
- expect(Grape::API::Instance).to receive(:decorated_routes).and_call_original # rubocop:disable Metrics/LineLength
158
-
159
- expect(helper_class.send(:respond_to_missing?, :test_path)).to eq(false)
160
- end
161
-
162
- context 'when method name with segments matches a Grape::Route path' do
63
+ context 'when method name matches a Grape::Route path with segments' do
163
64
  let(:method_name) { :api_v1_cats_path }
164
65
 
165
66
  it 'returns true' do
@@ -167,7 +68,7 @@ describe GrapePathHelpers::NamedRouteMatcher do
167
68
  end
168
69
  end
169
70
 
170
- context 'when method name matches a Grape::Route path helper name' do
71
+ context 'when method name matches a Grape::Route path' do
171
72
  let(:method_name) { :api_v1_ping_path }
172
73
 
173
74
  it 'returns true' do
@@ -175,7 +76,7 @@ describe GrapePathHelpers::NamedRouteMatcher do
175
76
  end
176
77
  end
177
78
 
178
- context 'when method name does not match a Grape::Route path helper name' do
79
+ context 'when method name does not match a Grape::Route path' do
179
80
  let(:method_name) { :some_other_path }
180
81
 
181
82
  it 'returns false' do
@@ -184,56 +85,44 @@ describe GrapePathHelpers::NamedRouteMatcher do
184
85
  end
185
86
  end
186
87
 
187
- describe '#method_missing' do
188
- context 'when method name matches a Grape::Route path helper name' do
189
- it 'returns the path for that route object' do
190
- path = api_v1_ping_path
191
- expect(path).to eq('/api/v1/ping.json')
192
- end
193
-
194
- context 'when argument to the helper is not a hash' do
195
- it 'raises an ArgumentError' do
196
- expect do
197
- api_v1_ping_path(1234)
198
- end.to raise_error(ArgumentError)
199
- end
200
- end
201
- end
202
-
203
- context 'when method name does not match a Grape::Route path helper name' do
204
- it 'raises a NameError' do
205
- expect do
206
- some_method_name
207
- end.to raise_error(NameError)
208
- end
209
- end
210
- end
211
-
212
88
  context 'when Grape::Route objects share the same helper name' do
213
89
  context 'when helpers require different segments to generate their path' do
214
90
  it 'uses arguments to infer which route to use' do
215
- show_path = api_v1_cats_path('id' => 1)
91
+ show_path = helper_class.api_v1_cats_path(
92
+ 'id' => 1
93
+ )
216
94
  expect(show_path).to eq('/api/v1/cats/1.json')
217
95
 
218
- index_path = api_v1_cats_path
96
+ index_path = helper_class.api_v1_cats_path
219
97
  expect(index_path).to eq('/api/v1/cats.json')
220
98
  end
221
99
 
222
100
  it 'does not get shadowed by another route with less segments' do
223
- show_path = api_v1_cats_owners_path('id' => 1)
101
+ show_path = helper_class.api_v1_cats_owners_path(
102
+ 'id' => 1
103
+ )
224
104
  expect(show_path).to eq('/api/v1/cats/1/owners.json')
225
105
 
226
- show_path = api_v1_cats_owners_path('id' => 1, 'owner_id' => 1)
106
+ show_path = helper_class.api_v1_cats_owners_path(
107
+ 'id' => 1,
108
+ 'owner_id' => 1
109
+ )
227
110
  expect(show_path).to eq('/api/v1/cats/1/owners/1.json')
228
111
  end
229
112
  end
230
113
 
231
114
  context 'when query params are passed in' do
232
115
  it 'uses arguments to infer which route to use' do
233
- show_path = api_v1_cats_path('id' => 1, params: { 'foo' => 'bar' })
116
+ show_path = helper_class.api_v1_cats_path(
117
+ 'id' => 1,
118
+ params: { 'foo' => 'bar' }
119
+ )
120
+
234
121
  expect(show_path).to eq('/api/v1/cats/1.json?foo=bar')
235
122
 
236
- index_path = api_v1_cats_path(params: { 'foo' => 'bar' })
123
+ index_path = helper_class.api_v1_cats_path(
124
+ params: { 'foo' => 'bar' }
125
+ )
237
126
  expect(index_path).to eq('/api/v1/cats.json?foo=bar')
238
127
  end
239
128
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-path-helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drew Blessing
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-11 00:00:00.000000000 Z
12
+ date: 2021-08-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - ">"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '12'
56
+ - !ruby/object:Gem::Dependency
57
+ name: ruby2_keywords
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.0.2
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.0.2
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: pry
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +120,7 @@ files:
106
120
  - ".rubocop.yml"
107
121
  - ".travis.yml"
108
122
  - CHANGELOG.md
123
+ - CONTRIBUTING.md
109
124
  - Gemfile
110
125
  - Gemfile.lock
111
126
  - LICENSE.txt