malline 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- Malline 1.0.2
1
+ Malline 1.1.0
2
2
  =============
3
3
 
4
4
  See documentation on http://www.malline.org/
@@ -7,18 +7,18 @@ Copyright © 2007,2008 Riku Palomäki, riku@palomaki.fi
7
7
  Malline is released under GNU Lesser General Public License.
8
8
 
9
9
 
10
- Example template file images.mn:
10
+ Example Rails template file images.html.mn:
11
11
 
12
- html do
13
- _render :partial => 'head'
14
- body do
15
- div.images! "There are some images:" do
16
- images.each do |im|
17
- a(:href => img_path(im)) { img :src => im.url }
18
- span.caption im.caption
12
+ xhtml do
13
+ _render :partial => 'head'
14
+ body do
15
+ div.images! "There are some images:" do
16
+ images.each do |im|
17
+ a(:href => img_path(im)) { img :src => im.url }
18
+ span.caption im.caption
19
+ end
20
+ _"No more images"
19
21
  end
20
- _"No more images"
22
+ div.footer! { _render :partial => 'footer' }
21
23
  end
22
- div.footer! { _render :partial => 'footer' }
23
24
  end
24
- end
File without changes
@@ -17,90 +17,91 @@
17
17
 
18
18
  require 'malline/view_proxy.rb'
19
19
  require 'malline/view_wrapper.rb'
20
- require 'malline/view_xhtml.rb'
21
20
  require 'malline/erb_out.rb'
22
- require 'malline/form_builder.rb'
23
21
  require 'malline/template.rb'
22
+ require 'malline/plugin.rb'
23
+ require 'malline/plugins/xhtml.rb'
24
24
 
25
25
  module Malline
26
- VERSION = '1.0.2'
26
+ # Always form ^\d+\.\d+\.\d+(-[^\s]*)?$
27
+ VERSION = '1.1.0'
27
28
 
28
- # Template-handler class that is registered to ActionView and initialized by it.
29
+ # Malline handler, always use Malline engine with this
30
+ # handler = Malline.new @view, :strict => false
31
+ # handler.
29
32
  class Base
30
- # Default options for new instances, can be changed with setopt
31
- @@options = { :strict => true, :xhtml => true, :encoding => 'UTF-8', :lang => 'en', :form_for_proxy => true }
32
- attr_reader :view
33
+ attr_accessor :malline
34
+
35
+ # Default options, can be changed with setopt
36
+ @@options = {
37
+ :strict => true,
38
+ :xhtml => true,
39
+ :encoding => 'UTF-8',
40
+ :lang => 'en',
41
+ :form_for_proxy => true
42
+ }
33
43
 
34
44
  # First parameter is the view object (if any)
35
45
  # Last parameter is optional options hash
36
- def initialize(*opts)
46
+ def initialize *opts
37
47
  @options = @@options.dup
38
48
  @options.merge! opts.pop if opts.last.is_a?(Hash)
39
49
 
40
50
  @view = opts.shift || Class.new
41
- unless @view.is_a?(ViewWrapper)
42
- @view.extend ViewWrapper
43
- @view.malline @options
44
- Malline::XHTML.load_plugin self if @options[:xhtml]
45
- else
46
- @view.malline @options
47
- end
51
+ @view.extend ViewWrapper unless @view.is_a?(ViewWrapper)
52
+ @malline = @view.malline @options
53
+ end
48
54
 
49
- if @options[:form_for_proxy]
50
- begin
51
- ActionView::Base.default_form_builder = ::Malline::FormBuilder
52
- rescue NameError
53
- end
54
- end
55
+ # Get the current filename
56
+ def path
57
+ @view.malline.path
55
58
  end
56
59
 
57
- def set_path path
58
- @view.malline.path = path
60
+ def path= npath
61
+ @view.malline.path = npath
59
62
  end
60
63
 
64
+ # for example:
65
+ # setopt :strict => false
66
+ #
67
+ # or:
68
+ # setopt :strict => false
69
+ # something
70
+ # setopt :strict => true do
71
+ # something strict
72
+ # end
61
73
  def self.setopt hash
