add radius

This commit is contained in:
chris 2011-02-24 10:48:21 +08:00 committed by ihower
parent 8d3e11cabe
commit 6e100d3499
42 changed files with 5795 additions and 0 deletions

42
vendor/plugins/radius/CHANGELOG vendored Normal file
View File

@ -0,0 +1,42 @@
= Change Log
== edge
* Support for Rubinius [jlong]
* Support for Ruby 1.9 [aemadrid]
* More tests [aemadrid]
* Fixed issue #5 - problem with other namespace tags [jemmyw]
* Switched to Jeweler for better gem management [jlong]
* Allow operation in a threaded environment (parser per-thread, shared context)
* Allow switching scanners that tokenize templates.
* Include SquiggleScanner to parse tags that look like "{ hello /}"
== 0.6.1
* Fixed a problem with non-tags that have no prefix or tag name (see test_parse_chirpy_bird)
== 0.6.0 (private release)
* Split radius.rb into multiple files.
* Ported the really hairy regexes from Radius::Parser to a single Ragel machine.
* Added and refactored tests.
* Refactored Rakefile and other administrativia.
== 0.5.1
* Fixed a problem with parsing quotes where a single tag preceding a double tag would consume the start tag of the double tag if both contained attributes.
== 0.5.0
* Created a DSL for tag definitions (introducing a DSL makes this version of Radiant incompatible with the last). The DSL has the following features:
- full support for nested tags
- global and local tag variables
- Contexts can now be defined dynamically (instead of being subclassed)
- see the QUICKSTART for more info
* Many refactorings of the library and unit tests.
* Changed the license to the MIT-LICENSE.
* Updated documentation to reflect the changes.
* Updated the version number to reflect the maturity of the code base.
== 0.0.2
* Refactored Parser to use Context#render_tag instead of #send when rendering tags defined on a Context.
* UndefinedTagError is now thrown when Parser tries to render a tag which doesn't exist on a Context.
* Added Context#tag_missing which works like method_method missing on Object, but is tag specific.
== 0.0.1
* First release.

19
vendor/plugins/radius/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2006-2010, John W. Long
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

322
vendor/plugins/radius/QUICKSTART.rdoc vendored Normal file
View File

