radius 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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