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,187 @@
1
+ #
2
+ # Rake tasklib for testing tasks
3
+
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ unless defined?( COVERAGE_MINIMUM )
10
+ if ENV['COVVERAGE_MINIMUM']
11
+ COVERAGE_MINIMUM = Float( ENV['COVERAGE_MINIMUM'] )
12
+ else
13
+ COVERAGE_MINIMUM = 85.0
14
+ end
15
+ end
16
+ SPEC_FILES = [] unless defined?( SPEC_FILES )
17
+ TEST_FILES = [] unless defined?( TEST_FILES )
18
+
19
+ COMMON_SPEC_OPTS = ['-Du'] unless defined?( COMMON_SPEC_OPTS )
20
+
21
+ COVERAGE_TARGETDIR = BASEDIR + 'coverage' unless defined?( COVERAGE_TARGETDIR )
22
+ RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' unless
23
+ defined?( RCOV_EXCLUDES )
24
+
25
+
26
+ desc "Run all defined tests"
27
+ task :test do
28
+ unless SPEC_FILES.empty?
29
+ log "Running specs"
30
+ Rake::Task['spec:quiet'].invoke
31
+ end
32
+
33
+ unless TEST_FILES.empty?
34
+ log "Running unit tests"
35
+ Rake::Task[:unittests].invoke
36
+ end
37
+ end
38
+
39
+
40
+ ### RSpec specifications
41
+ begin
42
+ gem 'rspec', '>= 1.1.3'
43
+
44
+ require 'spec'
45
+ require 'spec/rake/spectask'
46
+
47
+ ### Task: spec
48
+ desc "Run specs"
49
+ task :spec => 'spec:doc'
50
+
51
+ namespace :spec do
52
+ desc "Run rspec every time there's a change to one of the files"
53
+ task :autotest do
54
+ require 'autotest/rspec'
55
+
56
+ autotester = Autotest::Rspec.new
57
+ autotester.run
58
+ end
59
+
60
+ desc "Generate regular color 'doc' spec output"
61
+ Spec::Rake::SpecTask.new( :doc ) do |task|
62
+ task.spec_files = SPEC_FILES
63
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 's', '-c']
64
+ end
65
+
66
+ desc "Generate spec output with profiling"
67
+ Spec::Rake::SpecTask.new( :profile ) do |task|
68
+ task.spec_files = SPEC_FILES
69
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'o']
70
+ end
71
+
72
+ desc "Generate quiet non-colored plain-text output"
73
+ Spec::Rake::SpecTask.new( :quiet ) do |task|
74
+ task.spec_files = SPEC_FILES
75
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'p']
76
+ end
77
+
78
+ desc "Generate HTML output"
79
+ Spec::Rake::SpecTask.new( :html ) do |task|
80
+ task.spec_files = SPEC_FILES
81
+ task.spec_opts = COMMON_SPEC_OPTS + ['-f', 'h']
82
+ end
83
+
84
+ end
85
+ rescue LoadError => err
86
+ task :no_rspec do
87
+ $stderr.puts "Specification tasks not defined: %s" % [ err.message ]
88
+ end
89
+
90
+ task :spec => :no_rspec
91
+ namespace :spec do
92
+ task :autotest => :no_rspec
93
+ task :doc => :no_rspec
94
+ task :profile => :no_rspec
95
+ task :quiet => :no_rspec
96
+ task :html => :no_rspec
97
+ end
98
+ end
99
+
100
+
101
+ ### Test::Unit tests
102
+ begin
103
+ require 'rake/testtask'
104
+
105
+ Rake::TestTask.new( :unittests ) do |task|
106
+ task.libs += [LIBDIR]
107
+ task.test_files = TEST_FILES
108
+ task.verbose = true
109
+ end
110
+
111
+ rescue LoadError => err
112
+ task :no_test do
113
+ $stderr.puts "Test tasks not defined: %s" % [ err.message ]
114
+ end
115
+
116
+ task :unittests => :no_rspec
117
+ end
118
+
119
+
120
+ ### RCov (via RSpec) tasks
121
+ begin
122
+ gem 'rcov'
123
+ gem 'rspec', '>= 1.1.3'
124
+
125
+ require 'spec'
126
+ require 'rcov'
127
+
128
+ ### Task: coverage (via RCov)
129
+ desc "Build test coverage reports"
130
+ unless SPEC_FILES.empty?
131
+ Spec::Rake::SpecTask.new( :coverage ) do |task|
132
+ task.spec_files = SPEC_FILES
133
+ task.libs += [LIBDIR]
134
+ task.spec_opts = ['-f', 'p', '-b']
135
+ task.rcov_opts = RCOV_OPTS
136
+ task.rcov = true
137
+ end
138
+ end
139
+
140
+
141
+ ### Task: rcov
142
+ task :rcov => :coverage
143
+
144
+ ### Other coverage tasks
145
+ namespace :coverage do
146
+ desc "Generate a detailed text coverage report"
147
+ Spec::Rake::SpecTask.new( :text ) do |task|
148
+ task.spec_files = SPEC_FILES
149
+ task.rcov_opts = RCOV_OPTS + ['--text-report']
150
+ task.rcov = true
151
+ end
152
+
153
+ desc "Show differences in coverage from last run"
154
+ Spec::Rake::SpecTask.new( :diff ) do |task|
155
+ task.spec_files = SPEC_FILES
156
+ task.spec_opts = ['-f', 'p', '-b']
157
+ task.rcov_opts = RCOV_OPTS - ['--save'] + ['--text-coverage-diff']
158
+ task.rcov = true
159
+ end
160
+
161
+ desc "Run RCov in 'spec-only' mode to check coverage from specs"
162
+ Spec::Rake::SpecTask.new( :speconly ) do |task|
163
+ task.spec_files = SPEC_FILES
164
+ task.rcov_opts = ['--exclude', RCOV_EXCLUDES, '--text-report', '--save']
165
+ task.rcov = true
166
+ end
167
+ end
168
+
169
+ CLOBBER.include( COVERAGE_TARGETDIR )
170
+
171
+ rescue LoadError => err
172
+ task :no_rcov do
173
+ $stderr.puts "Coverage tasks not defined: RSpec+RCov tasklib not available: %s" %
174
+ [ err.message ]
175
+ end
176
+
177
+ task :coverage => :no_rcov
178
+ task :clobber_coverage
179
+ task :rcov => :no_rcov
180
+ namespace :coverage do
181
+ task :text => :no_rcov
182
+ task :diff => :no_rcov
183
+ end
184
+ task :verify => :no_rcov
185
+ end
186
+
187
+
@@ -0,0 +1,64 @@
1
+ #####################################################################
2
+ ### S U B V E R S I O N T A S K S A N D H E L P E R S
3
+ #####################################################################
4
+
5
+ require 'rake/tasklib'
6
+
7
+ #
8
+ # Work around the inexplicable behaviour of the original RDoc::VerifyTask, which
9
+ # errors if your coverage isn't *exactly* the threshold.
10
+ #
11
+
12
+ # A task that can verify that the RCov coverage doesn't
13
+ # drop below a certain threshold. It should be run after
14
+ # running Spec::Rake::SpecTask.
15
+ class VerifyTask < Rake::TaskLib
16
+
17
+ COVERAGE_PERCENTAGE_PATTERN =
18
+ %r{<tt class='coverage_code'>(\d+\.\d+)%</tt>}
19
+
20
+ # Name of the task. Defaults to :verify_rcov
21
+ attr_accessor :name
22
+
23
+ # Path to the index.html file generated by RCov, which
24
+ # is the file containing the total coverage.
25
+ # Defaults to 'coverage/index.html'
26
+ attr_accessor :index_html
27
+
28
+ # Whether or not to output details. Defaults to true.
29
+ attr_accessor :verbose
30
+
31
+ # The threshold value (in percent) for coverage. If the
32
+ # actual coverage is not equal to this value, the task will raise an
33
+ # exception.
34
+ attr_accessor :threshold
35
+
36
+ def initialize( name=:verify )
37
+ @name = name
38
+ @index_html = 'coverage/index.html'
39
+ @verbose = true
40
+ yield self if block_given?
41
+ raise "Threshold must be set" if @threshold.nil?
42
+ define
43
+ end
44
+
45
+ def define
46
+ desc "Verify that rcov coverage is at least #{threshold}%"
47
+
48
+ task @name do
49
+ total_coverage = nil
50
+ if match = File.read( index_html ).match( COVERAGE_PERCENTAGE_PATTERN )
51
+ total_coverage = Float( match[1] )
52
+ else
53
+ raise "Couldn't find the coverage percentage in #{index_html}"
54
+ end
55
+
56
+ puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
57
+ if total_coverage < threshold
58
+ raise "Coverage must be at least #{threshold}% but was #{total_coverage}%"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,157 @@
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
+
13
+ require 'spec'
14
+ require 'spec/lib/constants'
15
+ require 'spec/lib/helpers'
16
+ require 'arrow'
17
+ require 'arrow/constants'
18
+ require 'arrow/acceptparam'
19
+
20
+
21
+ #####################################################################
22
+ ### C O N T E X T S
23
+ #####################################################################
24
+
25
+ describe Arrow::AcceptParam do
26
+ include Arrow::SpecHelpers
27
+
28
+ before( :all ) do
29
+ setup_logging( :crit )
30
+ end
31
+
32
+ after( :all ) do
33
+ reset_logging()
34
+ end
35
+
36
+
37
+ ValidHeaders = {
38
+ '*/*' =>
39
+ {:type => nil, :subtype => nil, :qval => 1.0},
40
+ '*/*; q=0.1' =>
41
+ {:type => nil, :subtype => nil, :qval => 0.1},
42
+ '*/*;q=0.1' =>
43
+ {:type => nil, :subtype => nil, :qval => 0.1},
44
+ 'image/*' =>
45
+ {:type => 'image', :subtype => nil, :qval => 1.0},
46
+ 'image/*; q=0.18' =>
47
+ {:type => 'image', :subtype => nil, :qval => 0.18},
48
+ 'image/*;q=0.4' =>
49
+ {:type => 'image', :subtype => nil, :qval => 0.4},
50
+ 'image/*;q=0.9; porn=0; anseladams=1' =>
51
+ {:type => 'image', :subtype => nil, :qval => 0.9,
52
+ :extensions => %w[anseladams=1 porn=0]},
53
+ 'image/png' =>
54
+ {:type => 'image', :subtype => 'png', :qval => 1.0},
55
+ 'IMAGE/pNg' =>
56
+ {:type => 'image', :subtype => 'png', :qval => 1.0},
57
+ 'application/x-porno' =>
58
+ {:type => 'application', :subtype => 'x-porno', :qval => 1.0},
59
+ 'image/png; q=0.2' =>
60
+ {:type => 'image', :subtype => 'png', :qval => 0.2},
61
+ 'image/x-giraffes;q=0.2' =>
62
+ {:type => 'image', :subtype => 'x-giraffes', :qval => 0.2},
63
+ 'example/pork; headcheese=0;withfennel=1' =>
64
+ {:type => 'example', :subtype => 'pork', :qval => 1.0,
65
+ :extensions => %w[headcheese=0 withfennel=1]},
66
+ 'model/vnd.moml+xml' =>
67
+ {:type => 'model', :subtype => 'vnd.moml+xml', :qval => 1.0},
68
+ 'model/parasolid.transmit.binary; q=0.2' =>
69
+ {:type => 'model', :subtype => 'parasolid.transmit.binary',
70
+ :qval => 0.2},
71
+ 'image/png; q=0.2; compression=1' =>
72
+ {:type => 'image', :subtype => 'png', :qval => 0.2,
73
+ :extensions => %w[compression=1]},
74
+ }
75
+
76
+
77
+ it "parses valid Accept header values" do
78
+ ValidHeaders.each do |hdr, expectations|
79
+ rval = Arrow::AcceptParam.parse( hdr )
80
+
81
+ rval.should be_an_instance_of( Arrow::AcceptParam )
82
+ rval.type.should == expectations[:type]
83
+ rval.subtype.should == expectations[:subtype]
84
+ rval.qvalue.should == expectations[:qval]
85
+ if expectations[:extensions]
86
+ expectations[:extensions].each do |ext|
87
+ rval.extensions.should include(ext)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ it "is lenient (but warns) about invalid qvalues" do
94
+ rval = nil
95
+ lambda {
96
+ rval = Arrow::AcceptParam.parse( '*/*; q=18' )
97
+ }.should_not raise_error()
98
+
99
+ rval.should be_an_instance_of( Arrow::AcceptParam )
100
+ rval.qvalue.should == 1.0
101
+ end
102
+
103
+
104
+ it "rejects invalid Accept header values" do
105
+ lambda {
106
+ Arrow::AcceptParam.parse( 'porksausage' )
107
+ }.should raise_error()
108
+ end
109
+
110
+
111
+ it "can represent itself in a human-readable object format" do
112
+ header = "text/html; q=0.9; level=2"
113
+ acceptparam = Arrow::AcceptParam.parse( header )
114
+ acceptparam.inspect.should =~ %r{AcceptParam.*text/html.*q=0.9}
115
+ end
116
+
117
+
118
+ it "can represent itself as an Accept header" do
119
+ header = "text/html;q=0.9;level=2"
120
+ acceptparam = Arrow::AcceptParam.parse( header )
121
+ acceptparam.to_s.should == header
122
+ end
123
+
124
+
125
+ it "can compare and sort on specificity" do
126
+ header = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9," +
127
+ "text/html;q=0.9;level=1,text/plain;q=0.8,image/png,*/*;q=0.5"
128
+ params = header.split(/\s*,\s*/).collect {|par|
129
+ Arrow::AcceptParam.parse( par )
130
+ }.sort
131
+
132
+ params[0].to_s.should == 'application/xhtml+xml;q=1.0'
133
+ params[1].to_s.should == 'application/xml;q=1.0'
134
+ params[2].to_s.should == 'image/png;q=1.0'
135
+ params[3].to_s.should == 'text/xml;q=1.0'
136
+ params[4].to_s.should == 'text/html;q=0.9;level=1'
137
+ params[5].to_s.should == 'text/html;q=0.9'
138
+ params[6].to_s.should == 'text/plain;q=0.8'
139
+ params[7].to_s.should == '*/*;q=0.5'
140
+ end
141
+
142
+
143
+ it "can be compared against strings" do
144
+ specific_param = Arrow::AcceptParam.parse( 'text/html' )
145
+ type_wildcard_param = Arrow::AcceptParam.parse( '*/*' )
146
+ subtype_wildcard_param = Arrow::AcceptParam.parse( 'image/*' )
147
+
148
+ ( specific_param =~ 'text/html' ).should be_true()
149
+ ( specific_param =~ 'image/png' ).should be_false()
150
+
151
+ ( subtype_wildcard_param =~ 'image/png' ).should be_true()
152
+ ( subtype_wildcard_param =~ 'image/jpeg' ).should be_true()
153
+ ( subtype_wildcard_param =~ 'text/plain' ).should be_false()
154
+ end
155
+ end
156
+
157
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Specification for the Arrow::Applet class
4
+ # $Id$
5
+ #
6
+ # Copyright (c) 2004-2008 The FaerieMUD Consortium. Most rights reserved.
7
+ #
8
+
9
+ BEGIN {
10
+ require 'pathname'
11
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
12
+
13
+ libdir = basedir + "lib"
14
+
15
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
16
+ }
17
+
18
+ require 'spec'
19
+
20
+ require 'apache/fakerequest'
21
+ require 'arrow'
22
+ require 'arrow/applet'
23
+
24
+ require 'spec/lib/helpers'
25
+
26
+
27
+ #####################################################################
28
+ ### C O N T E X T S
29
+ #####################################################################
30
+
31
+ describe Arrow::Applet do
32
+ include Arrow::SpecHelpers
33
+
34
+ before( :all ) do
35
+ setup_logging( :crit )
36
+ end
37
+
38
+ after( :all ) do
39
+ reset_logging()
40
+ end
41
+
42
+
43
+ it "has a signature even if it doesn't declare one" do
44
+ plainclass = Class.new( Arrow::Applet )
45
+ plainclass.signature.should be_an_instance_of( Arrow::Applet::SignatureStruct )
46
+ plainclass.signature.members.should include( 'name' )
47
+ end
48
+
49
+ it "subclasses know if further subclasses of itself have been loaded" do
50
+ plainclass = Class.new( Arrow::Applet )
51
+ plainclass.should_not be_inherited_from()
52
+ subclass = Class.new( plainclass )
53
+ plainclass.should be_inherited_from()
54
+ end
55
+
56
+ it "raises an exception if an action method that doesn't accept arguments is declared" do
57
+ lambda {
58
+ Class.new( Arrow::Applet ) do
59
+ def no_arg_action
60
+ raise "shouldn't be callable"
61
+ end
62
+ end
63
+ }.should raise_error( ::ScriptError, 'Inappropriate arity for no_arg_action' )
64
+ end
65
+
66
+
67
+ describe "concrete child and grandchild classes" do
68
+ before(:each) do
69
+ @appletclass = Class.new( Arrow::Applet ) do
70
+ default_action :test
71
+
72
+ applet_name "SuperApplet"
73
+ applet_description "A superclass applet"
74
+ applet_version 177
75
+ applet_maintainer "ged@FaerieMUD.org"
76
+ end
77
+ @appletsubclass = Class.new( @appletclass )
78
+
79
+ @applet = @appletclass.new( nil, nil, nil )
80
+ end
81
+
82
+
83
+ it "don't return superclass applets from ::load by default" do
84
+ Kernel.stub!( :load ).and_return do
85
+ Arrow::Applet.newly_loaded << @appletclass << @appletsubclass
86
+ end
87
+
88
+ applets = Arrow::Applet.load( 'stubbedapplet' )
89
+
90
+ applets.should have(1).member
91
+ applets.should_not include( @appletclass )
92
+ applets.should include( @appletsubclass )
93
+ end
94
+
95
+
96
+ it "return superclass applets from ::load if asked to do so" do
97
+ Kernel.stub!( :load ).and_return do
98
+ Arrow::Applet.newly_loaded << @appletclass << @appletsubclass
99
+ end
100
+
101
+ applets = Arrow::Applet.load( 'stubbedapplet', true )
102
+
103
+ applets.should have(2).members
104
+ applets.should include( @appletclass )
105
+ applets.should include( @appletsubclass )
106
+ end
107
+
108
+
109
+ it "inherit some values from their parent's signature" do
110
+ @appletsubclass.signature.maintainer.should == @appletclass.signature.maintainer
111
+ @appletsubclass.signature.maintainer.should_not be_equal(@appletclass.signature.maintainer)
112
+ end
113
+
114
+ it "don't inherit the :version, :name, and :description from their parent's signature" do
115
+ @appletsubclass.signature.version.should_not == @appletclass.signature.version
116
+ @appletsubclass.signature.name.should == @appletsubclass.name
117
+ @appletsubclass.signature.description.should == '(none)'
118
+ end
119
+ end
120
+
121
+
122
+ describe "instance" do
123
+ before( :all ) do
124
+ setup_logging( :crit )
125
+ end
126
+
127
+ before( :each ) do
128
+ @appletclass = Class.new( Arrow::Applet ) do
129
+ def test_action( txn, *args )
130
+ return [txn, args]
131
+ end
132
+
133
+ def one_arg_action( txn, one )
134
+ return one
135
+ end
136
+
137
+ def two_args_action( txn, one, two )
138
+ return [ one, two ]
139
+ end
140
+
141
+ def action_missing_action( txn, missing, *args )
142
+ return [ :missing, missing, *args ]
143
+ end
144
+
145
+ def load_template_action( txn, template_name )
146
+ return self.load_template( template_name )
147
+ end
148
+ template :devdas, "dola/re/dola.tmpl"
149
+ end
150
+
151
+ @uri = '/test'
152
+ @factory = mock( "template factory" )
153
+
154
+ @applet = @appletclass.new( nil, @factory, @uri )
155
+
156
+ @txn = stub( "transaction", :vargs => nil, :form_request? => false, :vargs= => nil )
157
+ end
158
+
159
+
160
+ it "delegates template-loading to its template factory" do
161
+ @factory.should_receive( :get_template ).with( 'dola/re/dola.tmpl' ).
162
+ and_return( :the_template )
163
+ @applet.load_template_action( @txn, :devdas ).should == :the_template
164
+ end
165
+
166
+ it "eliminates URI arguments to match the arity of actions with only one argument" do
167
+ @applet.run( @txn, 'one_arg', :first, :second, :third ).should == :first
168
+ end
169
+
170
+ it "eliminates URI arguments to match the arity of actions with two arguments" do
171
+ @applet.run( @txn, 'two_args', :first, :second, :third ).should == [:first, :second]
172
+ end
173
+
174
+ it "appends nil URI arguments to match the arity of actions with two arguments" do
175
+ @applet.run( @txn, 'two_args', :first ).should == [:first, nil]
176
+ end
177
+
178
+
179
+ it "maps invocations of nonexistent actions to the action_missing action" do
180
+ @applet.run( @txn, "nonexistent", :first ).should == [ :missing, "nonexistent", :first ]
181
+ end
182
+
183
+ it "doesn't use libapreq's param parser for a POST with content-type " +
184
+ "other than 'application/www-form-url-encoded" do
185
+ request = mock( "request", :null_object => true )
186
+ request.should_not_receive( :paramtable )
187
+
188
+ txn = mock( "transaction", :null_object => true )
189
+ txn.should_receive( :form_request? ).at_least(1).and_return( false )
190
+ txn.stub!( :request ).and_return( request )
191
+
192
+ @applet.run( txn, 'test' )
193
+ end
194
+
195
+ end # describe "instance"
196
+
197
+
198
+ describe "instance with an action that accepts form parameters" do
199
+ before( :all ) do
200
+ setup_logging( :crit )
201
+ end
202
+
203
+ it "allows a validator to be declared using 'action, hash' syntax" do
204
+ appletclass = Class.new( Arrow::Applet ) do
205
+ def comma_action( txn, *args )
206
+ return 'yay'
207
+ end
208
+
209
+ validator :comma, :optional => :formy
210
+ end
211
+ appletclass.signature.validator_profiles[ :comma ].should == {
212
+ :optional => :formy
213
+ }
214
+ end
215
+
216
+ it "allows a validator to be declared using 'action => { hash }' syntax" do
217
+ appletclass = Class.new( Arrow::Applet ) do
218
+ def hash_action( txn, *args )
219
+ return 'yay'
220
+ end
221
+
222
+ validator :hash => { :optional => :formy }
223
+ end
224
+ appletclass.signature.validator_profiles[ :hash ].should == {
225
+ :optional => :formy
226
+ }
227
+ end
228
+ end
229
+
230
+
231
+ describe "instance without an #action_missing_action method" do
232
+ before( :all ) do
233
+ setup_logging( :crit )
234
+ end
235
+
236
+ before( :each ) do
237
+ @appletclass = Class.new( Arrow::Applet ) do
238
+ template :test => 'test.tmpl'
239
+ end
240
+
241
+ @uri = '/test'
242
+ @factory = mock( "template factory" )
243
+
244
+ @applet = @appletclass.new( nil, @factory, @uri )
245
+
246
+ @txn = stub( "transaction", :vargs => nil, :form_request? => false, :vargs= => nil )
247
+ end
248
+
249
+ it "maps missing actions to like-named templates" do
250
+ template = stub( "test template" )
251
+ @factory.should_receive( :get_template ).with( 'test.tmpl' ).and_return( template )
252
+
253
+ template.should_receive( :txn= ).with( @txn )
254
+ template.should_receive( :applet= ).with( @applet )
255
+
256
+ @applet.run( @txn, 'test' ).should == template
257
+ end
258
+
259
+ it "untaints the action before using it to look up the missing action template" do
260
+ template = stub( "test template" )
261
+ @factory.should_receive( :get_template ).with( 'test.tmpl' ).and_return( template )
262
+
263
+ template.should_receive( :txn= ).with( @txn )
264
+ template.should_receive( :applet= ).with( @applet )
265
+
266
+ action = 'test'
267
+ action.taint
268
+
269
+ Thread.new do
270
+ Thread.current.abort_on_exception = true
271
+ $SAFE = 1
272
+ lambda {
273
+ @applet.run( @txn, action )
274
+ }.should_not raise_error()
275
+ end.join
276
+ end
277
+
278
+
279
+
280
+ ### TODO: Convert these to specs
281
+
282
+ def test_load_template_should_raise_an_error_for_templates_not_in_the_signature
283
+ assert_raises( Arrow::AppletError ) do
284
+ applet = @appletclass.new( nil, nil, nil )
285
+ applet.__send__( :load_template, :foo )
286
+ end
287
+ end
288
+
289
+
290
+ def test_template_directive_should_add_a_template_to_the_signature
291
+ assert !@appletclass.signature.templates.key?( :foo )
292
+
293
+ assert_nothing_raised do
294
+ @appletclass.class_eval do
295
+ template :foo, "foo.tmpl"
296
+ end
297
+ end
298
+
299
+ assert_equal "foo.tmpl", @appletclass.signature.templates[:foo]
300
+ end
301
+
302
+ def test_template_directive_should_accept_single_hash_pair
303
+ assert !@appletclass.signature.templates.key?( :foo )
304
+
305
+ assert_nothing_raised do
306
+ @appletclass.class_eval do
307
+ template :foo => "foo.tmpl"
308
+ end
309
+ end
310
+
311
+ assert_equal "foo.tmpl", @appletclass.signature.templates[:foo]
312
+ end
313
+
314
+ def test_template_directive_should_accept_multiple_hash_pairs
315
+ assert !@appletclass.signature.templates.key?( :foo )
316
+ assert !@appletclass.signature.templates.key?( :bar )
317
+
318
+ assert_nothing_raised do
319
+ @appletclass.class_eval do
320
+ template :foo => "foo.tmpl",
321
+ :bar => "bar.tmpl"
322
+ end
323
+ end
324
+
325
+ assert_equal "foo.tmpl", @appletclass.signature.templates[:foo]
326
+ assert_equal "bar.tmpl", @appletclass.signature.templates[:bar]
327
+ end
328
+
329
+ def test_applet_name_directive_should_set_signature_name
330
+ assert_not_equal "foo", @appletclass.signature.name
331
+
332
+ assert_nothing_raised do
333
+ @appletclass.class_eval do
334
+ applet_name "foo"
335
+ end
336
+ end
337
+
338
+ assert_equal "foo", @appletclass.signature.name
339
+ end
340
+
341
+ def test_applet_description_directive_should_set_signature_description
342
+ assert_not_equal "foo", @appletclass.signature.description
343
+
344
+ assert_nothing_raised do
345
+ @appletclass.class_eval do
346
+ applet_description "foo"
347
+ end
348
+ end
349
+
350
+ assert_equal "foo", @appletclass.signature.description
351
+ end
352
+
353
+ def test_applet_maintainer_directive_should_set_signature_maintainer
354
+ assert_not_equal "foo", @appletclass.signature.maintainer
355
+
356
+ assert_nothing_raised do
357
+ @appletclass.class_eval do
358
+ applet_maintainer "foo"
359
+ end
360
+ end
361
+
362
+ assert_equal "foo", @appletclass.signature.maintainer
363
+ end
364
+
365
+ def test_applet_appicon_directive_should_set_signature_appicon
366
+ assert_not_equal "foo.png", @appletclass.signature.appicon
367
+
368
+ assert_nothing_raised do
369
+ @appletclass.class_eval do
370
+ applet_maintainer "foo.png"
371
+ end
372
+ end
373
+
374
+ assert_equal "foo.png", @appletclass.signature.maintainer
375
+ end
376
+
377
+ def test_applet_version_directive_should_set_signature_version
378
+ assert_not_equal "200.1", @appletclass.signature.version
379
+
380
+ assert_nothing_raised do
381
+ @appletclass.class_eval do
382
+ applet_version "200.1"
383
+ end
384
+ end
385
+
386
+ assert_equal "200.1", @appletclass.signature.version
387
+ end
388
+
389
+ def test_default_action_directive_should_set_signature_default_action
390
+ assert_not_equal :edit, @appletclass.signature.default_action
391
+
392
+ assert_nothing_raised do
393
+ @appletclass.class_eval do
394
+ default_action :edit
395
+ end
396
+ end
397
+
398
+ assert_equal "edit", @appletclass.signature.default_action
399
+ end
400
+
401
+
402
+ def test_run_should_track_run_times
403
+ assert_equal 0.0, @applet.total_utime
404
+ assert_equal 0.0, @applet.total_stime
405
+
406
+ @applet.def_action_body do |txn|
407
+ # This will hopefully take more than 0.0 seconds on any machine.
408
+ 10_000.times do
409
+ # This causes a system access, which makes for stime
410
+ # the previous code was only usertime on my linux machine -JJ
411
+ d = Dir.open('.')
412
+ d.close
413
+ end
414
+ end
415
+
416
+ with_run_fixtured_transaction do |txn|
417
+ @applet.run( txn, "test" )
418
+ end
419
+
420
+ assert (@applet.total_utime > 0.0),
421
+ "Applet utime after run: %0.5f" % [@applet.total_utime]
422
+ assert (@applet.total_stime > 0.0),
423
+ "Applet stime after run: %0.5f" % [@applet.total_stime]
424
+ end
425
+
426
+
427
+ def test_defining_an_action_method_with_inappropriate_arity_should_raise_scripterror
428
+ assert_raises( ScriptError ) do
429
+ @appletclass.class_eval { def malformed_action; end }
430
+ end
431
+ end
432
+
433
+
434
+ def test_running_against_action_method_with_inappropriate_arity_should_raise_appleterror
435
+ # We have to sneak a method past the ::method_added check...
436
+ @appletclass.class_eval do
437
+ def self::method_added(sym); end
438
+ def malformed_action; end
439
+ end
440
+
441
+ assert_raises( Arrow::AppletError ) do
442
+ with_run_fixtured_transaction do |txn|
443
+ @applet.run( txn, "malformed" )
444
+ end
445
+ end
446
+ end
447
+
448
+
449
+ def test_run_with_two_arg_action_sends_the_appropriate_number_of_args
450
+ @appletclass.class_eval do
451
+ def two_arg_action( txn, arg )
452
+ [ txn, arg ]
453
+ end
454
+ end
455
+ @applet = @appletclass.new( nil, nil, nil )
456
+
457
+ with_run_fixtured_transaction do |txn|
458
+ rval = @applet.run( txn, "two_arg", "arg1", "arg2", "arg3" )
459
+
460
+ assert_instance_of Array, rval
461
+ assert_equal 2, rval.length
462
+ assert_same txn, rval.first
463
+ assert_equal "arg1", rval[1]
464
+ end
465
+
466
+ end
467
+
468
+
469
+ def test_run_with_block_yields_action_method_and_transaction_instead_of_invoking
470
+ rval, meth, rtxn, rargs = nil
471
+
472
+ with_run_fixtured_transaction do |txn|
473
+ rval = @applet.run( txn, "test", "arg1", "arg2" ) do |metharg, txn2, *args|
474
+ meth = metharg
475
+ rtxn = txn2
476
+ rargs = args
477
+
478
+ :passed
479
+ end
480
+
481
+ assert_instance_of Method, meth
482
+ assert_match( /#test_action/, meth.to_s )
483
+ assert_same txn, rtxn
484
+ assert_equal ["arg1", "arg2"], rargs
485
+
486
+ assert_equal :passed, rval
487
+ end
488
+ end
489
+
490
+
491
+ def test_run_with_parameterless_action_method_raises_an_appleterror
492
+ @applet.def_action_body do
493
+ flunk "Expected exception before action body was run"
494
+ end
495
+
496
+ with_run_fixtured_transaction do |txn|
497
+ assert_raises( Arrow::AppletError ) do
498
+ @applet.run( txn, "malformed" )
499
+ end
500
+ end
501
+ end
502
+
503
+
504
+ def test_run_without_an_action_invokes_the_default_action
505
+ invoked = false
506
+
507
+ @applet.def_action_body do
508
+ invoked = true
509
+ end
510
+
511
+ with_run_fixtured_transaction do |txn|
512
+ rval = @applet.run( txn )
513
+ end
514
+
515
+ assert_equal true, invoked, "Default action was not invoked"
516
+ end
517
+
518
+
519
+ def test_looking_up_a_valid_action_method_should_return_method_object_for_it
520
+ rval = nil
521
+ args = []
522
+
523
+ assert_nothing_raised do
524
+ rval, *args = @applet.send( :lookup_action_method, nil, "test" )
525
+ end
526
+
527
+ assert_instance_of Method, rval
528
+ assert_match( /#test_action/, rval.to_s )
529
+ end
530
+
531
+
532
+
533
+
534
+ def test_default_delegation_method_just_yields
535
+ rval = nil
536
+
537
+ assert_nothing_raised do
538
+ rval = @applet.delegate( nil, [:chain] ) {|arg| rval = arg }
539
+ end
540
+
541
+ assert_equal [:chain], rval
542
+ end
543
+
544
+
545
+ def test_subrun_from_delegation_populates_txn_vargs
546
+
547
+ # Define the delegator and an action to subrun
548
+ @appletclass.class_eval do
549
+ def delegate( txn, chain, *args )
550
+ self.subrun( 'test_vargs', txn, *args )
551
+ end
552
+ def test_vargs_action( txn, *args )
553
+ end
554
+ end
555
+
556
+ applet = @appletclass.new( nil, nil, nil )
557
+
558
+ # Even though it's not a real #run, the transaction should be
559
+ # set up the same way passing through #subrun
560
+ with_run_fixtured_transaction do |txn, req|
561
+ txn.should_receive( :vargs ).and_return(nil).at_least.once
562
+
563
+ assert_nothing_raised do
564
+ applet.delegate( txn, nil )
565
+ end
566
+ end
567
+
568
+ end
569
+
570
+ end # describe "concrete subclass"
571
+
572
+ end
573
+
574
+
575
+ # vim: set nosta noet ts=4 sw=4: