haml 2.0.10 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/.yardopts +5 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +347 -0
  4. data/Rakefile +124 -19
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -0
  7. data/extra/haml-mode.el +397 -78
  8. data/extra/sass-mode.el +148 -36
  9. data/extra/update_watch.rb +13 -0
  10. data/lib/haml.rb +15 -993
  11. data/lib/haml/buffer.rb +131 -84
  12. data/lib/haml/engine.rb +129 -97
  13. data/lib/haml/error.rb +7 -7
  14. data/lib/haml/exec.rb +127 -42
  15. data/lib/haml/filters.rb +107 -42
  16. data/lib/haml/helpers.rb +210 -156
  17. data/lib/haml/helpers/action_view_extensions.rb +34 -39
  18. data/lib/haml/helpers/action_view_mods.rb +132 -139
  19. data/lib/haml/html.rb +77 -65
  20. data/lib/haml/precompiler.rb +404 -213
  21. data/lib/haml/shared.rb +78 -0
  22. data/lib/haml/template.rb +14 -14
  23. data/lib/haml/template/patch.rb +2 -2
  24. data/lib/haml/template/plugin.rb +2 -3
  25. data/lib/haml/util.rb +211 -6
  26. data/lib/haml/version.rb +30 -13
  27. data/lib/sass.rb +7 -856
  28. data/lib/sass/css.rb +169 -161
  29. data/lib/sass/engine.rb +344 -328
  30. data/lib/sass/environment.rb +79 -0
  31. data/lib/sass/error.rb +33 -11
  32. data/lib/sass/files.rb +139 -0
  33. data/lib/sass/plugin.rb +160 -117
  34. data/lib/sass/plugin/merb.rb +7 -6
  35. data/lib/sass/plugin/rails.rb +5 -6
  36. data/lib/sass/repl.rb +58 -0
  37. data/lib/sass/script.rb +59 -0
  38. data/lib/sass/script/bool.rb +17 -0
  39. data/lib/sass/script/color.rb +183 -0
  40. data/lib/sass/script/funcall.rb +50 -0
  41. data/lib/sass/script/functions.rb +198 -0
  42. data/lib/sass/script/lexer.rb +178 -0
  43. data/lib/sass/script/literal.rb +177 -0
  44. data/lib/sass/script/node.rb +14 -0
  45. data/lib/sass/script/number.rb +381 -0
  46. data/lib/sass/script/operation.rb +45 -0
  47. data/lib/sass/script/parser.rb +172 -0
  48. data/lib/sass/script/string.rb +12 -0
  49. data/lib/sass/script/unary_operation.rb +34 -0
  50. data/lib/sass/script/variable.rb +31 -0
  51. data/lib/sass/tree/comment_node.rb +73 -10
  52. data/lib/sass/tree/debug_node.rb +30 -0
  53. data/lib/sass/tree/directive_node.rb +42 -17
  54. data/lib/sass/tree/file_node.rb +41 -0
  55. data/lib/sass/tree/for_node.rb +48 -0
  56. data/lib/sass/tree/if_node.rb +54 -0
  57. data/lib/sass/tree/mixin_def_node.rb +29 -0
  58. data/lib/sass/tree/mixin_node.rb +48 -0
  59. data/lib/sass/tree/node.rb +214 -11
  60. data/lib/sass/tree/prop_node.rb +109 -0
  61. data/lib/sass/tree/rule_node.rb +178 -51
  62. data/lib/sass/tree/variable_node.rb +34 -0
  63. data/lib/sass/tree/while_node.rb +31 -0
  64. data/test/haml/engine_test.rb +331 -36
  65. data/test/haml/helper_test.rb +12 -1
  66. data/test/haml/results/content_for_layout.xhtml +0 -3
  67. data/test/haml/results/filters.xhtml +2 -0
  68. data/test/haml/results/list.xhtml +1 -1
  69. data/test/haml/template_test.rb +7 -2
  70. data/test/haml/templates/content_for_layout.haml +0 -2
  71. data/test/haml/templates/list.haml +1 -1
  72. data/test/haml/util_test.rb +92 -0
  73. data/test/sass/css2sass_test.rb +69 -24
  74. data/test/sass/engine_test.rb +586 -64
  75. data/test/sass/functions_test.rb +125 -0
  76. data/test/sass/more_results/more1.css +9 -0
  77. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  78. data/test/sass/more_results/more_import.css +29 -0
  79. data/test/sass/more_templates/_more_partial.sass +2 -0
  80. data/test/sass/more_templates/more1.sass +23 -0
  81. data/test/sass/more_templates/more_import.sass +11 -0
  82. data/test/sass/plugin_test.rb +81 -28
  83. data/test/sass/results/line_numbers.css +49 -0
  84. data/test/sass/results/{constants.css → script.css} +4 -4
  85. data/test/sass/results/subdir/subdir.css +2 -0
  86. data/test/sass/results/units.css +11 -0
  87. data/test/sass/script_test.rb +258 -0
  88. data/test/sass/templates/import.sass +1 -1
  89. data/test/sass/templates/importee.sass +7 -2
  90. data/test/sass/templates/line_numbers.sass +13 -0
  91. data/test/sass/templates/{constants.sass → script.sass} +11 -10
  92. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  93. data/test/sass/templates/subdir/subdir.sass +2 -2
  94. data/test/sass/templates/units.sass +11 -0
  95. data/test/test_helper.rb +14 -0
  96. metadata +77 -19
  97. data/FAQ +0 -138
  98. data/README.rdoc +0 -319
  99. data/lib/sass/constant.rb +0 -216
  100. data/lib/sass/constant/color.rb +0 -101
  101. data/lib/sass/constant/literal.rb +0 -54
  102. data/lib/sass/constant/nil.rb +0 -9
  103. data/lib/sass/constant/number.rb +0 -87
  104. data/lib/sass/constant/operation.rb +0 -30
  105. data/lib/sass/constant/string.rb +0 -22
  106. data/lib/sass/tree/attr_node.rb +0 -57
  107. data/lib/sass/tree/value_node.rb +0 -20
