bade 0.2.4 → 0.3.1

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: 38d9666b68d2c14fcebd6fe3e8f6581980d4e62fc86a62575863b9b9134c0cff
4
- data.tar.gz: e899fadf43703bbb5fc2ab6af91142cb145a149a16e3d3948b7c3be94655b822
3
+ metadata.gz: 9363fa63bbc18e9d9cb2af336438820127d766ef7e5b18306ddc37cdd608adfd
4
+ data.tar.gz: 6a6c86dea0211e852507fa26e7ec2d6d35b5b03469f5c6919455749134f102bd
5
5
  SHA512:
6
- metadata.gz: 309bcca03d9006c8fe4881f3119ff50b0544307205da57599a4892fefd7f95c9373f6e7d11d681ecb77edf90798afbcd3480855a58429e2ed476f238d2aa9493
7
- data.tar.gz: 45daec6d8d39c959a462053cbbc351e35d6a3055b63d9bc2ff64e8f70146e605f9447fabf48d024d682e66c6612d1ee40b866a8673f70dceecbb738f51e0b2ab
6
+ metadata.gz: 37b06b4ef4aa918a88e96c5bfd164ef5d41b9bc0e22949d857b3af143e6dcb462d41f8c10e880f29f4a201e0ca115309e4734e46268f0b1c6fc9f0c7c50802d2
7
+ data.tar.gz: a828c7d9c09450235b719c37381add83dcd0980d256f97b755dd6b5ceb287a1f6baca4f43a04c00523cf45a1d692aac6d7b7abd79f8b5694fe713f3686ceefa0
data/Bade.gemspec CHANGED
@@ -1,11 +1,10 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'bade/version'
7
7
 
8
-
9
8
  Gem::Specification.new do |spec|
10
9
  spec.name = 'bade'
11
10
  spec.version = Bade::VERSION
@@ -14,15 +13,17 @@ Gem::Specification.new do |spec|
14
13
  spec.summary = 'Minimalistic template engine for Ruby.'
15
14
  spec.homepage = 'https://github.com/epuber-io/bade'
16
15
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>= 2.0'
16
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
17
+ spec.required_ruby_version = '>= 2.5'
18
18
 
19
- spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w(Bade.gemspec Gemfile README.md)
19
+ spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w[Bade.gemspec Gemfile README.md]
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ['lib']
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1'
24
+ spec.add_dependency 'psych', '>= 2.2', '< 5.0'
25
+
26
+ spec.add_development_dependency 'fakefs', '~> 1.3'
25
27
  spec.add_development_dependency 'rspec', '~> 3.2'
26
- spec.add_development_dependency 'rake', '~> 11'
27
- spec.add_development_dependency 'rubocop', '~> 0.35'
28
+ spec.add_development_dependency 'rubocop', '~> 1.14'
28
29
  end
data/Gemfile CHANGED
@@ -1,8 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
4
6
 
5
- gem 'coveralls', require: false
6
7
  gem 'benchmark-ips', require: false
