grape-route-helpers 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 70edfbe7fe2afd10d2030ffbfec49dc642f57b40
4
+ data.tar.gz: 86b00458bc103bbd7c953bf4842ccedae8d4f9cd
5
+ SHA512:
6
+ metadata.gz: 5c98fd40561face8eb970ce8ac4d7de721835999a137f3ca6f25fd2761ac96160833d2c996f9d497a41202e8d51ea4c2cc7f95b17a13469d53c4f152c4b5dc8b
7
+ data.tar.gz: d6519d1439e8e86d67469d3cbac343d7f033123dd673a488469e800f3fa2567a152f6225b993a28d719f6ee68ce278ddbd4085a8648c131193fae90a544f82c0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.swp
2
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ Metrics/ClassLength:
2
+ Max: 104
3
+ Enabled: false
4
+
5
+ Metrics/MethodLength:
6
+ Max: 21
7
+ Enabled: false
8
+
9
+ Style/FileName:
10
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ grape-route-helpers (1.0.0)
5
+ activesupport
6
+ grape
7
+ rake
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.2.3)
13
+ i18n (~> 0.7)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.3, >= 0.3.4)
17
+ tzinfo (~> 1.1)
18
+ ast (2.0.0)
19
+ astrolabe (1.3.0)
20
+ parser (>= 2.2.0.pre.3, < 3.0)
21
+ axiom-types (0.1.1)
22
+ descendants_tracker (~> 0.0.4)
23
+ ice_nine (~> 0.11.0)
24
+ thread_safe (~> 0.3, >= 0.3.1)
25
+ builder (3.2.2)
26
+ coderay (1.1.0)
27
+ coercible (1.0.0)
28
+ descendants_tracker (~> 0.0.1)
29
+ descendants_tracker (0.0.4)
30
+ thread_safe (~> 0.3, >= 0.3.1)
31
+ diff-lcs (1.2.5)
32
+ equalizer (0.0.11)
33
+ grape (0.12.0)
34
+ activesupport
35
+ builder
36
+ hashie (>= 2.1.0)
37
+ multi_json (>= 1.3.2)
38
+ multi_xml (>= 0.5.2)
39
+ rack (>= 1.3.0)
40
+ rack-accept
41
+ rack-mount
42
+ virtus (>= 1.0.0)
43
+ hashie (3.4.2)
44
+ i18n (0.7.0)
45
+ ice_nine (0.11.1)
46
+ json (1.8.3)
47
+ method_source (0.8.2)
48
+ minitest (5.7.0)
49
+ multi_json (1.11.1)
50
+ multi_xml (0.5.5)
51
+ parser (2.3.0.pre.2)
52
+ ast (>= 1.1, < 3.0)
53
+ powerpack (0.1.1)
54
+ pry (0.10.1)
55
+ coderay (~> 1.1.0)
56
+ method_source (~> 0.8.1)
57
+ slop (~> 3.4)
58
+ rack (1.6.4)
59
+ rack-accept (0.4.5)
60
+ rack (>= 0.4)
61
+ rack-mount (0.8.3)
62
+ rack (>= 1.0.0)
63
+ rainbow (2.0.0)
64
+ rake (10.4.2)
65
+ rspec (3.3.0)
66
+ rspec-core (~> 3.3.0)
67
+ rspec-expectations (~> 3.3.0)
68
+ rspec-mocks (~> 3.3.0)
69
+ rspec-core (3.3.1)
70
+ rspec-support (~> 3.3.0)
71
+ rspec-expectations (3.3.0)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.3.0)
74
+ rspec-mocks (3.3.1)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.3.0)
77
+ rspec-support (3.3.0)
78
+ rubocop (0.32.1)
79
+ astrolabe (~> 1.3)
80
+ parser (>= 2.2.2.5, < 3.0)
81
+ powerpack (~> 0.1)
82
+ rainbow (>= 1.99.1, < 3.0)
83
+ ruby-progressbar (~> 1.4)
84
+ ruby-progressbar (1.7.5)
85
+ slop (3.6.0)
86
+ thread_safe (0.3.5)
87
+ tzinfo (1.2.2)
88
+ thread_safe (~> 0.1)
89
+ virtus (1.0.5)
90
+ axiom-types (~> 0.1)
91
+ coercible (~> 1.0)
92
+ descendants_tracker (~> 0.0, >= 0.0.3)
93
+ equalizer (~> 0.0, >= 0.0.9)
94
+
95
+ PLATFORMS
96
+ ruby
97
+
98
+ DEPENDENCIES
99
+ grape-route-helpers!
100
+ pry
101
+ rspec
102
+ rubocop
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Harper Henn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # grape-route-helpers
2
+
3
+ Provides named route helpers for Grape APIs, similar to [Rails' route helpers](http://edgeguides.rubyonrails.org/routing.html#path-and-url-helpers).
4
+
5
+ ### Installation
6
+
7
+ #### Rails
8
+
9
+ 1.) Add the gem to your Gemfile.
10
+
11
+ ```bash
12
+ $ bundle install grape-route-helpers
13
+ ```
14
+
15
+ #### Sinatra/Rack
16
+
17
+ 1.) Add the gem to your Gemfile if you're using Bundler.
18
+
19
+ If you're not using Bundler to install/manage dependencies:
20
+
21
+ ```bash
22
+ $ gem install grape-route-helpers
23
+ ```
24
+
25
+ ```ruby
26
+ # environment setup file
27
+ require 'grape'
28
+ require 'grape/route_helpers'
29
+ ```
30
+
31
+ 2.) Write a rake task called `:environment` that loads the application's environment first. This gem's tasks are dependent on it. You could put this in the root of your project directory:
32
+
33
+ ```ruby
34
+ # Rakefile
35
+
36
+ require 'rake'
37
+ require 'bundler'
38
+ Bundler.setup
39
+ require 'grape-route-helpers'
40
+ require 'grape-route-helpers/tasks'
41
+
42
+ desc 'load the Sinatra environment.'
43
+ task :environment do
44
+ require File.expand_path('your_app_file', File.dirname(__FILE__))
45
+ end
46
+ ```
47
+
48
+ ### Usage examples
49
+
50
+ To see which methods correspond to which paths, and which options you can pass them:
51
+
52
+ ```bash
53
+ $ rake grape:route_helpers
54
+ ```
55
+
56
+ Use the methods inside your Grape API actions. Given this example API:
57
+
58
+ ```ruby
59
+ class ExampleAPI < Grape::API
60
+ version 'v1'
61
+ prefix 'api'
62
+ format 'json'
63
+
64
+ get 'ping' do
65
+ 'pong'
66
+ end
67
+
68
+ resource :cats do
69
+ get '/' do
70
+ %w(cats cats cats)
71
+ end
72
+
73
+ route_param :id do
74
+ get do
75
+ 'cat'
76
+ end
77
+ end
78
+ end
79
+
80
+ route :any, '*anything' do
81
+ redirect api_v1_cats_path
82
+ end
83
+ end
84
+ ```
85
+
86
+ You'd have the following methods available inside your Grape API actions:
87
+
88
+ ```ruby
89
+ # specifying the version when using Grape's "path" versioning strategy
90
+ api_v1_ping_path # => '/api/v1/ping'
91
+
92
+ # specifying the format
93
+ api_v1_cats_path(format: 'xml') # => '/api/v1/cats.xml'
94
+
95
+ # passing in values required to build a path
96
+ api_v1_cats_path(id: 1) # => '/api/v1/cats/1'
97
+
98
+ # catch-all paths have helpers
99
+ api_v1_anything_path # => '/api/v1/*anything'
100
+ ```
101
+
102
+ ### Contributing
103
+
104
+ 1.) Fork it
105
+
106
+ 2.) Create your feature branch `(git checkout -b my-new-feature)`
107
+
108
+ 3.) Write specs for your feature
109
+
110
+ 4.) Commit your changes `(git commit -am 'Add some feature')`
111
+
112
+ 5.) Push to the branch `(git push origin my-new-feature)`
113
+
114
+ 6.) Create a new pull request
115
+
116
+ ### License
117
+
118
+ See LICENSE
@@ -0,0 +1,21 @@
1
+ require File.join(Dir.pwd, 'lib', 'grape-route-helpers', 'version')
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'grape-route-helpers'
5
+ gem.version = GrapeRouteHelpers::VERSION
6
+ gem.licenses = ['MIT']
7
+ gem.summary = 'Route helpers for Grape'
8
+ gem.description = 'Route helpers for Grape'
9
+ gem.authors = ['Harper Henn']
10
+ gem.email = 'harper.henn@legitscript.com'
11
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
12
+ gem.homepage = 'https://github.com/reprah/grape-route-helpers'
13
+
14
+ gem.add_runtime_dependency 'grape'
15
+ gem.add_runtime_dependency 'activesupport'
16
+ gem.add_runtime_dependency 'rake'
17
+
18
+ gem.add_development_dependency 'pry'
19
+ gem.add_development_dependency 'rspec'
20
+ gem.add_development_dependency 'rubocop'
21
+ end
@@ -0,0 +1 @@
1
+ require 'grape-route-helpers'
@@ -0,0 +1,15 @@
1
+ module GrapeRouteHelpers
2
+ # methods to extend Grape::API's behavior so it can get a
3
+ # list of routes from all APIs and decorate them with
4
+ # the DecoratedRoute class
5
+ module AllRoutes
6
+ def decorated_routes
7
+ # memoize so that construction of decorated routes happens once
8
+ @decorated_routes ||= all_routes.map { |r| DecoratedRoute.new(r) }
9
+ end
10
+
11
+ def all_routes
12
+ subclasses.flat_map { |s| s.send(:prepare_routes) }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,134 @@
1
+ module GrapeRouteHelpers
2
+ # wrapper around Grape::Route that adds a helper method
3
+ class DecoratedRoute
4
+ attr_reader :route, :helper_names, :helper_arguments
5
+
6
+ def initialize(route)
7
+ @route = route
8
+ @helper_names = []
9
+ @helper_arguments = required_helper_segments
10
+ define_path_helpers
11
+ end
12
+
13
+ def define_path_helpers
14
+ route_versions.each do |version|
15
+ route_attributes = { version: version }
16
+ method_name = path_helper_name(route_attributes)
17
+ @helper_names << method_name
18
+ define_path_helper(method_name, route_attributes)
19
+ end
20
+ end
21
+
22
+ def define_path_helper(method_name, route_attributes)
23
+ method_body = <<-RUBY
24
+ def #{method_name}(attributes = {})
25
+ attrs = HashWithIndifferentAccess.new(
26
+ #{route_attributes}.merge(attributes)
27
+ )
28
+
29
+ content_type = attrs.delete(:format)
30
+ path = '/' + path_segments_with_values(attrs).join('/')
31
+ extension = content_type ? '.' + content_type : ''
32
+
33
+ path + extension
34
+ end
35
+ RUBY
36
+ instance_eval method_body
37
+ end
38
+
39
+ def route_versions
40
+ if route_version
41
+ route_version.split('|')
42
+ else
43
+ [nil]
44
+ end
45
+ end
46
+
47
+ def path_helper_name(opts = {})
48
+ segments = path_segments_with_values(opts)
49
+
50
+ name = if segments.empty?
51
+ 'root'
52
+ else
53
+ segments.join('_')
54
+ end
55
+ name + '_path'
56
+ end
57
+
58
+ def segment_to_value(segment, opts = {})
59
+ options = HashWithIndifferentAccess.new(
60
+ route_options.merge(opts)
61
+ )
62
+
63
+ if dynamic_segment?(segment)
64
+ key = segment.slice(1..-1)
65
+ options[key]
66
+ else
67
+ segment
68
+ end
69
+ end
70
+
71
+ def path_segments_with_values(opts)
72
+ segments = path_segments.map { |s| segment_to_value(s, opts) }
73
+ segments.reject(&:blank?)
74
+ end
75
+
76
+ def path_segments
77
+ pattern = %r{\(/?\.:?\w+\)|/|\*}
78
+ route_path.split(pattern).reject(&:blank?)
79
+ end
80
+
81
+ def dynamic_path_segments
82
+ segments = path_segments.select do |segment|
83
+ dynamic_segment?(segment)
84
+ end
85
+ segments.map { |s| s.slice(1..-1) }
86
+ end
87
+
88
+ def dynamic_segment?(segment)
89
+ segment.start_with?(':')
90
+ end
91
+
92
+ def required_helper_segments
93
+ segments_in_options = dynamic_path_segments.select do |segment|
94
+ route_options[segment.to_sym]
95
+ end
96
+ dynamic_path_segments - segments_in_options
97
+ end
98
+
99
+ def optional_segments
100
+ ['format']
101
+ end
102
+
103
+ def uses_segments_in_path_helper?(segments)
104
+ requested = segments - optional_segments
105
+ required = required_helper_segments
106
+
107
+ if requested.empty? && required.empty?
108
+ true
109
+ else
110
+ requested.all? do |segment|
111
+ required.include?(segment)
112
+ end
113
+ end
114
+ end
115
+
116
+ # accessing underlying Grape::Route
117
+
118
+ def route_path
119
+ route.route_path
120
+ end
121
+
122
+ def route_options
123
+ route.instance_variable_get(:@options)
124
+ end
125
+
126
+ def route_version
127
+ route.route_version
128
+ end
129
+
130
+ def route_namespace
131
+ route.route_namespace
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,28 @@
1
+ module GrapeRouteHelpers
2
+ # methods to extend Grape::Endpoint so that calls
3
+ # to unknown methods will look for a route with a matching
4
+ # helper function name
5
+ module NamedRouteMatcher
6
+ def method_missing(method_id, *arguments)
7
+ segments = arguments.first || {}
8
+
9
+ route = Grape::API.decorated_routes.detect do |r|
10
+ route_match?(r, method_id, segments)
11
+ end
12
+
13
+ if route
14
+ route.send(method_id, *arguments)
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def route_match?(route, method_name, segments)
21
+ return false unless route.respond_to?(method_name)
22
+ fail ArgumentError,
23
+ 'Helper options must be a hash' unless segments.is_a?(Hash)
24
+ requested_segments = segments.keys.map(&:to_s)
25
+ route.uses_segments_in_path_helper?(requested_segments)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module GrapeRouteHelpers
2
+ #
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ files = File.join(File.dirname(__FILE__), '../tasks/*.rake')
6
+ Dir[files].each { |f| load f }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module GrapeRouteHelpers
2
+ # class for displaying the path, helper method name,
3
+ # and required arguments for every Grape::Route.
4
+ class RouteDisplayer
5
+ def route_attributes
6
+ Grape::API.decorated_routes.map do |route|
7
+ {
8
+ route_path: route.route_path,
9
+ helper_names: route.helper_names,
10
+ helper_arguments: route.helper_arguments
11
+ }
12
+ end
13
+ end
14
+
15
+ def display
16
+ puts("== GRAPE ROUTE HELPERS ==\n\n")
17
+ route_attributes.each do |attributes|
18
+ printf("%s: %s\n", 'Path', attributes[:route_path])
19
+ printf("%s: %s\n",
20
+ 'Helper Method',
21
+ attributes[:helper_names].join(', '))
22
+ printf("%s: %s\n",
23
+ 'Arguments',
24
+ attributes[:helper_arguments].join(', '))
25
+ puts("\n")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1 @@
1
+ Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |f| load f }
@@ -0,0 +1,4 @@
1
+ # Gem version
2
+ module GrapeRouteHelpers
3
+ VERSION = '1.0.0'
4
+ end
@@ -0,0 +1,16 @@
1
+ require 'grape'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/class'
4
+
5
+ require 'grape-route-helpers/decorated_route'
6
+ require 'grape-route-helpers/named_route_matcher'
7
+ require 'grape-route-helpers/all_routes'
8
+ require 'grape-route-helpers/route_displayer'
9
+
10
+ #
11
+ module GrapeRouteHelpers
12
+ require 'grape-route-helpers/railtie' if defined?(Rails)
13
+ end
14
+
15
+ Grape::API.extend GrapeRouteHelpers::AllRoutes
16
+ Grape::Endpoint.send(:include, GrapeRouteHelpers::NamedRouteMatcher)
@@ -0,0 +1,6 @@
1
+ namespace :grape do
2
+ desc 'Print route helper methods.'
3
+ task routes: :environment do
4
+ GrapeRouteHelpers::RouteDisplayer.new.display
5
+ end
6
+ end
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeRouteHelpers::DecoratedRoute do
4
+ let(:api) { Spec::Support::RouteMatcherHelpers.api }
5
+
6
+ let(:routes) do
7
+ api.routes.map do |route|
8
+ described_class.new(route)
9
+ end
10
+ end
11
+
12
+ let(:index_route) do
13
+ routes.detect { |route| route.route_namespace == '/cats' }
14
+ end
15
+
16
+ let(:show_route) do
17
+ routes.detect { |route| route.route_namespace == '/cats/:id' }
18
+ end
19
+
20
+ let(:catch_all_route) do
21
+ routes.detect { |route| route.route_path =~ /\*/ }
22
+ end
23
+
24
+ describe '#helper_names' do
25
+ context 'when an API has multiple versions' do
26
+ let(:api_versions) { %w(beta alpha v1) }
27
+
28
+ before do
29
+ api.version api_versions
30
+ end
31
+
32
+ it "returns the route's helper name for each version" do
33
+ helper_names = show_route.helper_names
34
+ expect(helper_names.size).to eq(api_versions.size)
35
+ end
36
+ end
37
+
38
+ context 'when an API has one version' do
39
+ it "returns the route's helper name for that version" do
40
+ helper_name = show_route.helper_names.first
41
+ expect(helper_name).to eq('api_v1_cats_path')
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#helper_arguments' do
47
+ context 'when no user input is needed to generate the correct path' do
48
+ it 'returns an empty array' do
49
+ expect(index_route.helper_arguments).to eq([])
50
+ end
51
+ end
52
+
53
+ context 'when user input is needed to generate the correct path' do
54
+ it 'returns an array of required segments' do
55
+ expect(show_route.helper_arguments).to eq(['id'])
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#path_segments_with_values' do
61
+ context 'when path has dynamic segments' do
62
+ it 'replaces segments with corresponding values found in @options' do
63
+ opts = { id: 1 }
64
+ result = show_route.path_segments_with_values(opts)
65
+ expect(result).to include(1)
66
+ end
67
+
68
+ context 'when options contains string keys' do
69
+ it 'replaces segments with corresponding values found in the options' do
70
+ opts = { 'id' => 1 }
71
+ result = show_route.path_segments_with_values(opts)
72
+ expect(result).to include(1)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#path_helper_name' do
79
+ it "returns the name of a route's helper method" do
80
+ expect(index_route.path_helper_name).to eq('api_v1_cats_path')
81
+ end
82
+
83
+ context 'when the path is the root path' do
84
+ let(:api_with_root) do
85
+ Class.new(Grape::API) do
86
+ get '/' do
87
+ end
88
+ end
89
+ end
90
+
91
+ let(:root_route) do
92
+ grape_route = api_with_root.routes.first
93
+ described_class.new(grape_route)
94
+ end
95
+
96
+ it 'returns "root_path"' do
97
+ result = root_route.path_helper_name
98
+ expect(result).to eq('root_path')
99
+ end
100
+ end
101
+
102
+ context 'when the path is a catch-all path' do
103
+ it 'returns a name without the glob star' do
104
+ result = catch_all_route.path_helper_name
105
+ expect(result).to eq('api_v1_path_path')
106
+ end
107
+ end
108
+ end
109
+
110
+ describe '#segment_to_value' do
111
+ context 'when segment is dynamic' do
112
+ it 'returns the value the segment corresponds to' do
113
+ result = index_route.segment_to_value(':version')
114
+ expect(result).to eq('v1')
115
+ end
116
+
117
+ context 'when segment is found in options' do
118
+ it 'returns the value found in options' do
119
+ options = { id: 1 }
120
+ result = show_route.segment_to_value(':id', options)
121
+ expect(result).to eq(1)
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'when segment is static' do
127
+ it 'returns the segment' do
128
+ result = index_route.segment_to_value('api')
129
+ expect(result).to eq('api')
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'path helper method' do
135
+ context 'when helper does not require arguments' do
136
+ it 'returns the correct path' do
137
+ path = index_route.api_v1_cats_path
138
+ expect(path).to eq('/api/v1/cats')
139
+ end
140
+ end
141
+
142
+ context 'when arguments are needed required to construct the right path' do
143
+ context 'when not missing arguments' do
144
+ it 'returns the correct path' do
145
+ path = show_route.api_v1_cats_path(id: 1)
146
+ expect(path).to eq('/api/v1/cats/1')
147
+ end
148
+ end
149
+ end
150
+
151
+ context "when a route's API has multiple versions" do
152
+ before(:each) do
153
+ api.version %w(v1 v2)
154
+ end
155
+
156
+ it 'returns a path for each version' do
157
+ expect(index_route.api_v1_cats_path).to eq('/api/v1/cats')
158
+ expect(index_route.api_v2_cats_path).to eq('/api/v2/cats')
159
+ end
160
+ end
161
+
162
+ context 'when a format is given' do
163
+ it 'returns the path with a correct extension' do
164
+ path = show_route.api_v1_cats_path(id: 1, format: 'xml')
165
+ expect(path).to eq('/api/v1/cats/1.xml')
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeRouteHelpers::NamedRouteMatcher do
4
+ include described_class
5
+
6
+ let(:api) { Spec::Support::RouteMatcherHelpers.api }
7
+
8
+ let(:routes) do
9
+ api
10
+ Grape::API.decorated_routes
11
+ end
12
+
13
+ let(:ping_route) do
14
+ routes.detect do |route|
15
+ route.route_path =~ /ping/
16
+ end
17
+ end
18
+
19
+ let(:index_route) do
20
+ routes.detect do |route|
21
+ route.route_namespace =~ /cats$/
22
+ end
23
+ end
24
+
25
+ let(:show_route) do
26
+ routes.detect do |route|
27
+ route.route_namespace =~ %r{cats/:id}
28
+ end
29
+ end
30
+
31
+ describe '#route_match?' do
32
+ context 'when route responds to a method name' do
33
+ let(:route) { ping_route }
34
+ let(:method_name) { :api_v1_ping_path }
35
+ let(:segments) { {} }
36
+
37
+ context 'when segments is not a hash' do
38
+ it 'raises an ArgumentError' do
39
+ expect do
40
+ route_match?(route, method_name, 1234)
41
+ end.to raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ it 'returns true' do
46
+ is_match = route_match?(route, method_name, segments)
47
+ expect(is_match).to eq(true)
48
+ end
49
+
50
+ context 'when requested segments contains expected options' do
51
+ let(:segments) { { 'format' => 'xml' } }
52
+
53
+ it 'returns true' do
54
+ is_match = route_match?(route, method_name, segments)
55
+ expect(is_match).to eq(true)
56
+ end
57
+
58
+ context 'when no dynamic segments are requested' do
59
+ context 'when the route requires dynamic segments' do
60
+ let(:route) { show_route }
61
+ let(:method_name) { :ap1_v1_cats_path }
62
+
63
+ it 'returns false' do
64
+ is_match = route_match?(route, method_name, segments)
65
+ expect(is_match).to eq(false)
66
+ end
67
+ end
68
+
69
+ context 'when the route does not require dynamic segments' do
70
+ it 'returns true' do
71
+ is_match = route_match?(route, method_name, segments)
72
+ expect(is_match).to eq(true)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'when route requires the requested segments' do
78
+ let(:route) { show_route }
79
+ let(:method_name) { :api_v1_cats_path }
80
+ let(:segments) { { id: 1 } }
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
+ end
87
+
88
+ context 'when route does not require the requested segments' do
89
+ let(:segments) { { some_option: 'some value' } }
90
+
91
+ it 'returns false' do
92
+ is_match = route_match?(route, method_name, segments)
93
+ expect(is_match).to eq(false)
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'when segments contains unexpected options' do
99
+ let(:segments) { { some_option: 'some value' } }
100
+
101
+ it 'returns false' do
102
+ is_match = route_match?(route, method_name, segments)
103
+ expect(is_match).to eq(false)
104
+ end
105
+ end
106
+ end
107
+
108
+ context 'when route does not respond to a method name' do
109
+ let(:method_name) { :some_other_path }
110
+ let(:route) { ping_route }
111
+ let(:segments) { {} }
112
+
113
+ it 'returns false' do
114
+ is_match = route_match?(route, method_name, segments)
115
+ expect(is_match).to eq(false)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#method_missing' do
121
+ context 'when method name matches a Grape::Route path helper name' do
122
+ it 'returns the path for that route object' do
123
+ api
124
+
125
+ path = api_v1_ping_path
126
+ expect(path).to eq('/api/v1/ping')
127
+ end
128
+
129
+ context 'when argument to the helper is not a hash' do
130
+ it 'raises an ArgumentError' do
131
+ api
132
+
133
+ expect do
134
+ api_v1_ping_path(1234)
135
+ end.to raise_error(ArgumentError)
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'when method name does not match a Grape::Route path helper name' do
141
+ it 'raises a NameError' do
142
+ api
143
+
144
+ expect do
145
+ some_method_name
146
+ end.to raise_error(NameError)
147
+ end
148
+ end
149
+ end
150
+
151
+ context 'when Grape::Route objects share the same helper name' do
152
+ context 'when helpers require different segments to generate their path' do
153
+ it 'uses arguments to infer which route to use' do
154
+ api
155
+
156
+ show_path = api_v1_cats_path('id' => 1)
157
+ expect(show_path).to eq('/api/v1/cats/1')
158
+
159
+ index_path = api_v1_cats_path
160
+ expect(index_path).to eq('/api/v1/cats')
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path('lib')
2
+
3
+ require 'pry'
4
+ require 'grape/route_helpers'
5
+
6
+ support_files = Dir.glob('spec/support/*')
7
+ support_files.each { |f| require File.expand_path(f) }
8
+
9
+ include Spec::Support::RouteMatcherHelpers
@@ -0,0 +1,34 @@
1
+ module Spec
2
+ module Support
3
+ # container for methods used in specs
4
+ module RouteMatcherHelpers
5
+ def self.api
6
+ Class.new(Grape::API) do
7
+ version 'v1'
8
+ prefix 'api'
9
+ format 'json'
10
+
11
+ get 'ping' do
12
+ 'pong'
13
+ end
14
+
15
+ resource :cats do
16
+ get '/' do
17
+ %w(cats cats cats)
18
+ end
19
+
20
+ route_param :id do
21
+ get do
22
+ 'cat'
23
+ end
24
+ end
25
+ end
26
+
27
+ route :any, '*path' do
28
+ 'catch-all route'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape-route-helpers
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Harper Henn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: grape
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Route helpers for Grape
98
+ email: harper.henn@legitscript.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - .rubocop.yml
105
+ - Gemfile
106
+ - Gemfile.lock
107
+ - LICENSE.txt
108
+ - README.md
109
+ - grape-route-helpers.gemspec
110
+ - lib/grape-route-helpers.rb
111
+ - lib/grape-route-helpers/all_routes.rb
112
+ - lib/grape-route-helpers/decorated_route.rb
113
+ - lib/grape-route-helpers/named_route_matcher.rb
114
+ - lib/grape-route-helpers/railtie.rb
115
+ - lib/grape-route-helpers/route_displayer.rb
116
+ - lib/grape-route-helpers/tasks.rb
117
+ - lib/grape-route-helpers/version.rb
118
+ - lib/grape/route_helpers.rb
119
+ - lib/tasks/grape_route_helpers.rake
120
+ - spec/grape_route_helpers/decorated_route_spec.rb
121
+ - spec/grape_route_helpers/named_route_matcher_spec.rb
122
+ - spec/spec_helper.rb
123
+ - spec/support/route_matcher_helpers.rb
124
+ homepage: https://github.com/reprah/grape-route-helpers
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.0.14
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Route helpers for Grape
148
+ test_files: []
149
+ has_rdoc: