isaac-malline 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/History.txt +34 -0
  4. data/Manifest.txt +72 -0
  5. data/README +24 -0
  6. data/README.txt +33 -0
  7. data/Rakefile +74 -0
  8. data/bin/malline +20 -0
  9. data/github.rb +6 -0
  10. data/init.rb +2 -0
  11. data/lib/malline.rb +109 -0
  12. data/lib/malline/adapters/rails-2.0.rb +72 -0
  13. data/lib/malline/adapters/rails-2.1.rb +103 -0
  14. data/lib/malline/erb_out.rb +34 -0
  15. data/lib/malline/form_builder.rb +44 -0
  16. data/lib/malline/plugin.rb +34 -0
  17. data/lib/malline/plugins/xhtml.rb +77 -0
  18. data/lib/malline/rails.rb +46 -0
  19. data/lib/malline/template.rb +185 -0
  20. data/lib/malline/view_proxy.rb +87 -0
  21. data/lib/malline/view_wrapper.rb +95 -0
  22. data/malline.gemspec +34 -0
  23. data/scripts/html2mn.rb +93 -0
  24. data/test/examples/_action.mn +4 -0
  25. data/test/examples/_action.target +1 -0
  26. data/test/examples/_one.mn +1 -0
  27. data/test/examples/_one.target +1 -0
  28. data/test/examples/_partial.mn +2 -0
  29. data/test/examples/_partial.target +1 -0
  30. data/test/examples/_three.rhtml +2 -0
  31. data/test/examples/_two.mn +1 -0
  32. data/test/examples/_two.target +1 -0
  33. data/test/examples/capture.mn +13 -0
  34. data/test/examples/capture.target +1 -0
  35. data/test/examples/class.mn +6 -0
  36. data/test/examples/class.target +1 -0
  37. data/test/examples/escape.mn +4 -0
  38. data/test/examples/escape.target +1 -0
  39. data/test/examples/frontpage.mn +6 -0
  40. data/test/examples/frontpage.target +1 -0
  41. data/test/examples/hello_world.mn +2 -0
  42. data/test/examples/hello_world.target +1 -0
  43. data/test/examples/helper.mn +5 -0
  44. data/test/examples/helper.target +1 -0
  45. data/test/examples/helper2.mn +5 -0
  46. data/test/examples/helper2.target +1 -0
  47. data/test/examples/helper_shortcut.mn +5 -0
  48. data/test/examples/helper_shortcut.target +1 -0
  49. data/test/examples/id.mn +3 -0
  50. data/test/examples/id.target +1 -0
  51. data/test/examples/layout.mn +8 -0
  52. data/test/examples/layout.target +4 -0
  53. data/test/examples/lists.mn +13 -0
  54. data/test/examples/lists.target +1 -0
  55. data/test/examples/nested.mn +6 -0
  56. data/test/examples/nested.target +1 -0
  57. data/test/examples/options.mn +10 -0
  58. data/test/examples/options.target +1 -0
  59. data/test/examples/partials.mn +5 -0
  60. data/test/examples/partials.target +1 -0
  61. data/test/examples/self.mn +2 -0
  62. data/test/examples/self.target +1 -0
  63. data/test/examples/text.mn +4 -0
  64. data/test/examples/text.target +1 -0
  65. data/test/examples/whitespace.mn +9 -0
  66. data/test/examples/whitespace.target +1 -0
  67. data/test/examples/xhtml.mn +11 -0
  68. data/test/examples/xhtml.target +4 -0
  69. data/test/kernel.org.html +107 -0
  70. data/test/kernel.org.mn +657 -0
  71. data/test/malline_test.rb +171 -0
  72. data/test/malline_test_helpers.rb +82 -0
  73. metadata +136 -0
