grape-starter 0.2.0
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/.gitignore +12 -0
- data/.rubocop.yml +36 -0
- data/.rubocop_todo.yml +12 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +70 -0
- data/Rakefile +14 -0
- data/bin/grape-starter +119 -0
- data/grape-starter.gemspec +36 -0
- data/lib/starter.rb +9 -0
- data/lib/starter/builder.rb +90 -0
- data/lib/starter/builder/names.rb +56 -0
- data/lib/starter/builder/template_endpoints.rb +97 -0
- data/lib/starter/builder/template_files.rb +73 -0
- data/lib/starter/rake/grape_tasks.rb +71 -0
- data/lib/starter/rspec/request_specs.rb +109 -0
- data/lib/starter/version.rb +4 -0
- data/template/.rubocop.yml +15 -0
- data/template/.rubocop_todo.yml +28 -0
- data/template/Gemfile +23 -0
- data/template/LICENSE +21 -0
- data/template/README.md +70 -0
- data/template/Rakefile +25 -0
- data/template/api/base.rb +18 -0
- data/template/api/endpoints/root.rb +20 -0
- data/template/api/entities/api_error.rb +17 -0
- data/template/api/entities/route.rb +22 -0
- data/template/config.ru +12 -0
- data/template/config/application.rb +22 -0
- data/template/config/boot.rb +3 -0
- data/template/config/environment.rb +4 -0
- data/template/lib/api.rb +6 -0
- data/template/lib/api/version.rb +4 -0
- data/template/script/console +8 -0
- data/template/script/server +14 -0
- data/template/script/setup +6 -0
- data/template/script/stop +8 -0
- data/template/script/test +17 -0
- data/template/script/update +6 -0
- data/template/spec/lib/api/version_spec.rb +8 -0
- data/template/spec/requests/documentation_spec.rb +12 -0
- data/template/spec/requests/root_spec.rb +25 -0
- data/template/spec/spec_helper.rb +25 -0
- metadata +216 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Starter
|
3
|
+
module Names
|
4
|
+
def klass_name
|
5
|
+
for_klass = prepare_klass
|
6
|
+
singular? ? for_klass.classify : for_klass.classify.pluralize
|
7
|
+
end
|
8
|
+
|
9
|
+
def base_file_name
|
10
|
+
@resource.tr('/', '-').downcase + '.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
def base_spec_name
|
14
|
+
base_file_name.gsub(/.rb$/, '_spec.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
def api_base_file_name
|
18
|
+
File.join(Dir.getwd, 'api', 'base.rb')
|
19
|
+
end
|
20
|
+
|
21
|
+
# resource file
|
22
|
+
def api_file_name
|
23
|
+
File.join(Dir.getwd, 'api', 'endpoints', base_file_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
# resource file
|
27
|
+
def entity_file_name
|
28
|
+
File.join(Dir.getwd, 'api', 'entities', base_file_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# lib file
|
32
|
+
def lib_file_name
|
33
|
+
File.join(Dir.getwd, 'lib', 'api', base_file_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# resource spec
|
37
|
+
def api_spec_name
|
38
|
+
File.join(Dir.getwd, 'spec', 'requests', base_spec_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
# lib spec
|
42
|
+
def lib_spec_name
|
43
|
+
File.join(Dir.getwd, 'spec', 'lib', 'api', base_spec_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def prepare_klass
|
49
|
+
@resource.tr('-', '/')
|
50
|
+
end
|
51
|
+
|
52
|
+
def singular?
|
53
|
+
@resource.singularize.inspect == @resource.inspect
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Starter
|
3
|
+
module Template
|
4
|
+
# defining the endpoints -> http methods of a resource
|
5
|
+
module Endpoints
|
6
|
+
def crud
|
7
|
+
%i(
|
8
|
+
post
|
9
|
+
get_all
|
10
|
+
get_specific
|
11
|
+
put_specific
|
12
|
+
patch_specific
|
13
|
+
delete_specific
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def singular_one
|
18
|
+
%i(
|
19
|
+
post
|
20
|
+
get_one
|
21
|
+
put_one
|
22
|
+
patch_one
|
23
|
+
delete_one
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# available API/HTTP methods
|
28
|
+
# POST
|
29
|
+
def post
|
30
|
+
"
|
31
|
+
desc 'create #{resource.singularize}'
|
32
|
+
params do
|
33
|
+
# TODO: specify the parameters
|
34
|
+
end
|
35
|
+
post do
|
36
|
+
# your code goes here
|
37
|
+
end"
|
38
|
+
end
|
39
|
+
|
40
|
+
# GET
|
41
|
+
def get_all
|
42
|
+
"
|
43
|
+
desc 'get all of #{resource.pluralize}',
|
44
|
+
is_array: true
|
45
|
+
get do
|
46
|
+
# your code goes here
|
47
|
+
end"
|
48
|
+
end
|
49
|
+
|
50
|
+
%w(get put patch delete).each do |verb|
|
51
|
+
define_method(:"#{verb}_one") do
|
52
|
+
"
|
53
|
+
desc '#{verb} #{resource.singularize}'
|
54
|
+
#{verb} do
|
55
|
+
# your code goes here
|
56
|
+
end"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
%w(get put patch delete).each do |verb|
|
61
|
+
define_method(:"#{verb}_specific") do
|
62
|
+
"
|
63
|
+
desc '#{verb} specific #{resource.singularize}'
|
64
|
+
params do
|
65
|
+
requires :id
|
66
|
+
end
|
67
|
+
#{verb} ':id' do
|
68
|
+
# your code goes here
|
69
|
+
present :params, params
|
70
|
+
end"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# request specs shared examples
|
75
|
+
#
|
76
|
+
def post_spec
|
77
|
+
"it_behaves_like 'POST', base_path: '/api/v1', resource: '#{resource}'"
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_all_spec
|
81
|
+
"it_behaves_like 'GET all', base_path: '/api/v1', resource: '#{resource}'"
|
82
|
+
end
|
83
|
+
|
84
|
+
%w(get put patch delete).each do |verb|
|
85
|
+
define_method(:"#{verb}_one_spec") do
|
86
|
+
"it_behaves_like '#{verb.upcase} one', base_path: '/api/v1', resource: '#{resource}'"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
%w(get put patch delete).each do |verb|
|
91
|
+
define_method(:"#{verb}_specific_spec") do
|
92
|
+
"it_behaves_like '#{verb.upcase} specific', base_path: '/api/v1', resource: '#{resource}', key: 1"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
module Starter
|
5
|
+
module Template
|
6
|
+
module Files
|
7
|
+
# API template for resource
|
8
|
+
def api_file
|
9
|
+
<<-FILE.strip_heredoc
|
10
|
+
# frozen_string_literal: true
|
11
|
+
|
12
|
+
module Api
|
13
|
+
module Endpoints
|
14
|
+
class #{klass_name} < Grape::API
|
15
|
+
namespace :#{resource.downcase} do
|
16
|
+
#{endpoints}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
FILE
|
22
|
+
end
|
23
|
+
|
24
|
+
def entity_file
|
25
|
+
<<-FILE.strip_heredoc
|
26
|
+
# frozen_string_literal: true
|
27
|
+
|
28
|
+
module Api
|
29
|
+
module Entities
|
30
|
+
class #{klass_name} < Grape::Entity
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
FILE
|
35
|
+
end
|
36
|
+
|
37
|
+
# LIB template for resource
|
38
|
+
def lib_file
|
39
|
+
<<-FILE.strip_heredoc
|
40
|
+
# frozen_string_literal: true
|
41
|
+
|
42
|
+
module Api
|
43
|
+
class #{klass_name}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
FILE
|
47
|
+
end
|
48
|
+
|
49
|
+
def api_spec
|
50
|
+
<<-FILE.strip_heredoc
|
51
|
+
# frozen_string_literal: true
|
52
|
+
require 'spec_helper'
|
53
|
+
|
54
|
+
RSpec.describe Api::#{klass_name} do
|
55
|
+
pending 'write specs'
|
56
|
+
#{endpoint_specs}
|
57
|
+
end
|
58
|
+
FILE
|
59
|
+
end
|
60
|
+
|
61
|
+
def lib_spec
|
62
|
+
<<-FILE.strip_heredoc
|
63
|
+
# frozen_string_literal: true
|
64
|
+
require 'spec_helper'
|
65
|
+
|
66
|
+
RSpec.describe Api::#{klass_name} do
|
67
|
+
pending 'write specs'
|
68
|
+
end
|
69
|
+
FILE
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/tasklib'
|
4
|
+
require 'rack/test'
|
5
|
+
|
6
|
+
module Starter
|
7
|
+
module Rake
|
8
|
+
require 'starter/builder'
|
9
|
+
|
10
|
+
class GrapeTasks < ::Rake::TaskLib
|
11
|
+
include Rack::Test::Methods
|
12
|
+
|
13
|
+
attr_reader :resource
|
14
|
+
attr_reader :api_class
|
15
|
+
|
16
|
+
def initialize(api_class = nil)
|
17
|
+
super()
|
18
|
+
|
19
|
+
@api_class = api_class
|
20
|
+
define_tasks
|
21
|
+
end
|
22
|
+
|
23
|
+
def api_routes
|
24
|
+
api_class.routes.each_with_object([]) do |route, memo|
|
25
|
+
memo << { verb: route.request_method, path: build_path(route), description: route.description }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def define_tasks
|
32
|
+
routes
|
33
|
+
end
|
34
|
+
|
35
|
+
# gets all api routes
|
36
|
+
def routes
|
37
|
+
desc 'shows all routes'
|
38
|
+
task routes: :environment do
|
39
|
+
print_routes api_routes
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# helper methods
|
44
|
+
#
|
45
|
+
def print_routes(routes_array)
|
46
|
+
routes_array.each do |route|
|
47
|
+
puts "\t#{route[:verb].ljust(7)}#{route[:path].ljust(42)}#{route[:description]}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_path(route)
|
52
|
+
path = route.path
|
53
|
+
|
54
|
+
path.sub!(/\(\.\w+?\)$/, '')
|
55
|
+
path.sub!('(.:format)', '')
|
56
|
+
|
57
|
+
if route.version
|
58
|
+
path.sub!(':version', route.version.to_s)
|
59
|
+
else
|
60
|
+
path.sub!('/:version', '')
|
61
|
+
end
|
62
|
+
|
63
|
+
path
|
64
|
+
end
|
65
|
+
|
66
|
+
def app
|
67
|
+
api_class.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# CRUD examples
|
4
|
+
#
|
5
|
+
# for single
|
6
|
+
RSpec.shared_examples 'single CRUD' do |base_path: '/api/v1', resource: ''|
|
7
|
+
it_behaves_like 'POST', base_path: base_path, resource: resource
|
8
|
+
it_behaves_like 'GET one', base_path: base_path, resource: resource
|
9
|
+
it_behaves_like 'PUT one', base_path: base_path, resource: resource
|
10
|
+
it_behaves_like 'PATCH one', base_path: base_path, resource: resource
|
11
|
+
it_behaves_like 'DELETE one', base_path: base_path, resource: resource
|
12
|
+
end
|
13
|
+
#
|
14
|
+
# for plural
|
15
|
+
RSpec.shared_examples 'plural CRUD' do |base_path: '/api/v1', resource: '', key: nil|
|
16
|
+
it_behaves_like 'POST', base_path: base_path, resource: resource
|
17
|
+
it_behaves_like 'GET all', base_path: base_path, resource: resource
|
18
|
+
it_behaves_like 'GET specific', base_path: base_path, resource: resource, key: key
|
19
|
+
it_behaves_like 'PUT specific', base_path: base_path, resource: resource, key: key
|
20
|
+
it_behaves_like 'PATCH specific', base_path: base_path, resource: resource, key: key
|
21
|
+
it_behaves_like 'DELETE specific', base_path: base_path, resource: resource, key: key
|
22
|
+
end
|
23
|
+
|
24
|
+
# singular forms
|
25
|
+
#
|
26
|
+
RSpec.shared_examples 'POST' do |base_path: '/api/v1', resource: ''|
|
27
|
+
let(:route) { "#{base_path}/#{resource}" }
|
28
|
+
|
29
|
+
subject { post route }
|
30
|
+
specify { expect(subject.status).to eql 201 }
|
31
|
+
end
|
32
|
+
|
33
|
+
RSpec.shared_examples 'GET one' do |base_path: '/api/v1', resource: ''|
|
34
|
+
let(:route) { "#{base_path}/#{resource}" }
|
35
|
+
|
36
|
+
subject { get route }
|
37
|
+
specify { expect(subject.status).to eql 200 }
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec.shared_examples 'PUT one' do |base_path: '/api/v1', resource: ''|
|
41
|
+
let(:route) { "#{base_path}/#{resource}" }
|
42
|
+
|
43
|
+
subject { put route }
|
44
|
+
specify { expect(subject.status).to eql 200 }
|
45
|
+
end
|
46
|
+
|
47
|
+
RSpec.shared_examples 'PATCH one' do |base_path: '/api/v1', resource: ''|
|
48
|
+
let(:route) { "#{base_path}/#{resource}" }
|
49
|
+
|
50
|
+
subject { patch route }
|
51
|
+
specify { expect(subject.status).to eql 200 }
|
52
|
+
end
|
53
|
+
|
54
|
+
RSpec.shared_examples 'DELETE one' do |base_path: '/api/v1', resource: ''|
|
55
|
+
let(:route) { "#{base_path}/#{resource}" }
|
56
|
+
|
57
|
+
subject { delete route }
|
58
|
+
specify { expect(subject.status).to eql 204 }
|
59
|
+
end
|
60
|
+
|
61
|
+
# plural forms
|
62
|
+
#
|
63
|
+
RSpec.shared_examples 'POST' do |base_path: '/api/v1', resource: '', key: nil|
|
64
|
+
let(:route) { "#{base_path}/#{resource}" }
|
65
|
+
let(:specific_route) { "#{route}/#{key}" }
|
66
|
+
|
67
|
+
subject { post route }
|
68
|
+
specify { expect(subject.status).to eql 201 }
|
69
|
+
end
|
70
|
+
|
71
|
+
RSpec.shared_examples 'GET all' do |base_path: '/api/v1', resource: '', key: nil|
|
72
|
+
let(:route) { "#{base_path}/#{resource}" }
|
73
|
+
let(:specific_route) { "#{route}/#{key}" }
|
74
|
+
|
75
|
+
subject { get route }
|
76
|
+
specify { expect(subject.status).to eql 200 }
|
77
|
+
end
|
78
|
+
|
79
|
+
RSpec.shared_examples 'GET specific' do |base_path: '/api/v1', resource: '', key: nil|
|
80
|
+
let(:route) { "#{base_path}/#{resource}" }
|
81
|
+
let(:specific_route) { "#{route}/#{key}" }
|
82
|
+
|
83
|
+
subject { get specific_route }
|
84
|
+
specify { expect(subject.status).to eql 200 }
|
85
|
+
end
|
86
|
+
|
87
|
+
RSpec.shared_examples 'PUT specific' do |base_path: '/api/v1', resource: '', key: nil|
|
88
|
+
let(:route) { "#{base_path}/#{resource}" }
|
89
|
+
let(:specific_route) { "#{route}/#{key}" }
|
90
|
+
|
91
|
+
subject { put specific_route }
|
92
|
+
specify { expect(subject.status).to eql 200 }
|
93
|
+
end
|
94
|
+
|
95
|
+
RSpec.shared_examples 'PATCH specific' do |base_path: '/api/v1', resource: '', key: nil|
|
96
|
+
let(:route) { "#{base_path}/#{resource}" }
|
97
|
+
let(:specific_route) { "#{route}/#{key}" }
|
98
|
+
|
99
|
+
subject { patch specific_route }
|
100
|
+
specify { expect(subject.status).to eql 200 }
|
101
|
+
end
|
102
|
+
|
103
|
+
RSpec.shared_examples 'DELETE specific' do |base_path: '/api/v1', resource: '', key: nil|
|
104
|
+
let(:route) { "#{base_path}/#{resource}" }
|
105
|
+
let(:specific_route) { "#{route}/#{key}" }
|
106
|
+
|
107
|
+
subject { delete specific_route }
|
108
|
+
specify { expect(subject.status).to eql 204 }
|
109
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-10-18 20:40:59 +0200 using RuboCop version 0.44.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
|
11
|
+
# URISchemes: http, https
|
12
|
+
Metrics/LineLength:
|
13
|
+
Max: 120
|
14
|
+
|
15
|
+
# Offense count: 1
|
16
|
+
# Configuration parameters: CountComments.
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 10
|
19
|
+
|
20
|
+
Style/AsciiComments:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
# Offense count: 6
|
24
|
+
Style/Documentation:
|
25
|
+
Exclude:
|
26
|
+
- 'spec/**/*'
|
27
|
+
- 'api/**/*'
|
28
|
+
- 'lib/**/*'
|