radius 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/DSL-SPEC +151 -0
- data/QUICKSTART +98 -0
- data/README +49 -0
- data/ROADMAP +18 -0
- data/Rakefile +41 -0
- data/lib/radius.rb +126 -0
- data/test/radius_test.rb +122 -0
- metadata +55 -0
data/DSL-SPEC
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
=DSL Specification
|
2
|
+
|
3
|
+
I haven't implemented a domain specific languages for Contexts yet, but I am hoping to
|
4
|
+
do so in a future release. Below are some thoughts (in code) for how I would like it to
|
5
|
+
work. Note that with a robust DSL you will be able to define that certain tags are
|
6
|
+
only valid within certain containing tags.
|
7
|
+
|
8
|
+
class StoreContext < Radius::Context
|
9
|
+
def initialize(options)
|
10
|
+
@prefix = "r" # all tags must be prefixed with "r"
|
11
|
+
@user = options[:user]
|
12
|
+
@cart = options[:cart]
|
13
|
+
@session = options[:session]
|
14
|
+
end
|
15
|
+
|
16
|
+
# expose the @user object variable
|
17
|
+
container(:user, :exposes => :user) do
|
18
|
+
# Use protect() on an object that has been exposed to prevent access to
|
19
|
+
# an attribute in a template. Conversely you could use the expose() method
|
20
|
+
# to expose specific attributes to the template and protect all others.
|
21
|
+
protect :password
|
22
|
+
|
23
|
+
# add a single tag that returns the session_id
|
24
|
+
tag :session_id do |attributes|
|
25
|
+
@session.id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# expose the @cart object as the basket tag
|
30
|
+
container(:basket, :exposes => :cart) do
|
31
|
+
expand do |attributes|
|
32
|
+
#
|
33
|
+
# some initialization code with attributes before handling
|
34
|
+
# content block
|
35
|
+
#
|
36
|
+
yeild
|
37
|
+
end
|
38
|
+
|
39
|
+
container(:items, :exposes => :item) do
|
40
|
+
expand do |attributes|
|
41
|
+
@cart.items.each do |@item|
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
container :cart do
|
49
|
+
expand do |attributes|
|
50
|
+
yield
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class User
|
56
|
+
attr_accessor :name, :login, :password, :email
|
57
|
+
def initialize(name, login, password, email)
|
58
|
+
@name, @login, @password, @email = name, login, password, email
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Session
|
63
|
+
attr_accessor :id
|
64
|
+
def initialize(id)
|
65
|
+
@id = id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Cart
|
70
|
+
attr_accessor :items
|
71
|
+
|
72
|
+
def initialize(*items)
|
73
|
+
@items = [items].flatten
|
74
|
+
end
|
75
|
+
|
76
|
+
def total
|
77
|
+
@items.map { |line_item| line_item.total }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class LineItem
|
82
|
+
attr_accessor :name, :description, :quantity, :item_price
|
83
|
+
def intialize(name, description, price, quantity)
|
84
|
+
@name, @description, @price, @quantity = name, description, price, quantity
|
85
|
+
end
|
86
|
+
def full_price
|
87
|
+
@price * @quantity
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
receipt = <<-RECEIPT
|
92
|
+
<p><r:user:name />, thank you for shopping with us! An order summary
|
93
|
+
is printed below for your convinience. Please print a copy for your records.</p>
|
94
|
+
<r:cart>
|
95
|
+
<table>
|
96
|
+
<thead>
|
97
|
+
<tr>
|
98
|
+
<td>Product</td>
|
99
|
+
<td>Price</td>
|
100
|
+
<td>Quantity</td>
|
101
|
+
<td>Totals</td>
|
102
|
+
</tr>
|
103
|
+
</thead>
|
104
|
+
<tbody>
|
105
|
+
<r:items>
|
106
|
+
<tr>
|
107
|
+
<td>
|
108
|
+
<strong><r:name /></strong><br >
|
109
|
+
<r:description />
|
110
|
+
</td>
|
111
|
+
<td><r:price /></td>
|
112
|
+
<td><r:quanity /></td>
|
113
|
+
<td><r:full_price /></td>
|
114
|
+
</tr>
|
115
|
+
</r:items>
|
116
|
+
</tbody>
|
117
|
+
<tr>
|
118
|
+
<td colspan="3">Total</td>
|
119
|
+
<td><r:total /></td>
|
120
|
+
</tr>
|
121
|
+
</table>
|
122
|
+
</r:cart>
|
123
|
+
RECEIPT
|
124
|
+
|
125
|
+
user = User.new('John', 'johnboy', 'm@x!mu5', 'johnboy@maximus.com')
|
126
|
+
cart = Cart.new(
|
127
|
+
LineItem.new('15in PowerBook', "Apple's premium notebook computer.", 1995.98, 1),
|
128
|
+
LineItem.new('Mac Notebook Case', "A beautiful black notebook case designed for Apple Powerbooks.", 54.05, 1)
|
129
|
+
)
|
130
|
+
session = Session.new('a4bd386e512bacd581')
|
131
|
+
|
132
|
+
context = StoreContext.new(
|
133
|
+
:user => user,
|
134
|
+
:cart => cart,
|
135
|
+
:session => session
|
136
|
+
)
|
137
|
+
|
138
|
+
template = Radius::Template.new(
|
139
|
+
:text => receipt,
|
140
|
+
:context => context
|
141
|
+
)
|
142
|
+
|
143
|
+
template.compile! # based on context parses text into abstract syntax tree
|
144
|
+
puts template.expand # outputs expanded template
|
145
|
+
|
146
|
+
# alternate usage
|
147
|
+
parser = Radius::Parser.new(context)
|
148
|
+
puts parser.parse(receipt) # compiles and outputs expanded template
|
149
|
+
|
150
|
+
# another alternate
|
151
|
+
puts Radius.parse(receipt, context)
|
data/QUICKSTART
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
=Quick Start
|
2
|
+
|
3
|
+
Before you can parse a template with Radius you need to create a Context object which defines
|
4
|
+
the tags that will be used in the template. This is pretty simple:
|
5
|
+
|
6
|
+
require 'radius'
|
7
|
+
|
8
|
+
class MyContext < Radius::Context
|
9
|
+
def hello(attr)
|
10
|
+
"Hello #{attr['name'] || 'World'}!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Once you have defined a context you can create a Parser and parse to your heart's content:
|
15
|
+
|
16
|
+
parser = Radius::Parser.new(MyContext.new)
|
17
|
+
puts parser.parse('<p><radius:hello /></p>')
|
18
|
+
puts parser.parse('<p><radius:hello name="John" /></p>')
|
19
|
+
|
20
|
+
This will output:
|
21
|
+
|
22
|
+
<p>Hello World!</p>
|
23
|
+
<p>Hello John!</p>
|
24
|
+
|
25
|
+
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.). Above the first tag that was parsed didn't have
|
27
|
+
a name attribute so the code in the +hello+ method uses "World" instead. The second time the
|
28
|
+
tag is parsed the name attribute is set to "John" which is used to create the string "Hello
|
29
|
+
John!".
|
30
|
+
|
31
|
+
Radius also allows you to define "container" tags. That is, tags that contain content and
|
32
|
+
that may optionally manipulate it in some way. For example, if you have RedCloth installed
|
33
|
+
you could define another tag to parse and create Textile output:
|
34
|
+
|
35
|
+
require 'redcloth'
|
36
|
+
|
37
|
+
class MyContext < Radius::Context
|
38
|
+
def textile(attr)
|
39
|
+
contents = yield
|
40
|
+
RedCloth.new(contents).to_html
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
With the code above your parser can easily handle Textile:
|
45
|
+
|
46
|
+
parser.parse('<radius:textile>h1. Hello **World**!</radius:textile>')
|
47
|
+
|
48
|
+
This will output:
|
49
|
+
|
50
|
+
<h1>Hello <strong>World</strong>!</h1>
|
51
|
+
|
52
|
+
But wait!--it gets better. Because container tags can manipulate what they contain you can use
|
53
|
+
them to iterate over collections:
|
54
|
+
|
55
|
+
class ThreeStoogesContext < Radius::Context
|
56
|
+
def initialize
|
57
|
+
@prefix = 'ts'
|
58
|
+
end
|
59
|
+
def stooge(attr)
|
60
|
+
content = ''
|
61
|
+
["Larry", "Moe", "Curly"].each do |name|
|
62
|
+
@name = name
|
63
|
+
content << yield
|
64
|
+
end
|
65
|
+
content
|
66
|
+
end
|
67
|
+
def name(attr)
|
68
|
+
@name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
parser = Radius::Parser.new(ThreeStoogesContext.new)
|
73
|
+
|
74
|
+
template = <<-TEMPLATE
|
75
|
+
<ul>
|
76
|
+
<ts:stooge>
|
77
|
+
<li><ts:name /></li>
|
78
|
+
</ts:stooge>
|
79
|
+
</ul>
|
80
|
+
TEMPLATE
|
81
|
+
|
82
|
+
puts parser.parse(template)
|
83
|
+
|
84
|
+
This will output:
|
85
|
+
|
86
|
+
<ul>
|
87
|
+
|
88
|
+
<li>Larry</li>
|
89
|
+
|
90
|
+
<li>Moe</li>
|
91
|
+
|
92
|
+
<li>Curly</li>
|
93
|
+
|
94
|
+
</ul>
|
95
|
+
|
96
|
+
The above code also illustrates how you can set the prefix instance variable to control the
|
97
|
+
string that prefixes Radius tags. By setting the prefix to "ts" our tags must begin with "ts"
|
98
|
+
instead of "radius" like they did in the other examples.
|
data/README
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= Radius -- Powerful Tag-Based Templates
|
2
|
+
|
3
|
+
Radius is a small, but powerful template language for Ruby inspired by the template languages
|
4
|
+
used in MovableType <http://www.movabletype.org> and TextPattern <http://www.textpattern.com>.
|
5
|
+
It uses tags similar to HTML, but can be used to generate any form of plain text (XML, e-mail,
|
6
|
+
etc...).
|
7
|
+
|
8
|
+
|
9
|
+
== Download
|
10
|
+
|
11
|
+
The latest version of Radius can be found on RubyForge:
|
12
|
+
|
13
|
+
http://rubyforge.org/projects/radius/
|
14
|
+
|
15
|
+
|
16
|
+
== Installation
|
17
|
+
|
18
|
+
It is recommended that you install Radius using the RubyGems packaging system:
|
19
|
+
|
20
|
+
% gem install --remote radius
|
21
|
+
|
22
|
+
You can also install Radius by copying radius.rb into the Ruby load path.
|
23
|
+
|
24
|
+
== License
|
25
|
+
|
26
|
+
Radius is free software and may be redistributed under the same terms and Ruby itself. For
|
27
|
+
more details, see the readme file in the Ruby distribution.
|
28
|
+
|
29
|
+
|
30
|
+
== Quick Start
|
31
|
+
|
32
|
+
To get up and running fast with Radius read:
|
33
|
+
|
34
|
+
link:files/QUICKSTART.html
|
35
|
+
|
36
|
+
|
37
|
+
== A Call to Action
|
38
|
+
|
39
|
+
Radius is still very much in the development stages. Take a look at the roadmap to see where
|
40
|
+
we want to go:
|
41
|
+
|
42
|
+
link:files/ROADMAP.html
|
43
|
+
|
44
|
+
If you are a smart developer with a passion for excellence, now is the time to jump on board.
|
45
|
+
Contact me and we'll talk. :)
|
46
|
+
|
47
|
+
--
|
48
|
+
|
49
|
+
John Long :: http://wiseheartdesign.com
|
data/ROADMAP
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= Roadmap
|
2
|
+
|
3
|
+
This is a prioritized roadmap for future releases:
|
4
|
+
|
5
|
+
1. Clean up the current code base.
|
6
|
+
|
7
|
+
2. Add support for multi-level contexts: tags should be able to be
|
8
|
+
defined to only be valid within other sets of tags.
|
9
|
+
|
10
|
+
3. Create a simple DSL for defining contexts. I had done some thinking
|
11
|
+
about this in the past. This thread on Ruby-Talk defined part of it:
|
12
|
+
|
13
|
+
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/127640/
|
14
|
+
|
15
|
+
Update: See link:files/DSL-SPEC.html for a fuller explanation of how
|
16
|
+
the DSL should behave.
|
17
|
+
|
18
|
+
4. Optimize for speed. Incorporate strscan?
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.pattern = 'test/**/*_test.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::RDocTask.new do |rd|
|
13
|
+
rd.title = 'Radius -- Powerful Tag-Based Templates'
|
14
|
+
rd.main = "README"
|
15
|
+
rd.rdoc_files.include("README", "QUICKSTART", "ROADMAP", "DSL-SPEC", "lib/**/*.rb")
|
16
|
+
rd.rdoc_dir = 'doc'
|
17
|
+
end
|
18
|
+
|
19
|
+
spec = Gem::Specification.new do |s|
|
20
|
+
s.platform = Gem::Platform::RUBY
|
21
|
+
s.summary = "Powerful tag-based template system."
|
22
|
+
s.name = 'radius'
|
23
|
+
s.version = '0.0.1'
|
24
|
+
s.requirements << 'none'
|
25
|
+
s.require_path = 'lib'
|
26
|
+
s.autorequire = 'radius'
|
27
|
+
s.has_rdoc = true
|
28
|
+
s.rdoc_options << '--title' << 'Radius -- Powerful Tag-Based Templates' <<
|
29
|
+
'--main' << 'README' <<
|
30
|
+
'--line-numbers' << 'ROADMAP' <<
|
31
|
+
'QUICKSTART' << 'DSL-SPEC' << 'README'
|
32
|
+
files = FileList['**/*']
|
33
|
+
files.exclude 'doc'
|
34
|
+
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
|
+
end
|
37
|
+
|
38
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
39
|
+
pkg.need_zip = true
|
40
|
+
pkg.need_tar = true
|
41
|
+
end
|
data/lib/radius.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
module Radius
|
2
|
+
class ParseError < StandardError # :nodoc:
|
3
|
+
end
|
4
|
+
|
5
|
+
class MissingEndTagError < ParseError # :nodoc:
|
6
|
+
def initialize(tag_name)
|
7
|
+
super("end tag not found for start tag `#{tag_name}'")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# An abstract class for creating a Context. A context defines the tags that
|
13
|
+
# are available for use in a template.
|
14
|
+
#
|
15
|
+
class Context
|
16
|
+
# The prefix attribute controls the string of text that is helps the parser
|
17
|
+
# identify template tags. By default this attribute is set to "radius", but
|
18
|
+
# you may want to override this for your own contexts.
|
19
|
+
attr_accessor :prefix
|
20
|
+
|
21
|
+
# Creates a new Context object.
|
22
|
+
def initialize
|
23
|
+
@prefix = 'radius'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Tag # :nodoc:
|
28
|
+
def initialize(&b)
|
29
|
+
@block = b
|
30
|
+
end
|
31
|
+
def on_parse(&b)
|
32
|
+
@block = b
|
33
|
+
end
|
34
|
+
def to_s
|
35
|
+
@block.call(self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ContainerTag < Tag # :nodoc:
|
40
|
+
attr_accessor :name, :attributes, :contents
|
41
|
+
|
42
|
+
def initialize(name="", attributes={}, contents=[], &b)
|
43
|
+
@name, @attributes, @contents = name, attributes, contents
|
44
|
+
super(&b)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# The Radius parser. Initialize a parser with the Context object that defines
|
50
|
+
# how tags should be expanded.
|
51
|
+
#
|
52
|
+
class Parser
|
53
|
+
# The Context object used to expand template tags.
|
54
|
+
attr_accessor :context
|
55
|
+
|
56
|
+
# Creates a new parser object initialized with a context.
|
57
|
+
def initialize(context = Context.new)
|
58
|
+
@context = context
|
59
|
+
end
|
60
|
+
|
61
|
+
# Parse string for tags, expand them, and return the result.
|
62
|
+
def parse(string)
|
63
|
+
@stack = [ContainerTag.new { |t| t.contents.to_s }]
|
64
|
+
pre_parse(string)
|
65
|
+
@stack.last.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def pre_parse(text) # :nodoc:
|
69
|
+
re = %r{<#{@context.prefix}:(\w+?)(?:\s+?([^/>]*?)|)>|</#{@context.prefix}:(\w+?)\s*?>}
|
70
|
+
if md = re.match(text)
|
71
|
+
start_tag, attr, end_tag = $1, $2, $3
|
72
|
+
@stack.last.contents << Tag.new { parse_individual(md.pre_match) }
|
73
|
+
remaining = md.post_match
|
74
|
+
if start_tag
|
75
|
+
parse_start_tag(start_tag, attr, remaining)
|
76
|
+
else
|
77
|
+
parse_end_tag(end_tag, remaining)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
if @stack.length == 1
|
81
|
+
@stack.last.contents << Tag.new { parse_individual(text) }
|
82
|
+
else
|
83
|
+
raise MissingEndTagError.new(@stack.last.name)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_start_tag(start_tag, attr, remaining) # :nodoc:
|
89
|
+
@stack.push(ContainerTag.new(start_tag, parse_attributes(attr)))
|
90
|
+
pre_parse(remaining)
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_end_tag(end_tag, remaining) # :nodoc:
|
94
|
+
popped = @stack.pop
|
95
|
+
if popped.name == end_tag
|
96
|
+
popped.on_parse { |t| @context.send(popped.name, popped.attributes) { t.contents.to_s } }
|
97
|
+
tag = @stack.last
|
98
|
+
tag.contents << popped
|
99
|
+
pre_parse(remaining)
|
100
|
+
else
|
101
|
+
raise MissingEndTagError.new(popped.name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse_individual(text) # :nodoc:
|
106
|
+
re = /<#{@context.prefix}:(\w+?)\s+?(.*?)\s*?\/>/
|
107
|
+
if md = re.match(text)
|
108
|
+
attr = parse_attributes($2)
|
109
|
+
replace = @context.send($1, attr)
|
110
|
+
md.pre_match + replace + parse_individual(md.post_match)
|
111
|
+
else
|
112
|
+
text || ''
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_attributes(text) # :nodoc:
|
117
|
+
attr = {}
|
118
|
+
re = /(\w+?)\s*=\s*('|")(.*?)\2/
|
119
|
+
while md = re.match(text)
|
120
|
+
attr[$1] = $3
|
121
|
+
text = md.post_match
|
122
|
+
end
|
123
|
+
attr
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/test/radius_test.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'radius'
|
3
|
+
|
4
|
+
class ContextTest < Test::Unit::TestCase
|
5
|
+
def test_initialize
|
6
|
+
c = Radius::Context.new
|
7
|
+
assert_equal 'radius', c.prefix
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class RadiusTest < Test::Unit::TestCase
|
12
|
+
class TestContext < Radius::Context
|
13
|
+
def initialize
|
14
|
+
@prefix = "test"
|
15
|
+
@items = ["Larry", "Moe", "Curly"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def echo(attr)
|
19
|
+
attr["text"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(attr)
|
23
|
+
(attr["param1"].to_i + attr["param2"].to_i).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def reverse(attr)
|
27
|
+
yield.reverse
|
28
|
+
end
|
29
|
+
|
30
|
+
def capitalize(attr)
|
31
|
+
yield.upcase
|
32
|
+
end
|
33
|
+
|
34
|
+
def count(attr)
|
35
|
+
case
|
36
|
+
when attr["set"]
|
37
|
+
@count = attr["set"].to_i
|
38
|
+
""
|
39
|
+
when attr["inc"] == "true"
|
40
|
+
@count = (@count || 0) + 1
|
41
|
+
""
|
42
|
+
else
|
43
|
+
@count.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def loop(attr)
|
48
|
+
t = attr["times"].to_i
|
49
|
+
result = ""
|
50
|
+
t.times { result += yield }
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def each_item(attr)
|
55
|
+
result = []
|
56
|
+
@items.each { |@item| result << yield }
|
57
|
+
@item = nil
|
58
|
+
result.join(attr["between"] || "")
|
59
|
+
end
|
60
|
+
|
61
|
+
def item(attr)
|
62
|
+
@item
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup
|
67
|
+
@t = Radius::Parser.new(TestContext.new )
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_parse_individual
|
71
|
+
r = @t.parse_individual(%{<<test:echo text="hello world!" />>})
|
72
|
+
assert_equal("<hello world!>", r)
|
73
|
+
|
74
|
+
r = @t.parse_individual(%{<test:add param1="1" param2='2'/>})
|
75
|
+
assert_equal("3", r)
|
76
|
+
|
77
|
+
r = @t.parse_individual(%{a <test:echo text="3 + 1 =" /> <test:add param1="3" param2="1"/> b})
|
78
|
+
assert_equal("a 3 + 1 = 4 b", r)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_parse_attributes
|
82
|
+
r = @t.parse_attributes(%{ a="1" b='2'c="3"d="'" })
|
83
|
+
assert_equal({"a" => "1", "b" => "2", "c" => "3", "d" => "'"}, r)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_parse
|
87
|
+
r = @t.parse(%{<<test:echo text="hello world!" />>})
|
88
|
+
assert_equal("<hello world!>", r)
|
89
|
+
|
90
|
+
r = @t.parse("<test:reverse>test</test:reverse>")
|
91
|
+
assert_equal("test".reverse, r)
|
92
|
+
|
93
|
+
r = @t.parse("<test:reverse>test</test:reverse> <test:capitalize>test</test:capitalize>")
|
94
|
+
assert_equal("tset TEST", r)
|
95
|
+
|
96
|
+
r = @t.parse("<test:echo text='hello world!' /> cool: <test:reverse>a <test:capitalize>test</test:capitalize> b</test:reverse> !")
|
97
|
+
assert_equal("hello world! cool: b TSET a !", r)
|
98
|
+
|
99
|
+
r = @t.parse("<test:reverse><test:echo text='hello world!' /></test:reverse>")
|
100
|
+
assert_equal("!dlrow olleh", r)
|
101
|
+
|
102
|
+
r = @t.parse("<test:reverse><test:capitalize>test</test:capitalize> <test:echo text='hello world!' /></test:reverse>")
|
103
|
+
assert_equal("!dlrow olleh TSET", r)
|
104
|
+
|
105
|
+
r = @t.parse("<test:reverse>12<test:capitalize>at</test:capitalize>34</test:reverse>")
|
106
|
+
assert_equal("43TA21", r)
|
107
|
+
end
|
108
|
+
def test_parse__loop
|
109
|
+
r = @t.parse(%{<test:count set="0" /><test:loop times="5"><test:count inc="true" /><test:count /></test:loop>})
|
110
|
+
assert_equal("12345", r)
|
111
|
+
|
112
|
+
r = @t.parse(%{Three Stooges: <test:each_item between=", ">"<test:item />"</test:each_item>})
|
113
|
+
assert_equal(%{Three Stooges: "Larry", "Moe", "Curly"}, r)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_parse__fail_on_no_end_tag
|
117
|
+
assert_raises(Radius::MissingEndTagError) { @t.parse("<test:reverse>") }
|
118
|
+
end
|
119
|
+
def test_parse__fail_on_no_end_tag_2
|
120
|
+
assert_raises(Radius::MissingEndTagError) { @t.parse("<test:reverse><test:capitalize></test:reverse>") }
|
121
|
+
end
|
122
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: radius
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2006-01-21
|
8
|
+
summary: Powerful tag-based template system.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email:
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description: "Radius is a small, but powerful tag-based template language inspired by the
|
15
|
+
template languages used in MovableType and TextPattern."
|
16
|
+
autorequire: radius
|
17
|
+
default_executable:
|
18
|
+
bindir: bin
|
19
|
+
has_rdoc: true
|
20
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
+
requirements:
|
22
|
+
-
|
23
|
+
- ">"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.0.0
|
26
|
+
version:
|
27
|
+
platform: ruby
|
28
|
+
authors: []
|
29
|
+
files:
|
30
|
+
- DSL-SPEC
|
31
|
+
- lib
|
32
|
+
- QUICKSTART
|
33
|
+
- Rakefile
|
34
|
+
- README
|
35
|
+
- ROADMAP
|
36
|
+
- test
|
37
|
+
- lib/radius.rb
|
38
|
+
- test/radius_test.rb
|
39
|
+
test_files: []
|
40
|
+
rdoc_options:
|
41
|
+
- "--title"
|
42
|
+
- "Radius -- Powerful Tag-Based Templates"
|
43
|
+
- "--main"
|
44
|
+
- README
|
45
|
+
- "--line-numbers"
|
46
|
+
- ROADMAP
|
47
|
+
- QUICKSTART
|
48
|
+
- DSL-SPEC
|
49
|
+
- README
|
50
|
+
extra_rdoc_files: []
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
requirements:
|
54
|
+
- none
|
55
|
+
dependencies: []
|