7
- gem 'ruby-prof'
8
- gem 'flamegraph'
8
+
9
+ group :dev, optional: true do
10
+ gem 'debase', require: false
11
+ gem 'ruby-debug-ide', require: false
12
+ end
13
+
14
+ group :benchmarks, optional: true do
15
+ gem 'flamegraph'
16
+ gem 'ruby-prof'
17
+ end
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Bade
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://travis-ci.org/epuber-io/bade.svg?branch=master)](https://travis-ci.org/epuber-io/bade) [![Coverage Status](https://coveralls.io/repos/epuber-io/bade/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/bade?branch=master) [![Inline docs](http://inch-ci.org/github/epuber-io/bade.svg?branch=master)](http://inch-ci.org/github/epuber-io/bade)
4
+ [![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://github.com/epuber-io/bade/actions/workflows/tests.yml/badge.svg)](https://github.com/epuber-io/bade/actions) [![Coverage Status](https://coveralls.io/repos/epuber-io/bade/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/bade?branch=master) [![Inline docs](https://inch-ci.org/github/epuber-io/bade.svg?branch=master)](https://inch-ci.org/github/epuber-io/bade)
5
5
 
6
6
 
7
7
  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).
@@ -30,7 +30,7 @@ Or install it yourself as:
30
30
 
31
31
  ## Development
32
32
 
33
- After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
33
+ After checking out the repo, run `bundle install --with dev` to install dependencies. Then, run `rake spec` to run the tests.
34
34
 
35
35
  To install this gem onto your local machine, run `bundle exec rake install`.
36
36
 
@@ -40,7 +40,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
40
40
  Bug reports and pull requests are welcome on GitHub at https://github.com/epuber-io/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.
41
41
 
42
42
 
43
- ## TODO
43
+ ## TODO
44
44
 
45
45
  - [ ] create documentation about syntax
46
46
  - [ ] create several examples
@@ -24,7 +24,7 @@ module Bade
24
24
 
25
25
  # @param root [Bade::Node]
26
26
  #
27
- def initialize(root: Node.new(:root), file_path: nil)
27
+ def initialize(root: Node.new(:root, nil), file_path: nil)
28
28
  @root = root
29
29
 
30
30
  @file_path = file_path.dup.freeze unless file_path.nil?
@@ -22,19 +22,19 @@ module Bade
22
22
 
23
23
  class MixinDeclarationNode < MixinCommonNode
24
24
  def allowed_parameter_types
25
- [:mixin_param, :mixin_key_param, :mixin_block_param]
25
+ %i[mixin_param mixin_key_param mixin_block_param]
26
26
  end
27
27
  end
28
28
 
29
29
  class MixinBlockNode < MixinCommonNode
30
30
  def allowed_parameter_types
31
- [:mixin_param, :mixin_key_param]
31
+ %i[mixin_param mixin_key_param]
32
32
  end
33
33
  end
34
34
 
35
35
  class MixinCallNode < MixinCommonNode
36
36
  def allowed_parameter_types
37
- [:mixin_param, :mixin_key_param]
37
+ %i[mixin_param mixin_key_param]
38
38
  end
39
39
 
40
40
  def blocks
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../ruby2_keywords'
4
+
3
5
  module Bade
4
6
  module AST
5
7
  class StaticTextNode < Node
@@ -11,8 +13,8 @@ module Bade
11
13
  #
12
14
  attr_accessor :escaped
13
15
 
14
- def initialize(*args)
15
- super
16
+ ruby2_keywords def initialize(*args)
17
+ super(*args)
16
18
 
17
19
  @escaped = false
18
20
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../ruby2_keywords'
4
+
3
5
  module Bade
4
6
  module AST
5
7
  class ValueNode < Node
@@ -15,17 +17,26 @@ module Bade
15
17
  #
16
18
  attr_accessor :conditional
17
19
 
18
- def initialize(*args)
19
- super
20
+ # @return [String, nil]
21
+ #
22
+ attr_accessor :default_value
23
+
24
+ ruby2_keywords def initialize(*args)
25
+ super(*args)
20
26
 
21
27
  @escaped = false
22
28
  @conditional = false
29
+ @default_value = nil
23
30
  end
24
31
 
25
32
  # @param [ValueNode] other
26
33
  #
27
34
  def ==(other)
28
- super && value == other.value && escaped == other.escaped && conditional == other.conditional
35
+ super &&
36
+ value == other.value &&
37
+ escaped == other.escaped &&
38
+ conditional == other.conditional &&
39
+ default_value == other.default_value
29
40
  end
30
41
  end
31
42
  end
data/lib/bade/ast/node.rb CHANGED
@@ -10,7 +10,11 @@ module Bade
10
10
  #
11
11
  attr_reader :type
12
12
 
13
- # @return [Array<Bade::Node>]
13
+ # @return [Bade::AST::Node, nil]
14
+ #
15
+ attr_accessor :parent
16
+
17
+ # @return [Array<Bade::AST::Node>]
14
18
  #
15
19
  attr_accessor :children
16
20
 
@@ -20,11 +24,16 @@ module Bade
20
24
  #
21
25
  attr_reader :lineno
22
26
 
23
- def initialize(type, lineno: nil)
27
+ # @return [String] filename
28
+ #
29
+ attr_reader :filename
30
+
31
+ def initialize(type, parent = nil, lineno: nil, filename: nil)
24
32
  @type = type
33
+ @parent = parent
25
34
  @children = []
26
-
27
35
  @lineno = lineno
36
+ @filename = filename
28
37
  end
29
38
 
30
39
  def to_s
@@ -38,12 +38,12 @@ module Bade
38
38
  #
39
39
  # @return [Bade::AST::Node]
40
40
  #
41
- def create(type, lineno)
41
+ def create(type, parent, lineno: nil, filename: nil)
42
42
  klass = registered_types[type]
43
43
 
44
44
  raise ::KeyError, "Undefined node type #{type.inspect}" if klass.nil?
45
45
 
46
- klass.new(type, lineno: lineno)
46
+ klass.new(type, parent, lineno: lineno, filename: filename)
47
47
  end
48
48
  end
49
49
 
@@ -55,6 +55,7 @@ module Bade
55
55
  register_type DoctypeNode, :doctype
56
56
 
57
57
  register_type ValueNode, :import
58
+ register_type ValueNode, :yield
58
59
 
59
60
  # --- Comments
60
61
 
@@ -37,13 +37,13 @@ module Bade
37
37
 
38
38
  children_s = ''
39
39
  if node.children.count > 0
40
- children_s = "\n" + node.children.map { |n| node_to_s(n, level + 1) }.join("\n") + "\n#{indent}"
40
+ children_s = "\n#{node.children.map { |n| node_to_s(n, level + 1) }.join("\n")}\n#{indent}"
41
41
  end
42
42
 
43
43
  other = ''
44
44
 
45
45
  case node
46
- when TagNode
46
+ when TagNode, MixinCommonNode
47
47
  other = node.name
48
48
  when KeyValueNode
49
49
  other = "#{node.name}:#{node.value}"
@@ -56,15 +56,13 @@ module Bade
56
56
  ''
57
57
  end
58
58
  other = "#{escaped_sign}#{node.value}"
59
- when MixinCommonNode
60
- other = node.name
61
59
  when Node
62
60
  # nothing
63
61
  else
64
62
  raise "Unknown node class #{node.class} of type #{node.type} for serializing"
65
63
  end
66
64
 
67
- other = ' ' + other if other && !other.empty?
65
+ other = " #{other}" if other && !other.empty?
68
66
 
69
67
  "#{indent}(#{type_s}#{other}#{children_s})"
70
68
  end
@@ -75,10 +75,9 @@ module Bade
75
75
  end
76
76
 
77
77
  def buff_code(text)
78
- @buff << ' ' * @code_indent + text
78
+ @buff << "#{' ' * @code_indent}#{text}"
79
79
  end
80
80
 
81
-
82
81
  # @param document [Bade::Document]
83
82
  #
84
83
  def visit_document(document)
@@ -87,6 +86,7 @@ module Bade
87
86
  end
88
87
 
89
88
  buff_code("# ----- start file #{document.file_path}") unless document.file_path.nil?
89
+ buff_code "__buffs_push(#{location(filename: document.file_path, lineno: 0, label: '<top>')})"
90
90
 
91
91
  new_root = if @optimize
92
92
  Optimizer.new(document.root).optimize
@@ -116,6 +116,8 @@ module Bade
116
116
  # @param current_node [Node]
117
117
  #
118
118
  def visit_node(current_node)
119
+ update_location_node(current_node)
120
+
119
121
  case current_node.type
120
122
  when :root
121
123
  visit_node_children(current_node)
@@ -135,7 +137,7 @@ module Bade
135
137
  buff_print_text ' -->'
136
138
 
137
139
  when :comment
138
- comment_text = '#' + current_node.children.map(&:value).join("\n#")
140
+ comment_text = "##{current_node.children.map(&:value).join("\n#")}"
139
141
  buff_code(comment_text)
140
142
 
141
143
  when :doctype
@@ -158,8 +160,7 @@ module Bade
158
160
  buff_print_text output_code
159
161
 
160
162
  when :newline
161
- # buff_print_value(NEW_LINE_NAME)
162
-
163
+ # no-op
163
164
  when :import
164
165
  base_path = File.expand_path(current_node.value, File.dirname(@document.file_path))
165
166
  load_path = if base_path.end_with?('.rb') && File.exist?(base_path)
@@ -168,8 +169,11 @@ module Bade
168
169
  "#{base_path}.rb"
169
170
  end
170
171
 
171
- buff_code "load('#{load_path}')" unless load_path.nil?
172
-
172
+ buff_code "__load('#{load_path}')" unless load_path.nil?
173
+ when :yield
174
+ block_name = DEFAULT_BLOCK_NAME
175
+ method = current_node.conditional ? 'call' : 'call!'
176
+ buff_code "#{block_name}.#{method}"
173
177
  else
174
178
  raise "Unknown type #{current_node.type}"
175
179
  end
@@ -269,19 +273,22 @@ module Bade
269
273
  params = mixin_node.params
270
274
  result = []
271
275
 
272
- if mixin_node.type == :mixin_call
276
+ case mixin_node.type
277
+ when :mixin_call
273
278
  blocks = mixin_node.blocks
274
279
 
275
280
  other_children = (mixin_node.children - mixin_node.blocks - mixin_node.params)
276
281
  if other_children.count { |n| n.type != :newline } > 0
277
- def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node.lineno)
282
+ def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node, lineno: mixin_node.lineno)
278
283
  def_block_node.name = DEFAULT_BLOCK_NAME
