rubyoshka 0.5 → 0.6

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: aabb46d1744db33af049b11eec91a9e18a938aea5391ab2f22e41c0bccce77ae
4
- data.tar.gz: 7d1a473f5c25ab35f38d0886dee156f2da38bce9306cb48a4e39e47d6d2e818d
3
+ metadata.gz: ba3dd09fc4ab56c9af5a69c47d9da50949180b0c636c788ed74660bbc04b4b5d
4
+ data.tar.gz: faf523787f5ff11bb3e0d5346ad684b8e3c22c8fdf522e30ff0f43ff4da40a74
5
5
  SHA512:
6
- metadata.gz: c5c43c9e8bb8f0b2a076a2a607ce3d735575748be1a6998dedb38ef795bfe3a8a33f2d3039c5ba379085b2750929966d4a9f41201651196dc2c1e5ea280c21c1
7
- data.tar.gz: 120ad28457f4c3c1775de5d2b542d365a3abb5fdcdf01946097b251dd1830eaee7d2d6bc63bf1fe72c195073e80b9c2e4b3b0d9bddc2445c81c31d45aa9157a2
6
+ metadata.gz: e10f0611cc2596ac27dd3f93df91db2d77e6ba12bc3159cdb3f29ab1b6abd3caedc66161e14a18b776cba72164a7dad725c1d60e8b4a09bcc39f96a34fb1790f
7
+ data.tar.gz: ad0b35a97f0d76fa7c82fd456751004f75af759b9d98efc824f3ae99d6fdb5d3a6905aecbb083dc5f44d272cd828c6728e27330ceab5355f1d25c24a7090f3fc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ 0.6 2021-03-03
2
+ --------------
3
+
4
+ * Fix Rubyoshka on Ruby 3.0
5
+ * Refactor and add more tests
6
+
1
7
  0.5 2021-02-27
2
8
  --------------
3
9
 
data/lib/rubyoshka.rb CHANGED
@@ -2,219 +2,10 @@
2
2
 
3
3
  require 'escape_utils'
4
4
 
5
+ require_relative 'rubyoshka/renderer'
6
+
5
7
  # A Rubyoshka is a template representing a piece of HTML
6
8
  class Rubyoshka
