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 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ PAPERCRAFT_VIEW_VERSION = "0.1"
5
+ 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: []