haml 4.0.7 → 5.0.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.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +42 -4
- data/FAQ.md +4 -14
- data/MIT-LICENSE +1 -1
- data/README.md +85 -42
- data/REFERENCE.md +108 -57
- data/Rakefile +46 -54
- data/lib/haml/attribute_builder.rb +163 -0
- data/lib/haml/attribute_compiler.rb +215 -0
- data/lib/haml/attribute_parser.rb +144 -0
- data/lib/haml/buffer.rb +22 -132
- data/lib/haml/compiler.rb +87 -295
- data/lib/haml/engine.rb +25 -41
- data/lib/haml/error.rb +3 -0
- data/lib/haml/escapable.rb +49 -0
- data/lib/haml/exec.rb +33 -19
- data/lib/haml/filters.rb +18 -24
- data/lib/haml/generator.rb +41 -0
- data/lib/haml/helpers/action_view_extensions.rb +3 -2
- data/lib/haml/helpers/action_view_mods.rb +36 -58
- data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
- data/lib/haml/helpers/safe_erubi_template.rb +27 -0
- data/lib/haml/helpers/safe_erubis_template.rb +4 -1
- data/lib/haml/helpers/xss_mods.rb +18 -12
- data/lib/haml/helpers.rb +133 -90
- data/lib/haml/options.rb +38 -47
- data/lib/haml/parser.rb +278 -216
- data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
- data/lib/haml/railtie.rb +21 -12
- data/lib/haml/sass_rails_filter.rb +17 -4
- data/lib/haml/template/options.rb +12 -2
- data/lib/haml/template.rb +12 -6
- data/lib/haml/temple_engine.rb +120 -0
- data/lib/haml/temple_line_counter.rb +29 -0
- data/lib/haml/util.rb +80 -199
- data/lib/haml/version.rb +2 -1
- data/lib/haml.rb +1 -0
- data/test/attribute_parser_test.rb +101 -0
- data/test/engine_test.rb +287 -176
- data/test/filters_test.rb +32 -19
- data/test/gemfiles/Gemfile.rails-4.0.x +9 -3
- data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
- data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
- data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
- data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
- data/test/helper_test.rb +224 -112
- data/test/options_test.rb +22 -0
- data/test/parser_test.rb +71 -4
- data/test/results/bemit.xhtml +4 -0
- data/test/results/eval_suppressed.xhtml +4 -4
- data/test/results/helpers.xhtml +43 -41
- data/test/results/helpful.xhtml +6 -3
- data/test/results/just_stuff.xhtml +21 -20
- data/test/results/list.xhtml +9 -9
- data/test/results/nuke_inner_whitespace.xhtml +22 -22
- data/test/results/nuke_outer_whitespace.xhtml +84 -92
- data/test/results/original_engine.xhtml +17 -17
- data/test/results/partial_layout.xhtml +4 -3
- data/test/results/partial_layout_erb.xhtml +4 -3
- data/test/results/partials.xhtml +11 -10
- data/test/results/silent_script.xhtml +63 -63
- data/test/results/standard.xhtml +156 -159
- data/test/results/tag_parsing.xhtml +19 -19
- data/test/results/very_basic.xhtml +2 -2
- data/test/results/whitespace_handling.xhtml +77 -76
- data/test/template_test.rb +24 -56
- data/test/template_test_helper.rb +38 -0
- data/test/templates/bemit.haml +3 -0
- data/test/templates/just_stuff.haml +1 -0
- data/test/templates/standard_ugly.haml +1 -0
- data/test/templates/with_bom.haml +1 -0
- data/test/temple_line_counter_test.rb +40 -0
- data/test/test_helper.rb +26 -8
- data/test/util_test.rb +6 -47
- metadata +53 -43
- data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
- data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
- data/test/haml-spec/LICENSE +0 -14
- data/test/haml-spec/README.md +0 -106
- data/test/haml-spec/lua_haml_spec.lua +0 -38
- data/test/haml-spec/perl_haml_test.pl +0 -81
- data/test/haml-spec/ruby_haml_test.rb +0 -23
- data/test/haml-spec/tests.json +0 -660
- data/test/templates/_av_partial_1_ugly.haml +0 -9
- data/test/templates/_av_partial_2_ugly.haml +0 -5
- data/test/templates/action_view_ugly.haml +0 -47
- data/test/templates/standard_ugly.haml +0 -43
data/REFERENCE.md
CHANGED
@@ -121,7 +121,7 @@ see {Haml::Options}.
|
|
121
121
|
|
122
122
|
### Encodings
|
123
123
|
|
124
|
-
|
124
|
+
Haml supports the same sorts of
|
125
125
|
encoding-declaration comments that Ruby does. Although both Ruby and Haml
|
126
126
|
support several different styles, the easiest it just to add `-# coding:
|
127
127
|
encoding-name` at the beginning of the Haml template (it must come before all
|
@@ -233,8 +233,7 @@ is compiled to:
|
|
233
233
|
|
234
234
|
<script src='javascripts/script_9' type='text/javascript'></script>
|
235
235
|
|
236
|
-
#### `:class` and `:id` Attributes
|
237
|
-
{#class-and-id-attributes}
|
236
|
+
#### `:class` and `:id` Attributes {#class-and-id-attributes}
|
238
237
|
|
239
238
|
The `:class` and `:id` attributes can also be specified as a Ruby array whose
|
240
239
|
elements will be joined together. A `:class` array is joined with `" "` and an
|
@@ -306,7 +305,7 @@ hash-style attributes:
|
|
306
305
|
|
307
306
|
#### Ruby 1.9-style Hashes
|
308
307
|
|
309
|
-
|
308
|
+
Haml also supports Ruby's new hash syntax:
|
310
309
|
|
311
310
|
%a{title: @title, href: href} Stuff
|
312
311
|
|
@@ -389,27 +388,49 @@ or using `true` and `false`:
|
|
389
388
|
|
390
389
|
%input(selected=true)
|
391
390
|
|
392
|
-
|
391
|
+
<!-- The title to the next section (Prefixed Attributes) has changed. This
|
392
|
+
<a> tag is so old links to here still work. -->
|
393
|
+
<a id="html5_custom_data_attributes" style="border:0;"></a>
|
393
394
|
|
394
|
-
|
395
|
-
attributes](http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#embedding-custom-non-visible-data)
|
396
|
-
to elements using attribute names beginning with `data-`. Custom data attributes
|
397
|
-
can be used in Haml by using the key `:data` with a Hash value in an attribute
|
398
|
-
hash. Each of the key/value pairs in the Hash will be transformed into a custom
|
399
|
-
data attribute. For example:
|
395
|
+
#### Prefixed Attributes
|
400
396
|
|
401
|
-
|
397
|
+
HTML5 allows for adding
|
398
|
+
[custom non-visible data attributes](http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
399
|
+
to elements using attribute names beginning with `data-`. The
|
400
|
+
[Accessible Rich Internet Applications](http://www.w3.org/WAI/intro/aria)
|
401
|
+
specification makes use of attributes beginning with `aria-`. There are also
|
402
|
+
frameworks that use non-standard attributes with a common prefix.
|
403
|
+
|
404
|
+
Haml can help generate collections of attributes that share a prefix like
|
405
|
+
these. Any entry in an attribute hash that has a Hash as its value is expanded
|
406
|
+
into a series of attributes, one for each key/value pair in the hash, with the
|
407
|
+
attribute name formed by joining the “parent” key name to the key name with a
|
408
|
+
hyphen.
|
409
|
+
|
410
|
+
For example:
|
411
|
+
|
412
|
+
%a{:href=>"/posts", :data => {:author_id => 123, :category => 7}} Posts By Author
|
402
413
|
|
403
414
|
will render as:
|
404
415
|
|
405
|
-
<a data-author-id='123' href='/posts'>Posts By Author</a>
|
416
|
+
<a data-author-id='123' data-category='7' href='/posts'>Posts By Author</a>
|
406
417
|
|
407
418
|
Notice that the underscore in `author_id` was replaced by a hyphen. If you wish
|
408
419
|
to suppress this behavior, you can set Haml's
|
409
420
|
{Haml::Options#hyphenate_data_attrs `:hyphenate_data_attrs` option} to `false`,
|
410
421
|
and the output will be rendered as:
|
411
422
|
|
412
|
-
<a data-author_id='123' href='/posts'>Posts By Author</a>
|
423
|
+
<a data-author_id='123' data-category='7' href='/posts'>Posts By Author</a>
|
424
|
+
|
425
|
+
This expansion of hashes is recursive – any value of the child hash that is
|
426
|
+
itself a hash will create an attribute for each entry, with the attribute name
|
427
|
+
prefixed with all ancestor keys. For example:
|
428
|
+
|
429
|
+
.book-info{:data => {:book => {:id => 123, :genre => 'programming'}, :category => 7}}
|
430
|
+
|
431
|
+
will render as:
|
432
|
+
|
433
|
+
<div class='book-info' data-book-genre='programming' data-book-id='123' data-category='7'></div>
|
413
434
|
|
414
435
|
### Class and ID: `.` and `#`
|
415
436
|
|
@@ -496,23 +517,23 @@ and is compiled to:
|
|
496
517
|
The forward slash character, when placed at the end of a tag definition, causes
|
497
518
|
Haml to treat it as being an empty (or void) element. Depending on the format,
|
498
519
|
the tag will be rendered either without a closing tag (`:html4` or `:html5`), or
|
499
|
-
as a self-closing tag (`:xhtml`).
|
520
|
+
as a self-closing tag (`:xhtml`).
|
521
|
+
|
522
|
+
Taking the following as an example:
|
500
523
|
|
501
524
|
%br/
|
502
525
|
%meta{'http-equiv' => 'Content-Type', :content => 'text/html'}/
|
503
526
|
|
504
|
-
is compiled to:
|
527
|
+
When the format is `:html4` or `:html5` this is compiled to:
|
505
528
|
|
506
529
|
<br>
|
507
530
|
<meta content='text/html' http-equiv='Content-Type'>
|
508
531
|
|
509
|
-
when the format is `:
|
532
|
+
and when the format is `:xhtml` it is compiled to:
|
510
533
|
|
511
534
|
<br />
|
512
535
|
<meta content='text/html' http-equiv='Content-Type' />
|
513
536
|
|
514
|
-
when the format is `:xhtml`.
|
515
|
-
|
516
537
|
Some tags are automatically treated as being empty, as long as they have no
|
517
538
|
content in the Haml source. `meta`, `img`, `link`, `br`, `hr`, `input`,
|
518
539
|
`area`, `param`, `col` and `base` tags are treated as empty by default. This
|
@@ -779,6 +800,21 @@ is compiled to:
|
|
779
800
|
</a>
|
780
801
|
<![endif]-->
|
781
802
|
|
803
|
+
To generate “downlevel-revealed” conditional comments, where the content is
|
804
|
+
hidden from IE but not other browsers, add a `!` before the brackets: `/![]`.
|
805
|
+
Haml will produce valid HTML when generating this kind of conditional comment.
|
806
|
+
|
807
|
+
For example:
|
808
|
+
|
809
|
+
/![if !IE]
|
810
|
+
You are not using Internet Explorer, or are using version 10+.
|
811
|
+
|
812
|
+
is compiled to:
|
813
|
+
|
814
|
+
<!--[if !IE]><!-->
|
815
|
+
You are not using Internet Explorer, or are using version 10+.
|
816
|
+
<!--<![endif]-->
|
817
|
+
|
782
818
|
### Haml Comments: `-#`
|
783
819
|
|
784
820
|
The hyphen followed immediately by the pound sign signifies a silent comment.
|
@@ -985,6 +1021,21 @@ might compile to:
|
|
985
1021
|
//]]>
|
986
1022
|
</script>
|
987
1023
|
|
1024
|
+
#### Gotchas
|
1025
|
+
|
1026
|
+
Haml uses an overly simplistic regular expression to identify string
|
1027
|
+
interpolation rather than a full-blown Ruby parser. This is fast and works for
|
1028
|
+
most code but you may have errors with code like the following:
|
1029
|
+
|
1030
|
+
%span #{'{'}
|
1031
|
+
|
1032
|
+
This code will generate a syntax error, complaining about unbalanced brackets.
|
1033
|
+
In cases like this, the recommended workaround is output the code as a Ruby
|
1034
|
+
string to force Haml to parse the code with Ruby.
|
1035
|
+
|
1036
|
+
%span= "#{'{'}"
|
1037
|
+
|
1038
|
+
|
988
1039
|
### Escaping HTML: `&=` {#escaping_html}
|
989
1040
|
|
990
1041
|
An ampersand followed by one or two equals characters evaluates Ruby code just
|
@@ -1087,53 +1138,53 @@ more info.
|
|
1087
1138
|
|
1088
1139
|
Haml comes with the following filters defined:
|
1089
1140
|
|
1090
|
-
{#cdata-filter}
|
1091
|
-
|
1141
|
+
### `:cdata` {#cdata-filter}
|
1142
|
+
|
1092
1143
|
Surrounds the filtered text with CDATA tags.
|
1093
1144
|
|
1094
|
-
{#coffee-filter}
|
1095
|
-
|
1096
|
-
Compiles the filtered text to Javascript using
|
1145
|
+
### `:coffee` {#coffee-filter}
|
1146
|
+
|
1147
|
+
Compiles the filtered text to Javascript using Coffeescript. You can also
|
1097
1148
|
reference this filter as `:coffeescript`. This filter is implemented using
|
1098
1149
|
Tilt.
|
1099
1150
|
|
1100
|
-
{#css-filter}
|
1101
|
-
|
1151
|
+
### `:css` {#css-filter}
|
1152
|
+
|
1102
1153
|
Surrounds the filtered text with `<style>` and (optionally) CDATA tags. Useful
|
1103
1154
|
for including inline CSS. Use the {Haml::Options#cdata `:cdata` option} to
|
1104
1155
|
control when CDATA tags are added.
|
1105
1156
|
|
1106
|
-
{#erb-filter}
|
1107
|
-
|
1108
|
-
Parses the filtered text with
|
1157
|
+
### `:erb` {#erb-filter}
|
1158
|
+
|
1159
|
+
Parses the filtered text with ERB, like an RHTML template. Not available if the
|
1109
1160
|
{Haml::Options#suppress_eval `:suppress_eval`} option is set to true. Embedded
|
1110
1161
|
Ruby code is evaluated in the same context as the Haml template. This filter is
|
1111
1162
|
implemented using Tilt.
|
1112
1163
|
|
1113
|
-
{#escaped-filter}
|
1114
|
-
|
1164
|
+
### `:escaped` {#escaped-filter}
|
1165
|
+
|
1115
1166
|
Works the same as plain, but HTML-escapes the text
|
1116
1167
|
before placing it in the document.
|
1117
1168
|
|
1118
|
-
{#javascript-filter}
|
1119
|
-
|
1169
|
+
### `:javascript` {#javascript-filter}
|
1170
|
+
|
1120
1171
|
Surrounds the filtered text with `<script>` and (optionally) CDATA tags.
|
1121
1172
|
Useful for including inline Javascript. Use the {Haml::Options#cdata `:cdata`
|
1122
1173
|
option} to control when CDATA tags are added.
|
1123
1174
|
|
1124
|
-
{#less-filter}
|
1125
|
-
|
1175
|
+
### `:less` {#less-filter}
|
1176
|
+
|
1126
1177
|
Parses the filtered text with [Less](http://lesscss.org/) to produce CSS output.
|
1127
1178
|
This filter is implemented using Tilt.
|
1128
1179
|
|
1129
|
-
{#markdown-filter}
|
1130
|
-
|
1180
|
+
### `:markdown` {#markdown-filter}
|
1181
|
+
|
1131
1182
|
Parses the filtered text with
|
1132
1183
|
[Markdown](http://daringfireball.net/projects/markdown). This filter is
|
1133
1184
|
implemented using Tilt.
|
1134
1185
|
|
1135
|
-
{#maruku-filter}
|
1136
|
-
|
1186
|
+
### `:maruku` {#maruku-filter}
|
1187
|
+
|
1137
1188
|
Parses the filtered text with [Maruku](https://github.com/nex3/maruku), which
|
1138
1189
|
has some non-standard extensions to Markdown.
|
1139
1190
|
|
@@ -1142,39 +1193,39 @@ contrib](https://github.com/haml/haml-contrib) but is loaded automatically for
|
|
1142
1193
|
historical reasons. In future versions of Haml it will likely not be loaded by
|
1143
1194
|
default. This filter is implemented using Tilt.
|
1144
1195
|
|
1145
|
-
{#plain-filter}
|
1146
|
-
|
1196
|
+
### `:plain` {#plain-filter}
|
1197
|
+
|
1147
1198
|
Does not parse the filtered text. This is useful for large blocks of text
|
1148
1199
|
without HTML tags, when you don't want lines starting with `.` or `-` to be
|
1149
1200
|
parsed.
|
1150
1201
|
|
1151
|
-
{#preserve-filter}
|
1152
|
-
|
1202
|
+
### `:preserve` {#preserve-filter}
|
1203
|
+
|
1153
1204
|
Inserts the filtered text into the template with whitespace preserved.
|
1154
1205
|
`preserve`d blocks of text aren't indented, and newlines are replaced with the
|
1155
1206
|
HTML escape code for newlines, to preserve nice-looking output. See also
|
1156
1207
|
[Whitespace Preservation](#whitespace_preservation).
|
1157
1208
|
|
1158
|
-
{#ruby-filter}
|
1159
|
-
|
1209
|
+
### `:ruby` {#ruby-filter}
|
1210
|
+
|
1160
1211
|
Parses the filtered text with the normal Ruby interpreter. Creates an `IO`
|
1161
1212
|
object named `haml_io`, anything written to it is output into the Haml document.
|
1162
1213
|
Not available if the {Haml::Options#suppress_eval `:suppress_eval`} option is
|
1163
1214
|
set to true. The Ruby code is evaluated in the same context as the Haml
|
1164
1215
|
template.
|
1165
1216
|
|
1166
|
-
{#sass-filter}
|
1167
|
-
|
1217
|
+
### `:sass` {#sass-filter}
|
1218
|
+
|
1168
1219
|
Parses the filtered text with [Sass](http://sass-lang.com/) to produce CSS
|
1169
1220
|
output. This filter is implemented using Tilt.
|
1170
1221
|
|
1171
|
-
{#scss-filter}
|
1172
|
-
|
1222
|
+
### `:scss` {#scss-filter}
|
1223
|
+
|
1173
1224
|
Parses the filtered text with Sass like the `:sass` filter, but uses the newer
|
1174
1225
|
SCSS syntax to produce CSS output. This filter is implemented using Tilt.
|
1175
1226
|
|
1176
|
-
{#textile-filter}
|
1177
|
-
|
1227
|
+
### `:textile` {#textile-filter}
|
1228
|
+
|
1178
1229
|
Parses the filtered text with [Textile](http://www.textism.com/tools/textile).
|
1179
1230
|
Only works if [RedCloth](http://redcloth.org) is installed.
|
1180
1231
|
|
@@ -1194,8 +1245,8 @@ the whitespace removal methods allow. There are a few helper methods that are
|
|
1194
1245
|
useful when dealing with inline content. All these methods take a Haml block to
|
1195
1246
|
modify.
|
1196
1247
|
|
1197
|
-
{#surround}
|
1198
|
-
|
1248
|
+
### surround {#surround}
|
1249
|
+
|
1199
1250
|
Surrounds a Haml block with text. Expects 1 or 2 string arguments used to
|
1200
1251
|
surround the Haml block. If a second argument is not provided, the first
|
1201
1252
|
argument is used as the second.
|
@@ -1203,15 +1254,15 @@ argument is used as the second.
|
|
1203
1254
|
= surround "(", ")" do
|
1204
1255
|
= link_to "learn more", "#"
|
1205
1256
|
|
1206
|
-
{#precede}
|
1207
|
-
|
1257
|
+
### precede {#precede}
|
1258
|
+
|
1208
1259
|
Prepends a Haml block with text. Expects 1 argument.
|
1209
1260
|
|
1210
1261
|
= precede "*" do
|
1211
1262
|
%span Required
|
1212
1263
|
|
1213
|
-
{#succeed}
|
1214
|
-
|
1264
|
+
### succeed {#succeed}
|
1265
|
+
|
1215
1266
|
Appends a Haml block with text. Expects 1 argument.
|
1216
1267
|
|
1217
1268
|
Begin by
|
data/Rakefile
CHANGED
@@ -1,45 +1,38 @@
|
|
1
1
|
require "rake/clean"
|
2
2
|
require "rake/testtask"
|
3
|
-
require "
|
3
|
+
require "bundler/gem_tasks"
|
4
4
|
|
5
5
|
task :default => :test
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
# FIXME: Redefining :test task to run test/options_test.rb in isolated process since it depends on whether Rails is loaded or not.
|
8
|
+
# Remove this task when we finished changing escape_html option to be true by default.
|
9
|
+
isolated_test = Rake::TestTask.new do |t|
|
10
|
+
t.libs << 'test'
|
11
|
+
t.test_files = %w[test/options_test.rb]
|
12
|
+
t.warning = true
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
Rake::TestTask.new do |t|
|
16
|
+
t.libs << 'test'
|
17
|
+
t.test_files = Dir['test/*_test.rb'] + Dir['test/haml-spec/*_test.rb'] - isolated_test.file_list
|
18
|
+
t.warning = true
|
19
|
+
t.verbose = true
|
14
20
|
end
|
15
21
|
|
16
|
-
|
22
|
+
CLEAN.replace %w(pkg doc coverage .yardoc test/haml vendor)
|
23
|
+
|
24
|
+
desc "Benchmark Haml against ERB. TIMES=n sets the number of runs, default is 1000."
|
17
25
|
task :benchmark do
|
18
26
|
sh "ruby benchmark.rb #{ENV['TIMES']}"
|
19
27
|
end
|
20
28
|
|
21
|
-
Rake::TestTask.new do |t|
|
22
|
-
t.libs << 'lib' << 'test'
|
23
|
-
# haml-spec tests are explicitly added after other tests so they don't
|
24
|
-
# interfere with the Haml loading process which can cause test failures
|
25
|
-
files = Dir["test/*_test.rb"]
|
26
|
-
files.concat(Dir['test/haml-spec/*_test.rb'])
|
27
|
-
t.test_files = files
|
28
|
-
t.verbose = true
|
29
|
-
end
|
30
|
-
|
31
29
|
task :set_coverage_env do
|
32
30
|
ENV["COVERAGE"] = "true"
|
33
31
|
end
|
34
32
|
|
35
|
-
desc "Run Simplecov
|
33
|
+
desc "Run Simplecov"
|
36
34
|
task :coverage => [:set_coverage_env, :test]
|
37
35
|
|
38
|
-
gemspec = File.expand_path("../haml.gemspec", __FILE__)
|
39
|
-
if File.exist? gemspec
|
40
|
-
Gem::PackageTask.new(eval(File.read(gemspec))) { |pkg| }
|
41
|
-
end
|
42
|
-
|
43
36
|
task :submodules do
|
44
37
|
if File.exist?(File.dirname(__FILE__) + "/.git")
|
45
38
|
sh %{git submodule sync}
|
@@ -47,31 +40,32 @@ task :submodules do
|
|
47
40
|
end
|
48
41
|
end
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
require '
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
task :undocumented do
|
58
|
-
command = 'yard --list --query '
|
59
|
-
command << '"object.docstring.blank? && '
|
60
|
-
command << '!(object.type == :method && object.is_alias?)"'
|
61
|
-
sh command
|
43
|
+
namespace :doc do
|
44
|
+
task :sass do
|
45
|
+
require 'sass'
|
46
|
+
Dir["yard/default/**/*.sass"].each do |sass|
|
47
|
+
File.open(sass.gsub(/sass$/, 'css'), 'w') do |f|
|
48
|
+
f.write(Sass::Engine.new(File.read(sass)).render)
|
49
|
+
end
|
62
50
|
end
|
63
51
|
end
|
64
52
|
|
65
|
-
desc "
|
66
|
-
task
|
53
|
+
desc "List all undocumented methods and classes."
|
54
|
+
task :undocumented do
|
55
|
+
command = 'yard --list --query '
|
56
|
+
command << '"object.docstring.blank? && '
|
57
|
+
command << '!(object.type == :method && object.is_alias?)"'
|
58
|
+
sh command
|
59
|
+
end
|
60
|
+
end
|
67
61
|
|
68
|
-
|
69
|
-
|
62
|
+
desc "Generate documentation"
|
63
|
+
task(:doc => 'doc:sass') {sh "yard"}
|
70
64
|
|
71
|
-
|
72
|
-
|
65
|
+
desc "Generate documentation incrementally"
|
66
|
+
task(:redoc) {sh "yard -c"}
|
73
67
|
|
74
|
-
|
68
|
+
desc <<END
|
75
69
|
Profile Haml.
|
76
70
|
TIMES=n sets the number of runs. Defaults to 1000.
|
77
71
|
FILE=str sets the file to profile. Defaults to 'standard'
|
@@ -80,15 +74,14 @@ Profile Haml.
|
|
80
74
|
END
|
81
75
|
task :profile do
|
82
76
|
times = (ENV['TIMES'] || '1000').to_i
|
83
|
-
file = ENV['FILE']
|
77
|
+
file = ENV['FILE'] || 'test/templates/standard.haml'
|
84
78
|
|
85
79
|
require 'bundler/setup'
|
86
80
|
require 'ruby-prof'
|
87
81
|
require 'haml'
|
88
|
-
|
89
|
-
file = File.read(File.expand_path("../test/templates/#{file || 'standard'}.haml", __FILE__))
|
82
|
+
file = File.read(File.expand_path("../#{file}", __FILE__))
|
90
83
|
obj = Object.new
|
91
|
-
Haml::Engine.new(file
|
84
|
+
Haml::Engine.new(file).def_method(obj, :render)
|
92
85
|
result = RubyProf.profile { times.times { obj.render } }
|
93
86
|
|
94
87
|
RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
|
@@ -103,14 +96,13 @@ def gemfiles
|
|
103
96
|
end
|
104
97
|
|
105
98
|
def with_each_gemfile
|
106
|
-
old_env = ENV['BUNDLE_GEMFILE']
|
107
99
|
gemfiles.each do |gemfile|
|
108
|
-
|
109
|
-
|
110
|
-
|
100
|
+
Bundler.with_clean_env do
|
101
|
+
puts "Using gemfile: #{gemfile}"
|
102
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
103
|
+
yield
|
104
|
+
end
|
111
105
|
end
|
112
|
-
ensure
|
113
|
-
ENV['BUNDLE_GEMFILE'] = old_env
|
114
106
|
end
|
115
107
|
|
116
108
|
namespace :test do
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
3
|
+
module AttributeBuilder
|
4
|
+
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
5
|
+
INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
|
9
|
+
# @TODO this is an absolutely ridiculous amount of arguments. At least
|
10
|
+
# some of this needs to be moved into an instance method.
|
11
|
+
join_char = hyphenate_data_attrs ? '-' : '_'
|
12
|
+
|
13
|
+
attributes.each do |key, value|
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
data_attributes = attributes.delete(key)
|
16
|
+
data_attributes = flatten_data_attributes(data_attributes, '', join_char)
|
17
|
+
data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
|
18
|
+
verify_attribute_names!(data_attributes.keys)
|
19
|
+
attributes = data_attributes.merge(attributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
result = attributes.collect do |attr, value|
|
24
|
+
next if value.nil?
|
25
|
+
|
26
|
+
value = filter_and_join(value, ' ') if attr == 'class'
|
27
|
+
value = filter_and_join(value, '_') if attr == 'id'
|
28
|
+
|
29
|
+
if value == true
|
30
|
+
next " #{attr}" if is_html
|
31
|
+
next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
|
32
|
+
elsif value == false
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
value =
|
37
|
+
if escape_attrs == :once
|
38
|
+
Haml::Helpers.escape_once(value.to_s)
|
39
|
+
elsif escape_attrs
|
40
|
+
Haml::Helpers.html_escape(value.to_s)
|
41
|
+
else
|
42
|
+
value.to_s
|
43
|
+
end
|
44
|
+
" #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
|
45
|
+
end
|
46
|
+
result.compact!
|
47
|
+
result.sort!
|
48
|
+
result.join
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [String, nil]
|
52
|
+
def filter_and_join(value, separator)
|
53
|
+
return '' if (value.respond_to?(:empty?) && value.empty?)
|
54
|
+
|
55
|
+
if value.is_a?(Array)
|
56
|
+
value = value.flatten
|
57
|
+
value.map! {|item| item ? item.to_s : nil}
|
58
|
+
value.compact!
|
59
|
+
value = value.join(separator)
|
60
|
+
else
|
61
|
+
value = value ? value.to_s : nil
|
62
|
+
end
|
63
|
+
!value.nil? && !value.empty? && value
|
64
|
+
end
|
65
|
+
|
66
|
+
# Merges two attribute hashes.
|
67
|
+
# This is the same as `to.merge!(from)`,
|
68
|
+
# except that it merges id, class, and data attributes.
|
69
|
+
#
|
70
|
+
# ids are concatenated with `"_"`,
|
71
|
+
# and classes are concatenated with `" "`.
|
72
|
+
# data hashes are simply merged.
|
73
|
+
#
|
74
|
+
# Destructively modifies `to`.
|
75
|
+
#
|
76
|
+
# @param to [{String => String,Hash}] The attribute hash to merge into
|
77
|
+
# @param from [{String => Object}] The attribute hash to merge from
|
78
|
+
# @return [{String => String,Hash}] `to`, after being merged
|
79
|
+
def merge_attributes!(to, from)
|
80
|
+
from.keys.each do |key|
|
81
|
+
to[key] = merge_value(key, to[key], from[key])
|
82
|
+
end
|
83
|
+
to
|
84
|
+
end
|
85
|
+
|
86
|
+
# Merge multiple values to one attribute value. No destructive operation.
|
87
|
+
#
|
88
|
+
# @param key [String]
|
89
|
+
# @param values [Array<Object>]
|
90
|
+
# @return [String,Hash]
|
91
|
+
def merge_values(key, *values)
|
92
|
+
values.inject(nil) do |to, from|
|
93
|
+
merge_value(key, to, from)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def verify_attribute_names!(attribute_names)
|
98
|
+
attribute_names.each do |attribute_name|
|
99
|
+
if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
|
100
|
+
raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Merge a couple of values to one attribute value. No destructive operation.
|
108
|
+
#
|
109
|
+
# @param to [String,Hash,nil]
|
110
|
+
# @param from [Object]
|
111
|
+
# @return [String,Hash]
|
112
|
+
def merge_value(key, to, from)
|
113
|
+
if from.kind_of?(Hash) || to.kind_of?(Hash)
|
114
|
+
from = { nil => from } if !from.is_a?(Hash)
|
115
|
+
to = { nil => to } if !to.is_a?(Hash)
|
116
|
+
to.merge(from)
|
117
|
+
elsif key == 'id'
|
118
|
+
merged_id = filter_and_join(from, '_')
|
119
|
+
if to && merged_id
|
120
|
+
merged_id = "#{to}_#{merged_id}"
|
121
|
+
elsif to || merged_id
|
122
|
+
merged_id ||= to
|
123
|
+
end
|
124
|
+
merged_id
|
125
|
+
elsif key == 'class'
|
126
|
+
merged_class = filter_and_join(from, ' ')
|
127
|
+
if to && merged_class
|
128
|
+
merged_class = (merged_class.split(' ') | to.split(' ')).sort.join(' ')
|
129
|
+
elsif to || merged_class
|
130
|
+
merged_class ||= to
|
131
|
+
end
|
132
|
+
merged_class
|
133
|
+
else
|
134
|
+
from
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def build_data_keys(data_hash, hyphenate, attr_name="data")
|
139
|
+
Hash[data_hash.map do |name, value|
|
140
|
+
if name == nil
|
141
|
+
[attr_name, value]
|
142
|
+
elsif hyphenate
|
143
|
+
["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
|
144
|
+
else
|
145
|
+
["#{attr_name}-#{name}", value]
|
146
|
+
end
|
147
|
+
end]
|
148
|
+
end
|
149
|
+
|
150
|
+
def flatten_data_attributes(data, key, join_char, seen = [])
|
151
|
+
return {key => data} unless data.is_a?(Hash)
|
152
|
+
|
153
|
+
return {key => nil} if seen.include? data.object_id
|
154
|
+
seen << data.object_id
|
155
|
+
|
156
|
+
data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
|
157
|
+
joined = key == '' ? k : [key, k].join(join_char)
|
158
|
+
hash.merge! flatten_data_attributes(v, joined, join_char, seen)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|