7
- # A Renderer is a rendering of a Rubyoshka
8
- class Renderer
9
- attr_reader :context
10
-
11
- # Initializes attributes and renders the given block
12
- # @param context [Hash] rendering context
13
- # @param block [Proc] template block
14
- # @return [void]
15
- def initialize(context, &block)
16
- @context = context
17
- @buffer = +''
18
- instance_eval(&block)
19
- end
20
-
21
- # Returns the result of the rendering
22
- # @return [String]
23
- def to_s
24
- @buffer
25
- end
26
-
27
- def escape_text(text)
28
- raise NotImplementedError
29
- end
30
-
31
- def escape_uri(uri)
32
- EscapeUtils.escape_uri(v)
33
- end
34
-
35
- S_TAG_METHOD_LINE = __LINE__ + 1
36
- S_TAG_METHOD = <<~EOF
37
- S_TAG_%<TAG>s_PRE = '<%<tag>s'
38
- S_TAG_%<TAG>s_CLOSE = '</%<tag>s>'
39
-
40
- def %<tag>s(text = nil, **props, &block)
41
- @buffer << S_TAG_%<TAG>s_PRE
42
- emit_props(props) unless props.empty?
43
-
44
- if block
45
- @buffer << S_GT
46
- instance_eval(&block)
47
- @buffer << S_TAG_%<TAG>s_CLOSE
48
- elsif Rubyoshka === text
49
- @buffer << S_GT
50
- emit(text)
51
- @buffer << S_TAG_%<TAG>s_CLOSE
52
- elsif text
53
- @buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
54
- else
55
- @buffer << S_SLASH_GT
56
- end
57
- end
58
- EOF
59
-
60
- R_CONST_SYM = /^[A-Z]/
61
-
62
- # Catches undefined tag method call and handles them by defining the method
63
- # @param sym [Symbol] HTML tag or component identifier
64
- # @param args [Array] method call arguments
65
- # @param block [Proc] block passed to method call
66
- # @return [void]
67
- def method_missing(sym, *args, &block)
68
- value = @local && @local[sym]
69
- return value if value
70
-
71
- if sym =~ R_CONST_SYM
72
- # Component reference (capitalized method name)
73
- o = instance_eval(sym.to_s) rescue Rubyoshka.const_get(sym) \
74
- rescue Object.const_get(sym)
75
- case o
76
- when ::Proc
77
- self.class.define_method(sym) { |*a, &b| emit(o.(*a, &b)) }
78
- emit(o.(*args, &block))
79
- when Rubyoshka
80
- self.class.define_method(sym) { |**ctx|
81
- ctx.empty? ? emit(o) : with(ctx) { emit(o) }
82
- }
83
- ctx = args.first
84
- Hash === ctx ? with(ctx) { emit(o) } : emit(o)
85
- when ::String
86
- @buffer << o
87
- else
88
- e = StandardError.new "Cannot render #{o.inspect}"
89
- e.set_backtrace(caller)
90
- raise e
91
- end
92
- else
93
- tag = sym.to_s
94
- code = S_TAG_METHOD % { tag: tag, TAG: tag.upcase }
95
- self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
96
- send(sym, *args, &block)
97
- end
98
- end
99
-
100
- # Emits the given object into the rendering buffer
101
- # @param o [Proc, Rubyoshka, Module, String] emitted object
102
- # @return [void]
103
- def emit(o)
104
- case o
105
- when ::Proc
106
- instance_eval(&o)
107
- when Rubyoshka
108
- instance_eval(&o.block)
109
- when Module
110
- # If module is given, the component is expected to be a const inside the module
111
- emit(o::Component)
112
- when nil
113
- else
114
- @buffer << o.to_s
115
- end
116
- end
117
- alias_method :e, :emit
118
-
119
- S_LT = '<'
120
- S_GT = '>'
121
- S_LT_SLASH = '</'
122
- S_SPACE_LT_SLASH = ' </'
123
- S_SLASH_GT = '/>'
124
- S_SPACE = ' '
125
- S_EQUAL_QUOTE = '="'
126
- S_QUOTE = '"'
127
-
128
- # Emits tag attributes into the rendering buffer
129
- # @param props [Hash] tag attributes
130
- # @return [void]
131
- def emit_props(props)
132
- props.each { |k, v|
133
- case k
134
- when :src, :href
135
- @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
136
- EscapeUtils.escape_uri(v) << S_QUOTE
137
- else
138
- case v
139
- when true
140
- @buffer << S_SPACE << k.to_s
141
- when false, nil
142
- # emit nothing
143
- else
144
- @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE << v << S_QUOTE
145
- end
146
- end
147
- }
148
- end
149
-
150
- # Emits the p tag (overrides Object#p)
151
- # @param text [String] text content of tag
152
- # @param props [Hash] tag attributes
153
- # @para block [Proc] nested HTML block
154
- # @return [void]
155
- def p(text = nil, **props, &block)
156
- method_missing(:p, text, **props, &block)
157
- end
158
-
159
- S_HTML5_DOCTYPE = '<!DOCTYPE html>'
160
-
161
- # Emits an HTML5 doctype tag and an html tag with the given block
162
- # @param block [Proc] nested HTML block
163
- # @return [void]
164
- def html5(&block)
165
- @buffer << S_HTML5_DOCTYPE
166
- self.html(&block)
167
- end
168
-
169
- # Emits text into the rendering buffer
170
- # @param data [String] text
171
- def text(data)
172
- @buffer << escape_text(data)
173
- end
174
-
175
- # Sets a local context for the given block
176
- # @param ctx [Hash] context hash
177
- # @param block [Proc] nested HTML block
178
- # @return [void]
179
- def with(**ctx, &block)
180
- old_local, @local = @local, ctx
181
- instance_eval(&block)
182
- ensure
183
- @local = old_local
184
- end
185
-
186
- # Caches the given block with the given arguments as cache key
187
- # @param vary [*Object] cache key
188
- # @param block [Proc] nested HTML block
189
- # @return [void]
190
- def cache(*vary, &block)
191
- key = [block.source_location, *vary]
192
-
193
- if (cached = Rubyoshka.cache[key])
194
- @buffer << cached
195
- return
196
- end
197
-
198
- cache_pos = @buffer.length
199
- instance_eval(&block)
200
- diff = @buffer[cache_pos..-1]
201
- Rubyoshka.cache[key] = diff
202
- end
203
- end
204
-
205
- class HTMLRenderer < Renderer
206
- def escape_text(text)
207
- EscapeUtils.escape_html(text.to_s)
208
- end
209
-
210
- end
211
-
212
- class XMLRenderer < Renderer
213
- def escape_text(text)
214
- EscapeUtils.escape_xml(text.to_s)
215
- end
216
- end
217
-
218
9
  attr_reader :block
219
10
 
220
11
  # Initializes a Rubyoshka with the given block
@@ -223,7 +14,7 @@ class Rubyoshka
223
14
  # @param [void]
224
15
  def initialize(mode: :html, **ctx, &block)
225
16
  @mode = mode
