haml 1.5.2 → 1.7.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 (66) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/Rakefile +1 -0
  3. data/VERSION +1 -1
  4. data/bin/css2sass +7 -0
  5. data/bin/html2haml +0 -82
  6. data/lib/haml.rb +43 -6
  7. data/lib/haml/buffer.rb +81 -72
  8. data/lib/haml/engine.rb +240 -110
  9. data/lib/haml/exec.rb +120 -5
  10. data/lib/haml/helpers.rb +88 -3
  11. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  12. data/lib/haml/helpers/action_view_mods.rb +30 -17
  13. data/lib/haml/html.rb +173 -0
  14. data/lib/haml/template.rb +1 -26
  15. data/lib/haml/util.rb +18 -0
  16. data/lib/sass.rb +181 -3
  17. data/lib/sass/constant.rb +38 -9
  18. data/lib/sass/constant/color.rb +25 -1
  19. data/lib/sass/constant/literal.rb +10 -8
  20. data/lib/sass/css.rb +197 -0
  21. data/lib/sass/engine.rb +239 -68
  22. data/lib/sass/error.rb +2 -2
  23. data/lib/sass/plugin.rb +11 -3
  24. data/lib/sass/tree/attr_node.rb +25 -17
  25. data/lib/sass/tree/comment_node.rb +14 -0
  26. data/lib/sass/tree/node.rb +18 -1
  27. data/lib/sass/tree/rule_node.rb +17 -5
  28. data/lib/sass/tree/value_node.rb +4 -0
  29. data/test/haml/engine_test.rb +42 -25
  30. data/test/haml/helper_test.rb +28 -3
  31. data/test/haml/results/eval_suppressed.xhtml +6 -0
  32. data/test/haml/results/helpers.xhtml +26 -2
  33. data/test/haml/results/helpful.xhtml +2 -0
  34. data/test/haml/results/just_stuff.xhtml +17 -2
  35. data/test/haml/results/standard.xhtml +1 -1
  36. data/test/haml/results/whitespace_handling.xhtml +1 -11
  37. data/test/haml/rhtml/standard.rhtml +1 -1
  38. data/test/haml/template_test.rb +7 -2
  39. data/test/haml/templates/eval_suppressed.haml +7 -2
  40. data/test/haml/templates/helpers.haml +16 -1
  41. data/test/haml/templates/helpful.haml +2 -0
  42. data/test/haml/templates/just_stuff.haml +23 -4
  43. data/test/haml/templates/standard.haml +3 -3
  44. data/test/haml/templates/whitespace_handling.haml +0 -50
  45. data/test/sass/engine_test.rb +35 -10
  46. data/test/sass/plugin_test.rb +10 -6
  47. data/test/sass/results/alt.css +4 -0
  48. data/test/sass/results/complex.css +4 -3
  49. data/test/sass/results/constants.css +3 -3
  50. data/test/sass/results/import.css +27 -0
  51. data/test/sass/results/nested.css +7 -0
  52. data/test/sass/results/parent_ref.css +13 -0
  53. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  54. data/test/sass/results/subdir/subdir.css +1 -0
  55. data/test/sass/templates/alt.sass +16 -0
  56. data/test/sass/templates/bork2.sass +2 -0
  57. data/test/sass/templates/complex.sass +19 -1
  58. data/test/sass/templates/constants.sass +8 -0
  59. data/test/sass/templates/import.sass +8 -0
  60. data/test/sass/templates/importee.sass +10 -0
  61. data/test/sass/templates/nested.sass +8 -0
  62. data/test/sass/templates/parent_ref.sass +25 -0
  63. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  64. data/test/sass/templates/subdir/subdir.sass +6 -0
  65. metadata +95 -75
  66. data/test/haml/results/semantic.cache +0 -15
@@ -16,12 +16,28 @@ module Haml
16
16
  end
17
17
 
18
18
  def parse!
