wireframe-haml 2.1.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.
- data/README.rdoc +332 -0
- data/VERSION.yml +4 -0
- data/bin/css2sass +7 -0
- data/bin/haml +9 -0
- data/bin/html2haml +7 -0
- data/bin/sass +8 -0
- data/lib/haml/buffer.rb +255 -0
- data/lib/haml/engine.rb +268 -0
- data/lib/haml/error.rb +22 -0
- data/lib/haml/exec.rb +395 -0
- data/lib/haml/filters.rb +276 -0
- data/lib/haml/helpers/action_view_extensions.rb +45 -0
- data/lib/haml/helpers/action_view_mods.rb +181 -0
- data/lib/haml/helpers.rb +468 -0
- data/lib/haml/html.rb +218 -0
- data/lib/haml/precompiler.rb +889 -0
- data/lib/haml/shared.rb +45 -0
- data/lib/haml/template/patch.rb +58 -0
- data/lib/haml/template/plugin.rb +72 -0
- data/lib/haml/template.rb +51 -0
- data/lib/haml/util.rb +77 -0
- data/lib/haml/version.rb +47 -0
- data/lib/haml.rb +1042 -0
- data/lib/sass/css.rb +388 -0
- data/lib/sass/engine.rb +499 -0
- data/lib/sass/environment.rb +33 -0
- data/lib/sass/error.rb +35 -0
- data/lib/sass/plugin/merb.rb +56 -0
- data/lib/sass/plugin/rails.rb +24 -0
- data/lib/sass/plugin.rb +203 -0
- data/lib/sass/repl.rb +51 -0
- data/lib/sass/script/bool.rb +13 -0
- data/lib/sass/script/color.rb +97 -0
- data/lib/sass/script/funcall.rb +28 -0
- data/lib/sass/script/functions.rb +122 -0
- data/lib/sass/script/lexer.rb +152 -0
- data/lib/sass/script/literal.rb +60 -0
- data/lib/sass/script/number.rb +231 -0
- data/lib/sass/script/operation.rb +30 -0
- data/lib/sass/script/parser.rb +142 -0
- data/lib/sass/script/string.rb +42 -0
- data/lib/sass/script/unary_operation.rb +21 -0
- data/lib/sass/script/variable.rb +20 -0
- data/lib/sass/script.rb +38 -0
- data/lib/sass/tree/attr_node.rb +64 -0
- data/lib/sass/tree/comment_node.rb +34 -0
- data/lib/sass/tree/debug_node.rb +22 -0
- data/lib/sass/tree/directive_node.rb +50 -0
- data/lib/sass/tree/file_node.rb +27 -0
- data/lib/sass/tree/for_node.rb +29 -0
- data/lib/sass/tree/if_node.rb +27 -0
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +34 -0
- data/lib/sass/tree/node.rb +99 -0
- data/lib/sass/tree/rule_node.rb +120 -0
- data/lib/sass/tree/variable_node.rb +24 -0
- data/lib/sass/tree/while_node.rb +20 -0
- data/lib/sass.rb +1062 -0
- data/test/benchmark.rb +99 -0
- data/test/haml/engine_test.rb +734 -0
- data/test/haml/helper_test.rb +224 -0
- data/test/haml/html2haml_test.rb +92 -0
- data/test/haml/markaby/standard.mab +52 -0
- data/test/haml/mocks/article.rb +6 -0
- data/test/haml/results/content_for_layout.xhtml +15 -0
- data/test/haml/results/eval_suppressed.xhtml +9 -0
- data/test/haml/results/filters.xhtml +62 -0
- data/test/haml/results/helpers.xhtml +93 -0
- data/test/haml/results/helpful.xhtml +10 -0
- data/test/haml/results/just_stuff.xhtml +68 -0
- data/test/haml/results/list.xhtml +12 -0
- data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
- data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
- data/test/haml/results/original_engine.xhtml +20 -0
- data/test/haml/results/partial_layout.xhtml +5 -0
- data/test/haml/results/partials.xhtml +21 -0
- data/test/haml/results/render_layout.xhtml +3 -0
- data/test/haml/results/silent_script.xhtml +74 -0
- data/test/haml/results/standard.xhtml +42 -0
- data/test/haml/results/tag_parsing.xhtml +23 -0
- data/test/haml/results/very_basic.xhtml +5 -0
- data/test/haml/results/whitespace_handling.xhtml +89 -0
- data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
- data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
- data/test/haml/rhtml/action_view.rhtml +62 -0
- data/test/haml/rhtml/standard.rhtml +54 -0
- data/test/haml/template_test.rb +204 -0
- data/test/haml/templates/_av_partial_1.haml +9 -0
- data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
- data/test/haml/templates/_av_partial_2.haml +5 -0
- data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
- data/test/haml/templates/_layout.erb +3 -0
- data/test/haml/templates/_layout_for_partial.haml +3 -0
- data/test/haml/templates/_partial.haml +8 -0
- data/test/haml/templates/_text_area.haml +3 -0
- data/test/haml/templates/action_view.haml +47 -0
- data/test/haml/templates/action_view_ugly.haml +47 -0
- data/test/haml/templates/breakage.haml +8 -0
- data/test/haml/templates/content_for_layout.haml +10 -0
- data/test/haml/templates/eval_suppressed.haml +11 -0
- data/test/haml/templates/filters.haml +66 -0
- data/test/haml/templates/helpers.haml +95 -0
- data/test/haml/templates/helpful.haml +11 -0
- data/test/haml/templates/just_stuff.haml +83 -0
- data/test/haml/templates/list.haml +12 -0
- data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
- data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
- data/test/haml/templates/original_engine.haml +17 -0
- data/test/haml/templates/partial_layout.haml +3 -0
- data/test/haml/templates/partialize.haml +1 -0
- data/test/haml/templates/partials.haml +12 -0
- data/test/haml/templates/render_layout.haml +2 -0
- data/test/haml/templates/silent_script.haml +40 -0
- data/test/haml/templates/standard.haml +42 -0
- data/test/haml/templates/standard_ugly.haml +1 -0
- data/test/haml/templates/tag_parsing.haml +21 -0
- data/test/haml/templates/very_basic.haml +4 -0
- data/test/haml/templates/whitespace_handling.haml +87 -0
- data/test/linked_rails.rb +12 -0
- data/test/sass/css2sass_test.rb +193 -0
- data/test/sass/engine_test.rb +786 -0
- data/test/sass/functions_test.rb +96 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +208 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +87 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/import.css +29 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/script_test.rb +153 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +309 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/import.sass +11 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/test_helper.rb +21 -0
- metadata +247 -0
data/lib/haml/engine.rb
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
require 'haml/helpers'
|
|
2
|
+
require 'haml/buffer'
|
|
3
|
+
require 'haml/precompiler'
|
|
4
|
+
require 'haml/filters'
|
|
5
|
+
require 'haml/error'
|
|
6
|
+
|
|
7
|
+
module Haml
|
|
8
|
+
# This is the class where all the parsing and processing of the Haml
|
|
9
|
+
# template is done. It can be directly used by the user by creating a
|
|
10
|
+
# new instance and calling <tt>to_html</tt> to render the template. For example:
|
|
11
|
+
#
|
|
12
|
+
# template = File.read('templates/really_cool_template.haml')
|
|
13
|
+
# haml_engine = Haml::Engine.new(template)
|
|
14
|
+
# output = haml_engine.to_html
|
|
15
|
+
# puts output
|
|
16
|
+
class Engine
|
|
17
|
+
include Precompiler
|
|
18
|
+
|
|
19
|
+
# Allow reading and writing of the options hash
|
|
20
|
+
attr :options, true
|
|
21
|
+
|
|
22
|
+
# This string contains the source code that is evaluated
|
|
23
|
+
# to produce the Haml document.
|
|
24
|
+
attr :precompiled, true
|
|
25
|
+
|
|
26
|
+
# A string containing the indentation used for the Haml document.
|
|
27
|
+
# nil if the indentation is ambiguous
|
|
28
|
+
# (for example, for a single-level document).
|
|
29
|
+
attr :indentation, true
|
|
30
|
+
|
|
31
|
+
# True if the format is XHTML
|
|
32
|
+
def xhtml?
|
|
33
|
+
not html?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# True if the format is any flavor of HTML
|
|
37
|
+
def html?
|
|
38
|
+
html4? or html5?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# True if the format is HTML4
|
|
42
|
+
def html4?
|
|
43
|
+
@options[:format] == :html4
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# True if the format is HTML5
|
|
47
|
+
def html5?
|
|
48
|
+
@options[:format] == :html5
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Creates a new instace of Haml::Engine that will compile the given
|
|
52
|
+
# template string when <tt>render</tt> is called.
|
|
53
|
+
# See the Haml module documentation for available options.
|
|
54
|
+
#
|
|
55
|
+
#--
|
|
56
|
+
# When adding options, remember to add information about them
|
|
57
|
+
# to lib/haml.rb!
|
|
58
|
+
#++
|
|
59
|
+
#
|
|
60
|
+
def initialize(template, options = {})
|
|
61
|
+
@options = {
|
|
62
|
+
:suppress_eval => false,
|
|
63
|
+
:attr_wrapper => "'",
|
|
64
|
+
|
|
65
|
+
# Don't forget to update the docs in lib/haml.rb if you update these
|
|
66
|
+
:autoclose => %w[meta img link br hr input area param col base],
|
|
67
|
+
:preserve => %w[textarea pre],
|
|
68
|
+
|
|
69
|
+
:filename => '(haml)',
|
|
70
|
+
:line => 1,
|
|
71
|
+
:ugly => false,
|
|
72
|
+
:format => :xhtml,
|
|
73
|
+
:escape_html => false
|
|
74
|
+
}
|
|
75
|
+
@options.merge! options
|
|
76
|
+
@index = 0
|
|
77
|
+
|
|
78
|
+
unless [:xhtml, :html4, :html5].include?(@options[:format])
|
|
79
|
+
raise Haml::Error, "Invalid format #{@options[:format].inspect}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# :eod is a special end-of-document marker
|
|
83
|
+
@template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod]
|
|
84
|
+
@template_index = 0
|
|
85
|
+
@to_close_stack = []
|
|
86
|
+
@output_tabs = 0
|
|
87
|
+
@template_tabs = 0
|
|
88
|
+
@flat = false
|
|
89
|
+
@newlines = 0
|
|
90
|
+
@precompiled = ''
|
|
91
|
+
@to_merge = []
|
|
92
|
+
@tab_change = 0
|
|
93
|
+
@temp_count = 0
|
|
94
|
+
|
|
95
|
+
if @options[:filters]
|
|
96
|
+
warn <<END
|
|
97
|
+
DEPRECATION WARNING:
|
|
98
|
+
The Haml :filters option is deprecated and will be removed in version 2.2.
|
|
99
|
+
Filters are now automatically registered.
|
|
100
|
+
END
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
precompile
|
|
104
|
+
rescue Haml::Error => e
|
|
105
|
+
e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" if @index
|
|
106
|
+
raise
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Processes the template and returns the result as a string.
|
|
110
|
+
#
|
|
111
|
+
# +scope+ is the context in which the template is evaluated.
|
|
112
|
+
# If it's a Binding or Proc object,
|
|
113
|
+
# Haml uses it as the second argument to Kernel#eval;
|
|
114
|
+
# otherwise, Haml just uses its #instance_eval context.
|
|
115
|
+
#
|
|
116
|
+
# Note that Haml modifies the evaluation context
|
|
117
|
+
# (either the scope object or the "self" object of the scope binding).
|
|
118
|
+
# It extends Haml::Helpers, and various instance variables are set
|
|
119
|
+
# (all prefixed with "haml").
|
|
120
|
+
# For example:
|
|
121
|
+
#
|
|
122
|
+
# s = "foobar"
|
|
123
|
+
# Haml::Engine.new("%p= upcase").render(s) #=> "<p>FOOBAR</p>"
|
|
124
|
+
#
|
|
125
|
+
# # s now extends Haml::Helpers
|
|
126
|
+
# s.responds_to?(:html_attrs) #=> true
|
|
127
|
+
#
|
|
128
|
+
# +locals+ is a hash of local variables to make available to the template.
|
|
129
|
+
# For example:
|
|
130
|
+
#
|
|
131
|
+
# Haml::Engine.new("%p= foo").render(Object.new, :foo => "Hello, world!") #=> "<p>Hello, world!</p>"
|
|
132
|
+
#
|
|
133
|
+
# If a block is passed to render,
|
|
134
|
+
# that block is run when +yield+ is called
|
|
135
|
+
# within the template.
|
|
136
|
+
#
|
|
137
|
+
# Due to some Ruby quirks,
|
|
138
|
+
# if scope is a Binding or Proc object and a block is given,
|
|
139
|
+
# the evaluation context may not be quite what the user expects.
|
|
140
|
+
# In particular, it's equivalent to passing <tt>eval("self", scope)</tt> as scope.
|
|
141
|
+
# This won't have an effect in most cases,
|
|
142
|
+
# but if you're relying on local variables defined in the context of scope,
|
|
143
|
+
# they won't work.
|
|
144
|
+
def render(scope = Object.new, locals = {}, &block)
|
|
145
|
+
buffer = Haml::Buffer.new(scope.instance_variable_get('@haml_buffer'), options_for_buffer)
|
|
146
|
+
|
|
147
|
+
if scope.is_a?(Binding) || scope.is_a?(Proc)
|
|
148
|
+
scope_object = eval("self", scope)
|
|
149
|
+
scope = scope_object.instance_eval{binding} if block_given?
|
|
150
|
+
else
|
|
151
|
+
scope_object = scope
|
|
152
|
+
scope = scope_object.instance_eval{binding}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
set_locals(locals.merge(:_hamlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
|
|
156
|
+
|
|
157
|
+
scope_object.instance_eval do
|
|
158
|
+
extend Haml::Helpers
|
|
159
|
+
@haml_buffer = buffer
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
eval(@precompiled, scope, @options[:filename], @options[:line])
|
|
163
|
+
|
|
164
|
+
# Get rid of the current buffer
|
|
165
|
+
scope_object.instance_eval do
|
|
166
|
+
@haml_buffer = buffer.upper
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
buffer.buffer
|
|
170
|
+
end
|
|
171
|
+
alias_method :to_html, :render
|
|
172
|
+
|
|
173
|
+
# Returns a proc that, when called,
|
|
174
|
+
# renders the template and returns the result as a string.
|
|
175
|
+
#
|
|
176
|
+
# +scope+ works the same as it does for render.
|
|
177
|
+
#
|
|
178
|
+
# The first argument of the returned proc is a hash of local variable names to values.
|
|
179
|
+
# However, due to an unfortunate Ruby quirk,
|
|
180
|
+
# the local variables which can be assigned must be pre-declared.
|
|
181
|
+
# This is done with the +local_names+ argument.
|
|
182
|
+
# For example:
|
|
183
|
+
#
|
|
184
|
+
# # This works
|
|
185
|
+
# Haml::Engine.new("%p= foo").render_proc(Object.new, :foo).call :foo => "Hello!"
|
|
186
|
+
# #=> "<p>Hello!</p>"
|
|
187
|
+
#
|
|
188
|
+
# # This doesn't
|
|
189
|
+
# Haml::Engine.new("%p= foo").render_proc.call :foo => "Hello!"
|
|
190
|
+
# #=> NameError: undefined local variable or method `foo'
|
|
191
|
+
#
|
|
192
|
+
# The proc doesn't take a block;
|
|
193
|
+
# any yields in the template will fail.
|
|
194
|
+
def render_proc(scope = Object.new, *local_names)
|
|
195
|
+
if scope.is_a?(Binding) || scope.is_a?(Proc)
|
|
196
|
+
scope_object = eval("self", scope)
|
|
197
|
+
else
|
|
198
|
+
scope_object = scope
|
|
199
|
+
scope = scope_object.instance_eval{binding}
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" +
|
|
203
|
+
precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line])
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Defines a method on +object+
|
|
207
|
+
# with the given name
|
|
208
|
+
# that renders the template and returns the result as a string.
|
|
209
|
+
#
|
|
210
|
+
# If +object+ is a class or module,
|
|
211
|
+
# the method will instead by defined as an instance method.
|
|
212
|
+
# For example:
|
|
213
|
+
#
|
|
214
|
+
# t = Time.now
|
|
215
|
+
# Haml::Engine.new("%p\n Today's date is\n .date= self.to_s").def_method(t, :render)
|
|
216
|
+
# t.render #=> "<p>\n Today's date is\n <div class='date'>Fri Nov 23 18:28:29 -0800 2007</div>\n</p>\n"
|
|
217
|
+
#
|
|
218
|
+
# Haml::Engine.new(".upcased= upcase").def_method(String, :upcased_div)
|
|
219
|
+
# "foobar".upcased_div #=> "<div class='upcased'>FOOBAR</div>\n"
|
|
220
|
+
#
|
|
221
|
+
# The first argument of the defined method is a hash of local variable names to values.
|
|
222
|
+
# However, due to an unfortunate Ruby quirk,
|
|
223
|
+
# the local variables which can be assigned must be pre-declared.
|
|
224
|
+
# This is done with the +local_names+ argument.
|
|
225
|
+
# For example:
|
|
226
|
+
#
|
|
227
|
+
# # This works
|
|
228
|
+
# obj = Object.new
|
|
229
|
+
# Haml::Engine.new("%p= foo").def_method(obj, :render, :foo)
|
|
230
|
+
# obj.render(:foo => "Hello!") #=> "<p>Hello!</p>"
|
|
231
|
+
#
|
|
232
|
+
# # This doesn't
|
|
233
|
+
# obj = Object.new
|
|
234
|
+
# Haml::Engine.new("%p= foo").def_method(obj, :render)
|
|
235
|
+
# obj.render(:foo => "Hello!") #=> NameError: undefined local variable or method `foo'
|
|
236
|
+
#
|
|
237
|
+
# Note that Haml modifies the evaluation context
|
|
238
|
+
# (either the scope object or the "self" object of the scope binding).
|
|
239
|
+
# It extends Haml::Helpers, and various instance variables are set
|
|
240
|
+
# (all prefixed with "haml").
|
|
241
|
+
def def_method(object, name, *local_names)
|
|
242
|
+
method = object.is_a?(Module) ? :module_eval : :instance_eval
|
|
243
|
+
|
|
244
|
+
object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end",
|
|
245
|
+
@options[:filename], @options[:line])
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
private
|
|
249
|
+
|
|
250
|
+
def set_locals(locals, scope, scope_object)
|
|
251
|
+
scope_object.send(:instance_variable_set, '@_haml_locals', locals)
|
|
252
|
+
set_locals = locals.keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n")
|
|
253
|
+
eval(set_locals, scope)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Returns a hash of options that Haml::Buffer cares about.
|
|
257
|
+
# This should remain loadable from #inspect.
|
|
258
|
+
def options_for_buffer
|
|
259
|
+
{
|
|
260
|
+
:autoclose => @options[:autoclose],
|
|
261
|
+
:preserve => @options[:preserve],
|
|
262
|
+
:attr_wrapper => @options[:attr_wrapper],
|
|
263
|
+
:ugly => @options[:ugly],
|
|
264
|
+
:format => @options[:format]
|
|
265
|
+
}
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
data/lib/haml/error.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Haml
|
|
2
|
+
# An exception raised by Haml code.
|
|
3
|
+
class Error < StandardError
|
|
4
|
+
# :stopdoc:
|
|
5
|
+
|
|
6
|
+
# By default, an error is taken to refer to the line of the template
|
|
7
|
+
# that was being processed when the exception was raised.
|
|
8
|
+
# However, if line is non-nil, it + 1 is used instead.
|
|
9
|
+
attr_reader :line
|
|
10
|
+
|
|
11
|
+
def initialize(message = nil, line = nil)
|
|
12
|
+
super(message)
|
|
13
|
+
@line = line
|
|
14
|
+
end
|
|
15
|
+
# :startdoc:
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# SyntaxError is the type of exception raised when Haml encounters an
|
|
19
|
+
# ill-formatted document.
|
|
20
|
+
# It's not particularly interesting, except in that it includes Haml::Error.
|
|
21
|
+
class SyntaxError < Haml::Error; end
|
|
22
|
+
end
|
data/lib/haml/exec.rb
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Haml
|
|
5
|
+
# This module contains code for working with the
|
|
6
|
+
# haml, sass, and haml2html executables,
|
|
7
|
+
# such as command-line parsing stuff.
|
|
8
|
+
# It shouldn't need to be invoked by client code.
|
|
9
|
+
module Exec # :nodoc:
|
|
10
|
+
# A class that encapsulates the executable code
|
|
11
|
+
# for all three executables.
|
|
12
|
+
class Generic # :nodoc:
|
|
13
|
+
def initialize(args)
|
|
14
|
+
@args = args
|
|
15
|
+
@options = {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parse!
|
|
19
|
+
begin
|
|
20
|
+
@opts = OptionParser.new(&method(:set_opts))
|
|
21
|
+
@opts.parse!(@args)
|
|
22
|
+
|
|
23
|
+
process_result
|
|
24
|
+
|
|
25
|
+
@options
|
|
26
|
+
rescue Exception => e
|
|
27
|
+
raise e if @options[:trace] || e.is_a?(SystemExit)
|
|
28
|
+
|
|
29
|
+
$stderr.puts e.message
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
exit 0
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
@opts.to_s
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
def get_line(exception)
|
|
42
|
+
# SyntaxErrors have weird line reporting
|
|
43
|
+
# when there's trailing whitespace,
|
|
44
|
+
# which there is for Haml documents.
|
|
45
|
+
return exception.message.scan(/:(\d+)/).first.first if exception.is_a?(::SyntaxError)
|
|
46
|
+
exception.backtrace[0].scan(/:(\d+)/).first.first
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def set_opts(opts)
|
|
52
|
+
opts.on('-s', '--stdin', :NONE, 'Read input from standard input instead of an input file') do
|
|
53
|
+
@options[:input] = $stdin
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts.on('--trace', :NONE, 'Show a full traceback on error') do
|
|
57
|
+
@options[:trace] = true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
opts.on_tail("-?", "-h", "--help", "Show this message") do
|
|
61
|
+
puts opts
|
|
62
|
+
exit
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on_tail("-v", "--version", "Print version") do
|
|
66
|
+
puts("Haml #{::Haml.version[:string]}")
|
|
67
|
+
exit
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def process_result
|
|
72
|
+
input, output = @options[:input], @options[:output]
|
|
73
|
+
input_file, output_file = if input
|
|
74
|
+
[nil, open_file(ARGV[0], 'w')]
|
|
75
|
+
else
|
|
76
|
+
@options[:filename] = ARGV[0]
|
|
77
|
+
[open_file(ARGV[0]), open_file(ARGV[1], 'w')]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
input ||= input_file
|
|
81
|
+
output ||= output_file
|
|
82
|
+
input ||= $stdin
|
|
83
|
+
output ||= $stdout
|
|
84
|
+
|
|
85
|
+
@options[:input], @options[:output] = input, output
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def open_file(filename, flag = 'r')
|
|
89
|
+
return if filename.nil?
|
|
90
|
+
File.open(filename, flag)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# A class encapsulating the executable functionality
|
|
95
|
+
# specific to Haml and Sass.
|
|
96
|
+
class HamlSass < Generic # :nodoc:
|
|
97
|
+
def initialize(args)
|
|
98
|
+
super
|
|
99
|
+
@options[:for_engine] = {}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def set_opts(opts)
|
|
105
|
+
opts.banner = <<END
|
|
106
|
+
Usage: #{@name.downcase} [options] [INPUT] [OUTPUT]
|
|
107
|
+
|
|
108
|
+
Description:
|
|
109
|
+
Uses the #{@name} engine to parse the specified template
|
|
110
|
+
and outputs the result to the specified file.
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
END
|
|
114
|
+
|
|
115
|
+
opts.on('--rails RAILS_DIR', "Install Haml and Sass from the Gem to a Rails project") do |dir|
|
|
116
|
+
original_dir = dir
|
|
117
|
+
|
|
118
|
+
dir = File.join(dir, 'vendor', 'plugins')
|
|
119
|
+
|
|
120
|
+
unless File.exists?(dir)
|
|
121
|
+
puts "Directory #{dir} doesn't exist"
|
|
122
|
+
exit
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
dir = File.join(dir, 'haml')
|
|
126
|
+
|
|
127
|
+
if File.exists?(dir)
|
|
128
|
+
print "Directory #{dir} already exists, overwrite [y/N]? "
|
|
129
|
+
exit if gets !~ /y/i
|
|
130
|
+
FileUtils.rm_rf(dir)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
begin
|
|
134
|
+
Dir.mkdir(dir)
|
|
135
|
+
rescue SystemCallError
|
|
136
|
+
puts "Cannot create #{dir}"
|
|
137
|
+
exit
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
File.open(File.join(dir, 'init.rb'), 'w') do |file|
|
|
141
|
+
file.puts "require 'rubygems'"
|
|
142
|
+
file << File.read(File.dirname(__FILE__) + "/../../init.rb")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
puts "Haml plugin added to #{original_dir}"
|
|
146
|
+
exit
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
|
|
150
|
+
require 'stringio'
|
|
151
|
+
@options[:check_syntax] = true
|
|
152
|
+
@options[:output] = StringIO.new
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
super
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def process_result
|
|
159
|
+
super
|
|
160
|
+
@options[:for_engine][:filename] = @options[:filename] if @options[:filename]
|
|
161
|
+
require File.dirname(__FILE__) + "/../#{@name.downcase}"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# A class encapsulating executable functionality
|
|
166
|
+
# specific to Sass.
|
|
167
|
+
class Sass < HamlSass # :nodoc:
|
|
168
|
+
def initialize(args)
|
|
169
|
+
super
|
|
170
|
+
@name = "Sass"
|
|
171
|
+
@options[:for_engine][:load_paths] = ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def set_opts(opts)
|
|
175
|
+
super
|
|
176
|
+
|
|
177
|
+
opts.on('-t', '--style NAME',
|
|
178
|
+
'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
|
|
179
|
+
@options[:for_engine][:style] = name.to_sym
|
|
180
|
+
end
|
|
181
|
+
opts.on('-l', '--line-comments',
|
|
182
|
+
'Line Comments. Emit comments in the generated CSS indicating the corresponding sass line.') do
|
|
183
|
+
@options[:for_engine][:line_comments] = true
|
|
184
|
+
end
|
|
185
|
+
opts.on('-i', '--interactive',
|
|
186
|
+
'Run an interactive SassScript shell.') do
|
|
187
|
+
@options[:interactive] = true
|
|
188
|
+
end
|
|
189
|
+
opts.on('-I', '--load-path PATH', 'Add a sass import path.') do |path|
|
|
190
|
+
@options[:for_engine][:load_paths] << path
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def process_result
|
|
195
|
+
if @options[:interactive]
|
|
196
|
+
require 'sass'
|
|
197
|
+
require 'sass/repl'
|
|
198
|
+
::Sass::Repl.new(@options).run
|
|
199
|
+
return
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
super
|
|
203
|
+
input = @options[:input]
|
|
204
|
+
output = @options[:output]
|
|
205
|
+
|
|
206
|
+
template = input.read()
|
|
207
|
+
input.close() if input.is_a? File
|
|
208
|
+
|
|
209
|
+
begin
|
|
210
|
+
# We don't need to do any special handling of @options[:check_syntax] here,
|
|
211
|
+
# because the Sass syntax checking happens alongside evaluation
|
|
212
|
+
# and evaluation doesn't actually evaluate any code anyway.
|
|
213
|
+
result = ::Sass::Engine.new(template, @options[:for_engine]).render
|
|
214
|
+
rescue ::Sass::SyntaxError => e
|
|
215
|
+
raise e if @options[:trace]
|
|
216
|
+
raise "Syntax error on line #{get_line e}: #{e.message}"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
output.write(result)
|
|
220
|
+
output.close() if output.is_a? File
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# A class encapsulating executable functionality
|
|
225
|
+
# specific to Haml.
|
|
226
|
+
class Haml < HamlSass # :nodoc:
|
|
227
|
+
def initialize(args)
|
|
228
|
+
super
|
|
229
|
+
@name = "Haml"
|
|
230
|
+
@options[:requires] = []
|
|
231
|
+
@options[:load_paths] = []
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def set_opts(opts)
|
|
235
|
+
super
|
|
236
|
+
|
|
237
|
+
opts.on('-t', '--style NAME',
|
|
238
|
+
'Output style. Can be indented (default) or ugly.') do |name|
|
|
239
|
+
@options[:for_engine][:ugly] = true if name.to_sym == :ugly
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
opts.on('-f', '--format NAME',
|
|
243
|
+
'Output format. Can be xhtml (default), html4, or html5.') do |name|
|
|
244
|
+
@options[:for_engine][:format] = name.to_sym
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
opts.on('-e', '--escape-html',
|
|
248
|
+
'Escape HTML characters (like ampersands and angle brackets) by default.') do
|
|
249
|
+
@options[:for_engine][:escape_html] = true
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
opts.on('-r', '--require FILE', "Same as 'ruby -r'.") do |file|
|
|
253
|
+
@options[:requires] << file
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
opts.on('-I', '--load-path PATH', "Same as 'ruby -I'.") do |path|
|
|
257
|
+
@options[:load_paths] << path
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
opts.on('--debug', "Print out the precompiled Ruby source.") do
|
|
261
|
+
@options[:debug] = true
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def process_result
|
|
266
|
+
super
|
|
267
|
+
input = @options[:input]
|
|
268
|
+
output = @options[:output]
|
|
269
|
+
|
|
270
|
+
template = input.read()
|
|
271
|
+
input.close() if input.is_a? File
|
|
272
|
+
|
|
273
|
+
begin
|
|
274
|
+
engine = ::Haml::Engine.new(template, @options[:for_engine])
|
|
275
|
+
if @options[:check_syntax]
|
|
276
|
+
puts "Syntax OK"
|
|
277
|
+
return
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
@options[:load_paths].each {|p| $LOAD_PATH << p}
|
|
281
|
+
@options[:requires].each {|f| require f}
|
|
282
|
+
|
|
283
|
+
if @options[:debug]
|
|
284
|
+
puts engine.precompiled
|
|
285
|
+
puts '=' * 100
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
result = engine.to_html
|
|
289
|
+
rescue Exception => e
|
|
290
|
+
raise e if @options[:trace]
|
|
291
|
+
|
|
292
|
+
case e
|
|
293
|
+
when ::Haml::SyntaxError; raise "Syntax error on line #{get_line e}: #{e.message}"
|
|
294
|
+
when ::Haml::Error; raise "Haml error on line #{get_line e}: #{e.message}"
|
|
295
|
+
else raise "Exception on line #{get_line e}: #{e.message}\n Use --trace for backtrace."
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
output.write(result)
|
|
300
|
+
output.close() if output.is_a? File
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# A class encapsulating executable functionality
|
|
305
|
+
# specific to the html2haml executable.
|
|
306
|
+
class HTML2Haml < Generic # :nodoc:
|
|
307
|
+
def initialize(args)
|
|
308
|
+
super
|
|
309
|
+
|
|
310
|
+
@module_opts = {}
|
|
311
|
+
|
|
312
|
+
begin
|
|
313
|
+
require 'haml/html'
|
|
314
|
+
rescue LoadError => err
|
|
315
|
+
dep = err.message.scan(/^no such file to load -- (.*)/)[0]
|
|
316
|
+
puts "Required dependency #{dep} not found!"
|
|
317
|
+
exit 1
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def set_opts(opts)
|
|
322
|
+
opts.banner = <<END
|
|
323
|
+
Usage: html2haml [options] [INPUT] [OUTPUT]
|
|
324
|
+
|
|
325
|
+
Description: Transforms an HTML file into corresponding Haml code.
|
|
326
|
+
|
|
327
|
+
Options:
|
|
328
|
+
END
|
|
329
|
+
|
|
330
|
+
opts.on('-r', '--rhtml', 'Parse RHTML tags.') do
|
|
331
|
+
@module_opts[:rhtml] = true
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
opts.on('--no-rhtml', "Don't parse RHTML tags.") do
|
|
335
|
+
@options[:no_rhtml] = true
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
opts.on('-x', '--xhtml', 'Parse the input using the more strict XHTML parser.') do
|
|
339
|
+
@module_opts[:xhtml] = true
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
super
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def process_result
|
|
346
|
+
super
|
|
347
|
+
|
|
348
|
+
input = @options[:input]
|
|
349
|
+
output = @options[:output]
|
|
350
|
+
|
|
351
|
+
@module_opts[:rhtml] ||= input.respond_to?(:path) && input.path =~ /\.(rhtml|erb)$/
|
|
352
|
+
@module_opts[:rhtml] &&= @options[:no_rhtml] != false
|
|
353
|
+
|
|
354
|
+
output.write(::Haml::HTML.new(input, @module_opts).render)
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# A class encapsulating executable functionality
|
|
359
|
+
# specific to the css2sass executable.
|
|
360
|
+
class CSS2Sass < Generic # :nodoc:
|
|
361
|
+
def initialize(args)
|
|
362
|
+
super
|
|
363
|
+
|
|
364
|
+
@module_opts = {}
|
|
365
|
+
|
|
366
|
+
require 'sass/css'
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def set_opts(opts)
|
|
370
|
+
opts.banner = <<END
|
|
371
|
+
Usage: css2sass [options] [INPUT] [OUTPUT]
|
|
372
|
+
|
|
373
|
+
Description: Transforms a CSS file into corresponding Sass code.
|
|
374
|
+
|
|
375
|
+
Options:
|
|
376
|
+
END
|
|
377
|
+
|
|
378
|
+
opts.on('-a', '--alternate', 'Output using alternative Sass syntax (margin: 1px)') do
|
|
379
|
+
@module_opts[:alternate] = true
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
super
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def process_result
|
|
386
|
+
super
|
|
387
|
+
|
|
388
|
+
input = @options[:input]
|
|
389
|
+
output = @options[:output]
|
|
390
|
+
|
|
391
|
+
output.write(::Sass::CSS.new(input, @module_opts).render)
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|