279
284
  def_block_node.children = other_children
280
285
 
281
286
  blocks << def_block_node
282
287
  end
283
288
 
284
- if !blocks.empty?
289
+ if blocks.empty?
290
+ result << '{}'
291
+ else
285
292
  buff_code '__blocks = {}'
286
293
 
287
294
  blocks.each do |block|
@@ -289,17 +296,18 @@ module Bade
289
296
  end
290
297
 
291
298
  result << '__blocks.dup'
292
- else
293
- result << '{}'
294
299
  end
295
- elsif mixin_node.type == :mixin_decl
300
+ when :mixin_decl
296
301
  result << '__blocks'
297
302
  end
298
303
 
304
+ # positional params
305
+ result += params.select { |n| n.type == :mixin_param }
306
+ .map { |param| param.default_value ? "#{param.value} = #{param.default_value}" : param.value }
299
307
 
300
- # normal params
301
- result += params.select { |n| n.type == :mixin_param }.map(&:value)
302
- result += params.select { |n| n.type == :mixin_key_param }.map { |param| "#{param.name}: #{param.value}" }
308
+ # key-value params
309
+ result += params.select { |n| n.type == :mixin_key_param }
310
+ .map { |param| "#{param.name}: #{param.value}" }
303
311
 
304
312
  result.join(', ')