19
- @opts = OptionParser.new(&(method(:set_opts).to_proc))
20
- @opts.parse!(@args)
19
+ begin
20
+ @opts = OptionParser.new(&(method(:set_opts).to_proc))
21
+ @opts.parse!(@args)
21
22
 
22
- process_result
23
+ process_result
24
+
25
+ @options
26
+ rescue Exception => e
27
+ raise e if e.is_a? SystemExit
23
28
 
24
- @options
29
+ line = e.backtrace[0].scan(/:(.*)/)[0]
30
+ puts "#{e.class} on line #{line}: #{e.message}"
31
+
32
+ if @options[:trace]
33
+ e.backtrace[1..-1].each { |t| puts " #{t}" }
34
+ else
35
+ puts " Use --trace to see traceback"
36
+ end
37
+
38
+ exit 1
39
+ end
40
+ exit 0
25
41
  end
26
42
 
27
43
  def to_s
@@ -39,6 +55,15 @@ module Haml
39
55
  @options[:output] = $stdout
40
56
  end
41
57
 
58
+ opts.on('-s', '--stdio', 'Read input from standard input and print output to standard output') do
59
+ @options[:input] = $stdin
60
+ @options[:output] = $stdout
61
+ end
62
+
63
+ opts.on('--trace', :NONE, 'Show a full traceback on error') do
64
+ @options[:trace] = true
65
+ end
66
+
42
67
  opts.on_tail("-?", "-h", "--help", "Show this message") do
43
68
  puts opts
44
69
  exit
@@ -102,6 +127,47 @@ Description:
102
127
  Options:
103
128
  END
104
129
 
130
+ opts.on('--rails RAILS_DIR', "Install Haml from the Gem to a Rails project") do |dir|
131
+ original_dir = dir
132
+
133
+ dir = File.join(dir, 'vendor', 'plugins')
134
+
135
+ unless File.exists?(dir)
136
+ puts "Directory #{dir} doesn't exist"
137
+ exit
138
+ end
139
+
140
+ dir = File.join(dir, 'haml')
141
+
142
+ if File.exists?(dir)
143
+ puts "Directory #{dir} already exists."
144
+ exit
145
+ end
146
+
147
+ begin
148
+ Dir.mkdir(dir)
149
+ rescue SystemCallError
150
+ puts "Cannot create #{dir}"
151
+ exit
152
+ end
153
+
154
+ File.open(File.join(dir, 'init.rb'), 'w') do |file|
155
+ file.puts <<END
156
+ require 'rubygems'
157
+ require 'haml'
158
+ require 'haml/template'
159
+ require 'sass'
160
+ require 'sass/plugin'
161
+
162
+ ActionView::Base.register_template_handler('haml', Haml::Template)
163
+ Sass::Plugin.update_stylesheets
164
+ END
165
+ end
166
+
167
+ puts "Haml plugin added to #{original_dir}"
168
+ exit
169
+ end
170
+
105
171
  super
106
172
  end
107
173
 
@@ -156,12 +222,61 @@ END
156
222
  # A class encapsulating executable functionality
157
223
  # specific to the html2haml executable.
158
224
  class HTML2Haml < Generic # :nodoc:
225
+ def initialize(args)
226
+ super
227
+
228
+ @module_opts = {}
229
+
230
+ begin
231
+ require 'haml/html'
232
+ rescue LoadError => err
233
+ dep = err.message.scan(/^no such file to load -- (.*)/)[0]
234
+ puts "Required dependency #{dep} not found!"
235
+ exit 1
236
+ end
237
+ end
238
+
159
239
  def set_opts(opts)
160
240
  opts.banner = <<END
161
241
  Usage: html2haml [options] (html file) (output file)
162
242
 
163
243
  Description: Transforms an HTML file into corresponding Haml code.
164
244
 
