malline 1.0.2 → 1.1.0

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