305
313
  end
@@ -311,14 +319,10 @@ module Bade
311
319
  # @return [nil]
312
320
  #
313
321
  def block_definition(block_node)
314
- buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}') do"
322
+ buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}', #{location_node(block_node)}) do"
315
323
 
316
324
  code_indent do
317
- buff_code '__buffs_push()'
318
-
319
325
  visit_node_children(block_node)
320
-
321
- buff_code '__buffs_pop()'
322
326
  end
323
327
 
324
328
  buff_code 'end'
@@ -352,7 +356,7 @@ module Bade
352
356
  #
353
357
  def visit_block_decl(current_node)
354
358
  params = formatted_mixin_params(current_node)
355
- buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', &lambda { |#{params}|"
359
+ buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', #{location_node(current_node)}, &lambda { |#{params}|"
356
360
 
357
361
  code_indent do
358
362
  blocks_name_declaration(current_node)
@@ -362,8 +366,6 @@ module Bade
362
366
  buff_code '})'
363
367
  end
364
368
 
365
-
366
-
367
369
  # @param [String] str
368
370
  #
369
371
  # @return [Void]
@@ -371,6 +373,54 @@ module Bade
371
373
  def escape_double_quotes!(str)
372
374
  str.gsub!(/"/, '\"')
373
375
  end
376
+
377
+ # @param [Bade::AST::Node]
378
+ # @return [Void]
379
+ def update_location_node(node)
380
+ should_skip = case node.type
381
+ when :code
382
+ value = node.value.strip
383
+
384
+ %w[end else }].include?(value) || value.match(/^when /)
385
+ when :newline
386
+ true
387
+ else
388
+ false
389
+ end
390
+ return if should_skip
391
+ return if node.lineno.nil?
392
+
393
+ buff_code "__update_lineno(#{node.lineno})"
394
+ end
395
+
396
+ # @param [String] filename
397
+ # @param [Fixnum] lineno
398
+ # @param [String] label
399
+ # @return [String]
400
+ def location(filename:, lineno:, label:)
401
+ args = [
402
+ filename ? "path: '#{filename}'" : nil,
403
+ "lineno: #{lineno}",
404
+ "label: '#{label}'",
405
+ ].compact
406
+
407
+ "Location.new(#{args.join(',')})"
408
+ end
409
+
410
+ # @param [Node] node
411
+ # @return [String]
412
+ def location_node(node)
413
+ label = case node.type
414
+ when :mixin_decl
415
+ "+#{node.name}"
416
+ when :mixin_block
417
+ "#{node.name} in +#{node.parent.name}"
418
+ else
419
+ node.name
420
+ end
421
+
422
+ location(filename: node.filename, lineno: node.lineno, label: label)
423
+ end
374
424
  end
