lib/serial_spec/parsed_body.rb in serial-spec-0.2.1 vs lib/serial_spec/parsed_body.rb in serial-spec-0.3.0
- old
+ new
@@ -2,60 +2,129 @@
require "active_support/core_ext/hash/indifferent_access"
module SerialSpec
class ParsedBody
+ class MissingSelectorError < StandardError ; end
+ class NotAnEnumerableObject < StandardError ; end
- attr_reader :raw_body
- attr_reader :selector
+ include Enumerable
- def initialize(body)
- @selector = []
- @raw_body = JSON.parse(body)
+ attr_accessor :raw_body
+ attr_accessor :selector
+ attr_reader :body
+
+ def initialize(raw_body=nil, options={})
+ @selector = options[:selector] || []
+ @raw_body = raw_body
end
- def self.add_selector(*methuds)
- methuds.each do |methud|
- class_eval <<-METHOD
- def #{methud.to_s}(*args)
- selector.push([#{methud.to_sym}, args])
- self
- end
- METHOD
- end
+ def body
+ @body ||= JSON.parse(raw_body)
end
- add_selector :first, :last, :[]
+ def each(&block)
+ current_obj = clone.execute
+ raise NotAnEnumerableObject unless current_obj.kind_of?(Hash) or current_obj.kind_of?(Array)
+
+
+ if current_obj.kind_of?(Array)
+ current_obj.each_with_index do |item, i|
+ yield(
+ clone.tap do |b|
+ b.selector.push([:[], i])
+ end
+ )
+ end
+ else
+ current_obj.each do |key, value|
+ yield(
+ key,
+ clone.tap do |b|
+ b.selector.push([:[], key])
+ end
+ )
+ end
+ end
+ end
+
def [](*args)
- selector.push([:[], *args])
- self
+ clone.tap do |b|
+ b.selector.push([:[], *args])
+ end
end
def first(*args)
- selector.push([:first])
- self
+ clone.tap do |b|
+ b.selector.push([:first])
+ end
end
def last(*args)
- selector.push([:last])
- self
+ clone.tap do |b|
+ b.selector.push([:last])
+ end
end
def execute
copy = selector.clone
selector.clear
- copy.inject(raw_body) do |remainder, method_and_args|
- begin
+ current_selector = []
+
+ failure = catch(:failed) do
+
+ return copy.inject(body) do |remainder, method_and_args|
+ current_selector << method_and_args
methud, *args = method_and_args
+
+ if [:first, :last].include?(methud) || args.first.kind_of?(Fixnum)
+ throw(:failed, [:expected_array, remainder, current_selector, method_and_args]) unless remainder.kind_of?(Array)
+ else
+ throw(:failed, [:expected_object, remainder, current_selector, method_and_args]) unless remainder.kind_of?(Hash)
+ end
+
if remainder.kind_of?(Hash)
remainder.with_indifferent_access.send methud, *args
else
remainder.send methud, *args
end
- rescue NoMethodError => ex
- raise ArgumentError, "could not find '#{methud.inspect}' \nfor:\n '#{remainder.inspect}' \nin\n #{raw_body}"
+
end
+
+ end
+
+ if failure.kind_of?(Array)
+ raise MissingSelectorError, failed_message(*failure)
+ end
+ end
+
+ private ###############################
+
+ def initialize_copy(other)
+ @selector = other.selector.clone
+ @raw_body = other.raw_body
+ end
+
+ def formatted_selector(selector)
+ output = ""
+
+ selector.each do |item|
+ if item.first == :[]
+ output << "[#{item.last.inspect}]"
+ else
+ output << "[#{item.first.inspect}]"
+ end
+ end
+ output
+ end
+
+ def failed_message(msg, remainder, selector, current_selector)
+ case msg
+ when :expected_object
+ "expected an object to have #{formatted_selector([current_selector])}, but found #{remainder.class}:\"#{remainder}\" at #{formatted_selector(selector[0..-2])}"
+ when :expected_array
+ "expected an array at \"#{formatted_selector(selector[0..-2])}\", but found #{remainder.class}:'#{remainder}'"
end
end
end