1 module Radiant::Taggable
2 mattr_accessor :last_description, :tag_descriptions, :tag_deprecations
3 @@tag_descriptions = {}
4 @@tag_deprecations = {}
5
6 def self.included(base)
7 base.extend(ClassMethods)
8 base.module_eval do
9 def self.included(new_base)
10 super
11 new_base.class_eval do
12 include ActionController::UrlWriter
13 end
14 class << new_base
15 def default_url_options
16 {:controller => "site", :action => "show_page", :only_path => true}
17 end
18 end
19 new_base.tag_descriptions.merge! self.tag_descriptions
20 end
21
22 protected
23 def params
24 @params ||= request.parameters unless request.nil?
25 end
26
27 def request_uri
28 @request_url ||= request.request_uri unless request.nil?
29 end
30 end
31 end
32
33 def render_tag(name, tag_binding)
34 tag_method_name = "tag:#{name}"
35 tag_method = method(tag_method_name)
36 if tag_method.arity == 0
37 tag_method.call
38 else
39 tag_method.call tag_binding
40 end
41 end
42
43 def tags
44 Util.tags_in_array(methods)
45 end
46
47 def tag_descriptions(hash=nil)
48 self.class.tag_descriptions hash
49 end
50
51 def warn_of_tag_deprecation(tag_name, options={})
52 message = "Deprecated radius tag <r:#{tag_name}>"
53 message << " will be removed or significantly changed in radiant #{options[:deadline]}." if options[:deadline]
54 message << " Please use <r:#{options[:substitute]}> instead." if options[:substitute]
55 ActiveSupport::Deprecation.warn(message)
56 end
57
58 module ClassMethods
59 def inherited(subclass)
60 subclass.tag_descriptions.reverse_merge! self.tag_descriptions
61 super
62 end
63
64 def tag_descriptions(hash = nil)
65 Radiant::Taggable.tag_descriptions[self.name] ||= (hash ||{})
66 end
67
68 def desc(text)
69 Radiant::Taggable.last_description = text
70 # Radiant::Taggable.last_description = RedCloth.new(Util.strip_leading_whitespace(text)).to_html
71 end
72
73 def tag(name, &block)
74 self.tag_descriptions[name] = Radiant::Taggable.last_description if Radiant::Taggable.last_description
75 Radiant::Taggable.last_description = nil
76 define_method("tag:#{name}", &block)
77 end
78
79 def tags
80 Util.tags_in_array(self.instance_methods)
81 end
82
83 # Define a tag while also deprecating it. Normal usage:
84 #
85 # deprecated_tag 'old:way', :substitute => 'new:way', :deadline => '1.1.1'
86 #
87 # If no substitute is given then a warning will be issued but nothing rendered.
88 # If a deadline version is provided then it will be mentioned in the deprecation warnings.
89 #
90 # In less standard situations you can use deprecated_tag in exactly the
91 # same way as tags are normally defined:
92 #
93 # desc %{
94 # Please note that the old r:busted namespace is no longer supported.
95 # Refer to the documentation for more about the new r:hotness tags.
96 # }
97 # deprecated_tag 'busted' do |tag|
98 # raise TagError "..."
99 # end
100 #
101 def deprecated_tag(name, options={}, &dblock)
102 Radiant::Taggable.tag_deprecations[name] = options.dup
103 if dblock
104 tag(name) do |tag|
105 warn_of_tag_deprecation(name, options)
106 dblock.call(tag)
107 end
108 else
109 tag(name) do |tag|
110 warn_of_tag_deprecation(name, options)
111 tag.render(options[:substitute], tag.attr.dup, &tag.block) if options[:substitute]
112 end
113 end
114 end
115 end
116
117 module Util
118 def self.tags_in_array(array)
119 array.grep(/^tag:/).map { |name| name[4..-1] }.sort
120 end
121
122 def self.strip_leading_whitespace(text)
123 text = text.dup
124 text.gsub!("\t", " ")
125 lines = text.split("\n")
126 leading = lines.map do |line|
127 unless line =~ /^\s*$/
128 line.match(/^(\s*)/)[0].length
129 else
130 nil
131 end
132 end.compact.min
133 lines.inject([]) {|ary, line| ary << line.sub(/^[ ]{#{leading}}/, "")}.join("\n")
134 end
135
136 end
137
138 en