hanami-papercraft 0.1
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 +7 -0
- data/CHANGELOG.md +0 -0
- data/README.md +114 -0
- data/lib/hanami/papercraft_view.rb +157 -0
- data/lib/hanami/papercraft_view_version.rb +5 -0
- data/papercraft.png +0 -0
- metadata +93 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8d00da72648def7ecd464737f0f4a3ebfd7090e9d615017d6b35bcda49d3e3d0
|
|
4
|
+
data.tar.gz: 35ea3d99c2ffba9249ff4195bce50000e7a6852f739e5f1a90df3da164025200
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: aad52744492ac284dd3fed0787326432ef69b3d6ce2572ae4d84503e0b0131bdabbb035ea2fecb9a48659a1a58a95e456a6bd55bc0bf2d0e610bebf11bb9bb6d
|
|
7
|
+
data.tar.gz: 0070ec8a237a4ada0e638888965b7bc935453411d29cffa8bcd9299b9f738d38250305f0d92561256a0ef9c601c2ebf8ac56a0da0df432e6679cb47d735c7098
|
data/CHANGELOG.md
ADDED
|
File without changes
|
data/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# hanami-papercraft
|
|
2
|
+
|
|
3
|
+
## Use Papercraft templates with Hanami
|
|
4
|
+
|
|
5
|
+
> **Note**: this project is still in an experimental stage.
|
|
6
|
+
|
|
7
|
+
The `hanami-papercraft` gem lets you create Hanami views using Papercraft
|
|
8
|
+
templates. Papercraft is an innovaive new gem for generating HTML using plain
|
|
9
|
+
Ruby. For more information on Papercraft please visit the Papercraft website:
|
|
10
|
+
[papercraft.noteflakes.com](https://papercraft.noteflakes.com).
|
|
11
|
+
|
|
12
|
+
This gem introduces a new view class called `Hanami::PapercraftView`, which you
|
|
13
|
+
can use instead of `Hanami::View` as the base class of your app's views.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
For the sake of simplicity, we'll base the following instructions on the Hanami
|
|
18
|
+
[getting started
|
|
19
|
+
guide](https://guides.hanamirb.org/v2.3/introduction/building-a-web-app/).
|
|
20
|
+
Please keep in mind that `hanami-papercraft` is still in a very early stage of
|
|
21
|
+
development, so things might not work as you expect, especially if you use the
|
|
22
|
+
more advanced features of Hanami.
|
|
23
|
+
|
|
24
|
+
### 1. Set your app's basic view class
|
|
25
|
+
|
|
26
|
+
In the `app/view.rb` file, change the `View` classes superclass to `Hanami::PapercraftView`:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
# app/view.rb
|
|
30
|
+
|
|
31
|
+
module Bookshelf
|
|
32
|
+
class View < Hanami::PapercraftView
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Use a Papercraft layout template
|
|
38
|
+
|
|
39
|
+
Replace the app's layout template stored in `app/templates/layouts/app.html.erb`
|
|
40
|
+
with a file named `app/templates/layouts/app.papercraft.rb`:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# app/templates/layouts/app.papercraft.rb
|
|
44
|
+
|
|
45
|
+
->(config:, context:, **props) {
|
|
46
|
+
html(lang: "en") {
|
|
47
|
+
head {
|
|
48
|
+
meta charset: "UTF-8"
|
|
49
|
+
meta name: "viewport", content: "width=device-width, initial-scale=1.0"
|
|
50
|
+
title "Bookshelf"
|
|
51
|
+
favicon_tag
|
|
52
|
+
stylesheet_tag(context, "app")
|
|
53
|
+
}
|
|
54
|
+
body {
|
|
55
|
+
render_children(config:, context:, **props)
|
|
56
|
+
javascript_tag(context, "app")
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Use Papercraft view templates
|
|
63
|
+
|
|
64
|
+
You can now start writing your view templates with Papercraft, e.g.:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# app/templates/books/index.papercraft.rb
|
|
68
|
+
|
|
69
|
+
->(context:, books:, **props) {
|
|
70
|
+
h1 "Books"
|
|
71
|
+
|
|
72
|
+
ul {
|
|
73
|
+
books.each do |book|
|
|
74
|
+
Kernel.p book
|
|
75
|
+
li book[:title]
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Passing Template Parameters
|
|
83
|
+
|
|
84
|
+
While theoretically you have access to the view class in your templates (through
|
|
85
|
+
`self`), you should use explicit arguments in your templates, as shown in the
|
|
86
|
+
examples above. The `PapercraftView` class always passes template parameters as
|
|
87
|
+
keyword arguments to the layout and the view templates.
|
|
88
|
+
|
|
89
|
+
In the view template above, the `books` keyword argument is defined because the
|
|
90
|
+
view class exposes such a parameter:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# app/views/books/index.rb
|
|
94
|
+
|
|
95
|
+
module Bookshelf
|
|
96
|
+
module Views
|
|
97
|
+
module Books
|
|
98
|
+
class Index < Bookshelf::View
|
|
99
|
+
expose :books do
|
|
100
|
+
[
|
|
101
|
+
{title: "Test Driven Development"},
|
|
102
|
+
{title: "Practical Object-Oriented Design in Ruby"}
|
|
103
|
+
]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Contributing
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Please feel free to contribute issues and PR's...
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/view"
|
|
4
|
+
require "hanami/helpers/assets_helper"
|
|
5
|
+
require "papercraft"
|
|
6
|
+
|
|
7
|
+
Hanami::View::ERB::Escape = ERB::Escape
|
|
8
|
+
|
|
9
|
+
module Hanami
|
|
10
|
+
class PapercraftView < Hanami::View
|
|
11
|
+
include Hanami::Helpers::AssetsHelper
|
|
12
|
+
class << self
|
|
13
|
+
include Hanami::Helpers::AssetsHelper
|
|
14
|
+
|
|
15
|
+
def _typed_url(context, source, ext)
|
|
16
|
+
source = "#{source}#{ext}" if source.is_a?(String) && _append_extension?(source, ext)
|
|
17
|
+
asset_url(context, source)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def _append_extension?(source, ext)
|
|
21
|
+
source !~ QUERY_STRING_MATCHER && source !~ /#{Regexp.escape(ext)}\z/
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def asset_url(context, source)
|
|
25
|
+
return source.url if source.respond_to?(:url)
|
|
26
|
+
return source if _absolute_url?(source)
|
|
27
|
+
|
|
28
|
+
context.assets[source].url
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def _absolute_url?(source)
|
|
32
|
+
ABSOLUTE_URL_MATCHER.match?(source.respond_to?(:url) ? source.url : source)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def _nonce(context, source, nonce_option)
|
|
36
|
+
if nonce_option == false
|
|
37
|
+
nil
|
|
38
|
+
elsif nonce_option == true || (nonce_option.nil? && !_absolute_url?(source))
|
|
39
|
+
content_security_policy_nonce(context)
|
|
40
|
+
else
|
|
41
|
+
nonce_option
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
CONTENT_SECURITY_POLICY_NONCE_REQUEST_KEY = "hanami.content_security_policy_nonce"
|
|
46
|
+
|
|
47
|
+
def content_security_policy_nonce(context)
|
|
48
|
+
return unless context.request
|
|
49
|
+
|
|
50
|
+
context.request.env[CONTENT_SECURITY_POLICY_NONCE_REQUEST_KEY]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def _subresource_integrity_value(context, source, ext)
|
|
54
|
+
return if _absolute_url?(source)
|
|
55
|
+
|
|
56
|
+
source = "#{source}#{ext}" unless /#{Regexp.escape(ext)}\z/.match?(source)
|
|
57
|
+
context.assets[source].sri
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
::Papercraft.extension(
|
|
62
|
+
favicon_tag: -> {
|
|
63
|
+
link href: "/assets/favicon.ico", rel: "shortcut icon", type: "image/x-icon"
|
|
64
|
+
},
|
|
65
|
+
stylesheet_tag: ->(context, *sources, **options) {
|
|
66
|
+
options = options.reject { |k, _| k.to_sym == :href }
|
|
67
|
+
nonce_option = options.delete(:nonce)
|
|
68
|
+
|
|
69
|
+
sources.each do |source|
|
|
70
|
+
attributes = {
|
|
71
|
+
href: Hanami::PapercraftView._typed_url(context, source, Hanami::PapercraftView::STYLESHEET_EXT),
|
|
72
|
+
type: Hanami::PapercraftView::STYLESHEET_MIME_TYPE,
|
|
73
|
+
rel: Hanami::PapercraftView::STYLESHEET_REL,
|
|
74
|
+
nonce: Hanami::PapercraftView._nonce(context, source, nonce_option)
|
|
75
|
+
}
|
|
76
|
+
attributes.merge!(options)
|
|
77
|
+
|
|
78
|
+
if context.assets.subresource_integrity? || attributes.include?(:integrity)
|
|
79
|
+
attributes[:integrity] ||= Hanami::PapercraftView._subresource_integrity_value(context, source, STYLESHEET_EXT)
|
|
80
|
+
attributes[:crossorigin] ||= Hanami::PapercraftView::CROSSORIGIN_ANONYMOUS
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
link(**attributes)
|
|
84
|
+
end
|
|
85
|
+
},
|
|
86
|
+
javascript_tag: ->(context, *sources, **options) {
|
|
87
|
+
options = options.reject { |k, _| k.to_sym == :src }
|
|
88
|
+
nonce_option = options.delete(:nonce)
|
|
89
|
+
|
|
90
|
+
sources.each do |source|
|
|
91
|
+
attributes = {
|
|
92
|
+
src: Hanami::PapercraftView._typed_url(context, source, Hanami::PapercraftView::JAVASCRIPT_EXT),
|
|
93
|
+
type: Hanami::PapercraftView::JAVASCRIPT_MIME_TYPE,
|
|
94
|
+
nonce: Hanami::PapercraftView._nonce(context, source, nonce_option)
|
|
95
|
+
}
|
|
96
|
+
attributes.merge!(options)
|
|
97
|
+
|
|
98
|
+
if context.assets.subresource_integrity? || attributes.include?(:integrity)
|
|
99
|
+
attributes[:integrity] ||= Hanami::PapercraftView._subresource_integrity_value(context, source, JAVASCRIPT_EXT)
|
|
100
|
+
attributes[:crossorigin] ||= Hanami::PapercraftView::CROSSORIGIN_ANONYMOUS
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
script(**attributes)
|
|
104
|
+
end
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def call(context:, layout: nil, **props)
|
|
109
|
+
layout ||= config.layout
|
|
110
|
+
|
|
111
|
+
locals = exposures.(context:, **props) do |value, exposure|
|
|
112
|
+
# TODO: what is decorate? and how do we support this without a rendering object
|
|
113
|
+
# if exposure.decorate? && value
|
|
114
|
+
# rendering.part(exposure.name, value, as: exposure.options[:as])
|
|
115
|
+
# else
|
|
116
|
+
value
|
|
117
|
+
# end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
template_params = {
|
|
121
|
+
context: context,
|
|
122
|
+
config: config,
|
|
123
|
+
**props,
|
|
124
|
+
**locals
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
puts '*' * 40
|
|
128
|
+
p params: template_params.keys
|
|
129
|
+
puts
|
|
130
|
+
|
|
131
|
+
template = load_template(config.template)
|
|
132
|
+
puts template.compiled_code
|
|
133
|
+
puts
|
|
134
|
+
|
|
135
|
+
if layout
|
|
136
|
+
layout_template = load_layout_template(layout)
|
|
137
|
+
layout_template.render(**template_params, &template)
|
|
138
|
+
else
|
|
139
|
+
template.render(**template_params)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def load_layout_template(name)
|
|
144
|
+
root ||= config.paths.first.root
|
|
145
|
+
fn = File.join(root, "layouts/#{name}.papercraft.rb")
|
|
146
|
+
source = IO.read(fn)
|
|
147
|
+
eval(source, binding, fn)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def load_template(name)
|
|
151
|
+
root ||= config.paths.first.root
|
|
152
|
+
fn = File.join(root, "#{name}.papercraft.rb")
|
|
153
|
+
source = IO.read(fn)
|
|
154
|
+
eval(source, binding, fn)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
data/papercraft.png
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hanami-papercraft
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '0.1'
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sharon Rosner
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: hanami-view
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: papercraft
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.17'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.17'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: minitest
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 5.25.5
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 5.25.5
|
|
54
|
+
email: sharon@noteflakes.com
|
|
55
|
+
executables: []
|
|
56
|
+
extensions: []
|
|
57
|
+
extra_rdoc_files:
|
|
58
|
+
- README.md
|
|
59
|
+
- papercraft.png
|
|
60
|
+
files:
|
|
61
|
+
- CHANGELOG.md
|
|
62
|
+
- README.md
|
|
63
|
+
- lib/hanami/papercraft_view.rb
|
|
64
|
+
- lib/hanami/papercraft_view_version.rb
|
|
65
|
+
- papercraft.png
|
|
66
|
+
homepage: http://github.com/digital-fabric/hanami-papercraft
|
|
67
|
+
licenses:
|
|
68
|
+
- MIT
|
|
69
|
+
metadata:
|
|
70
|
+
homepage_uri: https://github.com/digital-fabric/hanami-papercraft
|
|
71
|
+
changelog_uri: https://github.com/digital-fabric/hanami-papercraft/blob/master/CHANGELOG.md
|
|
72
|
+
rdoc_options:
|
|
73
|
+
- "--title"
|
|
74
|
+
- Hanami-Papercraft
|
|
75
|
+
- "--main"
|
|
76
|
+
- README.md
|
|
77
|
+
require_paths:
|
|
78
|
+
- lib
|
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '3.4'
|
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
requirements: []
|
|
90
|
+
rubygems_version: 3.7.0.dev
|
|
91
|
+
specification_version: 4
|
|
92
|
+
summary: 'Hanami-Papercraft: Papercraft views for Hanami'
|
|
93
|
+
test_files: []
|