375
425
 
376
426
  # backward compatibility
@@ -9,10 +9,10 @@ module Bade
9
9
  NAME_RE_STRING = "(#{WORD_RE}(?:#{WORD_RE}|:|-|_)*)".freeze
10
10
 
11
11
  ATTR_NAME_RE_STRING = "\\A\\s*#{NAME_RE_STRING}".freeze
12
- CODE_ATTR_RE = /#{ATTR_NAME_RE_STRING}\s*&?:\s*/
12
+ CODE_ATTR_RE = /#{ATTR_NAME_RE_STRING}\s*&?:\s*/.freeze
13
13
 
14
- TAG_RE = /\A#{NAME_RE_STRING}/
15
- CLASS_TAG_RE = /\A\.#{NAME_RE_STRING}/
16
- ID_TAG_RE = /\A##{NAME_RE_STRING}/
14
+ TAG_RE = /\A#{NAME_RE_STRING}/.freeze
15
+ CLASS_TAG_RE = /\A\.#{NAME_RE_STRING}/.freeze
16
+ ID_TAG_RE = /\A##{NAME_RE_STRING}/.freeze
17
17
  end
18
18
  end
@@ -6,19 +6,20 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module LineIndicatorRegexps
9
- IMPORT = /\Aimport /
10
- MIXIN_DECL = /\Amixin #{NAME_RE_STRING}/
11
- MIXIN_CALL = /\A\+#{NAME_RE_STRING}/
12
- BLOCK_DECLARATION = /\Ablock #{NAME_RE_STRING}/
13
- HTML_COMMENT = %r{\A//! }
14
- NORMAL_COMMENT = %r{\A//}
15
- TEXT_BLOCK_START = /\A\|( ?)/
16
- INLINE_HTML = /\A</
17
- CODE_BLOCK = /\A-/
18
- OUTPUT_BLOCK = /\A(\??)(&?)=/
19
- DOCTYPE = /\Adoctype\s/i
20
- TAG_CLASS_START_BLOCK = /\A\./
21
- TAG_ID_START_BLOCK = /\A#/
9
+ IMPORT = /\Aimport /.freeze
10
+ YIELD = /\Ayield(!?)/.freeze
11
+ MIXIN_DECL = /\Amixin #{NAME_RE_STRING}/.freeze
12
+ MIXIN_CALL = /\A\+#{NAME_RE_STRING}/.freeze
13
+ BLOCK_DECLARATION = /\Ablock #{NAME_RE_STRING}/.freeze
14
+ HTML_COMMENT = %r{\A//! }.freeze
15
+ NORMAL_COMMENT = %r{\A//}.freeze
16
+ TEXT_BLOCK_START = /\A\|( ?)/.freeze
17
+ INLINE_HTML = /\A</.freeze
18
+ CODE_BLOCK = /\A-/.freeze
19
+ OUTPUT_BLOCK = /\A(\??)(&?)=/.freeze
20
+ DOCTYPE = /\Adoctype\s/i.freeze
21
+ TAG_CLASS_START_BLOCK = /\A\./.freeze
22
+ TAG_ID_START_BLOCK = /\A#/.freeze
22
23
  end
23
24
 
24
25
  def reset(lines = nil, stacks = nil)
@@ -135,6 +136,11 @@ module Bade
135
136
  @line = $'
136
137
  parse_import
137
138
 
139
+ when LineIndicatorRegexps::YIELD
140
+ @line = $'
141
+ node = append_node(:yield, add: true)
142
+ node.conditional = $1.nil?
143
+
138
144
  when LineIndicatorRegexps::MIXIN_DECL
139
145
  # Mixin declaration
140
146
  @line = $'
@@ -195,12 +201,8 @@ module Bade
195
201
  @line = $' if $1
196
202
  parse_tag($&)
197
203
 
198
- when LineIndicatorRegexps::TAG_CLASS_START_BLOCK
199
- # Found class name -> implicit div
200
- parse_tag 'div'
201
-
202
- when LineIndicatorRegexps::TAG_ID_START_BLOCK
203
- # Found id name -> implicit div
204
+ when LineIndicatorRegexps::TAG_CLASS_START_BLOCK, LineIndicatorRegexps::TAG_ID_START_BLOCK
205
+ # Found a class or id selector.
204
206
  parse_tag 'div'
205
207
 
206
208
  else
@@ -6,18 +6,19 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module MixinRegexps
9
- TEXT_START = /\A /
10
- BLOCK_EXPANSION = /\A:\s+/
11
- OUTPUT_CODE = /\A(&?)=/
9
+ TEXT_START = /\A /.freeze
10
+ BLOCK_EXPANSION = /\A:\s+/.freeze
11
+ OUTPUT_CODE = /\A(&?)=/.freeze
12
12
 
13
- PARAMS_END = /\A\s*\)/
13
+ PARAMS_END = /\A\s*\)/.freeze
14
14
 
15
- PARAMS_END_SPACES = /^\s*$/
16
- PARAMS_ARGS_DELIMITER = /\A\s*,/
15
+ PARAMS_END_SPACES = /^\s*$/.freeze
16
+ PARAMS_ARGS_DELIMITER = /\A\s*,/.freeze
17
17
 
18
- PARAMS_PARAM_NAME = /\A\s*#{NAME_RE_STRING}/
19
- PARAMS_BLOCK_NAME = /\A\s*&#{NAME_RE_STRING}/
18
+ PARAMS_PARAM_NAME = /\A\s*#{NAME_RE_STRING}/.freeze
19
+ PARAMS_BLOCK_NAME = /\A\s*&#{NAME_RE_STRING}/.freeze
20
20
  PARAMS_KEY_PARAM_NAME = CODE_ATTR_RE
21
+ PARAMS_PARAM_DEFAULT_START = /\A\s*=/.freeze
21
22
  end
22
23
 
23
24
  def parse_mixin_call(mixin_name)
@@ -64,7 +65,7 @@ module Bade
64
65
  @line = $'
65
66
  attr_node = append_node(:mixin_key_param)
66
67
  attr_node.name = fixed_trailing_colon($1)
67
- attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
68
+ attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG, allow_multiline: true)
68
69
 
69
70
  when MixinRegexps::PARAMS_ARGS_DELIMITER
70
71
  # args delimiter
@@ -83,7 +84,7 @@ module Bade
83
84
 
84
85
  else
85
86
  attr_node = append_node(:mixin_param)
86
- attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
87
+ attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG, allow_multiline: true)
87
88
  end
