@@ -866,10 +866,11 module ApplicationHelper | |||||
866 | ( |
|
866 | ( | |
867 | \{\{ # opening tag |
|
867 | \{\{ # opening tag | |
868 | ([\w]+) # macro name |
|
868 | ([\w]+) # macro name | |
869 |
(\(( |
|
869 | (\(([^\n\r]*?)\))? # optional arguments | |
|
870 | ([\n\r].*[\n\r])? # optional block of text | |||
870 | \}\} # closing tag |
|
871 | \}\} # closing tag | |
871 | ) |
|
872 | ) | |
872 | )/x unless const_defined?(:MACROS_RE) |
|
873 | )/mx unless const_defined?(:MACROS_RE) | |
873 |
|
874 | |||
874 | MACRO_SUB_RE = /( |
|
875 | MACRO_SUB_RE = /( | |
875 | \{\{ |
|
876 | \{\{ | |
@@ -899,9 +900,9 module ApplicationHelper | |||||
899 | all, index = $1, $2.to_i |
|
900 | all, index = $1, $2.to_i | |
900 | orig = macros.delete(index) |
|
901 | orig = macros.delete(index) | |
901 | if execute && orig && orig =~ MACROS_RE |
|
902 | if execute && orig && orig =~ MACROS_RE | |
902 | esc, all, macro, args = $2, $3, $4.downcase, $6.to_s |
|
903 | esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip) | |
903 | if esc.nil? |
|
904 | if esc.nil? | |
904 | h(exec_macro(macro, obj, args) || all) |
|
905 | h(exec_macro(macro, obj, args, block) || all) | |
905 | else |
|
906 | else | |
906 | h(all) |
|
907 | h(all) | |
907 | end |
|
908 | end |
@@ -24,7 +24,7 module Redmine | |||||
24 | Redmine::WikiFormatting::Macros.available_macros.key?(name.to_sym) |
|
24 | Redmine::WikiFormatting::Macros.available_macros.key?(name.to_sym) | |
25 | end |
|
25 | end | |
26 |
|
26 | |||
27 | def exec_macro(name, obj, args) |
|
27 | def exec_macro(name, obj, args, text) | |
28 | macro_options = Redmine::WikiFormatting::Macros.available_macros[name.to_sym] |
|
28 | macro_options = Redmine::WikiFormatting::Macros.available_macros[name.to_sym] | |
29 | return unless macro_options |
|
29 | return unless macro_options | |
30 |
|
30 | |||
@@ -34,7 +34,13 module Redmine | |||||
34 | end |
|
34 | end | |
35 |
|
35 | |||
36 | begin |
|
36 | begin | |
37 | send(method_name, obj, args) if respond_to?(method_name) |
|
37 | if self.class.instance_method(method_name).arity == 3 | |
|
38 | send(method_name, obj, args, text) | |||
|
39 | elsif text | |||
|
40 | raise "This macro does not accept a block of text" | |||
|
41 | else | |||
|
42 | send(method_name, obj, args) | |||
|
43 | end | |||
38 | rescue => e |
|
44 | rescue => e | |
39 | "<div class=\"flash error\">Error executing the <strong>#{h name}</strong> macro (#{h e.to_s})</div>".html_safe |
|
45 | "<div class=\"flash error\">Error executing the <strong>#{h name}</strong> macro (#{h e.to_s})</div>".html_safe | |
40 | end |
|
46 | end | |
@@ -55,9 +61,11 module Redmine | |||||
55 |
|
61 | |||
56 | class << self |
|
62 | class << self | |
57 | # Called with a block to define additional macros. |
|
63 | # Called with a block to define additional macros. | |
58 | # Macro blocks accept 2 arguments: |
|
64 | # Macro blocks accept 2 or 3 arguments: | |
59 | # * obj: the object that is rendered |
|
65 | # * obj: the object that is rendered | |
60 | # * args: macro arguments |
|
66 | # * args: macro arguments | |
|
67 | # * text: a block of text (if the macro accepts | |||
|
68 | # 3 arguments) | |||
61 | # |
|
69 | # | |
62 | # Plugins can use this method to define new macros: |
|
70 | # Plugins can use this method to define new macros: | |
63 | # |
|
71 | # | |
@@ -66,7 +74,33 module Redmine | |||||
66 | # macro :my_macro do |obj, args| |
|
74 | # macro :my_macro do |obj, args| | |
67 | # "My macro output" |
|
75 | # "My macro output" | |
68 | # end |
|
76 | # end | |
|
77 | # | |||
|
78 | # desc "This is my macro that accepts a block of text" | |||
|
79 | # macro :my_macro do |obj, args, text| | |||
|
80 | # "My macro output" | |||
|
81 | # end | |||
69 | # end |
|
82 | # end | |
|
83 | # | |||
|
84 | # Macros are invoked in formatted text using the following | |||
|
85 | # syntax: | |||
|
86 | # | |||
|
87 | # No arguments: | |||
|
88 | # {{my_macro}} | |||
|
89 | # | |||
|
90 | # With arguments: | |||
|
91 | # {{my_macro(arg1, arg2)}} | |||
|
92 | # | |||
|
93 | # With a block of text: | |||
|
94 | # {{my_macro | |||
|
95 | # multiple lines | |||
|
96 | # of text | |||
|
97 | # }} | |||
|
98 | # | |||
|
99 | # With arguments and a block of text | |||
|
100 | # {{my_macro(arg1, arg2) | |||
|
101 | # multiple lines | |||
|
102 | # of text | |||
|
103 | # }} | |||
70 | def register(&block) |
|
104 | def register(&block) | |
71 | class_eval(&block) if block_given? |
|
105 | class_eval(&block) if block_given? | |
72 | end |
|
106 | end | |
@@ -79,7 +113,7 module Redmine | |||||
79 | # |
|
113 | # | |
80 | # Examples: |
|
114 | # Examples: | |
81 | # By default, when the macro is invoked, the coma separated list of arguments |
|
115 | # By default, when the macro is invoked, the coma separated list of arguments | |
82 |
# is |
|
116 | # is split and passed to the macro block as an array: | |
83 | # |
|
117 | # | |
84 | # macro :my_macro do |obj, args| |
|
118 | # macro :my_macro do |obj, args| | |
85 | # # args is an array |
|
119 | # # args is an array | |
@@ -106,8 +140,11 module Redmine | |||||
106 |
|
140 | |||
107 | # Builtin macros |
|
141 | # Builtin macros | |
108 | desc "Sample macro." |
|
142 | desc "Sample macro." | |
109 | macro :hello_world do |obj, args| |
|
143 | macro :hello_world do |obj, args, text| | |
110 | h("Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}")) |
|
144 | h("Hello world! Object: #{obj.class.name}, " + | |
|
145 | (args.empty? ? "Called with no argument" : "Arguments: #{args.join(', ')}") + | |||
|
146 | " and " + (text.present? ? "a #{text.size} bytes long block of text." : "no block of text.") | |||
|
147 | ) | |||
111 | end |
|
148 | end | |
112 |
|
149 | |||
113 | desc "Displays a list of all available macros, including description if available." |
|
150 | desc "Displays a list of all available macros, including description if available." |
@@ -65,6 +65,19 class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase | |||||
65 | assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar()}}") |
|
65 | assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar()}}") | |
66 | end |
|
66 | end | |
67 |
|
67 | |||
|
68 | def test_macro_registration_with_3_args_should_receive_text_argument | |||
|
69 | Redmine::WikiFormatting::Macros.register do | |||
|
70 | macro :baz do |obj, args, text| | |||
|
71 | "Baz: (#{args.join(',')}) (#{text.class.name}) (#{text})" | |||
|
72 | end | |||
|
73 | end | |||
|
74 | ||||
|
75 | assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz}}") | |||
|
76 | assert_equal "<p>Baz: () (NilClass) ()</p>", textilizable("{{baz()}}") | |||
|
77 | assert_equal "<p>Baz: () (String) (line1\nline2)</p>", textilizable("{{baz()\nline1\nline2\n}}") | |||
|
78 | assert_equal "<p>Baz: (arg1,arg2) (String) (line1\nline2)</p>", textilizable("{{baz(arg1, arg2)\nline1\nline2\n}}") | |||
|
79 | end | |||
|
80 | ||||
68 | def test_multiple_macros_on_the_same_line |
|
81 | def test_multiple_macros_on_the_same_line | |
69 | Redmine::WikiFormatting::Macros.macro :foo do |obj, args| |
|
82 | Redmine::WikiFormatting::Macros.macro :foo do |obj, args| | |
70 | args.any? ? "args: #{args.join(',')}" : "no args" |
|
83 | args.any? ? "args: #{args.join(',')}" : "no args" | |
@@ -79,14 +92,15 class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase | |||||
79 | def test_macro_should_receive_the_object_as_argument_when_with_object_and_attribute |
|
92 | def test_macro_should_receive_the_object_as_argument_when_with_object_and_attribute | |
80 | issue = Issue.find(1) |
|
93 | issue = Issue.find(1) | |
81 | issue.description = "{{hello_world}}" |
|
94 | issue.description = "{{hello_world}}" | |
82 | assert_equal '<p>Hello world! Object: Issue, Called with no argument.</p>', textilizable(issue, :description) |
|
95 | assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(issue, :description) | |
83 | end |
|
96 | end | |
84 |
|
97 | |||
85 | def test_macro_should_receive_the_object_as_argument_when_called_with_object_option |
|
98 | def test_macro_should_receive_the_object_as_argument_when_called_with_object_option | |
86 | text = "{{hello_world}}" |
|
99 | text = "{{hello_world}}" | |
87 | assert_equal '<p>Hello world! Object: Issue, Called with no argument.</p>', textilizable(text, :object => Issue.find(1)) |
|
100 | assert_equal '<p>Hello world! Object: Issue, Called with no argument and no block of text.</p>', textilizable(text, :object => Issue.find(1)) | |
88 | end |
|
101 | end | |
89 |
|
102 | |||
|
103 | ||||
90 | def test_macro_exception_should_be_displayed |
|
104 | def test_macro_exception_should_be_displayed | |
91 | Redmine::WikiFormatting::Macros.macro :exception do |obj, args| |
|
105 | Redmine::WikiFormatting::Macros.macro :exception do |obj, args| | |
92 | raise "My message" |
|
106 | raise "My message" | |
@@ -237,14 +251,14 class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase | |||||
237 | RAW |
|
251 | RAW | |
238 |
|
252 | |||
239 | expected = <<-EXPECTED |
|
253 | expected = <<-EXPECTED | |
240 | <p>Hello world! Object: NilClass, Arguments: foo</p> |
|
254 | <p>Hello world! Object: NilClass, Arguments: foo and no block of text.</p> | |
241 |
|
255 | |||
242 | <pre> |
|
256 | <pre> | |
243 | {{hello_world(pre)}} |
|
257 | {{hello_world(pre)}} | |
244 | !{{hello_world(pre)}} |
|
258 | !{{hello_world(pre)}} | |
245 | </pre> |
|
259 | </pre> | |
246 |
|
260 | |||
247 | <p>Hello world! Object: NilClass, Arguments: bar</p> |
|
261 | <p>Hello world! Object: NilClass, Arguments: bar and no block of text.</p> | |
248 | EXPECTED |
|
262 | EXPECTED | |
249 |
|
263 | |||
250 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(text).gsub(%r{[\r\n\t]}, '') |
|
264 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(text).gsub(%r{[\r\n\t]}, '') | |
@@ -257,6 +271,6 EXPECTED | |||||
257 |
|
271 | |||
258 | def test_macros_should_not_mangle_next_macros_outputs |
|
272 | def test_macros_should_not_mangle_next_macros_outputs | |
259 | text = '{{macro(2)}} !{{macro(2)}} {{hello_world(foo)}}' |
|
273 | text = '{{macro(2)}} !{{macro(2)}} {{hello_world(foo)}}' | |
260 | assert_equal '<p>{{macro(2)}} {{macro(2)}} Hello world! Object: NilClass, Arguments: foo</p>', textilizable(text) |
|
274 | assert_equal '<p>{{macro(2)}} {{macro(2)}} Hello world! Object: NilClass, Arguments: foo and no block of text.</p>', textilizable(text) | |
261 | end |
|
275 | end | |
262 | end |
|
276 | end |
General Comments 0
You need to be logged in to leave comments.
Login now