rubyoshka 0.5 → 0.6

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