summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/helpers/application_helper.rb9
-rw-r--r--lib/redmine/wiki_formatting/macros.rb49
-rw-r--r--test/unit/lib/redmine/wiki_formatting/macros_test.rb24
3 files changed, 67 insertions, 15 deletions
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 74032496f..cfa9cb5ab 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -866,10 +866,11 @@ module ApplicationHelper
(
\{\{ # opening tag
([\w]+) # macro name
- (\((.*?)\))? # optional arguments
+ (\(([^\n\r]*?)\))? # optional arguments
+ ([\n\r].*[\n\r])? # optional block of text
\}\} # closing tag
)
- )/x unless const_defined?(:MACROS_RE)
+ )/mx unless const_defined?(:MACROS_RE)
MACRO_SUB_RE = /(
\{\{
@@ -899,9 +900,9 @@ module ApplicationHelper
all, index = $1, $2.to_i
orig = macros.delete(index)
if execute && orig && orig =~ MACROS_RE
- esc, all, macro, args = $2, $3, $4.downcase, $6.to_s
+ esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
if esc.nil?
- h(exec_macro(macro, obj, args) || all)
+ h(exec_macro(macro, obj, args, block) || all)
else
h(all)
end
diff --git a/lib/redmine/wiki_formatting/macros.rb b/lib/redmine/wiki_formatting/macros.rb
index 55bde5e29..16a3239ee 100644
--- a/lib/redmine/wiki_formatting/macros.rb
+++ b/lib/redmine/wiki_formatting/macros.rb
@@ -24,7 +24,7 @@ module Redmine
Redmine::WikiFormatting::Macros.available_macros.key?(name.to_sym)
end
- def exec_macro(name, obj, args)
+ def exec_macro(name, obj, args, text)
macro_options = Redmine::WikiFormatting::Macros.available_macros[name.to_sym]
return unless macro_options
@@ -34,7 +34,13 @@ module Redmine
end
begin
- send(method_name, obj, args) if respond_to?(method_name)
+ if self.class.instance_method(method_name).arity == 3
+ send(method_name, obj, args, text)
+ elsif text
+ raise "This macro does not accept a block of text"
+ else
+ send(method_name, obj, args)
+ end
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{h name}</strong> macro (#{h e.to_s})</div>".html_safe
end
@@ -55,9 +61,11 @@ module Redmine
class << self
# Called with a block to define additional macros.
- # Macro blocks accept 2 arguments:
+ # Macro blocks accept 2 or 3 arguments:
# * obj: the object that is rendered
# * args: macro arguments
+ # * text: a block of text (if the macro accepts
+ # 3 arguments)
#
# Plugins can use this method to define new macros:
#
@@ -66,7 +74,33 @@ module Redmine
# macro :my_macro do |obj, args|
# "My macro output"
# end
+ #
+ # desc "This is my macro that accepts a block of text"
+ # macro :my_macro do |obj, args, text|
+ # "My macro output"
+ # end
# end
+ #
+ # Macros are invoked in formatted text using the following
+ # syntax:
+ #
+ # No arguments:
+ # {{my_macro}}
+ #
+ # With arguments:
+ # {{my_macro(arg1, arg2)}}
+ #
+ # With a block of text:
+ # {{my_macro
+ # multiple lines
+ # of text
+ # }}
+ #
+ # With arguments and a block of text
+ # {{my_macro(arg1, arg2)
+ # multiple lines
+ # of text
+ # }}
def register(&block)
class_eval(&block) if block_given?
end
@@ -79,7 +113,7 @@ module Redmine
#
# Examples:
# By default, when the macro is invoked, the coma separated list of arguments
- # is parsed and passed to the macro block as an array:
+ # is split and passed to the macro block as an array:
#
# macro :my_macro do |obj, args|
# # args is an array
@@ -106,8 +140,11 @@ module Redmine
# Builtin macros
desc "Sample macro."
- macro :hello_world do |obj, args|
- h("Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}"))
+ macro :hello_world do |obj, args, text|
+ h("Hello world! Object: #{obj.class.name}, " +
+ (args.empty? ? "Called with no argument" : "Arguments: #{args.join(', ')}") +
+ " and " + (text.present? ? "a #{text.size} bytes long block of text." : "no block of text.")
+ )
end
desc "Displays a list of all available macros, including description if available."
diff --git a/test/unit/lib/redmine/wiki_formatting/macros_test.rb b/test/unit/lib/redmine/wiki_formatting/macros_test.rb
index b313e8b80..901184909 100644
--- a/test/unit/lib/redmine/wiki_formatting/macros_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/macros_test.rb
@@ -65,6 +65,19 @@ class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase
assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar()}}")
end
+ def test_macro_registration_with_3_args_should_receive_text_argument
+ Redmine::WikiFormatting::Macros.register do
+ macro :baz do |obj, args, text|
+ "Baz: (#{args.join(',')}) (#{text.class.name}) (#{text})"
+ end
+ end
+
+ assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz}}")
+ assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz()}}")
+ assert_equal "<p>Baz: () (String) (line1\nline2)</p>", textilizable("{{baz()\nline1\nline2\n}}")
+ assert_equal "<p>Baz: (arg1,arg2) (String) (line1\nline2)</p>", textilizable("{{baz(arg1, arg2)\nline1\nline2\n}}")
+ end
+
def test_multiple_macros_on_the_same_line
Redmine::WikiFormatting::Macros.macro :foo do |obj, args|
args.any? ? "args: #{args.join(',')}" : "no args"
@@ -79,14 +92,15 @@ class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase
def test_macro_should_receive_the_object_as_argument_when_with_object_and_attribute
issue = Issue.find(1)
issue.description = "{{hello_world}}"
- assert_equal '<p>Hello world! Object: Issue, Called with no argument.</p>', textilizable(issue, :description)
+ assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(issue, :description)
end
def test_macro_should_receive_the_object_as_argument_when_called_with_object_option
text = "{{hello_world}}"
- assert_equal '<p>Hello world! Object: Issue, Called with no argument.</p>', textilizable(text, :object => Issue.find(1))
+ assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(text, :object => Issue.find(1))
end
+
def test_macro_exception_should_be_displayed
Redmine::WikiFormatting::Macros.macro :exception do |obj, args|
raise "My message"
@@ -237,14 +251,14 @@ class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase
RAW
expected = <<-EXPECTED
-<p>Hello world! Object: NilClass, Arguments: foo</p>
+<p>Hello world! Object: NilClass, Arguments: foo and no block of text.</p>
<pre>
{{hello_world(pre)}}
!{{hello_world(pre)}}
</pre>
-<p>Hello world! Object: NilClass, Arguments: bar</p>
+<p>Hello world! Object: NilClass, Arguments: bar and no block of text.</p>
EXPECTED
assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(text).gsub(%r{[\r\n\t]}, '')
@@ -257,6 +271,6 @@ EXPECTED
def test_macros_should_not_mangle_next_macros_outputs
text = '{{macro(2)}} !{{macro(2)}} {{hello_world(foo)}}'
- assert_equal '<p>{{macro(2)}} {{macro(2)}} Hello world! Object: NilClass, Arguments: foo</p>', textilizable(text)
+ assert_equal '<p>{{macro(2)}} {{macro(2)}} Hello world! Object: NilClass, Arguments: foo and no block of text.</p>', textilizable(text)
end
end