malline 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/README +24 -0
  4. data/bin/malline +20 -0
  5. data/lib/malline.rb +108 -0
  6. data/lib/malline/erb_out.rb +29 -0
  7. data/lib/malline/form_builder.rb +33 -0
  8. data/lib/malline/rails.rb +45 -0
  9. data/lib/malline/template.rb +124 -0
  10. data/lib/malline/view_proxy.rb +54 -0
  11. data/lib/malline/view_wrapper.rb +91 -0
  12. data/lib/malline/view_xhtml.rb +71 -0
  13. data/scripts/html2mn.rb +93 -0
  14. data/test/examples/_action.mn +4 -0
  15. data/test/examples/_action.target +1 -0
  16. data/test/examples/_one.mn +1 -0
  17. data/test/examples/_one.target +1 -0
  18. data/test/examples/_partial.mn +2 -0
  19. data/test/examples/_partial.target +1 -0
  20. data/test/examples/_three.rhtml +2 -0
  21. data/test/examples/_two.mn +1 -0
  22. data/test/examples/_two.target +1 -0
  23. data/test/examples/capture.mn +13 -0
  24. data/test/examples/capture.target +1 -0
  25. data/test/examples/class.mn +6 -0
  26. data/test/examples/class.target +1 -0
  27. data/test/examples/escape.mn +4 -0
  28. data/test/examples/escape.target +1 -0
  29. data/test/examples/fragment_cache.mn +10 -0
  30. data/test/examples/fragment_cache.target +1 -0
  31. data/test/examples/frontpage.mn +6 -0
  32. data/test/examples/frontpage.target +1 -0
  33. data/test/examples/hello_world.mn +2 -0
  34. data/test/examples/hello_world.target +1 -0
  35. data/test/examples/helper.mn +5 -0
  36. data/test/examples/helper.target +1 -0
  37. data/test/examples/helper2.mn +5 -0
  38. data/test/examples/helper2.target +1 -0
  39. data/test/examples/helper_shortcut.mn +5 -0
  40. data/test/examples/helper_shortcut.target +1 -0
  41. data/test/examples/id.mn +3 -0
  42. data/test/examples/id.target +1 -0
  43. data/test/examples/layout.mn +8 -0
  44. data/test/examples/layout.target +4 -0
  45. data/test/examples/lists.mn +13 -0
  46. data/test/examples/lists.target +1 -0
  47. data/test/examples/nested.mn +6 -0
  48. data/test/examples/nested.target +1 -0
  49. data/test/examples/options.mn +10 -0
  50. data/test/examples/options.target +1 -0
  51. data/test/examples/partials.mn +5 -0
  52. data/test/examples/partials.target +1 -0
  53. data/test/examples/self.mn +2 -0
  54. data/test/examples/self.target +1 -0
  55. data/test/examples/text.mn +4 -0
  56. data/test/examples/text.target +1 -0
  57. data/test/examples/whitespace.mn +9 -0
  58. data/test/examples/whitespace.target +1 -0
  59. data/test/examples/xhtml.mn +11 -0
  60. data/test/examples/xhtml.target +4 -0
  61. data/test/kernel.org.html +107 -0
  62. data/test/kernel.org.mn +657 -0
  63. data/test/malline_test.rb +192 -0
  64. data/test/malline_test_helpers.rb +57 -0
  65. metadata +110 -0