62
- output = nil
63
- if block_given?
64
- o = @@options.dup
65
- @@options.merge!(hash) if hash
66
- begin
67
- output = yield
68
- ensure
69
- @@options = o
70
- end
71
- else
72
- @@options.merge!(hash)
73
- end
74
- output
74
+ return @@options.merge!(hash) unless block_given?
75
+ old = @@options.dup
76
+ @@options.merge! hash if hash
77
+ yield
78
+ ensure
79
+ @@options = old if old
75
80
  end
76
81
 
77
- # n is there to keep things compatible with Markaby
78
- def render tpl = nil, local_assigns = {}, n = nil, &block
82
+ def render tpl = nil, local_assigns = {}, &block
79
83
  add_local_assigns local_assigns
80
- @view.malline_is_active = true
81
84
  @view.malline.run tpl, &block
82
85
  end
83
86
 
84
87
  def self.render tpl = nil, local_assigns = {}, &block
85
- self.new.render(tpl, local_assigns, &block)
88
+ self.new.render tpl, local_assigns, &block
86
89
  end
87
90
 
88
- # TODO: These should also be able to disable
89
- def definetags *tags
90
- tags.each do |tag|
91
- eval %{
92
- def @view.#{tag}(*args, &block)
93
- tag!('#{tag}', *args, &block)
94
- end
95
- }
96
- end
91
+ def definetags *args
92
+ @view.malline.definetags *args
93
+ end
94
+
95
+ def definetags! *args
96
+ @view.malline.definetags! *args
97
97
  end
98
98
 
99
99
  private
100
+ # Define hash as instance variables, for example { :foo => 'bar' }
101
+ # will work as @foo == 'bar' and foo == 'bar'
100
102
  def add_local_assigns l
101
103
  @view.instance_eval do
102
104
  l.each { |key, value| instance_variable_set "@#{key}", value }
103
- evaluate_assigns if respond_to?(:evaluate_assigns, true)
104
105
  class << self; self; end.send(:attr_accessor, *(l.keys))
105
106
  end
106
107
  end
@@ -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 'mn', 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 'mn', Malline::RailsHandler
@@ -16,11 +16,16 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  module Malline
19
- # ERB emulator
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.
20
24
  class ErbOut
21
25
  def initialize view
22
26
  @view = view
23
27
  end
28
+ # Redirect all data to view
24
29
  def concat value
25
30
  @view << value
26
31
  end
@@ -16,14 +16,25 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
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)
19
28
  class FormBuilder
20
- def initialize *args
29
+ # Wrap the Rails FormBuilder in @builder
30
+ def initialize *args, &block
21
31
  @view = eval('self', args.last)
22
- @view = nil unless @view.respond_to?(:is_malline?) && @view.is_malline?
23
- @builder = ::ActionView::Helpers::FormBuilder.new(*args)
32
+ @builder = ::ActionView::Helpers::FormBuilder.new(*args, &block)
24
33
  end
34
+ # Render every f.foo -method to view, unless we aren't using
35
+ # Malline template now
25
36
  def method_missing *args, &block
26
- if @view
37
+ if @view && @view.is_malline?
27
38
  @view << @builder.send(*args, &block)
28
39
  else
29
40
  @builder.send(*args, &block)
@@ -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
@@ -15,7 +15,13 @@
15
15
  # You should have received a copy of the GNU Lesser General Public License
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- module Malline::XHTML
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
19
25
  CUSTOM_TAGS = %w{head title meta}
20
26
 
21
27
  # grep ELEMENT xhtml1-transitional.dtd | cut -d' ' -f2 | tr "\n" " "
@@ -63,9 +69,9 @@ module Malline::XHTML
63
69
  end
64
70
  end
65
71
 
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
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
70
76
  end
71
77
  end
@@ -16,30 +16,31 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  require 'malline' unless Kernel.const_defined?('Malline')
19
+ require 'malline/form_builder.rb'
19
20
 
20
- ActionView::Base.register_template_handler 'mn', Malline::Base
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
21
26
 
22
- module ActionView
23
- class Base
24
- alias_method :orig_render_template, :render_template
25
- def render_template template_extension, template, file_path = nil, *rest
26
- @current_tpl_path = file_path
27
- orig_render_template(template_extension, template, file_path, *rest)
28
- end
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'
29
32
 
