arrow 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. metadata +339 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ require 'rubygems'
13
+ require 'spec'
14
+ require 'tempfile'
15
+ require 'tmpdir'
16
+ require 'pathname'
17
+ require 'apache/fakerequest'
18
+ require 'arrow'
19
+ require 'arrow/dispatcher'
20
+ require 'arrow/config-loaders/yaml'
21
+
22
+ require 'spec/lib/matchers'
23
+ require 'spec/lib/constants'
24
+
25
+
26
+ include Arrow::TestConstants
27
+
28
+
29
+ #####################################################################
30
+ ### C O N T E X T S
31
+ #####################################################################
32
+
33
+ describe Arrow::Dispatcher do
34
+ include Arrow::TimeMatchers
35
+
36
+ before(:all) do
37
+ @tmpfile = Tempfile.new( 'test.conf', '.' )
38
+ TEST_CONFIG.dup.write( @tmpfile.path )
39
+ @tmpfile.close
40
+ end
41
+
42
+ after( :all ) do
43
+ @tmpfile.delete
44
+ end
45
+
46
+
47
+ after(:each) do
48
+ Pathname.glob( "%s/arrow-fatal*" % Dir.tmpdir ).each do |f|
49
+ f.unlink
50
+ end
51
+ end
52
+
53
+
54
+ ### Specs
55
+
56
+ it "raises an error when its factory method is called with " +
57
+ "something other than a String or Hash" do
58
+ lambda {
59
+ Arrow::Dispatcher.create( :something )
60
+ }.should raise_error( ArgumentError, /invalid config hash/i )
61
+ end
62
+
63
+ it "writes a crashlog to TMPDIR when create fails" do
64
+ crashlog = Pathname.new( Dir.tmpdir + "/arrow-fatal.log.#{$$}" )
65
+ crashlog.unlink if crashlog.exist?
66
+
67
+ begin
68
+ Arrow::Dispatcher.create( :something )
69
+ rescue ArgumentError
70
+ end
71
+
72
+ crashlog.exist?.should == true
73
+ crashlog.ctime.should be_after( Time.now - 60 )
74
+ end
75
+
76
+ it "assumes a string configspec is a default configfile" do
77
+ dispatcher = Arrow::Dispatcher.create( @tmpfile.path )
78
+ Arrow::Dispatcher.instance.should equal( dispatcher )
79
+ end
80
+
81
+ it "reads dispatcher configs from an object's #read method if it has one" do
82
+ config_obj = mock( "configuration object", :null_object => true )
83
+ config_obj.should_receive( :respond_to? ).with( :read ).and_return( true )
84
+ config_obj.should_receive( :read ).and_return( YAML.dump({}) )
85
+
86
+ Arrow::Dispatcher.create_from_hosts_file( config_obj )
87
+ end
88
+
89
+
90
+ describe "instance" do
91
+ it "has an associated name" do
92
+ @config = Arrow::Config.new( TEST_CONFIG_HASH )
93
+ @dispatcher = Arrow::Dispatcher.new( "test", @config )
94
+ @dispatcher.name.should == "test"
95
+ end
96
+
97
+ describe " running under $SAFE = 1" do
98
+ before(:all) do
99
+ @configfile = Tempfile.new( 'test.conf', '.' )
100
+ @configfile.print( YAML.dump(TEST_CONFIG_HASH) )
101
+ @configfile.close
102
+
103
+ hosts_config = YAML.dump({
104
+ :testing => @configfile.path,
105
+ })
106
+ @host_configio = StringIO.new( hosts_config )
107
+ @host_configio.taint
108
+ end
109
+
110
+ after(:all) do
111
+ @configfile.delete
112
+ end
113
+
114
+ # Fake actually loading the config file
115
+ before(:each) do
116
+ @host_configio.rewind
117
+ end
118
+
119
+ it "should be able to create dispatchers from a tainted hosts file" do
120
+ $SAFE = 1
121
+ rval = nil
122
+
123
+ lambda {
124
+ rval = Arrow::Dispatcher.create_from_hosts_file( @host_configio )
125
+ }.should_not raise_error()
126
+
127
+ rval.should be_an_instance_of( Hash )
128
+ rval.should have(2).keys
129
+ rval.keys.should include( :testing )
130
+ rval.keys.should include( File.expand_path(@configfile.path) )
131
+ end
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ require 'spec'
13
+ require 'spec/lib/helpers'
14
+
15
+ require 'apache/fakerequest'
16
+
17
+ require 'arrow'
18
+ require 'arrow/dispatcherloader'
19
+
20
+
21
+ #####################################################################
22
+ ### C O N T E X T S
23
+ #####################################################################
24
+
25
+ describe Arrow::DispatcherLoader do
26
+ include Arrow::SpecHelpers
27
+
28
+
29
+ before( :all ) do
30
+ setup_logging( :crit )
31
+ end
32
+
33
+ after( :all ) do
34
+ reset_logging()
35
+ end
36
+
37
+ it "creates Arrow::Dispatchers from a registered configfile on child_init" do
38
+ req = Apache::Request.new
39
+ Arrow::Dispatcher.should_receive( :create_from_hosts_file ).with( 'hosts.yml' )
40
+ Arrow::DispatcherLoader.new( 'hosts.yml' ).child_init( req ).should == Apache::OK
41
+ end
42
+
43
+ it "handles errors while loading dispachers by logging to a tempfile and to Apache's log" do
44
+ req = Apache::Request.new
45
+ Arrow::Dispatcher.should_receive( :create_from_hosts_file ).with( 'hosts.yml' ).
46
+ and_raise( RuntimeError.new("something bad happened") )
47
+
48
+ logpath = mock( "error logfile Pathname" )
49
+ io = mock( "error logfile IO" )
50
+
51
+ Pathname.should_receive( :new ).with( instance_of(String) ).
52
+ and_return( logpath )
53
+ logpath.should_receive( :+ ).with( 'arrow-dispatcher-failure.log' ).and_return( logpath )
54
+ logpath.should_receive( :open ).with( IO::WRONLY|IO::TRUNC|IO::CREAT ).and_yield( io )
55
+ io.should_receive( :puts ).with( /something bad happened/ )
56
+ io.should_receive( :flush )
57
+
58
+ expect {
59
+ Arrow::DispatcherLoader.new( 'hosts.yml' ).child_init( req )
60
+ }.to raise_error( RuntimeError, /something bad happened/ )
61
+ end
62
+
63
+
64
+ end
65
+
@@ -0,0 +1,781 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ begin
13
+ require 'spec'
14
+ require 'apache/fakerequest'
15
+ require 'arrow'
16
+ require 'spec/lib/helpers'
17
+ require 'arrow/formvalidator'
18
+ require 'date'
19
+ rescue LoadError
20
+ unless Object.const_defined?( :Gem )
21
+ require 'rubygems'
22
+ retry
23
+ end
24
+ raise
25
+ end
26
+
27
+
28
+ #####################################################################
29
+ ### C O N T E X T S
30
+ #####################################################################
31
+ TestProfile = {
32
+ :required => [ :required ],
33
+ :optional => %w{
34
+ optional number int_constraint bool_constraint email_constraint
35
+ host_constraint regexp_w_captures regexp_w_one_capture
36
+ alpha_constraint alphanumeric_constraint printable_constraint
37
+ proc_constraint uri_constraint
38
+ },
39
+ :constraints => {
40
+ :number => /^\d+$/,
41
+ :regexp_w_captures => /(\w+)(\S+)?/,
42
+ :regexp_w_one_capture => /(\w+)/,
43
+ :int_constraint => :integer,
44
+ :bool_constraint => :boolean,
45
+ :email_constraint => :email,
46
+ :uri_constraint => :uri,
47
+ :host_constraint => :hostname,
48
+ :alpha_constraint => :alpha,
49
+ :alphanumeric_constraint => :alphanumeric,
50
+ :printable_constraint => :printable,
51
+ :proc_constraint => Proc.new {|d| Date.parse(d) rescue nil },
52
+ },
53
+ }
54
+
55
+
56
+ describe Arrow::FormValidator do
57
+ include Arrow::SpecHelpers
58
+
59
+ before( :all ) do
60
+ setup_logging( :crit )
61
+ end
62
+
63
+ before(:each) do
64
+ @validator = Arrow::FormValidator.new( TestProfile )
65
+ end
66
+
67
+ after( :all ) do
68
+ reset_logging()
69
+ end
70
+
71
+
72
+
73
+ # Test index operator interface
74
+ it "should provide read and write access to valid args via the index operator" do
75
+ rval = nil
76
+
77
+ @validator.validate( {'required' => "1"} )
78
+ @validator[:required].should == "1"
79
+
80
+ @validator[:required] = "bar"
81
+ @validator["required"].should == "bar"
82
+ end
83
+
84
+
85
+ it "should untaint valid args if told to do so" do
86
+ rval = nil
87
+ tainted_one = "1"
88
+ tainted_one.taint
89
+
90
+ @validator.validate( {'required' => 1, 'number' => tainted_one},
91
+ :untaint_all_constraints => true )
92
+
93
+ Arrow::Logger.global.notice "Validator: %p" % [@validator]
94
+
95
+ @validator[:number].should == "1"
96
+ @validator[:number].tainted?.should be_false()
97
+ end
98
+
99
+
100
+ it "should untaint field names" do
101
+ rval = nil
102
+ tainted_one = "1"
103
+ tainted_one.taint
104
+
105
+ @validator.validate( {'required' => 1, 'number' => tainted_one},
106
+ :untaint_all_constraints => true )
107
+
108
+ Arrow::Logger.global.notice "Validator: %p" % [@validator]
109
+
110
+ @validator[:number].should == "1"
111
+ @validator[:number].tainted?.should be_false()
112
+ end
113
+
114
+
115
+ it "should return the capture from a regexp constraint if it has only one" do
116
+ rval = nil
117
+ params = { 'required' => 1, 'regexp_w_one_capture' => " ygdrassil " }
118
+
119
+ @validator.validate( params, :untaint_all_constraints => true )
120
+
121
+ Arrow::Logger.global.notice "Validator: %p" % [@validator]
122
+
123
+ @validator[:regexp_w_one_capture].should == 'ygdrassil'
124
+ end
125
+
126
+ it "should return the captures from a regexp constraint as an array if it has more than one" do
127
+ rval = nil
128
+ params = { 'required' => 1, 'regexp_w_captures' => " the1tree(!) " }
129
+
130
+ @validator.validate( params, :untaint_all_constraints => true )
131
+
132
+ Arrow::Logger.global.notice "Validator: %p" % [@validator]
133
+
134
+ @validator[:regexp_w_captures].should == ['the1tree', '(!)']
135
+ end
136
+
137
+ it "should return the captures from a regexp constraint as an array " +
138
+ "even if an optional capture doesn't match anything" do
139
+ rval = nil
140
+ params = { 'required' => 1, 'regexp_w_captures' => " the1tree " }
141
+
142
+ @validator.validate( params, :untaint_all_constraints => true )
143
+
144
+ Arrow::Logger.global.notice "Validator: %p" % [@validator]
145
+
146
+ @validator[:regexp_w_captures].should == ['the1tree', nil]
147
+ end
148
+
149
+ it "knows the names of fields that were required but missing from the parameters" do
150
+ @validator.validate( {} )
151
+
152
+ @validator.should have_errors()
153
+ @validator.should_not be_okay()
154
+
155
+ @validator.missing.should have(1).members
156
+ @validator.missing.should == ['required']
157
+ end
158
+
159
+ it "knows the names of fields that did not meet their constraints" do
160
+ params = {'number' => 'rhinoceros'}
161
+ @validator.validate( params )
162
+
163
+ @validator.should have_errors()
164
+ @validator.should_not be_okay()
165
+
166
+ @validator.invalid.should have(1).keys
167
+ @validator.invalid.keys.should == ['number']
168
+ end
169
+
170
+ it "can return a combined list of all problem parameters, which includes " +
171
+ " both missing and invalid fields" do
172
+ params = {'number' => 'rhinoceros'}
173
+ @validator.validate( params )
174
+
175
+ @validator.should have_errors()
176
+ @validator.should_not be_okay()
177
+
178
+ @validator.error_fields.should have(2).members
179
+ @validator.error_fields.should include('number')
180
+ @validator.error_fields.should include('required')
181
+ end
182
+
183
+ it "can return human descriptions of validation errors" do
184
+ params = {'number' => 'rhinoceros', 'unknown' => "1"}
185
+ @validator.validate( params )
186
+
187
+ @validator.error_messages.should have(2).members
188
+ @validator.error_messages.should include("Missing value for 'Required'")
189
+ @validator.error_messages.should include("Invalid value for 'Number'")
190
+ end
191
+
192
+ it "can include unknown fields in its human descriptions of validation errors" do
193
+ params = {'number' => 'rhinoceros', 'unknown' => "1"}
194
+ @validator.validate( params )
195
+
196
+ @validator.error_messages(true).should have(3).members
197
+ @validator.error_messages(true).should include("Missing value for 'Required'")
198
+ @validator.error_messages(true).should include("Invalid value for 'Number'")
199
+ @validator.error_messages(true).should include("Unknown parameter 'Unknown'")
200
+ end
201
+
202
+ it "can use provided descriptions of parameters when constructing human " +
203
+ "validation error messages" do
204
+ descs = {
205
+ :number => "Numeral",
206
+ :required => "Test Name",
207
+ }
208
+ params = {'number' => 'rhinoceros', 'unknown' => "1"}
209
+ @validator.validate( params, :descriptions => descs )
210
+
211
+ @validator.error_messages.should have(2).members
212
+ @validator.error_messages.should include("Missing value for 'Test Name'")
213
+ @validator.error_messages.should include("Invalid value for 'Numeral'")
214
+ end
215
+
216
+
217
+ it "capitalizes the names of simple fields for descriptions" do
218
+ @validator.get_description( "required" ).should == 'Required'
219
+ end
220
+
221
+ it "splits apart underbarred field names into capitalized words for descriptions" do
222
+ @validator.get_description( "rodent_size" ).should == 'Rodent Size'
223
+ end
224
+
225
+ it "uses the key for descriptions of hash fields" do
226
+ @validator.get_description( "rodent[size]" ).should == 'Size'
227
+ end
228
+
229
+ it "uses separate capitalized words for descriptions of hash fields with underbarred keys " do
230
+ @validator.get_description( "castle[baron_id]" ).should == 'Baron Id'
231
+ end
232
+
233
+ it "should be able to coalesce simple hash fields into a hash of validated values" do
234
+ @validator.validate( {'rodent[size]' => 'unusual'}, :optional => ['rodent[size]'] )
235
+
236
+ @validator.valid.should == {'rodent' => {'size' => 'unusual'}}
237
+ end
238
+
239
+ it "should be able to coalesce complex hash fields into a nested hash of validated values" do
240
+ profile = {
241
+ :optional => [
242
+ 'recipe[ingredient][name]',
243
+ 'recipe[ingredient][cost]',
244
+ 'recipe[yield]'
245
+ ]
246
+ }
247
+ args = {
248
+ 'recipe[ingredient][name]' => 'nutmeg',
249
+ 'recipe[ingredient][cost]' => '$0.18',
250
+ 'recipe[yield]' => '2 loaves',
251
+ }
252
+
253
+ @validator.validate( args, profile )
254
+ @validator.valid.should == {
255
+ 'recipe' => {
256
+ 'ingredient' => { 'name' => 'nutmeg', 'cost' => '$0.18' },
257
+ 'yield' => '2 loaves'
258
+ }
259
+ }
260
+ end
261
+
262
+ it "should untaint both keys and values in complex hash fields if untainting is turned on" do
263
+ profile = {
264
+ :required => [
265
+ 'recipe[ingredient][rarity]',
266
+ ],
267
+ :optional => [
268
+ 'recipe[ingredient][name]',
269
+ 'recipe[ingredient][cost]',
270
+ 'recipe[yield]'
271
+ ],
272
+ :constraints => {
273
+ 'recipe[ingredient][rarity]' => /^([\w\-]+)$/,
274
+ },
275
+ :untaint_all_constraints => true,
276
+ }
277
+ args = {
278
+ 'recipe[ingredient][rarity]'.taint => 'super-rare'.taint,
279
+ 'recipe[ingredient][name]'.taint => 'nutmeg'.taint,
280
+ 'recipe[ingredient][cost]'.taint => '$0.18'.taint,
281
+ 'recipe[yield]'.taint => '2 loaves'.taint,
282
+ }
283
+
284
+ @validator.validate( args, profile )
285
+
286
+ @validator.valid.should == {
287
+ 'recipe' => {
288
+ 'ingredient' => { 'name' => 'nutmeg', 'cost' => '$0.18', 'rarity' => 'super-rare' },
289
+ 'yield' => '2 loaves'
290
+ }
291
+ }
292
+
293
+ @validator.valid.keys.all? {|key| key.should_not be_tainted() }
294
+ @validator.valid.values.all? {|key| key.should_not be_tainted() }
295
+ @validator.valid['recipe'].keys.all? {|key| key.should_not be_tainted() }
296
+ @validator.valid['recipe']['ingredient'].keys.all? {|key| key.should_not be_tainted() }
297
+ @validator.valid['recipe']['yield'].should_not be_tainted()
298
+ @validator.valid['recipe']['ingredient']['rarity'].should_not be_tainted()
299
+ @validator.valid['recipe']['ingredient']['name'].should_not be_tainted()
300
+ @validator.valid['recipe']['ingredient']['cost'].should_not be_tainted()
301
+ end
302
+
303
+ it "accepts the value 'true' for fields with boolean constraints" do
304
+ params = {'required' => '1', 'bool_constraint' => 'true'}
305
+
306
+ @validator.validate( params )
307
+
308
+ @validator.should be_okay()
309
+ @validator.should_not have_errors()
310
+
311
+ @validator[:bool_constraint].should be_true()
312
+ end
313
+
314
+ it "accepts the value 't' for fields with boolean constraints" do
315
+ params = {'required' => '1', 'bool_constraint' => 't'}
316
+
317
+ @validator.validate( params )
318
+
319
+ @validator.should be_okay()
320
+ @validator.should_not have_errors()
321
+
322
+ @validator[:bool_constraint].should be_true()
323
+ end
324
+
325
+ it "accepts the value 'yes' for fields with boolean constraints" do
326
+ params = {'required' => '1', 'bool_constraint' => 'yes'}
327
+
328
+ @validator.validate( params )
329
+
330
+ @validator.should be_okay()
331
+ @validator.should_not have_errors()
332
+
333
+ @validator[:bool_constraint].should be_true()
334
+ end
335
+
336
+ it "accepts the value 'y' for fields with boolean constraints" do
337
+ params = {'required' => '1', 'bool_constraint' => 'y'}
338
+
339
+ @validator.validate( params )
340
+
341
+ @validator.should be_okay()
342
+ @validator.should_not have_errors()
343
+
344
+ @validator[:bool_constraint].should be_true()
345
+ end
346
+
347
+ it "accepts the value '1' for fields with boolean constraints" do
348
+ params = {'required' => '1', 'bool_constraint' => '1'}
349
+
350
+ @validator.validate( params )
351
+
352
+ @validator.should be_okay()
353
+ @validator.should_not have_errors()
354
+
355
+ @validator[:bool_constraint].should be_true()
356
+ end
357
+
358
+ it "accepts the value 'false' for fields with boolean constraints" do
359
+ params = {'required' => '1', 'bool_constraint' => 'false'}
360
+
361
+ @validator.validate( params )
362
+
363
+ @validator.should be_okay()
364
+ @validator.should_not have_errors()
365
+
366
+ @validator[:bool_constraint].should be_false()
367
+ end
368
+
369
+ it "accepts the value 'f' for fields with boolean constraints" do
370
+ params = {'required' => '1', 'bool_constraint' => 'f'}
371
+
372
+ @validator.validate( params )
373
+
374
+ @validator.should be_okay()
375
+ @validator.should_not have_errors()
376
+
377
+ @validator[:bool_constraint].should be_false()
378
+ end
379
+
380
+ it "accepts the value 'no' for fields with boolean constraints" do
381
+ params = {'required' => '1', 'bool_constraint' => 'no'}
382
+
383
+ @validator.validate( params )
384
+
385
+ @validator.should be_okay()
386
+ @validator.should_not have_errors()
387
+
388
+ @validator[:bool_constraint].should be_false()
389
+ end
390
+
391
+ it "accepts the value 'n' for fields with boolean constraints" do
392
+ params = {'required' => '1', 'bool_constraint' => 'n'}
393
+
394
+ @validator.validate( params )
395
+
396
+ @validator.should be_okay()
397
+ @validator.should_not have_errors()
398
+
399
+ @validator[:bool_constraint].should be_false()
400
+ end
401
+
402
+ it "accepts the value '0' for fields with boolean constraints" do
403
+ params = {'required' => '1', 'bool_constraint' => '0'}
404
+
405
+ @validator.validate( params )
406
+
407
+ @validator.should be_okay()
408
+ @validator.should_not have_errors()
409
+
410
+ @validator[:bool_constraint].should be_false()
411
+ end
412
+
413
+ it "rejects non-boolean parameters for fields with boolean constraints" do
414
+ params = {'required' => '1', 'bool_constraint' => 'peanut'}
415
+
416
+ @validator.validate( params )
417
+
418
+ @validator.should_not be_okay()
419
+ @validator.should have_errors()
420
+
421
+ @validator[:bool_constraint].should be_nil()
422
+ end
423
+
424
+ it "accepts simple integers for fields with integer constraints" do
425
+ params = {'required' => '1', 'int_constraint' => '11'}
426
+
427
+ @validator.validate( params )
428
+
429
+ @validator.should be_okay()
430
+ @validator.should_not have_errors()
431
+
432
+ @validator[:int_constraint].should == 11
433
+ end
434
+
435
+ it "accepts '0' for fields with integer constraints" do
436
+ params = {'required' => '1', 'int_constraint' => '0'}
437
+
438
+ @validator.validate( params )
439
+
440
+ @validator.should be_okay()
441
+ @validator.should_not have_errors()
442
+
443
+ @validator[:int_constraint].should == 0
444
+ end
445
+
446
+ it "accepts negative integers for fields with integer constraints" do
447
+ params = {'required' => '1', 'int_constraint' => '-407'}
448
+
449
+ @validator.validate( params )
450
+
451
+ @validator.should be_okay()
452
+ @validator.should_not have_errors()
453
+
454
+ @validator[:int_constraint].should == -407
455
+ end
456
+
457
+ it "rejects non-integers for fields with integer constraints" do
458
+ params = {'required' => '1', 'int_constraint' => '11.1'}
459
+
460
+ @validator.validate( params )
461
+
462
+ @validator.should_not be_okay()
463
+ @validator.should have_errors()
464
+
465
+ @validator[:int_constraint].should be_nil()
466
+ end
467
+
468
+ it "rejects integer values with other cruft in them for fields with integer constraints" do
469
+ params = {'required' => '1', 'int_constraint' => '88licks'}
470
+
471
+ @validator.validate( params )
472
+
473
+ @validator.should_not be_okay()
474
+ @validator.should have_errors()
475
+
476
+ @validator[:int_constraint].should be_nil()
477
+ end
478
+
479
+ ValidURIs = %w{
480
+ http://127.0.0.1
481
+ http://127.0.0.1/
482
+ http://[127.0.0.1]/
483
+ http://ruby-lang.org/
484
+ http://www.rocketboom.com/vlog/rb_08_feb_01
485
+ http://del.icio.us/search/?fr=del_icio_us&p=ruby+arrow&type=all
486
+ http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8080/index.html
487
+ http://[1080:0:0:0:8:800:200C:417A]/index.html
488
+ http://[3ffe:2a00:100:7031::1]
489
+ http://[1080::8:800:200C:417A]/foo
490
+ http://[::192.9.5.5]/ipng
491
+ http://[::FFFF:129.144.52.38]:3474/index.html
492
+ http://[2010:836B:4179::836B:4179]
493
+
494
+ https://mail.google.com/
495
+ https://127.0.0.1/
496
+ https://r4.com:8080/
497
+
498
+ ftp://ftp.ruby-lang.org/pub/ruby/1.0/ruby-0.49.tar.gz
499
+ ftp://crashoverride:god@gibson.ellingsonmineral.com/root/.workspace/.garbage.
500
+
501
+ ldap:/o=University%20of%20Michigan,c=US
502
+ ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
503
+ ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress
504
+ ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)
505
+ ldap://ldap.itd.umich.edu/c=GB?objectClass?one
506
+ ldap://ldap.question.com/o=Question%3f,c=US?mail
507
+ ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04)
508
+ ldap:/??sub??bindname=cn=Manager%2co=Foo
509
+ ldap:/??sub??!bindname=cn=Manager%2co=Foo
510
+ }
511
+
512
+ ValidURIs.each do |uri_string|
513
+ it "accepts #{uri_string} for fields with URI constraints" do
514
+ params = {'required' => '1', 'uri_constraint' => uri_string}
515
+
516
+ @validator.validate( params )
517
+
518
+ @validator.should be_okay()
519
+ @validator.should_not have_errors()
520
+
521
+ @validator[:uri_constraint].should be_a_kind_of( URI::Generic )
522
+ @validator[:uri_constraint].to_s.should == uri_string
523
+ end
524
+ end
525
+
526
+ # :FIXME: I don't know LDAP uris very well, so I'm not sure how they're likely to
527
+ # be invalidly-occurring in the wild
528
+ InvalidURIs = %W{
529
+ glark:
530
+
531
+ http:
532
+ http://
533
+ http://_com/vlog/rb_08_feb_01
534
+ http://del.icio.us/search/\x20\x14\x18
535
+ http://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/index.html
536
+ http://1080:0:0:0:8:800:200C:417A/index.html
537
+ http://3ffe:2a00:100:7031::1
538
+ http://1080::8:800:200C:417A/foo
539
+ http://::192.9.5.5/ipng
540
+ http://::FFFF:129.144.52.38:80/index.html
541
+ http://2010:836B:4179::836B:4179
542
+
543
+ https:
544
+ https://user:pass@/
545
+ https://r4.com:nonnumericport/
546
+
547
+ ftp:
548
+ ftp:ruby-0.49.tar.gz
549
+ ftp://crashoverride:god@/root/.workspace/.garbage.
550
+
551
+ ldap:
552
+ ldap:/o=University\x20of\x20Michigan,c=US
553
+ ldap://ldap.itd.umich.edu/o=University+\x00of+Michigan
554
+ }
555
+
556
+ InvalidURIs.each do |uri_string|
557
+ it "rejects #{uri_string} for fields with URI constraints" do
558
+ params = {'required' => '1', 'uri_constraint' => uri_string}
559
+
560
+ # lambda {
561
+ @validator.validate( params )
562
+ # }.should_not raise_error()
563
+
564
+ @validator.should_not be_okay()
565
+ @validator.should have_errors()
566
+
567
+ @validator[:uri_constraint].should be_nil()
568
+ end
569
+ end
570
+
571
+ it "accepts simple RFC822 addresses for fields with email constraints" do
572
+ params = {'required' => '1', 'email_constraint' => 'jrandom@hacker.ie'}
573
+
574
+ @validator.validate( params )
575
+
576
+ @validator.should be_okay()
577
+ @validator.should_not have_errors()
578
+
579
+ @validator[:email_constraint].should == 'jrandom@hacker.ie'
580
+ end
581
+
582
+ it "accepts hyphenated domains in RFC822 addresses for fields with email constraints" do
583
+ params = {'required' => '1', 'email_constraint' => 'jrandom@just-another-hacquer.fr'}
584
+
585
+ @validator.validate( params )
586
+
587
+ @validator.should be_okay()
588
+ @validator.should_not have_errors()
589
+
590
+ @validator[:email_constraint].should == 'jrandom@just-another-hacquer.fr'
591
+ end
592
+
593
+ ComplexAddresses = [
594
+ 'ruby+hacker@random-example.org',
595
+ '"ruby hacker"@ph8675309.org',
596
+ 'jrandom@[ruby hacquer].com',
597
+ 'abcdefghijklmnopqrstuvwxyz@abcdefghijklmnopqrstuvwxyz',
598
+ ]
599
+ ComplexAddresses.each do |addy|
600
+ it "accepts #{addy} for fields with email constraints" do
601
+ params = {'required' => '1', 'email_constraint' => addy}
602
+
603
+ @validator.validate( params )
604
+
605
+ @validator.should be_okay()
606
+ @validator.should_not have_errors()
607
+
608
+ @validator[:email_constraint].should == addy
609
+ end
610
+ end
611
+
612
+
613
+ BogusAddresses = [
614
+ 'jrandom@hacquer com',
615
+ 'jrandom@ruby hacquer.com',
616
+ 'j random@rubyhacquer.com',
617
+ 'j random@ruby|hacquer.com',
618
+ 'j:random@rubyhacquer.com',
619
+ ]
620
+ BogusAddresses.each do |addy|
621
+ it "rejects #{addy} for fields with email constraints" do
622
+ params = {'required' => '1', 'email_constraint' => addy}
623
+
624
+ @validator.validate( params )
625
+
626
+ @validator.should_not be_okay()
627
+ @validator.should have_errors()
628
+
629
+ @validator[:email_constraint].should be_nil()
630
+ end
631
+ end
632
+
633
+ it "accepts simple hosts for fields with host constraints" do
634
+ params = {'required' => '1', 'host_constraint' => 'deveiate.org'}
635
+
636
+ @validator.validate( params )
637
+
638
+ @validator.should be_okay()
639
+ @validator.should_not have_errors()
640
+
641
+ @validator[:host_constraint].should == 'deveiate.org'
642
+ end
643
+
644
+ it "accepts hyphenated hosts for fields with host constraints" do
645
+ params = {'required' => '1', 'host_constraint' => 'your-characters-can-fly.kr'}
646
+
647
+ @validator.validate( params )
648
+
649
+ @validator.should be_okay()
650
+ @validator.should_not have_errors()
651
+
652
+ @validator[:host_constraint].should == 'your-characters-can-fly.kr'
653
+ end
654
+
655
+ BogusHosts = [
656
+ '.',
657
+ 'glah ',
658
+ 'glah[lock]',
659
+ 'glah.be$',
660
+ 'indus«tree».com',
661
+ ]
662
+
663
+ BogusHosts.each do |hostname|
664
+ it "rejects #{hostname} for fields with host constraints" do
665
+ params = {'required' => '1', 'host_constraint' => hostname}
666
+
667
+ @validator.validate( params )
668
+
669
+ @validator.should_not be_okay()
670
+ @validator.should have_errors()
671
+
672
+ @validator[:host_constraint].should be_nil()
673
+ end
674
+ end
675
+
676
+ it "accepts alpha characters for fields with alpha constraints" do
677
+ params = {'required' => '1', 'alpha_constraint' => 'abelincoln'}
678
+
679
+ @validator.validate( params )
680
+
681
+ @validator.should be_okay()
682
+ @validator.should_not have_errors()
683
+
684
+ @validator[:alpha_constraint].should == 'abelincoln'
685
+ end
686
+
687
+ it "rejects non-alpha characters for fields with alpha constraints" do
688
+ params = {'required' => '1', 'alpha_constraint' => 'duck45'}
689
+
690
+ @validator.validate( params )
691
+
692
+ @validator.should_not be_okay()
693
+ @validator.should have_errors()
694
+
695
+ @validator[:alpha_constraint].should be_nil()
696
+ end
697
+
698
+ ### 'alphanumeric'
699
+ it "accepts alphanumeric characters for fields with alphanumeric constraints" do
700
+ params = {'required' => '1', 'alphanumeric_constraint' => 'zombieabe11'}
701
+
702
+ @validator.validate( params )
703
+
704
+ @validator.should be_okay()
705
+ @validator.should_not have_errors()
706
+
707
+ @validator[:alphanumeric_constraint].should == 'zombieabe11'
708
+ end
709
+
710
+ it "rejects non-alphanumeric characters for fields with alphanumeric constraints" do
711
+ params = {'required' => '1', 'alphanumeric_constraint' => 'duck!ling'}
712
+
713
+ @validator.validate( params )
714
+
715
+ @validator.should_not be_okay()
716
+ @validator.should have_errors()
717
+
718
+ @validator[:alphanumeric_constraint].should be_nil()
719
+ end
720
+
721
+ ### 'printable'
722
+ it "accepts printable characters for fields with 'printable' constraints" do
723
+ test_content = <<-EOF
724
+ I saw you with some kind of medical apparatus strapped to your
725
+ spine. It was all glass and metal, a great crystaline hypodermic
726
+ spider, carrying you into the aether with a humming, crackling sound.
727
+ EOF
728
+
729
+ params = {
730
+ 'required' => '1',
731
+ 'printable_constraint' => test_content
732
+ }
733
+
734
+ @validator.validate( params )
735
+
736
+ @validator.should be_okay()
737
+ @validator.should_not have_errors()
738
+
739
+ @validator[:printable_constraint].should == test_content
740
+ end
741
+
742
+ it "rejects non-printable characters for fields with 'printable' constraints" do
743
+ params = {'required' => '1', 'printable_constraint' => %{\0Something cold\0}}
744
+
745
+ @validator.validate( params )
746
+
747
+ @validator.should_not be_okay()
748
+ @validator.should have_errors()
749
+
750
+ @validator[:printable_constraint].should be_nil()
751
+ end
752
+
753
+
754
+ it "accepts parameters for fields with Proc constraints if the Proc " +
755
+ "returns a true value" do
756
+ test_date = '2007-07-17'
757
+ params = {'required' => '1', 'proc_constraint' => test_date}
758
+
759
+ @validator.validate( params )
760
+
761
+ @validator.should be_okay()
762
+ @validator.should_not have_errors()
763
+
764
+ @validator[:proc_constraint].should == Date.parse( test_date )
765
+ end
766
+
767
+ it "rejects parameters for fields with Proc constraints if the Proc " +
768
+ "returns a false value" do
769
+
770
+ params = {'required' => '1', 'proc_constraint' => %{::::}}
771
+
772
+ @validator.validate( params )
773
+
774
+ @validator.should_not be_okay()
775
+ @validator.should have_errors()
776
+
777
+ @validator[:proc_constraint].should be_nil()
778
+ end
779
+
780
+ end
781
+