Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added webrat, thanks to jrun and gwynm for they're initial work on this
- Loading branch information
1 parent
2fd734f
commit 86879c1
Showing
13 changed files
with
335 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
module Webrat | ||
class Session | ||
include Merb::Test::RequestHelper | ||
|
||
attr_reader :response | ||
|
||
def get(url, data, headers = nil) | ||
do_request(url, data, headers, "GET") | ||
end | ||
|
||
def post(url, data, headers = nil) | ||
do_request(url, data, headers, "POST") | ||
end | ||
|
||
def put(url, data, headers = nil) | ||
do_request(url, data, headers, "PUT") | ||
end | ||
|
||
def delete(url, data, headers = nil) | ||
do_request(url, data, headers, "DELETE") | ||
end | ||
|
||
def response_body | ||
@response.body.to_s | ||
end | ||
|
||
def response_code | ||
@response.status | ||
end | ||
|
||
protected | ||
def do_request(url, data, headers, method) | ||
@response = request(url, :params => data, :headers => headers, :method => method) | ||
self.get(@response.headers['Location'], nil, @response.headers) if @response.status == 302 | ||
end | ||
|
||
end | ||
end | ||
|
||
class Merb::Test::RspecStory | ||
def browser | ||
@browser ||= Webrat::Session.new | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# This class has dubious semantics and we only have it so that | ||
# people can write params[:key] instead of params['key'] | ||
# and they get the same value for both keys. | ||
class HashWithIndifferentAccess < Hash | ||
def initialize(constructor = {}) | ||
if constructor.is_a?(Hash) | ||
super() | ||
update(constructor) | ||
else | ||
super(constructor) | ||
end | ||
end | ||
|
||
def default(key = nil) | ||
if key.is_a?(Symbol) && include?(key = key.to_s) | ||
self[key] | ||
else | ||
super | ||
end | ||
end | ||
|
||
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) | ||
alias_method :regular_update, :update unless method_defined?(:regular_update) | ||
|
||
# | ||
# Assigns a new value to the hash. | ||
# | ||
# Example: | ||
# | ||
# hash = HashWithIndifferentAccess.new | ||
# hash[:key] = "value" | ||
# | ||
def []=(key, value) | ||
regular_writer(convert_key(key), convert_value(value)) | ||
end | ||
|
||
# | ||
# Updates the instantized hash with values from the second. | ||
# | ||
# Example: | ||
# | ||
# >> hash_1 = HashWithIndifferentAccess.new | ||
# => {} | ||
# | ||
# >> hash_1[:key] = "value" | ||
# => "value" | ||
# | ||
# >> hash_2 = HashWithIndifferentAccess.new | ||
# => {} | ||
# | ||
# >> hash_2[:key] = "New Value!" | ||
# => "New Value!" | ||
# | ||
# >> hash_1.update(hash_2) | ||
# => {"key"=>"New Value!"} | ||
# | ||
def update(other_hash) | ||
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } | ||
self | ||
end | ||
|
||
alias_method :merge!, :update | ||
|
||
# Checks the hash for a key matching the argument passed in | ||
def key?(key) | ||
super(convert_key(key)) | ||
end | ||
|
||
alias_method :include?, :key? | ||
alias_method :has_key?, :key? | ||
alias_method :member?, :key? | ||
|
||
# Fetches the value for the specified key, same as doing hash[key] | ||
def fetch(key, *extras) | ||
super(convert_key(key), *extras) | ||
end | ||
|
||
# Returns an array of the values at the specified indicies. | ||
def values_at(*indices) | ||
indices.collect {|key| self[convert_key(key)]} | ||
end | ||
|
||
# Returns an exact copy of the hash. | ||
def dup | ||
HashWithIndifferentAccess.new(self) | ||
end | ||
|
||
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash | ||
# Does not overwrite the existing hash. | ||
def merge(hash) | ||
self.dup.update(hash) | ||
end | ||
|
||
# Removes a specified key from the hash. | ||
def delete(key) | ||
super(convert_key(key)) | ||
end | ||
|
||
def stringify_keys!; self end | ||
def symbolize_keys!; self end | ||
def to_options!; self end | ||
|
||
# Convert to a Hash with String keys. | ||
def to_hash | ||
Hash.new(default).merge(self) | ||
end | ||
|
||
protected | ||
def convert_key(key) | ||
key.kind_of?(Symbol) ? key.to_s : key | ||
end | ||
|
||
def convert_value(value) | ||
case value | ||
when Hash | ||
value.with_indifferent_access | ||
when Array | ||
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e } | ||
else | ||
value | ||
end | ||
end | ||
end | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module Webrat | ||
class ParamParser | ||
def self.parse_query_parameters(query_string) | ||
return {} if query_string.blank? | ||
|
||
pairs = query_string.split('&').collect do |chunk| | ||
next if chunk.empty? | ||
key, value = chunk.split('=', 2) | ||
next if key.empty? | ||
value = value.nil? ? nil : CGI.unescape(value) | ||
[ CGI.unescape(key), value ] | ||
end.compact | ||
|
||
UrlEncodedPairParser.new(pairs).result | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class Hash | ||
def with_indifferent_access | ||
hash = HashWithIndifferentAccess.new(self) | ||
hash.default = self.default | ||
hash | ||
end | ||
end | ||
class NilClass | ||
def to_param | ||
nil | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
class UrlEncodedPairParser < StringScanner #:nodoc: | ||
attr_reader :top, :parent, :result | ||
|
||
def initialize(pairs = []) | ||
super('') | ||
@result = {} | ||
pairs.each { |key, value| parse(key, value) } | ||
end | ||
|
||
KEY_REGEXP = %r{([^\[\]=&]+)} | ||
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]} | ||
|
||
# Parse the query string | ||
def parse(key, value) | ||
self.string = key | ||
@top, @parent = result, nil | ||
|
||
# First scan the bare key | ||
key = scan(KEY_REGEXP) or return | ||
key = post_key_check(key) | ||
|
||
# Then scan as many nestings as present | ||
until eos? | ||
r = scan(BRACKETED_KEY_REGEXP) or return | ||
key = self[1] | ||
key = post_key_check(key) | ||
end | ||
|
||
bind(key, value) | ||
end | ||
|
||
private | ||
# After we see a key, we must look ahead to determine our next action. Cases: | ||
# | ||
# [] follows the key. Then the value must be an array. | ||
# = follows the key. (A value comes next) | ||
# & or the end of string follows the key. Then the key is a flag. | ||
# otherwise, a hash follows the key. | ||
def post_key_check(key) | ||
if scan(/\[\]/) # a[b][] indicates that b is an array | ||
container(key, Array) | ||
nil | ||
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash | ||
container(key, Hash) | ||
nil | ||
else # End of key? We do nothing. | ||
key | ||
end | ||
end | ||
|
||
# Add a container to the stack. | ||
def container(key, klass) | ||
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) | ||
value = bind(key, klass.new) | ||
type_conflict! klass, value unless value.is_a?(klass) | ||
push(value) | ||
end | ||
|
||
# Push a value onto the 'stack', which is actually only the top 2 items. | ||
def push(value) | ||
@parent, @top = @top, value | ||
end | ||
|
||
# Bind a key (which may be nil for items in an array) to the provided value. | ||
def bind(key, value) | ||
if top.is_a? Array | ||
if key | ||
if top[-1].is_a?(Hash) && ! top[-1].key?(key) | ||
top[-1][key] = value | ||
else | ||
top << {key => value}.with_indifferent_access | ||
push top.last | ||
value = top[key] | ||
end | ||
else | ||
top << value | ||
end | ||
elsif top.is_a? Hash | ||
key = CGI.unescape(key) | ||
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array) | ||
top[key] ||= value | ||
return top[key] | ||
else | ||
raise ArgumentError, "Don't know what to do: top is #{top.inspect}" | ||
end | ||
|
||
return value | ||
end | ||
|
||
def type_conflict!(klass, value) | ||
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.