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