liquid4-blocks 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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