88
89
  end
89
90
  end
@@ -114,7 +115,12 @@ module Bade
114
115
 
115
116
  when MixinRegexps::PARAMS_PARAM_NAME
116
117
  @line = $'
117
- append_node(:mixin_param, value: $1)
118
+ attr_node = append_node(:mixin_param, value: $1)
119
+
120
+ if @line =~ MixinRegexps::PARAMS_PARAM_DEFAULT_START
121
+ @line = $'
122
+ attr_node.default_value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
123
+ end
118
124
 
119
125
  when MixinRegexps::PARAMS_BLOCK_NAME
120
126
  @line = $'
@@ -6,8 +6,8 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module ParseRubyCodeRegexps
9
- END_NEW_LINE = /\A\s*\n/
10
- END_PARAMS_ARG = /\A\s*[,)]/
9
+ END_NEW_LINE = /\A\s*\n/.freeze
10
+ END_PARAMS_ARG = /\A\s*[,)]/.freeze
11
11
  end
12
12
 
13
13
  # Parse ruby code, ended with outer delimiters
@@ -16,17 +16,27 @@ module Bade
16
16
  #
17
17
  # @return [Void] parsed ruby code
18
18
  #
19
- def parse_ruby_code(outer_delimiters)
19
+ def parse_ruby_code(outer_delimiters, allow_multiline: false)
20
20
  code = String.new