226
- @block = ctx.empty? ? block : proc { with(ctx, &block) }
17
+ @block = ctx.empty? ? block : proc { with(**ctx, &block) }
227
18
  end
228
19
 
229
20
  H_EMPTY = {}.freeze
@@ -255,7 +46,12 @@ class Rubyoshka
255
46
  def self.component(&block)
256
47
  proc { |*args| new { instance_exec(*args, &block) } }
257
48
  end
49
+
50
+ def self.xml(**ctx, &block)
51
+ new(mode: :xml, **ctx, &block)
52
+ end
258
53
  end
54
+ ::H = Rubyoshka
259
55
 
260
56
  module ::Kernel
261
57
  # Convenience method for creating a new Rubyoshka
@@ -263,6 +59,6 @@ module ::Kernel
263
59
  # @param block [Proc] nested block
264
60
  # @return [Rubyoshka] Rubyoshka template
265
61
  def H(**ctx, &block)
266
- Rubyoshka.new(ctx, &block)
62
+ Rubyoshka.new(**ctx, &block)
267
63
  end
268
64
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './html'
4
+
5
+ class Rubyoshka
6
+ # Markup extensions
7
+ module HTML
8
+ # Emits the p tag (overrides Object#p)
9
+ # @param text [String] text content of tag
10
+ # @param props [Hash] tag attributes
11
+ # @para block [Proc] nested HTML block
12
+ # @return [void]
13
+ def p(text = nil, **props, &block)
14
+ method_missing(:p, text, **props, &block)
15
+ end
16
+
17
+ S_HTML5_DOCTYPE = '<!DOCTYPE html>'
18
+
19
+ # Emits an HTML5 doctype tag and an html tag with the given block
20
+ # @param block [Proc] nested HTML block
21
+ # @return [void]
22
+ def html5(&block)
23
+ @buffer << S_HTML5_DOCTYPE
24
+ self.html(&block)
25
+ end
26
+
27
+ def link_stylesheet(href, custom_attributes = nil)
28
+ attributes = {
29
+ rel: 'stylesheet',
30
+ href: href
31
+ }
32
+ if custom_attributes
33
+ attributes = custom_attributes.merge(attributes)
34
+ end
35
+ link(**attributes)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './html'
4
+
5
+ class Rubyoshka
6
+ # A Renderer is a rendering of a Rubyoshka
7
+ class Renderer
8
+ attr_reader :context
9
+
10
+ # Initializes attributes and renders the given block
11
+ # @param context [Hash] rendering context
12
+ # @param block [Proc] template block
13
+ # @return [void]
14
+ def initialize(context, &block)
15
+ @context = context
16
+ @buffer = +''
17
+ instance_eval(&block)
18
+ end
19
+
20
+ # Returns the result of the rendering
21
+ # @return [String]
22
+ def to_s
23
+ @buffer
24
+ end
25
+
26
+ def escape_text(text)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def escape_uri(uri)
31
+ EscapeUtils.escape_uri(v)
32
+ end
33
+
34
+ S_TAG_METHOD_LINE = __LINE__ + 1
35
+ S_TAG_METHOD = <<~EOF
36
+ S_TAG_%<TAG>s_PRE = '<%<tag>s'
37
+ S_TAG_%<TAG>s_CLOSE = '</%<tag>s>'
38
+
39
+ def %<tag>s(text = nil, **props, &block)
40
+ @buffer << S_TAG_%<TAG>s_PRE
41
+ emit_props(props) unless props.empty?
42
+
43
+ if block
44
+ @buffer << S_GT
45
+ instance_eval(&block)
46
+ @buffer << S_TAG_%<TAG>s_CLOSE
47
+ elsif Rubyoshka === text
48
+ @buffer << S_GT
49
+ emit(text)
50
+ @buffer << S_TAG_%<TAG>s_CLOSE
51
+ elsif text
52
+ @buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
53
+ else
54
+ @buffer << S_SLASH_GT
55
+ end
56
+ end
57
+ EOF
58
+
59
+ R_CONST_SYM = /^[A-Z]/
60
+
61
+ # Catches undefined tag method call and handles them by defining the method
62
+ # @param sym [Symbol] HTML tag or component identifier
63
+ # @param args [Array] method call arguments
64
+ # @param block [Proc] block passed to method call
65
+ # @return [void]
66
+ def method_missing(sym, *args, **opts, &block)
67
+ value = @local && @local[sym]
68
+ return value if value
69
+
70
+ if sym =~ R_CONST_SYM
71
+ # Component reference (capitalized method name)
72
+ o = instance_eval(sym.to_s) rescue Rubyoshka.const_get(sym) \
73
+ rescue Object.const_get(sym)
74
+ case o
75
+ when ::Proc
76
+ self.class.define_method(sym) { |*a, **c, &b| emit(o.(*a, **c, &b)) }
77
+ emit(o.(*args, **opts,&block))
78
+ when Rubyoshka
79
+ self.class.define_method(sym) do |**ctx|
80
+ ctx.empty? ? emit(o) : with(**ctx) { emit(o) }
81
+ end
82
+ Hash === opts.empty? ? emit(o) : with(**opts) { emit(o) }
83
+ when ::String
84
+ @buffer << o
85
+ else
86
+ e = StandardError.new "Cannot render #{o.inspect}"
87
+ e.set_backtrace(caller)
88
+ raise e
89
+ end
90
+ else
91
+ tag = sym.to_s
92
+ code = S_TAG_METHOD % { tag: tag, TAG: tag.upcase }
93
+ self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
94
+ send(sym, *args, **opts, &block)
95
+ end
96
+ end
97
+
98
+ # Emits the given object into the rendering buffer
99
+ # @param o [Proc, Rubyoshka, Module, String] emitted object
100
+ # @return [void]
101
+ def emit(o)
102
+ case o
103
+ when ::Proc
104
+ instance_eval(&o)
105
+ when Rubyoshka
106
+ instance_eval(&o.block)
107
+ when Module
108
+ # If module is given, the component is expected to be a const inside the module
109
+ emit(o::Component)
110
+ when nil
111
+ else
112
+ @buffer << o.to_s
113
+ end
114
+ end
115
+ alias_method :e, :emit
116
+
117
+ S_LT = '<'
118
+ S_GT = '>'
119
+ S_LT_SLASH = '</'
120
+ S_SPACE_LT_SLASH = ' </'
121
+ S_SLASH_GT = '/>'
122
+ S_SPACE = ' '
123
+ S_EQUAL_QUOTE = '="'
124
+ S_QUOTE = '"'
125
+
126
+ # Emits tag attributes into the rendering buffer
127
+ # @param props [Hash] tag attributes
128
+ # @return [void]
129
+ def emit_props(props)
130
+ props.each { |k, v|
131
+ case k
132
+ when :src, :href
133
+ @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
134
+ EscapeUtils.escape_uri(v) << S_QUOTE
135
+ else
136
+ case v
137
+ when true
138
+ @buffer << S_SPACE << k.to_s
139
+ when false, nil
140
+ # emit nothing
141
+ else
142
+ @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE << v << S_QUOTE
143
+ end
144
+ end
145
+ }
146
+ end
147
+
148
+ # Emits text into the rendering buffer
149
+ # @param data [String] text
150
+ def text(data)
151
+ @buffer << escape_text(data)
152
+ end
153
+
154
+ # Sets a local context for the given block
155
+ # @param ctx [Hash] context hash
156
+ # @param block [Proc] nested HTML block
157
+ # @return [void]
158
+ def with(**ctx, &block)
159
+ old_local, @local = @local, ctx
160
+ instance_eval(&block)
161
+ ensure
162
+ @local = old_local
163
+ end
164
+
165
+ # Caches the given block with the given arguments as cache key
166
+ # @param vary [*Object] cache key
167
+ # @param block [Proc] nested HTML block
168
+ # @return [void]
169
+ def cache(*vary, **opts, &block)
170
+ key = [block.source_location.hash, vary.hash, opts.hash]
171
+
172
+ if (cached = Rubyoshka.cache[key])
173
+ @buffer << cached
174
+ return
175
+ end
176
+
177
+ cache_pos = @buffer.length
178
+ instance_eval(&block)
179
+ diff = @buffer[cache_pos..-1]
180
+ Rubyoshka.cache[key] = diff
181
+ end
182
+ end
183
+
184
+ class HTMLRenderer < Renderer
185
+ include HTML
186
+
187
+ def escape_text(text)
188
+ EscapeUtils.escape_html(text.to_s)
189
+ end
190
+ end
191
+
192
+ class XMLRenderer < Renderer
193
+ def escape_text(text)
194
+ EscapeUtils.escape_xml(text.to_s)
195
+ end
196
+ end
197
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Rubyoshka
4
- VERSION = '0.5'
4
+ VERSION = '0.6'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyoshka
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-27 00:00:00.000000000 Z
11
+ date: 2021-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -90,6 +90,8 @@ files:
90
90
  - CHANGELOG.md
91
91
  - README.md
92
92
  - lib/rubyoshka.rb
93
+ - lib/rubyoshka/html.rb
94
+ - lib/rubyoshka/renderer.rb
93
95
  - lib/rubyoshka/version.rb
94
96
  homepage: http://github.com/digital-fabric/rubyoshka
95
97
  licenses: