bade 0.1.3
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 +7 -0
- data/Bade.gemspec +26 -0
- data/Gemfile +4 -0
- data/README.md +49 -0
- data/lib/bade/document.rb +33 -0
- data/lib/bade/generator/html_generator.rb +80 -0
- data/lib/bade/generator/ruby_generator.rb +336 -0
- data/lib/bade/generator.rb +16 -0
- data/lib/bade/node/doctype_node.rb +21 -0
- data/lib/bade/node/key_value_node.rb +10 -0
- data/lib/bade/node/mixin_node.rb +69 -0
- data/lib/bade/node/tag_node.rb +29 -0
- data/lib/bade/node.rb +116 -0
- data/lib/bade/parser.rb +652 -0
- data/lib/bade/renderer.rb +148 -0
- data/lib/bade/ruby_extensions/object.rb +11 -0
- data/lib/bade/ruby_extensions/string.rb +74 -0
- data/lib/bade/runtime/block.rb +40 -0
- data/lib/bade/runtime/render_binding.rb +53 -0
- data/lib/bade/runtime.rb +7 -0
- data/lib/bade/version.rb +4 -0
- data/lib/bade.rb +8 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77771024dceda938d8320695d2acbd5731588cbb
|
4
|
+
data.tar.gz: 6c0b8f8c0f7cb9d17053599a754f0d28e25ea11c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 260bd643d97374af1e3209d25d3ad3c98090808191c8f13482e910e10e707f5fa41cf8160007325bf02066eeb0af01ccd603f93a84746bb6f26a8b95286c4259
|
7
|
+
data.tar.gz: 6d9fbcf48c636f38c608bbb37b7096c2e2aadfc208d4bf749fc9e74c87fc2314765752b93fb7124a4a8d663de82749e310bfb12afae38052b26ed4353ef1c78d
|
data/Bade.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'bade/version'
|
7
|
+
|
8
|
+
|
9
|
+
Gem::Specification.new do |spec|
|
10
|
+
spec.name = 'bade'
|
11
|
+
spec.version = Bade::VERSION
|
12
|
+
spec.authors = ['Roman Kříž']
|
13
|
+
spec.email = ['samnung@gmail.com']
|
14
|
+
spec.summary = %q{Minimalistic template engine for Ruby.}
|
15
|
+
spec.homepage = 'https://github.com/samnung/bade'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w(Bade.gemspec Gemfile README.md)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.4'
|
26
|
+
end
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
# Bade
|
3
|
+
|
4
|
+
[](https://semaphoreci.com/samnung/bade)
|
5
|
+
|
6
|
+
Minimalistic template engine written in Ruby for Ruby. Developed mainly to help with creating e-books. Highly influenced by [Jade](http://jade-lang.com) and [Slim](http://slim-lang.com).
|
7
|
+
|
8
|
+
The language is in development state, breaking changes can be made in future. Current version supports minimal list of features to make it working in production for my purposes.
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'bade'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install bade
|
26
|
+
|
27
|
+
|
28
|
+
## Development
|
29
|
+
|
30
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
|
31
|
+
|
32
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
33
|
+
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/samnung/bade. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
38
|
+
|
39
|
+
|
40
|
+
## TODO
|
41
|
+
|
42
|
+
- [ ] move all cards from Trello to GitHub
|
43
|
+
- [ ] create documentation about syntax
|
44
|
+
- [ ] create several examples
|
45
|
+
|
46
|
+
|
47
|
+
## License
|
48
|
+
|
49
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
require_relative 'node'
|
3
|
+
|
4
|
+
|
5
|
+
module Bade
|
6
|
+
class Document
|
7
|
+
# Root node of this document
|
8
|
+
#
|
9
|
+
# @return [Bade::Node]
|
10
|
+
#
|
11
|
+
attr_accessor :root
|
12
|
+
|
13
|
+
# Path to this document, but only if it is defined from file
|
14
|
+
#
|
15
|
+
# @return [String, nil]
|
16
|
+
#
|
17
|
+
attr_reader :file_path
|
18
|
+
|
19
|
+
# @return [Array<Bade::Document>]
|
20
|
+
#
|
21
|
+
attr_reader :sub_documents
|
22
|
+
|
23
|
+
# @param root [Bade::Node]
|
24
|
+
#
|
25
|
+
def initialize(root: nil, file_path: nil)
|
26
|
+
@root = root || Node.new(:root)
|
27
|
+
@root.parent = self
|
28
|
+
|
29
|
+
@file_path = file_path
|
30
|
+
@sub_documents = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative '../generator'
|
2
|
+
|
3
|
+
module Bade
|
4
|
+
class HTMLGenerator < Generator
|
5
|
+
|
6
|
+
# @param [Node] root node
|
7
|
+
# @return [String]
|
8
|
+
#
|
9
|
+
def self.node_to_lambda(root, new_line: "\n", indent: "\t")
|
10
|
+
str = node_to_html_array(root, new_line: new_line, indent: indent).join
|
11
|
+
|
12
|
+
lambda {
|
13
|
+
str
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# @param [Node] root node
|
21
|
+
# @return [Array<String>]
|
22
|
+
#
|
23
|
+
def self.node_to_html_array(root, new_line: "\n", indent: "\t", indent_level: 0)
|
24
|
+
unless root.kind_of? Node
|
25
|
+
return [ root.inspect ]
|
26
|
+
end
|
27
|
+
|
28
|
+
buff = []
|
29
|
+
|
30
|
+
if indent_level > 0 and not indent.empty?
|
31
|
+
buff << indent * indent_level
|
32
|
+
end
|
33
|
+
|
34
|
+
append_childrens = lambda { |indent_plus|
|
35
|
+
root.childrens.each { |node|
|
36
|
+
buff += node_to_html_array(node, new_line: new_line, indent: indent, indent_level: indent_level+indent_plus)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
case root.type
|
42
|
+
when :root
|
43
|
+
append_childrens.call(0)
|
44
|
+
|
45
|
+
when :text
|
46
|
+
buff << root.data
|
47
|
+
|
48
|
+
when :tag
|
49
|
+
attributes = formatted_attributes root
|
50
|
+
|
51
|
+
if attributes.length > 0
|
52
|
+
buff << "<#{root.data} #{attributes}>" + new_line
|
53
|
+
else
|
54
|
+
buff << "<#{root.data}>" + new_line
|
55
|
+
end
|
56
|
+
|
57
|
+
append_childrens.call(1)
|
58
|
+
|
59
|
+
buff << "</#{root.data}>" + new_line
|
60
|
+
end
|
61
|
+
|
62
|
+
buff
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [Node] tag_node
|
66
|
+
#
|
67
|
+
# @return [String] formatted attributes
|
68
|
+
#
|
69
|
+
def self.formatted_attributes(tag_node)
|
70
|
+
|
71
|
+
attributes = tag_node.childrens.select { |child|
|
72
|
+
child.type == :tag_attribute
|
73
|
+
}.map { |attr|
|
74
|
+
"#{attr.data}=\"#{attr.childrens.first.data}\""
|
75
|
+
}
|
76
|
+
|
77
|
+
attributes.join ' '
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
require_relative '../generator'
|
2
|
+
require_relative '../runtime'
|
3
|
+
require_relative '../document'
|
4
|
+
|
5
|
+
module Bade
|
6
|
+
class RubyGenerator < Generator
|
7
|
+
|
8
|
+
BUFF_NAME = '__buff'
|
9
|
+
MIXINS_NAME = '__mixins'
|
10
|
+
START_STRING = "
|
11
|
+
lambda {
|
12
|
+
#{BUFF_NAME} = []
|
13
|
+
#{MIXINS_NAME} = Hash.new { |hash, key| raise \"Undefined mixin '\#{key}'\" }
|
14
|
+
"
|
15
|
+
|
16
|
+
END_STRING = "
|
17
|
+
#{BUFF_NAME}.join
|
18
|
+
}"
|
19
|
+
|
20
|
+
# @param [Document] document
|
21
|
+
#
|
22
|
+
# @return [Proc]
|
23
|
+
#
|
24
|
+
def self.document_to_lambda(document, new_line: "\n", indent: "\t", filename: '')
|
25
|
+
generator = self.new(new_line, indent)
|
26
|
+
generator.generate_lambda(document, filename)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Document] document
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
#
|
33
|
+
def self.document_to_lambda_string(document, new_line: "\n", indent: "\t", filename: '')
|
34
|
+
generator = self.new(new_line, indent)
|
35
|
+
generator.generate_lambda_string(document)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
# @param [String] new_line_string
|
41
|
+
# @param [String] indent_string
|
42
|
+
#
|
43
|
+
def initialize(new_line_string, indent_string)
|
44
|
+
@new_line_string = new_line_string
|
45
|
+
@indent_string = indent_string
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Document] document
|
49
|
+
# @param [String] filename
|
50
|
+
#
|
51
|
+
def generate_lambda(document, filename)
|
52
|
+
eval(generate_lambda_string(document), nil, filename)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [Document] document
|
56
|
+
#
|
57
|
+
# @return [String] string to parse with Ruby
|
58
|
+
#
|
59
|
+
def generate_lambda_string(document)
|
60
|
+
@buff = []
|
61
|
+
@indent = 0
|
62
|
+
@code_indent = 1
|
63
|
+
|
64
|
+
@buff << START_STRING
|
65
|
+
|
66
|
+
visit_document(document)
|
67
|
+
|
68
|
+
@buff << END_STRING
|
69
|
+
|
70
|
+
@buff.join("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param [String] text
|
74
|
+
#
|
75
|
+
def buff_print_text(text, indent: false, new_line: false)
|
76
|
+
indent_text = if indent
|
77
|
+
@indent_string * @indent
|
78
|
+
else
|
79
|
+
''
|
80
|
+
end
|
81
|
+
|
82
|
+
prepended_text = indent_text + text
|
83
|
+
|
84
|
+
if prepended_text.length > 0
|
85
|
+
buff_code %Q{#{BUFF_NAME} << %Q{#{prepended_text}}}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def buff_code(text)
|
90
|
+
@buff << "\t" * @code_indent + text
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# @param document [Bade::Document]
|
95
|
+
#
|
96
|
+
def visit_document(document)
|
97
|
+
document.sub_documents.each do |sub_document|
|
98
|
+
visit_document(sub_document)
|
99
|
+
end
|
100
|
+
|
101
|
+
buff_code("# ----- start file #{document.file_path}") unless document.file_path.nil?
|
102
|
+
visit_node(document.root)
|
103
|
+
buff_code("# ----- end file #{document.file_path}") unless document.file_path.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
# @param current_node [Node]
|
107
|
+
#
|
108
|
+
def visit_node_childrens(current_node)
|
109
|
+
visit_nodes(current_node.childrens)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param nodes [Array<Node>]
|
113
|
+
#
|
114
|
+
def visit_nodes(nodes)
|
115
|
+
nodes.each { |node|
|
116
|
+
visit_node(node)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param current_node [Node]
|
121
|
+
#
|
122
|
+
def visit_node(current_node)
|
123
|
+
case current_node.type
|
124
|
+
when :root
|
125
|
+
visit_node_childrens(current_node)
|
126
|
+
|
127
|
+
when :text
|
128
|
+
buff_print_text current_node.data
|
129
|
+
|
130
|
+
when :tag
|
131
|
+
visit_tag(current_node)
|
132
|
+
|
133
|
+
when :ruby_code
|
134
|
+
buff_code current_node.data
|
135
|
+
|
136
|
+
when :html_comment
|
137
|
+
buff_print_text '<!-- '
|
138
|
+
visit_node_childrens(current_node)
|
139
|
+
buff_print_text ' -->'
|
140
|
+
|
141
|
+
when :comment
|
142
|
+
comment_text = current_node.childrens.select { |node|
|
143
|
+
!node.data.nil?
|
144
|
+
}.map { |node|
|
145
|
+
node.data
|
146
|
+
}.join(@new_line_string + '#')
|
147
|
+
|
148
|
+
buff_code '#' + comment_text
|
149
|
+
|
150
|
+
when :doctype
|
151
|
+
buff_print_text current_node.xml_output
|
152
|
+
|
153
|
+
when :mixin_declaration
|
154
|
+
params = formatted_mixin_params(current_node)
|
155
|
+
buff_code "#{MIXINS_NAME}['#{current_node.data}'] = lambda { |#{params}|"
|
156
|
+
|
157
|
+
indent {
|
158
|
+
blocks_name_declaration(current_node)
|
159
|
+
visit_node_childrens(current_node)
|
160
|
+
}
|
161
|
+
|
162
|
+
buff_code '}'
|
163
|
+
|
164
|
+
when :mixin_call
|
165
|
+
params = formatted_mixin_params(current_node)
|
166
|
+
buff_code "#{MIXINS_NAME}['#{current_node.data}'].call(#{params})"
|
167
|
+
|
168
|
+
when :output
|
169
|
+
data = current_node.data
|
170
|
+
output_code = if current_node.escaped
|
171
|
+
"\#{html_escaped(#{data})}"
|
172
|
+
else
|
173
|
+
"\#{#{data}}"
|
174
|
+
end
|
175
|
+
buff_print_text output_code
|
176
|
+
|
177
|
+
when :newline
|
178
|
+
buff_print_text @new_line_string if @new_line_string.length > 0
|
179
|
+
|
180
|
+
when :import
|
181
|
+
# nothing
|
182
|
+
|
183
|
+
else
|
184
|
+
raise "Unknown type #{current_node.type}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# @param [TagNode] current_node
|
189
|
+
#
|
190
|
+
def visit_tag(current_node)
|
191
|
+
attributes = formatted_attributes current_node
|
192
|
+
|
193
|
+
text = "<#{current_node.name}"
|
194
|
+
|
195
|
+
if attributes.length > 0
|
196
|
+
text += "#{attributes}"
|
197
|
+
end
|
198
|
+
|
199
|
+
other_than_new_lines = current_node.childrens.any? { |node|
|
200
|
+
node.type != :newline
|
201
|
+
}
|
202
|
+
|
203
|
+
if other_than_new_lines
|
204
|
+
text += '>'
|
205
|
+
else
|
206
|
+
text += '/>'
|
207
|
+
end
|
208
|
+
|
209
|
+
buff_print_text text, new_line: true, indent: true
|
210
|
+
|
211
|
+
if other_than_new_lines
|
212
|
+
last_node = current_node.childrens.last
|
213
|
+
is_last_newline = !last_node.nil? && last_node.type == :newline
|
214
|
+
nodes = if is_last_newline
|
215
|
+
current_node.childrens[0...-1]
|
216
|
+
else
|
217
|
+
current_node.childrens
|
218
|
+
end
|
219
|
+
|
220
|
+
indent do
|
221
|
+
visit_nodes(nodes)
|
222
|
+
end
|
223
|
+
|
224
|
+
buff_print_text "</#{current_node.name}>", new_line: true, indent: true
|
225
|
+
|
226
|
+
# print new line after the tag
|
227
|
+
visit_node(last_node) if is_last_newline
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# @param [TagNode] tag_node
|
232
|
+
#
|
233
|
+
# @return [String] formatted attributes
|
234
|
+
#
|
235
|
+
def formatted_attributes(tag_node)
|
236
|
+
all_attributes = Hash.new { |hash, key| hash[key] = [] }
|
237
|
+
xml_attributes = []
|
238
|
+
|
239
|
+
tag_node.attributes.each do |attr|
|
240
|
+
unless all_attributes.include?(attr.name)
|
241
|
+
xml_attributes << attr.name
|
242
|
+
end
|
243
|
+
|
244
|
+
all_attributes[attr.name] << attr.value
|
245
|
+
end
|
246
|
+
|
247
|
+
xml_attributes.map do |attr_name|
|
248
|
+
joined = all_attributes[attr_name].join('), (')
|
249
|
+
%Q{\#{tag_render_attribute('#{attr_name}', (#{joined}))}}
|
250
|
+
end.join
|
251
|
+
end
|
252
|
+
|
253
|
+
def indent(plus = 1)
|
254
|
+
@code_indent += plus
|
255
|
+
yield
|
256
|
+
@code_indent -= plus
|
257
|
+
end
|
258
|
+
|
259
|
+
# @param [MixinCommonNode] mixin_node
|
260
|
+
#
|
261
|
+
# @return [String] formatted params
|
262
|
+
#
|
263
|
+
def formatted_mixin_params(mixin_node)
|
264
|
+
params = mixin_node.params
|
265
|
+
result = []
|
266
|
+
|
267
|
+
|
268
|
+
|
269
|
+
if mixin_node.type == :mixin_call
|
270
|
+
if mixin_node.blocks.length > 0
|
271
|
+
buff_code '__blocks = {}'
|
272
|
+
|
273
|
+
mixin_node.blocks.each { |block|
|
274
|
+
block_name = block.data ? block.data : 'default_block'
|
275
|
+
buff_code "__blocks['#{block_name}'] = __create_block('#{block_name}') do"
|
276
|
+
indent {
|
277
|
+
visit_node_childrens(block)
|
278
|
+
}
|
279
|
+
buff_code 'end'
|
280
|
+
}
|
281
|
+
|
282
|
+
result << '__blocks.dup'
|
283
|
+
else
|
284
|
+
result << '{}'
|
285
|
+
end
|
286
|
+
elsif mixin_node.type == :mixin_declaration
|
287
|
+
result << '__blocks'
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
# normal params
|
292
|
+
result += params.select { |param|
|
293
|
+
param.type == :mixin_param
|
294
|
+
}.map { |param|
|
295
|
+
param.data
|
296
|
+
}
|
297
|
+
|
298
|
+
result += params.select { |param|
|
299
|
+
param.type == :mixin_key_param
|
300
|
+
}.map { |param|
|
301
|
+
"#{param.name}: #{param.value}"
|
302
|
+
}
|
303
|
+
|
304
|
+
result.join(', ')
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
# @param [String] block_name
|
309
|
+
#
|
310
|
+
def block_name_declaration(block_name)
|
311
|
+
buff_code "#{block_name} = __blocks.delete('#{block_name}') { __create_block('#{block_name}') }"
|
312
|
+
end
|
313
|
+
|
314
|
+
# @param [MixinDeclarationNode] mixin_node
|
315
|
+
#
|
316
|
+
def blocks_name_declaration(mixin_node)
|
317
|
+
mixin_node.params.select { |param|
|
318
|
+
param.type == :mixin_block_param
|
319
|
+
}.each { |param|
|
320
|
+
block_name_declaration(param.data)
|
321
|
+
}
|
322
|
+
|
323
|
+
block_name_declaration('default_block')
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
# @param [String] str
|
329
|
+
#
|
330
|
+
# @return [Void]
|
331
|
+
#
|
332
|
+
def escape_double_quotes!(str)
|
333
|
+
str.gsub!(/"/, '\"')
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'node'
|
2
|
+
|
3
|
+
module Bade
|
4
|
+
class Generator
|
5
|
+
require_relative 'generator/html_generator'
|
6
|
+
require_relative 'generator/ruby_generator'
|
7
|
+
|
8
|
+
# @param [Node] root
|
9
|
+
#
|
10
|
+
# @return [Lambda]
|
11
|
+
#
|
12
|
+
def self.node_to_lambda(root, new_line: "\n", indent: "\t", filename: '')
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative '../node'
|
2
|
+
|
3
|
+
|
4
|
+
module Bade
|
5
|
+
class DoctypeNode < Node
|
6
|
+
# @return [String]
|
7
|
+
#
|
8
|
+
def xml_output
|
9
|
+
case self.data
|
10
|
+
when 'xml'
|
11
|
+
'<?xml version="1.0" encoding="utf-8" ?>'
|
12
|
+
|
13
|
+
when 'html'
|
14
|
+
'<!DOCTYPE html>'
|
15
|
+
|
16
|
+
else
|
17
|
+
raise Parser::ParserInternalError 'Unknown doctype type'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../node'
|
2
|
+
|
3
|
+
|
4
|
+
module Bade
|
5
|
+
class MixinCommonNode < Node
|
6
|
+
|
7
|
+
# @return [Array<Node>]
|
8
|
+
#
|
9
|
+
attr_reader :params
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
|
14
|
+
@params = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def << (node)
|
18
|
+
if allowed_parameter_types.include?(node.type)
|
19
|
+
node.parent = self
|
20
|
+
@params << node
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class MixinDeclarationNode < MixinCommonNode
|
28
|
+
def allowed_parameter_types
|
29
|
+
[:mixin_param, :mixin_key_param, :mixin_block_param]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class MixinCallNode < MixinCommonNode
|
34
|
+
attr_reader :blocks
|
35
|
+
|
36
|
+
attr_reader :default_block
|
37
|
+
|
38
|
+
def initialize(*args)
|
39
|
+
super
|
40
|
+
|
41
|
+
@blocks = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def allowed_parameter_types
|
45
|
+
[:mixin_param, :mixin_key_param]
|
46
|
+
end
|
47
|
+
|
48
|
+
def << (node)
|
49
|
+
if allowed_parameter_types.include?(node.type)
|
50
|
+
node.parent = self
|
51
|
+
@params << node
|
52
|
+
elsif node.type == :mixin_block
|
53
|
+
node.parent = self
|
54
|
+
@blocks << node
|
55
|
+
else
|
56
|
+
if @default_block.nil?
|
57
|
+
if node.type == :newline
|
58
|
+
# skip newlines at start
|
59
|
+
return self
|
60
|
+
end
|
61
|
+
|
62
|
+
@default_block = Node.create(:mixin_block, self)
|
63
|
+
end
|
64
|
+
|
65
|
+
@default_block << node
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative '../node'
|
2
|
+
|
3
|
+
|
4
|
+
module Bade
|
5
|
+
class TagNode < Node
|
6
|
+
attr_forw_accessor :name, :data
|
7
|
+
|
8
|
+
# @return [Array<TagAttributeNode>]
|
9
|
+
#
|
10
|
+
attr_reader :attributes
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super(*args)
|
14
|
+
|
15
|
+
@attributes = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Node] node
|
19
|
+
#
|
20
|
+
def << (node)
|
21
|
+
if node.type == :tag_attribute
|
22
|
+
node.parent = self
|
23
|
+
@attributes << node
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|