30
- alias_method :orig_delegate_render, :delegate_render
31
- def delegate_render(handler, template, local_assigns)
32
- old = @malline_is_active
33
- tmp = if handler == Malline::Base
34
- h = handler.new(self)
35
- h.set_path(@current_tpl_path) if @current_tpl_path
36
- h.render(template, local_assigns)
37
- else
38
- @malline_is_active = false
39
- orig_delegate_render(handler, template, local_assigns)
40
- end
41
- @malline_is_active = old
42
- tmp
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)
43
41
  end
42
+ @malline.add_unescaped_text cache
44
43
  end
45
44
  end
45
+
46
+
@@ -16,11 +16,26 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
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'
19
22
  class Template
23
+ # Current options (like @@options in Base)
20
24
  attr_accessor :options
25
+ # List of every tag that doesn't support self-closing syntax
21
26
  attr_accessor :short_tag_excludes
27
+ # Current state of :whitespace-modifier (bool)
22
28
  attr_accessor :whitespace
29
+ # Current file name
23
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
24
39
 
25
40
  def initialize view, opts
26
41
  @view = view
@@ -28,13 +43,25 @@ module Malline
28
43
  @path = 'Malline template'
29
44
  @options = opts
30
45
  @short_tag_excludes = []
46
+ @helper_overrides = {}
47
+ @tags = {}
48
+ @plugins = []
49
+ @inited = false
31
50
  end
32
51
 
33
- # These two are stolen from ERB
34
- # © 1999-2000,2002,2003 Masatoshi SEKI
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
35
61
  def self.html_escape(s)
36
62
  s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
37
63
  end
64
+ # Stolen from ERB, © 1999-2000,2002,2003 Masatoshi SEKI
38
65
  def self.url_encode(s)
39
66
  s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
40
67
  end
@@ -51,23 +78,33 @@ module Malline
51
78
  @dom = tmp
52
79
  end
53
80
 
81
+ # Add escaped string to @dom
54
82
  def add_text *values
55
83
  @dom << ' ' if @whitespace
56
84
  @dom << Template.html_escape(values.join(' '))
57
85
  end
58
86
 
87
+ # Add unescaped string to @dom
59
88
  def add_unescaped_text value
60
89
  @dom << ' ' if @whitespace
61
90
  @dom << value.to_s unless value.nil?
62
91
  end
63
92
 
93
+ # Call a helper (a method defined outside malline whose
94
+ # output is stored to @dom)
64
95
  def helper helper, *args, &block
