haml-edge 2.3.209 → 2.3.210

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/.yardopts +2 -0
  2. data/EDGE_GEM_VERSION +1 -1
  3. data/Rakefile +24 -2
  4. data/VERSION +1 -1
  5. data/lib/haml/exec.rb +11 -4
  6. data/lib/haml/filters.rb +3 -0
  7. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  8. data/lib/haml/helpers/action_view_mods.rb +6 -4
  9. data/lib/haml/helpers.rb +2 -10
  10. data/lib/haml/html.rb +0 -1
  11. data/lib/haml/precompiler.rb +37 -30
  12. data/lib/haml/railtie.rb +6 -2
  13. data/lib/haml/root.rb +4 -0
  14. data/lib/haml/template.rb +2 -0
  15. data/lib/haml/util/subset_map.rb +101 -0
  16. data/lib/haml/util.rb +74 -0
  17. data/lib/haml.rb +5 -2
  18. data/lib/sass/engine.rb +36 -31
  19. data/lib/sass/files.rb +1 -1
  20. data/lib/sass/plugin/staleness_checker.rb +9 -9
  21. data/lib/sass/plugin.rb +21 -0
  22. data/lib/sass/script/color.rb +4 -3
  23. data/lib/sass/script/css_lexer.rb +11 -1
  24. data/lib/sass/script/css_parser.rb +4 -1
  25. data/lib/sass/script/funcall.rb +9 -0
  26. data/lib/sass/script/interpolation.rb +21 -0
  27. data/lib/sass/script/lexer.rb +30 -13
  28. data/lib/sass/script/node.rb +1 -1
  29. data/lib/sass/script/number.rb +4 -5
  30. data/lib/sass/script/parser.rb +13 -14
  31. data/lib/sass/script/string.rb +8 -2
  32. data/lib/sass/script/string_interpolation.rb +27 -4
  33. data/lib/sass/script.rb +1 -2
  34. data/lib/sass/scss/css_parser.rb +5 -3
  35. data/lib/sass/scss/parser.rb +146 -64
  36. data/lib/sass/scss/rx.rb +9 -1
  37. data/lib/sass/scss/sass_parser.rb +11 -0
  38. data/lib/sass/scss/script_lexer.rb +2 -0
  39. data/lib/sass/scss/static_parser.rb +48 -0
  40. data/lib/sass/scss.rb +3 -0
  41. data/lib/sass/selector/abstract_sequence.rb +40 -0
  42. data/lib/sass/selector/comma_sequence.rb +80 -0
  43. data/lib/sass/selector/sequence.rb +194 -0
  44. data/lib/sass/selector/simple.rb +107 -0
  45. data/lib/sass/selector/simple_sequence.rb +161 -0
  46. data/lib/sass/selector.rb +353 -0
  47. data/lib/sass/tree/comment_node.rb +1 -0
  48. data/lib/sass/tree/debug_node.rb +1 -0
  49. data/lib/sass/tree/directive_node.rb +1 -0
  50. data/lib/sass/tree/extend_node.rb +60 -0
  51. data/lib/sass/tree/for_node.rb +1 -0
  52. data/lib/sass/tree/if_node.rb +2 -0
  53. data/lib/sass/tree/import_node.rb +2 -0
  54. data/lib/sass/tree/mixin_def_node.rb +1 -0
  55. data/lib/sass/tree/mixin_node.rb +21 -5
  56. data/lib/sass/tree/node.rb +59 -12
  57. data/lib/sass/tree/prop_node.rb +20 -21
  58. data/lib/sass/tree/root_node.rb +8 -17
  59. data/lib/sass/tree/rule_node.rb +49 -100
  60. data/lib/sass/tree/variable_node.rb +1 -0
  61. data/lib/sass/tree/warn_node.rb +1 -0
  62. data/lib/sass/tree/while_node.rb +1 -0
  63. data/lib/sass.rb +1 -0
  64. data/test/haml/engine_test.rb +185 -3
  65. data/test/haml/helper_test.rb +25 -2
  66. data/test/haml/template_test.rb +2 -2
  67. data/test/haml/templates/helpers.haml +13 -0
  68. data/test/haml/util/subset_map_test.rb +91 -0
  69. data/test/haml/util_test.rb +25 -0
  70. data/test/sass/conversion_test.rb +23 -3
  71. data/test/sass/engine_test.rb +50 -7
  72. data/test/sass/extend_test.rb +1045 -0
  73. data/test/sass/results/complex.css +0 -1
  74. data/test/sass/results/script.css +1 -1
  75. data/test/sass/script_conversion_test.rb +16 -0
  76. data/test/sass/script_test.rb +37 -4
  77. data/test/sass/scss/css_test.rb +17 -3
  78. data/test/sass/scss/rx_test.rb +1 -1
  79. data/test/sass/scss/scss_test.rb +30 -0
  80. data/test/sass/templates/complex.sass +0 -2
  81. data/test/test_helper.rb +5 -0
  82. metadata +17 -3
