typewriter 0.1.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.
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require_relative 'attribute'
5
+ require_relative 'writer'
6
+ require 'erb/escape'
7
+
8
+ module Typewriter
9
+ # Typewriter Template will generate typed HTML. Each tag and attribute has a
10
+ # match method that is typed via Sorbet (which is optional).
11
+ #
12
+ # The template is designed to allow subclassing or using `start` to generate
13
+ # templates without subclassing.
14
+ #
15
+ # When subclassing understand that `new` generatings a "buffer". Each time a
16
+ # tag(div, b, body, etc) is called it will be added to the buffer. Once
17
+ # `render` is called the buffer is is returned and then cleared.
18
+ #
19
+ # class Example < Typewriter::Template
20
+ # def initialize(name)
21
+ # super()
22
+ # @name = name
23
+ # end
24
+ #
25
+ # def view
26
+ # doctype
27
+ # html do
28
+ # body do
29
+ # h1 { text @name }
30
+ # end
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # puts Example.new('My Example').view.render
36
+ # <!DOCTYPE html><html><body><h1>My Example</h1></body></html>
37
+ #
38
+ # If you need to create custom tags, create a method that integrates with the
39
+ # Writer#write method.
40
+ class Template
41
+ include Typewriter::Writer
42
+ include Typewriter::SpecElements::HTMLAllElements
43
+
44
+ # To avoid subclassing a template, `start` can be used to yeild and return a Template.
45
+ #
46
+ # html = Typewriter::Template.start do |t|
47
+ # t.doctype
48
+ # t.html do
49
+ # t.body { t.h1 "Body" }
50
+ # end
51
+ # end
52
+ #
53
+ def self.start(&block)
54
+ obj = new
55
+ yield obj if block
56
+ obj
57
+ end
58
+
59
+ JOIN_ERROR = <<~TXT
60
+ You can not join templates which are created by the parent template.
61
+
62
+ This will fail:
63
+
64
+ Typewriter::Template.start { |t| t.join [t.text("hello")] }
65
+
66
+ Instead only join new templates
67
+ Typewriter::Template.start do |t|
68
+ t.join [Typewriter::Template.start { |x| x.text('hello') }]
69
+ end
70
+ TXT
71
+
72
+ # join an array of other templates into this template.
73
+ def join(templates)
74
+ templates.each do |t|
75
+ raise JOIN_ERROR if t == self
76
+
77
+ @__buffer << t.render
78
+ end
79
+ self
80
+ end
81
+
82
+ # text will generate the text node, this is the only way to insert strings into the template.
83
+ #
84
+ # Template.start do |t|
85
+ # t.div { t.text "Hello" }
86
+ # end
87
+ #
88
+ def text(value)
89
+ @__buffer << ERB::Escape.html_escape(value)
90
+ self
91
+ end
92
+
93
+ # generate a comment block
94
+ def comment(comment_text = nil)
95
+ write('<!--', '-->', nil, closing_char: '') { text comment_text.to_s }
96
+ end
97
+
98
+ # generate the doctype
99
+ def doctype
100
+ @__buffer << '<!DOCTYPE html>'
101
+ self
102
+ end
103
+
104
+ # generate a script tag. The return the script code to the block.
105
+ # The code is not escaped.
106
+ def script(attributes = nil, &block) # rubocop:disable Lint/UnusedMethodArgument
107
+ body = yield
108
+ write('<script', '</script>', attributes) { send :unsafe_text, body }
109
+ end
110
+
111
+ # attr is short cut in the template to return a new Attribute
112
+ #
113
+ # Template.start { |t| t.div(t.attr.id('my-div'), t.text '...') }
114
+ def attr(&blk)
115
+ if blk
116
+ Attribute.new(&blk)
117
+ else
118
+ Attribute.new
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typewriter
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,54 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'spec_elements'
5
+ require 'erb/escape'
6
+
7
+ module Typewriter
8
+ # Writer collects the rendered elements and attributes into a string.
9
+ module Writer
10
+ include Kernel
11
+
12
+ def initialize
13
+ @__buffer = new_string
14
+ end
15
+
16
+ # Render produces the HTML string and clears the buffer.
17
+ def render
18
+ result = @__buffer
19
+ # empty the buffer to prevent double rendering
20
+ @__buffer = new_string
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def new_string = String.new(capacity: 1024)
27
+
28
+ # used when the text should not be escaped, see `script`
29
+ def unsafe_text(value)
30
+ @__buffer << value
31
+ self
32
+ end
33
+
34
+ CLOSE = '>'
35
+ CLOSE_VOID = '/>'
36
+
37
+ def write(open, close, attr = nil, closing_char: CLOSE, &block)
38
+ if attr
39
+ @__buffer << open << Attribute.to_html(attr) << closing_char
40
+ else
41
+ @__buffer << open << closing_char
42
+ end
43
+
44
+ yield self if block
45
+ @__buffer << close
46
+
47
+ self
48
+ end
49
+
50
+ def write_void(open, attr = nil)
51
+ @__buffer << open << Attribute.to_html(attr) << CLOSE_VOID
52
+ end
53
+ end
54
+ end
data/lib/typewriter.rb ADDED
@@ -0,0 +1,9 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'typewriter/template'
5
+
6
+ # Typewriter is a Ruby module to generate HTML and attributes. It is meant to be
7
+ # simple, reasonably fast, and support Sorbet typing.
8
+ module Typewriter
9
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typewriter
4
+ # HTML attributes autogenerated, do not edit
5
+ module SpecAttributes
6
+ sig { params(value: String).returns(Typewriter::Attribute) }
7
+ def accept(value); end
8
+ sig { params(value: String).returns(Typewriter::Attribute) }
9
+ def accept_charset(value); end
10
+ sig { params(value: String).returns(Typewriter::Attribute) }
11
+ def accesskey(value); end
12
+ sig { params(value: String).returns(Typewriter::Attribute) }
13
+ def action(value); end
14
+ sig { params(value: String).returns(Typewriter::Attribute) }
15
+ def align(value); end
16
+ sig { params(value: String).returns(Typewriter::Attribute) }
17
+ def alt(value); end
18
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
19
+ def async(value); end
20
+ sig { params(value: String).returns(Typewriter::Attribute) }
21
+ def autocomplete(value); end
22
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
23
+ def autofocus(value); end
24
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
25
+ def autoplay(value); end
26
+ sig { params(value: String).returns(Typewriter::Attribute) }
27
+ def bgcolor(value); end
28
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
29
+ def border(value); end
30
+ sig { params(value: String).returns(Typewriter::Attribute) }
31
+ def charset(value); end
32
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
33
+ def checked(value); end
34
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
35
+ def cols(value); end
36
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
37
+ def colspan(value); end
38
+ sig { params(value: String).returns(Typewriter::Attribute) }
39
+ def content(value); end
40
+ sig { params(value: String).returns(Typewriter::Attribute) }
41
+ def contenteditable(value); end
42
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
43
+ def controls(value); end
44
+ sig { params(value: String).returns(Typewriter::Attribute) }
45
+ def coords(value); end
46
+ sig { params(value: String).returns(Typewriter::Attribute) }
47
+ def datetime(value); end
48
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
49
+ def default(value); end
50
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
51
+ def defer(value); end
52
+ sig { params(value: String).returns(Typewriter::Attribute) }
53
+ def dir(value); end
54
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
55
+ def disabled(value); end
56
+ sig { params(value: T.any(String, T::Boolean)).returns(Typewriter::Attribute) }
57
+ def download(value); end
58
+ sig { params(value: String).returns(Typewriter::Attribute) }
59
+ def draggable(value); end
60
+ sig { params(value: String).returns(Typewriter::Attribute) }
61
+ def enctype(value); end
62
+ sig { params(value: String).returns(Typewriter::Attribute) }
63
+ def for(value); end
64
+ sig { params(value: String).returns(Typewriter::Attribute) }
65
+ def form(value); end
66
+ sig { params(value: String).returns(Typewriter::Attribute) }
67
+ def formaction(value); end
68
+ sig { params(value: String).returns(Typewriter::Attribute) }
69
+ def headers(value); end
70
+ sig { params(value: T.any(Numeric, String)).returns(Typewriter::Attribute) }
71
+ def height(value); end
72
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
73
+ def hidden(value); end
74
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
75
+ def high(value); end
76
+ sig { params(value: String).returns(Typewriter::Attribute) }
77
+ def href(value); end
78
+ sig { params(value: String).returns(Typewriter::Attribute) }
79
+ def hreflang(value); end
80
+ sig { params(value: String).returns(Typewriter::Attribute) }
81
+ def id(value); end
82
+ sig { params(value: String).returns(Typewriter::Attribute) }
83
+ def integrity(value); end
84
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
85
+ def ismap(value); end
86
+ sig { params(value: String).returns(Typewriter::Attribute) }
87
+ def kind(value); end
88
+ sig { params(value: String).returns(Typewriter::Attribute) }
89
+ def label(value); end
90
+ sig { params(value: String).returns(Typewriter::Attribute) }
91
+ def lang(value); end
92
+ sig { params(value: String).returns(Typewriter::Attribute) }
93
+ def list(value); end
94
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
95
+ def loop(value); end
96
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
97
+ def low(value); end
98
+ sig { params(value: T.any(Numeric, String)).returns(Typewriter::Attribute) }
99
+ def max(value); end
100
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
101
+ def maxlength(value); end
102
+ sig { params(value: String).returns(Typewriter::Attribute) }
103
+ def media(value); end
104
+ sig { params(value: String).returns(Typewriter::Attribute) }
105
+ def method(value); end
106
+ sig { params(value: T.any(Numeric, String)).returns(Typewriter::Attribute) }
107
+ def min(value); end
108
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
109
+ def multiple(value); end
110
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
111
+ def muted(value); end
112
+ sig { params(value: String).returns(Typewriter::Attribute) }
113
+ def name(value); end
114
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
115
+ def novalidate(value); end
116
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
117
+ def open(value); end
118
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
119
+ def optimum(value); end
120
+ sig { params(value: String).returns(Typewriter::Attribute) }
121
+ def pattern(value); end
122
+ sig { params(value: String).returns(Typewriter::Attribute) }
123
+ def placeholder(value); end
124
+ sig { params(value: String).returns(Typewriter::Attribute) }
125
+ def poster(value); end
126
+ sig { params(value: String).returns(Typewriter::Attribute) }
127
+ def preload(value); end
128
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
129
+ def readonly(value); end
130
+ sig { params(value: String).returns(Typewriter::Attribute) }
131
+ def rel(value); end
132
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
133
+ def required(value); end
134
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
135
+ def reversed(value); end
136
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
137
+ def rows(value); end
138
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
139
+ def rowspan(value); end
140
+ sig { params(value: String).returns(Typewriter::Attribute) }
141
+ def sandbox(value); end
142
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
143
+ def span(value); end
144
+ sig { params(value: String).returns(Typewriter::Attribute) }
145
+ def scope(value); end
146
+ sig { params(value: T::Boolean).returns(Typewriter::Attribute) }
147
+ def selected(value); end
148
+ sig { params(value: String).returns(Typewriter::Attribute) }
149
+ def shape(value); end
150
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
151
+ def size(value); end
152
+ sig { params(value: String).returns(Typewriter::Attribute) }
153
+ def sizes(value); end
154
+ sig { params(value: String).returns(Typewriter::Attribute) }
155
+ def spellcheck(value); end
156
+ sig { params(value: String).returns(Typewriter::Attribute) }
157
+ def src(value); end
158
+ sig { params(value: String).returns(Typewriter::Attribute) }
159
+ def srcdoc(value); end
160
+ sig { params(value: String).returns(Typewriter::Attribute) }
161
+ def srclang(value); end
162
+ sig { params(value: String).returns(Typewriter::Attribute) }
163
+ def srcset(value); end
164
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
165
+ def start(value); end
166
+ sig { params(value: T.any(Numeric, String)).returns(Typewriter::Attribute) }
167
+ def step(value); end
168
+ sig { params(value: String).returns(Typewriter::Attribute) }
169
+ def style(value); end
170
+ sig { params(value: Numeric).returns(Typewriter::Attribute) }
171
+ def tabindex(value); end
172
+ sig { params(value: String).returns(Typewriter::Attribute) }
173
+ def target(value); end
174
+ sig { params(value: String).returns(Typewriter::Attribute) }
175
+ def title(value); end
176
+ sig { params(value: String).returns(Typewriter::Attribute) }
177
+ def translate(value); end
178
+ sig { params(value: String).returns(Typewriter::Attribute) }
179
+ def type(value); end
180
+ sig { params(value: String).returns(Typewriter::Attribute) }
181
+ def usemap(value); end
182
+ sig { params(value: String).returns(Typewriter::Attribute) }
183
+ def value(value); end
184
+ sig { params(value: T.any(Numeric, String)).returns(Typewriter::Attribute) }
185
+ def width(value); end
186
+ sig { params(value: String).returns(Typewriter::Attribute) }
187
+ def wrap(value); end
188
+ end
189
+ end