hamlit 1.7.2 → 2.0.1

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.
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
@@ -0,0 +1,61 @@
1
+ module Hamlit
2
+ # An exception raised by Haml code.
3
+ class HamlError < StandardError
4
+
5
+ MESSAGES = {
6
+ :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
7
+ :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
8
+ :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
9
+ :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
10
+ :filter_not_defined => 'Filter "%s" is not defined.',
11
+ :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
12
+ :illegal_element => "Illegal element: classes and ids must have values.",
13
+ :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
14
+ :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
15
+ :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
16
+ :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
17
+ :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
18
+ :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
19
+ :indenting_at_start => "Indenting at the beginning of the document is illegal.",
20
+ :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
21
+ :invalid_attribute_list => 'Invalid attribute list: %s.',
22
+ :invalid_filter_name => 'Invalid filter name ":%s".',
23
+ :invalid_tag => 'Invalid tag: "%s".',
24
+ :missing_if => 'Got "%s" with no preceding "if"',
25
+ :no_ruby_code => "There's no Ruby code for %s to evaluate.",
26
+ :self_closing_content => "Self-closing tags can't have content.",
27
+ :unbalanced_brackets => 'Unbalanced brackets.',
28
+ :no_end => <<-END
29
+ You don't need to use "- end" in Haml. Un-indent to close a block:
30
+ - if foo?
31
+ %strong Foo!
32
+ - else
33
+ Not foo.
34
+ %p This line is un-indented, so it isn't part of the "if" block
35
+ END
36
+ }
37
+
38
+ def self.message(key, *args)
39
+ string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
40
+ (args.empty? ? string : string % args).rstrip
41
+ end
42
+
43
+ # The line of the template on which the error occurred.
44
+ #
45
+ # @return [Fixnum]
46
+ attr_reader :line
47
+
48
+ # @param message [String] The error message
49
+ # @param line [Fixnum] See \{#line}
50
+ def initialize(message = nil, line = nil)
51
+ super(message)
52
+ @line = line
53
+ end
54
+ end
55
+
56
+ # SyntaxError is the type of exception raised when Haml encounters an
57
+ # ill-formatted document.
58
+ # It's not particularly interesting,
59
+ # except in that it's a subclass of {Haml::Error}.
60
+ class HamlSyntaxError < HamlError; end
61
+ end
@@ -0,0 +1,727 @@
1
+ require 'hamlit/parser/haml_error'
2
+ require 'hamlit/parser/haml_buffer'
3
+ require 'hamlit/parser/haml_options'
4
+ require 'hamlit/parser/haml_compiler'
5
+ require 'hamlit/parser/haml_parser'
6
+
7
+ module Hamlit
8
+ # This module contains various helpful methods to make it easier to do various tasks.
9
+ # {Haml::Helpers} is automatically included in the context
10
+ # that a Haml template is parsed in, so all these methods are at your
11
+ # disposal from within the template.
12
+ module HamlHelpers
13
+ # An object that raises an error when \{#to\_s} is called.
14
+ # It's used to raise an error when the return value of a helper is used
15
+ # when it shouldn't be.
16
+ class ErrorReturn
17
+ def initialize(method)
18
+ @message = <<MESSAGE
19
+ #{method} outputs directly to the Haml template.
20
+ Disregard its return value and use the - operator,
21
+ or use capture_haml to get the value as a String.
22
+ MESSAGE
23
+ end
24
+
25
+ # Raises an error.
26
+ #
27
+ # @raise [Haml::Error] The error
28
+ def to_s
29
+ raise ::Hamlit::HamlError.new(@message)
30
+ rescue ::Hamlit::HamlError => e
31
+ e.backtrace.shift
32
+
33
+ # If the ErrorReturn is used directly in the template,
34
+ # we don't want Haml's stuff to get into the backtrace,
35
+ # so we get rid of the format_script line.
36
+ #
37
+ # We also have to subtract one from the Haml line number
38
+ # since the value is passed to format_script the line after
39
+ # it's actually used.
40
+ if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
41
+ e.backtrace.shift
42
+ e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
43
+ end
44
+ raise e
45
+ end
46
+
47
+ # @return [String] A human-readable string representation
48
+ def inspect
49
+ "::Hamlit::HamlHelpers::ErrorReturn(#{@message.inspect})"
50
+ end
51
+ end
52
+
53
+ self.extend self
54
+
55
+ @@action_view_defined = false
56
+
57
+ # @return [Boolean] Whether or not ActionView is loaded
58
+ def self.action_view?
59
+ @@action_view_defined
60
+ end
61
+
62
+ # Note: this does **not** need to be called when using Haml helpers
63
+ # normally in Rails.
64
+ #
65
+ # Initializes the current object as though it were in the same context
66
+ # as a normal ActionView instance using Haml.
67
+ # This is useful if you want to use the helpers in a context
68
+ # other than the normal setup with ActionView.
69
+ # For example:
70
+ #
71
+ # context = Object.new
72
+ # class << context
73
+ # include Haml::Helpers
74
+ # end
75
+ # context.init_haml_helpers
76
+ # context.haml_tag :p, "Stuff"
77
+ #
78
+ def init_haml_helpers
79
+ @haml_buffer = ::Hamlit::HamlBuffer.new(haml_buffer, ::Hamlit::HamlOptions.new.for_buffer)
80
+ nil
81
+ end
82
+
83
+ # Runs a block of code in a non-Haml context
84
+ # (i.e. \{#is\_haml?} will return false).
85
+ #
86
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
87
+ # particularly where helpers may behave differently when run from Haml.
88
+ #
89
+ # Note that this is automatically applied to Rails partials.
90
+ #
91
+ # @yield A block which won't register as Haml
92
+ def non_haml
93
+ was_active = @haml_buffer.active?
94
+ @haml_buffer.active = false
95
+ yield
96
+ ensure
97
+ @haml_buffer.active = was_active
98
+ end
99
+
100
+ # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
101
+ # into the HTML entities for endlines.
102
+ #
103
+ # @param tags [Array<String>] Tags that should have newlines escaped
104
+ #
105
+ # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
106
+ # Escapes newlines within a string.
107
+ #
108
+ # @param input [String] The string within which to escape newlines
109
+ # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
110
+ # Escapes newlines within a block of Haml code.
111
+ #
112
+ # @yield The block within which to escape newlines
113
+ def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
114
+ return find_and_preserve(capture_haml(&block), input || tags) if block
115
+ tags = tags.each_with_object('') do |t, s|
116
+ s << '|' unless s.empty?
117
+ s << Regexp.escape(t)
118
+ end
119
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
120
+ input.to_s.gsub(re) do |s|
121
+ s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
122
+ "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
123
+ end
124
+ end
125
+
126
+ # Takes any string, finds all the newlines, and converts them to
127
+ # HTML entities so they'll render correctly in
128
+ # whitespace-sensitive tags without screwing up the indentation.
129
+ #
130
+ # @overload preserve(input)
131
+ # Escapes newlines within a string.
132
+ #
133
+ # @param input [String] The string within which to escape all newlines
134
+ # @overload preserve
135
+ # Escapes newlines within a block of Haml code.
136
+ #
137
+ # @yield The block within which to escape newlines
138
+ def preserve(input = nil, &block)
139
+ return preserve(capture_haml(&block)) if block
140
+ s = input.to_s.chomp("\n")
141
+ s.gsub!(/\n/, '&#x000A;')
142
+ s.delete!("\r")
143
+ s
144
+ end
145
+ alias_method :flatten, :preserve
146
+
147
+ # Takes an `Enumerable` object and a block
148
+ # and iterates over the enum,
149
+ # yielding each element to a Haml block
150
+ # and putting the result into `<li>` elements.
151
+ # This creates a list of the results of the block.
152
+ # For example:
153
+ #
154
+ # = list_of([['hello'], ['yall']]) do |i|
155
+ # = i[0]
156
+ #
157
+ # Produces:
158
+ #
159
+ # <li>hello</li>
160
+ # <li>yall</li>
161
+ #
162
+ # And:
163
+ #
164
+ # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
165
+ # %h3= key.humanize
166
+ # %p= val
167
+ #
168
+ # Produces:
169
+ #
170
+ # <li>
171
+ # <h3>Title</h3>
172
+ # <p>All the stuff</p>
173
+ # </li>
174
+ # <li>
175
+ # <h3>Description</h3>
176
+ # <p>A book about all the stuff.</p>
177
+ # </li>
178
+ #
179
+ # While:
180
+ #
181
+ # = list_of(["Home", "About", "Contact", "FAQ"], {class: "nav", role: "nav"}) do |item|
182
+ # %a{ href="#" }= item
183
+ #
184
+ # Produces:
185
+ #
186
+ # <li class='nav' role='nav'>
187
+ # <a href='#'>Home</a>
188
+ # </li>
189
+ # <li class='nav' role='nav'>
190
+ # <a href='#'>About</a>
191
+ # </li>
192
+ # <li class='nav' role='nav'>
193
+ # <a href='#'>Contact</a>
194
+ # </li>
195
+ # <li class='nav' role='nav'>
196
+ # <a href='#'>FAQ</a>
197
+ # </li>
198
+ #
199
+ # `[[class", "nav"], [role", "nav"]]` could have been used instead of `{class: "nav", role: "nav"}` (or any enumerable collection where each pair of items responds to #to_s)
200
+ #
201
+ # @param enum [Enumerable] The list of objects to iterate over
202
+ # @param [Enumerable<#to_s,#to_s>] opts Each key/value pair will become an attribute pair for each list item element.
203
+ # @yield [item] A block which contains Haml code that goes within list items
204
+ # @yieldparam item An element of `enum`
205
+ def list_of(enum, opts={}, &block)
206
+ opts_attributes = opts.each_with_object('') {|(k, v), s| s << " #{k}='#{v}'"}
207
+ enum.each_with_object('') do |i, ret|
208
+ result = capture_haml(i, &block)
209
+
210
+ if result.count("\n") > 1
211
+ result.gsub!("\n", "\n ")
212
+ result = "\n #{result.strip!}\n"
213
+ else
214
+ result.strip!
215
+ end
216
+
217
+ ret << "\n" unless ret.empty?
218
+ ret << %Q!<li#{opts_attributes}>#{result}</li>!
219
+ end
220
+ end
221
+
222
+ # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
223
+ # attributes of the `html` HTML element.
224
+ # For example,
225
+ #
226
+ # %html{html_attrs}
227
+ #
228
+ # becomes
229
+ #
230
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
231
+ #
232
+ # @param lang [String] The value of `xml:lang` and `lang`
233
+ # @return [{#to_s => String}] The attribute hash
234
+ def html_attrs(lang = 'en-US')
235
+ if haml_buffer.options[:format] == :xhtml
236
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
237
+ else
238
+ {:lang => lang}
239
+ end
240
+ end
241
+
242
+ # Increments the number of tabs the buffer automatically adds
243
+ # to the lines of the template.
244
+ # For example:
245
+ #
246
+ # %h1 foo
247
+ # - tab_up
248
+ # %p bar
249
+ # - tab_down
250
+ # %strong baz
251
+ #
252
+ # Produces:
253
+ #
254
+ # <h1>foo</h1>
255
+ # <p>bar</p>
256
+ # <strong>baz</strong>
257
+ #
258
+ # @param i [Fixnum] The number of tabs by which to increase the indentation
259
+ # @see #tab_down
260
+ def tab_up(i = 1)
261
+ haml_buffer.tabulation += i
262
+ end
263
+
264
+ # Decrements the number of tabs the buffer automatically adds
265
+ # to the lines of the template.
266
+ #
267
+ # @param i [Fixnum] The number of tabs by which to decrease the indentation
268
+ # @see #tab_up
269
+ def tab_down(i = 1)
270
+ haml_buffer.tabulation -= i
271
+ end
272
+
273
+ # Sets the number of tabs the buffer automatically adds
274
+ # to the lines of the template,
275
+ # but only for the duration of the block.
276
+ # For example:
277
+ #
278
+ # %h1 foo
279
+ # - with_tabs(2) do
280
+ # %p bar
281
+ # %strong baz
282
+ #
283
+ # Produces:
284
+ #
285
+ # <h1>foo</h1>
286
+ # <p>bar</p>
287
+ # <strong>baz</strong>
288
+ #
289
+ #
290
+ # @param i [Fixnum] The number of tabs to use
291
+ # @yield A block in which the indentation will be `i` spaces
292
+ def with_tabs(i)
293
+ old_tabs = haml_buffer.tabulation
294
+ haml_buffer.tabulation = i
295
+ yield
296
+ ensure
297
+ haml_buffer.tabulation = old_tabs
298
+ end
299
+
300
+ # Surrounds a block of Haml code with strings,
301
+ # with no whitespace in between.
302
+ # For example:
303
+ #
304
+ # = surround '(', ')' do
305
+ # %a{:href => "food"} chicken
306
+ #
307
+ # Produces:
308
+ #
309
+ # (<a href='food'>chicken</a>)
310
+ #
311
+ # and
312
+ #
313
+ # = surround '*' do
314
+ # %strong angry
315
+ #
316
+ # Produces:
317
+ #
318
+ # *<strong>angry</strong>*
319
+ #
320
+ # @param front [String] The string to add before the Haml
321
+ # @param back [String] The string to add after the Haml
322
+ # @yield A block of Haml to surround
323
+ def surround(front, back = front, &block)
324
+ output = capture_haml(&block)
325
+
326
+ "#{front}#{output.chomp}#{back}\n"
327
+ end
328
+
329
+ # Prepends a string to the beginning of a Haml block,
330
+ # with no whitespace between.
331
+ # For example:
332
+ #
333
+ # = precede '*' do
334
+ # %span.small Not really
335
+ #
336
+ # Produces:
337
+ #
338
+ # *<span class='small'>Not really</span>
339
+ #
340
+ # @param str [String] The string to add before the Haml
341
+ # @yield A block of Haml to prepend to
342
+ def precede(str, &block)
343
+ "#{str}#{capture_haml(&block).chomp}\n"
344
+ end
345
+
346
+ # Appends a string to the end of a Haml block,
347
+ # with no whitespace between.
348
+ # For example:
349
+ #
350
+ # click
351
+ # = succeed '.' do
352
+ # %a{:href=>"thing"} here
353
+ #
354
+ # Produces:
355
+ #
356
+ # click
357
+ # <a href='thing'>here</a>.
358
+ #
359
+ # @param str [String] The string to add after the Haml
360
+ # @yield A block of Haml to append to
361
+ def succeed(str, &block)
362
+ "#{capture_haml(&block).chomp}#{str}\n"
363
+ end
364
+
365
+ # Captures the result of a block of Haml code,
366
+ # gets rid of the excess indentation,
367
+ # and returns it as a string.
368
+ # For example, after the following,
369
+ #
370
+ # .foo
371
+ # - foo = capture_haml(13) do |a|
372
+ # %p= a
373
+ #
374
+ # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
375
+ #
376
+ # @param args [Array] Arguments to pass into the block
377
+ # @yield [args] A block of Haml code that will be converted to a string
378
+ # @yieldparam args [Array] `args`
379
+ def capture_haml(*args, &block)
380
+ buffer = eval('if defined? _hamlout then _hamlout else nil end', block.binding) || haml_buffer
381
+ with_haml_buffer(buffer) do
382
+ position = haml_buffer.buffer.length
383
+
384
+ haml_buffer.capture_position = position
385
+ value = block.call(*args)
386
+
387
+ captured = haml_buffer.buffer.slice!(position..-1)
388
+
389
+ if captured == '' and value != haml_buffer.buffer
390
+ captured = (value.is_a?(String) ? value : nil)
391
+ end
392
+
393
+ return nil if captured.nil?
394
+ return (haml_buffer.options[:ugly] ? captured : prettify(captured))
395
+ end
396
+ ensure
397
+ haml_buffer.capture_position = nil
398
+ end
399
+
400
+ # Outputs text directly to the Haml buffer, with the proper indentation.
401
+ #
402
+ # @param text [#to_s] The text to output
403
+ def haml_concat(text = "")
404
+ haml_internal_concat text
405
+ ErrorReturn.new("haml_concat")
406
+ end
407
+
408
+ # Internal method to write directly to the buffer with control of
409
+ # whether the first line should be indented, and if there should be a
410
+ # final newline.
411
+ #
412
+ # Lines added will have the proper indentation. This can be controlled
413
+ # for the first line.
414
+ #
415
+ # Used by #haml_concat and #haml_tag.
416
+ #
417
+ # @param text [#to_s] The text to output
418
+ # @param newline [Boolean] Whether to add a newline after the text
419
+ # @param indent [Boolean] Whether to add indentation to the first line
420
+ def haml_internal_concat(text = "", newline = true, indent = true)
421
+ if haml_buffer.options[:ugly] || haml_buffer.tabulation == 0
422
+ haml_buffer.buffer << "#{text}#{"\n" if newline}"
423
+ else
424
+ haml_buffer.buffer << %[#{haml_indent if indent}#{text.to_s.gsub("\n", "\n#{haml_indent}")}#{"\n" if newline}]
425
+ end
426
+ end
427
+ private :haml_internal_concat
428
+
429
+ # Allows writing raw content. `haml_internal_concat_raw` isn't
430
+ # effected by XSS mods. Used by #haml_tag to write the actual tags.
431
+ alias :haml_internal_concat_raw :haml_internal_concat
432
+
433
+ # @return [String] The indentation string for the current line
434
+ def haml_indent
435
+ ' ' * haml_buffer.tabulation
436
+ end
437
+
438
+ # Creates an HTML tag with the given name and optionally text and attributes.
439
+ # Can take a block that will run between the opening and closing tags.
440
+ # If the block is a Haml block or outputs text using \{#haml\_concat},
441
+ # the text will be properly indented.
442
+ #
443
+ # `name` can be a string using the standard Haml class/id shorthand
444
+ # (e.g. "span#foo.bar", "#foo").
445
+ # Just like standard Haml tags, these class and id values
446
+ # will be merged with manually-specified attributes.
447
+ #
448
+ # `flags` is a list of symbol flags
449
+ # like those that can be put at the end of a Haml tag
450
+ # (`:/`, `:<`, and `:>`).
451
+ # Currently, only `:/` and `:<` are supported.
452
+ #
453
+ # `haml_tag` outputs directly to the buffer;
454
+ # its return value should not be used.
455
+ # If you need to get the results as a string,
456
+ # use \{#capture\_haml\}.
457
+ #
458
+ # For example,
459
+ #
460
+ # haml_tag :table do
461
+ # haml_tag :tr do
462
+ # haml_tag 'td.cell' do
463
+ # haml_tag :strong, "strong!"
464
+ # haml_concat "data"
465
+ # end
466
+ # haml_tag :td do
467
+ # haml_concat "more_data"
468
+ # end
469
+ # end
470
+ # end
471
+ #
472
+ # outputs
473
+ #
474
+ # <table>
475
+ # <tr>
476
+ # <td class='cell'>
477
+ # <strong>
478
+ # strong!
479
+ # </strong>
480
+ # data
481
+ # </td>
482
+ # <td>
483
+ # more_data
484
+ # </td>
485
+ # </tr>
486
+ # </table>
487
+ #
488
+ # @param name [#to_s] The name of the tag
489
+ #
490
+ # @overload haml_tag(name, *rest, attributes = {})
491
+ # @yield The block of Haml code within the tag
492
+ # @overload haml_tag(name, text, *flags, attributes = {})
493
+ # @param text [#to_s] The text within the tag
494
+ # @param flags [Array<Symbol>] Haml end-of-tag flags
495
+ def haml_tag(name, *rest, &block)
496
+ ret = ErrorReturn.new("haml_tag")
497
+
498
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
499
+ flags = []
500
+ flags << rest.shift while rest.first.is_a? Symbol
501
+ attrs = (rest.shift || {})
502
+ attrs.keys.each {|key| attrs[key.to_s] = attrs.delete(key)} unless attrs.empty?
503
+ name, attrs = merge_name_and_attributes(name.to_s, attrs)
504
+
505
+ attributes = ::Hamlit::HamlCompiler.build_attributes(haml_buffer.html?,
506
+ haml_buffer.options[:attr_wrapper],
507
+ haml_buffer.options[:escape_attrs],
508
+ haml_buffer.options[:hyphenate_data_attrs],
509
+ attrs)
510
+
511
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
512
+ haml_internal_concat_raw "<#{name}#{attributes}#{' /' if haml_buffer.options[:format] == :xhtml}>"
513
+ return ret
514
+ end
515
+
516
+ if flags.include?(:/)
517
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:self_closing_content)) if text
518
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_self_closing)) if block
519
+ end
520
+
521
+ tag = "<#{name}#{attributes}>"
522
+ end_tag = "</#{name}>"
523
+ if block.nil?
524
+ text = text.to_s
525
+ if text.include?("\n")
526
+ haml_internal_concat_raw tag
527
+ tab_up
528
+ haml_internal_concat text
529
+ tab_down
530
+ haml_internal_concat_raw end_tag
531
+ else
532
+ haml_internal_concat_raw tag, false
533
+ haml_internal_concat text, false, false
534
+ haml_internal_concat_raw end_tag, true, false
535
+ end
536
+ return ret
537
+ end
538
+
539
+ if text
540
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_line, name))
541
+ end
542
+
543
+ if flags.include?(:<)
544
+ haml_internal_concat_raw tag, false
545
+ haml_internal_concat "#{capture_haml(&block).strip}", false, false
546
+ haml_internal_concat_raw end_tag, true, false
547
+ return ret
548
+ end
549
+
550
+ haml_internal_concat_raw tag
551
+ tab_up
552
+ block.call
553
+ tab_down
554
+ haml_internal_concat_raw end_tag
555
+
556
+ ret
557
+ end
558
+
559
+ # Conditionally wrap a block in an element. If `condition` is `true` then
560
+ # this method renders the tag described by the arguments in `tag` (using
561
+ # \{#haml_tag}) with the given block inside, otherwise it just renders the block.
562
+ #
563
+ # For example,
564
+ #
565
+ # - haml_tag_if important, '.important' do
566
+ # %p
567
+ # A (possibly) important paragraph.
568
+ #
569
+ # will produce
570
+ #
571
+ # <div class='important'>
572
+ # <p>
573
+ # A (possibly) important paragraph.
574
+ # </p>
575
+ # </div>
576
+ #
577
+ # if `important` is truthy, and just
578
+ #
579
+ # <p>
580
+ # A (possibly) important paragraph.
581
+ # </p>
582
+ #
583
+ # otherwise.
584
+ #
585
+ # Like \{#haml_tag}, `haml_tag_if` outputs directly to the buffer and its
586
+ # return value should not be used. Use \{#capture_haml} if you need to use
587
+ # its results as a string.
588
+ #
589
+ # @param condition The condition to test to determine whether to render
590
+ # the enclosing tag
591
+ # @param tag Definition of the enclosing tag. See \{#haml_tag} for details
592
+ # (specifically the form that takes a block)
593
+ def haml_tag_if(condition, *tag)
594
+ if condition
595
+ haml_tag(*tag){ yield }
596
+ else
597
+ yield
598
+ end
599
+ ErrorReturn.new("haml_tag_if")
600
+ end
601
+
602
+ # Characters that need to be escaped to HTML entities from user input
603
+ HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#039;' }
604
+
605
+ HTML_ESCAPE_REGEX = /[\"><&]/
606
+
607
+ # Returns a copy of `text` with ampersands, angle brackets and quotes
608
+ # escaped into HTML entities.
609
+ #
610
+ # Note that if ActionView is loaded and XSS protection is enabled
611
+ # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
612
+ # this won't escape text declared as "safe".
613
+ #
614
+ # @param text [String] The string to sanitize
615
+ # @return [String] The sanitized string
616
+ def html_escape(text)
617
+ text = text.to_s
618
+ text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
619
+ end
620
+
621
+ HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
622
+
623
+ # Escapes HTML entities in `text`, but without escaping an ampersand
624
+ # that is already part of an escaped entity.
625
+ #
626
+ # @param text [String] The string to sanitize
627
+ # @return [String] The sanitized string
628
+ def escape_once(text)
629
+ text = text.to_s
630
+ text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
631
+ end
632
+
633
+ # Returns whether or not the current template is a Haml template.
634
+ #
635
+ # This function, unlike other {Haml::Helpers} functions,
636
+ # also works in other `ActionView` templates,
637
+ # where it will always return false.
638
+ #
639
+ # @return [Boolean] Whether or not the current template is a Haml template
640
+ def is_haml?
641
+ !@haml_buffer.nil? && @haml_buffer.active?
642
+ end
643
+
644
+ # Returns whether or not `block` is defined directly in a Haml template.
645
+ #
646
+ # @param block [Proc] A Ruby block
647
+ # @return [Boolean] Whether or not `block` is defined directly in a Haml template
648
+ def block_is_haml?(block)
649
+ eval('!!defined?(_hamlout)', block.binding)
650
+ end
651
+
652
+ private
653
+
654
+ # Parses the tag name used for \{#haml\_tag}
655
+ # and merges it with the Ruby attributes hash.
656
+ def merge_name_and_attributes(name, attributes_hash = {})
657
+ # skip merging if no ids or classes found in name
658
+ return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
659
+
660
+ return $1 || "div", ::Hamlit::HamlBuffer.merge_attrs(
661
+ ::Hamlit::HamlParser.parse_class_and_id($2), attributes_hash)
662
+ end
663
+
664
+ # Runs a block of code with the given buffer as the currently active buffer.
665
+ #
666
+ # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
667
+ # @yield A block in which the given buffer should be used
668
+ def with_haml_buffer(buffer)
669
+ @haml_buffer, old_buffer = buffer, @haml_buffer
670
+ old_buffer.active, old_was_active = false, old_buffer.active? if old_buffer
671
+ @haml_buffer.active, was_active = true, @haml_buffer.active?
672
+ yield
673
+ ensure
674
+ @haml_buffer.active = was_active
675
+ old_buffer.active = old_was_active if old_buffer
676
+ @haml_buffer = old_buffer
677
+ end
678
+
679
+ # The current {Haml::Buffer} object.
680
+ #
681
+ # @return [Haml::Buffer]
682
+ def haml_buffer
683
+ @haml_buffer if defined? @haml_buffer
684
+ end
685
+
686
+ # Gives a proc the same local `_hamlout` and `_erbout` variables
687
+ # that the current template has.
688
+ #
689
+ # @param proc [#call] The proc to bind
690
+ # @return [Proc] A new proc with the new variables bound
691
+ def haml_bind_proc(&proc)
692
+ _hamlout = haml_buffer
693
+ #double assignment is to avoid warnings
694
+ _erbout = _erbout = _hamlout.buffer
695
+ proc { |*args| proc.call(*args) }
696
+ end
697
+
698
+ def prettify(text)
699
+ text = text.split(/^/)
700
+ text.delete('')
701
+
702
+ min_tabs = nil
703
+ text.each do |line|
704
+ tabs = line.index(/[^ ]/) || line.length
705
+ min_tabs ||= tabs
706
+ min_tabs = min_tabs > tabs ? tabs : min_tabs
707
+ end
708
+
709
+ text.each_with_object('') do |line, str|
710
+ str << line.slice(min_tabs, line.length)
711
+ end
712
+ end
713
+ end
714
+ end
715
+
716
+ # @private
717
+ class Object
718
+ # Haml overrides various `ActionView` helpers,
719
+ # which call an \{#is\_haml?} method
720
+ # to determine whether or not the current context object
721
+ # is a proper Haml context.
722
+ # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
723
+ # it's a good idea to define \{#is\_haml?} for all objects.
724
+ def is_haml?
725
+ false
726
+ end
727
+ end