malline 1.0.2

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