arrow 1.0.7

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