radius 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/QUICKSTART +55 -17
- data/README +5 -3
- data/ROADMAP +1 -1
- data/Rakefile +15 -10
- data/lib/radius.rb +37 -6
- data/test/radius_test.rb +102 -55
- metadata +13 -10
data/CHANGELOG
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
= Change Log
|
2
|
+
|
3
|
+
=== 0.0.2
|
4
|
+
* Refactored Parser to use Context#render_tag instead of #send when rendering tags defined on a Context.
|
5
|
+
* UndefinedTagError is now thrown when Parser tries to render a tag which doesn't exist on a Context.
|
6
|
+
* Added Context#tag_missing which works like method_method missing on Object, but is tag specific.
|
7
|
+
|
8
|
+
=== 0.0.1
|
9
|
+
* First release.
|
data/QUICKSTART
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
=Quick Start
|
1
|
+
= Radius Quick Start
|
2
|
+
|
3
|
+
== Defining Tags
|
2
4
|
|
3
5
|
Before you can parse a template with Radius you need to create a Context object which defines
|
4
6
|
the tags that will be used in the template. This is pretty simple:
|
@@ -17,16 +19,19 @@ Once you have defined a context you can create a Parser and parse to your heart'
|
|
17
19
|
puts parser.parse('<p><radius:hello /></p>')
|
18
20
|
puts parser.parse('<p><radius:hello name="John" /></p>')
|
19
21
|
|
20
|
-
This will output:
|
22
|
+
This code will output:
|
21
23
|
|
22
24
|
<p>Hello World!</p>
|
23
25
|
<p>Hello John!</p>
|
24
26
|
|
25
27
|
Note how you can pass attributes from the template to the context using the attributes hash
|
26
|
-
(which is passed in as the first parameter
|
28
|
+
(which is passed in as the first parameter). Above, the first tag that was parsed didn't have
|
27
29
|
a name attribute so the code in the +hello+ method uses "World" instead. The second time the
|
28
30
|
tag is parsed the name attribute is set to "John" which is used to create the string "Hello
|
29
|
-
John!".
|
31
|
+
John!". <b>All tag definitions must accept only one parameter--the attributes hash.</b> Tags
|
32
|
+
that do not follow this rule will be treated as if they were undefined (like normal methods).
|
33
|
+
|
34
|
+
== Container Tags
|
30
35
|
|
31
36
|
Radius also allows you to define "container" tags. That is, tags that contain content and
|
32
37
|
that may optionally manipulate it in some way. For example, if you have RedCloth installed
|
@@ -45,17 +50,14 @@ With the code above your parser can easily handle Textile:
|
|
45
50
|
|
46
51
|
parser.parse('<radius:textile>h1. Hello **World**!</radius:textile>')
|
47
52
|
|
48
|
-
This will output:
|
53
|
+
This code will output:
|
49
54
|
|
50
55
|
<h1>Hello <strong>World</strong>!</h1>
|
51
56
|
|
52
|
-
But wait!--it gets better. Because container tags can manipulate
|
53
|
-
them to iterate over collections:
|
57
|
+
But wait!--it gets better. Because container tags can manipulate the content they contain
|
58
|
+
you can use them to iterate over collections:
|
54
59
|
|
55
60
|
class ThreeStoogesContext < Radius::Context
|
56
|
-
def initialize
|
57
|
-
@prefix = 'ts'
|
58
|
-
end
|
59
61
|
def stooge(attr)
|
60
62
|
content = ''
|
61
63
|
["Larry", "Moe", "Curly"].each do |name|
|
@@ -73,15 +75,15 @@ them to iterate over collections:
|
|
73
75
|
|
74
76
|
template = <<-TEMPLATE
|
75
77
|
<ul>
|
76
|
-
<
|
77
|
-
|
78
|
-
</
|
78
|
+
<radius:stooge>
|
79
|
+
<li><radius:name /></li>
|
80
|
+
</radius:stooge>
|
79
81
|
</ul>
|
80
82
|
TEMPLATE
|
81
83
|
|
82
84
|
puts parser.parse(template)
|
83
85
|
|
84
|
-
This will output:
|
86
|
+
This code will output:
|
85
87
|
|
86
88
|
<ul>
|
87
89
|
|
@@ -93,6 +95,42 @@ This will output:
|
|
93
95
|
|
94
96
|
</ul>
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
98
|
+
|
99
|
+
== Altering the Tag Prefix
|
100
|
+
|
101
|
+
By default, all Radius tags must begin with "radius". You can change this by altering the
|
102
|
+
prefix attribute on a Context. For example:
|
103
|
+
|
104
|
+
class MyContext < Radius::Context
|
105
|
+
def initialize
|
106
|
+
@prefix = 'r'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Now, when parsing templates with MyContext, Radius will require that tags begin with "r".
|
111
|
+
|
112
|
+
|
113
|
+
== Using Context#tag_missing to Define Behavior for Missing Tags
|
114
|
+
|
115
|
+
Context#tag_missing behaves much like Object#method_missing only it allows you to define
|
116
|
+
specific behavior for when a tag is not defined on a Context. For example:
|
117
|
+
|
118
|
+
class LazyContext < Radius::Context
|
119
|
+
def initialize
|
120
|
+
@prefix = 'lazy'
|
121
|
+
end
|
122
|
+
def tag_missing(tag, attr, &block)
|
123
|
+
"<strong>ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect}</strong>"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
parser = Radius::Parser.new(LazyContext.new)
|
128
|
+
puts parser.parse('<lazy:weird value="true" />')
|
129
|
+
|
130
|
+
This code will output:
|
131
|
+
|
132
|
+
<strong>ERROR: Undefined tag `weird' with attributes {"value"=>"true"}</strong>
|
133
|
+
|
134
|
+
Normally, when the Radius Parser encounters an undefined tag for a Context it raises an
|
135
|
+
UndefinedTagError, but since we have defined #tag_missing on LazyContext the Parser now
|
136
|
+
outputs our custom message.
|
data/README
CHANGED
@@ -19,7 +19,7 @@ It is recommended that you install Radius using the RubyGems packaging system:
|
|
19
19
|
|
20
20
|
% gem install --remote radius
|
21
21
|
|
22
|
-
You can also install Radius by copying radius.rb into the Ruby load path.
|
22
|
+
You can also install Radius by copying lib/radius.rb into the Ruby load path.
|
23
23
|
|
24
24
|
== License
|
25
25
|
|
@@ -44,6 +44,8 @@ link:files/ROADMAP.html
|
|
44
44
|
If you are a smart developer with a passion for excellence, now is the time to jump on board.
|
45
45
|
Contact me and we'll talk. :)
|
46
46
|
|
47
|
-
|
47
|
+
Enjoy!
|
48
48
|
|
49
|
-
|
49
|
+
--
|
50
|
+
John Long ::
|
51
|
+
http://wiseheartdesign.com
|
data/ROADMAP
CHANGED
@@ -13,6 +13,6 @@ This is a prioritized roadmap for future releases:
|
|
13
13
|
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/127640/
|
14
14
|
|
15
15
|
Update: See link:files/DSL-SPEC.html for a fuller explanation of how
|
16
|
-
the DSL
|
16
|
+
the DSL might behave.
|
17
17
|
|
18
18
|
4. Optimize for speed. Incorporate strscan?
|
data/Rakefile
CHANGED
@@ -9,30 +9,35 @@ Rake::TestTask.new do |t|
|
|
9
9
|
t.pattern = 'test/**/*_test.rb'
|
10
10
|
end
|
11
11
|
|
12
|
+
RDOC_TITLE = "Radius -- Powerful Tag-Based Templates"
|
13
|
+
RDOC_EXTRAS = ["README", "QUICKSTART", "ROADMAP", "DSL-SPEC", "CHANGELOG"]
|
14
|
+
|
12
15
|
Rake::RDocTask.new do |rd|
|
13
16
|
rd.title = 'Radius -- Powerful Tag-Based Templates'
|
14
|
-
rd.main = "
|
15
|
-
rd.rdoc_files.include("
|
17
|
+
rd.main = "Radius"
|
18
|
+
rd.rdoc_files.include("lib/**/*.rb")
|
19
|
+
rd.rdoc_files.include(RDOC_EXTRAS)
|
16
20
|
rd.rdoc_dir = 'doc'
|
17
21
|
end
|
18
22
|
|
19
23
|
spec = Gem::Specification.new do |s|
|
20
|
-
s.platform = Gem::Platform::RUBY
|
21
|
-
s.summary = "Powerful tag-based template system."
|
22
24
|
s.name = 'radius'
|
23
|
-
s.
|
25
|
+
s.rubyforge_project = 'radius'
|
26
|
+
s.version = '0.0.2'
|
27
|
+
s.summary = 'Powerful tag-based template system.'
|
28
|
+
s.description = "Radius is a small, but powerful tag-based template language for Ruby\nsimilar to the ones used in MovableType and TextPattern. It has tags\nsimilar to HTML or XML, but can be used to generate any form of plain\ntext (not just HTML)."
|
29
|
+
s.homepage = 'http://radius.rubyforge.org'
|
30
|
+
s.platform = Gem::Platform::RUBY
|
24
31
|
s.requirements << 'none'
|
25
32
|
s.require_path = 'lib'
|
26
33
|
s.autorequire = 'radius'
|
27
34
|
s.has_rdoc = true
|
28
|
-
s.rdoc_options << '--title' << '
|
29
|
-
|
30
|
-
'--line-numbers' << 'ROADMAP' <<
|
31
|
-
'QUICKSTART' << 'DSL-SPEC' << 'README'
|
35
|
+
s.rdoc_options << '--title' << RDOC_TITLE << '--line-numbers' << '--main' << 'README'
|
36
|
+
s.extra_rdoc_files = RDOC_EXTRAS
|
32
37
|
files = FileList['**/*']
|
33
38
|
files.exclude 'doc'
|
39
|
+
files.exclude '**/._*'
|
34
40
|
s.files = files.to_a
|
35
|
-
s.description = "Radius is a small, but powerful tag-based template language inspired\nby the template languages used in MovableType and TextPattern."
|
36
41
|
end
|
37
42
|
|
38
43
|
Rake::GemPackageTask.new(spec) do |pkg|
|
data/lib/radius.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
1
|
module Radius
|
2
|
-
class
|
2
|
+
# Abstract base class for all parsing errors.
|
3
|
+
class ParseError < StandardError
|
3
4
|
end
|
4
5
|
|
5
|
-
|
6
|
+
# Occurs when Parser cannot find an end tag for a given tag in a template or when
|
7
|
+
# tags are miss-matched in a template.
|
8
|
+
class MissingEndTagError < ParseError
|
9
|
+
# Create a new MissingEndTagError object for +tag_name+.
|
6
10
|
def initialize(tag_name)
|
7
11
|
super("end tag not found for start tag `#{tag_name}'")
|
8
12
|
end
|
9
13
|
end
|
10
14
|
|
15
|
+
# Occurs when Context#render_tag cannot find the specified tag on a Context.
|
16
|
+
class UndefinedTagError < ParseError
|
17
|
+
# Create a new MissingEndTagError object for +tag_name+.
|
18
|
+
def initialize(tag_name)
|
19
|
+
super("undefined tag `#{tag_name}'")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
11
23
|
#
|
12
24
|
# An abstract class for creating a Context. A context defines the tags that
|
13
25
|
# are available for use in a template.
|
@@ -15,22 +27,41 @@ module Radius
|
|
15
27
|
class Context
|
16
28
|
# The prefix attribute controls the string of text that is helps the parser
|
17
29
|
# identify template tags. By default this attribute is set to "radius", but
|
18
|
-
# you may want to override this
|
30
|
+
# you may want to override this when creating your own contexts.
|
19
31
|
attr_accessor :prefix
|
20
32
|
|
21
33
|
# Creates a new Context object.
|
22
34
|
def initialize
|
23
35
|
@prefix = 'radius'
|
24
36
|
end
|
37
|
+
|
38
|
+
# Returns the value of a rendered tag. Used internally by Parser#parse.
|
39
|
+
def render_tag(tag, attributes = {}, &block)
|
40
|
+
symbol = tag.to_s.intern
|
41
|
+
if respond_to?(symbol) and method(symbol).arity == 1
|
42
|
+
send(symbol, attributes, &block)
|
43
|
+
else
|
44
|
+
tag_missing(tag, attributes, &block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Like method_missing for objects, but fired when a tag is undefined.
|
49
|
+
# Override in your own Context to change what happens when a tag is
|
50
|
+
# undefined. By default this method raises an UndefinedTagError.
|
51
|
+
def tag_missing(tag, attributes, &block)
|
52
|
+
raise UndefinedTagError.new(tag)
|
53
|
+
end
|
25
54
|
end
|
26
55
|
|
27
56
|
class Tag # :nodoc:
|
28
57
|
def initialize(&b)
|
29
58
|
@block = b
|
30
59
|
end
|
60
|
+
|
31
61
|
def on_parse(&b)
|
32
62
|
@block = b
|
33
63
|
end
|
64
|
+
|
34
65
|
def to_s
|
35
66
|
@block.call(self)
|
36
67
|
end
|
@@ -53,7 +84,7 @@ module Radius
|
|
53
84
|
# The Context object used to expand template tags.
|
54
85
|
attr_accessor :context
|
55
86
|
|
56
|
-
# Creates a new parser object initialized with a
|
87
|
+
# Creates a new parser object initialized with a Context.
|
57
88
|
def initialize(context = Context.new)
|
58
89
|
@context = context
|
59
90
|
end
|
@@ -93,7 +124,7 @@ module Radius
|
|
93
124
|
def parse_end_tag(end_tag, remaining) # :nodoc:
|
94
125
|
popped = @stack.pop
|
95
126
|
if popped.name == end_tag
|
96
|
-
popped.on_parse { |t| @context.
|
127
|
+
popped.on_parse { |t| @context.render_tag(popped.name, popped.attributes) { t.contents.to_s } }
|
97
128
|
tag = @stack.last
|
98
129
|
tag.contents << popped
|
99
130
|
pre_parse(remaining)
|
@@ -106,7 +137,7 @@ module Radius
|
|
106
137
|
re = /<#{@context.prefix}:(\w+?)\s+?(.*?)\s*?\/>/
|
107
138
|
if md = re.match(text)
|
108
139
|
attr = parse_attributes($2)
|
109
|
-
replace = @context.
|
140
|
+
replace = @context.render_tag($1, attr)
|
110
141
|
md.pre_match + replace + parse_individual(md.post_match)
|
111
142
|
else
|
112
143
|
text || ''
|
data/test/radius_test.rb
CHANGED
@@ -1,68 +1,115 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'radius'
|
3
3
|
|
4
|
-
class
|
5
|
-
def
|
6
|
-
|
7
|
-
|
4
|
+
class TestContext < Radius::Context
|
5
|
+
def initialize
|
6
|
+
@prefix = "test"
|
7
|
+
@items = ["Larry", "Moe", "Curly"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def echo(attr)
|
11
|
+
attr["text"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(attr)
|
15
|
+
(attr["param1"].to_i + attr["param2"].to_i).to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def reverse(attr)
|
19
|
+
yield.reverse
|
20
|
+
end
|
21
|
+
|
22
|
+
def capitalize(attr)
|
23
|
+
yield.upcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def count(attr)
|
27
|
+
case
|
28
|
+
when attr["set"]
|
29
|
+
@count = attr["set"].to_i
|
30
|
+
""
|
31
|
+
when attr["inc"] == "true"
|
32
|
+
@count = (@count || 0) + 1
|
33
|
+
""
|
34
|
+
else
|
35
|
+
@count.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def repeat(attr)
|
40
|
+
string = ''
|
41
|
+
(attr['count'] || '1').to_i.times { string << yield }
|
42
|
+
string
|
43
|
+
end
|
44
|
+
|
45
|
+
def each_item(attr)
|
46
|
+
result = []
|
47
|
+
@items.each { |@item| result << yield }
|
48
|
+
@item = nil
|
49
|
+
result.join(attr["between"] || "")
|
50
|
+
end
|
51
|
+
|
52
|
+
def item(attr)
|
53
|
+
@item
|
54
|
+
end
|
55
|
+
|
56
|
+
def hello(attr)
|
57
|
+
"Hello #{attr['name'] || 'World'}!"
|
8
58
|
end
|
9
59
|
end
|
10
60
|
|
11
|
-
class
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
else
|
43
|
-
@count.to_s
|
61
|
+
class RadiusContextTest < Test::Unit::TestCase
|
62
|
+
def setup
|
63
|
+
@context = TestContext.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_initialize
|
67
|
+
@context = Radius::Context.new
|
68
|
+
assert_equal 'radius', @context.prefix
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_render_tag__individual
|
72
|
+
text = @context.render_tag('hello')
|
73
|
+
assert_equal('Hello World!', text)
|
74
|
+
|
75
|
+
text = @context.render_tag('hello', 'name' => 'John')
|
76
|
+
assert_equal('Hello John!', text)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_render_tag__container
|
80
|
+
text = @context.render_tag('repeat', 'count' => '5') { 'o' }
|
81
|
+
assert_equal('ooooo', text)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_render_tag__undefined_tag
|
85
|
+
e = assert_raises(Radius::UndefinedTagError) { @context.render_tag('undefined_tag') }
|
86
|
+
assert_equal "undefined tag `undefined_tag'", e.message
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_render_tag__undefined_tag_with_wrong_signature
|
90
|
+
class << @context
|
91
|
+
def helper_method
|
44
92
|
end
|
45
93
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
94
|
+
assert_raises(Radius::UndefinedTagError) { @context.render_tag('helper_method') }
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_tag_missing
|
98
|
+
class << @context
|
99
|
+
def tag_missing(tag, attr, &block)
|
100
|
+
"undefined tag `#{tag}' with attributes #{attr.inspect}"
|
101
|
+
end
|
52
102
|
end
|
53
103
|
|
54
|
-
|
55
|
-
|
56
|
-
@items.each { |@item| result << yield }
|
57
|
-
@item = nil
|
58
|
-
result.join(attr["between"] || "")
|
59
|
-
end
|
104
|
+
text = ''
|
105
|
+
assert_nothing_raised { text = @context.render_tag('undefined_tag', 'cool' => 'beans') }
|
60
106
|
|
61
|
-
|
62
|
-
|
63
|
-
end
|
107
|
+
expected = %{undefined tag `undefined_tag' with attributes {"cool"=>"beans"}}
|
108
|
+
assert_equal expected, text
|
64
109
|
end
|
65
|
-
|
110
|
+
end
|
111
|
+
|
112
|
+
class RadiusParserTest < Test::Unit::TestCase
|
66
113
|
def setup
|
67
114
|
@t = Radius::Parser.new(TestContext.new )
|
68
115
|
end
|
@@ -105,8 +152,8 @@ class RadiusTest < Test::Unit::TestCase
|
|
105
152
|
r = @t.parse("<test:reverse>12<test:capitalize>at</test:capitalize>34</test:reverse>")
|
106
153
|
assert_equal("43TA21", r)
|
107
154
|
end
|
108
|
-
def
|
109
|
-
r = @t.parse(%{<test:count set="0" /><test:
|
155
|
+
def test_parse__looping
|
156
|
+
r = @t.parse(%{<test:count set="0" /><test:repeat count="5"><test:count inc="true" /><test:count /></test:repeat>})
|
110
157
|
assert_equal("12345", r)
|
111
158
|
|
112
159
|
r = @t.parse(%{Three Stooges: <test:each_item between=", ">"<test:item />"</test:each_item>})
|
metadata
CHANGED
@@ -3,16 +3,17 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: radius
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2006-02-14
|
8
8
|
summary: Powerful tag-based template system.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email:
|
12
|
-
homepage:
|
13
|
-
rubyforge_project:
|
14
|
-
description: "Radius is a small, but powerful tag-based template language
|
15
|
-
|
12
|
+
homepage: http://radius.rubyforge.org
|
13
|
+
rubyforge_project: radius
|
14
|
+
description: "Radius is a small, but powerful tag-based template language for Ruby similar to
|
15
|
+
the ones used in MovableType and TextPattern. It has tags similar to HTML or
|
16
|
+
XML, but can be used to generate any form of plain text (not just HTML)."
|
16
17
|
autorequire: radius
|
17
18
|
default_executable:
|
18
19
|
bindir: bin
|
@@ -27,6 +28,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
27
28
|
platform: ruby
|
28
29
|
authors: []
|
29
30
|
files:
|
31
|
+
- CHANGELOG
|
30
32
|
- DSL-SPEC
|
31
33
|
- lib
|
32
34
|
- QUICKSTART
|
@@ -40,14 +42,15 @@ test_files: []
|
|
40
42
|
rdoc_options:
|
41
43
|
- "--title"
|
42
44
|
- "Radius -- Powerful Tag-Based Templates"
|
45
|
+
- "--line-numbers"
|
43
46
|
- "--main"
|
44
47
|
- README
|
45
|
-
|
46
|
-
-
|
48
|
+
extra_rdoc_files:
|
49
|
+
- README
|
47
50
|
- QUICKSTART
|
51
|
+
- ROADMAP
|
48
52
|
- DSL-SPEC
|
49
|
-
-
|
50
|
-
extra_rdoc_files: []
|
53
|
+
- CHANGELOG
|
51
54
|
executables: []
|
52
55
|
extensions: []
|
53
56
|
requirements:
|