65
- tmp = @view.send(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
66
102
  @dom << ' ' if @whitespace
67
103
  @dom << tmp.to_s
68
104
  tmp
69
105
  end
70
106
 
107
+ # Add a tag to @dom
71
108
  def tag s, *args, &block
72
109
  tag = { :name => s.to_s, :attrs => {}, :children => [] }
73
110
 
@@ -89,15 +126,15 @@ module Malline
89
126
  ViewProxy.new self, tag
90
127
  end
91
128
 
92
- # Render the xml tree at dom or root
129
+ # Render the XML tree at dom or @dom
93
130
  def render dom = nil
94
- (dom || @dom).inject('') do |out, tag|
131
+ @rendered = (dom || @dom).inject('') do |out, tag|
95
132
  if tag.is_a?(String)
96
133
  out << tag
97
134
  else
98
135
  out << ' ' if tag[:whitespace]
99
136
  out << "<#{tag[:name]}"
100
- out << tag[:attrs].inject(''){|s, a| s += " #{a.first}=\"#{Template.html_escape(a.last)}\""}
137
+ out << tag[:attrs].inject(''){|s, a| s + " #{a.first}=\"#{Template.html_escape(a.last)}\""}
101
138
 
102
139
  if tag[:children].empty?
103
140
  if @short_tag_excludes.include?(tag[:name])
@@ -116,9 +153,33 @@ module Malline
116
153
 
117
154
  # Execute and render a text or block
118
155
  def run tpl = nil, &block
156
+ init
119
157
  tmp = []
120
158
  execute tmp, tpl, &block
121
159
  render tmp
122
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
123
184
  end
124
185
  end
@@ -16,39 +16,72 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  module Malline
19
+ # Every tag returns a ViewProxy object that binds the tag to the template.
20
+ # ViewProxy also chains the process by returning itself.
21
+ #
22
+ # This Proxy object then makes possible the attribute syntax:
23
+ # div.foo.bar! { stuff }
24
+ #
25
+ # div returns new ViewProxy instance, so div.foo actually calls
26
+ # ViewProxy#foo, which is then generated to class="foo" -attribute to the
27
+ # original tag div. div.foo returns the same ViewProxy, and foo.bar! calls
28
+ # ViewProxy#bar!, which is interpreted as a id="bar" -attribute.
29
+ #
30
+ # Finally the given block { stuff } is evaluated the same way than it would
31
+ # be evaluated without the ViewProxy:
32
+ # div { stuff }
19
33
  class ViewProxy
20
34
  def initialize template, tag
21
35
  @tpl = template
22
36
  @tag = tag
23
37
  end
24
38
 
25
- def __yld &block
26
- @tpl.execute @tag[:children], &block
27
- end
28
-
29
- def method_missing(s, *args, &block)
39
+ # Allows to add new content to already closed tag, for example:
40
+ # t = div do
41
+ # _'text'
42
+ # end
43
+ # t.__yld :whitespace { stuff }
44
+ #
45
+ # Intended for internal use only
46
+ def __yld *args, &block
47
+ # div :title => 'data'
30
48
  if args.last.is_a?(Hash)
31
49
  @tag[:attrs].merge!(args.pop)
32
50
  end
33
51
 
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
-
52
+ # Modifiers
44
53
  whitespace = @tpl.whitespace
45
54
  @tpl.whitespace = true if args.delete(:whitespace)
46
- txt = args.flatten.join('')
55
+
56
+ # Rest is just content separated by a space
57
+ txt = args.flatten.join ' '
47
58
  @tag[:children] << txt unless txt.empty?
48
59
 
60
+ # Block
49
61
  @tpl.execute @tag[:children], &block if block_given?
62
+
63
+ # Restore modifiers
50
64
  @tpl.whitespace = whitespace
65
+
66
+ # Chain the calls, for example: div.foo.bar!.yeah.boring
51
67
  self
52
68
  end
69
+
70
+ # Capture attribute definitions, special modifiers and blocks
71
+ def method_missing s, *args, &block
72
+ # div.id!
73
+ if /^(.*)!$/ =~ s.to_s
74
+ @tag[:attrs]['id'] = $1
75
+ elsif s
76
+ # div.class
77
+ if @tag[:attrs]['class']
78
+ @tag[:attrs]['class'] << " #{s}"
79
+ else
80
+ @tag[:attrs]['class'] = s.to_s
81
+ end
82
+ end
83
+
84
+ __yld *args, &block
85
+ end
53
86
  end
54
87
  end
@@ -16,13 +16,14 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  module Malline
19
+ # ViewWrapper is extended into used view object, like ActiveView::Base.
20
+ # Every method in ViewWrapper will pollute the original namespace.
19
21
  module ViewWrapper
20
- attr_accessor :malline_is_active
21
-
22
22
  # List of all methods that may override some custom view methods
23
23
  # If is_malline?, then their _malline_ -prefix versions are called
24
- @@malline_methods = %w{_erbout cache capture _ tag! << txt!}
24
+ @@malline_methods = %w{_erbout capture _ tag! << txt!}
25
25
 
26
+ # Initialize @@malline_methods
26
27
  def init_malline_methods
27
28
  @malline_methods_inited = true
28
29
  @@malline_methods.each do |m|
@@ -31,61 +32,64 @@ module Malline
31
32
  end
32
33
  end
33
34
 
35
+ # Returns a current Template instance, makes a new if called first time
36
+ # Can also be used to set options to Template by giving them as hash opts:
37
+ # malline :whitespace => true
34
38
  def malline opts = nil
35
39
  if @malline
36
40
  @malline.options.merge!(opts) if opts.is_a?(Hash)
37
41
  else
38
- @malline = Template.new(self, opts)
42
+ @malline = Template.new(self, opts || {})
39
43
  end
40
44
  init_malline_methods unless @malline_methods_inited
41
45
  @malline
42
46
  end
43
47
 
48
+ # erbout emulator
44
49
  def _malline__erbout
45
50
  @_erbout ||= ErbOut.new(self)
46
51
  end
47
52
 
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
-
53
+ # capture and return the output of the block
59
54
  def _malline_capture &block
60
55
  @malline.run &block
61
56
  end
62
57
 
58
+ # _'escaped text'
63
59
  def _malline__ *args
64
60
  @malline.add_text(*args)
65
61
  end
66
62
  alias_method :_malline_txt!, :_malline__
67
63
 
64
+ # self << "<unescaped text>"
68
65
  def _malline_ltlt *args
69
66
  @malline.add_unescaped_text *args
70
67
  end
71
68
 
69
+ # Define a new tag of call a helper (if _prefixed)
72
70
  def method_missing s, *args, &block
73
71
  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)