@ -0,0 +1,322 @@
= Radius Quick Start
== Defining Tags
Before you can parse a template with Radius you need to create a Context object which defines
the tags that will be used in the template. This is actually quite simple:
require 'radius'
context = Radius::Context.new
context.define_tag "hello" do |tag|
"Hello #{tag.attr['name'] || 'World'}!"
end
Once you have defined a context you can easily create a Parser:
parser = Radius::Parser.new(context)
puts parser.parse('<p><radius:hello /></p>')
puts parser.parse('<p><radius:hello name="John" /></p>')
This code will output:
<p>Hello World!</p>
<p>Hello John!</p>
Note how you can pass attributes from the template to the context using the attributes hash.
Above, the first tag that was parsed didn't have a name attribute so the code in the +hello+
tag definition uses "World" instead. The second time the tag is parsed the name attribute is
set to "John" which is used to create the string "Hello John!". Tags that do not follow this
rule will be treated as if they were undefined (like normal methods).
== Container Tags
Radius also allows you to define "container" tags. That is, tags that contain content and
that may optionally manipulate it in some way. For example, if you have RedCloth installed
you could define another tag to parse and create Textile output:
require 'redcloth'
context.define_tag "textile" do |tag|
contents = tag.expand
RedCloth.new(contents).to_html
end
(The code <tt>tag.expand</tt> above returns the contents of the template between the start and end
tags.)
With the code above your parser can easily handle Textile:
parser.parse('<radius:textile>h1. Hello **World**!</radius:textile>')
This code will output:
<h1>Hello <b>World</b>!</h1>
== Nested Tags
But wait!--it gets better. Because container tags can manipulate the content they contain
you can use them to iterate over collections:
context = Radius::Context.new
context.define_tag "stooge" do |tag|
content = ''
["Larry", "Moe", "Curly"].each do |name|
tag.locals.name = name
content << tag.expand
end
content
end
context.define_tag "stooge:name" do |tag|
tag.locals.name
end
parser = Radius::Parser.new(context)
template = <<-TEMPLATE
<ul>
<radius:stooge>
<li><radius:name /></li>
</radius:stooge>
</ul>
TEMPLATE
puts parser.parse(template)
This code will output:
<ul>
<li>Larry</li>
<li>Moe</li>
<li>Curly</li>
</ul>
Note how the definition for the +name+ tag is defined. Because "name" is prefixed
with "stooge:" the +name+ tag cannot appear outside the +stooge+ tag. Had it been defined
simply as "name" it would be valid anywhere, even outside the +stooge+ tag (which was
not what we wanted). Using the colon operator you can define tags with any amount of
nesting.
== Exposing Objects to Templates
During normal operation, you will often want to expose certain objects to your templates.
Writing the tags to do this all by hand would be cumbersome of Radius did not provide
several mechanisms to make this easier. The first is a way of exposing objects as tags
on the context object. To expose an object simply call the +define_tag+
method with the +for+ option:
context.define_tag "count", :for => 1
This would expose the object <tt>1</tt> to the template as the +count+ tag. It's basically the
equivalent of writing:
context.define_tag("count") { 1 }
So far this doesn't save you a whole lot of typing, but suppose you want to expose certain
methods that are on that object? You could do this:
context.define_tag "user", :for => user, :expose => [ :name, :age, :email ]
This will add a total of four tags to the context. One for the <tt>user</tt> variable, and
one for each of the three methods listed in the +expose+ clause. You could now get the user's
name inside your template like this:
<radius:user><radius:name /></radius:user>
If "John" was the value stored in <tt>user.name</tt> the template would render as "John".
== Tag Shorthand
In the example above we made reference to <tt>user.name</tt> in our template by using the
following code:
<radius:user><radius:name /></radius:user>
There is a much easer way to refer to the <tt>user.name</tt> variable. Use the colon operator
to "scope" the reference to <tt>name</tt>:
<radius:user:name />
Radius allows you to use this shortcut for all tags.
== Changing the Tag Prefix
By default, all Radius tags must begin with "radius". You can change this by altering the
tag_prefix attribute on a Parser. For example:
parser = Radius::Parser.new(context, :tag_prefix => 'r')
Now, when parsing templates with +parser+, Radius will require that every tag begin with "r"
instead of "radius".
== Custom Behavior for Undefined Tags
Context#tag_missing behaves much like Object#method_missing only it allows you to define
specific behavior for when a tag is not defined on a Context. For example:
class LazyContext < Radius::Context
def tag_missing(tag, attr, &block)
"<strong>ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect}</strong>"
end
end
parser = Radius::Parser.new(LazyContext.new, :tag_prefix => 'lazy')
puts parser.parse('<lazy:weird value="true" />')
This will output:
<strong>ERROR: Undefined tag `weird' with attributes {"value"=>"true"}</strong>
Normally, when the Radius Parser encounters an undefined tag for a Context it raises an
UndefinedTagError, but since we have defined #tag_missing on LazyContext the Parser now
outputs a nicely formated error message when we parse a string that does not contain a
valid tag.
== Tag Bindings
Radius passes a TagBinding into the block of the Context#define_tag method. The tag
binding is useful for a number of tasks. A tag binding has an #expand instance method
which processes a tag's contents and returns the result. It also has a #attr method
which returns a hash of the attributes that were passed into the tag. TagBinding also
contains the TagBinding#single? and TagBinding#double? methods which return true or false
based on wether the tag is a container tag or not. More about the methods which are
available on tag bindings can be found on the Radius::TagBinding documentation page.
== Tag Binding Locals, Globals, and Context Sensitive Tags
A TagBinding also contains two OpenStruct-like objects which are useful when developing
tags. TagBinding#globals is useful for storing variables which you would like to be
accessible to all tags:
context.define_tag "inc" do |tag|
tag.globals.count ||= 0
tag.globals.count += 1
""
end
context.define_tag "count" do |tag|
tag.globals.count || 0
end
TagBinding#locals mirrors the variables that are in TagBinding#globals, but allows child
tags to redefine variables. This is valuable when defining context sensitive tags:
class Person
attr_accessor :name, :friend
def initialize(name)
@name = name
end
end
jack = Person.new('Jack')
jill = Person.new('Jill')
jack.friend = jill
jill.friend = jack
context = Radius::Context.new do |c|
c.define_tag "jack" do |tag|
tag.locals.person = jack
tag.expand
end
c.define_tag "jill" do |tag|
tag.locals.person = jill
tag.expand
end
c.define_tag "name" do |tag|
tag.locals.person.name rescue tag.missing!
end
c.define_tag "friend" do |tag|
tag.locals.person = tag.locals.person.friend rescue tag.missing!
tag.expand
end
end
parser = Radius::Parser.new(context, :tag_prefix => 'r')
parser.parse('<r:jack:name />') #=> "Jack"
parser.parse('<r:jill:name />') #=> "Jill"
parser.parse('<r:jill:friend:name />') #=> "Jack"
parser.parse('<r:jack:friend:friend:name />') #=> "Jack"
parser.parse('<r:jill><r:friend:name /> and <r:name /></r:jill>') #=> "Jack and Jill"
parser.parse('<r:name />') # raises a Radius::UndefinedTagError exception
Notice how TagBinding#locals enables intelligent nesting. "<r:jill:name />" evaluates to
"Jill", but "<r:jill:friend:name />" evaluates to "Jack". Locals lose scope as soon as
the tag they were defined in closes. Globals on the other hand, never lose scope.
The final line in the example above demonstrates that calling "<r:name />" raises a
TagMissing error. This is because of the way the name tag was defined:
tag.locals.person.name rescue tag.missing!
If person is not defined on locals it will return nil. Calling #name on nil would normally
raise a NoMethodError exception, but because of the 'rescue' clause the TagBinding#missing!
method is called which fires off Context#tag_missing. By default Context#tag_missing raises
a UndefinedTagError exception. The 'rescue tag.missing!' idiom is extremly useful for adding
simple error checking to context sensitive tags.
== Tag Specificity
When Radius is presented with two tags that have the same name, but different nesting
Radius uses an algorithm similar to the way winning rules are calculated in Cascading Style
Sheets (CSS) to determine which definition should be used. Each time a tag is encountered
in a template potential tags are assigned specificity values and the tag with the highest
specificity wins.
For example, given the following tag definitions:
nesting
extra:nesting
parent:child:nesting
And template:
<r:parent:extra:child:nesting />
Radius will calculate specificity values like this:
nesting => 1.0.0.0
extra:nesting => 1.0.1.0
parent:child:nesting => 1.1.0.1
Meaning that parent:child:nesting will win. If a template contained:
<r:parent:child:extra:nesting />
The following specificity values would be assigned to each of the tag definitions:
nesting => 1.0.0.0
extra:nesting => 1.1.0.0
parent:child:nesting => 1.0.1.1
Meaning that extra:nesting would win because it is more "specific".
Values are assigned by assigning points to each of the tags from right to left.
Given a tag found in a template with nesting four levels deep, the maximum
specificity a tag could be assigned would be:
1.1.1.1
One point for each of the levels.
In practice, you don't need to understand this topic to be effective with Radius.
For the most part you will find that Radius resolves tags precisely the way that
you would expect. If you find this section confusing forget about it and refer
back to it if you find that tags are resolving differently from the way that you
expected.

100
vendor/plugins/radius/README.rdoc vendored Normal file
View File

@ -0,0 +1,100 @@
= Radius -- Powerful Tag-Based Templates
Radius is a powerful tag-based template language for Ruby inspired by the
template languages used in MovableType[http://www.movabletype.org] and
TextPattern[http://www.textpattern.com]. It uses tags similar to XML, but can
be used to generate any form of plain text (HTML, e-mail, etc...).
== Usage
With Radius, it is extremely easy to create custom tags and parse them. Here's a small
example:
require 'radius'
# Define tags on a context that will be available to a template:
context = Radius::Context.new do |c|
c.define_tag 'hello' do
'Hello world'
end
c.define_tag 'repeat' do |tag|
number = (tag.attr['times'] || '1').to_i
result = ''
number.times { result << tag.expand }
result
end
end
# Create a parser to parse tags that begin with 'r:'
parser = Radius::Parser.new(context, :tag_prefix => 'r')
# Parse tags and output the result
puts parser.parse(%{A small example:\n<r:repeat times="3">* <r:hello />!\n</r:repeat>})
Output:
A small example:
* Hello world!
* Hello world!
* Hello world!
== Quick Start
Read the QUICKSTART file to get up and running with Radius.
== Requirements
Radius does not have any external requirements for using the library in your
own programs.
Ragel is required to create the ruby parser from the Ragel specification,
and both Ragel and Graphviz are required to draw the state graph for the
parser.
== Installation
It is recommended that you install Radius using the RubyGems packaging system:
% gem install --remote radius
== License
Radius is released under the MIT license and is copyright (c) 2006-2010
John W. Long. A copy of the MIT license can be found in the LICENSE file.
== Roadmap
This is a prioritized roadmap for future releases:
1. Clean up the current code base. [Done]
2. Add support for multi-level contexts: tags should be able to be
defined to only be valid within other sets of tags. [Done]
3. Create a simple DSL for defining contexts. [Done]
4. Optimize for speed, modify scan.rl to emit C.
== Development
The latest version of Radius can be found on RubyForge:
http://rubyforge.org/projects/radius
Experimental and development versions of Radius can be found on Github:
http://github.com/jlong/radius
If you are interested in helping with the development of Radius, feel free to
fork the project on GitHub and send me a pull request.
John Long ::
http://wiseheartdesign.com

8
vendor/plugins/radius/Rakefile vendored Normal file
View File

@ -0,0 +1,8 @@
require 'rubygems'
require 'rake'
require File.dirname(__FILE__) + '/lib/radius/version'
Dir['tasks/**/*.rake'].each { |t| load t }
task :default => :test

1
vendor/plugins/radius/VERSION vendored Normal file
View File

@ -0,0 +1 @@
0.7.0.prerelease

11
vendor/plugins/radius/lib/radius.rb vendored Normal file
View File

@ -0,0 +1,11 @@
require 'radius/version'
require 'radius/error'
require 'radius/tag_definitions'
require 'radius/delegating_open_struct'
require 'radius/tag_binding'
require 'radius/context'
require 'radius/parse_tag'
require 'radius/ord_string'
require 'radius/parser/scanner'
require 'radius/parser'
require 'radius/utility'

View File

@ -0,0 +1,147 @@
module Radius
#
# A context contains the tag definitions which are available for use in a template.
# See the QUICKSTART for a detailed explaination its
# usage.
#
class Context
# A hash of tag definition blocks that define tags accessible on a Context.
attr_accessor :definitions # :nodoc:
attr_accessor :globals # :nodoc:
# Creates a new Context object.
def initialize(&block)
@definitions = {}
@tag_binding_stack = []
@globals = DelegatingOpenStruct.new
with(&block) if block_given?
end
# Yield an instance of self for tag definitions:
#
# context.with do |c|
# c.define_tag 'test' do
# 'test'
# end
# end
#
def with
yield self
self
end
# Creates a tag definition on a context. Several options are available to you
# when creating a tag:
#
# +for+:: Specifies an object that the tag is in reference to. This is
# applicable when a block is not passed to the tag, or when the
# +expose+ option is also used.
#
# +expose+:: Specifies that child tags should be set for each of the methods
# contained in this option. May be either a single symbol/string or
# an array of symbols/strings.
#
# +attributes+:: Specifies whether or not attributes should be exposed
# automatically. Useful for ActiveRecord objects. Boolean. Defaults
# to +true+.
#
def define_tag(name, options = {}, &block)
type = Utility.impartial_hash_delete(options, :type).to_s
klass = Utility.constantize('Radius::TagDefinitions::' + Utility.camelize(type) + 'TagFactory') rescue raise(ArgumentError.new("Undefined type `#{type}' in options hash"))
klass.new(self).define_tag(name, options, &block)
end
# Returns the value of a rendered tag. Used internally by Parser#parse.
def render_tag(name, attributes = {}, &block)
if name =~ /^(.+?):(.+)$/
render_tag($1) { render_tag($2, attributes, &block) }
else
tag_definition_block = @definitions[qualified_tag_name(name.to_s)]
if tag_definition_block
stack(name, attributes, block) do |tag|
tag_definition_block.call(tag).to_s
end
else
tag_missing(name, attributes, &block)
end
end
end
# Like method_missing for objects, but fired when a tag is undefined.
# Override in your own Context to change what happens when a tag is
# undefined. By default this method raises an UndefinedTagError.
def tag_missing(name, attributes, &block)
raise UndefinedTagError.new(name)
end
# Returns the state of the current render stack. Useful from inside
# a tag definition. Normally just use TagBinding#nesting.
def current_nesting
@tag_binding_stack.collect { |tag| tag.name }.join(':')
end
# make a usable copy of this context
def dup # :nodoc:
rv = self.class.new
rv.globals = globals.dup
rv.definitions = definitions.dup
rv
end
private
# A convienence method for managing the various parts of the
# tag binding stack.
def stack(name, attributes, block)
previous = @tag_binding_stack.last
previous_locals = previous.nil? ? globals : previous.locals
locals = DelegatingOpenStruct.new(previous_locals)
binding = TagBinding.new(self, locals, name, attributes, block)
@tag_binding_stack.push(binding)
result = yield(binding)
@tag_binding_stack.pop
result
end
# Returns a fully qualified tag name based on state of the
# tag binding stack.
def qualified_tag_name(name)
nesting_parts = @tag_binding_stack.collect { |tag| tag.name }
nesting_parts << name unless nesting_parts.last == name
specific_name = nesting_parts.join(':') # specific_name always has the highest specificity
unless @definitions.has_key? specific_name
possible_matches = @definitions.keys.grep(/(^|:)#{name}$/)
specificity = possible_matches.inject({}) { |hash, tag| hash[numeric_specificity(tag, nesting_parts)] = tag; hash }
max = specificity.keys.max
if max != 0
specificity[max]
else
name
end
else
specific_name
end
end
# Returns the specificity for +tag_name+ at nesting defined
# by +nesting_parts+ as a number.
def numeric_specificity(tag_name, nesting_parts)
nesting_parts = nesting_parts.dup
name_parts = tag_name.split(':')
specificity = 0
value = 1
if nesting_parts.last == name_parts.last
while nesting_parts.size > 0
if nesting_parts.last == name_parts.last
specificity += value
name_parts.pop
end
nesting_parts.pop
value *= 0.1
end
specificity = 0 if (name_parts.size > 0)
end
specificity
end
end
end

View File

@ -0,0 +1,37 @@
module Radius
class DelegatingOpenStruct # :nodoc:
attr_accessor :object
def initialize(object = nil)
@object = object
@hash = {}
end
def dup
rv = self.class.new
rv.instance_variable_set(:@hash, @hash.dup)
rv
end
def method_missing(method, *args, &block)
symbol = (method.to_s =~ /^(.*?)=$/) ? $1.intern : method
if (0..1).include?(args.size)
if args.size == 1
@hash[symbol] = args.first
else
if @hash.has_key?(symbol)
@hash[symbol]
else
unless object.nil?
@object.send(method, *args, &block)
else
nil
end
end
end
else
super
end
end
end
end

View File

@ -0,0 +1,43 @@
module Radius
# Abstract base class for all parsing errors.
class ParseError < StandardError
end
# Occurs when the Parser expects an end tag for one tag and finds the end tag for another.
class WrongEndTagError < ParseError
def initialize(expected_tag, got_tag, stack)
stack_message = " with stack #{stack.inspect}" if stack
super("wrong end tag `#{got_tag}' found for start tag `#{expected_tag}'#{stack_message}")
end
end
# Occurs when Parser cannot find an end tag for a given tag in a template or when
# tags are miss-matched in a template.
class MissingEndTagError < ParseError
# Create a new MissingEndTagError object for +tag_name+.
def initialize(tag_name, stack)
stack_message = " with stack #{stack.inspect}" if stack
super("end tag not found for start tag `#{tag_name}'#{stack_message}")
end
end
# Occurs when Context#render_tag cannot find the specified tag on a Context.
class UndefinedTagError < ParseError
# Create a new UndefinedTagError object for +tag_name+.
def initialize(tag_name)
super("undefined tag `#{tag_name}'")
end
end
class TastelessTagError < ParseError #:nodoc:
def initialize(tag, stack)
super("internal error with tasteless tag #{tag.inspect} and stack #{stack.inspect}")
end
end
class UndefinedFlavorError < ParseError #:nodoc:
def initialize(tag, stack)
super("internal error with unknown flavored tag #{tag.inspect} and stack #{stack.inspect}")
end
end
end

View File

@ -0,0 +1,13 @@
module Radius
class OrdString < String
if RUBY_VERSION[0,3] == '1.9'
def [](*args)
if args.size == 1 && args.first.is_a?(Integer)
slice(args.first).ord
else
super
end
end
end
end
end

View File

@ -0,0 +1,24 @@
module Radius
class ParseTag # :nodoc:
def initialize(&b)
@block = b
end
def on_parse(&b)
@block = b
end
def to_s
@block.call(self)
end
end
class ParseContainerTag < ParseTag # :nodoc:
attr_accessor :name, :attributes, :contents
def initialize(name = "", attributes = {}, contents = [], &b)
@name, @attributes, @contents = name, attributes, contents
super(&b)
end
end
end

View File

@ -0,0 +1,79 @@
module Radius
#
# The Radius parser. Initialize a parser with a Context object that
# defines how tags should be expanded. See the QUICKSTART[link:files/QUICKSTART.html]
# for a detailed explaination of its usage.
#
class Parser
# The Context object used to expand template tags.
attr_accessor :context
# The string that prefixes all tags that are expanded by a parser
# (the part in the tag name before the first colon).
attr_accessor :tag_prefix
# The class that performs tokenization of the input string
attr_accessor :scanner
# Creates a new parser object initialized with a Context.
def initialize(context = Context.new, options = {})
if context.kind_of?(Hash) and options.empty?
options, context = context, (context[:context] || context['context'])
end
options = Utility.symbolize_keys(options)
self.context = context ? context.dup : Context.new
self.tag_prefix = options[:tag_prefix] || 'radius'
self.scanner = options[:scanner] || default_scanner
end
# Parses string for tags, expands them, and returns the result.
def parse(string)
@stack = [ ParseContainerTag.new { |t| Utility.array_to_s(t.contents) } ]
tokenize(string)
stack_up
@stack.last.to_s
end
protected
# Convert the string into a list of text blocks and scanners (tokens)
def tokenize(string)
@tokens = scanner.operate(tag_prefix, string)
end
def stack_up
@tokens.each do |t|
if t.is_a? String
@stack.last.contents << t
next
end
case t[:flavor]
when :open
@stack.push(ParseContainerTag.new(t[:name], t[:attrs]))
when :self
@stack.last.contents << ParseTag.new {@context.render_tag(t[:name], t[:attrs])}
when :close
popped = @stack.pop
raise WrongEndTagError.new(popped.name, t[:name], @stack) if popped.name != t[:name]
popped.on_parse { |b| @context.render_tag(popped.name, popped.attributes) { Utility.array_to_s(b.contents) } }
@stack.last.contents << popped
when :tasteless
raise TastelessTagError.new(t, @stack)
else
raise UndefinedFlavorError.new(t, @stack)
end
end
raise MissingEndTagError.new(@stack.last.name, @stack) if @stack.length != 1
end
def default_scanner
if RUBY_PLATFORM == 'java'
require 'java'
require 'radius/parser/java_scanner.jar'
::Radius.send(:include_package, 'radius.parser')
Radius::JavaScanner.new(JRuby.runtime)
else
Radius::Scanner.new
end
end
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,634 @@
// line 1 "JavaScanner.rl"
// line 84 "JavaScanner.rl"
package radius.parser;
import java.util.HashMap;
import java.util.LinkedList;
import org.jruby.Ruby; // runtime
import org.jruby.RubyObject;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.RubyArray;
import org.jruby.RubyString;
import org.jruby.RubyHash;
import org.jruby.RubySymbol;
public class JavaScanner {
Ruby runtime = null;
RubyArray rv = null;
void pass_through(String str) {
RubyObject last = ((RubyObject)rv.last());
if ( rv.size() > 0 && last != null && (last instanceof RubyString) ){
// XXX concat changes for ruby 1.9
((RubyString) last).concat(RubyString.newString(runtime, str));
} else {
rv.append(RubyString.newString(runtime, str));
}
}
void tag(String prefix, String name, RubyHash attr, RubySymbol flavor) {
RubyHash tag = RubyHash.newHash(runtime);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "prefix"),
RubyString.newString(runtime, prefix)
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "name"),
RubyString.newString(runtime, name)
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "attrs"),
attr
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "flavor"),
flavor
);
rv.append(tag);
}
public JavaScanner(Ruby runtime) {
this.runtime = runtime;
}
// line 65 "JavaScanner.java"
private static byte[] init__parser_actions_0()
{
return new byte [] {
0, 1, 0, 1, 3, 1, 4, 1, 5, 1, 6, 1,
7, 1, 8, 1, 9, 1, 10, 1, 14, 1, 15, 1,
19, 1, 21, 1, 22, 1, 23, 2, 1, 2, 2, 5,
6, 2, 6, 7, 2, 9, 5, 2, 9, 10, 2, 10,
9, 2, 11, 20, 2, 12, 20, 2, 13, 20, 2, 16,
17, 2, 16, 18, 3, 5, 6, 7, 3, 9, 5, 6,
3, 16, 6, 17, 4, 9, 5, 6, 7, 4, 16, 5,
6, 17, 5, 16, 9, 5, 6, 17
};
}
private static final byte _parser_actions[] = init__parser_actions_0();
private static short[] init__parser_key_offsets_0()
{
return new short [] {
0, 0, 11, 21, 34, 47, 61, 65, 70, 72, 74, 87,
100, 101, 103, 118, 133, 149, 155, 161, 176, 179, 182, 185,
200, 202, 204, 219, 235, 241, 247, 250, 253, 269, 285, 302,
309, 315, 331, 335, 351, 366, 369, 371, 381, 392, 402, 416,
420, 420, 421, 430, 430, 430, 432, 434, 437, 440, 442, 444
};
}
private static final short _parser_key_offsets[] = init__parser_key_offsets_0();
private static char[] init__parser_trans_keys_0()
{
return new char [] {
58, 63, 95, 45, 46, 48, 57, 65, 90, 97, 122, 63,
95, 45, 46, 48, 58, 65, 90, 97, 122, 32, 47, 62,
63, 95, 9, 13, 45, 58, 65, 90, 97, 122, 32, 47,
62, 63, 95, 9, 13, 45, 58, 65, 90, 97, 122, 32,
61, 63, 95, 9, 13, 45, 46, 48, 58, 65, 90, 97,
122, 32, 61, 9, 13, 32, 34, 39, 9, 13, 34, 92,
34, 92, 32, 47, 62, 63, 95, 9, 13, 45, 58, 65,
90, 97, 122, 32, 47, 62, 63, 95, 9, 13, 45, 58,
65, 90, 97, 122, 62, 34, 92, 32, 34, 47, 62, 63,
92, 95, 9, 13, 45, 58, 65, 90, 97, 122, 32, 34,
47, 62, 63, 92, 95, 9, 13, 45, 58, 65, 90, 97,
122, 32, 34, 61, 63, 92, 95, 9, 13, 45, 46, 48,
58, 65, 90, 97, 122, 32, 34, 61, 92, 9, 13, 32,
34, 39, 92, 9, 13, 32, 34, 47, 62, 63, 92, 95,
9, 13, 45, 58, 65, 90, 97, 122, 34, 62, 92, 34,
39, 92, 34, 39, 92, 32, 39, 47, 62, 63, 92, 95,
9, 13, 45, 58, 65, 90, 97, 122, 39, 92, 39, 92,
32, 39, 47, 62, 63, 92, 95, 9, 13, 45, 58, 65,
90, 97, 122, 32, 39, 61, 63, 92, 95, 9, 13, 45,
46, 48, 58, 65, 90, 97, 122, 32, 39, 61, 92, 9,
13, 32, 34, 39, 92, 9, 13, 34, 39, 92, 34, 39,
92, 32, 34, 39, 47, 62, 63, 92, 95, 9, 13, 45,
58, 65, 90, 97, 122, 32, 34, 39, 47, 62, 63, 92,
95, 9, 13, 45, 58, 65, 90, 97, 122, 32, 34, 39,
61, 63, 92, 95, 9, 13, 45, 46, 48, 58, 65, 90,
97, 122, 32, 34, 39, 61, 92, 9, 13, 32, 34, 39,
92, 9, 13, 32, 34, 39, 47, 62, 63, 92, 95, 9,
13, 45, 58, 65, 90, 97, 122, 34, 39, 62, 92, 32,
34, 39, 47, 62, 63, 92, 95, 9, 13, 45, 58, 65,
90, 97, 122, 32, 39, 47, 62, 63, 92, 95, 9, 13,
45, 58, 65, 90, 97, 122, 39, 62, 92, 39, 92, 63,
95, 45, 46, 48, 57, 65, 90, 97, 122, 58, 63, 95,
45, 46, 48, 57, 65, 90, 97, 122, 63, 95, 45, 46,
48, 58, 65, 90, 97, 122, 32, 62, 63, 95, 9, 13,
45, 46, 48, 58, 65, 90, 97, 122, 32, 62, 9, 13,
60, 47, 63, 95, 45, 57, 65, 90, 97, 122, 34, 92,
34, 92, 34, 39, 92, 34, 39, 92, 39, 92, 39, 92,
0
};
}
private static final char _parser_trans_keys[] = init__parser_trans_keys_0();
private static byte[] init__parser_single_lengths_0()
{
return new byte [] {
0, 3, 2, 5, 5, 4, 2, 3, 2, 2, 5, 5,
1, 2, 7, 7, 6, 4, 4, 7, 3, 3, 3, 7,
2, 2, 7, 6, 4, 4, 3, 3, 8, 8, 7, 5,
4, 8, 4, 8, 7, 3, 2, 2, 3, 2, 4, 2,
0, 1, 3, 0, 0, 2, 2, 3, 3, 2, 2, 0
};
}
private static final byte _parser_single_lengths[] = init__parser_single_lengths_0();
private static byte[] init__parser_range_lengths_0()
{
return new byte [] {
0, 4, 4, 4, 4, 5, 1, 1, 0, 0, 4, 4,
0, 0, 4, 4, 5, 1, 1, 4, 0, 0, 0, 4,
0, 0, 4, 5, 1, 1, 0, 0, 4, 4, 5, 1,
1, 4, 0, 4, 4, 0, 0, 4, 4, 4, 5, 1,
0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
}
private static final byte _parser_range_lengths[] = init__parser_range_lengths_0();
private static short[] init__parser_index_offsets_0()
{
return new short [] {
0, 0, 8, 15, 25, 35, 45, 49, 54, 57, 60, 70,
80, 82, 85, 97, 109, 121, 127, 133, 145, 149, 153, 157,
169, 172, 175, 187, 199, 205, 211, 215, 219, 232, 245, 258,
265, 271, 284, 289, 302, 314, 318, 321, 328, 336, 343, 353,
357, 358, 360, 367, 368, 369, 372, 375, 379, 383, 386, 389
};
}
private static final short _parser_index_offsets[] = init__parser_index_offsets_0();
private static byte[] init__parser_indicies_0()
{
return new byte [] {
2, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 3,
3, 3, 0, 4, 6, 7, 5, 5, 4, 5, 5, 5,
0, 8, 10, 11, 9, 9, 8, 9, 9, 9, 0, 13,
15, 14, 14, 13, 14, 14, 14, 14, 12, 16, 17, 16,
12, 17, 18, 19, 17, 12, 21, 22, 20, 24, 25, 23,
26, 28, 29, 27, 27, 26, 27, 27, 27, 12, 30, 32,
33, 31, 31, 30, 31, 31, 31, 12, 34, 12, 35, 25,
23, 36, 24, 38, 39, 37, 25, 37, 36, 37, 37, 37,
23, 40, 24, 42, 43, 41, 25, 41, 40, 41, 41, 41,
23, 44, 24, 46, 45, 25, 45, 44, 45, 45, 45, 45,
23, 47, 24, 48, 25, 47, 23, 48, 49, 50, 25, 48,
23, 51, 21, 53, 54, 52, 22, 52, 51, 52, 52, 52,
20, 24, 55, 25, 23, 57, 58, 59, 56, 61, 35, 62,
60, 64, 24, 66, 67, 65, 68, 65, 64, 65, 65, 65,
63, 24, 68, 63, 61, 68, 63, 69, 24, 71, 72, 70,
68, 70, 69, 70, 70, 70, 63, 73, 24, 75, 74, 68,
74, 73, 74, 74, 74, 74, 63, 76, 24, 77, 68, 76,
63, 77, 78, 79, 68, 77, 63, 80, 58, 59, 56, 81,
81, 62, 60, 82, 61, 35, 84, 85, 83, 62, 83, 82,
83, 83, 83, 60, 86, 61, 35, 88, 89, 87, 62, 87,
86, 87, 87, 87, 60, 90, 61, 35, 92, 91, 62, 91,
90, 91, 91, 91, 91, 60, 93, 61, 35, 94, 62, 93,
60, 94, 95, 96, 62, 94, 60, 97, 80, 58, 99, 100,
98, 59, 98, 97, 98, 98, 98, 56, 61, 35, 101, 62,
60, 97, 57, 58, 99, 100, 98, 59, 98, 97, 98, 98,
98, 56, 103, 21, 105, 106, 104, 107, 104, 103, 104, 104,
104, 102, 24, 108, 68, 63, 21, 107, 102, 109, 109, 109,
109, 109, 109, 0, 111, 110, 110, 110, 110, 110, 110, 0,
112, 112, 112, 112, 112, 112, 0, 113, 115, 114, 114, 113,
114, 114, 114, 114, 0, 116, 117, 116, 0, 118, 120, 119,
123, 122, 122, 122, 122, 122, 121, 124, 125, 24, 25, 23,
24, 25, 23, 61, 35, 62, 60, 61, 35, 62, 60, 24,
68, 63, 24, 68, 63, 126, 0
};
}
private static final byte _parser_indicies[] = init__parser_indicies_0();
private static byte[] init__parser_trans_targs_0()
{
return new byte [] {
49, 1, 2, 3, 4, 3, 12, 52, 4, 5, 12, 52,
49, 6, 5, 7, 6, 7, 8, 42, 9, 10, 13, 9,
10, 13, 11, 5, 12, 52, 11, 5, 12, 52, 51, 14,
15, 16, 20, 54, 15, 16, 20, 54, 17, 16, 18, 17,
18, 19, 21, 15, 16, 20, 54, 53, 22, 23, 14, 31,
22, 23, 31, 24, 26, 27, 41, 58, 25, 26, 27, 41,
58, 28, 27, 29, 28, 29, 30, 40, 23, 32, 33, 34,
38, 56, 33, 34, 38, 56, 35, 34, 36, 35, 36, 37,
39, 33, 34, 38, 56, 55, 24, 26, 27, 41, 58, 25,
57, 44, 44, 45, 46, 47, 46, 59, 47, 59, 0, 49,
50, 49, 1, 43, 49, 49, 49
};
}
private static final byte _parser_trans_targs[] = init__parser_trans_targs_0();
private static byte[] init__parser_trans_actions_0()
{
return new byte [] {
27, 0, 31, 3, 5, 0, 5, 5, 0, 11, 0, 0,
29, 13, 0, 13, 0, 0, 0, 0, 15, 43, 15, 0,
17, 0, 7, 64, 34, 34, 0, 37, 9, 9, 0, 17,
7, 64, 34, 81, 0, 37, 9, 72, 13, 0, 13, 0,
0, 17, 0, 40, 76, 68, 86, 58, 15, 46, 43, 15,
0, 17, 0, 0, 7, 64, 34, 81, 0, 0, 37, 9,
72, 13, 0, 13, 0, 0, 0, 17, 43, 17, 7, 64,
34, 81, 0, 37, 9, 72, 13, 0, 13, 0, 0, 17,
17, 40, 76, 68, 86, 58, 15, 40, 76, 68, 86, 15,
58, 1, 0, 31, 3, 5, 0, 5, 0, 0, 0, 23,
61, 25, 1, 0, 52, 49, 55
};
}
private static final byte _parser_trans_actions[] = init__parser_trans_actions_0();
private static byte[] init__parser_to_state_actions_0()
{
return new byte [] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
}
private static final byte _parser_to_state_actions[] = init__parser_to_state_actions_0();
private static byte[] init__parser_from_state_actions_0()
{
return new byte [] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
}
private static final byte _parser_from_state_actions[] = init__parser_from_state_actions_0();
private static short[] init__parser_eof_trans_0()
{
return new short [] {
0, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1,
0, 0, 122, 125, 126, 125, 126, 125, 126, 125, 126, 127
};
}
private static final short _parser_eof_trans[] = init__parser_eof_trans_0();
static final int parser_start = 49;
static final int parser_first_final = 49;
static final int parser_error = 0;
static final int parser_en_Closeout = 48;
static final int parser_en_main = 49;
// line 143 "JavaScanner.rl"
public RubyArray operate(String tag_prefix, String input) {
char[] data = input.toCharArray();
String disposable_string;
String name = "";
String prefix = "";
RubySymbol flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
RubyHash attributes = RubyHash.newHash(runtime);
int tagstart = 0;
int mark_pfx = 0;
int mark_stg = 0;
int mark_attr = 0;
int mark_nat = 0;
int mark_vat = 0;
String nat = "";
String vat = "";
int cs;
int p = 0;
int pe = data.length;
int eof = pe;
int act;
int ts;
int te;
rv = RubyArray.newArray(runtime);
char[] remainder = data;
// line 351 "JavaScanner.java"
{
cs = parser_start;
ts = -1;
te = -1;
act = 0;
}
// line 175 "JavaScanner.rl"
// line 361 "JavaScanner.java"
{
int _klen;
int _trans = 0;
int _acts;
int _nacts;
int _keys;
int _goto_targ = 0;
_goto: while (true) {
switch ( _goto_targ ) {
case 0:
if ( p == pe ) {
_goto_targ = 4;
continue _goto;
}
if ( cs == 0 ) {
_goto_targ = 5;
continue _goto;
}
case 1:
_acts = _parser_from_state_actions[cs];
_nacts = (int) _parser_actions[_acts++];
while ( _nacts-- > 0 ) {
switch ( _parser_actions[_acts++] ) {
case 15:
// line 1 "NONE"
{ts = p;}
break;
// line 390 "JavaScanner.java"
}
}
_match: do {
_keys = _parser_key_offsets[cs];
_trans = _parser_index_offsets[cs];
_klen = _parser_single_lengths[cs];
if ( _klen > 0 ) {
int _lower = _keys;
int _mid;
int _upper = _keys + _klen - 1;
while (true) {
if ( _upper < _lower )
break;
_mid = _lower + ((_upper-_lower) >> 1);
if ( data[p] < _parser_trans_keys[_mid] )
_upper = _mid - 1;
else if ( data[p] > _parser_trans_keys[_mid] )
_lower = _mid + 1;
else {
_trans += (_mid - _keys);
break _match;
}
}
_keys += _klen;
_trans += _klen;
}
_klen = _parser_range_lengths[cs];
if ( _klen > 0 ) {
int _lower = _keys;
int _mid;
int _upper = _keys + (_klen<<1) - 2;
while (true) {
if ( _upper < _lower )
break;
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( data[p] < _parser_trans_keys[_mid] )
_upper = _mid - 2;
else if ( data[p] > _parser_trans_keys[_mid+1] )
_lower = _mid + 2;
else {
_trans += ((_mid - _keys)>>1);
break _match;
}
}
_trans += _klen;
}
} while (false);
_trans = _parser_indicies[_trans];
case 3:
cs = _parser_trans_targs[_trans];
if ( _parser_trans_actions[_trans] != 0 ) {
_acts = _parser_trans_actions[_trans];
_nacts = (int) _parser_actions[_acts++];
while ( _nacts-- > 0 )
{
switch ( _parser_actions[_acts++] )
{
case 0:
// line 4 "JavaScanner.rl"
{ mark_pfx = p; }
break;
case 1:
// line 5 "JavaScanner.rl"
{
prefix = input.substring(mark_pfx, p);
}
break;
case 2:
// line 8 "JavaScanner.rl"
{
if ( !prefix.equals(tag_prefix) ) {
// have to manually add ':' / Sep
// pass the text through & reset state
pass_through(input.substring(tagstart, p) + ":");
prefix = "";
{cs = 49; _goto_targ = 2; if (true) continue _goto;}
}
}
break;
case 3:
// line 18 "JavaScanner.rl"
{ mark_stg = p; }
break;
case 4:
// line 19 "JavaScanner.rl"
{ name = input.substring(mark_stg, p); }
break;
case 5:
// line 20 "JavaScanner.rl"
{ mark_attr = p; }
break;
case 6:
// line 21 "JavaScanner.rl"
{
attributes.op_aset(
runtime.getCurrentContext(),
RubyString.newString(runtime, nat),
RubyString.newString(runtime, vat)
);
}
break;
case 7:
// line 29 "JavaScanner.rl"
{ mark_nat = p; }
break;
case 8:
// line 30 "JavaScanner.rl"
{ nat = input.substring(mark_nat, p); }
break;
case 9:
// line 31 "JavaScanner.rl"
{ mark_vat = p; }
break;
case 10:
// line 32 "JavaScanner.rl"
{ vat = input.substring(mark_vat, p); }
break;
case 11:
// line 34 "JavaScanner.rl"
{ flavor = RubySymbol.newSymbol(runtime, "open".intern()); }
break;
case 12:
// line 35 "JavaScanner.rl"
{ flavor = RubySymbol.newSymbol(runtime, "self".intern()); }
break;
case 13:
// line 36 "JavaScanner.rl"
{ flavor = RubySymbol.newSymbol(runtime, "close".intern()); }
break;
case 16:
// line 1 "NONE"
{te = p+1;}
break;
case 17:
// line 72 "JavaScanner.rl"
{act = 1;}
break;
case 18:
// line 79 "JavaScanner.rl"
{act = 2;}
break;
case 19:
// line 79 "JavaScanner.rl"
{te = p+1;{
pass_through(input.substring(p, p + 1));
tagstart = p + 1;
}}
break;
case 20:
// line 72 "JavaScanner.rl"
{te = p;p--;{
tag(prefix, name, attributes, flavor);
prefix = "";
name = "";
attributes = RubyHash.newHash(runtime);
flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
}}
break;
case 21:
// line 79 "JavaScanner.rl"
{te = p;p--;{
pass_through(input.substring(p, p + 1));
tagstart = p + 1;
}}
break;
case 22:
// line 79 "JavaScanner.rl"
{{p = ((te))-1;}{
pass_through(input.substring(p, p + 1));
tagstart = p + 1;
}}
break;
case 23:
// line 1 "NONE"
{ switch( act ) {
case 1:
{{p = ((te))-1;}
tag(prefix, name, attributes, flavor);
prefix = "";
name = "";
attributes = RubyHash.newHash(runtime);
flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
}
break;
case 2:
{{p = ((te))-1;}
pass_through(input.substring(p, p + 1));
tagstart = p + 1;
}
break;
}
}
break;
// line 590 "JavaScanner.java"
}
}
}
case 2:
_acts = _parser_to_state_actions[cs];
_nacts = (int) _parser_actions[_acts++];
while ( _nacts-- > 0 ) {
switch ( _parser_actions[_acts++] ) {
case 14:
// line 1 "NONE"
{ts = -1;}
break;
// line 604 "JavaScanner.java"
}
}
if ( cs == 0 ) {
_goto_targ = 5;
continue _goto;
}
if ( ++p != pe ) {
_goto_targ = 1;
continue _goto;
}
case 4:
if ( p == eof )
{
if ( _parser_eof_trans[cs] > 0 ) {
_trans = _parser_eof_trans[cs] - 1;
_goto_targ = 3;
continue _goto;
}
}
case 5:
}
break; }
}
// line 176 "JavaScanner.rl"
return rv;
}
}

