shaf 1.6.1 → 2.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.tar.gz.sig +0 -0
- data/iana_link_relations.csv.gz +0 -0
- data/lib/shaf.rb +6 -0
- data/lib/shaf/alps/attribute_serializer.rb +41 -0
- data/lib/shaf/alps/json_serializer.rb +50 -0
- data/lib/shaf/alps/relation_serializer.rb +70 -0
- data/lib/shaf/api_doc/link_relations.rb +77 -0
- data/lib/shaf/authenticator.rb +56 -0
- data/lib/shaf/authenticator/base.rb +161 -0
- data/lib/shaf/authenticator/basic_auth.rb +25 -0
- data/lib/shaf/authenticator/challenge.rb +32 -0
- data/lib/shaf/authenticator/parameter.rb +31 -0
- data/lib/shaf/authenticator/request.rb +17 -0
- data/lib/shaf/command/console.rb +1 -1
- data/lib/shaf/command/generate.rb +5 -2
- data/lib/shaf/command/new.rb +20 -7
- data/lib/shaf/command/templates/Gemfile.erb +1 -0
- data/{templates/config/settings.yml → lib/shaf/command/templates/config/settings.yml.erb} +1 -5
- data/lib/shaf/errors.rb +11 -0
- data/lib/shaf/extensions.rb +3 -3
- data/lib/shaf/extensions/api_routes.rb +60 -0
- data/lib/shaf/extensions/authorize.rb +11 -9
- data/lib/shaf/extensions/log.rb +1 -1
- data/lib/shaf/extensions/resource_uris.rb +139 -63
- data/lib/shaf/extensions/symbolic_routes.rb +22 -19
- data/lib/shaf/formable.rb +1 -2
- data/lib/shaf/formable/form.rb +1 -1
- data/lib/shaf/generator.rb +2 -0
- data/lib/shaf/generator/base.rb +2 -3
- data/lib/shaf/generator/controller.rb +11 -7
- data/lib/shaf/generator/doc.rb +17 -0
- data/lib/shaf/generator/forms.rb +1 -0
- data/lib/shaf/generator/helper.rb +2 -1
- data/lib/shaf/generator/migration/base.rb +7 -3
- data/lib/shaf/generator/migration/type.rb +4 -26
- data/lib/shaf/generator/migration/types.rb +45 -16
- data/lib/shaf/generator/model.rb +1 -2
- data/lib/shaf/generator/profile.rb +52 -0
- data/lib/shaf/generator/serializer.rb +38 -73
- data/lib/shaf/generator/templates/api/policy.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/profile.rb.erb +16 -0
- data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -2
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +1 -2
- data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +5 -5
- data/lib/shaf/helpers.rb +4 -0
- data/lib/shaf/helpers/authentication.rb +79 -0
- data/lib/shaf/helpers/payload.rb +14 -34
- data/lib/shaf/helpers/vary.rb +8 -0
- data/lib/shaf/logger.rb +12 -0
- data/lib/shaf/parser.rb +65 -0
- data/lib/shaf/parser/base.rb +44 -0
- data/lib/shaf/parser/form_data.rb +15 -0
- data/lib/shaf/parser/json.rb +26 -0
- data/lib/shaf/profile.rb +110 -0
- data/lib/shaf/profile/attribute.rb +29 -0
- data/lib/shaf/profile/evaluator.rb +46 -0
- data/lib/shaf/profile/relation.rb +29 -0
- data/lib/shaf/profile/unique_id.rb +58 -0
- data/lib/shaf/profiles.rb +42 -0
- data/lib/shaf/profiles/shaf_basic.rb +20 -0
- data/lib/shaf/profiles/shaf_error.rb +48 -0
- data/lib/shaf/profiles/shaf_form.rb +109 -0
- data/lib/shaf/responder.rb +41 -2
- data/lib/shaf/responder/alps_json.rb +25 -0
- data/lib/shaf/responder/base.rb +20 -17
- data/lib/shaf/responder/hal.rb +65 -4
- data/lib/shaf/responder/html.rb +43 -16
- data/lib/shaf/responder/problem_json.rb +1 -1
- data/lib/shaf/serializer.rb +31 -0
- data/lib/shaf/settings.rb +25 -12
- data/lib/shaf/spec.rb +1 -0
- data/lib/shaf/spec/authenticator.rb +13 -0
- data/lib/shaf/spec/base.rb +1 -1
- data/lib/shaf/spec/http_method_utils.rb +1 -1
- data/lib/shaf/spec/integration_spec.rb +25 -13
- data/lib/shaf/spec/payload_utils.rb +2 -2
- data/lib/shaf/supported_http_methods.rb +15 -0
- data/lib/shaf/tasks/api_doc_task.rb +24 -3
- data/lib/shaf/tasks/routes_task.rb +14 -17
- data/lib/shaf/upgrade/manifest.rb +11 -2
- data/lib/shaf/upgrade/package.rb +73 -43
- data/lib/shaf/upgrade/version.rb +11 -10
- data/lib/shaf/utils.rb +19 -5
- data/lib/shaf/version.rb +3 -1
- data/lib/shaf/yard.rb +34 -0
- data/lib/shaf/yard/attribute_method_handler.rb +19 -0
- data/lib/shaf/yard/attribute_object.rb +30 -0
- data/lib/shaf/yard/base_method_handler.rb +30 -0
- data/lib/shaf/yard/link_method_handler.rb +39 -0
- data/lib/shaf/yard/link_object.rb +60 -0
- data/lib/shaf/yard/nested_attributes.rb +37 -0
- data/lib/shaf/yard/parser.rb +64 -0
- data/lib/shaf/yard/profile_method_handler.rb +51 -0
- data/lib/shaf/yard/profile_object.rb +21 -0
- data/lib/shaf/yard/resource_object.rb +55 -0
- data/lib/shaf/yard/serializer_handler.rb +27 -0
- data/templates/api/controllers/base_controller.rb +0 -10
- data/templates/api/controllers/docs_controller.rb +5 -3
- data/templates/api/controllers/root_controller.rb +7 -1
- data/templates/api/policies/base_policy.rb +2 -0
- data/templates/api/serializers/base_serializer.rb +1 -3
- data/templates/api/serializers/error_serializer.rb +1 -5
- data/templates/api/serializers/form_serializer.rb +1 -5
- data/templates/api/serializers/validation_error_serializer.rb +1 -5
- data/templates/config/bootstrap.rb +1 -2
- data/templates/config/directories.rb +52 -44
- data/templates/config/helpers.rb +1 -1
- data/templates/config/initializers.rb +52 -8
- data/templates/config/initializers/authentication.rb +18 -0
- data/templates/config/initializers/db_migrations.rb +2 -2
- data/templates/config/initializers/logging.rb +2 -2
- data/templates/spec/spec_helper.rb +2 -0
- data/upgrades/0.5.0.tar.gz +0 -0
- data/upgrades/1.0.4.tar.gz +0 -0
- data/upgrades/1.1.0.tar.gz +0 -0
- data/upgrades/2.0.0.tar.gz +0 -0
- data/yard_templates/api_doc/doc_index/html/body.erb +3 -0
- data/yard_templates/api_doc/doc_index/setup.rb +8 -0
- data/yard_templates/api_doc/html/css/api-doc.css +222 -0
- data/yard_templates/api_doc/html/favicon.ico +0 -0
- data/yard_templates/api_doc/html/js/switch_tab.js +17 -0
- data/yard_templates/api_doc/html/setup.rb +59 -0
- data/yard_templates/api_doc/layout/html/footer.erb +3 -0
- data/yard_templates/api_doc/layout/html/header.erb +7 -0
- data/yard_templates/api_doc/layout/html/layout.erb +13 -0
- data/yard_templates/api_doc/layout/setup.rb +24 -0
- data/yard_templates/api_doc/profile/html/attributes.erb +10 -0
- data/yard_templates/api_doc/profile/html/profile.erb +6 -0
- data/yard_templates/api_doc/profile/html/relations.erb +10 -0
- data/yard_templates/api_doc/profile/setup.rb +38 -0
- data/yard_templates/api_doc/profile_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/profile_attribute/setup.rb +21 -0
- data/yard_templates/api_doc/profile_relation/html/relation.erb +37 -0
- data/yard_templates/api_doc/profile_relation/setup.rb +41 -0
- data/yard_templates/api_doc/resource/html/attributes.erb +10 -0
- data/yard_templates/api_doc/resource/html/profile.erb +14 -0
- data/yard_templates/api_doc/resource/html/relations.erb +10 -0
- data/yard_templates/api_doc/resource/html/resource.erb +5 -0
- data/yard_templates/api_doc/resource/setup.rb +56 -0
- data/yard_templates/api_doc/resource_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/resource_attribute/setup.rb +20 -0
- data/yard_templates/api_doc/resource_relation/html/relation.erb +47 -0
- data/yard_templates/api_doc/resource_relation/setup.rb +80 -0
- data/yard_templates/api_doc/setup.rb +31 -0
- data/yard_templates/api_doc/sidebar/html/profile_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/search.erb +7 -0
- data/yard_templates/api_doc/sidebar/html/serializer_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/sidebar.erb +13 -0
- data/yard_templates/api_doc/sidebar/setup.rb +56 -0
- metadata +137 -30
- metadata.gz.sig +1 -3
- data/lib/shaf/extensions/current_user.rb +0 -48
- data/lib/shaf/responder/hal_serializable.rb +0 -64
@@ -0,0 +1,16 @@
|
|
1
|
+
module Profiles
|
2
|
+
class <%= profile_class_name %> < Shaf::Profile
|
3
|
+
name '<%= profile_name %>'
|
4
|
+
|
5
|
+
doc 'FIXME: replace this with a description of the <%= profile_name %> profile'
|
6
|
+
|
7
|
+
use :delete, from: Shaf::Profiles::ShafBasic
|
8
|
+
|
9
|
+
<% attributes.each do |name, type| %>
|
10
|
+
attribute :<%= name %>,
|
11
|
+
type: '<%= type %>',
|
12
|
+
doc: 'FIXME: Write documentation for <%= name %>'
|
13
|
+
|
14
|
+
<% end %>
|
15
|
+
end
|
16
|
+
end
|
@@ -6,9 +6,9 @@ class <%= class_name %> < BaseSerializer
|
|
6
6
|
model <%= model_class_name %>
|
7
7
|
policy <%= policy_class_name %>
|
8
8
|
|
9
|
-
<%=
|
9
|
+
<%= print(profile_with_doc) %>
|
10
10
|
|
11
|
-
<%= print_nested(
|
11
|
+
<%= print_nested(attributes_with_doc) %>
|
12
12
|
|
13
13
|
<%= print_nested(links_with_doc) %>
|
14
14
|
|
@@ -14,8 +14,7 @@ describe <%= model_class_name %>, type: :integration do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it "lists all <%= plural_name %>" do
|
17
|
-
<%= model_class_name %>.create
|
18
|
-
<%= model_class_name %>.create
|
17
|
+
2.times { <%= model_class_name %>.create }
|
19
18
|
|
20
19
|
get <%= plural_name %>_uri
|
21
20
|
_(status).must_equal 200
|
@@ -15,8 +15,8 @@ describe <%= class_name %> do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "serializes attributes" do
|
18
|
-
<%
|
19
|
-
_(attributes.keys).must_include(
|
18
|
+
<% attribute_names.each do |attr| -%>
|
19
|
+
_(attributes.keys).must_include(:<%= attr %>)
|
20
20
|
<% end -%>
|
21
21
|
end
|
22
22
|
|
@@ -33,13 +33,13 @@ describe <%= class_name %> do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it "serializes attributes" do
|
36
|
-
<%
|
37
|
-
_(attributes.keys).must_include(
|
36
|
+
<% attribute_names.each do |attr| -%>
|
37
|
+
_(attributes.keys).must_include(:<%= attr %>)
|
38
38
|
<% end -%>
|
39
39
|
end
|
40
40
|
|
41
41
|
it "serializes links" do
|
42
|
-
<%
|
42
|
+
<% link_relations.each do |rel| -%>
|
43
43
|
_(link_rels).must_include(:'<%= rel %>')
|
44
44
|
<% end -%>
|
45
45
|
end
|
data/lib/shaf/helpers.rb
CHANGED
@@ -3,6 +3,8 @@ require 'shaf/helpers/json_html'
|
|
3
3
|
require 'shaf/helpers/paginate'
|
4
4
|
require 'shaf/helpers/payload'
|
5
5
|
require 'shaf/helpers/http_header'
|
6
|
+
require 'shaf/helpers/authentication'
|
7
|
+
require 'shaf/helpers/vary'
|
6
8
|
|
7
9
|
module Shaf
|
8
10
|
def self.helpers
|
@@ -12,6 +14,8 @@ module Shaf
|
|
12
14
|
Paginate,
|
13
15
|
Payload,
|
14
16
|
HttpHeader,
|
17
|
+
Authentication,
|
18
|
+
Vary,
|
15
19
|
]
|
16
20
|
end
|
17
21
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'shaf/authenticator'
|
5
|
+
require 'shaf/errors'
|
6
|
+
|
7
|
+
module Shaf
|
8
|
+
module Authentication
|
9
|
+
class NoChallengesError < Error
|
10
|
+
def initialize(realm)
|
11
|
+
super("No Authentication challenges for realm: #{realm.inspect}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class RealmChangedError < Error
|
16
|
+
def initialize(from:, to:)
|
17
|
+
super <<~ERR
|
18
|
+
Realm was changed from "#{from}" to "#{to}". This is not allowed!
|
19
|
+
Each request corresponds to a certain realm and cannot be changed.
|
20
|
+
This is probably caused by a call to `current_user` using the
|
21
|
+
default realm (from `Shaf::Settings.default_authentication_realm`)
|
22
|
+
and then using `#authenticate realm: 'some_other_realm'
|
23
|
+
ERR
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def www_authenticate(realm: Settings.default_authentication_realm)
|
28
|
+
challenges = Authenticator.challenges_for(realm: realm)
|
29
|
+
raise NoChallengesError.new(realm) if challenges.empty?
|
30
|
+
|
31
|
+
headers 'WWW-Authenticate' => challenges.map(&:to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def authenticate(realm: Settings.default_authentication_realm)
|
35
|
+
if defined?(@current_realm) && @current_realm&.to_s != realm&.to_s
|
36
|
+
raise RealmChangedError.new(from: @current_realm , to: realm)
|
37
|
+
else
|
38
|
+
@current_realm = realm
|
39
|
+
end
|
40
|
+
|
41
|
+
current_user.tap do |user|
|
42
|
+
www_authenticate(realm: realm) unless user
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def authenticate!(realm: Settings.default_authentication_realm)
|
47
|
+
user = authenticate(realm: realm)
|
48
|
+
return user if user
|
49
|
+
|
50
|
+
msg = +"Unauthorized action"
|
51
|
+
msg << " (Realm: #{realm})" if realm
|
52
|
+
raise Shaf::Errors::UnauthorizedError, msg
|
53
|
+
end
|
54
|
+
alias current_user! authenticate!
|
55
|
+
|
56
|
+
def authenticated?
|
57
|
+
!current_user.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_user
|
61
|
+
unless defined? @current_realm
|
62
|
+
if Settings.key? :default_authentication_realm
|
63
|
+
@current_realm = Settings.default_authentication_realm
|
64
|
+
else
|
65
|
+
Shaf.logger.info <<~MSG
|
66
|
+
No realm has been provided!
|
67
|
+
Authentication/authorization cannot be performed. Did you perhaps
|
68
|
+
forget to configure a realm in
|
69
|
+
`Settings.default_authentication_realm` or provide it when calling
|
70
|
+
`#authenticate!` (or `#authenticate!`)
|
71
|
+
MSG
|
72
|
+
return
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
@current_user ||= Authenticator.user(request.env, realm: @current_realm)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/shaf/helpers/payload.rb
CHANGED
@@ -2,50 +2,32 @@
|
|
2
2
|
|
3
3
|
require 'set'
|
4
4
|
require 'shaf/responder'
|
5
|
+
require 'shaf/parser'
|
6
|
+
require 'shaf/errors'
|
5
7
|
|
6
8
|
module Shaf
|
7
9
|
module Payload
|
8
|
-
EXCLUDED_FORM_PARAMS = ['captures', 'splat'].freeze
|
9
10
|
NO_VALUE = Object.new.freeze
|
10
11
|
|
11
12
|
private
|
12
13
|
|
13
14
|
def payload
|
14
|
-
@payload
|
15
|
-
|
16
|
-
|
17
|
-
def read_input
|
18
|
-
request.body.rewind
|
19
|
-
request.body.read
|
20
|
-
ensure
|
21
|
-
request.body.rewind
|
15
|
+
return @payload if defined? @payload
|
16
|
+
@payload = parse_payload
|
22
17
|
end
|
23
18
|
|
24
19
|
def parse_payload
|
25
|
-
|
26
|
-
return params.reject { |key, _| EXCLUDED_FORM_PARAMS.include? key }
|
27
|
-
end
|
20
|
+
return unless Parser.input? request
|
28
21
|
|
29
|
-
|
30
|
-
|
22
|
+
parser = Parser.for(request)
|
23
|
+
raise Errors::UnsupportedMediaTypeError.new(request: request) unless parser
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
rescue Errors::UnsupportedMediaTypeError
|
36
|
-
raise
|
37
|
-
rescue StandardError => e
|
25
|
+
log.debug "Parsing input using: #{parser.class}"
|
26
|
+
parser.call
|
27
|
+
rescue Parser::Error => e
|
38
28
|
raise Errors::BadRequestError, "Failed to parse input payload: #{e.message}"
|
39
29
|
end
|
40
30
|
|
41
|
-
def suported_media_type?
|
42
|
-
request.env['CONTENT_TYPE'].match? %r{\Aapplication/(hal\+)?json}
|
43
|
-
end
|
44
|
-
|
45
|
-
def raise_unsupported_media_type_error(request)
|
46
|
-
raise Errors::UnsupportedMediaTypeError.new(request: request)
|
47
|
-
end
|
48
|
-
|
49
31
|
def safe_params(*fields)
|
50
32
|
return {} unless payload
|
51
33
|
|
@@ -58,10 +40,6 @@ module Shaf
|
|
58
40
|
end
|
59
41
|
end
|
60
42
|
|
61
|
-
def ignore_form_input?(name)
|
62
|
-
name == '_method'
|
63
|
-
end
|
64
|
-
|
65
43
|
def profile(value = NO_VALUE)
|
66
44
|
return @profile if value == NO_VALUE
|
67
45
|
@profile = value
|
@@ -86,15 +64,17 @@ module Shaf
|
|
86
64
|
kwargs[:profile] ||= profile
|
87
65
|
|
88
66
|
log.info "#{request.request_method} #{request.path_info} => #{status}"
|
89
|
-
|
67
|
+
responder = Responder.for(request, resource)
|
68
|
+
payload = responder.call(self, resource, preload: preload, **kwargs)
|
90
69
|
add_cache_headers(payload, kwargs)
|
91
|
-
|
92
70
|
payload
|
93
71
|
rescue StandardError => err
|
94
72
|
log.error "Failure: #{err.message}\n#{err.backtrace}"
|
95
73
|
if status == 500
|
96
74
|
content_type mime_type(:json)
|
97
75
|
body JSON.generate(failure: err.message)
|
76
|
+
elsif err.is_a? Errors::ServerError
|
77
|
+
respond_with(err)
|
98
78
|
else
|
99
79
|
respond_with(Errors::ServerError.new(err.message))
|
100
80
|
end
|
data/lib/shaf/logger.rb
ADDED
data/lib/shaf/parser.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Shaf
|
6
|
+
module Parser
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
INPUT_BODY = 'shaf.input_body'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def register(parser)
|
13
|
+
parsers << parser
|
14
|
+
end
|
15
|
+
|
16
|
+
def unregister(parser)
|
17
|
+
parsers.delete(parser)
|
18
|
+
end
|
19
|
+
|
20
|
+
def input?(request)
|
21
|
+
!!input(request)
|
22
|
+
end
|
23
|
+
|
24
|
+
def for(request)
|
25
|
+
clazz = parser_for(request)
|
26
|
+
return unless clazz
|
27
|
+
|
28
|
+
body = input(request)
|
29
|
+
clazz.new(request: request, body: body)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parser_for(request)
|
35
|
+
parsers.find do |parser|
|
36
|
+
parser.can_handle? request
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parsers
|
41
|
+
@parsers ||= Set.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def input(request)
|
45
|
+
body = request.get_header(INPUT_BODY)
|
46
|
+
body ||= read_input(request).tap do |b|
|
47
|
+
request.set_header(INPUT_BODY, b)
|
48
|
+
end
|
49
|
+
|
50
|
+
body unless String(body).strip.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def read_input(request)
|
54
|
+
request.body.rewind
|
55
|
+
request.body.read
|
56
|
+
ensure
|
57
|
+
request.body.rewind
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'shaf/parser/base'
|
64
|
+
require 'shaf/parser/json'
|
65
|
+
require 'shaf/parser/form_data'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Parser
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
def inherited(child)
|
6
|
+
Parser.register(child)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type(type = nil, value = nil)
|
11
|
+
if type
|
12
|
+
@mime_type = type
|
13
|
+
@mime_type = Sinatra::Base.mime_type(type, value) if type.is_a? Symbol
|
14
|
+
end
|
15
|
+
|
16
|
+
@mime_type if defined? @mime_type
|
17
|
+
end
|
18
|
+
|
19
|
+
def can_handle?(request)
|
20
|
+
mime_type == request.content_type
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :request, :body
|
26
|
+
|
27
|
+
def initialize(request:, body:)
|
28
|
+
@request = request
|
29
|
+
@body = body
|
30
|
+
end
|
31
|
+
|
32
|
+
def call
|
33
|
+
raise NotImplementedError, "#{self.class} must implement #call"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def mime_type
|
39
|
+
self.class.mime_type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Parser
|
3
|
+
class FormData < Base
|
4
|
+
def self.can_handle?(request)
|
5
|
+
request.form_data? || request.parseable_data?
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
request.POST.tap do |data| # Returns form params from Rack::Request
|
10
|
+
data.delete '_method' # If the method override hack is used remove the _method key
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Parser
|
3
|
+
class Json < Base
|
4
|
+
|
5
|
+
mime_type :json, 'application/json'
|
6
|
+
|
7
|
+
def self.can_handle?(request)
|
8
|
+
request.content_type&.match? %r{application/(.*\+)?json}
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
@payload ||= parse_json
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parse_json
|
18
|
+
return {} if body.empty?
|
19
|
+
|
20
|
+
JSON.parse(body, symbolize_names: true)
|
21
|
+
rescue JSON::ParserError => e
|
22
|
+
raise Error, e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|