@@ -0,0 +1,45 @@
1
+ require 'set'
2
+ require 'sass/script/string'
3
+ require 'sass/script/number'
4
+ require 'sass/script/color'
5
+ require 'sass/script/functions'
6
+ require 'sass/script/unary_operation'
7
+
8
+ module Sass::Script
9
+ # A SassScript parse node representing a binary operation,
10
+ # such as `!a + !b` or `"foo" + 1`.
11
+ class Operation < Node
12
+ # @param operand1 [Script::Node] The parse-tree node
13
+ # for the right-hand side of the operator
14
+ # @param operand2 [Script::Node] The parse-tree node
15
+ # for the left-hand side of the operator
16
+ # @param operator [Symbol] The operator to perform.
17
+ # This should be one of the binary operator names in {Lexer::OPERATORS}
18
+ def initialize(operand1, operand2, operator)
19
+ @operand1 = operand1
20
+ @operand2 = operand2
21
+ @operator = operator
22
+ end
23
+
24
+ # @return [String] A human-readable s-expression representation of the operation
25
+ def inspect
26
+ "(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
27
+ end
28
+
29
+ # Evaluates the operation.
30
+ #
31
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
32
+ # @return [Literal] The SassScript object that is the value of the operation
33
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operands
34
+ def perform(environment)
35
+ literal1 = @operand1.perform(environment)
36
+ literal2 = @operand2.perform(environment)
37
+ begin
38
+ literal1.send(@operator, literal2)
39
+ rescue NoMethodError => e
40
+ raise e unless e.name.to_s == @operator.to_s
41
+ raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,172 @@
1
+ require 'sass/script/lexer'
2
+
3
+ module Sass
4
+ module Script
5
+ # The parser for SassScript.
6
+ # It parses a string of code into a tree of {Script::Node}s.
7
+ class Parser
8
+ # @param str [String, StringScanner] The source text to parse
9
+ # @param line [Fixnum] The line on which the SassScript appears.
10
+ # Used for error reporting
11
+ # @param offset [Fixnum] The number of characters in on which the SassScript appears.
12
+ # Used for error reporting
13
+ # @param filename [String] The name of the file in which the SassScript appears.
14
+ # Used for error reporting
15
+ def initialize(str, line, offset, filename = nil)
16
+ @filename = filename
17
+ @lexer = Lexer.new(str, line, offset)
18
+ end
19
+
20
+ # Parses a SassScript expression within an interpolated segment (`#{}`).
21
+ # This means that it stops when it comes across an unmatched `}`,
22
+ # which signals the end of an interpolated segment,
23
+ # it returns rather than throwing an error.
24
+ #
25
+ # @return [Script::Node] The root node of the parse tree
26
+ # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
27
+ def parse_interpolated
28
+ expr = assert_expr :expr
29
+ assert_tok :end_interpolation
30
+ expr
31
+ end
32
+
33
+ # Parses a SassScript expression.
34
+ #
35
+ # @return [Script::Node] The root node of the parse tree
36
+ # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
37
+ def parse
38
+ expr = assert_expr :expr
39
+ raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.") unless @lexer.done?
40
+ expr
41
+ end
42
+
43
+ # Parses a SassScript expression.
44
+ #
45
+ # @overload parse(str, line, offset, filename = nil)
46
+ # @return [Script::Node] The root node of the parse tree
47
+ # @see Parser#initialize
48
+ # @see Parser#parse
49
+ def self.parse(*args)
50
+ new(*args).parse
51
+ end
52
+
53
+ class << self
54
+ private
55
+
56
+ # Defines a simple left-associative production.
57
+ # name is the name of the production,
58
+ # sub is the name of the production beneath it,
59
+ # and ops is a list of operators for this precedence level
60
+ def production(name, sub, *ops)
61
+ class_eval <<RUBY
62
+ def #{name}
63
+ return unless e = #{sub}
64
+ while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
65
+ e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
66
+ end
67
+ e
68
+ end
69
+ RUBY
70
+ end
71
+
72
+ def unary(op, sub)
73
+ class_eval <<RUBY
74
+ def unary_#{op}
75
+ return #{sub} unless try_tok(:#{op})
76
+ UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
77
+ end
78
+ RUBY
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ production :expr, :concat, :comma
85
+
86
+ def concat
87
+ return unless e = or_expr
88
+ while sub = or_expr
89
+ e = Operation.new(e, sub, :concat)
90
+ end
91
+ e
92
+ end
93
+
94
+ production :or_expr, :and_expr, :or
95
+ production :and_expr, :eq_or_neq, :and
96
+ production :eq_or_neq, :relational, :eq, :neq
97
+ production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
98
+ production :plus_or_minus, :times_div_or_mod, :plus, :minus
99
+ production :times_div_or_mod, :unary_minus, :times, :div, :mod
100
+
101
+ unary :minus, :unary_div
102
+ unary :div, :unary_not # For strings, so /foo/bar works
103
+ unary :not, :funcall
104
+
105
+ def funcall
106
+ return paren unless name = try_tok(:ident)
107
+ # An identifier without arguments is just a string
108
+ unless try_tok(:lparen)
109
+ warn(<<END)
110
+ DEPRECATION WARNING:
111
+ On line #{name.line}, character #{name.offset}#{" of '#{@filename}'" if @filename}
112
+ Implicit strings have been deprecated and will be removed in version 2.4.
113
+ '#{name.value}' was not quoted. Please add double quotes (e.g. "#{name.value}").
114
+ END
115
+ Script::String.new(name.value)
116
+ else
117
+ args = arglist || []
118
+ assert_tok(:rparen)
119
+ Script::Funcall.new(name.value, args)
120
+ end
121
+ end
122
+
123
+ def arglist
124
+ return unless e = concat
125
+ return [e] unless try_tok(:comma)
126
+ [e, *arglist]
127
+ end
128
+
129
+ def paren
130
+ return variable unless try_tok(:lparen)
131
+ e = assert_expr(:expr)
132
+ assert_tok(:rparen)
133
+ return e
134
+ end
135
+
136
+ def variable
137
+ return string unless c = try_tok(:const)
138
+ Variable.new(c.value)
139
+ end
140
+
141
+ def string
142
+ return literal unless first = try_tok(:string)
143
+ return first.value unless try_tok(:begin_interpolation)
144
+ mid = parse_interpolated
145
+ last = assert_expr(:string)
146
+ Operation.new(first.value, Operation.new(mid, last, :plus), :plus)
147
+ end
148
+
149
+ def literal
150
+ (t = try_tok(:number, :color, :bool)) && (return t.value)
151
+ end
152
+
153
+ # It would be possible to have unified #assert and #try methods,
154
+ # but detecting the method/token difference turns out to be quite expensive.
155
+
156
+ def assert_expr(name)
157
+ (e = send(name)) && (return e)
158
+ raise Sass::SyntaxError.new("Expected expression, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.")
159
+ end
160
+
161
+ def assert_tok(*names)
162
+ (t = try_tok(*names)) && (return t)
163
+ raise Sass::SyntaxError.new("Expected #{names.join(' or ')} token, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.")
164
+ end
165
+
166
+ def try_tok(*names)
167
+ peeked = @lexer.peek
168
+ peeked && names.include?(peeked.type) && @lexer.next
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,12 @@
1
+ require 'sass/script/literal'
2
+
3
+ module Sass::Script
4
+ # A SassScript object representing a string of text.
5
+ class String < Literal
6
+ # The Ruby value of the string.
7
+ #
8
+ # @return [String]
9
+ attr_reader :value
10
+ alias_method :to_s, :value
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ module Sass::Script
2
+ # A SassScript parse node representing a unary operation,
3
+ # such as `-!b` or `not true`.
4
+ #
5
+ # Currently only `-`, `/`, and `not` are unary operators.
6
+ class UnaryOperation < Node
7
+ # @param operand [Script::Node] The parse-tree node
8
+ # for the object of the operator
9
+ # @param operator [Symbol] The operator to perform
10
+ def initialize(operand, operator)
11
+ @operand = operand
12
+ @operator = operator
13
+ end
14
+
15
+ # @return [String] A human-readable s-expression representation of the operation
16
+ def inspect
17
+ "(#{@operator.inspect} #{@operand.inspect})"
18
+ end
19
+
20
+ # Evaluates the operation.
21
+ #
22
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
23
+ # @return [Literal] The SassScript object that is the value of the operation
24
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operand
25
+ def perform(environment)
26
+ operator = "unary_#{@operator}"
27
+ literal = @operand.perform(environment)
28
+ literal.send(operator)
29
+ rescue NoMethodError => e
30
+ raise e unless e.name.to_s == operator.to_s
31
+ raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module Sass
2
+ module Script
3
+ # A SassScript parse node representing a variable.
4
+ class Variable < Node
5
+ # The name of the variable.
6
+ #
7
+ # @return [String]
8
+ attr_reader :name
9
+
10
+ # @param name [String] See \{#name}
11
+ def initialize(name)
12
+ @name = name
13
+ end
14
+
15
+ # @return [String] A string representation of the variable
16
+ def inspect
17
+ "!#{name}"
18
+ end
19
+
20
+ # Evaluates the variable.
21
+ #
22
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
23
+ # @return [Literal] The SassScript object that is the value of the variable
24
+ # @raise [Sass::SyntaxError] if the variable is undefined
25
+ def perform(environment)
26
+ (val = environment.var(name)) && (return val)
27
+ raise SyntaxError.new("Undefined variable: \"!#{name}\".")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,20 +1,83 @@
1
1
  require 'sass/tree/node'
