liquid4-blocks 0.7.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 498631587a1fba29be74ba965e6d7910c6bb0e2a
4
+ data.tar.gz: 16c041c4ed0fa461a371adb5d6a9fac7911f8b36
5
+ SHA512:
6
+ metadata.gz: 3348e6ddfc376816f3c06652e946bccf176b2f849787f2bea2a0a9c525504779669291f4e4fed1b8a18da48548dc9c2f19bc6a8fca9c9a7dcc49b679eef6caf6
7
+ data.tar.gz: 3fefad0d6bee58a0e627543cc680fafc22bb0c52c274af4295fab6f75c97421c6051110d42fe491ddcc243c6df42ece89e3123fcfb2b599bc6fbeb36f1e57ea4
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 Dan Webb
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ require 'liquid'
2
+
3
+ module LiquidBlocks
4
+ autoload :Extends, 'liquid_blocks/extends'
5
+ autoload :Block, 'liquid_blocks/block'
6
+ end
7
+
8
+ Liquid::Template.register_tag(:extends, LiquidBlocks::Extends)
9
+ Liquid::Template.register_tag(:block, LiquidBlocks::Block)
@@ -0,0 +1,63 @@
1
+ module LiquidBlocks
2
+
3
+ class BlockDrop < ::Liquid::Drop
4
+ def initialize(block)
5
+ @block = block
6
+ end
7
+
8
+ def super
9
+ @block.call_super(@context)
10
+ end
11
+ end
12
+
13
+ class Block < ::Liquid::Block
14
+ SYNTAX = /([\w!]+)/
15
+
16
+ attr_accessor :parent
17
+ attr_reader :name
18
+
19
+ def initialize(tag_name, markup, tokens)
20
+ @parent = nil
21
+
22
+ if markup =~ SYNTAX
23
+ @name = $1
24
+ else
25
+ raise Liquid::SyntaxError.new("Syntax Error in 'block' - Valid syntax: block [name]")
26
+ end
27
+
28
+ super if tokens
29
+ end
30
+
31
+ def render(context)
32
+ context.stack do
33
+ context['block'] = BlockDrop.new(self)
34
+
35
+ super
36
+ end
37
+ end
38
+
39
+ def add_parent(nodelist)
40
+ if @parent != nil
41
+ @parent.add_parent(nodelist)
42
+ else
43
+ @parent = Block.parse(@tag_name, @name, Liquid::Tokenizer.new(@name + '{% end' + @tag_name + '%}'), @parse_context)
44
+ @parent.nodelist.clear
45
+ nodelist.each {|item| @parent.nodelist << item}
46
+ end
47
+ end
48
+
49
+ def blank?
50
+ false
51
+ end
52
+
53
+ def call_super(context)
54
+ if @parent
55
+ @parent.render(context)
56
+ else
57
+ ''
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,159 @@
1
+ module LiquidBlocks
2
+
3
+ class Extends < ::Liquid::Block
4
+ SYNTAX = /(#{Liquid::QuotedFragment}+)/
5
+
6
+ attr_reader :template_name
7
+
8
+ def initialize(tag_name, markup, tokens)
9
+ if markup =~ SYNTAX
10
+ @template_name = $1[1..-2]
11
+ else
12
+ raise Liquid::SyntaxError.new("Syntax Error in 'extends' - Valid syntax: extends [template]")
13
+ end
14
+ super
15
+ end
16
+
17
+ def parse(tokens)
18
+ @body = Liquid::BlockBody.new
19
+ parse_all(tokens)
20
+ end
21
+
22
+ def render(context)
23
+ origin = populate_nodelist(self, context)
24
+ @body.nodelist.clear
25
+ origin.root.nodelist.each {|item| @body.nodelist << item}
26
+ super
27
+ end
28
+
29
+ def nodelist
30
+ @body.nodelist
31
+ end
32
+
33
+ def blank?
34
+ false
35
+ end
36
+
37
+ # Load the template that is being extended by the current tag.
38
+ #
39
+ # @param template_name [String] the context to use when loading the template
40
+ # @return [Liquid::Document] the parsed template
41
+ def load_template(template_name)
42
+ source = Liquid::Template.file_system.read_template_file(template_name)
43
+ Liquid::Template.parse(source)
44
+ end
45
+
46
+ private
47
+
48
+ def create_variable(token, parse_context)
49
+ token.scan(Liquid::BlockBody::ContentOfVariable) do |content|
50
+ markup = content.first
51
+ return Liquid::Variable.new(markup, parse_context)
52
+ end
53
+ raise Liquid::SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
54
+ end
55
+
56
+ def parse_all(tokens)
57
+ @body.nodelist.clear
58
+
59
+ while token = tokens.shift
60
+ case token
61
+ when /^#{Liquid::TagStart}/
62
+ if token =~ /^#{Liquid::TagStart}\s*(\w+)\s*(.*)?#{Liquid::TagEnd}$/
63
+ # fetch the tag from registered blocks
64
+ if tag = Liquid::Template.tags[$1]
65
+ @body.nodelist << tag.parse($1, $2, tokens, @parse_context)
66
+ else
67
+ # this tag is not registered with the system
68
+ # pass it to the current block for special handling or error reporting
69
+ unknown_tag($1, $2, tokens)
70
+ end
71
+ else
72
+ raise Liquid::SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{Liquid::TagEnd.inspect}"
73
+ end
74
+ when /^#{Liquid::VariableStart}/
75
+ @body.nodelist << create_variable(token, @parse_context)
76
+ when ''
77
+ # pass
78
+ else
79
+ @body.nodelist << token
80
+ end
81
+ end
82
+ end
83
+
84
+ # Find all +block+ tags defined as children of the given node.
85
+ #
86
+ # The returned hash will have keys that are the names of the declared
87
+ # blocks, with values of +LiquidBlocks::Block+ instances.
88
+ #
89
+ # @param node [Liquid::Tag] a possible +block+ tag
90
+ # @param blocks [Hash] a set of existing blocks to build on
91
+ # @return [Hash] all blocks provided by the given node
92
+ def find_blocks(node, blocks={})
93
+ if node.respond_to?(:nodelist) && !node.nodelist.nil?
94
+ node.nodelist.inject(blocks) do |b, node|
95
+ if node.is_a?(LiquidBlocks::Block)
96
+ b[node.name] = node
97
+ end
98
+ find_blocks(node, b)
99
+
100
+ b
101
+ end
102
+ end
103
+
104
+ blocks
105
+ end
106
+
107
+ # Get the first +extends+ tag in the given template, falling back to
108
+ # +nil+ if the template does not contain an +extends+ tag.
109
+ #
110
+ # @param template [Liquid::Document] a template in which to search for an +extends+ tag
111
+ # @return [LiquidBlocks::Extends] the first +extends+ tag, or +nil+
112
+ def get_extends_tag_for_template(template)
113
+ template.root.nodelist.select { |node| node.is_a?(Extends) }.first
114
+ end
115
+
116
+ # Populate the nodelist for the highest-level template being extended with
117
+ # the contents of its child +block+ tags.
118
+ #
119
+ # @param tag [Liquid::Tag] an instance of an +extends+ tag
120
+ # @param context [Liquid::Context] the context to use when loading the template
121
+ # @return [Liquid::Document] the top-level template with a modified nodelist
122
+ def populate_nodelist(tag, context)
123
+
124
+ # Get the template being extended by the tag and get an appropriate root
125
+ # node for it based on whether or not it extends another template
126
+ parent = tag.load_template(@template_name)
127
+ parent_blocks = find_blocks(parent.root)
128
+ extends = get_extends_tag_for_template(parent)
129
+ parent_node = extends || parent.root
130
+
131
+ # Examine every block in the current tag, replacing a matching block in
132
+ # its parent or adding it to its parent's nodelist, provided that the
133
+ # parent is not the top-level template
134
+ find_blocks(tag).each do |name, block|
135
+ parent_block = parent_blocks[name]
136
+ if parent_block
137
+ parent_block.parent = block.parent
138
+ parent_block.add_parent(parent_block.nodelist)
139
+ parent_block.nodelist.clear
140
+ block.nodelist.each {|item| parent_block.nodelist << item}
141
+ elsif extends
142
+ parent_node.nodelist << block
143
+ end
144
+ end
145
+
146
+ # Pass likely context variables on to parent templates
147
+ included_templates = (context.registers[:cached_partials] || {}).keys
148
+ included_templates.each do |included_template|
149
+ if context.key?(included_template)
150
+ context[tag.template_name] ||= context[included_template]
151
+ end
152
+ end
153
+
154
+ parent
155
+ end
156
+
157
+ end
158
+
159
+ end
@@ -0,0 +1,3 @@
1
+ module LiquidBlocks
2
+ VERSION = '0.7.0'
3
+ end
@@ -0,0 +1,315 @@
1
+ require 'test/unit'
2
+ require 'liquid_blocks'
3
+
4
+ class TestFileSystem
5
+ def read_template_file(path)
6
+ if path == 'simple'
7
+ 'test'
8
+ elsif path == 'complex'
9
+ %{
10
+ beginning
11
+
12
+ {% block thing %}
13
+ rarrgh
14
+ {% endblock %}
15
+
16
+ {% block another %}
17
+ bum
18
+ {% endblock %}
19
+
20
+ end
21
+ }
22
+ elsif path == 'nested'
23
+ %{
24
+ {% extends 'complex' %}
25
+
26
+ {% block thing %}
27
+ from nested
28
+ {% endblock %}
29
+
30
+ {% block another %}
31
+ from nested (another)
32
+ {% endblock %}
33
+ }
34
+ elsif path == 'nested_more'
35
+ %{
36
+ {% extends 'complex' %}
37
+
38
+ {% block thing %}
39
+ thing
40
+ {% endblock %}
41
+ }
42
+ elsif path == 'similar'
43
+ %{
44
+ {% block aralia %}
45
+ aralia
46
+ {% endblock %}
47
+
48
+ {% block azalea %}
49
+ azalea
50
+ {% endblock %}
51
+ }
52
+ elsif path == 'deep'
53
+ %{
54
+ {% block one %}
55
+ one
56
+ {% block two %}
57
+ two
58
+ {% block three %}three{% endblock %}
59
+ {% block four %}four{% endblock %}
60
+ {% endblock %}
61
+ {% endblock %}
62
+ }
63
+ elsif path == 'nested_deep'
64
+ %{
65
+ {% extends 'deep' %}
66
+ }
67
+ elsif path == 'ruby'
68
+ %{
69
+ {% block test %}{% endblock %}
70
+ {% block test! %}{% endblock %}
71
+ }
72
+ elsif path == 'context_variable'
73
+ %{
74
+ {% block body %}{{ context_variable }}{% endblock %}
75
+ }
76
+ elsif path == 'context_variable_extended'
77
+ %{
78
+ {% extends 'context_variable' %}
79
+ {% block body %}
80
+ 1{{ block.super }}
81
+ 2{{ context_variable_extended }}
82
+ {% endblock %}
83
+ }
84
+ end
85
+ end
86
+ end
87
+
88
+ Liquid::Template.file_system = TestFileSystem.new
89
+
90
+ class LiquidBlocksTest < Test::Unit::TestCase
91
+ def test_output_the_contents_of_the_extended_template
92
+ template = Liquid::Template.parse %{
93
+ {% extends 'simple' %}
94
+
95
+ {% block thing %}
96
+ yeah
97
+ {% endblock %}
98
+ }
99
+
100
+ assert_match /test/, template.render
101
+ end
102
+
103
+ def test_render_original_content_of_block_if_no_child_block_given
104
+ template = Liquid::Template.parse %{
105
+ {% extends 'complex' %}
106
+ }
107
+
108
+ assert_match /rarrgh/, template.render
109
+ assert_match /bum/, template.render
110
+ end
111
+
112
+ def test_render_child_content_of_block_if_child_block_given
113
+ template = Liquid::Template.parse %{
114
+ {% extends 'complex' %}
115
+
116
+ {% block thing %}
117
+ booyeah
118
+ {% endblock %}
119
+ }
120
+
121
+ assert_match /booyeah/, template.render
122
+ assert_match /bum/, template.render
123
+ end
124
+
125
+ def test_render_child_content_of_blocks_if_multiple_child_blocks_given
126
+ template = Liquid::Template.parse %{
127
+ {% extends 'complex' %}
128
+
129
+ {% block thing %}
130
+ booyeah
131
+ {% endblock %}
132
+
133
+ {% block another %}
134
+ blurb
135
+ {% endblock %}
136
+ }
137
+
138
+ assert_match /booyeah/, template.render
139
+ assert_match /blurb/, template.render
140
+ end
141
+
142
+ def test_remember_context_of_child_template
143
+ template = Liquid::Template.parse %{
144
+ {% extends 'complex' %}
145
+
146
+ {% block thing %}
147
+ booyeah
148
+ {% endblock %}
149
+
150
+ {% block another %}
151
+ {{ a }}
152
+ {% endblock %}
153
+ }
154
+
155
+ res = template.render 'a' => 1234
156
+
157
+ assert_match /booyeah/, res
158
+ assert_match /1234/, res
159
+ end
160
+
161
+ def test_work_with_nested_templates
162
+ template = Liquid::Template.parse %{
163
+ {% extends 'nested' %}
164
+
165
+ {% block thing %}
166
+ booyeah
167
+ {% endblock %}
168
+ }
169
+
170
+ res = template.render 'a' => 1234
171
+
172
+ assert_match /booyeah/, res
173
+ assert_match /from nested/, res
174
+ end
175
+
176
+ def test_work_with_nested_templates_if_middle_template_skips_a_block
177
+ template = Liquid::Template.parse %{
178
+ {% extends 'nested_more' %}
179
+
180
+ {% block another %}
181
+ win
182
+ {% endblock %}
183
+ }
184
+
185
+ res = template.render
186
+
187
+ assert_match /win/, res
188
+ assert_no_match /bum/, res
189
+ end
190
+
191
+ def test_render_parent_for_block_super
192
+ template = Liquid::Template.parse %{
193
+ {% extends 'complex' %}
194
+
195
+ {% block thing %}
196
+ {{ block.super }}
197
+ {% endblock %}
198
+ }
199
+
200
+ res = template.render 'a' => 1234
201
+
202
+ assert_match /rarrgh/, res
203
+ end
204
+
205
+ def test_render_separate_block_content_for_blocks_with_identical_first_or_last_letters
206
+ template = Liquid::Template.parse %{
207
+ {% extends 'similar' %}
208
+
209
+ {% block aralia %}
210
+ spikenard
211
+ {% endblock %}
212
+
213
+ {% block azalea %}
214
+ tsutsuji
215
+ {% endblock %}
216
+ }
217
+
218
+ res = template.render
219
+
220
+ assert_match /spikenard/, res
221
+ assert_match /tsutsuji/, res
222
+ end
223
+
224
+ def test_render_deep_blocks
225
+ template = Liquid::Template.parse %{
226
+ {% extends 'deep' %}
227
+ }
228
+
229
+ res = template.render
230
+
231
+ assert_match /three/, res
232
+ end
233
+
234
+ def test_render_deep_blocks_override_inner_blocks
235
+ template = Liquid::Template.parse %{
236
+ {% extends 'deep' %}
237
+
238
+ {% block two %}extra {{ block.super }}{% endblock %}
239
+ {% block three %}override{% endblock %}
240
+ }
241
+
242
+ res = template.render
243
+
244
+ assert_match /one/, res
245
+ assert_match /two/, res
246
+ assert_match /extra/, res
247
+ assert_match /override/, res
248
+ assert_no_match /three/, res
249
+ end
250
+
251
+ def test_render_deep_blocks_hide_child_blocks_if_parent_empty
252
+ template = Liquid::Template.parse %{
253
+ {% extends 'nested_deep' %}
254
+
255
+ {% block two %}{% endblock %}
256
+ {% block three %}hidden{% endblock %}
257
+ }
258
+
259
+ res = template.render
260
+
261
+ assert_no_match /two/, res
262
+ assert_no_match /hidden/, res
263
+ assert_no_match /four/, res
264
+ end
265
+
266
+ def test_render_blocks_ruby_name
267
+ template = Liquid::Template.parse %{
268
+ {% extends 'ruby' %}
269
+
270
+ {% block test %}quiet{% endblock %}
271
+ {% block test! %}loud{% endblock %}
272
+ }
273
+
274
+ res = template.render
275
+
276
+ assert_match /quiet/, res
277
+ assert_match /loud/, res
278
+ end
279
+
280
+ def test_render_context_variable
281
+ template = Liquid::Template.parse %{
282
+ {% assign var = "context" %}
283
+ {% include 'context_variable' with var %}
284
+ }
285
+
286
+ res = template.render
287
+
288
+ assert_match /context/, res
289
+ end
290
+
291
+ def test_render_context_variable_extended
292
+ template = Liquid::Template.parse %{
293
+ {% assign var = "context" %}
294
+ {% include 'context_variable_extended' with var %}
295
+ }
296
+
297
+ res = template.render
298
+
299
+ assert_match /1context/, res
300
+ assert_match /2context/, res
301
+ end
302
+
303
+ def test_render_context_variable_extended_preserve_existing
304
+ template = Liquid::Template.parse %{
305
+ {% assign context_variable = "preserve" %}
306
+ {% assign var = "pass" %}
307
+ {% include 'context_variable_extended' with var %}
308
+ }
309
+
310
+ res = template.render
311
+
312
+ assert_match /1preserve/, res
313
+ assert_match /2pass/, res
314
+ end
315
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liquid4-blocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Dan Webb
8
+ - Silas Sewell
9
+ - Justin Locsei
10
+ - Rafał Mikołajun
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2016-04-18 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: liquid
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: 4.0.0.rc2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 4.0.0.rc2
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.0.0
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ - !ruby/object:Gem::Dependency
45
+ name: rake
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: test-unit
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ description: Django-style template inheritance for Liquid
73
+ email:
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - LICENSE
79
+ - lib/liquid_blocks.rb
80
+ - lib/liquid_blocks/block.rb
81
+ - lib/liquid_blocks/extends.rb
82
+ - lib/liquid_blocks/version.rb
83
+ - test/test_liquid_blocks.rb
84
+ homepage: https://github.com/mikoweb/liquid4-blocks
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 1.3.6
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.4.5.1
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Liquid Blocks
108
+ test_files:
109
+ - test/test_liquid_blocks.rb