Method: JSI::JSON::Pointer#modified_document_copy

Defined in:
lib/jsi/json/pointer.rb

#modified_document_copy(document) {|Object| ... } ⇒ Object

takes a document and a block. the block is yielded the content of the given document at this pointer's location. the block must result a modified copy of that content (and MUST NOT modify the object it is given). this modified copy of that content is incorporated into a modified copy of the given document, which is then returned. the structure and contents of the document outside the path pointed to by this pointer is not modified.

Parameters:

  • document (Object)

    the document to apply this pointer to

Yields:

  • (Object)

    the content this pointer applies to in the given document the block must result in the new content which will be placed in the modified document copy.

Returns:

  • (Object)

    a copy of the given document, with the content this pointer applies to replaced by the result of the block



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/jsi/json/pointer.rb', line 209

def modified_document_copy(document, &block)
  # we need to preserve the rest of the document, but modify the content at our path.
  #
  # this is actually a bit tricky. we can't modify the original document, obviously.
  # we could do a deep copy, but that's expensive. instead, we make a copy of each array
  # or hash in the path above this node. this node's content is modified by the caller, and
  # that is recursively merged up to the document root. the recursion is done with a
  # y combinator, for no other reason than that was a fun way to implement it.
  modified_document = JSI::Util.ycomb do |rec|
    proc do |subdocument, subpath|
      if subpath == []
        Typelike.modified_copy(subdocument, &block)
      else
        car = subpath[0]
        cdr = subpath[1..-1]
        if subdocument.respond_to?(:to_hash)
          subdocument_car = (subdocument.respond_to?(:[]) ? subdocument : subdocument.to_hash)[car]
          car_object = rec.call(subdocument_car, cdr)
          if car_object.object_id == subdocument_car.object_id
            subdocument
          else
            (subdocument.respond_to?(:merge) ? subdocument : subdocument.to_hash).merge({car => car_object})
          end
        elsif subdocument.respond_to?(:to_ary)
          if car.is_a?(String) && car =~ /\A\d+\z/
            car = car.to_i
          end
          unless car.is_a?(Integer)
            raise(TypeError, "bad subscript #{car.pretty_inspect.chomp} with remaining subpath: #{cdr.inspect} for array: #{subdocument.pretty_inspect.chomp}")
          end
          subdocument_car = (subdocument.respond_to?(:[]) ? subdocument : subdocument.to_ary)[car]
          car_object = rec.call(subdocument_car, cdr)
          if car_object.object_id == subdocument_car.object_id
            subdocument
          else
            (subdocument.respond_to?(:[]=) ? subdocument : subdocument.to_ary).dup.tap do |arr|
              arr[car] = car_object
            end
          end
        else
          raise(TypeError, "bad subscript: #{car.pretty_inspect.chomp} with remaining subpath: #{cdr.inspect} for content: #{subdocument.pretty_inspect.chomp}")
        end
      end
    end
  end.call(document, reference_tokens)
  modified_document
end