data/.yardopts CHANGED
@@ -3,6 +3,8 @@
3
3
  --markup-provider maruku
4
4
  --default-return ""
5
5
  --title "Haml/Sass Documentation"
6
+ --query 'object.type != :classvariable'
7
+ --query 'object.type != :constant || @api && @api.text == "public"'
6
8
  --hide-void-return
7
9
  --protected
8
10
  --no-private
data/EDGE_GEM_VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.209
1
+ 2.3.210
data/Rakefile CHANGED
@@ -239,7 +239,7 @@ end
239
239
  begin
240
240
  require 'yard'
241
241
 
242
- namespace :yard do
242
+ namespace :doc do
243
243
  task :sass do
244
244
  require scope('lib/sass')
245
245
  Dir[scope("yard/default/**/*.sass")].each do |sass|
@@ -248,12 +248,27 @@ begin
248
248
  end
249
249
  end
250
250
  end
251
+
252
+ desc "List all undocumented methods and classes."
253
+ task :undocumented do
254
+ opts = ENV["YARD_OPTS"] || ""
255
+ ENV["YARD_OPTS"] = opts.dup + <<OPTS
256
+ --list --query "
257
+ object.docstring.blank? &&
258
+ !(object.type == :method && object.is_alias?)"
259
+ OPTS
260
+ Rake::Task['yard'].execute
261
+ end
251
262
  end
252
263
 
253
264
  YARD::Rake::YardocTask.new do |t|
254
265
  t.files = FileList.new(scope('lib/**/*.rb')) do |list|
255
266
  list.exclude('lib/haml/template/*.rb')
267
+ list.exclude('lib/haml/railtie.rb')
256
268
  list.exclude('lib/haml/helpers/action_view_mods.rb')
269
+ list.exclude('lib/haml/helpers/xss_mods.rb')
270
+ list.exclude('lib/sass/plugin/merb.rb')
271
+ list.exclude('lib/sass/plugin/rails.rb')
257
272
  end.to_a
258
273
  t.options << '--incremental' if Rake.application.top_level_tasks.include?('redoc')
259
274
  t.options += FileList.new(scope('yard/*.rb')).to_a.map {|f| ['-e', f]}.flatten
@@ -261,8 +276,15 @@ begin
261
276
  t.options << '--files' << files.join(',')
262
277
  t.options << '--template-path' << scope('yard')
263
278
  t.options << '--title' << ENV["YARD_TITLE"] if ENV["YARD_TITLE"]
279
+
280
+ t.before = lambda do
281
+ if ENV["YARD_OPTS"]
282
+ require 'shellwords'
283
+ t.options.concat(Shellwords.shellwords(ENV["YARD_OPTS"]))
284
+ end
285
+ end
264
286
  end
265
- Rake::Task['yard'].prerequisites.insert(0, 'yard:sass')
287
+ Rake::Task['yard'].prerequisites.insert(0, 'doc:sass')
266
288
  Rake::Task['yard'].instance_variable_set('@comment', nil)