2
2
 
3
3
  module Sass::Tree
4
- class CommentNode < ValueNode
5
- def initialize(value, style)
6
- super(value[2..-1].strip, style)
4
+ # A static node representing a Sass comment (silent or loud).
5
+ #
6
+ # @see Sass::Tree
7
+ class CommentNode < Node
8
+ # The lines of text nested beneath the comment.
9
+ #
10
+ # @return [Array<Sass::Engine::Line>]
11
+ attr_accessor :lines
12
+
13
+ # The text on the same line as the comment starter.
14
+ #
15
+ # @return [String]
16
+ attr_accessor :value
17
+
18
+ # Whether or not the comment is silent (that is, doesn't output to CSS).
19
+ #
20
+ # @return [Boolean]
21
+ attr_accessor :silent
22
+
23
+ # @param value [String] See \{#value}
24
+ # @param silent [Boolean] See \{#silent}
25
+ def initialize(value, silent)
26
+ @lines = []
27
+ @value = value[2..-1].strip
28
+ @silent = silent
29
+ super()
7
30
  end
8
31
 
9
- def to_s(tabs = 0, parent_name = nil)
10
- return if @style == :compressed
32
+ # Compares the contents of two comments.
33
+ #
34
+ # @param other [Object] The object to compare with
35
+ # @return [Boolean] Whether or not this node and the other object
36
+ # are the same
37
+ def ==(other)
38
+ self.class == other.class && value == other.value && silent == other.silent && lines == other.lines
39
+ end
40
+
41
+ # Computes the CSS for the comment.
42
+ #
43
+ # Returns `nil` if this is a silent comment
44
+ # or the current style doesn't render comments.
45
+ #
46
+ # @overload to_s(tabs = 0)
47
+ # @param tabs [Fixnum] The level of indentation for the CSS
48
+ # @return [String, nil] The resulting CSS
49
+ # @see #invisible?
50
+ def to_s(tabs = 0, _ = nil)
51
+ return if invisible?
52
+
53
+ content = (value.split("\n") + lines.map {|l| l.text})
54
+ content.map! {|l| (l.empty? ? "" : " ") + l}
55
+ content.first.gsub!(/^ /, '')
56
+ content.last.gsub!(%r{ ?\*/ *$}, '')
11
57
 
