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 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