rack-sanitizer 2.0.3 → 2.1.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +12 -5
- data/.github/workflows/release.yml +24 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +12 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +38 -7
- data/LICENSE.txt +2 -1
- data/README.md +1 -1
- data/Rakefile +4 -2
- data/bin/bacon +488 -0
- data/bin/rubocop +27 -0
- data/dev.yml +17 -0
- data/gemfiles/rack_2.2.gemfile +9 -0
- data/gemfiles/rack_3.0.gemfile +9 -0
- data/gemfiles/rack_3.1.gemfile +9 -0
- data/gemfiles/rack_3.2.gemfile +9 -0
- data/lib/rack/sanitizer.rb +34 -27
- data/rack-sanitizer.gemspec +6 -11
- data/test/test_sanitizer.rb +149 -69
- metadata +15 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fca030fbcd503f8e7a4f61ebd1286552baf24d3b1a8607af3c02ca12dd9533e0
|
|
4
|
+
data.tar.gz: 44770acabfcea4af68db609520fd158986c72e5a033151352384a08f16174cda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a88c1f9a8f7aa00cf4b7a6c2ad9306f53218d467177bc154efcb12a9cebf7c2bb79a7404dbd0c1a813929b426a5169d714149488e92318ce4b0b0c80067971e
|
|
7
|
+
data.tar.gz: 0fc65bc666ac425913aa02abb117545e29dc193961e41a91cf4f704be14567f2271e8e028527a3e0872c2d129029500880959a3058d36eedd3150b079fc4d781
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -10,15 +10,22 @@ jobs:
|
|
|
10
10
|
strategy:
|
|
11
11
|
fail-fast: false
|
|
12
12
|
matrix:
|
|
13
|
-
ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", ruby-head, jruby-9.
|
|
13
|
+
ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4", ruby-head, jruby-9.3, jruby-head]
|
|
14
|
+
rack: ["2.2", "3.0", "3.1", "3.2"]
|
|
15
|
+
include:
|
|
16
|
+
- ruby: "3.4"
|
|
17
|
+
rubyopt: "--enable-frozen-string-literal --debug-frozen-string-literal"
|
|
18
|
+
|
|
19
|
+
env:
|
|
20
|
+
BUNDLE_GEMFILE: gemfiles/rack_${{ matrix.rack }}.gemfile
|
|
14
21
|
|
|
15
22
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
24
|
- name: Set up Ruby
|
|
18
|
-
uses: ruby/setup-ruby@v1
|
|
25
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
19
26
|
with:
|
|
20
27
|
bundler-cache: true # 'bundle install' and cache gems
|
|
21
28
|
ruby-version: ${{ matrix.ruby }}
|
|
22
29
|
bundler: latest
|
|
23
|
-
- name: Run tests
|
|
24
|
-
run: bundle exec rake
|
|
30
|
+
- name: Run tests on Rack ${{ matrix.rack }} ${{ matrix.rubyopt }}
|
|
31
|
+
run: bundle exec rake RUBYOPT="${{ matrix.rubyopt }}"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
environment: release
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
21
|
+
with:
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
ruby-version: 3.4.4
|
|
24
|
+
- uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,26 +1,57 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rack-sanitizer (2.0
|
|
4
|
+
rack-sanitizer (2.1.0)
|
|
5
5
|
rack (>= 1.0, < 4.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
ast (2.4.3)
|
|
11
|
+
json (2.21.2)
|
|
12
|
+
language_server-protocol (3.17.0.6)
|
|
13
|
+
lint_roller (1.1.0)
|
|
14
|
+
ostruct (0.6.1)
|
|
15
|
+
parallel (2.1.0)
|
|
16
|
+
parser (3.3.12.0)
|
|
17
|
+
ast (~> 2.4.1)
|
|
18
|
+
racc
|
|
19
|
+
prism (1.9.0)
|
|
20
|
+
racc (1.8.1)
|
|
21
|
+
rack (3.1.21)
|
|
22
|
+
rainbow (3.1.1)
|
|
14
23
|
rake (13.1.0)
|
|
24
|
+
regexp_parser (2.12.0)
|
|
25
|
+
rubocop (1.90.0)
|
|
26
|
+
json (>= 2.3)
|
|
27
|
+
language_server-protocol (~> 3.17.0.2)
|
|
28
|
+
lint_roller (~> 1.1.0)
|
|
29
|
+
parallel (>= 1.10)
|
|
30
|
+
parser (>= 3.3.0.2)
|
|
31
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
32
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
33
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
34
|
+
ruby-progressbar (~> 1.7)
|
|
35
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
36
|
+
rubocop-ast (1.50.0)
|
|
37
|
+
parser (>= 3.3.7.2)
|
|
38
|
+
prism (~> 1.7)
|
|
39
|
+
rubocop-shopify (3.0.2)
|
|
40
|
+
lint_roller
|
|
41
|
+
rubocop (~> 1.72, >= 1.72.1)
|
|
42
|
+
ruby-progressbar (1.13.0)
|
|
43
|
+
unicode-display_width (3.2.0)
|
|
44
|
+
unicode-emoji (~> 4.1)
|
|
45
|
+
unicode-emoji (4.2.0)
|
|
15
46
|
|
|
16
47
|
PLATFORMS
|
|
17
48
|
ruby
|
|
18
49
|
|
|
19
50
|
DEPENDENCIES
|
|
20
|
-
|
|
21
|
-
bacon-colored_output
|
|
51
|
+
ostruct
|
|
22
52
|
rack-sanitizer!
|
|
23
53
|
rake
|
|
54
|
+
rubocop-shopify
|
|
24
55
|
|
|
25
56
|
BUNDLED WITH
|
|
26
57
|
2.5.7
|
data/LICENSE.txt
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
Copyright (c) 2026 Shopify Inc.
|
|
1
2
|
Copyright (c) 2013 Peter Zotov
|
|
2
3
|
|
|
3
4
|
MIT License
|
|
@@ -19,4 +20,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
19
20
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
21
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
22
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -44,7 +44,7 @@ The generic sanitization algorithm is as follows:
|
|
|
44
44
|
1. Force the encoding to ASCII8-BIT.
|
|
45
45
|
2. Re-encode it as UTF-8, replacing invalid and undefined characters as U+FFFD.
|
|
46
46
|
|
|
47
|
-
For fields with "raw data", the algorithm is applied once
|
|
47
|
+
For fields with "raw data", the algorithm is applied once. ASCII-only results remain UTF-8. Results with non-ASCII characters use ASCII-8BIT encoding to satisfy the Rack environment specification.
|
|
48
48
|
|
|
49
49
|
For fields with "percent-encoded data", the algorithm is applied twice to catch both invalid characters appearing as-is and invalid characters appearing in the percent encoding. The percent encoded, ASCII-8BIT encoded result is left in the environment.
|
|
50
50
|
|
data/Rakefile
CHANGED
data/bin/bacon
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
# -*- ruby -*-
|
|
4
|
+
|
|
5
|
+
# Bacon -- small RSpec clone.
|
|
6
|
+
#
|
|
7
|
+
# "Truth will sooner come out from error than from confusion." ---Francis Bacon
|
|
8
|
+
|
|
9
|
+
# Copyright (C) 2007, 2008, 2012 Christian Neukirchen <purl.org/net/chneukirchen>
|
|
10
|
+
#
|
|
11
|
+
# Bacon is freely distributable under the terms of an MIT-style license.
|
|
12
|
+
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
13
|
+
|
|
14
|
+
module Bacon
|
|
15
|
+
VERSION = "1.2"
|
|
16
|
+
|
|
17
|
+
Counter = Hash.new(0)
|
|
18
|
+
ErrorLog = +""
|
|
19
|
+
Shared = Hash.new { |_, name|
|
|
20
|
+
raise NameError, "no such context: #{name.inspect}"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
RestrictName = // unless defined? RestrictName
|
|
24
|
+
RestrictContext = // unless defined? RestrictContext
|
|
25
|
+
|
|
26
|
+
Backtraces = true unless defined? Backtraces
|
|
27
|
+
|
|
28
|
+
def self.summary_on_exit
|
|
29
|
+
return if Counter[:installed_summary] > 0
|
|
30
|
+
@timer = Time.now
|
|
31
|
+
at_exit {
|
|
32
|
+
handle_summary
|
|
33
|
+
if $!
|
|
34
|
+
raise $!
|
|
35
|
+
elsif Counter[:errors] + Counter[:failed] > 0
|
|
36
|
+
exit 1
|
|
37
|
+
end
|
|
38
|
+
}
|
|
39
|
+
Counter[:installed_summary] += 1
|
|
40
|
+
end
|
|
41
|
+
class <<self; alias summary_at_exit summary_on_exit; end
|
|
42
|
+
|
|
43
|
+
module SpecDoxOutput
|
|
44
|
+
def handle_specification(name)
|
|
45
|
+
puts spaces + name
|
|
46
|
+
yield
|
|
47
|
+
puts if Counter[:context_depth] == 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def handle_requirement(description)
|
|
51
|
+
print "#{spaces} - #{description}"
|
|
52
|
+
error = yield
|
|
53
|
+
puts error.empty? ? "" : " [#{error}]"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_summary
|
|
57
|
+
print ErrorLog if Backtraces
|
|
58
|
+
puts "%d specifications (%d requirements), %d failures, %d errors" %
|
|
59
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def spaces
|
|
63
|
+
" " * (Counter[:context_depth] - 1)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module TestUnitOutput
|
|
68
|
+
def handle_specification(name) yield end
|
|
69
|
+
|
|
70
|
+
def handle_requirement(description)
|
|
71
|
+
error = yield
|
|
72
|
+
if error.empty?
|
|
73
|
+
print "."
|
|
74
|
+
else
|
|
75
|
+
print error[0..0]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def handle_summary
|
|
80
|
+
puts "", "Finished in #{Time.now - @timer} seconds."
|
|
81
|
+
puts ErrorLog if Backtraces
|
|
82
|
+
puts "%d tests, %d assertions, %d failures, %d errors" %
|
|
83
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
module TapOutput
|
|
88
|
+
def handle_specification(name) yield end
|
|
89
|
+
|
|
90
|
+
def handle_requirement(description)
|
|
91
|
+
ErrorLog.replace ""
|
|
92
|
+
error = yield
|
|
93
|
+
if error.empty?
|
|
94
|
+
puts "ok %-3d - %s" % [Counter[:specifications], description]
|
|
95
|
+
else
|
|
96
|
+
puts "not ok %d - %s: %s" %
|
|
97
|
+
[Counter[:specifications], description, error]
|
|
98
|
+
puts ErrorLog.strip.gsub(/^/, '# ') if Backtraces
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_summary
|
|
103
|
+
puts "1..#{Counter[:specifications]}"
|
|
104
|
+
puts "# %d tests, %d assertions, %d failures, %d errors" %
|
|
105
|
+
Counter.values_at(:specifications, :requirements, :failed, :errors)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
module KnockOutput
|
|
110
|
+
def handle_specification(name) yield end
|
|
111
|
+
|
|
112
|
+
def handle_requirement(description)
|
|
113
|
+
ErrorLog.replace ""
|
|
114
|
+
error = yield
|
|
115
|
+
if error.empty?
|
|
116
|
+
puts "ok - %s" % [description]
|
|
117
|
+
else
|
|
118
|
+
puts "not ok - %s: %s" % [description, error]
|
|
119
|
+
puts ErrorLog.strip.gsub(/^/, '# ') if Backtraces
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def handle_summary; end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
extend SpecDoxOutput # default
|
|
127
|
+
|
|
128
|
+
class Error < RuntimeError
|
|
129
|
+
attr_accessor :count_as
|
|
130
|
+
|
|
131
|
+
def initialize(count_as, message)
|
|
132
|
+
@count_as = count_as
|
|
133
|
+
super message
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class Context
|
|
138
|
+
attr_reader :name, :block
|
|
139
|
+
|
|
140
|
+
def initialize(name, &block)
|
|
141
|
+
@name = name
|
|
142
|
+
@before, @after = [], []
|
|
143
|
+
@block = block
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def run
|
|
147
|
+
return unless name =~ RestrictContext
|
|
148
|
+
Counter[:context_depth] += 1
|
|
149
|
+
Bacon.handle_specification(name) { instance_eval(&block) }
|
|
150
|
+
Counter[:context_depth] -= 1
|
|
151
|
+
self
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def before(&block); @before << block; end
|
|
155
|
+
def after(&block); @after << block; end
|
|
156
|
+
|
|
157
|
+
def behaves_like(*names)
|
|
158
|
+
names.each { |name| instance_eval(&Shared[name]) }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def it(description, &block)
|
|
162
|
+
return unless description =~ RestrictName
|
|
163
|
+
block ||= lambda { should.flunk "not implemented" }
|
|
164
|
+
Counter[:specifications] += 1
|
|
165
|
+
run_requirement description, block
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def should(*args, &block)
|
|
169
|
+
if Counter[:depth]==0
|
|
170
|
+
it('should '+args.first,&block)
|
|
171
|
+
else
|
|
172
|
+
super(*args,&block)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def run_requirement(description, spec)
|
|
177
|
+
Bacon.handle_requirement description do
|
|
178
|
+
begin
|
|
179
|
+
Counter[:depth] += 1
|
|
180
|
+
rescued = false
|
|
181
|
+
begin
|
|
182
|
+
@before.each { |block| instance_eval(&block) }
|
|
183
|
+
prev_req = Counter[:requirements]
|
|
184
|
+
instance_eval(&spec)
|
|
185
|
+
rescue Object => e
|
|
186
|
+
rescued = true
|
|
187
|
+
raise e
|
|
188
|
+
ensure
|
|
189
|
+
if Counter[:requirements] == prev_req and not rescued
|
|
190
|
+
raise Error.new(:missing,
|
|
191
|
+
"empty specification: #{@name} #{description}")
|
|
192
|
+
end
|
|
193
|
+
begin
|
|
194
|
+
@after.each { |block| instance_eval(&block) }
|
|
195
|
+
rescue Object => e
|
|
196
|
+
raise e unless rescued
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
rescue Object => e
|
|
200
|
+
ErrorLog << "#{e.class}: #{e.message}\n"
|
|
201
|
+
e.backtrace.find_all { |line| line !~ /bin\/bacon|\/bacon\.rb:\d+/ }.
|
|
202
|
+
each_with_index { |line, i|
|
|
203
|
+
ErrorLog << "\t#{line}#{i==0 ? ": #@name - #{description}" : ""}\n"
|
|
204
|
+
}
|
|
205
|
+
ErrorLog << "\n"
|
|
206
|
+
|
|
207
|
+
if e.kind_of? Error
|
|
208
|
+
Counter[e.count_as] += 1
|
|
209
|
+
e.count_as.to_s.upcase
|
|
210
|
+
else
|
|
211
|
+
Counter[:errors] += 1
|
|
212
|
+
"ERROR: #{e.class}"
|
|
213
|
+
end
|
|
214
|
+
else
|
|
215
|
+
""
|
|
216
|
+
ensure
|
|
217
|
+
Counter[:depth] -= 1
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def describe(*args, &block)
|
|
223
|
+
context = Bacon::Context.new(args.join(' '), &block)
|
|
224
|
+
(parent_context = self).methods(false).each {|e|
|
|
225
|
+
class<<context; self end.send(:define_method, e) {|*args| parent_context.send(e, *args)}
|
|
226
|
+
}
|
|
227
|
+
@before.each { |b| context.before(&b) }
|
|
228
|
+
@after.each { |b| context.after(&b) }
|
|
229
|
+
context.run
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def raise?(*args, &block); block.raise?(*args); end
|
|
233
|
+
def throw?(*args, &block); block.throw?(*args); end
|
|
234
|
+
def change?(&block); lambda{}.change?(&block); end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
module ColoredOutput
|
|
238
|
+
def handle_requirement(*args)
|
|
239
|
+
error = yield
|
|
240
|
+
|
|
241
|
+
print error.empty? ? color("\e[32m") : color("\e[1;31m")
|
|
242
|
+
super(*args) { error }
|
|
243
|
+
print color("\e[0m")
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def color(escape_seq)
|
|
247
|
+
if $stdout.respond_to?(:tty?) && $stdout.tty?
|
|
248
|
+
escape_seq
|
|
249
|
+
else
|
|
250
|
+
""
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
extend ColoredOutput
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class Object
|
|
260
|
+
def true?; false; end
|
|
261
|
+
def false?; false; end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
class TrueClass
|
|
265
|
+
def true?; true; end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
class FalseClass
|
|
269
|
+
def false?; true; end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
class Proc
|
|
273
|
+
def raise?(*exceptions)
|
|
274
|
+
call
|
|
275
|
+
rescue *(exceptions.empty? ? RuntimeError : exceptions) => e
|
|
276
|
+
e
|
|
277
|
+
else
|
|
278
|
+
false
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def throw?(sym)
|
|
282
|
+
catch(sym) {
|
|
283
|
+
call
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
return true
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def change?
|
|
290
|
+
pre_result = yield
|
|
291
|
+
call
|
|
292
|
+
post_result = yield
|
|
293
|
+
pre_result != post_result
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
class Numeric
|
|
298
|
+
def close?(to, delta)
|
|
299
|
+
(to.to_f - self).abs <= delta.to_f rescue false
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Object
|
|
305
|
+
def should(*args, &block) Should.new(self).be(*args, &block) end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
module Kernel
|
|
309
|
+
private
|
|
310
|
+
def describe(*args, &block) Bacon::Context.new(args.join(' '), &block).run end
|
|
311
|
+
def shared(name, &block) Bacon::Shared[name] = block end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
class Should
|
|
315
|
+
# Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?,
|
|
316
|
+
# kind_of?, nil?, respond_to?, tainted?
|
|
317
|
+
instance_methods.each { |name| undef_method name if name =~ /\?|^\W+$/ }
|
|
318
|
+
|
|
319
|
+
def initialize(object)
|
|
320
|
+
@object = object
|
|
321
|
+
@negated = false
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def not(*args, &block)
|
|
325
|
+
@negated = !@negated
|
|
326
|
+
|
|
327
|
+
if args.empty?
|
|
328
|
+
self
|
|
329
|
+
else
|
|
330
|
+
be(*args, &block)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def be(*args, &block)
|
|
335
|
+
if args.empty?
|
|
336
|
+
self
|
|
337
|
+
else
|
|
338
|
+
block = args.shift unless block_given?
|
|
339
|
+
satisfy(*args, &block)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
alias a be
|
|
344
|
+
alias an be
|
|
345
|
+
|
|
346
|
+
def satisfy(description="", &block)
|
|
347
|
+
r = yield(@object)
|
|
348
|
+
if Bacon::Counter[:depth] > 0
|
|
349
|
+
Bacon::Counter[:requirements] += 1
|
|
350
|
+
raise Bacon::Error.new(:failed, description) unless @negated ^ r
|
|
351
|
+
r
|
|
352
|
+
else
|
|
353
|
+
@negated ? !r : !!r
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def method_missing(name, *args, &block)
|
|
358
|
+
name = "#{name}?" if name.to_s =~ /\w[^?]\z/
|
|
359
|
+
|
|
360
|
+
desc = @negated ? "not ".dup : "".dup
|
|
361
|
+
desc << @object.inspect << "." << name.to_s
|
|
362
|
+
desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed"
|
|
363
|
+
|
|
364
|
+
satisfy(desc) { |x| x.__send__(name, *args, &block) }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def equal(value) self == value end
|
|
368
|
+
def match(value) self =~ value end
|
|
369
|
+
def identical_to(value) self.equal? value end
|
|
370
|
+
alias same_as identical_to
|
|
371
|
+
|
|
372
|
+
def flunk(reason="Flunked")
|
|
373
|
+
raise Bacon::Error.new(:failed, reason)
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
require 'optparse'
|
|
378
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib/')
|
|
379
|
+
module Bacon; end
|
|
380
|
+
|
|
381
|
+
automatic = false
|
|
382
|
+
output = 'SpecDoxOutput'
|
|
383
|
+
|
|
384
|
+
opts = OptionParser.new("", 24, ' ') { |opts|
|
|
385
|
+
opts.banner = "Usage: bacon [options] [files | -a] [-- untouched arguments]"
|
|
386
|
+
|
|
387
|
+
opts.separator ""
|
|
388
|
+
opts.separator "Ruby options:"
|
|
389
|
+
|
|
390
|
+
lineno = 1
|
|
391
|
+
opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
|
|
392
|
+
eval line, TOPLEVEL_BINDING, "-e", lineno
|
|
393
|
+
lineno += 1
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
|
397
|
+
$DEBUG = true
|
|
398
|
+
}
|
|
399
|
+
opts.on("-w", "--warn", "turn warnings on for your script") {
|
|
400
|
+
$-w = true
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
opts.on("-I", "--include PATH",
|
|
404
|
+
"specify $LOAD_PATH (may be used more than once)") { |path|
|
|
405
|
+
$LOAD_PATH.unshift(*path.split(":"))
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
opts.on("-r", "--require LIBRARY",
|
|
409
|
+
"require the library, before executing your script") { |library|
|
|
410
|
+
require library
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
opts.separator ""
|
|
414
|
+
opts.separator "bacon options:"
|
|
415
|
+
|
|
416
|
+
opts.on("-s", "--specdox", "do AgileDox-like output (default)") {
|
|
417
|
+
output = 'SpecDoxOutput'
|
|
418
|
+
}
|
|
419
|
+
opts.on("-q", "--quiet", "do Test::Unit-like non-verbose output") {
|
|
420
|
+
output = 'TestUnitOutput'
|
|
421
|
+
}
|
|
422
|
+
opts.on("-p", "--tap", "do TAP (Test Anything Protocol) output") {
|
|
423
|
+
output = 'TapOutput'
|
|
424
|
+
}
|
|
425
|
+
opts.on("-k", "--knock", "do Knock output") {
|
|
426
|
+
output = 'KnockOutput'
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
opts.on("-o", "--output FORMAT",
|
|
430
|
+
"do FORMAT (SpecDox/TestUnit/Tap) output") { |format|
|
|
431
|
+
output = format + "Output"
|
|
432
|
+
}
|
|
433
|
+
opts.on("-Q", "--no-backtrace", "don't print backtraces") {
|
|
434
|
+
Bacon.const_set :Backtraces, false
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
opts.on("-a", "--automatic", "gather tests from ./test/, include ./lib/") {
|
|
438
|
+
$LOAD_PATH.unshift "lib" if File.directory? "lib"
|
|
439
|
+
automatic = true
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
opts.on('-n', '--name NAME', String,
|
|
443
|
+
"runs tests matching regexp NAME") { |n|
|
|
444
|
+
Bacon.const_set :RestrictName, Regexp.new(n)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
opts.on('-t', '--testcase TESTCASE', String,
|
|
448
|
+
"runs tests in TestCases matching regexp TESTCASE") { |t|
|
|
449
|
+
Bacon.const_set :RestrictContext, Regexp.new(t)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
opts.separator ""
|
|
453
|
+
opts.separator "Common options:"
|
|
454
|
+
|
|
455
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
456
|
+
puts opts
|
|
457
|
+
exit
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
opts.on_tail("--version", "Show version") do
|
|
461
|
+
require 'bacon'
|
|
462
|
+
puts "bacon #{Bacon::VERSION}"
|
|
463
|
+
exit
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
opts.parse! ARGV
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
files = ARGV
|
|
470
|
+
|
|
471
|
+
if automatic
|
|
472
|
+
files.concat Dir["test/**/test_*.rb"]
|
|
473
|
+
files.concat Dir["test/**/spec_*.rb"]
|
|
474
|
+
files.concat Dir["spec/**/spec_*.rb"]
|
|
475
|
+
files.concat Dir["spec/**/*_spec.rb"]
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
if files.empty?
|
|
479
|
+
puts opts.banner
|
|
480
|
+
exit 1
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
Bacon.extend Bacon.const_get(output) rescue abort "No such formatter: #{output}"
|
|
484
|
+
Bacon.summary_on_exit
|
|
485
|
+
|
|
486
|
+
files.each { |file|
|
|
487
|
+
load file
|
|
488
|
+
}
|