shaf 1.5.2 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '028cf2f2137e5f2e08115766a8e7b53e4213005fbc6677a15aa6d86dbe430f80'
4
- data.tar.gz: 61b109326927ecb8a74811fd418b3bd8b2124a96820786be90dbcc9d43d578bf
3
+ metadata.gz: bd5e02da6f878d5f9e0ef2e4d3adb641e9ea34362c943cff5a9e94d93ffaad26
4
+ data.tar.gz: 565dd3872f4b46a48955b94ebbb6324472ec6293bf1f7ead551a55fdc5bd4d4a
5
5
  SHA512:
6
- metadata.gz: 5da2756d178e7521539c8cc43a8661671434f1ccab7b0c8881905643155cf72eef5569e2d82d7a7c8a89b33e82607f106192c2a882c0c55bd6fab00dd2910ab6
7
- data.tar.gz: b9859646533cf4b293e2f6a715f0309619b7ed280ba312cb0cdd6207b9b29e007ae0f5a5629575f929fe7f08ab47a94ad8a063ef0749b70d668102718c5abe71
6
+ metadata.gz: 129bf19d6c7ff6ff0f1df840078fbba5df8e47045e69a5b3a3e40ebdc2680f46ad36dc3c4660f8dd8ba5187368fa3b0ecdb503b0575def9f1403ed0d6e743935
7
+ data.tar.gz: c4fa82f52be8f910f40cebe13b0c5df52239a780788d7d083bc6f969cbfb02c9d72a4aa1fa1e2dce6cc23013be92e9d0eaacc849beaf9e05decacc34ccb9ed3d
Binary file
data.tar.gz.sig CHANGED
@@ -1 +1,2 @@
1
- ��������Er� ¬ѫ�麲!�X\���( o�Oۏ�u:}$lt�K��A�k�!s�e�*31�4��4��>g֣mڪ�0i}d��C|��2I��v Z(�B-�:Y�?���|~�<����u�� $�}��l=��E�T����u �����D �*D�31Tpy~�t"6"���Z�O~L%V!X�c4P>ڧ7�Uz�_��>T���_t&O"���)(I�c���:I��Tv}��Ԗ��{0 �
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, indent, pre_indent)
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}<span>[</span>
32
- #{array_of_strings.join(",\n")}
33
- #{indentation(indent)}<span>]</span>
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}<span>{</span>
40
- #{h.map { |k,v| sub_hash(k,v, indent + 1) }.join(",\n")},
41
- #{indentation(indent)}<span>}</span>
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, indent, pre_indent)
46
- q = around(s)
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
- if key == 'href'
52
- %Q(#{indentation(indent)}"#{key}"<span>:</span> <a href="#{value}">#{value}</a>)
53
- else
54
- "#{indentation(indent)}\"#{key}\"<span>:</span> #{to_html(value, indent: indent) }"
55
- end
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 around(obj)
63
- return '"' if obj.is_a? String
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
@@ -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 = nil)
65
- return @profile unless value
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
- profile: profile,
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
- etag sha1, :weak # Weak or Strong??
114
+ [sha1, :weak] # Weak or Strong??
110
115
  end
111
116
  end
112
117
  end
@@ -9,7 +9,7 @@ module Shaf
9
9
  mime_type :hal, 'application/hal+json'
10
10
 
11
11
  def body
12
- @body ||= JSON.generate(serialized_hash)
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
- return [] unless hal
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
@@ -8,12 +8,41 @@ module Shaf
8
8
  mime_type :html
9
9
 
10
10
  def body
11
- case resource
12
- when Formable::Form
13
- controller.erb(:form, locals: {form: resource, serialized: serialized_hash})
14
- else
15
- controller.erb(:payload, locals: {serialized: serialized_hash})
16
- end
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
@@ -1,3 +1,3 @@
1
1
  module Shaf
2
- VERSION = "1.5.2"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -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: #b5aeae;
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
- <%= yield %>
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>
@@ -1,6 +1,7 @@
1
1
  <% @page_title = "HAL payload" %>
2
2
  <div class="container">
3
3
  <section class="section">
4
+ <h3>Response</h3>
4
5
  <div class="hal-payload">
5
6
  <%= as_html(serialized) %>
6
7
  </div>
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.5.2
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-02-29 00:00:00.000000000 Z
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