desc: Tests manipulation operations on objects
tests:

    # Define some objects to work with
    - def: obj = r.expr({'a':1, 'b':2,'c':"str",'d':null,'e':{'f':'buzz'}})

    ##  Get attr

    - cd: obj['a']
      js: [obj('a'), obj.getField('a')]
      ot: 1
    - cd: obj['c']
      js: [obj('c'), obj.getField('c')]
      ot: "'str'"

    ## Has_Fields

    - cd: obj.has_fields('b')
      ot: true
    - cd: obj.keys().contains('d')
      ot: true
    - cd: obj.has_fields('d')
      ot: false
    - cd: obj.has_fields({'e':'f'})
      ot: true
    - cd: obj.has_fields({'e':'g'})
      ot: false
    - cd: obj.has_fields('f')
      ot: false

    # Has_Fields is variadic
    - cd: obj.has_fields('a', 'b')
      ot: true
    - cd: obj.has_fields('a', 'd')
      ot: false
    - cd: obj.has_fields('a', 'f')
      ot: false
    - cd: obj.has_fields('a', {'e':'f'})
      ot: true

    # Has_Fields is polymorphic
    - cd: r.expr([obj, obj.pluck('a', 'b')]).has_fields('a', 'b').count()
      ot: 2
    - cd: r.expr([obj, obj.pluck('a', 'b')]).has_fields('a', 'c').count()
      ot: 1
    - cd: r.expr([obj, obj.pluck('a', 'e')]).has_fields('a', {'e':'f'}).count()
      ot: 2

    ## Pluck

    - cd: obj.pluck('a')
      ot: ({'a':1})
    - cd: obj.pluck('a', 'b')
      ot: ({'a':1, 'b':2})

    ## Without

    - cd: obj.without('a')
      ot: ({'b':2, 'c':'str', 'd':null, 'e':{'f':'buzz'}})
    - cd: obj.without('a', 'b')
      ot: ({'c':'str', 'd':null,'e':{'f':'buzz'}})
    - cd: obj.without('a', 'b', 'c', 'd')
      ot: ({'e':{'f':'buzz'}})
    - cd: obj.without({'e':'f'})
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':{}})
    - cd: obj.without({'e':'buzz'})
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':{'f':'buzz'}})

    ## Merge
    #obj = r.expr({'a':1, 'b':2,'c':"str",'d':null,'e':{'f':'buzz'}})

    # complete replacement
    - cd: "obj.merge(1)"
      ot: 1

    # add attr
    - cd: "obj.merge({'e':-2})"
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':-2})

    # delete attr
    - cd: "obj.merge({'e':r.literal()})"
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null})

    # recursive merge
    - cd: "obj.merge({'e':{'f':'quux'}})"
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':{'f':'quux'}})

    - cd: "obj.merge({'e':{'g':'quux'}})"
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':{'f':'buzz', 'g':'quux'}})

    - cd: "obj.merge({'e':r.literal({'g':'quux'})})"
      ot: ({'a':1, 'b':2, 'c':'str', 'd':null, 'e':{'g':'quux'}})

    # overwrite
    - cd: "obj.merge({'a':-1})"
      ot: ({'a':-1, 'b':2, 'c':'str', 'd':null, 'e':{'f':'buzz'}})

    # errors
    - cd: "r.literal('foo')"
      ot: err("RqlRuntimeError", "Stray literal keyword found, literal can only be present inside merge and cannot nest inside other literals.", [])

    - cd: "obj.merge(r.literal('foo'))"
      ot: err("RqlRuntimeError", "Stray literal keyword found, literal can only be present inside merge and cannot nest inside other literals.", [])

    - cd: "obj.merge({'foo':r.literal(r.literal('foo'))})"
      ot: err("RqlRuntimeError", "Stray literal keyword found, literal can only be present inside merge and cannot nest inside other literals.", [])

    ## keys
    - cd: obj.keys()
      ot: (['a', 'b', 'c', 'd', 'e'])