245
+ Options:
246
+ END
247
+
248
+ opts.on('-r', '--rhtml', 'Parse RHTML tags.') do
249
+ @module_opts[:rhtml] = true
250
+ end
251
+
252
+ super
253
+ end
254
+
255
+ def process_result
256
+ super
257
+
258
+ input = @options[:input]
259
+ output = @options[:output]
260
+
261
+ output.write(::Haml::HTML.new(input, @module_opts).render)
262
+ end
263
+ end
264
+
265
+ # A class encapsulating executable functionality
266
+ # specific to the css2sass executable.
267
+ class CSS2Sass < Generic # :nodoc:
268
+ def initialize(args)
269
+ super
270
+
271
+ require 'sass/css'
272
+ end
273
+
274
+ def set_opts(opts)
275
+ opts.banner = <<END
276
+ Usage: css2sass [options] (css file) (output file)
277
+
278
+ Description: Transforms a CSS file into corresponding Sass code.
279
+
165
280
  Options:
166
281
  END
167
282
 
@@ -174,7 +289,7 @@ END
174
289
  input = @options[:input]
175
290
  output = @options[:output]
176
291
 
177
- output.write(Hpricot(input).to_haml)
292
+ output.write(::Sass::CSS.new(input).render)
178
293
  end
179
294
  end
180
295
  end
@@ -1,4 +1,5 @@
1
1
  require 'haml/helpers/action_view_mods'
2
+ require 'haml/helpers/action_view_extensions'
2
3
 
3
4
  module Haml
4
5
  # This module contains various helpful methods to make it easier to do
@@ -7,7 +8,7 @@ module Haml
7
8
  # disposal from within the template.
8
9
  module Helpers
9
10
  self.extend self
10
-
11
+
11
12
  @@action_view_defined = defined?(ActionView)
12
13
  @@force_no_action_view = false
13
14
 
@@ -83,6 +84,22 @@ module Haml
83
84
  to_return.join("\n")
84
85
  end
85
86
 
87
+ # Returns a hash containing default assignments for the xmlns and xml:lang
88
+ # attributes of the <tt>html</tt> HTML element.
89
+ # It also takes an optional argument for the value of xml:lang and lang,
90
+ # which defaults to 'en-US'.
91
+ # For example,
92
+ #
93
+ # %html{html_attrs}
94
+ #
95
+ # becomes
96
+ #
97
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
98
+ #
99
+ def html_attrs(lang = 'en-US')
100
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
101
+ end
102
+
86
103
  # Increments the number of tabs the buffer automatically adds
87
104
  # to the lines of the template.
88
105
  # For example:
@@ -184,6 +201,73 @@ module Haml
184
201
  def capture_haml(*args, &block)
185
202
  capture_haml_with_buffer(buffer.buffer, *args, &block)
186
203
  end
204
+
205
+ # Outputs text directly to the Haml buffer, with the proper tabulation
206
+ def puts(text = "")
207
+ buffer.buffer << (' ' * buffer.tabulation) << text.to_s << "\n"
208
+ nil
209
+ end
210
+
211
+ #
212
+ # call-seq:
213
+ # open(name, attributes = {}) {...}
214
+ # open(name, text, attributes = {}) {...}
215
+ #
216
+ # Creates an HTML tag with the given name and optionally text and attributes.
217
+ # Can take a block that will be executed
218
+ # between when the opening and closing tags are output.
219
+ # If the block is a Haml block or outputs text using puts,
220
+ # the text will be properly indented.
221
+ #
222
+ # For example,
223
+ #
224
+ # open :table do
225
+ # open :tr do
226
+ # open :td, {:class => 'cell'} do
227
+ # open :strong, "strong!"
228
+ # puts "data"
229
+ # end
230
+ # open :td do
231
+ # puts "more_data"
232
+ # end
233
+ # end
234
+ # end
235
+ #
236
+ # outputs
237
+ #
238
+ # <table>
239
+ # <tr>
240
+ # <td class='cell'>
241
+ # <strong>
242
+ # strong!
243
+ # </strong>
244
+ # data
245
+ # </td>
246
+ # <td>
247
+ # more_data
248
+ # </td>
249
+ # </tr>
250
+ # </table>
251
+ #
252
+ def open(name, attributes = {}, alt_atts = {}, &block)
253
+ text = nil
254
+ if attributes.is_a? String
255
+ text = attributes
256
+ attributes = alt_atts
257
+ end
258
+
259
+ puts "<#{name}#{buffer.build_attributes(attributes)}>"
260
+ tab_up
261
+ # Print out either the text (using push_text) or call the block and add an endline
262
+ if text
263
+ puts(text)
264
+ elsif block
265
+ block.call
266
+ end
267
+ tab_down
268
+ puts "</#{name}>"
269
+ nil
270
+ end
187
271
 