267
289
 
268
290
  desc "Generate Documentation"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.209
1
+ 2.3.210
data/lib/haml/exec.rb CHANGED
@@ -115,7 +115,6 @@ module Haml
115
115
  @options[:input], @options[:output] = input, output
116
116
  end
117
117
 
118
- # @private
119
118
  COLORS = { :red => 31, :green => 32, :yellow => 33 }
120
119
 
121
120
  # Prints a status message about performing the given action,
@@ -298,7 +297,7 @@ END
298
297
  @options[:for_engine][:cache_location] = loc
299
298
  end
300
299
  opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
301
- @options[:for_engine][:cache] = false
300
+ @options[:for_engine][:read_cache] = false
302
301
  end
303
302
  end
304
303
 
@@ -569,8 +568,9 @@ END
569
568
  # @param args [Array<String>] The command-line arguments
570
569
  def initialize(args)
571
570
  super
571
+ require 'sass'
572
572
  @options[:for_tree] = {}
573
- @options[:for_engine] = {}
573
+ @options[:for_engine] = {:cache => false, :read_cache => true}
574
574
  end
575
575
 
576
576
  # Tells optparse how to parse the arguments.
@@ -630,7 +630,7 @@ END
630
630
  end
631
631
 
632
632
  opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
633
- @options[:for_engine][:cache] = false
633
+ @options[:for_engine][:read_cache] = false
634
634
  end
635
635
 
636
636
  super
@@ -654,6 +654,8 @@ END
654
654
  process_file(input, output)
655
655
  end
656
656
 
657
+ private
658
+
657
659
  def process_directory
658
660
  input = @options[:input] = @args.shift
659
661
  output = @options[:output] = @args.shift
@@ -665,6 +667,11 @@ END
665
667
  end
666
668
  @options[:output] ||= @options[:input]
667
669
 
670
+ if @options[:to] == @options[:from] && !@options[:in_place]
671
+ fmt = @options[:from]
672
+ raise "Error: converting from #{fmt} to #{fmt} without --in-place"
673
+ end
674
+
668
675
  ext = @options[:from]
669
676
  ext = :sass if ext == :sass2
670
677
  Dir.glob("#{@options[:input]}/**/*.#{ext}") do |f|
data/lib/haml/filters.rb CHANGED
@@ -325,6 +325,9 @@ END
325
325
  ::RedCloth.new(text).to_html(:textile)
326
326
  end
327
327
  end
328
+ # An alias for the Textile filter,
329
+ # since the only available Textile parser is RedCloth.
330
+ # @api public
328
331
  RedCloth = Textile
329
332
  Filters.defined['redcloth'] = RedCloth
330
333
 
@@ -1,7 +1,7 @@
1
- require 'haml/helpers/action_view_mods'
2
-
3
1
  module Haml
4
2
  module Helpers
3
+ @@action_view_defined = true
4
+
5
5
  # This module contains various useful helper methods
6
6
  # that either tie into ActionView or the rest of the ActionPack stack,
7
7
  # or are only useful in that context.
@@ -51,5 +51,7 @@ module Haml
51
51
  @_haml_concat_raw = old
52
52
  end
53
53
  end
54
+
55
+ include ActionViewExtensions
54
56
  end
55
57
  end
@@ -126,7 +126,11 @@ module ActionView
126
126
  end
127
127
 
128
128
  def content_tag(*args)
129
- content_tag_with_haml(*args)
129
+ html_tag = content_tag_with_haml(*args)
130
+ return html_tag unless respond_to?(:error_wrapping)
131
+ return error_wrapping(html_tag) if method(:error_wrapping).arity == 1
132
+ return html_tag unless object.respond_to?(:errors) && object.errors.respond_to?(:on)
133
+ return error_wrapping(html_tag, object.errors.on(@method_name))
130
134
  end
131
135
  end
132
136
 
@@ -156,9 +160,7 @@ module ActionView
156
160
  def form_for_with_haml(object_name, *args, &proc)