12
58
  spaces = ' ' * (tabs - 1)
13
- join_string = @style == :compact ? ' ' : "\n#{spaces} * "
14
- str = "#{spaces}/* #{value}"
15
- str << join_string unless children.empty?
16
- str << "#{children.join join_string} */"
17
- str
59
+ spaces + "/* " + content.join(style == :compact ? '' : "\n#{spaces} *") + " */"
60
+ end
61
+
62
+ # Returns `true` if this is a silent comment
63
+ # or the current style doesn't render comments.
64
+ #
65
+ # @return [Boolean]
66
+ def invisible?
67
+ style == :compressed || @silent
68
+ end
69
+
70
+ protected
71
+
72
+ # Removes this node from the tree if it's a silent comment.
73
+ #
74
+ # @param environment [Sass::Environment] The lexical environment containing
75
+ # variable and mixin values
76
+ # @return [Tree::Node, Array<Tree::Node>] The resulting static nodes
77
+ # @see Sass::Tree
78
+ def _perform(environment)
79
+ return [] if @silent
80
+ self
18
81
  end
19
82
  end
20
83
  end
@@ -0,0 +1,30 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing a Sass `@debug` statement.
4
+ #
5
+ # @see Sass::Tree
6
+ class DebugNode < Node
7
+ # @param expr [Script::Node] The expression to print
8
+ def initialize(expr)
9
+ @expr = expr
10
+ super()
11
+ end
12
+
13
+ protected
14
+
15
+ # Prints the expression to STDERR.
16
+ #
17
+ # @param environment [Sass::Environment] The lexical environment containing
18
+ # variable and mixin values
19
+ def _perform(environment)
20
+ res = @expr.perform(environment)
21
+ if filename
22
+ STDERR.puts "#{filename}:#{line} DEBUG: #{res}"
23
+ else
24
+ STDERR.puts "Line #{line} DEBUG: #{res}"
25
+ end
26
+ []
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,44 +1,69 @@
1
- require 'sass/tree/node'
2
- require 'sass/tree/value_node'
3
-
4
1
  module Sass::Tree