188
272
  private
189
273
 
@@ -205,7 +289,7 @@ module Haml
205
289
  def capture_haml_with_buffer(local_buffer, *args, &block)
206
290
  position = local_buffer.length
207
291
 
208
- block.call(*args)
292
+ block.call *args
209
293
 
210
294
  captured = local_buffer.slice!(position..-1)
211
295
 
@@ -221,7 +305,6 @@ module Haml
221
305
  end
222
306
  result.to_s
223
307
  end
224
- alias_method :capture_erb_with_buffer, :capture_haml_with_buffer
225
308
 
226
309
  # Returns whether or not the current template is a Haml template.
227
310
  #
@@ -231,6 +314,8 @@ module Haml
231
314
  def is_haml?
232
315
  @haml_is_haml
233
316
  end
317
+
318
+ include ActionViewExtensions if self.const_defined? "ActionViewExtensions"
234
319
  end
235
320
  end
236
321
 
@@ -0,0 +1,45 @@
1
+ require 'haml/helpers/action_view_mods'
2
+
3
+ if defined?(ActionView)
4
+ module Haml
5
+ module Helpers
6
+ # This module contains various useful helper methods
7
+ # that either tie into ActionView or the rest of the ActionPack stack,
8
+ # or are only useful in that context.
9
+ # Thus, the methods defined here are only available
10
+ # if ActionView is installed.
11
+ module ActionViewExtensions
12
+ # Returns a value for the "class" attribute
13
+ # unique to this controller/action pair.
14
+ # This can be used to target styles specifically at this action or controller.
15
+ # For example, if the current action were EntryController#show,
16
+ #
17
+ # %div{:class => page_class} My Div
18
+ #
19
+ # would become
20
+ #
21
+ # <div class="entry show">My Div</div>
22
+ #
23
+ # Then, in a stylesheet
24
+ # (shown here as Sass),
25
+ # you could refer to this specific action:
26
+ #
27
+ # .entry.show
28
+ # :font-weight bold
29
+ #
30
+ # or to all actions in the entry controller:
31
+ #
32
+ # .entry
33
+ # :color #00f
34
+ #
35
+ def page_class
36
+ controller.controller_name + " " + controller.action_name
37
+ end
38
+
39
+ # :stopdoc:
40
+ alias_method :generate_content_class_names, :page_class
41
+ # :startdoc:
42
+ end
43
+ end
44
+ end
45
+ end
@@ -8,7 +8,7 @@ rescue LoadError
8
8
  action_view_included = false
9
9
  end
10
10
 
11
- if action_view_included
11
+ if action_view_included
12
12
  module ActionView
13
13
  class Base # :nodoc:
14
14
  def render_with_haml(*args)
@@ -26,6 +26,18 @@ if action_view_included
26
26
  # to make them work more effectively with Haml.
27
27
  module Helpers
28
28
  # :stopdoc:
29
+ module CaptureHelper
30
+ def capture_erb_with_buffer_with_haml(*args, &block)
31
+ if is_haml?
32
+ capture_haml_with_buffer(*args, &block)
33
+ else
34
+ capture_erb_with_buffer_without_haml(*args, &block)
35
+ end
36
+ end
37
+ alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
38
+ alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
39
+ end
40
+
29
41
  module TextHelper
30
42
  def concat_with_haml(string, binding = nil)
31
43
  if is_haml?
@@ -37,26 +49,30 @@ if action_view_included
37
49
  alias_method :concat_without_haml, :concat
38
50
  alias_method :concat, :concat_with_haml
39
51
  end
40
-
52
+
41
53
  module FormTagHelper
42
54
  def form_tag_with_haml(url_for_options = {}, options = {}, *parameters_for_url, &proc)
43
- if block_given? && is_haml?
44
- oldproc = proc
45
- proc = bind_proc do |*args|
46
- concat "\n"
47
- tab_up
48
- oldproc.call(*args)
49
- tab_down
55
+ if is_haml?
56
+ if block_given?
57
+ oldproc = proc
58
+ proc = bind_proc do |*args|
59
+ concat "\n"
60
+ tab_up
61
+ oldproc.call(*args)
62
+ tab_down
63
+ end
50
64
  end
65
+ res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) + "\n"
66
+ concat "\n" if block_given? && is_haml?
67
+ res
68
+ else
69
+ form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc)
51
70
  end
52
- res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) + "\n"
53
- concat "\n" if block_given? && is_haml?
54
- res
55
71
  end
56
72
  alias_method :form_tag_without_haml, :form_tag
57
73
  alias_method :form_tag, :form_tag_with_haml
58
74
  end
59
-
75
+
60
76
  module FormHelper
61
77
  def form_for_with_haml(object_name, *args, &proc)
62
78
  if block_given? && is_haml?
@@ -73,11 +89,8 @@ if action_view_included
73
89
  alias_method :form_for_without_haml, :form_for
74
90
  alias_method :form_for, :form_for_with_haml
75
91
  end
76
-
77
- def generate_content_class_names
78
- controller.controller_name + " " + controller.action_name
79
- end
80
92
  # :startdoc:
81
93
  end
82
94
  end
83
95
  end
