shaf 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/shaf/app.rb +27 -9
- data/lib/shaf/command/base.rb +4 -0
- data/lib/shaf/command/new.rb +5 -1
- data/lib/shaf/command/server.rb +5 -1
- data/lib/shaf/command/templates/config/settings.yml.erb +9 -8
- data/lib/shaf/extensions/resource_uris.rb +37 -155
- data/lib/shaf/extensions/symbolic_routes.rb +5 -18
- data/lib/shaf/formable/builder.rb +58 -19
- data/lib/shaf/formable/form.rb +13 -10
- data/lib/shaf/formable.rb +10 -23
- data/lib/shaf/generator/base.rb +82 -0
- data/lib/shaf/generator/controller.rb +19 -35
- data/lib/shaf/generator/forms.rb +10 -14
- data/lib/shaf/generator/migration/add_column.rb +0 -4
- data/lib/shaf/generator/migration/add_index.rb +0 -4
- data/lib/shaf/generator/migration/base.rb +8 -0
- data/lib/shaf/generator/migration/create_table.rb +0 -4
- data/lib/shaf/generator/migration/drop_column.rb +0 -4
- data/lib/shaf/generator/migration/rename_column.rb +0 -4
- data/lib/shaf/generator/model.rb +29 -14
- data/lib/shaf/generator/policy.rb +8 -14
- data/lib/shaf/generator/profile.rb +9 -14
- data/lib/shaf/generator/scaffold.rb +6 -9
- data/lib/shaf/generator/serializer.rb +31 -30
- data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
- data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
- data/lib/shaf/generator/templates/api/profile.rb.erb +1 -1
- data/lib/shaf/generator/templates/api/serializer.rb.erb +1 -1
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +14 -14
- data/lib/shaf/helpers/paginate.rb +1 -1
- data/lib/shaf/profile.rb +8 -8
- data/lib/shaf/registrable_factory.rb +62 -32
- data/lib/shaf/responder/problem_json.rb +1 -1
- data/lib/shaf/router.rb +65 -12
- data/lib/shaf/spec/integration_spec.rb +1 -1
- data/lib/shaf/tasks.rb +0 -1
- data/lib/shaf/upgrade/package.rb +5 -7
- data/lib/shaf/version.rb +1 -1
- data/templates/Rakefile +0 -6
- data/templates/api/controllers/base_controller.rb +0 -2
- data/templates/api/serializers/root_serializer.rb +0 -11
- data/templates/config/initializers/middleware.rb +6 -0
- data/templates/spec/spec_helper.rb +4 -1
- data/upgrades/3.0.0.tar.gz +0 -0
- data.tar.gz.sig +0 -0
- metadata +19 -22
- metadata.gz.sig +0 -0
- data/lib/shaf/api_doc/comment.rb +0 -27
- data/lib/shaf/api_doc/document.rb +0 -137
- data/lib/shaf/api_doc/link_relations.rb +0 -77
- data/lib/shaf/middleware.rb +0 -1
- data/lib/shaf/tasks/api_doc_task.rb +0 -146
@@ -11,19 +11,8 @@ module Shaf
|
|
11
11
|
create_profile
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
return n unless n.empty?
|
17
|
-
raise Command::ArgumentError,
|
18
|
-
"Please provide a model name when using the serializer generator!"
|
19
|
-
end
|
20
|
-
|
21
|
-
def plural_name
|
22
|
-
Utils.pluralize(name)
|
23
|
-
end
|
24
|
-
|
25
|
-
def model_class_name
|
26
|
-
Utils.model_name(name)
|
14
|
+
def serializer_class_name
|
15
|
+
"#{model_class_name}Serializer"
|
27
16
|
end
|
28
17
|
|
29
18
|
def policy_class_name
|
@@ -38,21 +27,31 @@ module Shaf
|
|
38
27
|
'spec/serializer_spec.rb'
|
39
28
|
end
|
40
29
|
|
41
|
-
def
|
42
|
-
|
30
|
+
def target_dir
|
31
|
+
'api/serializers'
|
32
|
+
end
|
33
|
+
|
34
|
+
def target_name
|
35
|
+
"#{name}_serializer.rb"
|
43
36
|
end
|
44
37
|
|
45
38
|
def spec_target
|
46
|
-
|
39
|
+
target(directory: 'spec/serializers', name: "#{name}_serializer_spec.rb")
|
40
|
+
end
|
41
|
+
|
42
|
+
def policy_file
|
43
|
+
File.join(['policies', namespace, "#{name}_policy"].compact)
|
47
44
|
end
|
48
45
|
|
49
46
|
def create_serializer
|
50
47
|
content = render(template, opts)
|
48
|
+
content = wrap_in_module(content, module_name)
|
51
49
|
write_output(target, content)
|
52
50
|
end
|
53
51
|
|
54
52
|
def create_serializer_spec
|
55
53
|
content = render(spec_template, opts)
|
54
|
+
content = wrap_in_module(content, module_name, search: "describe #{serializer_class_name}")
|
56
55
|
write_output(spec_target, content)
|
57
56
|
end
|
58
57
|
|
@@ -74,16 +73,16 @@ module Shaf
|
|
74
73
|
|
75
74
|
def profile_with_doc
|
76
75
|
doc = <<~DOC
|
77
|
-
# Adds a link to the '#{
|
76
|
+
# Adds a link to the '#{resource_name}' profile and a curie. By default the
|
78
77
|
# curie prefix is 'doc', use the `curie_prefix` keyword argument to
|
79
78
|
# change this.
|
80
79
|
# Note: the target of the profile link and the curie will be set to
|
81
|
-
# `profile_uri('#{
|
80
|
+
# `profile_uri('#{resource_name}')` resp. `doc_curie_uri('#{resource_name}')`. To
|
82
81
|
# create links for external profiles or curies, delete the next line
|
83
82
|
# and use `::link` and/or `::curie` instead.
|
84
83
|
DOC
|
85
84
|
|
86
|
-
doc.split("\n") << %Q(profile #{Utils.symbol_string(
|
85
|
+
doc.split("\n") << %Q(profile #{Utils.symbol_string(resource_name)})
|
87
86
|
end
|
88
87
|
|
89
88
|
def links_with_doc
|
@@ -98,7 +97,7 @@ module Shaf
|
|
98
97
|
def collection_link
|
99
98
|
link(
|
100
99
|
rel: "collection",
|
101
|
-
uri_helper: "#{
|
100
|
+
uri_helper: "#{collection_name}_uri",
|
102
101
|
kwargs: {embed_depth: 0}
|
103
102
|
)
|
104
103
|
end
|
@@ -106,21 +105,21 @@ module Shaf
|
|
106
105
|
def self_link
|
107
106
|
link(
|
108
107
|
rel: "self",
|
109
|
-
uri_helper: "#{
|
108
|
+
uri_helper: "#{resource_name}_uri(resource)"
|
110
109
|
)
|
111
110
|
end
|
112
111
|
|
113
112
|
def edit_link
|
114
113
|
link(
|
115
114
|
rel: "edit-form",
|
116
|
-
uri_helper: "edit_#{
|
115
|
+
uri_helper: "edit_#{resource_name}_uri(resource)"
|
117
116
|
)
|
118
117
|
end
|
119
118
|
|
120
119
|
def delete_link
|
121
120
|
link(
|
122
121
|
rel: "delete",
|
123
|
-
uri_helper: "#{
|
122
|
+
uri_helper: "#{resource_name}_uri(resource)",
|
124
123
|
kwargs: {curie: :doc}
|
125
124
|
)
|
126
125
|
end
|
@@ -128,7 +127,7 @@ module Shaf
|
|
128
127
|
def create_link
|
129
128
|
link(
|
130
129
|
rel: "create-form",
|
131
|
-
uri_helper: "new_#{
|
130
|
+
uri_helper: "new_#{resource_name}_uri"
|
132
131
|
)
|
133
132
|
end
|
134
133
|
|
@@ -147,9 +146,9 @@ module Shaf
|
|
147
146
|
def collection_with_doc
|
148
147
|
<<~EOS.split("\n")
|
149
148
|
collection of: '#{plural_name}' do
|
150
|
-
curie(:doc) { doc_curie_uri('#{
|
149
|
+
curie(:doc) { doc_curie_uri('#{resource_name}') }
|
151
150
|
|
152
|
-
link :self, #{
|
151
|
+
link :self, #{collection_name}_uri
|
153
152
|
link :up, root_uri
|
154
153
|
|
155
154
|
#{create_link.join("\n ")}
|
@@ -160,10 +159,12 @@ module Shaf
|
|
160
159
|
def opts
|
161
160
|
{
|
162
161
|
name: name,
|
163
|
-
|
162
|
+
resource_name: resource_name,
|
163
|
+
collection_name: collection_name,
|
164
|
+
class_name: serializer_class_name,
|
164
165
|
model_class_name: model_class_name,
|
165
166
|
policy_class_name: policy_class_name,
|
166
|
-
|
167
|
+
policy_file: policy_file,
|
167
168
|
attribute_names: attribute_names,
|
168
169
|
link_relations: link_relations,
|
169
170
|
profile_with_doc: profile_with_doc,
|
@@ -174,12 +175,12 @@ module Shaf
|
|
174
175
|
end
|
175
176
|
|
176
177
|
def create_policy
|
177
|
-
policy_args = ["policy",
|
178
|
+
policy_args = ["policy", name_arg, *attribute_names]
|
178
179
|
Generator::Factory.create(*policy_args, **options).call
|
179
180
|
end
|
180
181
|
|
181
182
|
def create_profile
|
182
|
-
profile_args = ["profile",
|
183
|
+
profile_args = ["profile", name_arg, *attributes]
|
183
184
|
Generator::Factory.create(*profile_args, **options).call
|
184
185
|
end
|
185
186
|
end
|
@@ -4,45 +4,45 @@ class <%= controller_class_name %> < BaseController
|
|
4
4
|
|
5
5
|
authorize_with <%= policy_class_name %>
|
6
6
|
|
7
|
-
resource_uris_for :<%= name %>
|
7
|
+
resource_uris_for :<%= name %><%= namespace ? ", namespace: '#{namespace}'" : "" %>
|
8
8
|
|
9
|
-
get :<%=
|
9
|
+
get :<%= collection_name %>_path do
|
10
10
|
authorize! :read
|
11
11
|
collection = paginate(<%= model_class_name %>.order(:created_at).reverse)
|
12
12
|
respond_with_collection collection, serializer: <%= serializer_class_name %>
|
13
13
|
end
|
14
14
|
|
15
|
-
get :new_<%=
|
15
|
+
get :new_<%= resource_name %>_path do
|
16
16
|
authorize! :read
|
17
17
|
cache_control(:private, http_cache_max_age: :short)
|
18
18
|
respond_with create_form
|
19
19
|
end
|
20
20
|
|
21
|
-
post :<%=
|
21
|
+
post :<%= collection_name %>_path do
|
22
22
|
authorize! :write
|
23
23
|
<%= name %> = <%= model_class_name %>.create(<%= name %>_params)
|
24
|
-
headers('Location' => <%=
|
24
|
+
headers('Location' => <%= resource_name %>_uri(<%= name %>))
|
25
25
|
respond_with <%= name %>, status: 201
|
26
26
|
end
|
27
27
|
|
28
|
-
get :<%=
|
28
|
+
get :<%= resource_name %>_path do
|
29
29
|
authorize! :read
|
30
30
|
respond_with <%= name %>
|
31
31
|
end
|
32
32
|
|
33
|
-
get :edit_<%=
|
33
|
+
get :edit_<%= resource_name %>_path do
|
34
34
|
authorize! :write
|
35
35
|
cache_control(:private, http_cache_max_age: :short)
|
36
36
|
respond_with edit_form
|
37
37
|
end
|
38
38
|
|
39
|
-
put :<%=
|
39
|
+
put :<%= resource_name %>_path do
|
40
40
|
authorize! :write
|
41
41
|
<%= name %>.update(<%= name %>_params)
|
42
42
|
respond_with <%= name %>
|
43
43
|
end
|
44
44
|
|
45
|
-
delete :<%=
|
45
|
+
delete :<%= resource_name %>_path do
|
46
46
|
authorize! :write
|
47
47
|
<%= name %>.destroy
|
48
48
|
status 204
|
@@ -62,15 +62,15 @@ class <%= controller_class_name %> < BaseController
|
|
62
62
|
|
63
63
|
def create_form
|
64
64
|
<%= model_class_name %>.create_form.tap do |form|
|
65
|
-
form.self_link = new_<%=
|
66
|
-
form.href = <%=
|
65
|
+
form.self_link = new_<%= resource_name %>_uri
|
66
|
+
form.href = <%= collection_name %>_uri
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
def edit_form
|
71
71
|
<%= name %>.edit_form.tap do |form|
|
72
|
-
form.self_link = edit_<%=
|
73
|
-
form.href = <%=
|
72
|
+
form.self_link = edit_<%= resource_name %>_uri(<%= name %>)
|
73
|
+
form.href = <%= resource_name %>_uri(<%= name %>)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -4,12 +4,12 @@ class <%= class_name %>
|
|
4
4
|
forms_for(<%= model_class_name %>) do
|
5
5
|
<%= fields.join("\n ") %>
|
6
6
|
|
7
|
-
|
7
|
+
create_form do
|
8
8
|
title 'Create <%= model_class_name %>'
|
9
9
|
name 'create-<%= model_name %>'
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
edit_form do
|
13
13
|
instance_accessor
|
14
14
|
title 'Update <%= model_class_name %>'
|
15
15
|
name 'update-<%= model_name %>'
|
@@ -4,10 +4,10 @@ describe <%= model_class_name %>, type: :integration do
|
|
4
4
|
|
5
5
|
it "returns a <%= name %>" do
|
6
6
|
<%= name %> = <%= model_class_name %>.create
|
7
|
-
get <%=
|
7
|
+
get <%= resource_name %>_uri(<%= name %>)
|
8
8
|
_(status).must_equal 200
|
9
9
|
_(link_rels).must_include(:self)
|
10
|
-
_(links[:self][:href]).must_equal <%=
|
10
|
+
_(links[:self][:href]).must_equal <%= resource_name %>_uri(<%= name %>)
|
11
11
|
<% params.each do |param| -%>
|
12
12
|
_(attributes).must_include(:'<%= param[0] %>')
|
13
13
|
<% end -%>
|
@@ -16,10 +16,10 @@ describe <%= model_class_name %>, type: :integration do
|
|
16
16
|
it "lists all <%= plural_name %>" do
|
17
17
|
2.times { <%= model_class_name %>.create }
|
18
18
|
|
19
|
-
get <%=
|
19
|
+
get <%= collection_name %>_uri
|
20
20
|
_(status).must_equal 200
|
21
21
|
_(link_rels).must_include(:self)
|
22
|
-
_(links[:self][:href]).must_include <%=
|
22
|
+
_(links[:self][:href]).must_include <%= collection_name %>_uri
|
23
23
|
_(embedded(:'<%= plural_name %>').size).must_equal 2
|
24
24
|
|
25
25
|
each_embedded :'<%= plural_name %>' do
|
@@ -31,12 +31,12 @@ describe <%= model_class_name %>, type: :integration do
|
|
31
31
|
end
|
32
32
|
<% if params.size > 0 %>
|
33
33
|
it "can create <%= plural_name %>" do
|
34
|
-
get <%=
|
34
|
+
get <%= collection_name %>_uri
|
35
35
|
|
36
36
|
_(link_rels).must_include(:'create-form')
|
37
37
|
follow_rel :'create-form'
|
38
|
-
_(links[:self][:href]).must_equal new_<%=
|
39
|
-
_(attributes[:href]).must_equal <%=
|
38
|
+
_(links[:self][:href]).must_equal new_<%= resource_name %>_uri
|
39
|
+
_(attributes[:href]).must_equal <%= collection_name %>_uri
|
40
40
|
_(attributes[:method]).must_equal "POST"
|
41
41
|
_(attributes[:name]).must_equal "create-<%= name %>"
|
42
42
|
_(attributes[:title]).must_equal "Create <%= model_class_name %>"
|
@@ -49,9 +49,9 @@ describe <%= model_class_name %>, type: :integration do
|
|
49
49
|
_(link_rels).must_include(:self)
|
50
50
|
_(headers["Location"]).must_equal links[:self][:href]
|
51
51
|
|
52
|
-
get <%=
|
52
|
+
get <%= collection_name %>_uri
|
53
53
|
_(status).must_equal 200
|
54
|
-
_(links[:self][:href]).must_include <%=
|
54
|
+
_(links[:self][:href]).must_include <%= collection_name %>_uri
|
55
55
|
_(embedded(:'<%= plural_name %>').size).must_equal 1
|
56
56
|
|
57
57
|
embedded :'<%= plural_name %>' do
|
@@ -68,14 +68,14 @@ describe <%= model_class_name %>, type: :integration do
|
|
68
68
|
|
69
69
|
it "<%= plural_name %> can be updated" do
|
70
70
|
<%= name %> = <%= model_class_name %>.create
|
71
|
-
get <%=
|
71
|
+
get <%= resource_name %>_uri(<%= name %>)
|
72
72
|
_(status).must_equal 200
|
73
73
|
|
74
74
|
_(link_rels).must_include(:'edit-form')
|
75
75
|
follow_rel :'edit-form'
|
76
76
|
|
77
|
-
_(links[:self][:href]).must_equal edit_<%=
|
78
|
-
_(attributes[:href]).must_equal <%=
|
77
|
+
_(links[:self][:href]).must_equal edit_<%= resource_name %>_uri(<%= name %>)
|
78
|
+
_(attributes[:href]).must_equal <%= resource_name %>_uri(<%= name %>)
|
79
79
|
_(attributes[:method]).must_equal "PUT"
|
80
80
|
_(attributes[:name]).must_equal "update-<%= name %>"
|
81
81
|
_(attributes[:title]).must_equal "Update <%= model_class_name %>"
|
@@ -91,14 +91,14 @@ describe <%= model_class_name %>, type: :integration do
|
|
91
91
|
|
92
92
|
it "<%= plural_name %> can be deleted" do
|
93
93
|
<%= name %> = <%= model_class_name %>.create
|
94
|
-
get <%=
|
94
|
+
get <%= resource_name %>_uri(<%= name %>)
|
95
95
|
_(status).must_equal 200
|
96
96
|
_(link_rels).must_include(:'doc:delete')
|
97
97
|
|
98
98
|
follow_rel(:'doc:delete', method: :delete)
|
99
99
|
_(status).must_equal 204
|
100
100
|
|
101
|
-
get <%=
|
101
|
+
get <%= resource_name %>_uri(<%= name %>)
|
102
102
|
_(status).must_equal 404
|
103
103
|
end
|
104
104
|
|
data/lib/shaf/profile.rb
CHANGED
@@ -5,6 +5,14 @@ require 'shaf/extensions/resource_uris'
|
|
5
5
|
|
6
6
|
module Shaf
|
7
7
|
class Profile
|
8
|
+
module NormalizeName
|
9
|
+
private def normalize(name)
|
10
|
+
name.to_s.downcase.tr('-', '_')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
extend NormalizeName
|
15
|
+
include NormalizeName
|
8
16
|
include Shaf::UriHelper
|
9
17
|
|
10
18
|
class << self
|
@@ -98,18 +106,10 @@ module Shaf
|
|
98
106
|
def evaluator
|
99
107
|
Evaluator.new(parent: self)
|
100
108
|
end
|
101
|
-
|
102
|
-
def normalize(name)
|
103
|
-
name.to_s.downcase.tr('-', '_')
|
104
|
-
end
|
105
109
|
end
|
106
110
|
|
107
111
|
def name
|
108
112
|
normalize(self.class.name)
|
109
113
|
end
|
110
|
-
|
111
|
-
def normalize(str)
|
112
|
-
self.class.normalize(str)
|
113
|
-
end
|
114
114
|
end
|
115
115
|
end
|
@@ -1,15 +1,58 @@
|
|
1
1
|
module Shaf
|
2
2
|
module RegistrableFactory
|
3
|
-
|
4
3
|
class NotFoundError < StandardError; end
|
4
|
+
class NoIdentifiersError < StandardError
|
5
|
+
|
6
|
+
def initialize(clazz)
|
7
|
+
super <<~ERR
|
8
|
+
#{clazz} does not have an @identifiers ivar.
|
9
|
+
Did you perhaps forget to call `#{clazz}.identifier`?
|
10
|
+
ERR
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Entry
|
15
|
+
attr_reader :clazz
|
16
|
+
|
17
|
+
def initialize(clazz)
|
18
|
+
@clazz = clazz
|
19
|
+
end
|
20
|
+
|
21
|
+
def match?(strings)
|
22
|
+
raise NoIdentifiersError, clazz unless identifiers
|
23
|
+
return false if strings.size < identifiers.size
|
24
|
+
identifiers.zip(strings).all? { |pattern, str| matching_identifier? str, pattern }
|
25
|
+
end
|
26
|
+
|
27
|
+
def identifier_count
|
28
|
+
identifiers&.size || 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def usage
|
32
|
+
clazz.instance_variable_get(:@usage)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def identifiers
|
38
|
+
clazz.identified_by
|
39
|
+
end
|
40
|
+
|
41
|
+
def matching_identifier?(str, pattern)
|
42
|
+
return false if pattern.nil? || str.nil? || str.empty?
|
43
|
+
pattern = pattern.to_s if pattern.is_a? Symbol
|
44
|
+
return str == pattern if pattern.is_a? String
|
45
|
+
!!str.match(pattern)
|
46
|
+
end
|
47
|
+
end
|
5
48
|
|
6
49
|
def all
|
7
|
-
reg.
|
50
|
+
reg.map(&:clazz)
|
8
51
|
end
|
9
52
|
|
10
|
-
def each
|
53
|
+
def each(&block)
|
11
54
|
return all.each unless block_given?
|
12
|
-
all.each
|
55
|
+
all.each(&block)
|
13
56
|
end
|
14
57
|
|
15
58
|
def size
|
@@ -17,34 +60,31 @@ module Shaf
|
|
17
60
|
end
|
18
61
|
|
19
62
|
def register(clazz)
|
20
|
-
reg << clazz
|
63
|
+
reg << Entry.new(clazz)
|
21
64
|
end
|
22
65
|
|
23
66
|
def unregister(*str)
|
24
67
|
return if str.empty? || !str.all?
|
25
|
-
reg.delete_if { |
|
68
|
+
reg.delete_if { |entry| entry.match? str }
|
26
69
|
end
|
27
70
|
|
28
71
|
def lookup(*str)
|
29
|
-
|
30
|
-
reg.select { |clazz| matching_class? str, clazz }
|
31
|
-
.sort_by(&method(:identifier_count))
|
32
|
-
.last
|
72
|
+
lookup_entry(*str)&.clazz
|
33
73
|
end
|
34
74
|
|
35
75
|
def usage
|
36
76
|
reg.compact.map do |entry|
|
37
|
-
usage = entry.
|
77
|
+
usage = entry.usage
|
38
78
|
usage.respond_to?(:call) ? usage.call : usage
|
39
79
|
end
|
40
80
|
end
|
41
81
|
|
42
82
|
def create(*params, **options)
|
43
|
-
|
44
|
-
raise NotFoundError.new(%Q(Command '#{ARGV}' is not supported)) unless
|
83
|
+
entry = lookup_entry(*params)
|
84
|
+
raise NotFoundError.new(%Q(Command '#{ARGV}' is not supported)) unless entry
|
45
85
|
|
46
|
-
args = init_args(
|
47
|
-
clazz.new(*args, **options)
|
86
|
+
args = init_args(entry, params)
|
87
|
+
entry.clazz.new(*args, **options)
|
48
88
|
end
|
49
89
|
|
50
90
|
private
|
@@ -53,25 +93,15 @@ module Shaf
|
|
53
93
|
@reg ||= []
|
54
94
|
end
|
55
95
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
def matching_identifier?(str, pattern)
|
63
|
-
return false if pattern.nil? || str.nil? || str.empty?
|
64
|
-
pattern = pattern.to_s if pattern.is_a? Symbol
|
65
|
-
return str == pattern if pattern.is_a? String
|
66
|
-
str.match(pattern) || false
|
67
|
-
end
|
68
|
-
|
69
|
-
def identifier_count(clazz)
|
70
|
-
clazz.instance_variable_get(:@identifiers)&.size || 0
|
96
|
+
def lookup_entry(*str)
|
97
|
+
return if str.empty? || !str.all?
|
98
|
+
reg.select { |entry| entry.match? str }
|
99
|
+
.sort_by { |entry| entry.identifier_count }
|
100
|
+
.last
|
71
101
|
end
|
72
102
|
|
73
|
-
def init_args(
|
74
|
-
first_non_id = identifier_count
|
103
|
+
def init_args(entry, params)
|
104
|
+
first_non_id = entry.identifier_count
|
75
105
|
params[first_non_id..-1]
|
76
106
|
end
|
77
107
|
end
|
data/lib/shaf/router.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'shaf/middleware'
|
4
3
|
require 'set'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'shaf/errors'
|
5
6
|
|
6
7
|
module Shaf
|
7
8
|
class Router
|
@@ -21,16 +22,56 @@ module Shaf
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
class NullController
|
26
|
+
def call(env)
|
27
|
+
request = request(env)
|
28
|
+
responder = Responder.for(request, error)
|
29
|
+
responder.call(self, error)
|
30
|
+
|
31
|
+
response.finish
|
32
|
+
end
|
33
|
+
|
34
|
+
# Called from responder
|
35
|
+
def content_type(mime)
|
36
|
+
response["Content-Type"] = mime
|
37
|
+
end
|
38
|
+
|
39
|
+
# Called from responder
|
40
|
+
def body(body)
|
41
|
+
response.body = body
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def status
|
47
|
+
500
|
48
|
+
end
|
49
|
+
|
50
|
+
def request(env)
|
51
|
+
Sinatra::Request.new(env)
|
52
|
+
end
|
53
|
+
|
54
|
+
def response
|
55
|
+
@response ||= Sinatra::Response.new(nil, status)
|
56
|
+
end
|
57
|
+
|
58
|
+
def error
|
59
|
+
@error ||= Errors::ServerError.new(
|
60
|
+
'Internal error: No controller has been mounted on Router',
|
61
|
+
code: 'NO_MOUNTED_CONTROLLERS',
|
62
|
+
title: 'Shaf::Router must have at least one mounted controller',
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
24
67
|
class << self
|
25
68
|
def mount(controller, default: false)
|
26
69
|
@default_controller = controller if default
|
27
|
-
|
28
|
-
@controllers << controller
|
70
|
+
controllers << controller
|
29
71
|
end
|
30
72
|
|
31
73
|
def routes
|
32
|
-
|
33
|
-
@routes
|
74
|
+
@routes ||= init_routes
|
34
75
|
end
|
35
76
|
|
36
77
|
# This controller will be used when no other can handle the request
|
@@ -41,32 +82,44 @@ module Shaf
|
|
41
82
|
|
42
83
|
private
|
43
84
|
|
44
|
-
|
85
|
+
def controllers
|
86
|
+
@controllers ||= []
|
87
|
+
end
|
45
88
|
|
46
89
|
def init_routes
|
47
|
-
|
90
|
+
routes = Hash.new do |hash, key|
|
48
91
|
hash[key] = Hash.new { |h, k| h[k] = Set.new }
|
49
92
|
end
|
50
|
-
controllers.each { |controller|
|
93
|
+
controllers.each { |controller| init(controller, routes) }
|
94
|
+
routes
|
51
95
|
end
|
52
96
|
|
53
|
-
def
|
97
|
+
def init(controller, routes)
|
54
98
|
controller.routes.each do |method, controller_routes|
|
55
99
|
routes[method][controller] += controller_routes.map(&:first)
|
56
100
|
end
|
57
101
|
end
|
58
102
|
end
|
59
103
|
|
60
|
-
def initialize(app)
|
104
|
+
def initialize(app = NullController.new)
|
61
105
|
@app = app
|
62
106
|
end
|
63
107
|
|
64
108
|
def call(env)
|
109
|
+
# When the api is mounted in Rails then the mount point will be not be
|
110
|
+
# present in PATH_INFO but it will instead be available in SCRIPT_NAME
|
111
|
+
# Shaf need to know about the full path in order to make all path helpers
|
112
|
+
# work, so we need to add the mountpoint back to PATH_INFO.
|
113
|
+
unless String(env['SCRIPT_NAME']).empty?
|
114
|
+
env['PATH_INFO'] = '' if env['PATH_INFO'] == '/'
|
115
|
+
env['PATH_INFO'] = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
116
|
+
end
|
117
|
+
|
65
118
|
http_method, path = http_details(env)
|
66
119
|
|
67
120
|
result = nil
|
68
121
|
|
69
|
-
|
122
|
+
each_controller_for(http_method, path) do |controller|
|
70
123
|
result = controller.call(env)
|
71
124
|
break unless cascade? result
|
72
125
|
end
|
@@ -80,7 +133,7 @@ module Shaf
|
|
80
133
|
[env['REQUEST_METHOD'], env['PATH_INFO']]
|
81
134
|
end
|
82
135
|
|
83
|
-
def
|
136
|
+
def each_controller_for(http_method, path)
|
84
137
|
find_cached(http_method, path).each { |ctrlr| yield ctrlr }
|
85
138
|
|
86
139
|
if controller = find(http_method, path)
|