Skip to content

Commit

Permalink
Specify rack.input#read to behave the same like IO#read.
Browse files Browse the repository at this point in the history
  • Loading branch information
FooBarWidget committed Apr 15, 2009
1 parent b95cec5 commit 385f621
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 9 deletions.
37 changes: 30 additions & 7 deletions lib/rack/lint.rb
Expand Up @@ -236,21 +236,44 @@ def gets(*args)
v v
end end


## * +read+ must be called without or with one integer argument ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
## and return a string, or +nil+ on EOF. ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must
## be a String and may not be nil. If +length+ is given and not nil, then this method
## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
## then this method reads all data until EOF.
## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
## if +length+ is not given or is nil.
## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
## newly created String object.
def read(*args) def read(*args)
assert("rack.input#read called with too many arguments") { assert("rack.input#read called with too many arguments") {
args.size <= 1 args.size <= 2
} }
if args.size == 1 if args.size >= 1
assert("rack.input#read called with non-integer argument") { assert("rack.input#read called with non-integer and non-nil length") {
args.first.kind_of? Integer args.first.kind_of?(Integer) || args.first.nil?
}
assert("rack.input#read called with a negative length") {
args.first.nil? || args.first >= 0
}
end
if args.size >= 2
assert("rack.input#read called with non-String buffer") {
args[1].kind_of?(String)
} }
end end

v = @input.read(*args) v = @input.read(*args)
assert("rack.input#read didn't return a String") {
assert("rack.input#read didn't return nil or a String") {
v.nil? or v.instance_of? String v.nil? or v.instance_of? String
} }
if args[0].nil?
assert("rack.input#read(nil) returned nil on EOF") {
!v.nil?
}
end

v v
end end


Expand Down
105 changes: 103 additions & 2 deletions test/spec_rack_lint.rb
Expand Up @@ -258,13 +258,45 @@ def env(*args)
}.should.raise(Rack::Lint::LintError). }.should.raise(Rack::Lint::LintError).
message.should.match(/gets called with arguments/) message.should.match(/gets called with arguments/)


lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(1, 2, 3)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({}))
}.should.raise(Rack::Lint::LintError).
message.should.match(/read called with too many arguments/)

lambda { lambda {
Rack::Lint.new(lambda { |env| Rack::Lint.new(lambda { |env|
env["rack.input"].read("foo") env["rack.input"].read("foo")
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({})) }).call(env({}))
}.should.raise(Rack::Lint::LintError). }.should.raise(Rack::Lint::LintError).
message.should.match(/read called with non-integer argument/) message.should.match(/read called with non-integer and non-nil length/)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(-1)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({}))
}.should.raise(Rack::Lint::LintError).
message.should.match(/read called with a negative length/)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(nil, nil)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({}))
}.should.raise(Rack::Lint::LintError).
message.should.match(/read called with non-String buffer/)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(nil, 1)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({}))
}.should.raise(Rack::Lint::LintError).
message.should.match(/read called with non-String buffer/)


lambda { lambda {
Rack::Lint.new(lambda { |env| Rack::Lint.new(lambda { |env|
Expand Down Expand Up @@ -293,6 +325,23 @@ def rewind
raise Errno::ESPIPE, "Errno::ESPIPE" raise Errno::ESPIPE, "Errno::ESPIPE"
end end
end end

eof_weirdio = Object.new
class << eof_weirdio
def gets
nil
end

def read(*args)
nil
end

def each
end

def rewind
end
end


lambda { lambda {
Rack::Lint.new(lambda { |env| Rack::Lint.new(lambda { |env|
Expand All @@ -316,7 +365,15 @@ def rewind
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env("rack.input" => weirdio)) }).call(env("rack.input" => weirdio))
}.should.raise(Rack::Lint::LintError). }.should.raise(Rack::Lint::LintError).
message.should.match(/read didn't return a String/) message.should.match(/read didn't return nil or a String/)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env("rack.input" => eof_weirdio))
}.should.raise(Rack::Lint::LintError).
message.should.match(/read\(nil\) returned nil on EOF/)


lambda { lambda {
Rack::Lint.new(lambda { |env| Rack::Lint.new(lambda { |env|
Expand Down Expand Up @@ -368,6 +425,50 @@ def rewind
}.should.raise(Rack::Lint::LintError). }.should.raise(Rack::Lint::LintError).
message.should.match(/body was given for HEAD/) message.should.match(/body was given for HEAD/)
end end

specify "passes valid read calls" do
lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(0)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(1)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(nil)
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(nil, '')
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)

lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].read(1, '')
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({"rack.input" => StringIO.new("hello world")}))
}.should.not.raise(Rack::Lint::LintError)
end
end end


context "Rack::Lint::InputWrapper" do context "Rack::Lint::InputWrapper" do
Expand Down

0 comments on commit 385f621

Please sign in to comment.