157
161
  if block_given? && is_haml?
158
162
  oldproc = proc
159
- proc = haml_bind_proc do |*args|
160
- with_tabs(1) {oldproc.call(*args)}
161
- end
163
+ proc = proc {|*args| with_tabs(1) {oldproc.call(*args)}}
162
164
  end
163
165
  res = form_for_without_haml(object_name, *args, &proc)
164
166
  res << "\n" if block_given? && is_haml?
data/lib/haml/helpers.rb CHANGED
@@ -1,8 +1,3 @@
1
- if defined?(ActionView)
2
- require 'haml/helpers/action_view_mods'
3
- require 'haml/helpers/action_view_extensions'
4
- end
5
-
6
1
  module Haml
7
2
  # This module contains various helpful methods to make it easier to do various tasks.
8
3
  # {Haml::Helpers} is automatically included in the context
@@ -52,8 +47,7 @@ MESSAGE
52
47
 
53
48
  self.extend self
54
49
 
55
- @@action_view_defined = defined?(ActionView)
56
- @@force_no_action_view = false
50
+ @@action_view_defined = false
57
51
 
58
52
  # @return [Boolean] Whether or not ActionView is loaded
59
53
  def self.action_view?
@@ -500,7 +494,6 @@ MESSAGE
500
494
  end
501
495
 
502
496
  # Characters that need to be escaped to HTML entities from user input
503
- # @private
504
497
  HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
505
498
 
506
499
  # Returns a copy of `text` with ampersands, angle brackets and quotes
@@ -594,11 +587,10 @@ MESSAGE
594
587
  _erbout = _hamlout.buffer
595
588
  proc { |*args| proc.call(*args) }
596
589
  end
597
-
598
- include ActionViewExtensions if self.const_defined? "ActionViewExtensions"
599
590
  end
600
591
  end
601
592
 
593
+ # @private
602
594
  class Object
603
595
  # Haml overrides various `ActionView` helpers,
604
596
  # which call an \{#is\_haml?} method
data/lib/haml/html.rb CHANGED
@@ -146,7 +146,6 @@ module Haml
146
146
  end
147
147
  alias_method :to_haml, :render
148
148
 
149
- # @private
150
149
  TEXT_REGEXP = /^(\s*).*$/
151
150
 
152
151
  # @see Hpricot
@@ -8,60 +8,46 @@ module Haml
8
8
  include Haml::Util
9
9
 
10
10
  # Designates an XHTML/XML element.
11
- # @private
12
11
  ELEMENT = ?%
13
12
 
14
13
  # Designates a `<div>` element with the given class.
15
- # @private
16
14
  DIV_CLASS = ?.
17
15
 
18
16
  # Designates a `<div>` element with the given id.
19
- # @private
20
17
  DIV_ID = ?#
21
18
 
22
19
  # Designates an XHTML/XML comment.
23
- # @private
24
20
  COMMENT = ?/
25
21
 
26
22
  # Designates an XHTML doctype or script that is never HTML-escaped.
27
- # @private
28
23
  DOCTYPE = ?!
29
24
 
30
25
  # Designates script, the result of which is output.
31
- # @private
32
26
  SCRIPT = ?=
33
27
 
34
28
  # Designates script that is always HTML-escaped.
35
- # @private
36
29
  SANITIZE = ?&
37
30
 
38
31
  # Designates script, the result of which is flattened and output.
39
- # @private
40
32
  FLAT_SCRIPT = ?~
41
33
 
42
34
  # Designates script which is run but not output.
43
- # @private
44
35
  SILENT_SCRIPT = ?-
45
36
 
46
37
  # When following SILENT_SCRIPT, designates a comment that is not output.
47
- # @private
48
38
  SILENT_COMMENT = ?#
49
39
 
50
40
  # Designates a non-parsed line.
51
- # @private
52
41
  ESCAPE = ?\\
53
42
 
54
43
  # Designates a block of filtered text.
55
- # @private
56
44
  FILTER = ?:
