hamlit 1.7.2 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +25 -37
  5. data/CHANGELOG.md +18 -0
  6. data/Gemfile +16 -0
  7. data/LICENSE.txt +23 -2
  8. data/README.md +106 -48
  9. data/REFERENCE.md +222 -0
  10. data/Rakefile +77 -19
  11. data/benchmark/boolean_attribute.haml +6 -0
  12. data/benchmark/class_attribute.haml +5 -0
  13. data/benchmark/common_attribute.haml +3 -0
  14. data/benchmark/data_attribute.haml +4 -0
  15. data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
  16. data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
  17. data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
  18. data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
  19. data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
  20. data/benchmark/etc/attribute_builder.haml +5 -0
  21. data/benchmark/etc/real_sample.haml +888 -0
  22. data/benchmark/etc/real_sample.rb +11 -0
  23. data/benchmark/etc/static_analyzer.haml +1 -0
  24. data/benchmark/etc/tags.haml +3 -0
  25. data/benchmark/ext/build_data.rb +15 -0
  26. data/benchmark/ext/build_id.rb +13 -0
  27. data/benchmark/id_attribute.haml +3 -0
  28. data/benchmark/plain.haml +4 -0
  29. data/benchmark/script.haml +4 -0
  30. data/benchmark/slim/LICENSE +21 -0
  31. data/{benchmarks → benchmark/slim}/context.rb +2 -4
  32. data/benchmark/slim/run-benchmarks.rb +94 -0
  33. data/{benchmarks → benchmark/slim}/view.erb +3 -3
  34. data/{benchmarks → benchmark/slim}/view.haml +0 -0
  35. data/{benchmarks/view.escaped.slim → benchmark/slim/view.slim} +1 -1
  36. data/benchmark/string_interpolation.haml +2 -0
  37. data/benchmark/utils/benchmark_ips_extension.rb +43 -0
  38. data/bin/bench +85 -0
  39. data/bin/clone +14 -0
  40. data/bin/console +11 -0
  41. data/bin/lineprof +48 -0
  42. data/bin/ruby +3 -0
  43. data/bin/setup +7 -0
  44. data/bin/stackprof +27 -0
  45. data/{test → bin/test} +6 -10
  46. data/{bin → exe}/hamlit +0 -0
  47. data/ext/hamlit/extconf.rb +14 -0
  48. data/ext/hamlit/hamlit.c +512 -0
  49. data/ext/hamlit/houdini/.gitignore +3 -0
  50. data/ext/hamlit/houdini/COPYING +7 -0
  51. data/ext/hamlit/houdini/Makefile +79 -0
  52. data/ext/hamlit/houdini/README.md +59 -0
  53. data/ext/hamlit/houdini/buffer.c +249 -0
  54. data/ext/hamlit/houdini/buffer.h +113 -0
  55. data/ext/hamlit/houdini/houdini.h +46 -0
  56. data/ext/hamlit/houdini/houdini_href_e.c +115 -0
  57. data/ext/hamlit/houdini/houdini_html_e.c +90 -0
  58. data/ext/hamlit/houdini/houdini_html_u.c +122 -0
  59. data/ext/hamlit/houdini/houdini_js_e.c +90 -0
  60. data/ext/hamlit/houdini/houdini_js_u.c +60 -0
  61. data/ext/hamlit/houdini/houdini_uri_e.c +107 -0
  62. data/ext/hamlit/houdini/houdini_uri_u.c +68 -0
  63. data/ext/hamlit/houdini/houdini_xml_e.c +136 -0
  64. data/ext/hamlit/houdini/html_unescape.gperf +258 -0
  65. data/ext/hamlit/houdini/html_unescape.h +754 -0
  66. data/ext/hamlit/houdini/tools/build_table.py +13 -0
  67. data/ext/hamlit/houdini/tools/build_tables.c +51 -0
  68. data/ext/hamlit/houdini/tools/wikipedia_table.txt +2025 -0
  69. data/hamlit.gemspec +30 -31
  70. data/lib/hamlit.rb +3 -1
  71. data/lib/hamlit/attribute_builder.rb +12 -0
  72. data/lib/hamlit/cli.rb +44 -43
  73. data/lib/hamlit/compiler.rb +92 -16
  74. data/lib/hamlit/compiler/attribute_compiler.rb +148 -0
  75. data/lib/hamlit/compiler/children_compiler.rb +111 -0
  76. data/lib/hamlit/compiler/comment_compiler.rb +36 -0
  77. data/lib/hamlit/compiler/doctype_compiler.rb +45 -0
  78. data/lib/hamlit/compiler/script_compiler.rb +97 -0
  79. data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
  80. data/lib/hamlit/compiler/tag_compiler.rb +69 -0
  81. data/lib/hamlit/engine.rb +12 -7
  82. data/lib/hamlit/error.rb +14 -0
  83. data/lib/hamlit/escapable.rb +12 -0
  84. data/lib/hamlit/filters.rb +65 -0
  85. data/lib/hamlit/filters/base.rb +4 -62
  86. data/lib/hamlit/filters/coffee.rb +9 -7
  87. data/lib/hamlit/filters/css.rb +25 -8
  88. data/lib/hamlit/filters/erb.rb +4 -6
  89. data/lib/hamlit/filters/escaped.rb +11 -9
  90. data/lib/hamlit/filters/javascript.rb +25 -8
  91. data/lib/hamlit/filters/less.rb +9 -7
  92. data/lib/hamlit/filters/markdown.rb +5 -6
  93. data/lib/hamlit/filters/plain.rb +11 -15
  94. data/lib/hamlit/filters/preserve.rb +15 -5
  95. data/lib/hamlit/filters/ruby.rb +3 -5
  96. data/lib/hamlit/filters/sass.rb +9 -7
  97. data/lib/hamlit/filters/scss.rb +9 -7
  98. data/lib/hamlit/filters/text_base.rb +24 -0
  99. data/lib/hamlit/filters/tilt_base.rb +47 -0
  100. data/lib/hamlit/hash_parser.rb +107 -0
  101. data/lib/hamlit/html.rb +9 -6
  102. data/lib/hamlit/identity.rb +12 -0
  103. data/lib/hamlit/object_ref.rb +29 -0
  104. data/lib/hamlit/parser.rb +25 -142
  105. data/lib/hamlit/parser/MIT-LICENSE +20 -0
  106. data/lib/hamlit/parser/README.md +28 -0
  107. data/lib/hamlit/parser/haml_buffer.rb +348 -0
  108. data/lib/hamlit/parser/haml_compiler.rb +553 -0
  109. data/lib/hamlit/parser/haml_error.rb +61 -0
  110. data/lib/hamlit/parser/haml_helpers.rb +727 -0
  111. data/lib/hamlit/parser/haml_options.rb +286 -0
  112. data/lib/hamlit/parser/haml_parser.rb +801 -0
  113. data/lib/hamlit/parser/haml_util.rb +283 -0
  114. data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
  115. data/lib/hamlit/{helpers.rb → rails_helpers.rb} +2 -7
  116. data/lib/hamlit/rails_template.rb +30 -0
  117. data/lib/hamlit/railtie.rb +1 -12
  118. data/lib/hamlit/ruby_expression.rb +31 -0
  119. data/lib/hamlit/static_analyzer.rb +49 -0
  120. data/lib/hamlit/string_interpolation.rb +69 -0
  121. data/lib/hamlit/template.rb +8 -0
  122. data/lib/hamlit/utils.rb +9 -0
  123. data/lib/hamlit/version.rb +1 -1
  124. metadata +116 -324
  125. data/.rspec +0 -2
  126. data/benchmarks/benchmark.rb +0 -110
  127. data/benchmarks/view.slim +0 -17
  128. data/doc/README.md +0 -19
  129. data/doc/engine/indent.md +0 -48
  130. data/doc/engine/new_attribute.md +0 -77
  131. data/doc/engine/old_attributes.md +0 -198
  132. data/doc/engine/silent_script.md +0 -97
  133. data/doc/engine/tag.md +0 -48
  134. data/doc/engine/text.md +0 -64
  135. data/doc/faml/README.md +0 -16
  136. data/doc/faml/engine/indent.md +0 -48
  137. data/doc/faml/engine/old_attributes.md +0 -111
  138. data/doc/faml/engine/silent_script.md +0 -97
  139. data/doc/faml/engine/text.md +0 -59
  140. data/doc/faml/filters/erb.md +0 -24
  141. data/doc/faml/filters/javascript.md +0 -27
  142. data/doc/faml/filters/less.md +0 -57
  143. data/doc/faml/filters/plain.md +0 -25
  144. data/doc/filters/erb.md +0 -31
  145. data/doc/filters/javascript.md +0 -83
  146. data/doc/filters/less.md +0 -57
  147. data/doc/filters/markdown.md +0 -31
  148. data/doc/filters/plain.md +0 -25
  149. data/doc/haml/README.md +0 -15
  150. data/doc/haml/engine/new_attribute.md +0 -77
  151. data/doc/haml/engine/old_attributes.md +0 -142
  152. data/doc/haml/engine/tag.md +0 -48
  153. data/doc/haml/engine/text.md +0 -29
  154. data/doc/haml/filters/erb.md +0 -26
  155. data/doc/haml/filters/javascript.md +0 -76
  156. data/doc/haml/filters/markdown.md +0 -31
  157. data/lib/hamlit/attribute.rb +0 -78
  158. data/lib/hamlit/compilers/attributes.rb +0 -108
  159. data/lib/hamlit/compilers/comment.rb +0 -13
  160. data/lib/hamlit/compilers/doctype.rb +0 -39
  161. data/lib/hamlit/compilers/filter.rb +0 -53
  162. data/lib/hamlit/compilers/new_attribute.rb +0 -115
  163. data/lib/hamlit/compilers/old_attribute.rb +0 -241
  164. data/lib/hamlit/compilers/runtime_attribute.rb +0 -58
  165. data/lib/hamlit/compilers/script.rb +0 -31
  166. data/lib/hamlit/compilers/strip.rb +0 -19
  167. data/lib/hamlit/compilers/text.rb +0 -111
  168. data/lib/hamlit/concerns/attribute_builder.rb +0 -22
  169. data/lib/hamlit/concerns/balanceable.rb +0 -68
  170. data/lib/hamlit/concerns/deprecation.rb +0 -20
  171. data/lib/hamlit/concerns/error.rb +0 -31
  172. data/lib/hamlit/concerns/escapable.rb +0 -17
  173. data/lib/hamlit/concerns/included.rb +0 -28
  174. data/lib/hamlit/concerns/indentable.rb +0 -117
  175. data/lib/hamlit/concerns/lexable.rb +0 -32
  176. data/lib/hamlit/concerns/line_reader.rb +0 -62
  177. data/lib/hamlit/concerns/registerable.rb +0 -24
  178. data/lib/hamlit/concerns/string_interpolation.rb +0 -48
  179. data/lib/hamlit/concerns/whitespace.rb +0 -91
  180. data/lib/hamlit/filters/tilt.rb +0 -41
  181. data/lib/hamlit/parsers/attribute.rb +0 -71
  182. data/lib/hamlit/parsers/comment.rb +0 -30
  183. data/lib/hamlit/parsers/doctype.rb +0 -18
  184. data/lib/hamlit/parsers/filter.rb +0 -18
  185. data/lib/hamlit/parsers/multiline.rb +0 -58
  186. data/lib/hamlit/parsers/script.rb +0 -126
  187. data/lib/hamlit/parsers/tag.rb +0 -83
  188. data/lib/hamlit/parsers/text.rb +0 -28
  189. data/lib/hamlit/temple.rb +0 -9
  190. data/release +0 -6
  191. data/spec/Rakefile +0 -72
  192. data/spec/hamlit/engine/comment_spec.rb +0 -56
  193. data/spec/hamlit/engine/doctype_spec.rb +0 -19
  194. data/spec/hamlit/engine/error_spec.rb +0 -135
  195. data/spec/hamlit/engine/indent_spec.rb +0 -42
  196. data/spec/hamlit/engine/multiline_spec.rb +0 -44
  197. data/spec/hamlit/engine/new_attribute_spec.rb +0 -110
  198. data/spec/hamlit/engine/old_attributes_spec.rb +0 -404
  199. data/spec/hamlit/engine/script_spec.rb +0 -116
  200. data/spec/hamlit/engine/silent_script_spec.rb +0 -213
  201. data/spec/hamlit/engine/tag_spec.rb +0 -295
  202. data/spec/hamlit/engine/text_spec.rb +0 -239
  203. data/spec/hamlit/engine_spec.rb +0 -58
  204. data/spec/hamlit/filters/coffee_spec.rb +0 -60
  205. data/spec/hamlit/filters/css_spec.rb +0 -33
  206. data/spec/hamlit/filters/erb_spec.rb +0 -16
  207. data/spec/hamlit/filters/javascript_spec.rb +0 -82
  208. data/spec/hamlit/filters/less_spec.rb +0 -37
  209. data/spec/hamlit/filters/markdown_spec.rb +0 -30
  210. data/spec/hamlit/filters/plain_spec.rb +0 -15
  211. data/spec/hamlit/filters/ruby_spec.rb +0 -24
  212. data/spec/hamlit/filters/sass_spec.rb +0 -33
  213. data/spec/hamlit/filters/scss_spec.rb +0 -37
  214. data/spec/hamlit/haml_spec.rb +0 -910
  215. data/spec/rails/.gitignore +0 -18
  216. data/spec/rails/.rspec +0 -2
  217. data/spec/rails/Gemfile +0 -19
  218. data/spec/rails/README.rdoc +0 -28
  219. data/spec/rails/Rakefile +0 -6
  220. data/spec/rails/app/assets/images/.keep +0 -0
  221. data/spec/rails/app/assets/javascripts/application.js +0 -15
  222. data/spec/rails/app/assets/stylesheets/application.css +0 -15
  223. data/spec/rails/app/controllers/application_controller.rb +0 -8
  224. data/spec/rails/app/controllers/concerns/.keep +0 -0
  225. data/spec/rails/app/controllers/users_controller.rb +0 -23
  226. data/spec/rails/app/helpers/application_helper.rb +0 -2
  227. data/spec/rails/app/mailers/.keep +0 -0
  228. data/spec/rails/app/models/.keep +0 -0
  229. data/spec/rails/app/models/concerns/.keep +0 -0
  230. data/spec/rails/app/views/application/index.html.haml +0 -18
  231. data/spec/rails/app/views/layouts/application.html.haml +0 -12
  232. data/spec/rails/app/views/users/capture.html.haml +0 -5
  233. data/spec/rails/app/views/users/capture_haml.html.haml +0 -5
  234. data/spec/rails/app/views/users/form.html.haml +0 -2
  235. data/spec/rails/app/views/users/helpers.html.haml +0 -10
  236. data/spec/rails/app/views/users/index.html.haml +0 -9
  237. data/spec/rails/app/views/users/inline.html.haml +0 -6
  238. data/spec/rails/app/views/users/old_attributes.html.haml +0 -5
  239. data/spec/rails/app/views/users/safe_buffer.html.haml +0 -4
  240. data/spec/rails/app/views/users/whitespace.html.haml +0 -4
  241. data/spec/rails/bin/bundle +0 -3
  242. data/spec/rails/bin/rails +0 -8
  243. data/spec/rails/bin/rake +0 -8
  244. data/spec/rails/bin/setup +0 -29
  245. data/spec/rails/bin/spring +0 -15
  246. data/spec/rails/config.ru +0 -4
  247. data/spec/rails/config/application.rb +0 -34
  248. data/spec/rails/config/boot.rb +0 -3
  249. data/spec/rails/config/database.yml +0 -25
  250. data/spec/rails/config/environment.rb +0 -5
  251. data/spec/rails/config/environments/development.rb +0 -41
  252. data/spec/rails/config/environments/production.rb +0 -79
  253. data/spec/rails/config/environments/test.rb +0 -42
  254. data/spec/rails/config/initializers/assets.rb +0 -11
  255. data/spec/rails/config/initializers/backtrace_silencers.rb +0 -7
  256. data/spec/rails/config/initializers/cookies_serializer.rb +0 -3
  257. data/spec/rails/config/initializers/filter_parameter_logging.rb +0 -4
  258. data/spec/rails/config/initializers/inflections.rb +0 -16
  259. data/spec/rails/config/initializers/mime_types.rb +0 -4
  260. data/spec/rails/config/initializers/session_store.rb +0 -3
  261. data/spec/rails/config/initializers/wrap_parameters.rb +0 -14
  262. data/spec/rails/config/locales/en.yml +0 -24
  263. data/spec/rails/config/routes.rb +0 -16
  264. data/spec/rails/config/secrets.yml +0 -22
  265. data/spec/rails/db/schema.rb +0 -16
  266. data/spec/rails/db/seeds.rb +0 -7
  267. data/spec/rails/lib/assets/.keep +0 -0
  268. data/spec/rails/lib/tasks/.keep +0 -0
  269. data/spec/rails/log/.keep +0 -0
  270. data/spec/rails/public/404.html +0 -67
  271. data/spec/rails/public/422.html +0 -67
  272. data/spec/rails/public/500.html +0 -66
  273. data/spec/rails/public/favicon.ico +0 -0
  274. data/spec/rails/public/robots.txt +0 -5
  275. data/spec/rails/spec/hamlit_spec.rb +0 -123
  276. data/spec/rails/spec/rails_helper.rb +0 -56
  277. data/spec/rails/spec/spec_helper.rb +0 -91
  278. data/spec/rails/vendor/assets/javascripts/.keep +0 -0
  279. data/spec/rails/vendor/assets/stylesheets/.keep +0 -0
  280. data/spec/spec_helper.rb +0 -36
  281. data/spec/spec_helper/document_generator.rb +0 -93
  282. data/spec/spec_helper/render_helper.rb +0 -120
  283. data/spec/spec_helper/test_case.rb +0 -55
data/bin/ruby ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ bundle exec ruby -Ilib:test -rtest_helper $@
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/bin/stackprof ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'hamlit'
5
+ require 'stackprof'
6
+
7
+ def open_flamegraph(report)
8
+ temp = `mktemp /tmp/stackflame-XXXXXXXX`.strip
9
+ data_path = "#{temp}.js"
10
+ system("mv #{temp} #{data_path}")
11
+
12
+ File.open(data_path, 'w') do |f|
13
+ report.print_flamegraph(f)
14
+ end
15
+
16
+ viewer_path = File.join(`bundle show stackprof`.strip, 'lib/stackprof/flamegraph/viewer.html')
17
+ url = "file://#{viewer_path}?data=#{data_path}"
18
+ system(%Q[osascript -e 'open location "#{url}"'])
19
+ end
20
+
21
+ haml = File.read(ARGV.first)
22
+ StackProf.start(mode: :wall, interval: 1, raw: false)
23
+ Hamlit::Engine.new.call(haml)
24
+ StackProf.stop
25
+
26
+ report = StackProf::Report.new(StackProf.results)
27
+ report.print_text(false)
data/{test → bin/test} RENAMED
@@ -1,14 +1,14 @@
1
1
  #!/bin/bash
2
2
 
3
- set -e
4
- trap 'rm .ruby-version' 0
5
-
6
3
  VERSIONS=(
7
4
  2.0.0
8
- 2.1.6
9
- 2.2.2
5
+ 2.1.7
6
+ 2.2.3
10
7
  )
11
8
 
9
+ set -e
10
+ trap 'echo "${VERSIONS[2]}" > .ruby-version' 0
11
+
12
12
  function test_with() {
13
13
  version=$1
14
14
  rbenv local $version
@@ -16,13 +16,9 @@ function test_with() {
16
16
  bundle install
17
17
  fi
18
18
  ruby -v
19
- rake spec
19
+ rake test
20
20
  }
21
21
 
22
22
  for version in ${VERSIONS[@]}; do
23
23
  test_with $version
24
24
  done
25
-
26
- rbenv local ${VERSIONS[2]}
27
- ruby -v
28
- rake rails:spec
data/{bin → exe}/hamlit RENAMED
File without changes
@@ -0,0 +1,14 @@
1
+ require 'mkmf'
2
+
3
+ houdini_dir = File.expand_path('./houdini', __dir__)
4
+ $INCFLAGS << " -I#{houdini_dir}"
5
+ $CFLAGS << ' -Wall -Wextra'
6
+
7
+ $srcs = %w[hamlit.c]
8
+ Dir[File.join(houdini_dir, '*.c')].each do |path|
9
+ src = File.basename(path)
10
+ FileUtils.ln_s(path, src, force: true)
11
+ $srcs << src
12
+ end
13
+
14
+ create_makefile('hamlit/hamlit')
@@ -0,0 +1,512 @@
1
+ #include <ruby.h>
2
+ #include <ruby/encoding.h>
3
+ #include "houdini.h"
4
+ #include "string.h"
5
+
6
+ VALUE mAttributeBuilder, mObjectRef;
7
+ static ID id_flatten, id_keys, id_parse, id_prepend, id_tr, id_uniq_bang;
8
+ static ID id_data, id_equal, id_hyphen, id_space, id_underscore;
9
+ static ID id_boolean_attributes, id_xhtml;
10
+
11
+ static VALUE str_data() { return rb_const_get(mAttributeBuilder, id_data); }
12
+ static VALUE str_equal() { return rb_const_get(mAttributeBuilder, id_equal); }
13
+ static VALUE str_hyphen() { return rb_const_get(mAttributeBuilder, id_hyphen); }
14
+ static VALUE str_space() { return rb_const_get(mAttributeBuilder, id_space); }
15
+ static VALUE str_underscore() { return rb_const_get(mAttributeBuilder, id_underscore); }
16
+
17
+ static void
18
+ delete_falsey_values(VALUE values)
19
+ {
20
+ VALUE value;
21
+ long i;
22
+
23
+ for (i = RARRAY_LEN(values) - 1; 0 <= i; i--) {
24
+ value = rb_ary_entry(values, i);
25
+ if (!RTEST(value)) {
26
+ rb_ary_delete_at(values, i);
27
+ }
28
+ }
29
+ }
30
+
31
+ static int
32
+ str_eq(VALUE str, const char *cstr)
33
+ {
34
+ return strcmp(RSTRING_PTR(str), cstr) == 0;
35
+ }
36
+
37
+ static VALUE
38
+ to_s(VALUE value)
39
+ {
40
+ return rb_convert_type(value, T_STRING, "String", "to_s");
41
+ }
42
+
43
+ static VALUE
44
+ hyphenate(VALUE str)
45
+ {
46
+ long i;
47
+
48
+ if (OBJ_FROZEN(str)) str = rb_str_dup(str);
49
+
50
+ for (i = 0; i < RSTRING_LEN(str); i++) {
51
+ if (RSTRING_PTR(str)[i] == '_') {
52
+ rb_str_update(str, i, 1, str_hyphen());
53
+ }
54
+ }
55
+ return str;
56
+ }
57
+
58
+ static VALUE
59
+ escape_html(VALUE str)
60
+ {
61
+ gh_buf buf = GH_BUF_INIT;
62
+
63
+ Check_Type(str, T_STRING);
64
+
65
+ if (houdini_escape_html0(&buf, (const uint8_t *)RSTRING_PTR(str), RSTRING_LEN(str), 0)) {
66
+ str = rb_enc_str_new(buf.ptr, buf.size, rb_utf8_encoding());
67
+ gh_buf_free(&buf);
68
+ }
69
+
70
+ return str;
71
+ }
72
+
73
+ static VALUE
74
+ escape_attribute(VALUE escape_attrs, VALUE str)
75
+ {
76
+ if (RTEST(escape_attrs)) {
77
+ return escape_html(str);
78
+ } else {
79
+ return str;
80
+ }
81
+ }
82
+
83
+ static VALUE
84
+ rb_escape_html(RB_UNUSED_VAR(VALUE self), VALUE value)
85
+ {
86
+ return escape_html(to_s(value));
87
+ }
88
+
89
+ static VALUE
90
+ hamlit_build_id(VALUE escape_attrs, VALUE values)
91
+ {
92
+ VALUE attr_value;
93
+
94
+ values = rb_funcall(values, id_flatten, 0);
95
+ delete_falsey_values(values);
96
+
97
+ attr_value = rb_ary_join(values, str_underscore());
98
+ return escape_attribute(escape_attrs, attr_value);
99
+ }
100
+
101
+ static VALUE
102
+ hamlit_build_single_class(VALUE escape_attrs, VALUE value)
103
+ {
104
+ switch (TYPE(value)) {
105
+ case T_STRING:
106
+ break;
107
+ case T_ARRAY:
108
+ value = rb_funcall(value, id_flatten, 0);
109
+ delete_falsey_values(value);
110
+ value = rb_ary_join(value, str_space());
111
+ break;
112
+ default:
113
+ if (RTEST(value)) {
114
+ value = to_s(value);
115
+ } else {
116
+ return rb_str_new_cstr("");
117
+ }
118
+ break;
119
+ }
120
+ return escape_attribute(escape_attrs, value);
121
+ }
122
+
123
+ static VALUE
124
+ hamlit_build_multi_class(VALUE escape_attrs, VALUE values)
125
+ {
126
+ long i, j;
127
+ VALUE value, buf;
128
+
129
+ buf = rb_ary_new2(RARRAY_LEN(values));
130
+
131
+ for (i = 0; i < RARRAY_LEN(values); i++) {
132
+ value = rb_ary_entry(values, i);
133
+ switch (TYPE(value)) {
134
+ case T_STRING:
135
+ rb_ary_concat(buf, rb_str_split(value, " "));
136
+ break;
137
+ case T_ARRAY:
138
+ value = rb_funcall(value, id_flatten, 0);
139
+ delete_falsey_values(value);
140
+ for (j = 0; j < RARRAY_LEN(value); j++) {
141
+ rb_ary_push(buf, to_s(rb_ary_entry(value, j)));
142
+ }
143
+ break;
144
+ default:
145
+ if (RTEST(value)) {
146
+ rb_ary_push(buf, to_s(value));
147
+ }
148
+ break;
149
+ }
150
+ }
151
+
152
+ rb_ary_sort_bang(buf);
153
+ rb_funcall(buf, id_uniq_bang, 0);
154
+
155
+ return escape_attribute(escape_attrs, rb_ary_join(buf, str_space()));
156
+ }
157
+
158
+ static VALUE
159
+ hamlit_build_class(VALUE escape_attrs, VALUE array)
160
+ {
161
+ if (RARRAY_LEN(array) == 1) {
162
+ return hamlit_build_single_class(escape_attrs, rb_ary_entry(array, 0));
163
+ } else {
164
+ return hamlit_build_multi_class(escape_attrs, array);
165
+ }
166
+ }
167
+
168
+ static int
169
+ merge_data_attrs_i(VALUE key, VALUE value, VALUE merged)
170
+ {
171
+ if (NIL_P(key)) {
172
+ rb_hash_aset(merged, str_data(), value);
173
+ } else {
174
+ key = rb_str_concat(rb_str_new_cstr("data-"), to_s(key));
175
+ rb_hash_aset(merged, key, value);
176
+ }
177
+ return ST_CONTINUE;
178
+ }
179
+
180
+ static VALUE
181
+ merge_data_attrs(VALUE values)
182
+ {
183
+ long i;
184
+ VALUE value, merged = rb_hash_new();
185
+
186
+ for (i = 0; i < RARRAY_LEN(values); i++) {
187
+ value = rb_ary_entry(values, i);
188
+ switch (TYPE(value)) {
189
+ case T_HASH:
190
+ rb_hash_foreach(value, merge_data_attrs_i, merged);
191
+ break;
192
+ default:
193
+ rb_hash_aset(merged, str_data(), value);
194
+ break;
195
+ }
196
+ }
197
+ return merged;
198
+ }
199
+
200
+ struct flatten_data_attrs_i2_arg {
201
+ VALUE flattened;
202
+ VALUE key;
203
+ };
204
+
205
+ static int
206
+ flatten_data_attrs_i2(VALUE k, VALUE v, VALUE ptr)
207
+ {
208
+ VALUE key;
209
+ struct flatten_data_attrs_i2_arg *arg = (struct flatten_data_attrs_i2_arg *)ptr;
210
+
211
+ if (!RTEST(v)) return ST_CONTINUE;
212
+
213
+ if (k == Qnil) {
214
+ rb_hash_aset(arg->flattened, arg->key, v);
215
+ } else {
216
+ key = rb_str_dup(arg->key);
217
+ rb_str_cat(key, "-", 1);
218
+ rb_str_concat(key, to_s(k));
219
+
220
+ rb_hash_aset(arg->flattened, key, v);
221
+ }
222
+ return ST_CONTINUE;
223
+ }
224
+
225
+ static VALUE flatten_data_attrs(VALUE attrs);
226
+
227
+ static int
228
+ flatten_data_attrs_i(VALUE key, VALUE value, VALUE flattened)
229
+ {
230
+ struct flatten_data_attrs_i2_arg arg;
231
+ key = hyphenate(to_s(key));
232
+
233
+ switch (TYPE(value)) {
234
+ case T_HASH:
235
+ value = flatten_data_attrs(value);
236
+ arg.key = key;
237
+ arg.flattened = flattened;
238
+ rb_hash_foreach(value, flatten_data_attrs_i2, (VALUE)(&arg));
239
+ break;
240
+ default:
241
+ if (RTEST(value)) rb_hash_aset(flattened, key, value);
242
+ break;
243
+ }
244
+ return ST_CONTINUE;
245
+ }
246
+
247
+ static VALUE
248
+ flatten_data_attrs(VALUE attrs)
249
+ {
250
+ VALUE flattened = rb_hash_new();
251
+ rb_hash_foreach(attrs, flatten_data_attrs_i, flattened);
252
+
253
+ return flattened;
254
+ }
255
+
256
+ static VALUE
257
+ hamlit_build_data(VALUE escape_attrs, VALUE quote, VALUE values)
258
+ {
259
+ long i;
260
+ VALUE attrs, buf, keys, key, value;
261
+
262
+ attrs = merge_data_attrs(values);
263
+ attrs = flatten_data_attrs(attrs);
264
+ keys = rb_ary_sort_bang(rb_funcall(attrs, id_keys, 0));
265
+ buf = rb_str_new("", 0);
266
+
267
+ for (i = 0; i < RARRAY_LEN(keys); i++) {
268
+ key = rb_ary_entry(keys, i);
269
+ value = rb_hash_aref(attrs, key);
270
+
271
+ switch (value) {
272
+ case Qtrue:
273
+ rb_str_concat(buf, str_space());
274
+ rb_str_concat(buf, key);
275
+ break;
276
+ case Qnil:
277
+ break; // noop
278
+ case Qfalse:
279
+ break; // noop
280
+ default:
281
+ rb_str_concat(buf, str_space());
282
+ rb_str_concat(buf, key);
283
+ rb_str_concat(buf, str_equal());
284
+ rb_str_concat(buf, quote);
285
+ rb_str_concat(buf, escape_attribute(escape_attrs, to_s(value)));
286
+ rb_str_concat(buf, quote);
287
+ break;
288
+ }
289
+ }
290
+
291
+ return buf;
292
+ }
293
+
294
+ static VALUE
295
+ parse_object_ref(VALUE object_ref)
296
+ {
297
+ return rb_funcall(mObjectRef, id_parse, 1, object_ref);
298
+ }
299
+
300
+ static int
301
+ merge_all_attrs_i(VALUE key, VALUE value, VALUE merged)
302
+ {
303
+ VALUE array;
304
+
305
+ key = to_s(key);
306
+ if (str_eq(key, "id") || str_eq(key, "class") || str_eq(key, "data")) {
307
+ array = rb_hash_aref(merged, key);
308
+ if (NIL_P(array)) {
309
+ array = rb_ary_new2(1);
310
+ rb_hash_aset(merged, key, array);
311
+ }
312
+ rb_ary_push(array, value);
313
+ } else {
314
+ rb_hash_aset(merged, key, value);
315
+ }
316
+ return ST_CONTINUE;
317
+ }
318
+
319
+ static VALUE
320
+ merge_all_attrs(VALUE hashes)
321
+ {
322
+ long i;
323
+ VALUE hash, merged = rb_hash_new();
324
+
325
+ for (i = 0; i < RARRAY_LEN(hashes); i++) {
326
+ hash = rb_ary_entry(hashes, i);
327
+ rb_hash_foreach(hash, merge_all_attrs_i, merged);
328
+ }
329
+ return merged;
330
+ }
331
+
332
+ int
333
+ is_boolean_attribute(VALUE key)
334
+ {
335
+ VALUE boolean_attributes;
336
+ if (str_eq(rb_str_substr(key, 0, 5), "data-")) return 1;
337
+
338
+ boolean_attributes = rb_const_get(mAttributeBuilder, id_boolean_attributes);
339
+ return RTEST(rb_ary_includes(boolean_attributes, key));
340
+ }
341
+
342
+ void
343
+ hamlit_build_for_id(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
344
+ {
345
+ rb_str_cat(buf, " id=", 4);
346
+ rb_str_concat(buf, quote);
347
+ rb_str_concat(buf, hamlit_build_id(escape_attrs, values));
348
+ rb_str_concat(buf, quote);
349
+ }
350
+
351
+ void
352
+ hamlit_build_for_class(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
353
+ {
354
+ rb_str_cat(buf, " class=", 7);
355
+ rb_str_concat(buf, quote);
356
+ rb_str_concat(buf, hamlit_build_class(escape_attrs, values));
357
+ rb_str_concat(buf, quote);
358
+ }
359
+
360
+ void
361
+ hamlit_build_for_data(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE values)
362
+ {
363
+ rb_str_concat(buf, hamlit_build_data(escape_attrs, quote, values));
364
+ }
365
+
366
+ void
367
+ hamlit_build_for_others(VALUE escape_attrs, VALUE quote, VALUE buf, VALUE key, VALUE value)
368
+ {
369
+ rb_str_cat(buf, " ", 1);
370
+ rb_str_concat(buf, key);
371
+ rb_str_cat(buf, "=", 1);
372
+ rb_str_concat(buf, quote);
373
+ rb_str_concat(buf, escape_attribute(escape_attrs, to_s(value)));
374
+ rb_str_concat(buf, quote);
375
+ }
376
+
377
+ void
378
+ hamlit_build_for_boolean(VALUE escape_attrs, VALUE quote, VALUE format, VALUE buf, VALUE key, VALUE value)
379
+ {
380
+ switch (value) {
381
+ case Qtrue:
382
+ rb_str_cat(buf, " ", 1);
383
+ rb_str_concat(buf, key);
384
+ if ((TYPE(format) == T_SYMBOL || TYPE(format) == T_STRING) && rb_to_id(format) == id_xhtml) {
385
+ rb_str_cat(buf, "=", 1);
386
+ rb_str_concat(buf, quote);
387
+ rb_str_concat(buf, key);
388
+ rb_str_concat(buf, quote);
389
+ }
390
+ break;
391
+ case Qfalse:
392
+ break; // noop
393
+ case Qnil:
394
+ break; // noop
395
+ default:
396
+ hamlit_build_for_others(escape_attrs, quote, buf, key, value);
397
+ break;
398
+ }
399
+ }
400
+
401
+ static VALUE
402
+ hamlit_build(VALUE escape_attrs, VALUE quote, VALUE format, VALUE object_ref, VALUE hashes)
403
+ {
404
+ long i;
405
+ VALUE attrs, buf, key, keys, value;
406
+
407
+ if (!NIL_P(object_ref)) rb_ary_push(hashes, parse_object_ref(object_ref));
408
+ attrs = merge_all_attrs(hashes);
409
+ buf = rb_str_new("", 0);
410
+ keys = rb_ary_sort_bang(rb_funcall(attrs, id_keys, 0));
411
+
412
+ for (i = 0; i < RARRAY_LEN(keys); i++) {
413
+ key = rb_ary_entry(keys, i);
414
+ value = rb_hash_aref(attrs, key);
415
+ if (str_eq(key, "id")) {
416
+ hamlit_build_for_id(escape_attrs, quote, buf, value);
417
+ } else if (str_eq(key, "class")) {
418
+ hamlit_build_for_class(escape_attrs, quote, buf, value);
419
+ } else if (str_eq(key, "data")) {
420
+ hamlit_build_for_data(escape_attrs, quote, buf, value);
421
+ } else if (is_boolean_attribute(key)) {
422
+ hamlit_build_for_boolean(escape_attrs, quote, format, buf, key, value);
423
+ } else {
424
+ hamlit_build_for_others(escape_attrs, quote, buf, key, value);
425
+ }
426
+ }
427
+
428
+ return buf;
429
+ }
430
+
431
+ static VALUE
432
+ rb_hamlit_build_id(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
433
+ {
434
+ VALUE array;
435
+
436
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
437
+ rb_scan_args(argc - 1, argv + 1, "*", &array);
438
+
439
+ return hamlit_build_id(argv[0], array);
440
+ }
441
+
442
+ static VALUE
443
+ rb_hamlit_build_class(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
444
+ {
445
+ VALUE array;
446
+
447
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
448
+ rb_scan_args(argc - 1, argv + 1, "*", &array);
449
+
450
+ return hamlit_build_class(argv[0], array);
451
+ }
452
+
453
+ static VALUE
454
+ rb_hamlit_build_data(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
455
+ {
456
+ VALUE array;
457
+
458
+ rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
459
+ rb_scan_args(argc - 2, argv + 2, "*", &array);
460
+
461
+ return hamlit_build_data(argv[0], argv[1], array);
462
+ }
463
+
464
+ static VALUE
465
+ rb_hamlit_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
466
+ {
467
+ VALUE array;
468
+
469
+ rb_check_arity(argc, 4, UNLIMITED_ARGUMENTS);
470
+ rb_scan_args(argc - 4, argv + 4, "*", &array);
471
+
472
+ return hamlit_build(argv[0], argv[1], argv[2], argv[3], array);
473
+ }
474
+
475
+ void
476
+ Init_hamlit(void)
477
+ {
478
+ VALUE mHamlit, mUtils;
479
+
480
+ mHamlit = rb_define_module("Hamlit");
481
+ mObjectRef = rb_define_module_under(mHamlit, "ObjectRef");
482
+ mUtils = rb_define_module_under(mHamlit, "Utils");
483
+ mAttributeBuilder = rb_define_module_under(mHamlit, "AttributeBuilder");
484
+
485
+ rb_define_singleton_method(mUtils, "escape_html", rb_escape_html, 1);
486
+ rb_define_singleton_method(mAttributeBuilder, "build", rb_hamlit_build, -1);
487
+ rb_define_singleton_method(mAttributeBuilder, "build_id", rb_hamlit_build_id, -1);
488
+ rb_define_singleton_method(mAttributeBuilder, "build_class", rb_hamlit_build_class, -1);
489
+ rb_define_singleton_method(mAttributeBuilder, "build_data", rb_hamlit_build_data, -1);
490
+
491
+ id_flatten = rb_intern("flatten");
492
+ id_keys = rb_intern("keys");
493
+ id_parse = rb_intern("parse");
494
+ id_prepend = rb_intern("prepend");
495
+ id_tr = rb_intern("tr");
496
+ id_uniq_bang = rb_intern("uniq!");
497
+
498
+ id_data = rb_intern("DATA");
499
+ id_equal = rb_intern("EQUAL");
500
+ id_hyphen = rb_intern("HYPHEN");
501
+ id_space = rb_intern("SPACE");
502
+ id_underscore = rb_intern("UNDERSCORE");
503
+
504
+ id_boolean_attributes = rb_intern("BOOLEAN_ATTRIBUTES");
505
+ id_xhtml = rb_intern("xhtml");
506
+
507
+ rb_const_set(mAttributeBuilder, id_data, rb_obj_freeze(rb_str_new_cstr("data")));
508
+ rb_const_set(mAttributeBuilder, id_equal, rb_obj_freeze(rb_str_new_cstr("=")));
509
+ rb_const_set(mAttributeBuilder, id_hyphen, rb_obj_freeze(rb_str_new_cstr("-")));
510
+ rb_const_set(mAttributeBuilder, id_space, rb_obj_freeze(rb_str_new_cstr(" ")));
511
+ rb_const_set(mAttributeBuilder, id_underscore, rb_obj_freeze(rb_str_new_cstr("_")));
512
+ }