@@ -860,8 +860,7 module ApplicationHelper | |||||
860 | # Macros substitution |
|
860 | # Macros substitution | |
861 | def parse_macros(text, project, obj, attr, only_path, options) |
|
861 | def parse_macros(text, project, obj, attr, only_path, options) | |
862 | text.gsub!(MACROS_RE) do |
|
862 | text.gsub!(MACROS_RE) do | |
863 | esc, all, macro = $1, $2, $3.downcase |
|
863 | esc, all, macro, args = $1, $2, $3.downcase, $5.to_s | |
864 | args = ($5 || '').split(',').each(&:strip) |
|
|||
865 | if esc.nil? |
|
864 | if esc.nil? | |
866 | begin |
|
865 | begin | |
867 | exec_macro(macro, obj, args) |
|
866 | exec_macro(macro, obj, args) |
@@ -20,7 +20,13 module Redmine | |||||
20 | module Macros |
|
20 | module Macros | |
21 | module Definitions |
|
21 | module Definitions | |
22 | def exec_macro(name, obj, args) |
|
22 | def exec_macro(name, obj, args) | |
|
23 | macro_options = Redmine::WikiFormatting::Macros.available_macros[name.to_sym] | |||
|
24 | return unless macro_options | |||
|
25 | ||||
23 | method_name = "macro_#{name}" |
|
26 | method_name = "macro_#{name}" | |
|
27 | unless macro_options[:parse_args] == false | |||
|
28 | args = args.split(',').map(&:strip) | |||
|
29 | end | |||
24 | send(method_name, obj, args) if respond_to?(method_name) |
|
30 | send(method_name, obj, args) if respond_to?(method_name) | |
25 | end |
|
31 | end | |
26 |
|
32 | |||
@@ -35,6 +41,7 module Redmine | |||||
35 | end |
|
41 | end | |
36 |
|
42 | |||
37 | @@available_macros = {} |
|
43 | @@available_macros = {} | |
|
44 | mattr_accessor :available_macros | |||
38 |
|
45 | |||
39 | class << self |
|
46 | class << self | |
40 | # Called with a block to define additional macros. |
|
47 | # Called with a block to define additional macros. | |
@@ -54,11 +61,28 module Redmine | |||||
54 | class_eval(&block) if block_given? |
|
61 | class_eval(&block) if block_given? | |
55 | end |
|
62 | end | |
56 |
|
63 | |||
57 | private |
|
64 | # Defines a new macro with the given name, options and block. | |
58 | # Defines a new macro with the given name and block. |
|
65 | # | |
59 | def macro(name, &block) |
|
66 | # Options: | |
|
67 | # * :parse_args => false - Disables arguments parsing (the whole arguments string | |||
|
68 | # is passed to the macro) | |||
|
69 | # | |||
|
70 | # Examples: | |||
|
71 | # By default, when the macro is invoked, the coma separated list of arguments | |||
|
72 | # is parsed and passed to the macro block as an array: | |||
|
73 | # | |||
|
74 | # macro :my_macro do |obj, args| | |||
|
75 | # # args is an array | |||
|
76 | # end | |||
|
77 | # | |||
|
78 | # You can disable arguments parsing with the :parse_args => false option: | |||
|
79 | # | |||
|
80 | # macro :my_macro, :parse_args => false do |obj, args| | |||
|
81 | # # args is a string | |||
|
82 | # end | |||
|
83 | def macro(name, options={}, &block) | |||
60 | name = name.to_sym if name.is_a?(String) |
|
84 | name = name.to_sym if name.is_a?(String) | |
61 | @@available_macros[name] = @@desc || '' |
|
85 | @@available_macros[name] = {:desc => @@desc || ''}.merge(options) | |
62 | @@desc = nil |
|
86 | @@desc = nil | |
63 | raise "Can not create a macro without a block!" unless block_given? |
|
87 | raise "Can not create a macro without a block!" unless block_given? | |
64 | Definitions.send :define_method, "macro_#{name}".downcase, &block |
|
88 | Definitions.send :define_method, "macro_#{name}".downcase, &block | |
@@ -79,9 +103,9 module Redmine | |||||
79 | desc "Displays a list of all available macros, including description if available." |
|
103 | desc "Displays a list of all available macros, including description if available." | |
80 | macro :macro_list do |obj, args| |
|
104 | macro :macro_list do |obj, args| | |
81 | out = ''.html_safe |
|
105 | out = ''.html_safe | |
82 |
@@available_macros |
|
106 | @@available_macros.each do |macro, options| | |
83 | out << content_tag('dt', content_tag('code', macro)) |
|
107 | out << content_tag('dt', content_tag('code', macro.to_s)) | |
84 |
out << content_tag('dd', textilizable( |
|
108 | out << content_tag('dd', textilizable(options[:desc])) | |
85 | end |
|
109 | end | |
86 | content_tag('dl', out) |
|
110 | content_tag('dl', out) | |
87 | end |
|
111 | end |
@@ -43,12 +43,26 class Redmine::WikiFormatting::MacrosTest < ActionView::TestCase | |||||
43 | def test_macro_registration |
|
43 | def test_macro_registration | |
44 | Redmine::WikiFormatting::Macros.register do |
|
44 | Redmine::WikiFormatting::Macros.register do | |
45 | macro :foo do |obj, args| |
|
45 | macro :foo do |obj, args| | |
46 | "Foo macro output" |
|
46 | "Foo: #{args.size} (#{args.join(',')}) (#{args.class.name})" | |
47 | end |
|
47 | end | |
48 | end |
|
48 | end | |
49 |
|
49 | |||
50 | text = "{{foo}}" |
|
50 | assert_equal '<p>Foo: 0 () (Array)</p>', textilizable("{{foo}}") | |
51 |
assert_equal '<p>Foo |
|
51 | assert_equal '<p>Foo: 0 () (Array)</p>', textilizable("{{foo()}}") | |
|
52 | assert_equal '<p>Foo: 1 (arg1) (Array)</p>', textilizable("{{foo(arg1)}}") | |||
|
53 | assert_equal '<p>Foo: 2 (arg1,arg2) (Array)</p>', textilizable("{{foo(arg1, arg2)}}") | |||
|
54 | end | |||
|
55 | ||||
|
56 | def test_macro_registration_parse_args_set_to_false_should_disable_arguments_parsing | |||
|
57 | Redmine::WikiFormatting::Macros.register do | |||
|
58 | macro :bar, :parse_args => false do |obj, args| | |||
|
59 | "Bar: (#{args}) (#{args.class.name})" | |||
|
60 | end | |||
|
61 | end | |||
|
62 | ||||
|
63 | assert_equal '<p>Bar: (args, more args) (String)</p>', textilizable("{{bar(args, more args)}}") | |||
|
64 | assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar}}") | |||
|
65 | assert_equal '<p>Bar: () (String)</p>', textilizable("{{bar()}}") | |||
52 | end |
|
66 | end | |
53 |
|
67 | |||
54 | def test_macro_hello_world |
|
68 | def test_macro_hello_world |
General Comments 0
You need to be logged in to leave comments.
Login now