@@ -0,0 +1,54 @@
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
+ class ViewProxy
20
+ def initialize template, tag
21
+ @tpl = template
22
+ @tag = tag
23
+ end
24
+
25
+ def __yld &block
26
+ @tpl.execute @tag[:children], &block
27
+ end
28
+
29
+ def method_missing(s, *args, &block)
30
+ if args.last.is_a?(Hash)
31
+ @tag[:attrs].merge!(args.pop)
32
+ end
33
+
34
+ if /\!$/ =~ s.to_s
35
+ @tag[:attrs]['id'] = s.to_s.chomp('!')
36
+ else
37
+ if @tag[:attrs]['class']
38
+ @tag[:attrs]['class'] << " #{s}"
39
+ else
40
+ @tag[:attrs]['class'] = s.to_s
41
+ end
42
+ end
43
+
44
+ whitespace = @tpl.whitespace
45
+ @tpl.whitespace = true if args.delete(:whitespace)
46
+ txt = args.flatten.join('')
47
+ @tag[:children] << txt unless txt.empty?
48
+
49
+ @tpl.execute @tag[:children], &block if block_given?
50
+ @tpl.whitespace = whitespace
51
+ self
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,91 @@
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
+ module ViewWrapper
20
+ attr_accessor :malline_is_active
21
+
22
+ # List of all methods that may override some custom view methods
23
+ # If is_malline?, then their _malline_ -prefix versions are called
24
+ @@malline_methods = %w{_erbout cache capture _ tag! << txt!}
25
+
26
+ def init_malline_methods
27
+ @malline_methods_inited = true
28
+ @@malline_methods.each do |m|
29
+ mf = m.gsub('<', 'lt')
30
+ eval %{def #{m}(*x, &b) is_malline? ? _malline_#{mf}(*x, &b) : super; end}
31
+ end
32
+ end
33
+
34
+ def malline opts = nil
35
+ if @malline
36
+ @malline.options.merge!(opts) if opts.is_a?(Hash)
37
+ else
38
+ @malline = Template.new(self, opts)
39
+ end
40
+ init_malline_methods unless @malline_methods_inited
41
+ @malline
42
+ end
43
+
44
+ def _malline__erbout
45
+ @_erbout ||= ErbOut.new(self)
46
+ end
47
+
48
+ def _malline_cache name = {}, options = {}, &block
49
+ return block.call unless @controller.perform_caching
50
+ cache = @controller.read_fragment(name, options)
51
+
52
+ unless cache
53
+ cache = _malline_capture { block.call }
54
+ @controller.write_fragment(name, cache, options)
55
+ end
56
+ @malline.add_unescaped_text cache
57
+ end
58
+
59
+ def _malline_capture &block
60
+ @malline.run &block
61
+ end
62
+
63
+ def _malline__ *args
64
+ @malline.add_text(*args)
65
+ end
66
+ alias_method :_malline_txt!, :_malline__
67
+
68
+ def _malline_ltlt *args
69
+ @malline.add_unescaped_text *args
70
+ end
71
+
72
+ def method_missing s, *args, &block
73
+ return super unless is_malline?
74
+ helper = (s.to_s[0].chr == '_') ? s.to_s[1..255].to_sym : s.to_sym
75
+ if respond_to?(helper)
76
+ @malline.helper(helper, *args, &block)
77
+ else
78
+ return super if @malline.options[:strict]
79
+ _malline_tag! s, *args, &block
80
+ end
81
+ end
82
+
83
+ def _malline_tag! *args, &block
84
+ @malline.tag *args, &block
85
+ end
86
+
87
+ def is_malline?
88
+ @malline_is_active
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,71 @@
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::XHTML
19
+ CUSTOM_TAGS = %w{head title meta}
20
+
21
+ # grep ELEMENT xhtml1-transitional.dtd | cut -d' ' -f2 | tr "\n" " "
22
+ XHTML_TAGS = %w{html head title base meta link style script noscript iframe
23
+ noframes body div p h1 h2 h3 h4 h5 h6 ul ol menu dir li dl dt dd address
24
+ hr pre blockquote center ins del a span bdo br em strong dfn code samp
25
+ kbd var cite abbr acronym q sub sup tt i b big small u s strike basefont
26
+ font object param applet img map area form label input select optgroup
27
+ option textarea fieldset legend button isindex table caption thead tfoot
28
+ tbody colgroup col tr th td} - CUSTOM_TAGS
29
+
30
+ # grep 'ELEMENT.*EMPTY' xhtml1-transitional.dtd | cut -d' ' -f2 | tr "\n" " "
31
+ SHORT_TAG_EXCLUDES = XHTML_TAGS + CUSTOM_TAGS - %w{base meta link hr br
32
+ basefont param img area input isindex col}
33
+
34
+ module Tags
35
+ def xhtml *args, &block
36
+ attrs = { :xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => malline.options[:lang] }
37
+ attrs.merge!(args.pop) if args.last.is_a?(Hash)
38
+
39
+ self << "<?xml version=\"1.0\" encoding=\"#{malline.options[:encoding] || 'UTF-8'}\"?>\n"
40
+ self << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 #{malline.options[:xhtml_dtd] || 'Transitional'}//EN\"\n"
41
+ self << " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-#{(malline.options[:xhtml_dtd] || 'Transitional').downcase}.dtd\">\n"
42
+
43
+ tag! 'html', args.join(''), attrs, &block
44
+ end
45
+
46
+ def title *args, &block
47
+ @__xhtml_title = true
48
+ tag! 'title', *args, &block
49
+ end
50
+
51
+ def meta *args, &block
52
+ @__xhtml_meta = true
53
+ tag! 'meta', *args, &block
54
+ end
55
+
56
+ def head *args, &block
57
+ @__xhtml_title = false
58
+ proxy = tag! 'head', *args, &block
59
+ proxy.__yld { title } unless @__xhtml_title
60
+ proxy.__yld do
61
+ meta :content => "text/html; charset=#{malline.options[:encoding] || 'UTF-8'}", 'http-equiv' => 'Content-Type'
62
+ end unless @__xhtml_meta
63
+ end
64
+ end
65
+
66
+ def self.load_plugin base
67
+ base.definetags *XHTML_TAGS
68
+ base.view.malline.short_tag_excludes += SHORT_TAG_EXCLUDES
69
+ base.view.extend Tags
70
+ end
71
+ end
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright © 2007,2008 Riku Palomäki
3
+ #
4
+ # This file is part of Malline.
5
+ #
6
+ # Malline is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Malline is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Malline. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ require "rexml/document"
20
+ include REXML
21
+
22
+ def html_unescape str
23
+ str.to_s.gsub('&amp;', '&').gsub('&quot;', '"').gsub('&gt;', '>').gsub('&lt;', '<')
24
+ end
25
+
26
+ def esc str
27
+ if str =~ /["#]/ || !(str =~ /'/)
28
+ "'"+(str.split("'").join("\\'"))+"'"
29
+ else
30
+ "\"#{str}\""
31
+ end
32
+ end
33
+
34
+ def attributes element
35
+ element.attributes.keys.collect {|k| "#{esc k} => #{esc html_unescape(element.attributes[k])}" }.join(', ')
36
+ end
37
+
38
+ def txtize txt
39
+ #txt.gsub(/(\s)\s*$/, '\1').gsub(/^(\s)\s*/, '\1')
40
+ txt.gsub(/\s*$/, '').gsub(/^\s*/, '')
41
+ end
42
+
43
+ def convert element, prefix=''
44
+ valid_method = /^[A-Za-z][\w_]*$/
45
+ output = ''
46
+ if element.is_a?(Array)
47
+ element.each {|e| output << convert(e) }
48
+ elsif element.is_a?(Element)
49
+ output << prefix << element.name
50
+ attrs = []
51
+ element.attributes['class'].to_s.split.uniq.each do |cl|
52
+ if valid_method =~ cl
53
+ output << ".#{cl}"
54
+ else
55
+ attrs << cl
56
+ end
57
+ end
58
+ element.attributes.delete('class')
59
+ element.attributes['class'] = attrs.join(' ') unless attrs.empty?
60
+ if element.attributes['id'].to_s =~ valid_method
61
+ output << ".#{element.attributes['id']}!"
62
+ element.attributes.delete('id')
63
+ end
64
+ txt = ''
65
+ children = element.children
66
+ unless children.empty?
67
+ if children.first.is_a?(Text)
68
+ txt = txtize children.shift.to_s
69
+ output << " #{esc html_unescape(txt)}" unless txt.empty?
70
+ end
71
+ end
72
+
73
+ output << (txt.empty? ? ' ' : ', ') << attributes(element) if element.has_attributes?
74
+ unless children.empty?
75
+ output << " do\n"
76
+ children.each {|e| output << convert(e, prefix + "\t") }
77
+ output << prefix << "end"
78
+ end
79
+ output << "\n"
80
+ elsif element.is_a?(Text)
81
+ txt = txtize(element.to_s)
82
+ output << prefix << "txt! #{esc html_unescape(txt)}\n" unless txt.empty?
83
+ end
84
+ output
85
+ end
86
+
87
+ input = File.open(ARGV.shift, 'r') rescue $stdin
88
+ output = File.open(ARGV.shift, 'w') rescue $stdout
89
+
90
+ doc = Document.new input
91
+
92
+
93
+ output.puts convert(doc.children)
@@ -0,0 +1,4 @@
1
+ @title = 'Data'
2
+ ul.data! do
3
+ 3.times {|i| li "Data #{i}" }
4
+ end
@@ -0,0 +1 @@
1
+ <ul id="data"><li>Data 0</li><li>Data 1</li><li>Data 2</li></ul>
@@ -0,0 +1 @@
1
+ span.one 'This is the first'
@@ -0,0 +1 @@
1
+ <span class="one">This is the first</span>
@@ -0,0 +1,2 @@
1
+ # This is just a stupid partial
2
+ div 'I always thought something was fundamentally wrong with the universe.'
@@ -0,0 +1 @@
1
+ <div>I always thought something was fundamentally wrong with the universe.</div>
@@ -0,0 +1,2 @@
1
+ Blop, <%= 'this is rhtml'.upcase %>.
2
+ <%= render :partial => 'examples/two' %>
@@ -0,0 +1 @@
1
+ img :src => image_path('second')
@@ -0,0 +1 @@
1
+ <img src="/images/img"/>
@@ -0,0 +1,13 @@
1
+ div do
2
+ @foo = capture do
3
+ div do
4
+ img :src => '/images/image.png'
5
+ br
6
+ span.caption 'Taken at the location of the event'
7
+ end
8
+ end
9
+
10
+ h4 'Captured'
11
+ self << @foo
12
+ _'EOF'
13
+ end
@@ -0,0 +1 @@
1
+ <div><h4>Captured</h4><div><img src="/images/image.png"/><br/><span class="caption">Taken at the location of the event</span></div>EOF</div>
@@ -0,0 +1,6 @@
1
+ # These two things are the same
2
+ div :class => 'page'
3
+ div.page
4
+
5
+ # You can even give more than one class
6
+ div.foo.bar.many
@@ -0,0 +1 @@
1
+ <div class="page"></div><div class="page"></div><div class="foo bar many"></div>
@@ -0,0 +1,4 @@
1
+ p 'Do you know, that 2 < 5?', :title => '2 < 5'
2
+ p do
3
+ _'Me & Myself & Malline'
4
+ end
@@ -0,0 +1 @@
1
+ <p title="2 &lt; 5">Do you know, that 2 &lt; 5?</p><p>Me &amp; Myself &amp; Malline</p>
@@ -0,0 +1,10 @@
1
+ div do
2
+ cache "stuff" do
3
+ # This block is evaluated only if no cache with keyword stuff is found
4
+ # After evaluation the block is saved to cache
5
+ # If caching is not enabled, the cache block is transparent
6
+ h4 'Some partial stuff'
7
+ _render :partial => 'examples/partial'
8
+ end
9
+ _'dynamic content'
10
+ end
@@ -0,0 +1 @@
1
+ <div><h4>Some partial stuff</h4><div>I always thought something was fundamentally wrong with the universe.</div>dynamic content</div>
@@ -0,0 +1,6 @@
1
+ div.page do
2
+ h1 'Header one'
3
+ ul.list! do
4
+ li { a 'Link to somewhere', :href => '/foo' }
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ <div class="page"><h1>Header one</h1><ul id="list"><li><a href="/foo">Link to somewhere</a></li></ul></div>
@@ -0,0 +1,2 @@
1
+ # Lets say hello to the world
2
+ div 'Hello world', :title => 'Hello'
@@ -0,0 +1 @@
1
+ <div title="Hello">Hello world</div>
@@ -0,0 +1,5 @@
1
+ def rot13 name
2
+ '<div>' + (name.tr "A-Za-z", "N-ZA-Mn-za-m") + '</div>'
3
+ end
4
+
5
+ self << rot13('secret stuff')
@@ -0,0 +1 @@
1
+ <div>frperg fghss</div>
@@ -0,0 +1,5 @@
1
+ def rot13 name
2
+ div name.tr("A-Za-z", "N-ZA-Mn-za-m")
3
+ end
4
+
5
+ rot13('frperg fghss')
@@ -0,0 +1 @@
1
+ <div>secret stuff</div>
@@ -0,0 +1,5 @@
1
+ p do
2
+ # These two do exactly the same
3
+ self << link_to('Frontpage', '/')
4
+ _link_to('Frontpage', '/')
5
+ end
@@ -0,0 +1 @@
1
+ <p>linklink</p>
@@ -0,0 +1,3 @@
1
+ # id:s are given by shouting loud enough
2
+ div.page!
3
+ div.foo.page!.bar