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
@@ -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