haml-edge 2.3.209 → 2.3.210
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/.yardopts +2 -0
- data/EDGE_GEM_VERSION +1 -1
- data/Rakefile +24 -2
- data/VERSION +1 -1
- data/lib/haml/exec.rb +11 -4
- data/lib/haml/filters.rb +3 -0
- data/lib/haml/helpers/action_view_extensions.rb +4 -2
- data/lib/haml/helpers/action_view_mods.rb +6 -4
- data/lib/haml/helpers.rb +2 -10
- data/lib/haml/html.rb +0 -1
- data/lib/haml/precompiler.rb +37 -30
- data/lib/haml/railtie.rb +6 -2
- data/lib/haml/root.rb +4 -0
- data/lib/haml/template.rb +2 -0
- data/lib/haml/util/subset_map.rb +101 -0
- data/lib/haml/util.rb +74 -0
- data/lib/haml.rb +5 -2
- data/lib/sass/engine.rb +36 -31
- data/lib/sass/files.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +9 -9
- data/lib/sass/plugin.rb +21 -0
- data/lib/sass/script/color.rb +4 -3
- data/lib/sass/script/css_lexer.rb +11 -1
- data/lib/sass/script/css_parser.rb +4 -1
- data/lib/sass/script/funcall.rb +9 -0
- data/lib/sass/script/interpolation.rb +21 -0
- data/lib/sass/script/lexer.rb +30 -13
- data/lib/sass/script/node.rb +1 -1
- data/lib/sass/script/number.rb +4 -5
- data/lib/sass/script/parser.rb +13 -14
- data/lib/sass/script/string.rb +8 -2
- data/lib/sass/script/string_interpolation.rb +27 -4
- data/lib/sass/script.rb +1 -2
- data/lib/sass/scss/css_parser.rb +5 -3
- data/lib/sass/scss/parser.rb +146 -64
- data/lib/sass/scss/rx.rb +9 -1
- data/lib/sass/scss/sass_parser.rb +11 -0
- data/lib/sass/scss/script_lexer.rb +2 -0
- data/lib/sass/scss/static_parser.rb +48 -0
- data/lib/sass/scss.rb +3 -0
- data/lib/sass/selector/abstract_sequence.rb +40 -0
- data/lib/sass/selector/comma_sequence.rb +80 -0
- data/lib/sass/selector/sequence.rb +194 -0
- data/lib/sass/selector/simple.rb +107 -0
- data/lib/sass/selector/simple_sequence.rb +161 -0
- data/lib/sass/selector.rb +353 -0
- data/lib/sass/tree/comment_node.rb +1 -0
- data/lib/sass/tree/debug_node.rb +1 -0
- data/lib/sass/tree/directive_node.rb +1 -0
- data/lib/sass/tree/extend_node.rb +60 -0
- data/lib/sass/tree/for_node.rb +1 -0
- data/lib/sass/tree/if_node.rb +2 -0
- data/lib/sass/tree/import_node.rb +2 -0
- data/lib/sass/tree/mixin_def_node.rb +1 -0
- data/lib/sass/tree/mixin_node.rb +21 -5
- data/lib/sass/tree/node.rb +59 -12
- data/lib/sass/tree/prop_node.rb +20 -21
- data/lib/sass/tree/root_node.rb +8 -17
- data/lib/sass/tree/rule_node.rb +49 -100
- data/lib/sass/tree/variable_node.rb +1 -0
- data/lib/sass/tree/warn_node.rb +1 -0
- data/lib/sass/tree/while_node.rb +1 -0
- data/lib/sass.rb +1 -0
- data/test/haml/engine_test.rb +185 -3
- data/test/haml/helper_test.rb +25 -2
- data/test/haml/template_test.rb +2 -2
- data/test/haml/templates/helpers.haml +13 -0
- data/test/haml/util/subset_map_test.rb +91 -0
- data/test/haml/util_test.rb +25 -0
- data/test/sass/conversion_test.rb +23 -3
- data/test/sass/engine_test.rb +50 -7
- data/test/sass/extend_test.rb +1045 -0
- data/test/sass/results/complex.css +0 -1
- data/test/sass/results/script.css +1 -1
- data/test/sass/script_conversion_test.rb +16 -0
- data/test/sass/script_test.rb +37 -4
- data/test/sass/scss/css_test.rb +17 -3
- data/test/sass/scss/rx_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +30 -0
- data/test/sass/templates/complex.sass +0 -2
- data/test/test_helper.rb +5 -0
- metadata +17 -3
data/.yardopts
CHANGED
data/EDGE_GEM_VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.210
|
data/Rakefile
CHANGED
@@ -239,7 +239,7 @@ end
|
|
239
239
|
begin
|
240
240
|
require 'yard'
|
241
241
|
|
242
|
-
namespace :
|
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, '
|
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.
|
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][:
|
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][:
|
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
@@ -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 =
|
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 =
|
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 = { '&'=>'&', '<'=>'<', '>'=>'>', '"'=>'"', "'"=>''', }
|
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
data/lib/haml/precompiler.rb
CHANGED
@@ -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
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
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?(
|
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
|
-
|
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
data/lib/haml/template.rb
CHANGED
@@ -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
|
-
|
41
|
-
require 'haml/
|
41
|
+
unless $0 =~ /sass(-convert)?$/
|
42
|
+
require 'haml/engine'
|
43
|
+
require 'haml/railtie'
|
44
|
+
end
|