bkerley-radius 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,122 @@
1
+ %%{
2
+ machine parser;
3
+
4
+
5
+ action _prefix { mark_pfx = p }
6
+ action prefix {
7
+ if data[mark_pfx..p-1] != @prefix
8
+ fnext Closeout;
9
+ end
10
+ }
11
+ action _starttag { mark_stg = p }
12
+ action starttag { @starttag = data[mark_stg..p-1] }
13
+ action _attr { mark_attr = p }
14
+ action attr {
15
+ @attrs[@nat] = @vat
16
+ }
17
+
18
+ action prematch {
19
+ @prematch_end = p
20
+ @prematch = data[0..p] if p > 0
21
+ }
22
+
23
+ action _nameattr { mark_nat = p }
24
+ action nameattr { @nat = data[mark_nat..p-1] }
25
+ action _valattr { mark_vat = p }
26
+ action valattr { @vat = data[mark_vat..p-1] }
27
+
28
+ action opentag { @flavor = :open }
29
+ action selftag { @flavor = :self }
30
+ action closetag { @flavor = :close }
31
+
32
+ action stopparse {
33
+ @cursor = p;
34
+ fbreak;
35
+ }
36
+
37
+
38
+ Closeout := empty;
39
+
40
+ # words
41
+ PrefixChar = [\-A-Za-z0-9._?] ;
42
+ NameChar = [\-A-Za-z0-9._:?] ;
43
+ TagName = NameChar+ >_starttag %starttag;
44
+ Prefix = PrefixChar+ >_prefix %prefix;
45
+
46
+ Name = Prefix ":" TagName;
47
+
48
+ NameAttr = NameChar+ >_nameattr %nameattr;
49
+ Q1Char = ( "\\\'" | [^'] ) ;
50
+ Q1Attr = Q1Char* >_valattr %valattr;
51
+ Q2Char = ( "\\\"" | [^"] ) ;
52
+ Q2Attr = Q2Char* >_valattr %valattr;
53
+
54
+ Attr = NameAttr space* "=" space* ('"' Q2Attr '"' | "'" Q1Attr "'") space* >_attr %attr;
55
+ Attrs = (space+ Attr* | empty);
56
+
57
+ CloseTrailer = "/>" %selftag;
58
+ OpenTrailer = ">" %opentag;
59
+
60
+ Trailer = (OpenTrailer | CloseTrailer);
61
+
62
+ OpenOrSelfTag = Name Attrs? Trailer;
63
+ CloseTag = "/" Name space* ">" %closetag;
64
+
65
+ SomeTag = '<' (OpenOrSelfTag | CloseTag);
66
+
67
+ main := |*
68
+ SomeTag => {
69
+ tag = {:prefix=>@prefix, :name=>@starttag, :flavor => @flavor, :attrs => @attrs}
70
+ @prefix = nil
71
+ @name = nil
72
+ @flavor = :tasteless
73
+ @attrs = {}
74
+ @nodes << tag << ''
75
+ fbreak;
76
+ };
77
+ any => {
78
+ @nodes.last << data[p]
79
+ @tagstart = p
80
+ };
81
+ *|;
82
+ }%%
83
+
84
+ module Radius
85
+ class Scanner
86
+ def self.operate(prefix, data)
87
+ buf = ""
88
+ csel = ""
89
+ @prematch = ''
90
+ @starttag = nil
91
+ @attrs = {}
92
+ @flavor = :tasteless
93
+ @cursor = 0
94
+ @tagstart = 0
95
+ @nodes = ['']
96
+ remainder = data.dup
97
+
98
+ until remainder.length == 0
99
+ p = perform_parse(prefix, remainder)
100
+ remainder = remainder[p..-1]
101
+ end
102
+
103
+ return @nodes
104
+ end
105
+
106
+ private
107
+ def self.perform_parse(prefix, data)
108
+ stack = []
109
+ p = 0
110
+ ts = 0
111
+ te = 0
112
+ act = 0
113
+ eof = data.length
114
+
115
+ @prefix = prefix
116
+ %% write data;
117
+ %% write init;
118
+ %% write exec;
119
+ return p
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,24 @@
1
+ module Radius
2
+ class ParseTag # :nodoc:
3
+ def initialize(&b)
4
+ @block = b
5
+ end
6
+
7
+ def on_parse(&b)
8
+ @block = b
9
+ end
10
+
11
+ def to_s
12
+ @block.call(self)
13
+ end
14
+ end
15
+
16
+ class ParseContainerTag < ParseTag # :nodoc:
17
+ attr_accessor :name, :attributes, :contents
18
+
19
+ def initialize(name = "", attributes = {}, contents = [], &b)
20
+ @name, @attributes, @contents = name, attributes, contents
21
+ super(&b)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ module Radius
2
+ #
3
+ # A tag binding is passed into each tag definition and contains helper methods for working
4
+ # with tags. Use it to gain access to the attributes that were passed to the tag, to
5
+ # render the tag contents, and to do other tasks.
6
+ #
7
+ class TagBinding
8
+ # The Context that the TagBinding is associated with. Used internally. Try not to use
9
+ # this object directly.
10
+ attr_reader :context
11
+
12
+ # The locals object for the current tag.
13
+ attr_reader :locals
14
+
15
+ # The name of the tag (as used in a template string).
16
+ attr_reader :name
17
+
18
+ # The attributes of the tag. Also aliased as TagBinding#attr.
19
+ attr_reader :attributes
20
+ alias :attr :attributes
21
+
22
+ # The render block. When called expands the contents of the tag. Use TagBinding#expand
23
+ # instead.
24
+ attr_reader :block
25
+
26
+ # Creates a new TagBinding object.
27
+ def initialize(context, locals, name, attributes, block)
28
+ @context, @locals, @name, @attributes, @block = context, locals, name, attributes, block
29
+ end
30
+
31
+ # Evaluates the current tag and returns the rendered contents.
32
+ def expand
33
+ double? ? block.call : ''
34
+ end
35
+
36
+ # Returns true if the current tag is a single tag.
37
+ def single?
38
+ block.nil?
39
+ end
40
+
41
+ # Returns true if the current tag is a container tag.
42
+ def double?
43
+ not single?
44
+ end
45
+
46
+ # The globals object from which all locals objects ultimately inherit their values.
47
+ def globals
48
+ @context.globals
49
+ end
50
+
51
+ # Returns a list of the way tags are nested around the current tag as a string.
52
+ def nesting
53
+ @context.current_nesting
54
+ end
55
+
56
+ # Fires off Context#tag_missing for the current tag.
57
+ def missing!
58
+ @context.tag_missing(name, attributes, &block)
59
+ end
60
+
61
+ # Renders the tag using the current context .
62
+ def render(tag, attributes = {}, &block)
63
+ @context.render_tag(tag, attributes, &block)
64
+ end
65
+
66
+ # Shortcut for accessing tag.attr[key]
67
+ def [](key)
68
+ attr[key]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,78 @@
1
+ module Radius
2
+ module TagDefinitions # :nodoc:
3
+ class TagFactory # :nodoc:
4
+ def initialize(context)
5
+ @context = context
6
+ end
7
+
8
+ def define_tag(name, options, &block)
9
+ options = prepare_options(name, options)
10
+ validate_params(name, options, &block)
11
+ construct_tag_set(name, options, &block)
12
+ expose_methods_as_tags(name, options)
13
+ end
14
+
15
+ protected
16
+
17
+ # Adds the tag definition to the context. Override in subclasses to add additional tags
18
+ # (child tags) when the tag is created.
19
+ def construct_tag_set(name, options, &block)
20
+ if block
21
+ @context.definitions[name.to_s] = block
22
+ else
23
+ lp = last_part(name)
24
+ @context.define_tag(name) do |tag|
25
+ if tag.single?
26
+ options[:for]
27
+ else
28
+ tag.locals.send("#{ lp }=", options[:for]) unless options[:for].nil?
29
+ tag.expand
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Normalizes options pased to tag definition. Override in decendants to preform
36
+ # additional normalization.
37
+ def prepare_options(name, options)
38
+ options = Util.symbolize_keys(options)
39
+ options[:expose] = expand_array_option(options[:expose])
40
+ object = options[:for]
41
+ options[:attributes] = object.respond_to?(:attributes) unless options.has_key? :attributes
42
+ options[:expose] += object.attributes.keys if options[:attributes]
43
+ options
44
+ end
45
+
46
+ # Validates parameters passed to tag definition. Override in decendants to add custom
47
+ # validations.
48
+ def validate_params(name, options, &block)
49
+ unless options.has_key? :for
50
+ raise ArgumentError.new("tag definition must contain a :for option or a block") unless block
51
+ raise ArgumentError.new("tag definition must contain a :for option when used with the :expose option") unless options[:expose].empty?
52
+ end
53
+ end
54
+
55
+ # Exposes the methods of an object as child tags.
56
+ def expose_methods_as_tags(name, options)
57
+ options[:expose].each do |method|
58
+ tag_name = "#{name}:#{method}"
59
+ lp = last_part(name)
60
+ @context.define_tag(tag_name) do |tag|
61
+ object = tag.locals.send(lp)
62
+ object.send(method)
63
+ end
64
+ end
65
+ end
66
+
67
+ protected
68
+
69
+ def expand_array_option(value)
70
+ [*value].compact.map { |m| m.to_s.intern }
71
+ end
72
+
73
+ def last_part(name)
74
+ name.split(':').last
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,30 @@
1
+ module Radius
2
+ module Util # :nodoc:
3
+ def self.symbolize_keys(hash)
4
+ new_hash = {}
5
+ hash.keys.each do |k|
6
+ new_hash[k.to_s.intern] = hash[k]
7
+ end
8
+ new_hash
9
+ end
10
+
11
+ def self.impartial_hash_delete(hash, key)
12
+ string = key.to_s
13
+ symbol = string.intern
14
+ value1 = hash.delete(symbol)
15
+ value2 = hash.delete(string)
16
+ value1 || value2
17
+ end
18
+
19
+ def self.constantize(camelized_string)
20
+ raise "invalid constant name `#{camelized_string}'" unless camelized_string.split('::').all? { |part| part =~ /^[A-Za-z]+$/ }
21
+ Object.module_eval(camelized_string)
22
+ end
23
+
24
+ def self.camelize(underscored_string)
25
+ string = ''
26
+ underscored_string.split('_').each { |part| string << part.capitalize }
27
+ string
28
+ end
29
+ end
30
+ end
data/tasks/scan.rake ADDED
@@ -0,0 +1,27 @@
1
+ namespace :scan do
2
+ desc 'Generate the parser'
3
+ task 'build' => ['lib/radius/parser/scan.rb']
4
+
5
+ desc 'Generate a PDF state graph from the parser'
6
+ task 'graph' => ['doc/scan.pdf']
7
+
8
+ desc 'turn the scan.rl file into a ruby file'
9
+ file 'lib/radius/parser/scan.rb' => ['lib/radius/parser/scan.rl'] do |t|
10
+ cd 'lib/radius/parser' do
11
+ sh "ragel -R scan.rl"
12
+ end
13
+ end
14
+
15
+ desc 'pdf of the ragel scanner'
16
+ file 'doc/scan.pdf' => 'lib/radius/parser/scan.dot' do |t|
17
+ cd 'lib/radius/parser' do
18
+ sh "dot -Tpdf -o ../../../doc/scan.pdf scan.dot"
19
+ end
20
+ end
21
+
22
+ file 'lib/radius/parser/scan.dot' => ['lib/radius/parser/scan.rl'] do |t|
23
+ cd 'lib/radius/parser' do
24
+ sh "ragel -Vp scan.rl > scan.dot"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class RadiusContextTest < Test::Unit::TestCase
4
+ include RadiusTestHelper
5
+
6
+ def setup
7
+ @context = new_context
8
+ end
9
+
10
+ def test_initialize
11
+ @context = Radius::Context.new
12
+ end
13
+
14
+ def test_initialize_with_block
15
+ @context = Radius::Context.new do |c|
16
+ assert_kind_of Radius::Context, c
17
+ c.define_tag('test') { 'just a test' }
18
+ end
19
+ assert_not_equal Hash.new, @context.definitions
20
+ end
21
+
22
+ def test_with
23
+ got = @context.with do |c|
24
+ assert_equal @context, c
25
+ end
26
+ assert_equal @context, got
27
+ end
28
+
29
+ def test_render_tag
30
+ define_tag "hello" do |tag|
31
+ "Hello #{tag.attr['name'] || 'World'}!"
32
+ end
33
+ assert_render_tag_output 'Hello World!', 'hello'
34
+ assert_render_tag_output 'Hello John!', 'hello', 'name' => 'John'
35
+ end
36
+
37
+ def test_render_tag__undefined_tag
38
+ e = assert_raises(Radius::UndefinedTagError) { @context.render_tag('undefined_tag') }
39
+ assert_equal "undefined tag `undefined_tag'", e.message
40
+ end
41
+
42
+ def test_tag_missing
43
+ class << @context
44
+ def tag_missing(tag, attr, &block)
45
+ "undefined tag `#{tag}' with attributes #{attr.inspect}"
46
+ end
47
+ end
48
+
49
+ text = ''
50
+ expected = %{undefined tag `undefined_tag' with attributes {"cool"=>"beans"}}
51
+ assert_nothing_raised { text = @context.render_tag('undefined_tag', 'cool' => 'beans') }
52
+ assert_equal expected, text
53
+ end
54
+
55
+ private
56
+
57
+ def assert_render_tag_output(output, *render_tag_params)
58
+ assert_equal output, @context.render_tag(*render_tag_params)
59
+ end
60
+
61
+ end
@@ -0,0 +1,278 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class RadiusParserTest < Test::Unit::TestCase
4
+ include RadiusTestHelper
5
+
6
+ def setup
7
+ @context = new_context
8
+ @parser = Radius::Parser.new(@context, :tag_prefix => 'r')
9
+ end
10
+
11
+ def test_initialize
12
+ @parser = Radius::Parser.new
13
+ assert_kind_of Radius::Context, @parser.context
14
+ end
15
+
16
+ def test_initialize_with_params
17
+ @parser = Radius::Parser.new(TestContext.new)
18
+ assert_kind_of TestContext, @parser.context
19
+
20
+ @parser = Radius::Parser.new(:context => TestContext.new)
21
+ assert_kind_of TestContext, @parser.context
22
+
23
+ @parser = Radius::Parser.new('context' => TestContext.new)
24
+ assert_kind_of TestContext, @parser.context
25
+
26
+ @parser = Radius::Parser.new(:tag_prefix => 'r')
27
+ assert_kind_of Radius::Context, @parser.context
28
+ assert_equal 'r', @parser.tag_prefix
29
+
30
+ @parser = Radius::Parser.new(TestContext.new, :tag_prefix => 'r')
31
+ assert_kind_of TestContext, @parser.context
32
+ assert_equal 'r', @parser.tag_prefix
33
+ end
34
+
35
+ def test_parse_individual_tags_and_parameters
36
+ define_tag "add" do |tag|
37
+ tag.attr["param1"].to_i + tag.attr["param2"].to_i
38
+ end
39
+ assert_parse_output "<3>", %{<<r:add param1="1" param2='2'/>>}
40
+ end
41
+
42
+ def test_parse_attributes
43
+ attributes = %{{"a"=>"1", "b"=>"2", "c"=>"3", "d"=>"'"}}
44
+ assert_parse_output attributes, %{<r:attr a="1" b='2'c="3"d="'" />}
45
+ assert_parse_output attributes, %{<r:attr a="1" b='2'c="3"d="'"></r:attr>}
46
+ end
47
+
48
+ def test_parse_attributes_with_slashes_or_angle_brackets
49
+ slash = %{{"slash"=>"/"}}
50
+ angle = %{{"angle"=>">"}}
51
+ assert_parse_output slash, %{<r:attr slash="/"></r:attr>}
52
+ assert_parse_output slash, %{<r:attr slash="/"><r:attr /></r:attr>}
53
+ assert_parse_output angle, %{<r:attr angle=">"></r:attr>}
54
+ end
55
+
56
+ def test_parse_quotes
57
+ assert_parse_output "test []", %{<r:echo value="test" /> <r:wrap attr="test"></r:wrap>}
58
+ end
59
+
60
+ def test_things_that_should_be_left_alone
61
+ [
62
+ %{ test="2"="4" },
63
+ %{="2" }
64
+ ].each do |middle|
65
+ assert_parsed_is_unchanged "<r:attr#{middle}/>"
66
+ assert_parsed_is_unchanged "<r:attr#{middle}>"
67
+ end
68
+ end
69
+
70
+ def test_tags_inside_html_tags
71
+ assert_parse_output %{<div class="xzibit">tags in yo tags</div>},
72
+ %{<div class="<r:reverse>tibizx</r:reverse>">tags in yo tags</div>}
73
+ end
74
+
75
+ def test_parse_result_is_always_a_string
76
+ define_tag("twelve") { 12 }
77
+ assert_parse_output "12", "<r:twelve />"
78
+ end
79
+
80
+ def test_parse_double_tags
81
+ assert_parse_output "test".reverse, "<r:reverse>test</r:reverse>"
82
+ assert_parse_output "tset TEST", "<r:reverse>test</r:reverse> <r:capitalize>test</r:capitalize>"
83
+ end
84
+
85
+ def test_parse_tag_nesting
86
+ define_tag("parent", :for => '')
87
+ define_tag("parent:child", :for => '')
88
+ define_tag("extra", :for => '')
89
+ define_tag("nesting") { |tag| tag.nesting }
90
+ define_tag("extra:nesting") { |tag| tag.nesting.gsub(':', ' > ') }
91
+ define_tag("parent:child:nesting") { |tag| tag.nesting.gsub(':', ' * ') }
92
+ assert_parse_output "nesting", "<r:nesting />"
93
+ assert_parse_output "parent:nesting", "<r:parent:nesting />"
94
+ assert_parse_output "extra > nesting", "<r:extra:nesting />"
95
+ assert_parse_output "parent * child * nesting", "<r:parent:child:nesting />"
96
+ assert_parse_output "parent > extra > nesting", "<r:parent:extra:nesting />"
97
+ assert_parse_output "parent > child > extra > nesting", "<r:parent:child:extra:nesting />"
98
+ assert_parse_output "parent * extra * child * nesting", "<r:parent:extra:child:nesting />"
99
+ assert_parse_output "parent > extra > child > extra > nesting", "<r:parent:extra:child:extra:nesting />"
100
+ assert_parse_output "parent > extra > child > extra > nesting", "<r:parent><r:extra><r:child><r:extra><r:nesting /></r:extra></r:child></r:extra></r:parent>"
101
+ assert_parse_output "extra * parent * child * nesting", "<r:extra:parent:child:nesting />"
102
+ assert_parse_output "extra > parent > nesting", "<r:extra><r:parent:nesting /></r:extra>"
103
+ assert_parse_output "extra * parent * child * nesting", "<r:extra:parent><r:child:nesting /></r:extra:parent>"
104
+ assert_raises(Radius::UndefinedTagError) { @parser.parse("<r:child />") }
105
+ end
106
+ def test_parse_tag_nesting_2
107
+ define_tag("parent", :for => '')
108
+ define_tag("parent:child", :for => '')
109
+ define_tag("content") { |tag| tag.nesting }
110
+ assert_parse_output 'parent:child:content', '<r:parent><r:child:content /></r:parent>'
111
+ end
112
+
113
+ def test_parse_tag__binding_do_missing
114
+ define_tag 'test' do |tag|
115
+ tag.missing!
116
+ end
117
+ e = assert_raises(Radius::UndefinedTagError) { @parser.parse("<r:test />") }
118
+ assert_equal "undefined tag `test'", e.message
119
+ end
120
+
121
+ def test_parse_chirpy_bird
122
+ # :> chirp chirp
123
+ assert_parse_output "<:", "<:"
124
+ end
125
+
126
+ def test_parse_tag__binding_render_tag
127
+ define_tag('test') { |tag| "Hello #{tag.attr['name']}!" }
128
+ define_tag('hello') { |tag| tag.render('test', tag.attr) }
129
+ assert_parse_output 'Hello John!', '<r:hello name="John" />'
130
+ end
131
+
132
+ def test_accessing_tag_attributes_through_tag_indexer
133
+ define_tag('test') { |tag| "Hello #{tag['name']}!" }
134
+ assert_parse_output 'Hello John!', '<r:test name="John" />'
135
+ end
136
+
137
+ def test_parse_tag__binding_render_tag_with_block
138
+ define_tag('test') { |tag| "Hello #{tag.expand}!" }
139
+ define_tag('hello') { |tag| tag.render('test') { tag.expand } }
140
+ assert_parse_output 'Hello John!', '<r:hello>John</r:hello>'
141
+ end
142
+
143
+ def test_tag_locals
144
+ define_tag "outer" do |tag|
145
+ tag.locals.var = 'outer'
146
+ tag.expand
147
+ end
148
+ define_tag "outer:inner" do |tag|
149
+ tag.locals.var = 'inner'
150
+ tag.expand
151
+ end
152
+ define_tag "outer:var" do |tag|
153
+ tag.locals.var
154
+ end
155
+ assert_parse_output 'outer', "<r:outer><r:var /></r:outer>"
156
+ assert_parse_output 'outer:inner:outer', "<r:outer><r:var />:<r:inner><r:var /></r:inner>:<r:var /></r:outer>"
157
+ assert_parse_output 'outer:inner:outer:inner:outer', "<r:outer><r:var />:<r:inner><r:var />:<r:outer><r:var /></r:outer>:<r:var /></r:inner>:<r:var /></r:outer>"
158
+ assert_parse_output 'outer', "<r:outer:var />"
159
+ end
160
+
161
+ def test_tag_globals
162
+ define_tag "set" do |tag|
163
+ tag.globals.var = tag.attr['value']
164
+ ''
165
+ end
166
+ define_tag "var" do |tag|
167
+ tag.globals.var
168
+ end
169
+ assert_parse_output " true false", %{<r:var /> <r:set value="true" /> <r:var /> <r:set value="false" /> <r:var />}
170
+ end
171
+
172
+ def test_parse_loops
173
+ @item = nil
174
+ define_tag "each" do |tag|
175
+ result = []
176
+ ["Larry", "Moe", "Curly"].each do |item|
177
+ tag.locals.item = item
178
+ result << tag.expand
179
+ end
180
+ result.join(tag.attr["between"] || "")
181
+ end
182
+ define_tag "each:item" do |tag|
183
+ tag.locals.item
184
+ end
185
+ assert_parse_output %{Three Stooges: "Larry", "Moe", "Curly"}, %{Three Stooges: <r:each between=", ">"<r:item />"</r:each>}
186
+ end
187
+
188
+ def test_parse_speed
189
+ define_tag "set" do |tag|
190
+ tag.globals.var = tag.attr['value']
191
+ ''
192
+ end
193
+ define_tag "var" do |tag|
194
+ tag.globals.var
195
+ end
196
+ parts = %w{decima nobis augue at facer processus commodo legentis odio lectorum dolore nulla esse lius qui nonummy ullamcorper erat ii notare}
197
+ multiplier = parts.map{|p| "#{p}=\"#{rand}\""}.join(' ')
198
+ assert_nothing_raised do
199
+ Timeout.timeout(10) do
200
+ assert_parse_output " false", %{<r:set value="false" #{multiplier} /> <r:var />}
201
+ end
202
+ end
203
+ end
204
+
205
+ def test_tag_option_for
206
+ define_tag 'fun', :for => 'just for kicks'
207
+ assert_parse_output 'just for kicks', '<r:fun />'
208
+ end
209
+
210
+ def test_tag_expose_option
211
+ define_tag 'user', :for => users.first, :expose => ['name', :age]
212
+ assert_parse_output 'John', '<r:user:name />'
213
+ assert_parse_output '25', '<r:user><r:age /></r:user>'
214
+ e = assert_raises(Radius::UndefinedTagError) { @parser.parse "<r:user:email />" }
215
+ assert_equal "undefined tag `email'", e.message
216
+ end
217
+
218
+ def test_tag_expose_attributes_option_on_by_default
219
+ define_tag 'user', :for => user_with_attributes
220
+ assert_parse_output 'John', '<r:user:name />'
221
+ end
222
+ def test_tag_expose_attributes_set_to_false
223
+ define_tag 'user_without_attributes', :for => user_with_attributes, :attributes => false
224
+ assert_raises(Radius::UndefinedTagError) { @parser.parse "<r:user_without_attributes:name />" }
225
+ end
226
+
227
+ def test_tag_options_must_contain_a_for_option_if_methods_are_exposed
228
+ e = assert_raises(ArgumentError) { define_tag('fun', :expose => :today) { 'test' } }
229
+ assert_equal "tag definition must contain a :for option when used with the :expose option", e.message
230
+ end
231
+
232
+ def test_parse_fail_on_missing_end_tag
233
+ assert_raises(Radius::MissingEndTagError) { @parser.parse("<r:reverse>") }
234
+ end
235
+
236
+ def test_parse_fail_on_wrong_end_tag
237
+ assert_raises(Radius::WrongEndTagError) { @parser.parse("<r:reverse><r:capitalize></r:reverse>") }
238
+ end
239
+
240
+ protected
241
+
242
+ def assert_parse_output(output, input, message = nil)
243
+ r = @parser.parse(input)
244
+ assert_equal(output, r, message)
245
+ end
246
+
247
+ def assert_parsed_is_unchanged(something)
248
+ assert_parse_output something, something
249
+ end
250
+
251
+ class User
252
+ attr_accessor :name, :age, :email, :friend
253
+ def initialize(name, age, email)
254
+ @name, @age, @email = name, age, email
255
+ end
256
+ def <=>(other)
257
+ name <=> other.name
258
+ end
259
+ end
260
+
261
+ class UserWithAttributes < User
262
+ def attributes
263
+ { :name => name, :age => age, :email => email }
264
+ end
265
+ end
266
+
267
+ def users
268
+ [
269
+ User.new('John', 25, 'test@johnwlong.com'),
270
+ User.new('James', 27, 'test@jameslong.com')
271
+ ]
272
+ end
273
+
274
+ def user_with_attributes
275
+ UserWithAttributes.new('John', 25, 'test@johnwlong.com')
276
+ end
277
+
278
+ end