lib/endpoint/stub.rb in endpoint_stub-1.1.0 vs lib/endpoint/stub.rb in endpoint_stub-1.4.5
- old
+ new
@@ -1,5 +1,6 @@
+require 'endpoint/response'
require 'endpoint_stub'
require 'webmock'
module Endpoint
##
@@ -19,11 +20,11 @@
# If a block is supplied, it will be executed in the context
# of the new Endpoint::Stub, allowing you to elegantly mock
# custom responses if needed.
def create_for(model, options={}, &block)
model = assure_model model
- return if stubs.keys.include? model
+ return if stubs.keys.map(&:name).include? model.name
new_stub = Stub.new(model, options)
EndpointStub::Config.default_responses.each do |response|
new_stub.mock_response(*response)
end
@@ -44,10 +45,14 @@
def get_for(model)
@stubs[assure_model(model)]
end
+ def clear_all_records!
+ @stubs.values.each(&:clear_records!)
+ end
+
##
# Clears all endpoint stubs.
def clear!
@stubs = {}
end
@@ -57,10 +62,14 @@
# i.e. Endpoint::Stub[Post]
def [](model)
create_for model or get_for model
end
+ def each(&block)
+ @stubs.each(&block)
+ end
+
private
def assure_model(model)
if model.ancestors.include? ActiveResource::Base
model
else
@@ -69,16 +78,17 @@
end
end
attr_reader :defaults
attr_reader :model
+ attr_reader :site
attr_accessor :records
def initialize(model, options)
@defaults = options[:defaults] || {}
@model = model
- @site = URI "#{model.site}/#{model.name.underscore.pluralize}"
+ @site = URI "#{model.site}/#{model.name.split('::').last.underscore.pluralize}"
@responses = {}
@records = []
end
@@ -91,10 +101,11 @@
raise "Endpoint::Stub#add_record expects a Hash. Got #{attrs.class.name}."
end
attrs[:id] = current_id
attrs.merge!(@defaults) { |k,a,b| a }
@records << attrs
+ attrs
end
##
# Updates the record with the given id with the given attributes.
def update_record(id, attrs)
@@ -115,10 +126,18 @@
@records[id] = nil
true
end
end
+ ##
+ # Clear all records in this stub.
+ def clear_records!
+ @records = []
+ end
+
+ ##
+ # Get the record at the given id. Accepts strings as well as ints.
def record(id)
@records[id.to_i]
end
##
@@ -169,32 +188,60 @@
# The proc will be called with the request object, the extracted
# parameters from the uri, and the stub object so that you can
# interact with the stubbed records.
def mock_response(type, route='', proc=nil, &block)
proc = block if block_given?
+ route = clean_route route
+ @responses[type] ||= {}
+ @responses[type][route].deactivate! if @responses[type][route]
+ @responses[type][route] = Response.new(type, prepare_uri(type, route), self, &proc)
+ @responses[type][route].activate!
+ end
+
+ ##
+ # Same thing as mock_response, except it will not overWRITE existing
+ # mocks. Instead, it allows you to call a block inside of your response
+ # which will act as a 'super' call, invoking previously defined
+ # responses. Yielding inside a top-level response will give you
+ # an empty hash, so no nil related issues should arrise (unless of
+ # course the super-response returns nil, which it shouldn't).
+ #
+ # Also note that this does not re-activate a deactivated response.
+ def override_response(type, route, proc=nil, &block)
+ proc = block if block_given?
route = clean_route route
- site = "#{@site.scheme}://#{@site.host}"
- path = @site.path.split(/\/+/).reject(&:empty?)
- if route[0] == '.' && !route.include?('/')
- # This allows passing '.json', etc as the route
- if path.last
- path = path[0...-1] + [path.last+route]
- else
- site += route
- end
+ if @responses[type] and @responses[type][route]
+ @responses[type][route].add_to_stack(&proc)
else
- path += route.split('/')
+ mock_response(type, route, proc)
end
+ end
- @responses[type] ||= {}
- @responses[type][route] = Response.new(type, URI.parse(site+'/'+path.join('/')), self, &proc)
- @responses[type][route].activate!
+ ##
+ # Overrides all currently assigned responses. Will not have any effect
+ # on responses mocked after this method is called.
+ def override_all(&block)
+ @responses.each do |type, responses|
+ responses.each do |route, response|
+ response.add_to_stack(&block)
+ end
+ end
end
##
+ # Removes all overrides, reducing each response to their originals.
+ def drop_overrides!
+ @responses.each do |type, responses|
+ responses.each do |route, response|
+ response.drop_overrides!
+ end
+ end
+ end
+
+ ##
# Remove a mocked response with the given type and route.
def unmock_response(type, route)
route = clean_route route
if @responses[type] && @responses[type][route]
@responses[type][route].deactivate!
@@ -202,106 +249,30 @@
true
end
end
private
- def clean_route(route)
- route = route[1..-1] if route[0] == '/'
- route = route[0...-1] if route[-1] == '/'
- route
- end
+ def prepare_uri(type, route)
+ site = "#{@site.scheme}://#{@site.host}:#{@site.port}"
+ path = @site.path.split(/\/+/).reject(&:empty?)
- class Response
- include WebMock::API
-
- # For remembering where a uri-based parameter is located.
- ParamIndices = Struct.new(:slash, :dot)
- # Allows more comfortable use of Symbol keys when accessing
- # params (which are string keys).
- class Params < Hash
- def [](key)
- super(key.to_s)
+ if route[0] == '.' && !route.include?('/')
+ # This allows passing '.json', etc as the route
+ if path.last
+ path = path[0...-1] + [path.last+route]
+ else
+ site += route
end
- def []=(key, value)
- super(key.to_s, value)
- end
+ else
+ path += route.split('/')
end
- def initialize(type, url, stub, &proc)
- @param_indices = {}
-
- @url_regex = build_url_regex!(url)
+ URI.parse site+'/'+path.join('/')
+ end
- @type = type
- @proc = proc
- @stub = stub
- end
-
- # Should be called only once, internally to perform the actual WebMock stubbing.
- def activate!
- @stubbed_request = stub_request(@type, @url_regex).to_return do |request|
- params = extract_params(request)
-
- results = @proc.call(request, params, @stub)
- results[:body] = results[:body].to_json unless results[:body].is_a? String
- results
- end
- end
-
- # This should remove the request stubbed by #activate!
- def deactivate!
- remove_request_stub @stubbed_request
- end
-
- private
- # Bang is there because this method populates @param_indices.
- def build_url_regex!(url)
- regex = ""
- separate(url).each_with_index do |x, slash_index|
- regex += '/' unless slash_index == 0
- # If there is a colon, it's a parameter. i.e. /resource/:id.json
- if x.include? ':' and !(x[1..-1] =~ /^\d$/) # If it's just numbers, it's probably a port number
- # We split by dot at this point to separate the parameter from any
- # format/domain related suffix.
- dot_split = x.split('.')
- inner_regex = []
-
- dot_split.each_with_index do |name, dot_index|
- # A parameter can show up after a dot as well. i.e. /resource/:id.:format
- inner_regex << if name.include? ':'
- param_name = name[1..-1]
- @param_indices[param_name] = ParamIndices.new(slash_index, dot_index)
- # Add .+ regex to capture any data at this point in the url.
- ".+"
- else
- # If there's no colon, it's a static part of the target url.
- Regexp.escape(name)
- end
- end
-
- # "inner_regex" was built by splitting on dots, so we put the dots back.
- regex += inner_regex.join('\.')
- else
- # No colon, so this segment is static.
- regex += Regexp.escape(x)
- end
- end
- Regexp.new regex
- end
-
- def extract_params(request)
- url = separate request.uri
- params = Params.new
- @param_indices.each do |param_name, index|
- value = url[index.slash].split('.')[index.dot]
-
- params[param_name] = value
- end
- params
- end
-
- def separate(url)
- url.to_s[url.to_s.index('://')+3..-1].split '/'
- end
+ def clean_route(route)
+ route = route[1..-1] if route[0] == '/'
+ route = route[0...-1] if route[-1] == '/'
+ route
end
end
end
\ No newline at end of file