radius 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ %%{
2
+ machine parser;
3
+
4
+
5
+ action _prefix { mark_pfx = p }
6
+ action prefix {
7
+ if data[mark_pfx..p-1] != @prefix
8
+ @nodes.last << data[mark_pfx-1..p]
9
+ fbreak;
10
+ end
11
+ }
12
+ action _starttag { mark_stg = p }
13
+ action starttag { @starttag = data[mark_stg..p-1] }
14
+ action _attr { mark_attr = p }
15
+ action attr {
16
+ @attrs[@nat] = @vat
17
+ }
18
+
19
+ action prematch {
20
+ @prematch_end = p
21
+ @prematch = data[0..p] if p > 0
22
+ }
23
+
24
+ action _nameattr { mark_nat = p }
25
+ action nameattr { @nat = data[mark_nat..p-1] }
26
+ action _valattr { mark_vat = p }
27
+ action valattr { @vat = data[mark_vat..p-1] }
28
+
29
+ action opentag { @flavor = :open }
30
+ action selftag { @flavor = :self }
31
+ action closetag { @flavor = :close }
32
+
33
+ action stopparse {
34
+ @cursor = p;
35
+ fbreak;
36
+ }
37
+
38
+
39
+ Closeout := empty;
40
+
41
+ # words
42
+ PrefixChar = [\-A-Za-z0-9._?] ;
43
+ NameChar = [\-A-Za-z0-9._:?] ;
44
+ TagName = NameChar+ >_starttag %starttag;
45
+ Prefix = PrefixChar+ >_prefix %prefix;
46
+
47
+ Name = Prefix ":" TagName;
48
+
49
+ NameAttr = NameChar+ >_nameattr %nameattr;
50
+ Q1Char = ( "\\\'" | [^'] ) ;
51
+ Q1Attr = Q1Char* >_valattr %valattr;
52
+ Q2Char = ( "\\\"" | [^"] ) ;
53
+ Q2Attr = Q2Char* >_valattr %valattr;
54
+
55
+ Attr = NameAttr space* "=" space* ('"' Q2Attr '"' | "'" Q1Attr "'") space* >_attr %attr;
56
+ Attrs = (space+ Attr* | empty);
57
+
58
+ CloseTrailer = "/>" %selftag;
59
+ OpenTrailer = ">" %opentag;
60
+
61
+ Trailer = (OpenTrailer | CloseTrailer);
62
+
63
+ OpenOrSelfTag = Name Attrs? Trailer;
64
+ CloseTag = "/" Name space* ">" %closetag;
65
+
66
+ SomeTag = '<' (OpenOrSelfTag | CloseTag);
67
+
68
+ main := |*
69
+ SomeTag => {
70
+ tag = {:prefix=>@prefix, :name=>@starttag, :flavor => @flavor, :attrs => @attrs}
71
+ @prefix = nil
72
+ @name = nil
73
+ @flavor = :tasteless
74
+ @attrs = {}
75
+ @nodes << tag << ''
76
+ fbreak;
77
+ };
78
+ any => {
79
+ @nodes.last << data[p]
80
+ @tagstart = p
81
+ };
82
+ *|;
83
+ }%%
84
+
85
+ module Radius
86
+ class Scanner
87
+ def self.operate(prefix, data)
88
+ buf = ""
89
+ csel = ""
90
+ @prematch = ''
91
+ @starttag = nil
92
+ @attrs = {}
93
+ @flavor = :tasteless
94
+ @cursor = 0
95
+ @tagstart = 0
96
+ @nodes = ['']
97
+ remainder = data.dup
98
+
99
+ until remainder.length == 0
100
+ p = perform_parse(prefix, remainder)
101
+ remainder = remainder[p..-1]
102
+ end
103
+
104
+ return @nodes
105
+ end
106
+
107
+ private
108
+ def self.perform_parse(prefix, data)
109
+ stack = []
110
+ p = 0
111
+ ts = 0
112
+ te = 0
113
+ act = 0
114
+ eof = data.length
115
+
116
+ @prefix = prefix
117
+ %% write data;
118
+ %% write init;
119
+ %% write exec;
120
+ return p
121
+ end
122
+ end
123
+ 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 = Utility.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 Utility # :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
@@ -0,0 +1,14 @@
1
+ module Radius
2
+ module Version
3
+ Major = '0'
4
+ Minor = '6'
5
+ Tiny = '1'
6
+
7
+ class << self
8
+ def to_s
9
+ [Major, Minor, Tiny].join('.')
10
+ end
11
+ alias :to_str :to_s
12
+ end
13
+ end
14
+ end
@@ -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
@@ -1,83 +1,4 @@
1
- require 'test/unit'
2
- require 'radius'
3
-
4
- module RadiusTestHelper
5
- class TestContext < Radius::Context; end
6
-
7
- def new_context
8
- Radius::Context.new do |c|
9
- c.define_tag("reverse" ) { |tag| tag.expand.reverse }
10
- c.define_tag("capitalize") { |tag| tag.expand.upcase }
11
- c.define_tag("attr" ) { |tag| tag.attr.inspect }
12
- c.define_tag("echo" ) { |tag| tag.attr['value'] }
13
- c.define_tag("wrap" ) { |tag| "[#{tag.expand}]" }
14
- end
15
- end
16
-
17
- def define_tag(name, options = {}, &block)
18
- @context.define_tag name, options, &block
19
- end
20
- end
21
-
22
- class RadiusContextTest < Test::Unit::TestCase
23
- include RadiusTestHelper
24
-
25
- def setup
26
- @context = new_context
27
- end
28
-
29
- def test_initialize
30
- @context = Radius::Context.new
31
- end
32
-
33
- def test_initialize_with_block
34
- @context = Radius::Context.new do |c|
35
- assert_kind_of Radius::Context, c
36
- c.define_tag('test') { 'just a test' }
37
- end
38
- assert_not_equal Hash.new, @context.definitions
39
- end
40
-
41
- def test_with
42
- got = @context.with do |c|
43
- assert_equal @context, c
44
- end
45
- assert_equal @context, got
46
- end
47
-
48
- def test_render_tag
49
- define_tag "hello" do |tag|
50
- "Hello #{tag.attr['name'] || 'World'}!"
51
- end
52
- assert_render_tag_output 'Hello World!', 'hello'
53
- assert_render_tag_output 'Hello John!', 'hello', 'name' => 'John'
54
- end
55
-
56
- def test_render_tag__undefined_tag
57
- e = assert_raises(Radius::UndefinedTagError) { @context.render_tag('undefined_tag') }
58
- assert_equal "undefined tag `undefined_tag'", e.message
59
- end
60
-
61
- def test_tag_missing
62
- class << @context
63
- def tag_missing(tag, attr, &block)
64
- "undefined tag `#{tag}' with attributes #{attr.inspect}"
65
- end
66
- end
67
-
68
- text = ''
69
- expected = %{undefined tag `undefined_tag' with attributes {"cool"=>"beans"}}
70
- assert_nothing_raised { text = @context.render_tag('undefined_tag', 'cool' => 'beans') }
71
- assert_equal expected, text
72
- end
73
-
74
- private
75
-
76
- def assert_render_tag_output(output, *render_tag_params)
77
- assert_equal output, @context.render_tag(*render_tag_params)
78
- end
79
-
80
- end
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
81
2
 
82
3
  class RadiusParserTest < Test::Unit::TestCase
83
4
  include RadiusTestHelper
@@ -145,6 +66,11 @@ class RadiusParserTest < Test::Unit::TestCase
145
66
  assert_parsed_is_unchanged "<r:attr#{middle}>"
146
67
  end
147
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
148
74
 
149
75
  def test_parse_result_is_always_a_string
150
76
  define_tag("twelve") { 12 }
@@ -191,12 +117,23 @@ class RadiusParserTest < Test::Unit::TestCase
191
117
  e = assert_raises(Radius::UndefinedTagError) { @parser.parse("<r:test />") }
192
118
  assert_equal "undefined tag `test'", e.message
193
119
  end
194
-
120
+
121
+ def test_parse_chirpy_bird
122
+ # :> chirp chirp
123
+ assert_parse_output "<:", "<:"
124
+ end
125
+
195
126
  def test_parse_tag__binding_render_tag
196
127
  define_tag('test') { |tag| "Hello #{tag.attr['name']}!" }
197
128
  define_tag('hello') { |tag| tag.render('test', tag.attr) }
198
129
  assert_parse_output 'Hello John!', '<r:hello name="John" />'
199
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
+
200
137
  def test_parse_tag__binding_render_tag_with_block
201
138
  define_tag('test') { |tag| "Hello #{tag.expand}!" }
202
139
  define_tag('hello') { |tag| tag.render('test') { tag.expand } }
@@ -248,6 +185,23 @@ class RadiusParserTest < Test::Unit::TestCase
248
185
  assert_parse_output %{Three Stooges: "Larry", "Moe", "Curly"}, %{Three Stooges: <r:each between=", ">"<r:item />"</r:each>}
249
186
  end
250
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
+
251
205
  def test_tag_option_for
252
206
  define_tag 'fun', :for => 'just for kicks'
253
207
  assert_parse_output 'just for kicks', '<r:fun />'
@@ -277,7 +231,22 @@ class RadiusParserTest < Test::Unit::TestCase
277
231
 
278
232
  def test_parse_fail_on_missing_end_tag
279
233
  assert_raises(Radius::MissingEndTagError) { @parser.parse("<r:reverse>") }
280
- assert_raises(Radius::MissingEndTagError) { @parser.parse("<r:reverse><r:capitalize></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
+ def test_parse_with_default_tag_prefix
241
+ define_tag("hello") { |tag| "Hello world!" }
242
+ @parser = Radius::Parser.new(@context)
243
+ assert_equal "<p>Hello world!</p>", @parser.parse('<p><radius:hello /></p>')
244
+ end
245
+
246
+ def test_parse_with_other_radius_like_tags
247
+ define_tag('hello') { "hello" }
248
+ @parser = Radius::Parser.new(@context, :tag_prefix => "ralph")
249
+ assert_equal "<r:ralph:hello />", @parser.parse("<r:ralph:hello />")
281
250
  end
282
251
 
283
252
  protected