72
+ if @malline.tags[s]
73
+ @malline.tag s, *args, &block
77
74
  else
78
- return super if @malline.options[:strict]
79
- _malline_tag! s, *args, &block
75
+ helper = ((s.to_s[0] == ?_) ? s.to_s[1..-1] : s).to_sym
76
+ if respond_to?(helper)
77
+ @malline.helper(helper, *args, &block)
78
+ else
79
+ return super if @malline.options[:strict]
80
+ _malline_tag! s, *args, &block
81
+ end
80
82
  end
81
83
  end
82
84
 
85
+ # Define a new tag
83
86
  def _malline_tag! *args, &block
84
87
  @malline.tag *args, &block
85
88
  end
86
89
 
90
+ # Are we in a Malline template
87
91
  def is_malline?
88
- @malline_is_active
92
+ true
89
93
  end
90
94
  end
91
95
  end
File without changes
@@ -19,35 +19,6 @@ $: << t
19
19
  $: << File.join(t, 'lib')
20
20
  require 'test/unit'
21
21
  require 'test/malline_test_helpers.rb'
22
- require 'malline.rb'
23
-
24
- class Controller
25
- def perform_caching
26
- false
27
- end
28
- end
29
-
30
- class Comment
31
- end
32
-
33
- class View
34
- def initialize
35
- @controller = Controller.new
36
- end
37
- def image_path(im)
38
- "/images/#{im.is_a?(MallineTestHelpers::Image) ? im.id : 'img'}"
39
- end
40
- def truncate(str, size = 10)
41
- str[0...size]
42
- end
43
- def render hash
44
- Malline::Base.new(View.new).render File.read(File.join(File.dirname(__FILE__), hash[:partial].sub(/\//, '/_') + '.mn')) rescue ''
45
- end
46
- def link_to *args
47
- 'link'
48
- end
49
- end
50
-
51
22
 
52
23
  class MallineTest < Test::Unit::TestCase
53
24
  include Malline
@@ -55,10 +26,11 @@ class MallineTest < Test::Unit::TestCase
55
26
 
56
27
  def test_simple
57
28
  Base.setopt :strict => false, :xhtml => false do
58
- assert_xml_equal('<foo id="a"><bar class="a b"/></foo>',
29
+ assert_xml_equal('<foo id="a"><bar class="a b"/>blabla</foo>',
59
30
  Base.render do
60
31
  foo.a! do
61
32
  bar.a.b
33
+ _'blabla'
62
34
  end
63
35
  end
64
36
  )
@@ -109,22 +81,29 @@ class MallineTest < Test::Unit::TestCase
109
81
  end
110
82
  b = Proc.new do
111
83
  foo do
112
- xxx :a => 'b' do
84
+ zoo :a => 'b' do
113
85
  bar
114
86
  end
87
+ _zoo
88
+ xoo.bar
115
89
  end
116
90
  end
117
91
  out = tpl.render nil, &b
118
- assert_xml_equal('<foo/>', out)
92
+ # zoo isn't rendered, because there is helper named zoo
93
+ assert_xml_equal('<foo>zoo output<xoo class="bar"/></foo>', out)
94
+
95
+ tpl.definetags :zoo
96
+ out = tpl.render nil, &b
97
+ assert_xml_equal('<foo>zoo output<xoo class="bar"/></foo>', out)
119
98
 
120
- tpl.definetags :xxx
99
+ tpl.definetags! :zoo
121
100
  out = tpl.render nil, &b
122
- assert_xml_equal('<foo><xxx a="b"><bar/></xxx></foo>', out)
101
+ assert_xml_equal('<foo><zoo a="b"><bar/></zoo>zoo output<xoo class="bar"/></foo>', out)
123
102
 
124
103
  out = Base.setopt :strict => false, :xhtml => false do
125
104
  Base.render &b
126
105
  end
127
- assert_xml_equal('<foo/>', out)
106
+ assert_xml_equal('<foo><zoo a="b"><bar/></zoo><_zoo/><xoo class="bar"/></foo>', out)
128
107
  end
129
108
 
130
109
  def test_xhtml
@@ -16,11 +16,7 @@
16
16
  # along with Malline. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
18
  require "rexml/document"
19
-
20
- module Kernel
21
- def xxx *args
22
- end
23
- end
19
+ require 'malline.rb'
24
20
 
25
21
  module MallineTestHelpers
26
22
  include REXML
@@ -55,3 +51,32 @@ module MallineTestHelpers
55
51
  end
56
52
  end
57
53
 
54
+ class Controller
55
+ def perform_caching
56
+ false
57
+ end
58
+ end
59
+
60
+ class Comment
61
+ end
62
+
63
+ class View
64
+ def initialize
65
+ @controller = Controller.new
66
+ end
67
+ def image_path(im)
68
+ "/images/#{im.is_a?(MallineTestHelpers::Image) ? im.id : 'img'}"
69
+ end
70
+ def truncate(str, size = 10)
71
+ str[0...size]
72
+ end
73
+ def render hash
74
+ Malline::Base.new(View.new).render File.read(File.join(File.dirname(__FILE__), hash[:partial].sub(/\//, '/_') + '.mn')) rescue ''
75
+ end
76
+ def link_to *args
77
+ 'link'
78
+ end
79
+ def zoo *args
80
+ 'zoo output'
81
+ end
82
+ end
metadata CHANGED
@@ -1,110 +1,118 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: malline
5
3
  version: !ruby/object:Gem::Version
6
- version: 1.0.2
7
- date: 2008-04-01 00:00:00 +03:00
8
- summary: Malline is a full-featured pure Ruby template system designed to be a replacement for ERB views in Rails or any other framework. See http://www.malline.org/ for more info.
9
- require_paths:
10
- - lib
11
- email: riku@palomaki.fi
12
- homepage: http://www.malline.org/
13
- rubyforge_project: malline
14
- description: Malline is a full-featured template system designed to be a replacement for ERB views in Rails or any other framework. It also includes standalone bin/malline to compile Malline templates to XML in commandline. All Malline templates are pure Ruby, see http://www.malline.org/ for more info.
15
- autorequire: malline
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 1.1.0
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - "Riku Palom\xC3\xA4ki"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-06 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Malline is a full-featured template system designed to be a replacement for ERB views in Rails or any other framework. It also includes standalone bin/malline to compile Malline templates to XML in commandline. All Malline templates are pure Ruby, see http://www.malline.org/ for more info.
17
+ email: riku@palomaki.fi
18
+ executables:
19
+ - malline
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
31
24
  files:
32
25
  - lib/malline.rb
33
26
  - lib/malline/erb_out.rb
34
- - lib/malline/view_xhtml.rb
35
- - lib/malline/form_builder.rb
36
- - lib/malline/view_wrapper.rb
37
- - lib/malline/template.rb
38
27
  - lib/malline/view_proxy.rb
39
28
  - lib/malline/rails.rb
29
+ - lib/malline/view_wrapper.rb
30
+ - lib/malline/form_builder.rb
31
+ - lib/malline/adapters/rails-2.1.rb
32
+ - lib/malline/adapters/rails-2.0.rb
33
+ - lib/malline/template.rb
34
+ - lib/malline/plugin.rb
35
+ - lib/malline/plugins/xhtml.rb
40
36
  - bin/malline
41
37
  - COPYING.LESSER
42
38
  - COPYING
43
39
  - README
44
40
  - scripts/html2mn.rb
41
+ - test/malline_test_helpers.rb
45
42
  - test/examples
46
43
  - test/kernel.org.mn
47
- - test/malline_test.rb
48
- - test/malline_test_helpers.rb
49
44
  - test/kernel.org.html
50
- - test/examples/_partial.target
51
- - test/examples/options.target
45
+ - test/malline_test.rb
52
46
  - test/examples/_two.mn
47
+ - test/examples/options.mn
48
+ - test/examples/whitespace.mn
49
+ - test/examples/id.mn
50
+ - test/examples/helper2.mn
51
+ - test/examples/_partial.mn
52
+ - test/examples/helper2.target
53
+ - test/examples/text.target
54
+ - test/examples/escape.target
55
+ - test/examples/_two.target
56
+ - test/examples/class.mn
57
+ - test/examples/_partial.target
58
+ - test/examples/nested.mn
53
59
  - test/examples/xhtml.target
54
- - test/examples/hello_world.mn
60
+ - test/examples/capture.target
61
+ - test/examples/partials.target
62
+ - test/examples/_action.mn
63
+ - test/examples/self.mn
64
+ - test/examples/_three.rhtml
65
+ - test/examples/helper.target
55
66
  - test/examples/lists.target
56
- - test/examples/layout.mn
67
+ - test/examples/_action.target
68
+ - test/examples/layout.target
69
+ - test/examples/hello_world.target
70
+ - test/examples/id.target
57
71
  - test/examples/helper.mn
58
- - test/examples/_one.mn
59
72
  - test/examples/class.target
60
- - test/examples/nested.mn
61
- - test/examples/text.mn
62
- - test/examples/frontpage.mn
63
- - test/examples/helper_shortcut.target
64
- - test/examples/_action.target
65
- - test/examples/_partial.mn
66
- - test/examples/options.mn
67
- - test/examples/partials.target
73
+ - test/examples/capture.mn
74
+ - test/examples/layout.mn
75
+ - test/examples/escape.mn
76
+ - test/examples/frontpage.target
77
+ - test/examples/partials.mn
78
+ - test/examples/_one.target
79
+ - test/examples/hello_world.mn
68
80
  - test/examples/xhtml.mn
69
81
  - test/examples/lists.mn
70
- - test/examples/fragment_cache.target
71
- - test/examples/class.mn
72
- - test/examples/self.target
73
- - test/examples/id.target
82
+ - test/examples/_one.mn
83
+ - test/examples/nested.target
84
+ - test/examples/options.target
74
85
  - test/examples/whitespace.target
86
+ - test/examples/self.target
87
+ - test/examples/helper_shortcut.target
88
+ - test/examples/text.mn
75
89
  - test/examples/helper_shortcut.mn
76
- - test/examples/_action.mn
77
- - test/examples/escape.target
78
- - test/examples/helper2.target
79
- - test/examples/capture.target
80
- - test/examples/partials.mn
81
- - test/examples/_three.rhtml
82
- - test/examples/_two.target
83
- - test/examples/hello_world.target
84
- - test/examples/fragment_cache.mn
85
- - test/examples/layout.target
86
- - test/examples/self.mn
87
- - test/examples/helper.target
88
- - test/examples/id.mn
89
- - test/examples/_one.target
90
- - test/examples/whitespace.mn
91
- - test/examples/nested.target
92
- - test/examples/escape.mn
93
- - test/examples/text.target
94
- - test/examples/helper2.mn
95
- - test/examples/frontpage.target
96
- - test/examples/capture.mn
97
- test_files:
98
- - test/malline_test.rb
90
+ - test/examples/frontpage.mn
91
+ has_rdoc: true
92
+ homepage: http://www.malline.org/
93
+ post_install_message:
99
94
  rdoc_options: []
100
95
 
101
- extra_rdoc_files:
102
- - README
103
- executables:
104
- - malline
105
- extensions: []
106
-
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ version:
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ version:
107
110
  requirements: []
108
111
 
109
- dependencies: []
110
-
112
+ rubyforge_project: malline
113
+ rubygems_version: 1.1.1
114
+ signing_key:
115
+ specification_version: 2
116
+ summary: Malline is a full-featured pure Ruby template system designed to be a replacement for ERB views in Rails or any other framework. See http://www.malline.org/ for more info.
117
+ test_files:
118
+ - test/malline_test.rb
@@ -1,10 +0,0 @@
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
@@ -1 +0,0 @@
1
- <div><h4>Some partial stuff</h4><div>I always thought something was fundamentally wrong with the universe.</div>dynamic content</div>