#!/usr/bin/env ruby require File.expand_path(File.join(File.dirname(__FILE__), "test_helper")) # A mock data object with customizable fields. class MockBinDataObject def initialize(methods = {}, params = {}, parent = nil) meta = class << self ; self; end methods.each do |k,v| meta.send(:define_method, k.to_sym) { v } end @parameters = params @parent = parent end attr_accessor :parent def has_parameter?(key) @parameters.has_key?(key) end def get_parameter(key) @parameters[key] end def lazy_evaluator BinData::LazyEvaluator.new(self) end alias_method :safe_respond_to?, :respond_to? end def lazy_eval(*rest) subject.lazy_evaluator.lazy_eval(*rest) end describe BinData::LazyEvaluator, "with no parents" do subject { methods = {m1: 'm1', com: 'mC'} params = {p1: 'p1', com: 'pC'} MockBinDataObject.new(methods, params) } it "evaluates raw value when instantiated" do _(lazy_eval(5)).must_equal 5 end it "evaluates raw value" do _(lazy_eval(5)).must_equal 5 end it "evaluates value" do _(lazy_eval(lambda { 5 })).must_equal 5 end it "evaluates overrides" do _(lazy_eval(lambda { o1 }, o1: 'o1')).must_equal 'o1' end it "does not resolve any unknown methods" do _ { lazy_eval(lambda { unknown }) }.must_raise NameError _ { lazy_eval(lambda { m1 }) }.must_raise NameError _ { lazy_eval(lambda { p1 }) }.must_raise NameError end it "does not have a parent" do _(lazy_eval(lambda { parent })).must_be_nil end it "does not resolve #index" do _ { lazy_eval(lambda { index }) }.must_raise NoMethodError end end describe BinData::LazyEvaluator, "with one parent" do subject { parent_methods = {m1: 'Pm1', m2: 'Pm2', com1: 'PmC', mm: 3} parent_params = {p1: 'Pp1', com1: 'PpC'} parent_obj = MockBinDataObject.new(parent_methods, parent_params) class << parent_obj def echo(a1, a2) [a1, a2] end private :m2 end methods = {m1: 'm1', com1: 'mC'} params = {p1: 'p1', com1: 'pC'} MockBinDataObject.new(methods, params, parent_obj) } it "evaluates raw value" do _(lazy_eval(5)).must_equal 5 end it "evaluates value" do _(lazy_eval(lambda { 5 })).must_equal 5 end it "evaluates overrides before params" do _(lazy_eval(lambda { p1 }, p1: 'o1')).must_equal 'o1' end it "evaluates overrides before methods" do _(lazy_eval(lambda { m1 }, m1: 'o1')).must_equal 'o1' end it "does not resolve any unknown methods" do _ { lazy_eval(lambda { unknown }) }.must_raise NoMethodError end it "resolves parameters in the parent" do _(lazy_eval(lambda { p1 })).must_equal 'Pp1' end it "resolves methods in the parent" do _(lazy_eval(lambda { m1 })).must_equal 'Pm1' end it "invokes methods in the parent" do _(lazy_eval(lambda { echo(p1, m1) })).must_equal ['Pp1', 'Pm1'] end it "invokes private methods in the parent" do _(lazy_eval(lambda { m2 })).must_equal 'Pm2' end it "resolves parameters in preference to methods in the parent" do _(lazy_eval(lambda { com1 })).must_equal 'PpC' end it "has a parent" do _(lazy_eval(lambda { parent })).wont_be_nil end it "does not resolve #index" do _ { lazy_eval(lambda { index }) }.must_raise NoMethodError end end describe BinData::LazyEvaluator, "with nested parents" do subject { pparent_methods = {m1: 'PPm1', m2: 'PPm2', com1: 'PPmC'} pparent_params = {p1: 'PPp1', p2: 'PPp2', com1: 'PPpC'} pparent_obj = MockBinDataObject.new(pparent_methods, pparent_params) def pparent_obj.echo(arg) ["PP", arg] end def pparent_obj.echo2(arg) ["PP2", arg] end parent_methods = {m1: 'Pm1', com1: 'PmC', sym1: :m2, sym2: -> { m2 }} parent_params = {p1: 'Pp1', com1: 'PpC'} parent_obj = MockBinDataObject.new(parent_methods, parent_params, pparent_obj) def parent_obj.echo(arg) ["P", arg] end methods = {m1: 'm1', com1: 'mC'} params = {p1: 'p1', com1: 'pC'} MockBinDataObject.new(methods, params, parent_obj) } it "accepts symbols as a shortcut to lambdas" do _(lazy_eval(:p1)).must_equal 'Pp1' _(lazy_eval(:p2)).must_equal 'PPp2' _(lazy_eval(:m1)).must_equal 'Pm1' _(lazy_eval(:m2)).must_equal 'PPm2' end it "does not resolve any unknown methods" do _ { lazy_eval(lambda { unknown }) }.must_raise NoMethodError end it "resolves parameters in the parent" do _(lazy_eval(lambda { p1 })).must_equal 'Pp1' end it "resolves methods in the parent" do _(lazy_eval(lambda { m1 })).must_equal 'Pm1' end it "resolves parameters in the parent's parent" do _(lazy_eval(lambda { p2 })).must_equal 'PPp2' end it "resolves methods in the parent's parent" do _(lazy_eval(lambda { m2 })).must_equal 'PPm2' end it "invokes methods in the parent" do _(lazy_eval(lambda { echo(m1) })).must_equal ['P', 'Pm1'] end it "invokes methods in the parent's parent" do _(lazy_eval(lambda { parent.echo(m1) }, { m1: 'o1'})).must_equal ['PP', 'o1'] end it "invokes methods in the parent's parent" do _(lazy_eval(lambda { echo2(m1) })).must_equal ['PP2', 'Pm1'] end it "resolves parameters in preference to methods in the parent" do _(lazy_eval(lambda { com1 })).must_equal 'PpC' end it "resolves methods in the parent explicitly" do _(lazy_eval(lambda { parent.m1 })).must_equal 'PPm1' end it "cascades lambdas " do _(lazy_eval(lambda { sym1 })).must_equal 'PPm2' _(lazy_eval(lambda { sym2 })).must_equal 'PPm2' end it "does not resolve #index" do _ { lazy_eval(lambda { index }) }.must_raise NoMethodError end end