5
- class DirectiveNode < ValueNode
2
+ # A static node representing an unproccessed Sass `@`-directive.
3
+ # Directives known to Sass, like `@for` and `@debug`,
4
+ # are handled by their own nodes;
5
+ # only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
6
+ #
7
+ # `@import` is a bit of a weird case;
8
+ # if it's importing a Sass file,
9
+ # it becomes a {FileNode},
10
+ # but if it's importing a plain CSS file,
11
+ # it becomes a {DirectiveNode}.
12
+ #
13
+ # @see Sass::Tree
14
+ class DirectiveNode < Node
15
+ # The text of the directive, `@` and all.
16
+ #
17
+ # @return [String]
18
+ attr_accessor :value
19
+
20
+ # @param value [String] See \{#value}
21
+ def initialize(value)
22
+ @value = value
23
+ super()
24
+ end
25
+
26
+ # Computes the CSS for the directive.
27
+ #
28
+ # @param tabs [Fixnum] The level of indentation for the CSS
29
+ # @return [String] The resulting CSS
6
30
  def to_s(tabs)
7
31
  if children.empty?
8
32
  value + ";"
9
33
  else
10
- result = if @style == :compressed
34
+ result = if style == :compressed
11
35
  "#{value}{"
12
36
  else
13
- "#{' ' * (tabs - 1)}#{value} {" + (@style == :compact ? ' ' : "\n")
37
+ "#{' ' * (tabs - 1)}#{value} {" + (style == :compact ? ' ' : "\n")
14
38
  end