@@ -0,0 +1,72 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ ActionView::Base.register_template_handler 'rb', Malline::Base
19
+ module ActionView
20
+ # We need to redefine some ActionView::Base methods, Since Rails 2.0 doesn't
21
+ # offer any better way to do some things.
22
+ class Base
23
+ alias_method :orig_render_template, :render_template
24
+ # We want to save the name of the current file to @current_tpl_path,
25
+ # because then the error backtrace from Rails will include the
26
+ # name of the file. I didn't find better way to get this
27
+ def render_template template_extension, template, file_path = nil, *rest
28
+ @current_tpl_path = file_path
29
+ orig_render_template(template_extension, template, file_path, *rest)
30
+ end
31
+
32
+ alias_method :orig_compile_and_render_template, :compile_and_render_template
33
+ def compile_and_render_template handler, *rest
34
+ if self.respond_to? :is_malline?
35
+ old, @malline_is_active = is_malline?, false
36
+ output = orig_compile_and_render_template handler, *rest
37
+ @malline_is_active = old
38
+ output
39
+ else
40
+ @malline_is_active = false
41
+ orig_compile_and_render_template handler, *rest
42
+ end
43
+ end
44
+
45
+ alias_method :orig_delegate_render, :delegate_render
46
+ # Update the current file to malline and tell Malline to be deactivated
47
+ # if there is a non-Malline partial inside Malline template.
48
+ def delegate_render(handler, template, local_assigns)
49
+ old = is_malline?
50
+ tmp = if handler == Malline::Base
51
+ h = handler.new(self)
52
+ h.path = @current_tpl_path if @current_tpl_path
53
+ @malline_is_active = true
54
+ h.render(template, local_assigns)
55
+ else
56
+ @malline_is_active = false
57
+ orig_delegate_render(handler, template, local_assigns)
58
+ end
59
+ @malline_is_active = old
60
+ tmp
61
+ end
62
+ end
63
+ end
64
+
65
+ module Malline::ViewWrapper
66
+ # Activate Malline if we are not using ActionView::Base or if
67
+ # the current template is a Malline template
68
+ def is_malline?
69
+ @malline_is_active.nil? ? true : @malline_is_active
70
+ end
71
+ end
72
+
@@ -0,0 +1,103 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Malline
19
+ # Malline template handler for Rails 2.1
20
+ #
21
+ # We use Compilable-interface, even though Malline templates really doesn't
22
+ # compile into anything, but at least template files won't always be re-read
23
+ # from files. Builder templates (.builder|.rxml) also use this interface.
24
+ class RailsHandler < ActionView::TemplateHandler
25
+ include ActionView::TemplateHandlers::Compilable
26
+
27
+ # We have three lines framework code before real template code in
28
+ # 'compiled code'
29
+ def self.line_offset
30
+ 3
31
+ end
32
+
33
+ # Compiles the template, i.e. return a runnable ruby code that initializes
34
+ # a new Malline::Base objects and renders the template.
35
+ def compile template
36
+ path = template.path.gsub('\\', '\\\\\\').gsub("'", "\\\\'")
37
+ "__malline_handler = Malline::Base.new self
38
+ malline.path = '#{path}'
39
+ __malline_handler.render do
40
+ #{template.source}
41
+ end"
42
+ end
43
+
44
+ # Get the rendered fragment contents
45
+ def cache_fragment block, name = {}, options = nil
46
+ @view.fragment_for(block, name, options) do
47
+ eval("__malline_handler.rendered", block.binding)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ module Malline::ViewWrapper
54
+ # Activate Malline if we are not using ActionView::Base or if
55
+ # the current template is a Malline template
56
+ def is_malline?
57
+ !(is_a?(ActionView::Base) && ActionView::Template.handler_class_for_extension(
58
+ current_render_extension) != Malline::RailsHandler)
59
+ end
60
+ end
61
+
62
+ # Rails has a bug with current_render_extension, lets fix it
63
+ if ActionView.const_defined?('Renderer')
64
+ module ActionView::Renderer
65
+ alias_method :orig_render, :render
66
+ def render *args
67
+ out = orig_render *args
68
+ @view.current_render_extension = @prev_extension
69
+ out
70
+ end
71
+
72
+ alias_method :orig_prepare!, :prepare!
73
+ def prepare! *args
74
+ @prev_extension = @view.current_render_extension
75
+ orig_prepare! *args
76
+ end
77
+ end
78
+ else
79
+ class ActionView::Template
80
+ alias_method :orig_render, :render
81
+ def render *args
82
+ out = orig_render *args
83
+ @view.current_render_extension = @prev_extension
84
+ out
85
+ end
86
+
87
+ alias_method :orig_prepare!, :prepare!
88
+ def prepare! *args
89
+ @prev_extension = @view.current_render_extension
90
+ orig_prepare! *args
91
+ end
92
+ end
93
+ class ActionView::PartialTemplate
94
+ alias_method :orig_render, :render
95
+ def render *args
96
+ out = orig_render *args
97
+ @view.current_render_extension = @prev_extension
98
+ out
99
+ end
100
+ end
101
+ end
102
+
103
+ ActionView::Template.register_template_handler 'rb', Malline::RailsHandler
@@ -0,0 +1,34 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Malline
19
+ # Since some parts of Rails use ERB directly instead of current template
20
+ # handler, we have to capture all that data.
21
+ #
22
+ # In practice the erb buffer object (named ActiveView::Base.erb_variable)
23
+ # is a string, where data is simply concatted.
24
+ class ErbOut
25
+ def initialize view
26
+ @view = view
27
+ end
28
+ # Redirect all data to view
29
+ def concat value
30
+ @view << value
31
+ end
32
+ alias_method :<<, :concat
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Malline
19
+ # Capture form elements directly from FormBuilder, so that there is no
20
+ # need to specially render any elements.
21
+ # In other words, with our own FormBuilder-wrapper we can do this:
22
+ # form_for :comment, Comment.new, :url => edit_url do |f|
23
+ # f.text_field :name
24
+ # end
25
+ # instead of
26
+ # ..
27
+ # self << f.text_field(:name)
28
+ class FormBuilder
29
+ # Wrap the Rails FormBuilder in @builder
30
+ def initialize *args, &block
31
+ @view = eval('self', args.last)
32
+ @builder = ::ActionView::Helpers::FormBuilder.new(*args, &block)
33
+ end
34
+ # Render every f.foo -method to view, unless we aren't using
35
+ # Malline template now
36
+ def method_missing *args, &block
37
+ if @view && @view.is_malline?
38
+ @view << @builder.send(*args, &block)
39
+ else
40
+ @builder.send(*args, &block)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Malline very incomplete Plugin interface.
19
+ class Malline::Plugin
20
+ # Install a new plugin: Malline::WhatEverPlugin.install view
21
+ def self.install view
22
+ return if view.malline.plugins.include? self
23
+ self.do_install view
24
+ view.malline.plugins << self
25
+ end
26
+
27
+ protected
28
+ def self.do_install view
29
+ raise NotImplementedError.new
30
+ end
31
+ def self.do_uninstall view
32
+ raise NotImplementedError.new("#{self} cannot be uninstalled")
33
+ end
34
+ end
@@ -0,0 +1,77 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Quite stupid plugin for XHTML, we should be able to do this only with a DTD
19
+ # or similar.
20
+ #
21
+ # Defines all usable tags, list which one can be self-close, defines a short
22
+ # cut tag *xhtml*. Also makes sure that there is some necessary elements in
23
+ # the document.
24
+ class Malline::XHTML < Malline::Plugin
25
+ CUSTOM_TAGS = %w{head title meta}
26
+
27
+ # grep ELEMENT xhtml1-transitional.dtd | cut -d' ' -f2 | tr "\n" " "
28
+ XHTML_TAGS = %w{html head title base meta link style script noscript iframe
29
+ noframes body div p h1 h2 h3 h4 h5 h6 ul ol menu dir li dl dt dd address
30
+ hr pre blockquote center ins del a span bdo br em strong dfn code samp
31
+ kbd var cite abbr acronym q sub sup tt i b big small u s strike basefont
32
+ font object param applet img map area form label input select optgroup
33
+ option textarea fieldset legend button isindex table caption thead tfoot
34
+ tbody colgroup col tr th td} - CUSTOM_TAGS
35
+
36
+ # grep 'ELEMENT.*EMPTY' xhtml1-transitional.dtd | cut -d' ' -f2 | tr "\n" " "
37
+ SHORT_TAG_EXCLUDES = XHTML_TAGS + CUSTOM_TAGS - %w{base meta link hr br
38
+ basefont param img area input isindex col}
39
+
40
+ module Tags
41
+ def xhtml *args, &block
42
+ attrs = { :xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => malline.options[:lang] }
43
+ attrs.merge!(args.pop) if args.last.is_a?(Hash)
44
+
45
+ self << "<?xml version=\"1.0\" encoding=\"#{malline.options[:encoding] || 'UTF-8'}\"?>\n"
46
+ self << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 #{malline.options[:xhtml_dtd] || 'Transitional'}//EN\"\n"
47
+ self << " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-#{(malline.options[:xhtml_dtd] || 'Transitional').downcase}.dtd\">\n"
48
+
49
+ tag! 'html', args.join(''), attrs, &block
50
+ end
51
+
52
+ def title *args, &block
53
+ @__xhtml_title = true
54
+ tag! 'title', *args, &block
55
+ end
56
+
57
+ def meta *args, &block
58
+ @__xhtml_meta = true
59
+ tag! 'meta', *args, &block
60
+ end
61
+
62
+ def head *args, &block
63
+ @__xhtml_title = false
64
+ proxy = tag! 'head', *args, &block
65
+ proxy.__yld { title } unless @__xhtml_title
66
+ proxy.__yld do
67
+ meta :content => "text/html; charset=#{malline.options[:encoding] || 'UTF-8'}", 'http-equiv' => 'Content-Type'
68
+ end unless @__xhtml_meta
69
+ end
70
+ end
71
+
72
+ def self.do_install view
73
+ view.malline.definetags! XHTML_TAGS
74
+ view.malline.short_tag_excludes += SHORT_TAG_EXCLUDES
75
+ view.extend Tags
76
+ end
77
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'malline' unless Kernel.const_defined?('Malline')
19
+ require 'malline/form_builder.rb'
20
+
21
+ if Rails::VERSION::STRING <= "2.0.z"
22
+ require 'malline/adapters/rails-2.0'
23
+ else
24
+ require 'malline/adapters/rails-2.1'
25
+ end
26
+
27
+ # Activate our FormBuilder wrapper, so we can use forms more easily
28
+ ActionView::Base.default_form_builder = Malline::FormBuilder
29
+
30
+ module Malline::ViewWrapper
31
+ @@malline_methods << 'cache'
32
+
33
+ # Rails caching
34
+ def _malline_cache name = {}, options = {}, &block
35
+ return block.call unless @controller.perform_caching
36
+ cache = @controller.read_fragment(name, options)
37
+
38
+ unless cache
39
+ cache = _malline_capture { block.call }
40
+ @controller.write_fragment(name, cache, options)
41
+ end
42
+ @malline.add_unescaped_text cache
43
+ end
44
+ end
45
+
46
+
@@ -0,0 +1,185 @@
1
+ # Copyright © 2007,2008 Riku Palomäki
2
+ #
3
+ # This file is part of Malline.
4
+ #
5
+ # Malline is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # Malline is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Malline
19
+ # This is the class that really evaluates the template and is accessible
20
+ # from the view by "malline", for example:
21
+ # malline.path = 'File name'
22
+ class Template
23
+ # Current options (like @@options in Base)
24
+ attr_accessor :options
25
+ # List of every tag that doesn't support self-closing syntax
26
+ attr_accessor :short_tag_excludes
27
+ # Current state of :whitespace-modifier (bool)
28
+ attr_accessor :whitespace
29
+ # Current file name
30
+ attr_accessor :path
31
+ # Every overriden (in definetags!) helper method (:name => method)
32
+ attr_accessor :helper_overrides
33
+ # Every available tag, excluding the specific methods (:name => bool)
34
+ attr_accessor :tags
35
+ # Render result of the last #render
36
+ attr_reader :rendered
37
+ # List all installed plugins
38
+ attr_accessor :plugins
39
+
40
+ def initialize view, opts
41
+ @view = view
42
+ @whitespace = false
43
+ @path = 'Malline template'
44
+ @options = opts
45
+ @short_tag_excludes = []
46
+ @helper_overrides = {}
47
+ @tags = {}
48
+ @plugins = []
49
+ @inited = false
50
+ end
51
+
52
+ # Install plugins and do every thing that cannot be done in initialize
53
+ # Plugin install will use @view.malline, that will create a duplicate
54
+ # Template instance, if it's called from initialize.
55
+ def init
56
+ return if @inited
57
+ XHTML.install @view if @options[:xhtml]
58
+ end
59
+
60
+ # Stolen from ERB, © 1999-2000,2002,2003 Masatoshi SEKI
61
+ def self.html_escape(s)
62
+ s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
63
+ end
64
+ # Stolen from ERB, © 1999-2000,2002,2003 Masatoshi SEKI
65
+ def self.url_encode(s)
66
+ s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
67
+ end
68
+
69
+ # Changes dom to active @dom, and executes tpl / block
70
+ def execute dom, tpl = nil, &block
71
+ tmp = @dom
72
+ @dom = dom
73
+ if block_given?
74
+ @view.instance_eval &block
75
+ else
76
+ @view.instance_eval tpl, @path
77
+ end
78
+ @dom = tmp
79
+ end
80
+
81
+ # Add escaped string to @dom
82
+ def add_text *values
83
+ @dom << ' ' if @whitespace
84
+ @dom << Template.html_escape(values.join(' '))
85
+ end
86
+
87
+ # Add unescaped string to @dom
88
+ def add_unescaped_text value
89
+ @dom << ' ' if @whitespace
90
+ @dom << value.to_s unless value.nil?
91
+ end
92
+
93
+ # Call a helper (a method defined outside malline whose
94
+ # output is stored to @dom)
95
+ def helper helper, *args, &block
96
+ helper = helper.to_sym
97
+ tmp = if h = @helper_overrides[helper]
98
+ h.call *args, &block
99
+ else
100
+ @view.send helper, *args, &block
101
+ end
102
+ @dom << ' ' if @whitespace
103
+ @dom << tmp.to_s
104
+ tmp
105
+ end
106
+
107
+ # Add a tag to @dom
108
+ def tag s, *args, &block
109
+ tag = { :name => s.to_s, :attrs => {}, :children => [] }
110
+
111
+ tag[:whitespace] = true if @whitespace
112
+ whitespace = @whitespace
113
+ @whitespace = true if args.delete(:whitespace)
114
+
115
+ if args.last.is_a?(Hash)
116
+ tag[:attrs].merge!(args.pop)
117
+ end
118
+
119
+ txt = args.flatten.join('')
120
+ tag[:children] << Template.html_escape(txt) unless txt.empty?
121
+
122
+ @dom << tag
123
+ execute tag[:children], &block if block_given?
124
+ @whitespace = whitespace
125
+
126
+ ViewProxy.new self, tag
127
+ end
128
+
129
+ # Render the XML tree at dom or @dom
130
+ def render dom = nil
131
+ @rendered = (dom || @dom).inject('') do |out, tag|
132
+ if tag.is_a?(String)
133
+ out << tag
134
+ else
135
+ out << ' ' if tag[:whitespace]
136
+ out << "<#{tag[:name]}"
137
+ out << tag[:attrs].inject(''){|s, a| s + " #{a.first}=\"#{Template.html_escape(a.last)}\""}
138
+
139
+ if tag[:children].empty?
140
+ if @short_tag_excludes.include?(tag[:name])
141
+ out << "></#{tag[:name]}>"
142
+ else
143
+ out << '/>'
144
+ end
145
+ else
146
+ out << '>'
147
+ out << render(tag[:children])
148
+ out << "</#{tag[:name]}>"
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ # Execute and render a text or block
155
+ def run tpl = nil, &block
156
+ init
157
+ tmp = []
158
+ execute tmp, tpl, &block
159
+ render tmp
160
+ end
161
+
162
+ # Define tags as a methods, overriding all same named methods
163
+ def definetags! *tags
164
+ tags.flatten.each do |tag|
165
+ tag = tag.to_sym
166
+ @helper_overrides[tag] = @view.method(tag) if @view.respond_to?(tag)
167
+ define_tag! tag
168
+ end
169
+ end
170
+
171
+ # Marking tags as usable, but not overriding anything
172
+ def definetags *tags
173
+ tags.flatten.each{|tag| @tags[tag] = true }
174
+ end
175
+
176
+ # Define a method tag
177
+ def define_tag! tag
178
+ eval %{
179
+ def @view.#{tag}(*args, &block)
180
+ tag!('#{tag}', *args, &block)
181
+ end
182
+ }
183
+ end
184
+ end
185
+ end