21
21
  end_re = if outer_delimiters.is_a?(Regexp)
22
22
  outer_delimiters
23
23
  else
24
24
  /\A\s*[#{Regexp.escape outer_delimiters.to_s}]/
25
25
  end
26
+
26
27
  delimiters = []
27
28
  string_start_quote_char = nil
28
29
 
29
- until @line.empty? || (delimiters.empty? && @line =~ end_re)
30
+ loop do
31
+ break if !allow_multiline && @line.empty?
32
+ break if allow_multiline && @line.empty? && (@lines && @lines.empty?)
33
+ break if delimiters.empty? && @line =~ end_re
34
+
35
+ if @line.empty? && allow_multiline && !(@lines && @lines.empty?)
36
+ next_line
37
+ code << "\n"
38
+ end
39
+
30
40
  char = @line[0]
31
41
 
32
42
  # backslash escaped delimiter
@@ -70,7 +80,7 @@ module Bade
70
80
  '{' => '}',
71
81
  }.freeze
72
82
 
73
- RUBY_QUOTES = %w(' ").freeze
83
+ RUBY_QUOTES = %w[' "].freeze
74
84
 
75
85
  RUBY_NOT_NESTABLE_DELIMITERS = RUBY_QUOTES
76
86
 
@@ -78,7 +88,7 @@ module Bade
78
88
  RUBY_END_DELIMITERS = (%w(\) ] }) + RUBY_NOT_NESTABLE_DELIMITERS).freeze
79
89
  RUBY_ALL_DELIMITERS = (RUBY_START_DELIMITERS + RUBY_END_DELIMITERS).uniq.freeze
80
90
 
81
- RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]/
82
- RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]/
91
+ RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]/.freeze
92
+ RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]/.freeze
83
93
  end
84
94
  end