View File

@ -0,0 +1,179 @@
%%{
machine parser;
action _prefix { mark_pfx = p; }
action prefix {
prefix = input.substring(mark_pfx, p);
}
action _check_prefix {
if ( !prefix.equals(tag_prefix) ) {
// have to manually add ':' / Sep
// pass the text through & reset state
pass_through(input.substring(tagstart, p) + ":");
prefix = "";
fgoto main;
}
}
action _starttag { mark_stg = p; }
action starttag { name = input.substring(mark_stg, p); }
action _attr { mark_attr = p; }
action attr {
attributes.op_aset(
runtime.getCurrentContext(),
RubyString.newString(runtime, nat),
RubyString.newString(runtime, vat)
);
}
action _nameattr { mark_nat = p; }
action nameattr { nat = input.substring(mark_nat, p); }
action _valattr { mark_vat = p; }
action valattr { vat = input.substring(mark_vat, p); }
action opentag { flavor = RubySymbol.newSymbol(runtime, "open".intern()); }
action selftag { flavor = RubySymbol.newSymbol(runtime, "self".intern()); }
action closetag { flavor = RubySymbol.newSymbol(runtime, "close".intern()); }
Closeout := empty;
# words
PrefixChar = [\-A-Za-z0-9._?] ;
NameChar = [\-A-Za-z0-9._:?] ;
TagName = NameChar+ >_starttag %starttag;
Prefix = PrefixChar+ >_prefix %prefix;
Open = "<";
Sep = ":" >_check_prefix;
End = "/";
Close = ">";
Name = Prefix Sep TagName;
NameAttr = NameChar+ >_nameattr %nameattr;
Q1Char = ( "\\\'" | [^'] ) ;
Q1Attr = Q1Char* >_valattr %valattr;
Q2Char = ( "\\\"" | [^"] ) ;
Q2Attr = Q2Char* >_valattr %valattr;
Attr = NameAttr space* "=" space* ('"' Q2Attr '"' | "'" Q1Attr "'") space* >_attr %attr;
Attrs = (space+ Attr* | empty);
CloseTrailer = End Close %selftag;
OpenTrailer = Close %opentag;
Trailer = (OpenTrailer | CloseTrailer);
OpenOrSelfTag = Name Attrs? Trailer;
CloseTag = End Name space* Close %closetag;
SomeTag = Open (OpenOrSelfTag | CloseTag);
main := |*
SomeTag => {
tag(prefix, name, attributes, flavor);
prefix = "";
name = "";
attributes = RubyHash.newHash(runtime);
flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
};
any => {
pass_through(input.substring(p, p + 1));
tagstart = p + 1;
};
*|;
}%%
package radius.parser;
import java.util.HashMap;
import java.util.LinkedList;
import org.jruby.Ruby; // runtime
import org.jruby.RubyObject;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.RubyArray;
import org.jruby.RubyString;
import org.jruby.RubyHash;
import org.jruby.RubySymbol;
public class JavaScanner {
Ruby runtime = null;
RubyArray rv = null;
void pass_through(String str) {
RubyObject last = ((RubyObject)rv.last());
if ( rv.size() > 0 && last != null && (last instanceof RubyString) ){
// XXX concat changes for ruby 1.9
((RubyString) last).concat(RubyString.newString(runtime, str));
} else {
rv.append(RubyString.newString(runtime, str));
}
}
void tag(String prefix, String name, RubyHash attr, RubySymbol flavor) {
RubyHash tag = RubyHash.newHash(runtime);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "prefix"),
RubyString.newString(runtime, prefix)
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "name"),
RubyString.newString(runtime, name)
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "attrs"),
attr
);
tag.op_aset(
runtime.getCurrentContext(),
RubySymbol.newSymbol(runtime, "flavor"),
flavor
);
rv.append(tag);
}
public JavaScanner(Ruby runtime) {
this.runtime = runtime;
}
%% write data;
public RubyArray operate(String tag_prefix, String input) {
char[] data = input.toCharArray();
String disposable_string;
String name = "";
String prefix = "";
RubySymbol flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
RubyHash attributes = RubyHash.newHash(runtime);
int tagstart = 0;
int mark_pfx = 0;
int mark_stg = 0;
int mark_attr = 0;
int mark_nat = 0;
int mark_vat = 0;
String nat = "";
String vat = "";
int cs;
int p = 0;
int pe = data.length;
int eof = pe;
int act;
int ts;
int te;
rv = RubyArray.newArray(runtime);
char[] remainder = data;
%% write init;
%% write exec;
return rv;
}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
%%{
machine parser;
action _prefix { mark_pfx = p }
action prefix {
if data[mark_pfx..p-1] != @prefix
closing = data[mark_pfx-1,1] == '/'
@nodes.last << data[mark_pfx-(closing ? 2 : 1)..p]
fbreak;
end
}
action _starttag { mark_stg = p }
action starttag { @starttag = data[mark_stg..p-1] }
action _attr { mark_attr = p }
action attr {
@attrs[@nat] = @vat
}
action prematch {
@prematch_end = p
@prematch = data[0..p] if p > 0
}
action _nameattr { mark_nat = p }
action nameattr { @nat = data[mark_nat..p-1] }
action _valattr { mark_vat = p }
action valattr { @vat = data[mark_vat..p-1] }
action opentag { @flavor = :open }
action selftag { @flavor = :self }
action closetag { @flavor = :close }
action stopparse {
@cursor = p;
fbreak;
}
Closeout := empty;
# words
PrefixChar = [\-A-Za-z0-9._?] ;
NameChar = [\-A-Za-z0-9._:?] ;
TagName = NameChar+ >_starttag %starttag;
Prefix = PrefixChar+ >_prefix %prefix;
Name = Prefix ":" TagName;
NameAttr = NameChar+ >_nameattr %nameattr;
Q1Char = ( "\\\'" | [^'] ) ;
Q1Attr = Q1Char* >_valattr %valattr;
Q2Char = ( "\\\"" | [^"] ) ;
Q2Attr = Q2Char* >_valattr %valattr;
Attr = NameAttr space* "=" space* ('"' Q2Attr '"' | "'" Q1Attr "'") space* >_attr %attr;
Attrs = (space+ Attr* | empty);
CloseTrailer = "/>" %selftag;
OpenTrailer = ">" %opentag;
Trailer = (OpenTrailer | CloseTrailer);
OpenOrSelfTag = Name Attrs? Trailer;
CloseTag = "/" Name space* ">" %closetag;
SomeTag = '<' (OpenOrSelfTag | CloseTag);
main := |*
SomeTag => {
tag = {:prefix=>@prefix, :name=>@starttag, :flavor => @flavor, :attrs => @attrs}
@prefix = nil
@name = nil
@flavor = :tasteless
@attrs = {}
@nodes << tag << ''
fbreak;
};
any => {
@nodes.last << data[p]
@tagstart = p
};
*|;
}%%
module Radius
class Scanner
def operate(prefix, data)
data = Radius::OrdString.new data
buf = ""
csel = ""
@prematch = ''
@starttag = nil
@attrs = {}
@flavor = :tasteless
@cursor = 0
@tagstart = 0
@nodes = ['']
remainder = data.dup
until remainder.length == 0
p = perform_parse(prefix, remainder)
remainder = remainder[p..-1]
end
return @nodes
end
private
def perform_parse(prefix, data)
stack = []
p = 0
ts = 0
te = 0
act = 0
eof = data.length
@prefix = prefix
%% write data;
%% write init;
%% write exec;
return p
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
%%{
machine parser;
# action _prefix { mark_pfx = p }
# action prefix {
# if data[mark_pfx..p-1] != @prefix
# @nodes.last << data[mark_pfx-1..p]
# fbreak;
# end
# }
action _starttag { mark_stg = p }
action starttag { @starttag = data[mark_stg..p-1] }
action _attr { mark_attr = p }
action attr {
@attrs[@nat] = @vat
}
action prematch {
@prematch_end = p
@prematch = data[0..p] if p > 0
}
action _nameattr { mark_nat = p }
action nameattr { @nat = data[mark_nat..p-1] }
action _valattr { mark_vat = p }
action valattr { @vat = data[mark_vat..p-1] }
action opentag { @flavor = :open }
action selftag { @flavor = :self }
action closetag { @flavor = :close }
action stopparse {
@cursor = p;
fbreak;
}
Closeout := empty;
# words
# PrefixChar = [\-A-Za-z0-9._?] ;
NameChar = [\-A-Za-z0-9._:?] ;
TagName = NameChar+ >_starttag %starttag;
# Prefix = PrefixChar+ >_prefix %prefix;
# Name = Prefix ":" TagName;
NameAttr = NameChar+ >_nameattr %nameattr;
Q1Char = ( "\\\'" | [^'] ) ;
Q1Attr = Q1Char* >_valattr %valattr;
Q2Char = ( "\\\"" | [^"] ) ;
Q2Attr = Q2Char* >_valattr %valattr;
Attr = NameAttr space* "=" space* ('"' Q2Attr '"' | "'" Q1Attr "'") space* >_attr %attr;
Attrs = (space+ Attr* | empty);
CloseTrailer = "/}" %selftag;
OpenTrailer = "}" %opentag;
Trailer = (OpenTrailer | CloseTrailer);
# OpenOrSelfTag = Name Attrs? Trailer;
OpenOrSelfTag = TagName Attrs? Trailer;
# CloseTag = "/" Name space* "}" %closetag;
CloseTag = "/" TagName space* "}" %closetag;
SomeTag = '{' space* (OpenOrSelfTag | CloseTag);
main := |*
SomeTag => {
tag = {:prefix=>@prefix, :name=>@starttag, :flavor => @flavor, :attrs => @attrs}
@prefix = nil
@name = nil
@flavor = :tasteless
@attrs = {}
@nodes << tag << ''
fbreak;
};
any => {
@nodes.last << data[p]
@tagstart = p
};
*|;
}%%
module Radius
class SquiggleScanner
def operate(prefix, data)
data = Radius::OrdString.new data
buf = ""
csel = ""
@prematch = ''
@starttag = nil
@attrs = {}
@flavor = :tasteless
@cursor = 0
@tagstart = 0
@nodes = ['']
remainder = data.dup
until remainder.length == 0
p = perform_parse(prefix, remainder)
remainder = remainder[p..-1]
end
return @nodes
end
private
def perform_parse(prefix, data)
stack = []
p = 0
ts = 0
te = 0
act = 0
eof = data.length
@prefix = prefix
%% write data;
%% write init;
%% write exec;
return p
end
end
end

View File

@ -0,0 +1,71 @@
module Radius
#
# A tag binding is passed into each tag definition and contains helper methods for working
# with tags. Use it to gain access to the attributes that were passed to the tag, to
# render the tag contents, and to do other tasks.
#
class TagBinding
# The Context that the TagBinding is associated with. Used internally. Try not to use
# this object directly.
attr_reader :context
# The locals object for the current tag.
attr_reader :locals
# The name of the tag (as used in a template string).
attr_reader :name
# The attributes of the tag. Also aliased as TagBinding#attr.
attr_reader :attributes
alias :attr :attributes
# The render block. When called expands the contents of the tag. Use TagBinding#expand
# instead.
attr_reader :block
# Creates a new TagBinding object.
def initialize(context, locals, name, attributes, block)
@context, @locals, @name, @attributes, @block = context, locals, name, attributes, block
end
# Evaluates the current tag and returns the rendered contents.
def expand
double? ? block.call : ''
end
# Returns true if the current tag is a single tag.
def single?
block.nil?
end
# Returns true if the current tag is a container tag.
def double?
not single?
end
# The globals object from which all locals objects ultimately inherit their values.
def globals
@context.globals
end
# Returns a list of the way tags are nested around the current tag as a string.
def nesting
@context.current_nesting
end
# Fires off Context#tag_missing for the current tag.
def missing!
@context.tag_missing(name, attributes, &block)
end
# Renders the tag using the current context .
def render(tag, attributes = {}, &block)
@context.render_tag(tag, attributes, &block)
end
# Shortcut for accessing tag.attr[key]
def [](key)
attr[key]
end
end
end

View File

@ -0,0 +1,78 @@
module Radius
module TagDefinitions # :nodoc:
class TagFactory # :nodoc:
def initialize(context)
@context = context
end
def define_tag(name, options, &block)
options = prepare_options(name, options)
validate_params(name, options, &block)
construct_tag_set(name, options, &block)
expose_methods_as_tags(name, options)
end
protected
# Adds the tag definition to the context. Override in subclasses to add additional tags
# (child tags) when the tag is created.
def construct_tag_set(name, options, &block)
if block
@context.definitions[name.to_s] = block
else
lp = last_part(name)
@context.define_tag(name) do |tag|
if tag.single?
options[:for]
else
tag.locals.send("#{ lp }=", options[:for]) unless options[:for].nil?
tag.expand
end
end
end
end
# Normalizes options pased to tag definition. Override in decendants to preform
# additional normalization.
def prepare_options(name, options)
options = Utility.symbolize_keys(options)
options[:expose] = expand_array_option(options[:expose])
object = options[:for]
options[:attributes] = object.respond_to?(:attributes) unless options.has_key? :attributes
options[:expose] += object.attributes.keys if options[:attributes]
options
end
# Validates parameters passed to tag definition. Override in decendants to add custom
# validations.
def validate_params(name, options, &block)
unless options.has_key? :for
raise ArgumentError.new("tag definition must contain a :for option or a block") unless block
raise ArgumentError.new("tag definition must contain a :for option when used with the :expose option") unless options[:expose].empty?
end
end
# Exposes the methods of an object as child tags.
def expose_methods_as_tags(name, options)
options[:expose].each do |method|
tag_name = "#{name}:#{method}"
lp = last_part(name)
@context.define_tag(tag_name) do |tag|
object = tag.locals.send(lp)
object.send(method)
end
end
end
protected
def expand_array_option(value)
[*value].compact.map { |m| m.to_s.intern }
end
def last_part(name)
name.split(':').last
end
end
end
end

View File

@ -0,0 +1,40 @@
module Radius
module Utility # :nodoc:
def self.symbolize_keys(hash)
new_hash = {}
hash.keys.each do |k|
new_hash[k.to_s.intern] = hash[k]
end
new_hash
end
def self.impartial_hash_delete(hash, key)
string = key.to_s
symbol = string.intern
value1 = hash.delete(symbol)
value2 = hash.delete(string)
value1 || value2
end
def self.constantize(camelized_string)
raise "invalid constant name `#{camelized_string}'" unless camelized_string.split('::').all? { |part| part =~ /^[A-Za-z]+$/ }
Object.module_eval(camelized_string)
end
def self.camelize(underscored_string)
string = ''
underscored_string.split('_').each { |part| string << part.capitalize }
string
end
if RUBY_VERSION[0,3] == '1.8'
def self.array_to_s(c)
c.to_s
end
else
def self.array_to_s(c)
c.map{|x| x.is_a?(Array) ? array_to_s(x) : x.to_s }.join
end
end
end
end

View File

@ -0,0 +1,8 @@
module Radius #:nodoc:
def self.version
@version ||= begin
filename = File.join(File.dirname(__FILE__), '..', '..', 'VERSION')
IO.read(filename).strip
end
end
end

91
vendor/plugins/radius/radius.gemspec vendored Normal file
View File

@ -0,0 +1,91 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{radius}
s.version = "0.7.0.prerelease"
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
s.authors = ["John W. Long (me@johnwlong.com)", "David Chelimsky (dchelimsky@gmail.com)", "Bryce Kerley (bkerley@brycekerley.net)"]
s.date = %q{2010-05-24}
s.description = %q{Radius is a powerful tag-based template language for Ruby inspired by the template languages used in MovableType and TextPattern. It uses tags similar to XML, but can be used to generate any form of plain text (HTML, e-mail, etc...).}
s.email = %q{me@johnwlong.com}
s.extra_rdoc_files = [
"CHANGELOG",
"LICENSE",
"QUICKSTART.rdoc",
"README.rdoc"
]
s.files = [
"CHANGELOG",
"LICENSE",
"QUICKSTART.rdoc",
"README.rdoc",
"Rakefile",
"VERSION",
"lib/radius.rb",
"lib/radius/context.rb",
"lib/radius/delegating_open_struct.rb",
"lib/radius/error.rb",
"lib/radius/parse_tag.rb",
"lib/radius/parser.rb",
"lib/radius/parser/JavaScanner$Flavor.class",
"lib/radius/parser/JavaScanner$Tag.class",
"lib/radius/parser/JavaScanner.class",
"lib/radius/parser/JavaScanner.java",
"lib/radius/parser/JavaScanner.rl",
"lib/radius/parser/java_scanner.jar",
"lib/radius/parser/scanner.rb",
"lib/radius/parser/scanner.rl",
"lib/radius/parser/squiggle_scanner.rb",
"lib/radius/parser/squiggle_scanner.rl",
"lib/radius/tag_binding.rb",
"lib/radius/tag_definitions.rb",
"lib/radius/utility.rb",
"lib/radius/version.rb",
"tasks/jeweler.rake",
"tasks/rdoc.rake",
"tasks/rubinius.rake",
"tasks/scan.rake",
"tasks/test.rake",
"test/benchmarks.rb",
"test/context_test.rb",
"test/multithreaded_test.rb",
"test/parser_test.rb",
"test/quickstart_test.rb",
"test/squiggle_test.rb",
"test/test_helper.rb",
"test/utility_test.rb"
]
s.homepage = %q{http://github.com/jlong/radius}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.6}
s.summary = %q{A tag-based templating language for Ruby.}
s.test_files = [
"test/context_test.rb",
"test/parser_test.rb",
"test/quickstart_test.rb",
"test/test_helper.rb",
"test/multithreaded_test.rb",
"test/squiggle_test.rb",
"test/utility_test.rb",
"test/benchmarks.rb"
]
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<RedCloth>, [">= 0"])
else
s.add_dependency(%q<RedCloth>, [">= 0"])
end
else
s.add_dependency(%q<RedCloth>, [">= 0"])
end
end

View File

@ -0,0 +1,22 @@
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "radius"
gem.summary = "A tag-based templating language for Ruby."
gem.description = "Radius is a powerful tag-based template language for Ruby inspired by the template languages used in MovableType and TextPattern. It uses tags similar to XML, but can be used to generate any form of plain text (HTML, e-mail, etc...)."
gem.email = "me@johnwlong.com"
gem.homepage = "http://github.com/jlong/radius"
gem.authors = [
"John W. Long (me@johnwlong.com)",
"David Chelimsky (dchelimsky@gmail.com)",
"Bryce Kerley (bkerley@brycekerley.net)"
]
gem.files = FileList["[A-Z]*", "{bin,lib,tasks,test}/**/*"].exclude("tmp")
gem.extra_rdoc_files = ['README.rdoc', 'QUICKSTART.rdoc', 'LICENSE', 'CHANGELOG']
gem.add_development_dependency('RedCloth')
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end

13
vendor/plugins/radius/tasks/rdoc.rake vendored Normal file
View File

@ -0,0 +1,13 @@
require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "Radius #{version}"
rdoc.main = "README.rdoc"
rdoc.rdoc_files.include('*.rdoc')
rdoc.rdoc_files.include('LICENSE')
rdoc.rdoc_files.include('CHANGELOG')
rdoc.rdoc_files.include('QUICKSTART.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View File

@ -0,0 +1,4 @@
desc "remove Rubinius rbc files"
task "rubinius:clean" do
(Dir['**/*.rbc']).each { |f| rm f }
end

79
vendor/plugins/radius/tasks/scan.rake vendored Normal file
View File

@ -0,0 +1,79 @@
namespace :scan do
desc 'Generate the parsers'
task 'build' => [
'lib/radius/parser/scanner.rb',
'lib/radius/parser/squiggle_scanner.rb',
'lib/radius/parser/java_scanner.jar'
]
desc 'Generate a PDF state graph from the parsers'
task 'graph' => ['doc/scanner.pdf', 'doc/squiggle_scanner.pdf']
desc 'turn the scanner.rl file into a ruby file'
file 'lib/radius/parser/scanner.rb' => 'lib/radius/parser/scanner.rl' do |t|
cd 'lib/radius/parser' do
sh "ragel -R -F1 scanner.rl"
end
end
desc 'turn the squiggle_scanner.rl file into a ruby file'
file 'lib/radius/parser/squiggle_scanner.rb' =>
['lib/radius/parser/squiggle_scanner.rl'] \
do |t|
cd 'lib/radius/parser' do
sh "ragel -R -F1 squiggle_scanner.rl"
end
end
desc 'package JavaScanner into a jar file'
file 'lib/radius/parser/java_scanner.jar' => 'lib/radius/parser/JavaScanner.class' do
cd 'lib' do
sh "jar -cf radius/parser/java_scanner.jar radius/parser/*.class"
end
end
desc 'turn the JavaScanner.java file into a java class file'
file 'lib/radius/parser/JavaScanner.class' => 'lib/radius/parser/JavaScanner.java' do |t|
cd 'lib' do
jruby_path = ENV['JRUBY_HOME'] || '/usr/local/jruby/current'
sh "javac -cp #{jruby_path}/lib/jruby.jar radius/parser/JavaScanner.java"
end
end
desc 'turn the JavaScanner.rl file into a java source file'
file 'lib/radius/parser/JavaScanner.java' => 'lib/radius/parser/JavaScanner.rl' do |t|
cd 'lib/radius/parser' do
sh "ragel -J -F1 JavaScanner.rl"
end
end
desc 'pdf of the ragel scanner'
file 'doc/scanner.pdf' => 'lib/radius/parser/scanner.dot' do |t|
cd 'lib/radius/parser' do
sh "dot -Tpdf -o ../../../doc/scanner.pdf scanner.dot"
end
end
desc 'pdf of the ragel squiggle scanner'
file 'doc/squiggle_scanner.pdf' =>
['lib/radius/parser/squiggle_scanner.dot'] \
do |t|
cd 'lib/radius/parser' do
sh "dot -Tpdf -o ../../../doc/squiggle_scanner.pdf squiggle_scanner.dot"
end
end
file 'lib/radius/parser/scanner.dot' => 'lib/radius/parser/scanner.rl' do |t|
cd 'lib/radius/parser' do
sh "ragel -Vp scanner.rl > scanner.dot"
end
end
file 'lib/radius/parser/squiggle_scanner.dot' =>
['lib/radius/parser/squiggle_scanner.rl'] \
do |t|
cd 'lib/radius/parser' do
sh "ragel -Vp squiggle_scanner.rl > squiggle_scanner.dot"
end
end
end

7
vendor/plugins/radius/tasks/test.rake vendored Normal file
View File

@ -0,0 +1,7 @@
require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs << "lib" << "test"
t.test_files = FileList['test/*_test.rb']
t.verbose = true
end

View File

@ -0,0 +1,35 @@
$: << File.join(File.dirname(__FILE__), '..', 'lib')
require 'radius'
if RUBY_PLATFORM == 'java'
require 'java'
require 'radius/parser/jscanner'
end
require 'benchmark'
document = <<EOF
Before it all
<r:foo>
Middle Top
<r:bar />
Middle Bottom
</r:foo>
After it all
EOF
amount = 1000
Benchmark.bmbm do |bm|
bm.report('vanilla') do
scanner = Radius::Scanner.new(:scanner => Radius::Scanner)
amount.times { scanner.operate('r', document) }
end
if RUBY_PLATFORM == 'java'
bm.report('JavaScanner') do
scanner = Radius::JavaScanner.new(JRuby.runtime)
amount.times { scanner.operate('r', document) }
end
end
end

View File

@ -0,0 +1,61 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
class RadiusContextTest < Test::Unit::TestCase
include RadiusTestHelper
def setup
@context = new_context
end
def test_initialize
@context = Radius::Context.new
end
def test_initialize_with_block
@context = Radius::Context.new do |c|
assert_kind_of Radius::Context, c
c.define_tag('test') { 'just a test' }
end
assert_not_equal Hash.new, @context.definitions
end
def test_with
got = @context.with do |c|
assert_equal @context, c
end
assert_equal @context, got
end
def test_render_tag
define_global_tag "hello" do |tag|
"Hello #{tag.attr['name'] || 'World'}!"
end
assert_render_tag_output 'Hello World!', 'hello'
assert_render_tag_output 'Hello John!', 'hello', 'name' => 'John'
end
def test_render_tag__undefined_tag
e = assert_raises(Radius::UndefinedTagError) { @context.render_tag('undefined_tag') }
assert_equal "undefined tag `undefined_tag'", e.message
end
def test_tag_missing
class << @context
def tag_missing(tag, attr, &block)
"undefined tag `#{tag}' with attributes #{attr.inspect}"
end
end
text = ''
expected = %{undefined tag `undefined_tag' with attributes {"cool"=>"beans"}}
assert_nothing_raised { text = @context.render_tag('undefined_tag', 'cool' => 'beans') }
assert_equal expected, text
end
private
def assert_render_tag_output(output, *render_tag_params)
assert_equal output, @context.render_tag(*render_tag_params)
end
end

View File

@ -0,0 +1,62 @@
require 'test/unit'
require 'radius'
class MultithreadTest < Test::Unit::TestCase
def setup
Thread.abort_on_exception
@context = Radius::Context.new do |c|
c.define_tag('thread') do |tag|
"#{tag.locals.thread_id} / #{tag.globals.object_id}"
end
end
end
if RUBY_PLATFORM == 'java'
require 'java'
# call once before the thread to keep from using hidden require in a thread
Radius::Parser.new
def test_runs_multithreaded
lock = java.lang.String.new("lock")
threads = []
1000.times do |t|
thread = Thread.new do
parser = Radius::Parser.new(@context, :tag_prefix => 'r')
parser.context.globals.thread_id = Thread.current.object_id
expected = "#{Thread.current.object_id} / "+
"#{parser.context.globals.object_id}"
actual = parser.parse('<r:thread />')
assert_equal expected, actual
end
lock.synchronized do
threads << thread
end
end
lock.synchronized do
threads.each{|t| t.join }
end
end
else
def test_runs_multithreaded
threads = []
mute = Mutex.new
1000.times do |t|
thread = Thread.new do
parser = Radius::Parser.new(@context, :tag_prefix => 'r')
parser.context.globals.thread_id = Thread.current.object_id
expected = "#{Thread.current.object_id} / "+
"#{parser.context.globals.object_id}"
actual = parser.parse('<r:thread />')
assert_equal expected, actual
end
mute.synchronize do
threads << thread
end
end
mute.synchronize do
threads.each{|t| t.join }
end
end
end
end

View File

@ -0,0 +1,18 @@
require 'test/unit'
require 'radius'
class RadiusOrdStringTest < Test::Unit::TestCase
def test_string_slice_integer
str = Radius::OrdString.new "abc"
assert_equal str[0], 97
assert_equal str[1], 98
assert_equal str[2], 99
end
def test_string_slice_range
str = Radius::OrdString.new "abc"
assert_equal str[0..-1], "abc"
end
end

View File

@ -0,0 +1,307 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
class RadiusParserTest < Test::Unit::TestCase
include RadiusTestHelper
def setup
@context = new_context
@parser = Radius::Parser.new(@context, :tag_prefix => 'r')
end
def test_initialize
@parser = Radius::Parser.new
assert_kind_of Radius::Context, @parser.context
end
def test_initialize_with_params
@parser = Radius::Parser.new(TestContext.new)
assert_kind_of TestContext, @parser.context
@parser = Radius::Parser.new(:context => TestContext.new)
assert_kind_of TestContext, @parser.context
@parser = Radius::Parser.new('context' => TestContext.new)
assert_kind_of TestContext, @parser.context
@parser = Radius::Parser.new(:tag_prefix => 'r')
assert_kind_of Radius::Context, @parser.context
assert_equal 'r', @parser.tag_prefix
@parser = Radius::Parser.new(TestContext.new, :tag_prefix => 'r')
assert_kind_of TestContext, @parser.context
assert_equal 'r', @parser.tag_prefix
end
def test_parse_individual_tags_and_parameters
define_tag "add" do |tag|
tag.attr["param1"].to_i + tag.attr["param2"].to_i
end
assert_parse_output "<3>", %{<<r:add param1="1" param2='2'/>>}
end
def test_parse_attributes
attributes = %{{"a"=>"1", "b"=>"2", "c"=>"3", "d"=>"'"}}
assert_parse_output attributes, %{<r:attr a="1" b='2'c="3"d="'" />}
assert_parse_output attributes, %{<r:attr a="1" b='2'c="3"d="'"></r:attr>}
end
def test_parse_attributes_with_slashes_or_angle_brackets
slash = %{{"slash"=>"/"}}
angle = %{{"angle"=>">"}}
assert_parse_output slash, %{<r:attr slash="/"></r:attr>}
assert_parse_output slash, %{<r:attr slash="/"><r:attr /></r:attr>}
assert_parse_output angle, %{<r:attr angle=">"></r:attr>}
end
def test_parse_quotes
assert_parse_output "test []", %{<r:echo value="test" /> <r:wrap attr="test"></r:wrap>}
end
def test_things_that_should_be_left_alone
[
%{ test="2"="4" },
%{="2" }
].each do |middle|
assert_parsed_is_unchanged "<r:attr#{middle}/>"
assert_parsed_is_unchanged "<r:attr#{middle}>"
end
end
def test_tags_inside_html_tags
assert_parse_output %{<div class="xzibit">tags in yo tags</div>},
%{<div class="<r:reverse>tibizx</r:reverse>">tags in yo tags</div>}
end
def test_parse_result_is_always_a_string
define_tag("twelve") { 12 }
assert_parse_output "12", "<r:twelve />"
end
def test_parse_double_tags
assert_parse_output "test".reverse, "<r:reverse>test</r:reverse>"
assert_parse_output "tset TEST", "<r:reverse>test</r:reverse> <r:capitalize>test</r:capitalize>"
end
def test_parse_tag_nesting
define_tag("parent", :for => '')
define_tag("parent:child", :for => '')
define_tag("extra", :for => '')
define_tag("nesting") { |tag| tag.nesting }
define_tag("extra:nesting") { |tag| tag.nesting.gsub(':', ' > ') }
define_tag("parent:child:nesting") { |tag| tag.nesting.gsub(':', ' * ') }
assert_parse_output "nesting", "<r:nesting />"
assert_parse_output "parent:nesting", "<r:parent:nesting />"
assert_parse_output "extra > nesting", "<r:extra:nesting />"
assert_parse_output "parent * child * nesting", "<r:parent:child:nesting />"
assert_parse_output "parent > extra > nesting", "<r:parent:extra:nesting />"
assert_parse_output "parent > child > extra > nesting", "<r:parent:child:extra:nesting />"
assert_parse_output "parent * extra * child * nesting", "<r:parent:extra:child:nesting />"
assert_parse_output "parent > extra > child > extra > nesting", "<r:parent:extra:child:extra:nesting />"
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>"
assert_parse_output "extra * parent * child * nesting", "<r:extra:parent:child:nesting />"
assert_parse_output "extra > parent > nesting", "<r:extra><r:parent:nesting /></r:extra>"
assert_parse_output "extra * parent * child * nesting", "<r:extra:parent><r:child:nesting /></r:extra:parent>"
assert_raises(Radius::UndefinedTagError) { @parser.parse("<r:child />") }
end
def test_parse_tag_nesting_2
define_tag("parent", :for => '')
define_tag("parent:child", :for => '')
define_tag("content") { |tag| tag.nesting }
assert_parse_output 'parent:child:content', '<r:parent><r:child:content /></r:parent>'
end
def test_parse_tag__binding_do_missing
define_tag 'test' do |tag|
tag.missing!
end
e = assert_raises(Radius::UndefinedTagError) { @parser.parse("<r:test />") }
assert_equal "undefined tag `test'", e.message
end
def test_parse_chirpy_bird
# :> chirp chirp
assert_parse_output "<:", "<:"
end
def test_parse_tag__binding_render_tag
define_tag('test') { |tag| "Hello #{tag.attr['name']}!" }
define_tag('hello') { |tag| tag.render('test', tag.attr) }
assert_parse_output 'Hello John!', '<r:hello name="John" />'
end
def test_accessing_tag_attributes_through_tag_indexer
define_tag('test') { |tag| "Hello #{tag['name']}!" }
assert_parse_output 'Hello John!', '<r:test name="John" />'
end
def test_parse_tag__binding_render_tag_with_block
define_tag('test') { |tag| "Hello #{tag.expand}!" }
define_tag('hello') { |tag| tag.render('test') { tag.expand } }
assert_parse_output 'Hello John!', '<r:hello>John</r:hello>'
end
def test_tag_locals
define_tag "outer" do |tag|
tag.locals.var = 'outer'
tag.expand
end
define_tag "outer:inner" do |tag|
tag.locals.var = 'inner'
tag.expand
end
define_tag "outer:var" do |tag|
tag.locals.var
end
assert_parse_output 'outer', "<r:outer><r:var /></r:outer>"
assert_parse_output 'outer:inner:outer', "<r:outer><r:var />:<r:inner><r:var /></r:inner>:<r:var /></r:outer>"
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>"
assert_parse_output 'outer', "<r:outer:var />"
end
def test_tag_globals
define_tag "set" do |tag|
tag.globals.var = tag.attr['value']
''
end
define_tag "var" do |tag|
tag.globals.var
end
assert_parse_output " true false", %{<r:var /> <r:set value="true" /> <r:var /> <r:set value="false" /> <r:var />}
end
def test_parse_loops
@item = nil
define_tag "each" do |tag|
result = []
["Larry", "Moe", "Curly"].each do |item|
tag.locals.item = item
result << tag.expand
end
result.join(tag.attr["between"] || "")
end
define_tag "each:item" do |tag|
tag.locals.item
end
assert_parse_output %{Three Stooges: "Larry", "Moe", "Curly"}, %{Three Stooges: <r:each between=", ">"<r:item />"</r:each>}
end
def test_parse_speed
define_tag "set" do |tag|
tag.globals.var = tag.attr['value']
''
end
define_tag "var" do |tag|
tag.globals.var
end
parts = %w{decima nobis augue at facer processus commodo legentis odio lectorum dolore nulla esse lius qui nonummy ullamcorper erat ii notare}
multiplier = parts.map{|p| "#{p}=\"#{rand}\""}.join(' ')
assert_nothing_raised do
Timeout.timeout(10) do
assert_parse_output " false", %{<r:set value="false" #{multiplier} /> <r:var />}
end
end
end
def test_tag_option_for
define_tag 'fun', :for => 'just for kicks'
assert_parse_output 'just for kicks', '<r:fun />'
end
def test_tag_expose_option
define_tag 'user', :for => users.first, :expose => ['name', :age]
assert_parse_output 'John', '<r:user:name />'
assert_parse_output '25', '<r:user><r:age /></r:user>'
e = assert_raises(Radius::UndefinedTagError) { @parser.parse "<r:user:email />" }
assert_equal "undefined tag `email'", e.message
end
def test_tag_expose_attributes_option_on_by_default
define_tag 'user', :for => user_with_attributes
assert_parse_output 'John', '<r:user:name />'
end
def test_tag_expose_attributes_set_to_false
define_tag 'user_without_attributes', :for => user_with_attributes, :attributes => false
assert_raises(Radius::UndefinedTagError) { @parser.parse "<r:user_without_attributes:name />" }
end
def test_tag_options_must_contain_a_for_option_if_methods_are_exposed
e = assert_raises(ArgumentError) { define_tag('fun', :expose => :today) { 'test' } }
assert_equal "tag definition must contain a :for option when used with the :expose option", e.message
end
def test_parse_fail_on_missing_end_tag
assert_raises(Radius::MissingEndTagError) { @parser.parse("<r:reverse>") }
end
def test_parse_fail_on_wrong_end_tag
assert_raises(Radius::WrongEndTagError) { @parser.parse("<r:reverse><r:capitalize></r:reverse>") }
end
def test_parse_with_default_tag_prefix
@parser = Radius::Parser.new(@context)
define_tag("hello") { |tag| "Hello world!" }
assert_equal "<p>Hello world!</p>", @parser.parse('<p><radius:hello /></p>')
end
def test_parse_with_other_radius_like_tags
@parser = Radius::Parser.new(@context, :tag_prefix => "ralph")
define_tag('hello') { "hello" }
assert_equal "<r:ralph:hello />", @parser.parse("<r:ralph:hello />")
end
def test_copyin_global_values
@context.globals.foo = 'bar'
assert_equal 'bar', Radius::Parser.new(@context).context.globals.foo
end
def test_does_not_pollute_copied_globals
@context.globals.foo = 'bar'
parser = Radius::Parser.new(@context)
parser.context.globals.foo = '[baz]'
assert_equal 'bar', @context.globals.foo
end
def test_parse_with_other_namespaces
@parser = Radius::Parser.new(@context, :tag_prefix => 'r')
assert_equal "<fb:test>hello world</fb:test>", @parser.parse("<fb:test>hello world</fb:test>")
end
protected
def assert_parse_output(output, input, message = nil)
r = @parser.parse(input)
assert_equal(output, r, message)
end
def assert_parsed_is_unchanged(something)
assert_parse_output something, something
end
class User
attr_accessor :name, :age, :email, :friend
def initialize(name, age, email)
@name, @age, @email = name, age, email
end
def <=>(other)
name <=> other.name
end
end
class UserWithAttributes < User
def attributes
{ :name => name, :age => age, :email => email }
end
end
def users
[
User.new('John', 25, 'test@johnwlong.com'),
User.new('James', 27, 'test@jameslong.com')
]
end
def user_with_attributes
UserWithAttributes.new('John', 25, 'test@johnwlong.com')
end
end

View File

@ -0,0 +1,151 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
class QuickstartTest < Test::Unit::TestCase
def test_hello_world
context = Radius::Context.new
context.define_tag "hello" do |tag|
"Hello #{tag.attr['name'] || 'World'}!"
end
parser = Radius::Parser.new(context)
assert_equal "<p>Hello World!</p>", parser.parse('<p><radius:hello /></p>')
assert_equal "<p>Hello John!</p>", parser.parse('<p><radius:hello name="John" /></p>')
end
def test_example_2
require 'redcloth'
context = Radius::Context.new
context.define_tag "textile" do |tag|
contents = tag.expand
RedCloth.new(contents).to_html
end
parser = Radius::Parser.new(context)
assert_equal "<p>Hello <b>World</b>!</p>", parser.parse('<radius:textile>Hello **World**!</radius:textile>')
end
def test_nested_example
context = Radius::Context.new
context.define_tag "stooge" do |tag|
content = ''
["Larry", "Moe", "Curly"].each do |name|
tag.locals.name = name
content << tag.expand
end
content
end
context.define_tag "stooge:name" do |tag|
tag.locals.name
end
parser = Radius::Parser.new(context)
template = <<-TEMPLATE
<ul>
<radius:stooge>
<li><radius:name /></li>
</radius:stooge>
</ul>
TEMPLATE
output = <<-OUTPUT
<ul>
<li>Larry</li>
<li>Moe</li>
<li>Curly</li>
</ul>
OUTPUT
assert_equal output, parser.parse(template)
end
class User
attr_accessor :name, :age, :email
end
def test_exposing_objects_example
parser = Radius::Parser.new
parser.context.define_tag "count", :for => 1
assert_equal "1", parser.parse("<radius:count />")
user = User.new
user.name, user.age, user.email = "John", 29, "john@example.com"
parser.context.define_tag "user", :for => user, :expose => [ :name, :age, :email ]
assert_equal "John", parser.parse("<radius:user><radius:name /></radius:user>")
assert_equal "John", parser.parse("<radius:user:name />")
end
class LazyContext < Radius::Context
def tag_missing(tag, attr, &block)
"<strong>ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect}</strong>"
end
end
def test_tag_missing_example
parser = Radius::Parser.new(LazyContext.new, :tag_prefix => 'lazy')
output = %{<strong>ERROR: Undefined tag `weird' with attributes {"value"=>"true"}</strong>}
assert_equal output, parser.parse('<lazy:weird value="true" />')
end
def test_tag_globals_example
parser = Radius::Parser.new
parser.context.define_tag "inc" do |tag|
tag.globals.count ||= 0
tag.globals.count += 1
""
end
parser.context.define_tag "count" do |tag|
tag.globals.count || 0
end
assert_equal "0 1", parser.parse("<radius:count /> <radius:inc /><radius:count />")
end
class Person
attr_accessor :name, :friend
def initialize(name)
@name = name
end
end
def test_tag_locals_and_globals_example
jack = Person.new('Jack')
jill = Person.new('Jill')
jack.friend = jill
jill.friend = jack
context = Radius::Context.new do |c|
c.define_tag "jack" do |tag|
tag.locals.person = jack
tag.expand
end
c.define_tag "jill" do |tag|
tag.locals.person = jill
tag.expand
end
c.define_tag "name" do |tag|
tag.locals.person.name rescue tag.missing!
end
c.define_tag "friend" do |tag|
tag.locals.person = tag.locals.person.friend rescue tag.missing!
tag.expand
end
end
parser = Radius::Parser.new(context, :tag_prefix => 'r')
assert_equal "Jack", parser.parse('<r:jack:name />') #=> "Jack"
assert_equal "Jill", parser.parse('<r:jill:name />') #=> "Jill"
assert_equal "Jack", parser.parse('<r:jill:friend:name />') #=> "Jack"
assert_equal "Jack", parser.parse('<r:jack:friend:friend:name />') #=> "Jack"
assert_equal "Jack and Jill", parser.parse('<r:jill><r:friend:name /> and <r:name /></r:jill>') #=> "Jack and Jill"
assert_raises(Radius::UndefinedTagError) { parser.parse('<r:name />') } # raises a Radius::UndefinedTagError exception
end
end

View File

@ -0,0 +1,281 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
require 'radius/parser/squiggle_scanner'
class RadiusSquiggleTest < Test::Unit::TestCase
include RadiusTestHelper
def setup
@context = new_context
@parser = Radius::Parser.new(@context, :scanner => Radius::SquiggleScanner.new)
end
def test_sane_scanner_default
assert !Radius::Parser.new.scanner.is_a?(Radius::SquiggleScanner)
end
def test_initialize_with_params
@parser = Radius::Parser.new(:scanner => Radius::SquiggleScanner.new)
assert_kind_of Radius::SquiggleScanner, @parser.scanner
end
def test_parse_individual_tags_and_parameters
define_tag "add" do |tag|
tag.attr["param1"].to_i + tag.attr["param2"].to_i
end
assert_parse_output "{3}", %[{{ add param1="1" param2='2'/}}]
end
def test_parse_attributes
attributes = %[{"a"=>"1", "b"=>"2", "c"=>"3", "d"=>"'"}]
assert_parse_output attributes, %[{attr a="1" b='2'c="3"d="'" /}]
assert_parse_output attributes, %[{attr a="1" b='2'c="3"d="'"}{/attr}]
end
def test_parse_attributes_with_slashes_or_angle_brackets
slash = %[{"slash"=>"/"}]
angle = %[{"angle"=>">"}]
assert_parse_output slash, %[{attr slash="/"}{/attr}]
assert_parse_output slash, %[{attr slash="/"}{attr /}{/attr}]
assert_parse_output angle, %[{attr angle=">"}{/attr}]
end
def test_parse_quotes
assert_parse_output "test []", %[{echo value="test" /} {wrap attr="test"}{/wrap}]
end
def test_things_that_should_be_left_alone
[
%[ test="2"="4" ],
%[="2" ]
].each do |middle|
assert_parsed_is_unchanged "{attr#{middle}/}"
assert_parsed_is_unchanged "{attr#{middle}}"
end
end
def test_tags_inside_html_tags
assert_parse_output %[<div class="xzibit">tags in yo tags</div>],
%[<div class="{reverse}tibizx{/reverse}">tags in yo tags</div>]
end
def test_parse_result_is_always_a_string
define_tag("twelve") { 12 }
assert_parse_output "12", "{ twelve /}"
end
def test_parse_double_tags
assert_parse_output "test".reverse, "{reverse}test{/reverse}"
assert_parse_output "tset TEST", "{reverse}test{/reverse} {capitalize}test{/capitalize}"
end
def test_parse_tag_nesting
define_tag("parent", :for => '')
define_tag("parent:child", :for => '')
define_tag("extra", :for => '')
define_tag("nesting") { |tag| tag.nesting }
define_tag("extra:nesting") { |tag| tag.nesting.gsub(':', ' > ') }
define_tag("parent:child:nesting") { |tag| tag.nesting.gsub(':', ' * ') }
assert_parse_output "nesting", "{nesting /}"
assert_parse_output "parent:nesting", "{parent:nesting /}"
assert_parse_output "extra > nesting", "{extra:nesting /}"
assert_parse_output "parent * child * nesting", "{parent:child:nesting /}"
assert_parse_output "parent > extra > nesting", "{parent:extra:nesting /}"
assert_parse_output "parent > child > extra > nesting", "{parent:child:extra:nesting /}"
assert_parse_output "parent * extra * child * nesting", "{parent:extra:child:nesting /}"
assert_parse_output "parent > extra > child > extra > nesting", "{parent:extra:child:extra:nesting /}"
assert_parse_output "parent > extra > child > extra > nesting", "{parent}{extra}{child}{extra}{nesting /}{/extra}{/child}{/extra}{/parent}"
assert_parse_output "extra * parent * child * nesting", "{extra:parent:child:nesting /}"
assert_parse_output "extra > parent > nesting", "{extra}{parent:nesting /}{/extra}"
assert_parse_output "extra * parent * child * nesting", "{extra:parent}{child:nesting /}{/extra:parent}"
assert_raises(Radius::UndefinedTagError) { @parser.parse("{child /}") }
end
def test_parse_tag_nesting_2
define_tag("parent", :for => '')
define_tag("parent:child", :for => '')
define_tag("content") { |tag| tag.nesting }
assert_parse_output 'parent:child:content', '{parent}{child:content /}{/parent}'
end
def test_parse_tag__binding_do_missing
define_tag 'test' do |tag|
tag.missing!
end
e = assert_raises(Radius::UndefinedTagError) { @parser.parse("{test /}") }
assert_equal "undefined tag `test'", e.message
end
def test_parse_chirpy_bird
# :> chirp chirp
assert_parse_output "<:", "<:"
end
def test_parse_tag__binding_render_tag
define_tag('test') { |tag| "Hello #{tag.attr['name']}!" }
define_tag('hello') { |tag| tag.render('test', tag.attr) }
assert_parse_output 'Hello John!', '{hello name="John" /}'
end
def test_accessing_tag_attributes_through_tag_indexer
define_tag('test') { |tag| "Hello #{tag['name']}!" }
assert_parse_output 'Hello John!', '{test name="John" /}'
end
def test_parse_tag__binding_render_tag_with_block
define_tag('test') { |tag| "Hello #{tag.expand}!" }
define_tag('hello') { |tag| tag.render('test') { tag.expand } }
assert_parse_output 'Hello John!', '{hello}John{/hello}'
end
def test_tag_locals
define_tag "outer" do |tag|
tag.locals.var = 'outer'
tag.expand
end
define_tag "outer:inner" do |tag|
tag.locals.var = 'inner'
tag.expand
end
define_tag "outer:var" do |tag|
tag.locals.var
end
assert_parse_output 'outer', "{outer}{var /}{/outer}"
assert_parse_output 'outer:inner:outer', "{outer}{var /}:{inner}{var /}{/inner}:{var /}{/outer}"
assert_parse_output 'outer:inner:outer:inner:outer', "{outer}{var /}:{inner}{var /}:{outer}{var /}{/outer}:{var /}{/inner}:{var /}{/outer}"
assert_parse_output 'outer', "{outer:var /}"
end
def test_tag_globals
define_tag "set" do |tag|
tag.globals.var = tag.attr['value']
''
end
define_tag "var" do |tag|
tag.globals.var
end
assert_parse_output " true false", %[{var /} {set value="true" /} {var /} {set value="false" /} {var /}]
end
def test_parse_loops
@item = nil
define_tag "each" do |tag|
result = []
["Larry", "Moe", "Curly"].each do |item|
tag.locals.item = item
result << tag.expand
end
result.join(tag.attr["between"] || "")
end
define_tag "each:item" do |tag|
tag.locals.item
end
assert_parse_output %[Three Stooges: "Larry", "Moe", "Curly"], %[Three Stooges: {each between=", "}"{item /}"{/each}]
end
def test_parse_speed
define_tag "set" do |tag|
tag.globals.var = tag.attr['value']
''
end
define_tag "var" do |tag|
tag.globals.var
end
parts = %w{decima nobis augue at facer processus commodo legentis odio lectorum dolore nulla esse lius qui nonummy ullamcorper erat ii notare}
multiplier = parts.map{|p| "#{p}=\"#{rand}\""}.join(' ')
assert_nothing_raised do
Timeout.timeout(10) do
assert_parse_output " false", %[{set value="false" #{multiplier} /} {var /}]
end
end
end
def test_tag_option_for
define_tag 'fun', :for => 'just for kicks'
assert_parse_output 'just for kicks', '{fun /}'
end
def test_tag_expose_option
define_tag 'user', :for => users.first, :expose => ['name', :age]
assert_parse_output 'John', '{user:name /}'
assert_parse_output '25', '{user}{age /}{/user}'
e = assert_raises(Radius::UndefinedTagError) { @parser.parse "{user:email /}" }
assert_equal "undefined tag `email'", e.message
end
def test_tag_expose_attributes_option_on_by_default
define_tag 'user', :for => user_with_attributes
assert_parse_output 'John', '{user:name /}'
end
def test_tag_expose_attributes_set_to_false
define_tag 'user_without_attributes', :for => user_with_attributes, :attributes => false
assert_raises(Radius::UndefinedTagError) { @parser.parse "{user_without_attributes:name /}" }
end
def test_tag_options_must_contain_a_for_option_if_methods_are_exposed
e = assert_raises(ArgumentError) { define_tag('fun', :expose => :today) { 'test' } }
assert_equal "tag definition must contain a :for option when used with the :expose option", e.message
end
def test_parse_fail_on_missing_end_tag
assert_raises(Radius::MissingEndTagError) { @parser.parse("{reverse}") }
end
def test_parse_fail_on_wrong_end_tag
assert_raises(Radius::WrongEndTagError) { @parser.parse("{reverse}{capitalize}{/reverse}") }
end
def test_copyin_global_values
@context.globals.foo = 'bar'
assert_equal 'bar', Radius::Parser.new(@context).context.globals.foo
end
def test_does_not_pollute_copied_globals
@context.globals.foo = 'bar'
parser = Radius::Parser.new(@context)
parser.context.globals.foo = '[baz]'
assert_equal 'bar', @context.globals.foo
end
def test_parse_with_other_namespaces
@parser = Radius::Parser.new(@context, :tag_prefix => 'r')
assert_equal "{fb:test}hello world{/fb:test}", @parser.parse("{fb:test}hello world{/fb:test}")
end
protected
def assert_parse_output(output, input, message = nil)
r = @parser.parse(input)
assert_equal(output, r, message)
end
def assert_parsed_is_unchanged(something)
assert_parse_output something, something
end
class User
attr_accessor :name, :age, :email, :friend
def initialize(name, age, email)
@name, @age, @email = name, age, email
end
def <=>(other)
name <=> other.name
end
end
class UserWithAttributes < User
def attributes
{ :name => name, :age => age, :email => email }
end
end
def users
[
User.new('John', 25, 'test@johnwlong.com'),
User.new('James', 27, 'test@jameslong.com')
]
end
def user_with_attributes
UserWithAttributes.new('John', 25, 'test@johnwlong.com')
end
end

View File

@ -0,0 +1,36 @@
require 'rubygems'
require 'timeout'
unless defined? RADIUS_LIB
RADIUS_LIB = File.join(File.dirname(__FILE__), '..', 'lib')
$LOAD_PATH << RADIUS_LIB
require 'radius'
require 'test/unit'
module RadiusTestHelper
class TestContext < Radius::Context; end
def new_context
Radius::Context.new do |c|
c.define_tag("reverse" ) { |tag| tag.expand.reverse }
c.define_tag("capitalize") { |tag| tag.expand.upcase }
c.define_tag("echo" ) { |tag| tag.attr['value'] }
c.define_tag("wrap" ) { |tag| "[#{tag.expand}]" }
c.define_tag("attr") do |tag|
kv = tag.attr.keys.sort.collect{|k| "#{k.inspect}=>#{tag[k].inspect}"}
"{#{kv.join(', ')}}"
end
end
end
def define_tag(name, options = {}, &block)
@parser.context.define_tag name, options, &block
end
def define_global_tag(name, options = {}, &block)
@context.define_tag name, options, &block
end
end
end

View File

@ -0,0 +1,30 @@
require 'test/unit'
require 'radius'
class RadiusUtilityTest < Test::Unit::TestCase
def test_symbolize_keys
h = Radius::Utility.symbolize_keys({ 'a' => 1, :b => 2 })
assert_equal h[:a], 1
assert_equal h[:b], 2
end
def test_impartial_hash_delete
h = { 'a' => 1, :b => 2 }
assert_equal Radius::Utility.impartial_hash_delete(h, :a), 1
assert_equal Radius::Utility.impartial_hash_delete(h, 'b'), 2
assert_equal h.empty?, true
end
def test_constantize
assert_equal Radius::Utility.constantize('String'), String
end
def test_camelize
assert_equal Radius::Utility.camelize('ab_cd_ef'), 'AbCdEf'
end
def test_array_to_s
assert_equal Radius::Utility.array_to_s(['a', 1, [:c]]), 'a1c'
end
end