57
45
 
58
46
  # Designates a non-parsed line. Not actually a character.
59
- # @private
60
47
  PLAIN_TEXT = -1
61
48
 
62
49
  # Keeps track of the ASCII values of the characters that begin a
63
50
  # specially-interpreted line.
64
- # @private
65
51
  SPECIAL_CHARACTERS = [
66
52
  ELEMENT,
67
53
  DIV_CLASS,
@@ -78,7 +64,6 @@ module Haml
78
64
 
79
65
  # The value of the character that designates that a line is part
80
66
  # of a multiline string.
81
- # @private
82
67
  MULTILINE_CHAR_VALUE = ?|
83
68
 
84
69
  # Regex to match keywords that appear in the middle of a Ruby block
@@ -94,15 +79,12 @@ module Haml
94
79
  #
95
80
  # The block is ended after `%p no!`, because `else`
96
81
  # is a member of this array.
97
- # @private
98
82
  MID_BLOCK_KEYWORD_REGEX = /^-\s*(#{%w[else elsif rescue ensure when end].join('|')})\b/
99
83
 
100
84
  # The Regex that matches a Doctype command.
101
- # @private
102
85
  DOCTYPE_REGEX = /(\d(?:\.\d)?)?[\s]*([a-z]*)/i
103
86
 
104
87
  # The Regex that matches a literal string or symbol value
105
- # @private
106
88
  LITERAL_VALUE_REGEX = /:(\w*)|(["'])((?![\\#]|\2).|\\.)*\2/
107
89
 
108
90
  private
@@ -146,6 +128,7 @@ END
146
128
  class Line < Struct.new(:text, :unstripped, :full, :index, :precompiler, :eod)
147
129
  alias_method :eod?, :eod
148
130
 
131
+ # @private
149
132
  def tabs
150
133
  line = self
151
134
  @tabs ||= precompiler.instance_eval do
@@ -252,6 +235,7 @@ You don't need to use "- end" in Haml. Un-indent to close a block:
252
235
  %p This line is un-indented, so it isn't part of the "if" block
253
236
  END
254
237
 
238
+ text = handle_ruby_multiline(text)
255
239
  push_silent(text[1..-1], true)
256
240
  newline_now
257
241
 
@@ -395,6 +379,7 @@ END
395
379
  # the result before it is added to `@buffer`
396
380
  def push_script(text, opts = {})
397
381
  raise SyntaxError.new("There's no Ruby code for = to evaluate.") if text.empty?
382
+ text = handle_ruby_multiline(text)
398
383
  return if options[:suppress_eval]
399
384
  opts[:escape_html] = options[:escape_html] if opts[:escape_html].nil?
400
385
 
@@ -781,10 +766,13 @@ END
781
766
  end.compact!
782
767
 
783
768
  raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
784
- raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index) if block_opened? && !value.empty?
785
769
  raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
786
770
  raise SyntaxError.new("Self-closing tags can't have content.", last_line - 1) if self_closing && !value.empty?
787
771
 
772
+ if block_opened? && !value.empty? && !is_ruby_multiline?(value)
773
+ raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index)
774
+ end
775
+
788
776
  self_closing ||= !!(!block_opened? && value.empty? && @options[:autoclose].any? {|t| t === tag_name})
789
777
  value = nil if value.empty? && (block_opened? || self_closing)
790
778
 
@@ -984,18 +972,17 @@ END
984
972
  end
985
973
 
986
974
  def handle_multiline(line)
987
- if is_multiline?(line.text)
988
- line.text.slice!(-1)
989
- while new_line = raw_next_line.first
990
- break if new_line == :eod
991
- newline and next if new_line.strip.empty?
992
- break unless is_multiline?(new_line.strip)
993
- line.text << new_line.strip[0...-1]
994
- newline
995
- end
996
- un_next_line new_line
997
- resolve_newlines
975
+ return unless is_multiline?(line.text)
976
+ line.text.slice!(-1)
977
+ while new_line = raw_next_line.first
978
+ break if new_line == :eod
979
+ newline and next if new_line.strip.empty?
980
+ break unless is_multiline?(new_line.strip)
981
+ line.text << new_line.strip[0...-1]
982
+ newline
998
983
  end
984
+ un_next_line new_line
985
+ resolve_newlines
999
986
  end
1000
987
 
1001
988
  # Checks whether or not +line+ is in a multiline sequence.
@@ -1003,6 +990,26 @@ END
1003
990
  text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s
1004
991
  end
1005
992
 
993
+ def handle_ruby_multiline(text)
994
+ text = text.rstrip
995
+ return text unless is_ruby_multiline?(text)
996
+ un_next_line @next_line.full
997
+ begin
998
+ new_line = raw_next_line.first
999
+ break if new_line == :eod
1000
+ newline and next if new_line.strip.empty?
1001
+ text << " " << new_line.strip
1002
+ newline
1003
+ end while is_ruby_multiline?(new_line.strip)
1004
+ next_line
1005
+ resolve_newlines
1006
+ text
1007
+ end
1008
+
1009
+ def is_ruby_multiline?(text)
1010
+ text && text.length > 1 && text[-1] == ?, && text[-2] != ?? && text[-3..-2] != "?\\"
1011
+ end
1012
+
1006
1013
  def contains_interpolation?(str)
1007
1014
  str.include?('#{')
1008
1015
  end
data/lib/haml/railtie.rb CHANGED
@@ -3,10 +3,14 @@
3
3
  # Yehuda promises there will be soon,
4
4
  # and once there is we should switch to that.
5
5
 
6
- if defined?(Rails::Railtie)
6
+ if defined?(ActiveSupport) && Haml::Util.has?(:public_method, ActiveSupport, :on_load)
7
+ # Rails 3.0.0.beta.2+
8
+ ActiveSupport.on_load(:action_view) {Haml.init_rails(binding)}
9
+ elsif defined?(Rails::Railtie)
10
+ # Rails 3.0.0.beta1
7
11
  module Haml
8
12
  class Railtie < Rails::Railtie
9
- initializer :haml do
13
+ ActiveSupport.on_load(:action_view) do
10
14
  Haml.init_rails(binding)
11
15
  end
12
16
  end
data/lib/haml/root.rb CHANGED
@@ -1,3 +1,7 @@
1
1
  module Haml
2
+ # The root directory of the Haml source tree.
3
+ # This may be overridden by the package manager
4
+ # if the lib directory is separated from the main source tree.
5
+ # @api public
2
6
  ROOT_DIR = File.expand_path("../../..", __FILE__)
3
7
  end
data/lib/haml/template.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'haml/engine'
2
+ require 'haml/helpers/action_view_mods'
3
+ require 'haml/helpers/action_view_extensions'
2
4
 
3
5
  module Haml
4
6
  # The class that keeps track of the global options for Haml within Rails.
@@ -0,0 +1,101 @@
1
+ require 'set'
2
+
3
+ module Haml
4
+ module Util
5
+ # A map from sets to values.
6
+ # A value is \{#\[]= set} by providing a set (the "set-set") and a value,
7
+ # which is then recorded as corresponding to that set.
8
+ # Values are \{#\[] accessed} by providing a set (the "get-set")
9
+ # and returning all values that correspond to set-sets
10
+ # that are subsets of the get-set.
11
+ #
12
+ # SubsetMap preserves the order of values as they're inserted.
13
+ #
14
+ # @example
15
+ # ssm = SubsetMap.new
16
+ # ssm[Set[1, 2]] = "Foo"
17
+ # ssm[Set[2, 3]] = "Bar"
18
+ # ssm[Set[1, 2, 3]] = "Baz"
19
+ #
20
+ # ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
21
+ class SubsetMap
22
+ # Creates a new, empty SubsetMap.
23
+ def initialize
24
+ @hash = {}
25
+ @vals = []
26
+ end
27
+
28
+ # Whether or not this SubsetMap has any key-value pairs.
29
+ #
30
+ # @return [Boolean]
31
+ def empty?
32
+ @hash.empty?
33
+ end
34
+
35
+ # Associates a value with a set.
36
+ # When `set` or any of its supersets is accessed,
37
+ # `value` will be among the values returned.
38
+ #
39
+ # Note that if the same `set` is passed to this method multiple times,
40
+ # all given `value`s will be associated with that `set`.
41
+ #
42
+ # This runs in `O(n)` time, where `n` is the size of `set`.
43
+ #
44
+ # @param set [#to_set] The set to use as the map key. May not be empty.
45
+ # @param value [Object] The value to associate with `set`.
46
+ # @raise [ArgumentError] If `set` is empty.
47
+ def []=(set, value)
48
+ raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
49
+
50
+ index = @vals.size
51
+ @vals << value
52
+ set.each do |k|
53
+ @hash[k] ||= []
54
+ @hash[k] << [set, set.to_set, index]
55
+ end
56
+ end
57
+
58
+ # Returns all values associated with subsets of `set`.
59
+ #
60
+ # In the worst case, this runs in `O(m*max(n, log m))` time,
61
+ # where `n` is the size of `set`
62
+ # and `m` is the number of assocations in the map.
63
+ # However, unless many keys in the map overlap with `set`,
64
+ # `m` will typically be much smaller.
65
+ #
66
+ # @param set [Set] The set to use as the map key.
67
+ # @return [Array<(Object, #to_set)>] An array of pairs,
68
+ # where the first value is the value associated with a subset of `set`,
69
+ # and the second value is that subset of `set`
70
+ # (or whatever `#to_set` object was used to set the value)
71
+ # This array is in insertion order.
72
+ # @see #[]
73
+ def get(set)
74
+ res = set.map do |k|
75
+ next unless subsets = @hash[k]
76
+ subsets.map do |subenum, subset, index|
77
+ next unless subset.subset?(set)
78
+ [index, subenum]
79
+ end
80
+ end
81
+ res.flatten!(1)
82
+ res.compact!
83
+ res.uniq!
84
+ res.sort!
85
+ res.map! {|i, s| [@vals[i], s]}
86
+ return res
87
+ end
88
+
89
+ # Same as \{#get}, but doesn't return the subsets of the argument
90
+ # for which values were found.
91
+ #
92
+ # @param set [Set] The set to use as the map key.
93
+ # @return [Array] The array of all values
94
+ # associated with subsets of `set`, in insertion order.
95
+ # @see #get
96
+ def [](set)
97
+ get(set).map {|v, _| v}
98
+ end
99
+ end
100
+ end
101
+ end
data/lib/haml/util.rb CHANGED
@@ -3,6 +3,7 @@ require 'set'
3
3
  require 'enumerator'
4
4
  require 'stringio'
5
5
  require 'haml/root'
6
+ require 'haml/util/subset_map'
6
7
 
7
8
  module Haml
8
9
  # A module containing various useful functions.
@@ -10,6 +11,7 @@ module Haml
10
11
  extend self
11
12
 
12
13
  # An array of ints representing the Ruby version number.
14
+ # @api public
13
15
  RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
14
16
 
15
17
  # Returns the path of a file relative to the Haml root directory.
@@ -135,6 +137,33 @@ module Haml
135
137
  end
136
138
  end
137
139
 
140
+ # Intersperses a value in an enumerable, as would be done with `Array#join`
141
+ # but without concatenating the array together afterwards.
142
+ #
143
+ # @param enum [Enumerable]
144
+ # @param val
145
+ # @return [Array]
146
+ def intersperse(enum, val)
147
+ enum.inject([]) {|a, e| a << e << val}[0...-1]
148
+ end
149
+
150
+ # Substitutes a sub-array of one array with another sub-array.
151
+ #
152
+ # @param ary [Array] The array in which to make the substitution
153
+ # @param from [Array] The sequence of elements to replace with `to`
154
+ # @param to [Array] The sequence of elements to replace `from` with
155
+ def substitute(ary, from, to)
156
+ res = ary.dup
157
+ i = 0
158
+ while i < res.size
159
+ if res[i...i+from.size] == from
160
+ res[i...i+from.size] = to
161
+ end
162
+ i += 1
163
+ end
164
+ res
165
+ end
166
+
138
167
  # Destructively strips whitespace from the beginning and end
139
168
  # of the first and last elements, respectively,
140
169
  # in the array (if those elements are strings).
@@ -147,6 +176,23 @@ module Haml
147
176
  arr
148
177
  end
149
178
 
179
+ # Return an array of all possible paths through the given arrays.
180
+ #
181
+ # @param arrs [Array<Array>]
182
+ # @return [Array<Arrays>]
183
+ #
184
+ # @example
185
+ # paths([[1, 2], [3, 4], [5]]) #=>
186
+ # # [[1, 3, 5],
187
+ # # [2, 3, 5],
188
+ # # [1, 4, 5],
189
+ # # [2, 4, 5]]
190
+ def paths(arrs)
191
+ arrs.inject([[]]) do |paths, arr|
192
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
193
+ end
194
+ end
195
+
150
196
  # Returns information about the caller of the previous method.
151
197
  #
152
198
  # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
@@ -237,6 +283,30 @@ module Haml
237
283
  ActionPack::VERSION::TINY == "0.beta")
238
284
  end
239
285
 
286
+ # Returns whether this environment is using ActionPack
287
+ # version 3.0.0.beta.3 or greater.
288
+ #
289
+ # @return [Boolean]
290
+ def ap_geq_3_beta_3?
291
+ # The ActionPack module is always loaded automatically in Rails >= 3
292
+ return false unless defined?(ActionPack) && defined?(ActionPack::VERSION)
293
+
294
+ version =
295
+ if defined?(ActionPack::VERSION::MAJOR)
296
+ ActionPack::VERSION::MAJOR
297
+ else
298
+ # Rails 1.2
299
+ ActionPack::VERSION::Major
300
+ end
301
+ version >= 3 &&
302
+ ((defined?(ActionPack::VERSION::TINY) &&
303
+ ActionPack::VERSION::TINY.is_a?(Fixnum) &&
304
+ ActionPack::VERSION::TINY >= 1) ||
305
+ (defined?(ActionPack::VERSION::BUILD) &&
306
+ ActionPack::VERSION::BUILD =~ /beta(\d+)/ &&
307
+ $1.to_i >= 3))
308
+ end
309
+
240
310
  # Returns an ActionView::Template* class.
241
311
  # In pre-3.0 versions of Rails, most of these classes
242
312
  # were of the form `ActionView::TemplateFoo`,
@@ -282,6 +352,10 @@ module Haml
282
352
  raise Haml::Error.new("Expected #{text.inspect} to be HTML-safe.")
283
353
  end
284
354
 
355
+ # The class for the Rails SafeBuffer XSS protection class.
356
+ # This varies depending on Rails version.
357
+ #
358
+ # @return [Class]
285
359
  def rails_safe_buffer_class
286
360
  return ActionView::SafeBuffer if defined?(ActionView::SafeBuffer)
287
361
  ActiveSupport::SafeBuffer
data/lib/haml.rb CHANGED
@@ -17,6 +17,7 @@ module Haml
17
17
 
18
18
  # A string representing the version of Haml.
19
19
  # A more fine-grained representation is available from Haml.version.
20
+ # @api public
20
21
  VERSION = version[:string] unless defined?(Haml::VERSION)
21
22
 
22
23
  # Initializes Haml for Rails.
@@ -37,5 +38,7 @@ module Haml
37
38
  end
38
39
 
39
40
  require 'haml/util'
40
- require 'haml/engine'
41
- require 'haml/railtie'
41
+ unless $0 =~ /sass(-convert)?$/
42
+ require 'haml/engine'
43
+ require 'haml/railtie'
44
+ end