15
- was_attr = false
39
+ was_prop = false
16
40
  first = true
17
41
  children.each do |child|
18
- if @style == :compact
19
- if child.is_a?(AttrNode)
20
- result << "#{child.to_s(first || was_attr ? 1 : tabs + 1)} "
42
+ next if child.invisible?
43
+ if style == :compact
44
+ if child.is_a?(PropNode)
45
+ result << "#{child.to_s(first || was_prop ? 1 : tabs + 1)} "
21
46
  else
22
- if was_attr
47
+ if was_prop
23
48
  result[-1] = "\n"
24
49
  end
25
50
  rendered = child.to_s(tabs + 1)
26
51
  rendered.lstrip! if first
27
52
  result << rendered
28
53
  end
29
- was_attr = child.is_a?(AttrNode)
54
+ was_prop = child.is_a?(PropNode)
30
55
  first = false
31
- elsif @style == :compressed
32
- result << (was_attr ? ";#{child.to_s(1)}" : child.to_s(1))
33
- was_attr = child.is_a?(AttrNode)
56
+ elsif style == :compressed
57
+ result << (was_prop ? ";#{child.to_s(1)}" : child.to_s(1))
58
+ was_prop = child.is_a?(PropNode)
34
59
  else
35
60
  result << child.to_s(tabs + 1) + "\n"
36
61
  end
37
62
  end
38
- result.rstrip + if @style == :compressed
63
+ result.rstrip + if style == :compressed
39
64
  "}"
40
65
  else
41
- (@style == :expanded ? "\n" : " ") + "}\n"
66
+ (style == :expanded ? "\n" : " ") + "}\n"
42
67
  end
43
68
  end
44
69
  end