shaf 1.5.2 → 1.6.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 +2 -1
- data/lib/shaf/helpers/json_html.rb +58 -18
- data/lib/shaf/helpers/payload.rb +13 -8
- data/lib/shaf/responder/hal.rb +1 -7
- data/lib/shaf/responder/hal_serializable.rb +13 -3
- data/lib/shaf/responder/html.rb +35 -6
- data/lib/shaf/version.rb +1 -1
- data/templates/frontend/assets/css/main.css +33 -1
- data/templates/frontend/views/headers.erb +20 -0
- data/templates/frontend/views/layout.erb +7 -1
- data/templates/frontend/views/payload.erb +1 -0
- data/upgrades/1.6.0.tar.gz +0 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd5e02da6f878d5f9e0ef2e4d3adb641e9ea34362c943cff5a9e94d93ffaad26
|
4
|
+
data.tar.gz: 565dd3872f4b46a48955b94ebbb6324472ec6293bf1f7ead551a55fdc5bd4d4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 129bf19d6c7ff6ff0f1df840078fbba5df8e47045e69a5b3a3e40ebdc2680f46ad36dc3c4660f8dd8ba5187368fa3b0ecdb503b0575def9f1403ed0d6e743935
|
7
|
+
data.tar.gz: c4fa82f52be8f910f40cebe13b0c5df52239a780788d7d083bc6f969cbfb02c9d72a4aa1fa1e2dce6cc23013be92e9d0eaacc849beaf9e05decacc34ccb9ed3d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
�u�
|
2
|
+
�� %5,2kc����S�Py�S��I�����
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Shaf
|
2
4
|
module JsonHtml
|
5
|
+
STRUCTURAL_PATTERN = /^[\[\]\{\}:,]$/.freeze
|
3
6
|
|
4
7
|
def json2html(json)
|
5
8
|
as_html JSON.parse(json)
|
@@ -18,7 +21,7 @@ module Shaf
|
|
18
21
|
when Hash
|
19
22
|
html_hash(obj, indent, pre_indent)
|
20
23
|
else
|
21
|
-
html_scalar(obj,
|
24
|
+
html_scalar(obj, pre_indent)
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
@@ -28,41 +31,78 @@ module Shaf
|
|
28
31
|
end
|
29
32
|
|
30
33
|
<<~EOS.chomp
|
31
|
-
#{pre_indent}
|
32
|
-
#{array_of_strings.join(
|
33
|
-
#{indentation(indent)}
|
34
|
+
#{pre_indent}#{span '['}
|
35
|
+
#{array_of_strings.join(item_separator)}
|
36
|
+
#{indentation(indent)}#{span ']'}
|
34
37
|
EOS
|
35
38
|
end
|
36
39
|
|
37
40
|
def html_hash(h, indent, pre_indent)
|
38
41
|
<<~EOS.chomp
|
39
|
-
#{pre_indent}
|
40
|
-
#{h.map { |k,v| sub_hash(k,v, indent + 1) }.join(
|
41
|
-
#{indentation(indent)}
|
42
|
+
#{pre_indent}#{span '{'}
|
43
|
+
#{h.map { |k,v| sub_hash(k,v, indent + 1) }.join(item_separator)}#{item_separator}
|
44
|
+
#{indentation(indent)}#{span '}'}
|
42
45
|
EOS
|
43
46
|
end
|
44
47
|
|
45
|
-
def html_scalar(s,
|
46
|
-
|
47
|
-
format "%s%s%s%s", pre_indent, q, s, q
|
48
|
+
def html_scalar(s, pre_indent)
|
49
|
+
format '%s%s', pre_indent, span(s)
|
48
50
|
end
|
49
51
|
|
50
52
|
def sub_hash(key, value, indent)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
left_side = format '%s%s%s ', indentation(indent), quoted(key), span(':')
|
54
|
+
left_side +
|
55
|
+
if key.to_s == 'href'
|
56
|
+
link(value)
|
57
|
+
else
|
58
|
+
to_html(value, indent: indent)
|
59
|
+
end
|
56
60
|
end
|
57
61
|
|
58
62
|
def indentation(i)
|
59
63
|
" " * i
|
60
64
|
end
|
61
65
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
66
|
+
def quoted(obj)
|
67
|
+
case obj
|
68
|
+
when STRUCTURAL_PATTERN
|
69
|
+
obj
|
70
|
+
when String, Symbol
|
71
|
+
format '"%s"', obj
|
72
|
+
when NilClass
|
73
|
+
'null'
|
74
|
+
else
|
75
|
+
obj
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def link(href)
|
80
|
+
format '<a href="%s">%s</a>', href, quoted(href)
|
65
81
|
end
|
66
82
|
|
83
|
+
def span(value)
|
84
|
+
clazz = span_class(value)
|
85
|
+
value = quoted(value)
|
86
|
+
format '<span class="%s">%s</span>', clazz, value
|
87
|
+
end
|
88
|
+
|
89
|
+
def span_class(obj)
|
90
|
+
case obj
|
91
|
+
when TrueClass, FalseClass
|
92
|
+
'boolean'
|
93
|
+
when NilClass
|
94
|
+
'null'
|
95
|
+
when Numeric
|
96
|
+
'number'
|
97
|
+
when STRUCTURAL_PATTERN
|
98
|
+
'structural'
|
99
|
+
else
|
100
|
+
'string'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def item_separator
|
105
|
+
"#{span ','}\n"
|
106
|
+
end
|
67
107
|
end
|
68
108
|
end
|
data/lib/shaf/helpers/payload.rb
CHANGED
@@ -6,6 +6,7 @@ require 'shaf/responder'
|
|
6
6
|
module Shaf
|
7
7
|
module Payload
|
8
8
|
EXCLUDED_FORM_PARAMS = ['captures', 'splat'].freeze
|
9
|
+
NO_VALUE = Object.new.freeze
|
9
10
|
|
10
11
|
private
|
11
12
|
|
@@ -61,8 +62,8 @@ module Shaf
|
|
61
62
|
name == '_method'
|
62
63
|
end
|
63
64
|
|
64
|
-
def profile(value =
|
65
|
-
return @profile
|
65
|
+
def profile(value = NO_VALUE)
|
66
|
+
return @profile if value == NO_VALUE
|
66
67
|
@profile = value
|
67
68
|
end
|
68
69
|
|
@@ -81,15 +82,13 @@ module Shaf
|
|
81
82
|
status ||= resource.respond_to?(:http_status) ? resource.http_status : 200
|
82
83
|
status(status)
|
83
84
|
|
84
|
-
kwargs.merge!(
|
85
|
-
|
86
|
-
serializer: serializer,
|
87
|
-
collection: collection
|
88
|
-
)
|
85
|
+
kwargs.merge!(serializer: serializer, collection: collection)
|
86
|
+
kwargs[:profile] ||= profile
|
89
87
|
|
90
88
|
log.info "#{request.request_method} #{request.path_info} => #{status}"
|
91
89
|
payload = Responder.for(request, resource).call(self, resource, preload: preload, **kwargs)
|
92
90
|
add_cache_headers(payload, kwargs)
|
91
|
+
|
93
92
|
payload
|
94
93
|
rescue StandardError => err
|
95
94
|
log.error "Failure: #{err.message}\n#{err.backtrace}"
|
@@ -103,10 +102,16 @@ module Shaf
|
|
103
102
|
|
104
103
|
def add_cache_headers(payload, kwargs)
|
105
104
|
return unless kwargs.delete(:http_cache) { Settings.http_cache }
|
105
|
+
|
106
|
+
chksum, kind = etag_for(payload)
|
107
|
+
etag(chksum, kind: kind) if chksum
|
108
|
+
end
|
109
|
+
|
110
|
+
def etag_for(payload)
|
106
111
|
return if payload.nil? || payload.empty?
|
107
112
|
|
108
113
|
sha1 = Digest::SHA1.hexdigest payload
|
109
|
-
|
114
|
+
[sha1, :weak] # Weak or Strong??
|
110
115
|
end
|
111
116
|
end
|
112
117
|
end
|
data/lib/shaf/responder/hal.rb
CHANGED
@@ -9,7 +9,7 @@ module Shaf
|
|
9
9
|
mime_type :hal, 'application/hal+json'
|
10
10
|
|
11
11
|
def body
|
12
|
-
@body ||=
|
12
|
+
@body ||= generate_json
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
@@ -19,12 +19,6 @@ module Shaf
|
|
19
19
|
type = "#{type};profile=#{profile}" if profile
|
20
20
|
type
|
21
21
|
end
|
22
|
-
|
23
|
-
def profile
|
24
|
-
return unless serializer
|
25
|
-
|
26
|
-
@profile ||= options[:profile] || serializer.semantic_profile
|
27
|
-
end
|
28
22
|
end
|
29
23
|
end
|
30
24
|
end
|
@@ -6,9 +6,7 @@ module Shaf
|
|
6
6
|
module HalSerializable
|
7
7
|
def lookup_rel(rel, response)
|
8
8
|
hal = response.serialized_hash
|
9
|
-
|
10
|
-
|
11
|
-
links = hal.dig(:_links, rel.to_sym)
|
9
|
+
links = hal&.dig(:_links, rel.to_sym)
|
12
10
|
return [] unless links
|
13
11
|
|
14
12
|
links = [links] unless links.is_a? Array
|
@@ -49,6 +47,18 @@ module Shaf
|
|
49
47
|
|
50
48
|
@serialized_hash
|
51
49
|
end
|
50
|
+
|
51
|
+
def profile
|
52
|
+
@profile ||= options[:profile]
|
53
|
+
return unless @profile || serializer
|
54
|
+
|
55
|
+
@profile ||= serializer.semantic_profile
|
56
|
+
end
|
57
|
+
|
58
|
+
def generate_json
|
59
|
+
# FIXME: change to Oj??
|
60
|
+
JSON.generate(serialized_hash)
|
61
|
+
end
|
52
62
|
end
|
53
63
|
end
|
54
64
|
end
|
data/lib/shaf/responder/html.rb
CHANGED
@@ -8,12 +8,41 @@ module Shaf
|
|
8
8
|
mime_type :html
|
9
9
|
|
10
10
|
def body
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
locals = variables
|
12
|
+
|
13
|
+
template =
|
14
|
+
case resource
|
15
|
+
when Formable::Form
|
16
|
+
locals.merge!(form: resource)
|
17
|
+
:form
|
18
|
+
else
|
19
|
+
:payload
|
20
|
+
end
|
21
|
+
|
22
|
+
controller.erb(template, locals: locals)
|
23
|
+
end
|
24
|
+
|
25
|
+
def variables
|
26
|
+
{
|
27
|
+
request_headers: request_headers,
|
28
|
+
response_headers: response_headers,
|
29
|
+
serialized: serialized_hash
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def request_headers
|
34
|
+
controller.request_headers
|
35
|
+
end
|
36
|
+
|
37
|
+
def response_headers
|
38
|
+
etag, kind = controller.send(:etag_for, generate_json)
|
39
|
+
prefix = kind == :weak ? 'W/' : ''
|
40
|
+
etag = %Q{#{prefix}"#{etag}"}
|
41
|
+
|
42
|
+
type = Hal.mime_type
|
43
|
+
type = "#{type};profile=#{profile}" if profile
|
44
|
+
|
45
|
+
controller.headers.merge('Content-Type' => type, 'ETag' => etag)
|
17
46
|
end
|
18
47
|
end
|
19
48
|
end
|
data/lib/shaf/version.rb
CHANGED
@@ -16,10 +16,18 @@ ul {
|
|
16
16
|
list-style-type: none;
|
17
17
|
}
|
18
18
|
|
19
|
+
td {
|
20
|
+
min-width: 20em;
|
21
|
+
}
|
22
|
+
|
19
23
|
pre {
|
20
24
|
margin: 0;
|
21
25
|
}
|
22
26
|
|
27
|
+
main {
|
28
|
+
padding-bottom: 8em;
|
29
|
+
}
|
30
|
+
|
23
31
|
.container {
|
24
32
|
display: flex;
|
25
33
|
flex-direction: row;
|
@@ -33,6 +41,10 @@ pre {
|
|
33
41
|
height: 100%;
|
34
42
|
}
|
35
43
|
|
44
|
+
.headers {
|
45
|
+
margin-bottom: 2em;
|
46
|
+
}
|
47
|
+
|
36
48
|
.hal-form {
|
37
49
|
height: auto;
|
38
50
|
display: flex;
|
@@ -60,11 +72,31 @@ pre {
|
|
60
72
|
min-height: 100%;
|
61
73
|
height: auto;
|
62
74
|
overflow: auto;
|
63
|
-
background-color: #
|
75
|
+
background-color: #e0e0e0;
|
64
76
|
display: flex;
|
65
77
|
flex-direction: row;
|
66
78
|
justify-content: flex-start;
|
67
79
|
align-items: flex-start;
|
68
80
|
padding: 1em;
|
81
|
+
border-radius: 20px;
|
82
|
+
}
|
83
|
+
|
84
|
+
span.boolean {
|
85
|
+
color: #0277BD;
|
69
86
|
}
|
70
87
|
|
88
|
+
span.null {
|
89
|
+
color: #757575;
|
90
|
+
}
|
91
|
+
|
92
|
+
span.number {
|
93
|
+
color: #3F51B5;
|
94
|
+
}
|
95
|
+
|
96
|
+
span.structural {
|
97
|
+
color: black;
|
98
|
+
}
|
99
|
+
|
100
|
+
span.string {
|
101
|
+
color: #43A047;
|
102
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<div class="container">
|
2
|
+
<section class="section headers">
|
3
|
+
<h3><%= title %></h3>
|
4
|
+
<table class="hal-payload">
|
5
|
+
<% headers.each do |key, value| %>
|
6
|
+
<tr>
|
7
|
+
<td><%= key %></td>
|
8
|
+
<td><%= value %></td>
|
9
|
+
</tr>
|
10
|
+
<% end %>
|
11
|
+
</table>
|
12
|
+
<ul>
|
13
|
+
<% notes.each do |note| %>
|
14
|
+
<li>
|
15
|
+
<p><sub><%= note %></sub></p>
|
16
|
+
</li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
</section>
|
20
|
+
</div>
|
@@ -6,6 +6,12 @@
|
|
6
6
|
<link rel="stylesheet" type="text/css" href="/css/main.css">
|
7
7
|
</head>
|
8
8
|
<body>
|
9
|
-
|
9
|
+
<div class="main-container">
|
10
|
+
<main>
|
11
|
+
<%= yield %>
|
12
|
+
</main>
|
13
|
+
<%= erb :headers, locals: {title: 'Request headers', headers: request_headers, notes: []} %>
|
14
|
+
<%= erb :headers, locals: {title: 'Response headers', headers: response_headers, notes: ["This is an example of a response to a request with Accept: #{response_headers['Content-Type']}. YMMV"]} %>
|
15
|
+
</div>
|
10
16
|
</body>
|
11
17
|
</html>
|
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shaf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sammy Henningsson
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
ZMhjYR7sRczGJx+GxGU2EaR0bjRsPVlC4ywtFxoOfRG3WaJcpWGEoAoMJX6Z0bRv
|
31
31
|
M40=
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2020-
|
33
|
+
date: 2020-04-21 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: minitest
|
@@ -328,6 +328,7 @@ files:
|
|
328
328
|
- templates/config/settings.yml
|
329
329
|
- templates/frontend/assets/css/main.css
|
330
330
|
- templates/frontend/views/form.erb
|
331
|
+
- templates/frontend/views/headers.erb
|
331
332
|
- templates/frontend/views/layout.erb
|
332
333
|
- templates/frontend/views/payload.erb
|
333
334
|
- templates/spec/integration/root_spec.rb
|
@@ -345,6 +346,7 @@ files:
|
|
345
346
|
- upgrades/1.3.0.tar.gz
|
346
347
|
- upgrades/1.4.0.tar.gz
|
347
348
|
- upgrades/1.5.0.tar.gz
|
349
|
+
- upgrades/1.6.0.tar.gz
|
348
350
|
homepage:
|
349
351
|
licenses:
|
350
352
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|