radius 0.7.4 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +8 -0
- data/CONTRIBUTORS +2 -0
- data/Gemfile +16 -2
- data/README.rdoc +2 -6
- data/Rakefile +10 -1
- data/coverage/assets/{0.7.1 → 0.10.2}/application.css +52 -363
- data/coverage/assets/{0.7.1 → 0.10.2}/application.js +1153 -72
- data/coverage/assets/0.10.2/colorbox/border.png +0 -0
- data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.13.1/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.13.1/application.css +1 -0
- data/coverage/assets/0.13.1/application.js +7 -0
- data/coverage/assets/0.13.1/colorbox/border.png +0 -0
- data/coverage/assets/0.13.1/colorbox/controls.png +0 -0
- data/coverage/assets/0.13.1/colorbox/loading.gif +0 -0
- data/coverage/assets/0.13.1/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.13.1/favicon_green.png +0 -0
- data/coverage/assets/0.13.1/favicon_red.png +0 -0
- data/coverage/assets/0.13.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.13.1/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.13.1/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.13.1/loading.gif +0 -0
- data/coverage/assets/0.13.1/magnify.png +0 -0
- data/lib/radius/context.rb +2 -1
- data/lib/radius/delegating_open_struct.rb +15 -19
- data/lib/radius/ord_string.rb +6 -8
- data/lib/radius/parser/JavaScanner.class +0 -0
- data/lib/radius/parser/JavaScanner.java +211 -161
- data/lib/radius/parser/JavaScanner.rl +36 -7
- data/lib/radius/parser/scanner.rb +2 -2
- data/lib/radius/parser.rb +69 -40
- data/lib/radius/utility.rb +5 -14
- data/lib/radius/version.rb +1 -1
- data/lib/radius.rb +11 -11
- data/pkg/radius-0.8.0.gem +0 -0
- data/radius.gemspec +12 -4
- data/test/context_test.rb +13 -5
- data/test/multithreaded_test.rb +60 -43
- data/test/ord_string_test.rb +3 -3
- data/test/parser_test.rb +11 -3
- data/test/quickstart_test.rb +2 -2
- data/test/squiggle_test.rb +3 -3
- data/test/test_helper.rb +21 -26
- data/test/utility_test.rb +3 -3
- metadata +86 -70
- data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/pkg/radius-0.7.0.prerelease.gem +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/favicon_green.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/favicon_red.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/favicon_yellow.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/loading.gif +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/magnify.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-icons_222222_256x240.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-icons_454545_256x240.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-icons_888888_256x240.png +0 -0
- /data/coverage/assets/{0.7.1 → 0.10.2}/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
@@ -3,20 +3,37 @@
|
|
3
3
|
|
4
4
|
action _prefix { mark_pfx = p; }
|
5
5
|
action prefix {
|
6
|
-
prefix = input.substring(mark_pfx, p);
|
6
|
+
prefix = String.valueOf(input.substring(mark_pfx, p));
|
7
7
|
}
|
8
8
|
action _check_prefix {
|
9
9
|
if ( !prefix.equals(tag_prefix) ) {
|
10
|
-
//
|
11
|
-
|
12
|
-
|
10
|
+
// Pass through the entire tag markup as text
|
11
|
+
pass_through(input.substring(tagstart, p + 1));
|
12
|
+
// Reset all state
|
13
13
|
prefix = "";
|
14
|
+
name = "";
|
15
|
+
attributes = RubyHash.newHash(runtime);
|
16
|
+
flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
|
17
|
+
tagstart = p + 1;
|
14
18
|
fgoto main;
|
15
19
|
}
|
16
20
|
}
|
17
21
|
|
18
22
|
action _starttag { mark_stg = p; }
|
19
|
-
action starttag {
|
23
|
+
action starttag {
|
24
|
+
name = String.valueOf(input.substring(mark_stg, p));
|
25
|
+
if (name == null || name.trim().isEmpty()) {
|
26
|
+
// Pass through the entire tag markup as text
|
27
|
+
pass_through(input.substring(tagstart, p + 1));
|
28
|
+
// Reset all state
|
29
|
+
prefix = "";
|
30
|
+
name = "";
|
31
|
+
attributes = RubyHash.newHash(runtime);
|
32
|
+
flavor = RubySymbol.newSymbol(runtime, "tasteless".intern());
|
33
|
+
tagstart = p + 1;
|
34
|
+
fgoto main;
|
35
|
+
}
|
36
|
+
}
|
20
37
|
action _attr { mark_attr = p; }
|
21
38
|
action attr {
|
22
39
|
attributes.op_aset(
|
@@ -40,7 +57,7 @@
|
|
40
57
|
# words
|
41
58
|
PrefixChar = [\-A-Za-z0-9._?] ;
|
42
59
|
NameChar = [\-A-Za-z0-9._:?] ;
|
43
|
-
TagName =
|
60
|
+
TagName = ([\-A-Za-z0-9._?]+ (':' [\-A-Za-z0-9._?]+)*) >_starttag %starttag;
|
44
61
|
Prefix = PrefixChar+ >_prefix %prefix;
|
45
62
|
Open = "<";
|
46
63
|
Sep = ":" >_check_prefix;
|
@@ -111,11 +128,23 @@ public class JavaScanner {
|
|
111
128
|
}
|
112
129
|
|
113
130
|
void tag(String prefix, String name, RubyHash attr, RubySymbol flavor) {
|
131
|
+
// Validate both prefix and name
|
132
|
+
if ((prefix == null || prefix.trim().isEmpty()) &&
|
133
|
+
(name == null || name.trim().isEmpty())) {
|
134
|
+
pass_through("<");
|
135
|
+
return;
|
136
|
+
}
|
137
|
+
|
138
|
+
if (name == null || name.trim().isEmpty()) {
|
139
|
+
pass_through("<" + prefix + ":");
|
140
|
+
return;
|
141
|
+
}
|
142
|
+
|
114
143
|
RubyHash tag = RubyHash.newHash(runtime);
|
115
144
|
tag.op_aset(
|
116
145
|
runtime.getCurrentContext(),
|
117
146
|
RubySymbol.newSymbol(runtime, "prefix"),
|
118
|
-
RubyString.newString(runtime, prefix)
|
147
|
+
RubyString.newString(runtime, prefix != null ? prefix : "")
|
119
148
|
);
|
120
149
|
tag.op_aset(
|
121
150
|
runtime.getCurrentContext(),
|
@@ -4,7 +4,7 @@ module Radius
|
|
4
4
|
# The regular expression used to find (1) opening and self-enclosed tag names, (2) self-enclosing trailing slash,
|
5
5
|
# (3) attributes and (4) closing tag
|
6
6
|
def scanner_regex(prefix = nil)
|
7
|
-
%r{<#{prefix}:([-\w:]+?)(\s+(
|
7
|
+
%r{<#{prefix}:([-\w:]+?)(\s+(?:[-\w]+\s*=\s*(?:"[^"]*?"|'[^']*?')\s*)*|)(\/?)>|<\/#{prefix}:([-\w:]+?)\s*>}
|
8
8
|
end
|
9
9
|
|
10
10
|
# Parses a given string and returns an array of nodes.
|
@@ -47,7 +47,7 @@ module Radius
|
|
47
47
|
|
48
48
|
def parse_attributes(text) # :nodoc:
|
49
49
|
attr = {}
|
50
|
-
re = /(
|
50
|
+
re = /([-\w]+?)\s*=\s*('|")(.*?)\2/
|
51
51
|
while md = re.match(text)
|
52
52
|
attr[$1] = $3
|
53
53
|
text = md.post_match
|
data/lib/radius/parser.rb
CHANGED
@@ -17,63 +17,92 @@ module Radius
|
|
17
17
|
|
18
18
|
# Creates a new parser object initialized with a Context.
|
19
19
|
def initialize(context = Context.new, options = {})
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
self.tag_prefix = options[:tag_prefix] || 'radius'
|
26
|
-
self.scanner = options[:scanner] || default_scanner
|
20
|
+
context, options = normalize_initialization_params(context, options)
|
21
|
+
@context = context ? context.dup : Context.new
|
22
|
+
@tag_prefix = options[:tag_prefix] || 'radius'
|
23
|
+
@scanner = options[:scanner] || default_scanner
|
24
|
+
@stack = nil # Pre-initialize stack
|
27
25
|
end
|
28
26
|
|
29
27
|
# Parses string for tags, expands them, and returns the result.
|
30
28
|
def parse(string)
|
31
|
-
@stack = [
|
32
|
-
|
33
|
-
stack_up
|
29
|
+
@stack = [create_root_container]
|
30
|
+
process_tokens(scanner.operate(tag_prefix, string))
|
34
31
|
@stack.last.to_s
|
35
32
|
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
def
|
40
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalize_initialization_params(context, options)
|
37
|
+
return [context['context'] || context[:context], context] if context.is_a?(Hash) && options.empty?
|
38
|
+
[context, Utility.symbolize_keys(options)]
|
41
39
|
end
|
42
|
-
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
else
|
62
|
-
raise UndefinedFlavorError.new(t, @stack)
|
63
|
-
end
|
40
|
+
|
41
|
+
def create_root_container
|
42
|
+
ParseContainerTag.new { |t| Utility.array_to_s(t.contents) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_tokens(tokens)
|
46
|
+
tokens.each { |token| process_token(token) }
|
47
|
+
validate_final_stack
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_token(token)
|
51
|
+
return @stack.last.contents << token if token.is_a?(String)
|
52
|
+
|
53
|
+
case token[:flavor]
|
54
|
+
when :open then handle_open_tag(token)
|
55
|
+
when :self then handle_self_tag(token)
|
56
|
+
when :close then handle_close_tag(token)
|
57
|
+
when :tasteless then raise TastelessTagError.new(token, @stack)
|
58
|
+
else raise UndefinedFlavorError.new(token, @stack)
|
64
59
|
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_open_tag(token)
|
63
|
+
@stack.push(ParseContainerTag.new(token[:name], token[:attrs]))
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_self_tag(token)
|
67
|
+
@stack.last.contents << ParseTag.new { @context.render_tag(token[:name], token[:attrs]) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def handle_close_tag(token)
|
71
|
+
popped = @stack.pop
|
72
|
+
validate_tag_match(popped, token[:name])
|
73
|
+
wrap_and_push_tag(popped)
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_tag_match(popped, name)
|
77
|
+
raise WrongEndTagError.new(popped.name, name, @stack) if popped.name != name
|
78
|
+
end
|
79
|
+
|
80
|
+
def wrap_and_push_tag(tag)
|
81
|
+
tag.on_parse { |b| @context.render_tag(tag.name, tag.attributes) { Utility.array_to_s(b.contents) } }
|
82
|
+
@stack.last.contents << tag
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_final_stack
|
65
86
|
raise MissingEndTagError.new(@stack.last.name, @stack) if @stack.length != 1
|
66
87
|
end
|
67
88
|
|
68
89
|
def default_scanner
|
69
90
|
if RUBY_PLATFORM == 'java'
|
70
|
-
|
71
|
-
require 'radius/parser/java_scanner.jar'
|
72
|
-
::Radius.send(:include_package, 'radius.parser')
|
73
|
-
Radius::JavaScanner.new(JRuby.runtime)
|
91
|
+
load_java_scanner
|
74
92
|
else
|
75
93
|
Radius::Scanner.new
|
76
94
|
end
|
77
95
|
end
|
96
|
+
|
97
|
+
def load_java_scanner
|
98
|
+
if Gem::Version.new(JRUBY_VERSION) >= Gem::Version.new('9.3')
|
99
|
+
require 'jruby'
|
100
|
+
else
|
101
|
+
require 'java'
|
102
|
+
end
|
103
|
+
require 'radius/parser/java_scanner.jar'
|
104
|
+
::Radius.send(:include_package, 'radius.parser')
|
105
|
+
Radius::JavaScanner.new(JRuby.runtime)
|
106
|
+
end
|
78
107
|
end
|
79
108
|
end
|
data/lib/radius/utility.rb
CHANGED
@@ -22,22 +22,13 @@ module Radius
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.camelize(underscored_string)
|
25
|
-
|
26
|
-
underscored_string.split('_').each { |part| string << part.capitalize }
|
27
|
-
string
|
25
|
+
underscored_string.split('_').map(&:capitalize).join
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
else
|
35
|
-
def self.array_to_s(c)
|
36
|
-
c.map do |x|
|
37
|
-
res = (x.is_a?(Array) ? array_to_s(x) : x.to_s)
|
38
|
-
(res.frozen? ? res.dup : res).force_encoding(Encoding::UTF_8)
|
39
|
-
end.join
|
40
|
-
end
|
28
|
+
def self.array_to_s(c)
|
29
|
+
+c.map do |x|
|
30
|
+
x.is_a?(Array) ? array_to_s(x) : x.to_s
|
31
|
+
end.join
|
41
32
|
end
|
42
33
|
end
|
43
34
|
end
|
data/lib/radius/version.rb
CHANGED
data/lib/radius.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require_relative "radius/version"
|
2
|
+
require_relative "radius/error"
|
3
|
+
require_relative "radius/tag_definitions"
|
4
|
+
require_relative "radius/delegating_open_struct"
|
5
|
+
require_relative "radius/tag_binding"
|
6
|
+
require_relative "radius/context"
|
7
|
+
require_relative "radius/parse_tag"
|
8
|
+
require_relative "radius/ord_string"
|
9
|
+
require_relative "radius/parser/scanner"
|
10
|
+
require_relative "radius/parser"
|
11
|
+
require_relative "radius/utility"
|
Binary file
|
data/radius.gemspec
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
require File.join(File.dirname(__FILE__), 'lib', 'radius', 'version')
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
5
|
+
s.name = "radius"
|
6
6
|
s.version = ::Radius.version
|
7
|
+
s.licenses = ["MIT"]
|
7
8
|
|
8
|
-
s.
|
9
|
-
|
9
|
+
s.required_ruby_version = ">= 2.6.0"
|
10
|
+
|
11
|
+
s.authors = ["John W. Long", "David Chelimsky", "Bryce Kerley"]
|
10
12
|
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...).}
|
11
|
-
s.email =
|
13
|
+
s.email = ["me@johnwlong.com", "dchelimsky@gmail.com", "bkerley@brycekerley.net"]
|
12
14
|
s.extra_rdoc_files = [
|
13
15
|
"CHANGELOG",
|
14
16
|
"CONTRIBUTORS",
|
@@ -22,5 +24,11 @@ Gem::Specification.new do |s|
|
|
22
24
|
|
23
25
|
s.homepage = %q{http://github.com/jlong/radius}
|
24
26
|
s.summary = %q{A tag-based templating language for Ruby.}
|
27
|
+
|
28
|
+
s.metadata = {
|
29
|
+
"homepage_uri" => "https://github.com/jlong/radius",
|
30
|
+
"changelog_uri" => "https://github.com/jlong/radius/blob/master/CHANGELOG",
|
31
|
+
"bug_tracker_uri" => "https://github.com/jlong/radius/issues"
|
32
|
+
}
|
25
33
|
end
|
26
34
|
|
data/test/context_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
|
-
class RadiusContextTest < Test
|
3
|
+
class RadiusContextTest < Minitest::Test
|
4
4
|
include RadiusTestHelper
|
5
5
|
|
6
6
|
class SuperContext < Radius::Context
|
@@ -24,7 +24,7 @@ class RadiusContextTest < Test::Unit::TestCase
|
|
24
24
|
assert_kind_of Radius::Context, c
|
25
25
|
c.define_tag('test') { 'just a test' }
|
26
26
|
end
|
27
|
-
|
27
|
+
refute_equal Hash.new, @context.definitions
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_initialize_with_arguments
|
@@ -35,6 +35,14 @@ class RadiusContextTest < Test::Unit::TestCase
|
|
35
35
|
assert_equal 'arg', @context.name
|
36
36
|
end
|
37
37
|
|
38
|
+
def test_dup_preserves_delegated_values
|
39
|
+
@context = Radius::Context.new
|
40
|
+
@context.globals.object = Object.new.tap { |o| def o.special_method; "special"; end }
|
41
|
+
duped = @context.dup
|
42
|
+
|
43
|
+
assert_equal "special", duped.globals.special_method, "Duped context should preserve delegated object methods"
|
44
|
+
end
|
45
|
+
|
38
46
|
def test_with
|
39
47
|
got = @context.with do |c|
|
40
48
|
assert_equal @context, c
|
@@ -58,13 +66,13 @@ class RadiusContextTest < Test::Unit::TestCase
|
|
58
66
|
def test_tag_missing
|
59
67
|
class << @context
|
60
68
|
def tag_missing(tag, attr, &block)
|
61
|
-
"undefined tag `#{tag}' with attributes #{attr.inspect}"
|
69
|
+
"undefined tag `#{tag}' with attributes #{attr.inspect.sub(" => ", "=>")}"
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
65
73
|
text = ''
|
66
74
|
expected = %{undefined tag `undefined_tag' with attributes {"cool"=>"beans"}}
|
67
|
-
|
75
|
+
text = @context.render_tag('undefined_tag', 'cool' => 'beans')
|
68
76
|
assert_equal expected, text
|
69
77
|
end
|
70
78
|
|
data/test/multithreaded_test.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require '
|
2
|
+
require 'test_helper'
|
3
3
|
require 'radius'
|
4
|
+
require 'etc'
|
4
5
|
|
5
|
-
class MultithreadTest < Test
|
6
|
+
class MultithreadTest < Minitest::Test
|
6
7
|
|
7
8
|
def setup
|
8
|
-
Thread.abort_on_exception
|
9
|
+
Thread.abort_on_exception = true
|
9
10
|
@context = Radius::Context.new do |c|
|
10
11
|
c.define_tag('thread') do |tag|
|
11
12
|
"#{tag.locals.thread_id} / #{tag.globals.object_id}"
|
@@ -13,51 +14,67 @@ class MultithreadTest < Test::Unit::TestCase
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
17
|
+
def test_runs_multithreaded
|
18
|
+
thread_count = [Etc.nprocessors, 16].min
|
19
|
+
iterations_per_thread = 500
|
20
|
+
failures = Queue.new
|
21
|
+
results = Array.new(thread_count) { [] }
|
22
|
+
threads = []
|
23
|
+
|
24
|
+
thread_count.times do |i|
|
25
|
+
threads << Thread.new do
|
26
|
+
local_results = []
|
27
|
+
iterations_per_thread.times do
|
28
|
+
begin
|
29
|
+
thread_context = @context.dup
|
30
|
+
parser = Radius::Parser.new(thread_context, :tag_prefix => 'r')
|
31
|
+
parser.context.globals.thread_id = Thread.current.object_id
|
32
|
+
expected = "#{Thread.current.object_id} / #{parser.context.globals.object_id}"
|
33
|
+
result = parser.parse('<r:thread />')
|
34
|
+
|
35
|
+
local_results << {
|
36
|
+
result: result,
|
37
|
+
thread_id: Thread.current.object_id,
|
38
|
+
iteration: local_results.size
|
39
|
+
}
|
40
|
+
|
41
|
+
failures << "Expected: #{expected}, Got: #{result}" unless result == expected
|
42
|
+
rescue => e
|
43
|
+
failures << "Thread #{Thread.current.object_id} failed: #{e.message}\n#{e.backtrace.join("\n")}"
|
44
|
+
end
|
34
45
|
end
|
35
|
-
|
36
|
-
lock.synchronized do
|
37
|
-
threads.each{|t| t.join }
|
46
|
+
results[i] = local_results
|
38
47
|
end
|
39
48
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
|
50
|
+
threads.each(&:join)
|
51
|
+
|
52
|
+
# Only try to show failures if there are any
|
53
|
+
failure_message = if failures.empty?
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
failed_items = []
|
57
|
+
5.times { failed_items << failures.pop unless failures.empty? }
|
58
|
+
"Thread failures detected:\n#{failures.size} times:\n#{failed_items.join("\n")}"
|
59
|
+
end
|
60
|
+
|
61
|
+
assert(failures.empty?, failure_message)
|
62
|
+
total_results = results.flatten.uniq.size
|
63
|
+
expected_unique_results = thread_count * iterations_per_thread
|
64
|
+
|
65
|
+
if total_results != expected_unique_results
|
66
|
+
duplicates = results.flatten.group_by { |r| r[:result] }
|
67
|
+
.select { |_, v| v.size > 1 }
|
68
|
+
|
69
|
+
puts "\nDuplicates found:"
|
70
|
+
duplicates.each do |result, occurrences|
|
71
|
+
puts "\nResult: #{result[:result]}"
|
72
|
+
occurrences.each { |o| puts " Thread: #{o[:thread_id]}, Iteration: #{o[:iteration]}" }
|
59
73
|
end
|
60
74
|
end
|
75
|
+
|
76
|
+
assert_equal expected_unique_results, total_results,
|
77
|
+
"Expected #{expected_unique_results} unique results (#{thread_count} threads × #{iterations_per_thread} iterations)"
|
61
78
|
end
|
62
79
|
|
63
80
|
end
|
data/test/ord_string_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_helper'
|
2
2
|
require 'radius'
|
3
3
|
|
4
|
-
class RadiusOrdStringTest < Test
|
4
|
+
class RadiusOrdStringTest < Minitest::Test
|
5
5
|
|
6
6
|
def test_string_slice_integer
|
7
7
|
str = Radius::OrdString.new "abc"
|
@@ -15,4 +15,4 @@ class RadiusOrdStringTest < Test::Unit::TestCase
|
|
15
15
|
assert_equal str[0..-1], "abc"
|
16
16
|
end
|
17
17
|
|
18
|
-
end
|
18
|
+
end
|
data/test/parser_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
|
3
|
-
class RadiusParserTest < Test
|
3
|
+
class RadiusParserTest < Minitest::Test
|
4
4
|
include RadiusTestHelper
|
5
5
|
|
6
6
|
def setup
|
@@ -39,6 +39,14 @@ class RadiusParserTest < Test::Unit::TestCase
|
|
39
39
|
|
40
40
|
assert_parse_output 'ok', '<r:some-tag>nope</r:some-tag>'
|
41
41
|
end
|
42
|
+
|
43
|
+
def test_parse_tag_with_dashed_attributes
|
44
|
+
define_tag 'tag-with-dashed-attributes' do |tag|
|
45
|
+
"dashed: #{tag.attr['data-dashed']} regular: #{tag.attr['regular']}"
|
46
|
+
end
|
47
|
+
assert_parse_output 'dashed: dashed-value regular: value', '<r:tag-with-dashed-attributes data-dashed="dashed-value" regular="value"></r:tag-with-dashed-attributes>'
|
48
|
+
end
|
49
|
+
|
42
50
|
|
43
51
|
def test_parse_individual_tags_and_parameters
|
44
52
|
define_tag "add" do |tag|
|
@@ -203,11 +211,11 @@ class RadiusParserTest < Test::Unit::TestCase
|
|
203
211
|
end
|
204
212
|
parts = %w{decima nobis augue at facer processus commodo legentis odio lectorum dolore nulla esse lius qui nonummy ullamcorper erat ii notare}
|
205
213
|
multiplier = parts.map{|p| "#{p}=\"#{rand}\""}.join(' ')
|
206
|
-
|
214
|
+
assert ->{
|
207
215
|
Timeout.timeout(10) do
|
208
216
|
assert_parse_output " false", %{<r:set value="false" #{multiplier} /> <r:var />}
|
209
217
|
end
|
210
|
-
|
218
|
+
}.call
|
211
219
|
end
|
212
220
|
|
213
221
|
def test_tag_option_for
|
data/test/quickstart_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
|
3
|
-
class QuickstartTest < Test
|
3
|
+
class QuickstartTest < Minitest::Test
|
4
4
|
|
5
5
|
def test_hello_world
|
6
6
|
context = Radius::Context.new
|
@@ -83,7 +83,7 @@ class QuickstartTest < Test::Unit::TestCase
|
|
83
83
|
|
84
84
|
class LazyContext < Radius::Context
|
85
85
|
def tag_missing(tag, attr, &block)
|
86
|
-
"<strong>ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect}</strong>"
|
86
|
+
"<strong>ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect.sub(" => ", "=>")}</strong>"
|
87
87
|
end
|
88
88
|
end
|
89
89
|
def test_tag_missing_example
|
data/test/squiggle_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
require 'radius/parser/squiggle_scanner'
|
3
3
|
|
4
|
-
class RadiusSquiggleTest < Test
|
4
|
+
class RadiusSquiggleTest < Minitest::Test
|
5
5
|
include RadiusTestHelper
|
6
6
|
|
7
7
|
def setup
|
@@ -181,11 +181,11 @@ class RadiusSquiggleTest < Test::Unit::TestCase
|
|
181
181
|
end
|
182
182
|
parts = %w{decima nobis augue at facer processus commodo legentis odio lectorum dolore nulla esse lius qui nonummy ullamcorper erat ii notare}
|
183
183
|
multiplier = parts.map{|p| "#{p}=\"#{rand}\""}.join(' ')
|
184
|
-
|
184
|
+
assert ->{
|
185
185
|
Timeout.timeout(10) do
|
186
186
|
assert_parse_output " false", %[{set value="false" #{multiplier} /} {var /}]
|
187
187
|
end
|
188
|
-
|
188
|
+
}.call
|
189
189
|
end
|
190
190
|
|
191
191
|
def test_tag_option_for
|
data/test/test_helper.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'timeout'
|
1
2
|
require 'simplecov'
|
2
3
|
SimpleCov.start do
|
3
4
|
add_filter 'test'
|
@@ -8,38 +9,32 @@ if ENV['COVERALLS']
|
|
8
9
|
Coveralls.wear!
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
unless defined? RADIUS_LIB
|
14
|
-
|
15
|
-
RADIUS_LIB = File.join(File.dirname(__FILE__), '..', 'lib')
|
16
|
-
$LOAD_PATH << RADIUS_LIB
|
12
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
17
13
|
|
18
|
-
|
19
|
-
require 'test/unit'
|
14
|
+
require 'radius'
|
20
15
|
|
21
|
-
|
22
|
-
|
16
|
+
module RadiusTestHelper
|
17
|
+
class TestContext < Radius::Context; end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
19
|
+
def new_context
|
20
|
+
Radius::Context.new do |c|
|
21
|
+
c.define_tag("reverse" ) { |tag| tag.expand.reverse }
|
22
|
+
c.define_tag("capitalize") { |tag| tag.expand.upcase }
|
23
|
+
c.define_tag("echo" ) { |tag| tag.attr['value'] }
|
24
|
+
c.define_tag("wrap" ) { |tag| "[#{tag.expand}]" }
|
25
|
+
c.define_tag("attr") do |tag|
|
26
|
+
kv = tag.attr.keys.sort.collect{|k| "#{k.inspect}=>#{tag[k].inspect}"}
|
27
|
+
"{#{kv.join(', ')}}"
|
34
28
|
end
|
35
29
|
end
|
30
|
+
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
def define_tag(name, options = {}, &block)
|
33
|
+
@parser.context.define_tag name, options, &block
|
34
|
+
end
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
36
|
+
def define_global_tag(name, options = {}, &block)
|
37
|
+
@context.define_tag name, options, &block
|
44
38
|
end
|
45
39
|
end
|
40
|
+
require 'minitest/autorun'
|