96
+
@@ -0,0 +1,173 @@
1
+ require File.dirname(__FILE__) + '/../haml'
2
+
3
+ require 'haml/engine'
4
+ require 'rubygems'
5
+ require 'hpricot'
6
+ require 'cgi'
7
+
8
+ module Haml
9
+ # This class contains the functionality used in the +html2haml+ utility,
10
+ # namely converting HTML documents to Haml templates.
11
+ # It depends on Hpricot for HTML parsing (http://code.whytheluckystiff.net/hpricot/).
12
+ class HTML
13
+ # Creates a new instance of Haml::HTML that will compile the given template,
14
+ # which can either be a string containing HTML or an Hpricot node,
15
+ # to a Haml string when +render+ is called.
16
+ def initialize(template, options = {})
17
+ @@options = options
18
+
19
+ if template.is_a? Hpricot::Node
20
+ @template = template
21
+ else
22
+ if template.is_a? IO
23
+ template = template.read
24
+ end
25
+
26
+ if @@options[:rhtml]
27
+ match_to_html(template, /<%=(.*?)-?%>/m, 'loud')
28
+ match_to_html(template, /<%(.*?)-?%>/m, 'silent')
29
+ end
30
+ @template = Hpricot(template)
31
+ end
32
+ end
33
+
34
+ # Processes the document and returns the result as a string
35
+ # containing the Haml template.
36
+ def render
37
+ @template.to_haml(0)
38
+ end
39
+ alias_method :to_haml, :render
40
+
41
+ module ::Hpricot::Node
42
+ # Returns the Haml representation of the given node,
43
+ # at the given tabulation.
44
+ def to_haml(tabs = 0)
45
+ parse_text(self.to_s, tabs)
46
+ end
47
+
48
+ private
49
+
50
+ def tabulate(tabs)
51
+ ' ' * tabs
52
+ end
53
+
54
+ def parse_text(text, tabs)
55
+ text.strip!
56
+ if text.empty?
57
+ String.new
58
+ else
59
+ lines = text.split("\n")
60
+
61
+ lines.map do |line|
62
+ line.strip!
63
+ "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
64
+ end.join
65
+ end
66
+ end
67
+ end
68
+
69
+ # :stopdoc:
70
+
71
+ def self.options
72
+ @@options
73
+ end
74
+
75
+ TEXT_REGEXP = /^(\s*).*$/
76
+
77
+ class ::Hpricot::Doc
78
+ def to_haml(tabs = 0)
79
+ output = ''
80
+ children.each { |child| output += child.to_haml(0) }
81
+ output
82
+ end
83
+ end
84
+
85
+ class ::Hpricot::XMLDecl
86
+ def to_haml(tabs = 0)
87
+ "#{tabulate(tabs)}!!! XML\n"
88
+ end
89
+ end
90
+
91
+ class ::Hpricot::DocType
92
+ def to_haml(tabs = 0)
93
+ attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
94
+ if attrs == nil
95
+ raise Exception.new("Invalid doctype")
96
+ end
97
+
98
+ type, version, strictness = attrs.map { |a| a.downcase }
99
+ if type == "html"
100
+ version = "1.0"
101
+ strictness = "transitional"
102
+ end
103
+
104
+ if version == "1.0" || version.empty?
105
+ version = nil
106
+ end
107
+
108
+ if strictness == 'transitional' || strictness.empty?
109
+ strictness = nil
110
+ end
111
+
112
+ version = " #{version}" if version
113
+ if strictness
114
+ strictness[0] = strictness[0] - 32
115
+ strictness = " #{strictness}"
116
+ end
117
+
118
+ "#{tabulate(tabs)}!!!#{version}#{strictness}\n"
119
+ end
120
+ end
121
+
122
+ class ::Hpricot::Comment
123
+ def to_haml(tabs = 0)
124
+ "#{tabulate(tabs)}/\n#{parse_text(self.content, tabs + 1)}"
125
+ end
126
+ end
127
+
128
+ class ::Hpricot::Elem
129
+ def to_haml(tabs = 0)
130
+ output = "#{tabulate(tabs)}"
131
+ if HTML.options[:rhtml] && name[0...5] == 'haml:'
132
+ return output + HTML.send("haml_tag_#{name[5..-1]}", self.innerHTML)
133
+ end
134
+
135
+ output += "%#{name}" unless name == 'div' && (attributes.include?('id') || attributes.include?('class'))
136
+
137
+ if attributes
138
+ output += "##{attributes['id']}" if attributes['id']
139
+ attributes['class'].split(' ').each { |c| output += ".#{c}" } if attributes['class']
140
+ attributes.delete("id")
141
+ attributes.delete("class")
142
+ output += attributes.inspect if attributes.length > 0
143
+ end
144
+
145
+ output += "/" if children.length == 0
146
+ output += "\n"
147
+
148
+ self.children.each do |child|
149
+ output += child.to_haml(tabs + 1)
150
+ end
151
+
152
+ output
153
+ end
154
+ end
155
+
156
+ def self.haml_tag_loud(text)
157
+ "= #{text.gsub(/\n\s*/, '; ').strip}\n"
158
+ end
159
+
160
+ def self.haml_tag_silent(text)
161
+ text.split("\n").map { |line| "- #{line.strip}\n" }.join
162
+ end
163
+
164
+ private
165
+
166
+ def match_to_html(string, regex, tag)
167
+ string.gsub!(regex) do
168
+ "<haml:#{tag}>#{CGI.escapeHTML($1)}</haml:#{tag}>"
169
+ end
170
+ end
171
+ # :startdoc:
172
+ end
173
+ end