json_patch 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/README.md +10 -0
- data/Rakefile +5 -0
- data/json_patch.gemspec +32 -0
- data/lib/json/patch.rb +69 -0
- data/lib/json/patch/version.rb +5 -0
- data/lib/mongoid/patchable.rb +110 -0
- data/spec/json/patch_spec.rb +55 -0
- data/spec/mongoid/patchable_spec.rb +119 -0
- data/spec/spec_helper.rb +12 -0
- metadata +130 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use "ruby-1.9.2-p180@json_patch"
|
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/json_patch.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "json/patch/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "json_patch"
|
7
|
+
s.version = JSON::Patch::VERSION
|
8
|
+
s.authors = ["Travis Vachon"]
|
9
|
+
s.email = ["travis@copious.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{JSON patch implementation in ruby}
|
12
|
+
s.description = %q{An implementation of JSON patch in Ruby.
|
13
|
+
|
14
|
+
http://tools.ietf.org/html/draft-pbryan-json-patch-01
|
15
|
+
|
16
|
+
Utilities for applying JSON patches to arbitary objects. To
|
17
|
+
participate in the patch protocol, classes can implement #apply_patch
|
18
|
+
}
|
19
|
+
|
20
|
+
s.rubyforge_project = "json_patch"
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
s.add_development_dependency "mocha"
|
28
|
+
s.add_development_dependency "rspec"
|
29
|
+
s.add_development_dependency "bson_ext"
|
30
|
+
s.add_development_dependency "mongoid"
|
31
|
+
s.add_development_dependency "gemfury"
|
32
|
+
end
|
data/lib/json/patch.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "json/patch/version"
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
class Patch
|
5
|
+
attr_reader :hunks
|
6
|
+
|
7
|
+
def initialize(hunks)
|
8
|
+
@hunks = hunks.map {|h| Hunk.new(h)}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Apply this patch to an object
|
12
|
+
#
|
13
|
+
# The object to be modified (which may be nested inside the object
|
14
|
+
# in the method signature) MUST implement #apply_patch.
|
15
|
+
#
|
16
|
+
# #apply_patch should return true if the patch can be succesfully
|
17
|
+
# applied and false otherwise.
|
18
|
+
#
|
19
|
+
# If any hunk in the patch cannot be applied successfully, the
|
20
|
+
# PATCH rfc dictates that the entire patch MUST not be
|
21
|
+
# applied. This atomicity must be handled by the #apply_patch
|
22
|
+
# implementation.
|
23
|
+
def apply_to(object)
|
24
|
+
object.apply_patch(self)# if object.respond_to?(:apply_patch)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Hunk
|
29
|
+
attr_reader :op, :path, :value
|
30
|
+
|
31
|
+
def initialize(attributes={})
|
32
|
+
op = case
|
33
|
+
when attributes.has_key?('add') then 'add'
|
34
|
+
when attributes.has_key?('remove') then 'remove'
|
35
|
+
when attributes.has_key?('replace') then 'replace'
|
36
|
+
end
|
37
|
+
if op
|
38
|
+
@op = op.to_sym
|
39
|
+
else
|
40
|
+
raise ArgumentError
|
41
|
+
end
|
42
|
+
self.path = attributes[op]
|
43
|
+
@value = attributes['value']
|
44
|
+
end
|
45
|
+
|
46
|
+
def path=(path_string)
|
47
|
+
raise ArgumentError if path_string and not path_string[0] == '/'
|
48
|
+
path_string ||= ''
|
49
|
+
@path = path_string.split('/').drop(1).map {|section| section.to_sym}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Given a root object, resolve the paths in this patch.
|
53
|
+
# Return a tuple of the object and element to be modified.
|
54
|
+
#
|
55
|
+
# For example (from the specs):
|
56
|
+
#
|
57
|
+
# OpenStruct.new(:foo => 1, :bar => OpenStruct.new(:baz => 2))
|
58
|
+
# JSON::Hunk.new('add' => '/bar/baz').resolve_path(obj).should == [obj.bar, :baz]
|
59
|
+
def resolve_path(object)
|
60
|
+
path_to_element = path[0..-2]
|
61
|
+
element = path[-1]
|
62
|
+
[path_to_element.inject(object) {|o, e| o.send(e) }, element]
|
63
|
+
end
|
64
|
+
|
65
|
+
def ==(other)
|
66
|
+
op == other.op and path == other.path and value = other.value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Patchable
|
3
|
+
# Apply a patch this this document.
|
4
|
+
#
|
5
|
+
# Currently assumes add_to_set and set operations will succeed.
|
6
|
+
#
|
7
|
+
# TODO: is there a better way to ensure atomicity of multiple
|
8
|
+
# operations while avoiding race conditions from multiple clients?
|
9
|
+
#
|
10
|
+
# it looks like
|
11
|
+
#
|
12
|
+
# Preferences.collection.update({'user_id' => 1}, {'$addToSet' => {'follow_suggest_blacklist' => 4}, '$set' => {'hams' => 2}})
|
13
|
+
#
|
14
|
+
# would do this, but a) it needs to deal with multiple hunks
|
15
|
+
# acting on the same attribute and b) it looks like mongoid
|
16
|
+
# doesn't support this ootb. more research needed...
|
17
|
+
#
|
18
|
+
# TODO: handle numeric path segments
|
19
|
+
def apply_patch(patch)
|
20
|
+
# compile operation information for verification
|
21
|
+
ops = patch.hunks.map do |hunk|
|
22
|
+
(obj, element) = hunk.resolve_path(self)
|
23
|
+
[hunk, obj, element, obj.fields[element.to_s]]
|
24
|
+
end
|
25
|
+
|
26
|
+
if patch_ops_valid?(ops)
|
27
|
+
# if something goes wrong here, raise an error to let the client
|
28
|
+
# know the patch may be partially applied
|
29
|
+
ops.each_with_index do |(hunk, obj, element, field), index|
|
30
|
+
value = hunk.value
|
31
|
+
case hunk.op
|
32
|
+
when :add
|
33
|
+
process_add(obj, field, element, value)
|
34
|
+
when :replace
|
35
|
+
process_replace(obj, field, element, value)
|
36
|
+
when :remove
|
37
|
+
process_remove(obj, field, element, value)
|
38
|
+
else
|
39
|
+
raise "Illegal operation #{hunk.op} in hunk #{index}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def process_add(obj, field, element, value)
|
49
|
+
case field.type.name
|
50
|
+
when 'Array'
|
51
|
+
obj.add_to_set(element, (value.is_a?(Array) ? {'$each' => value} : value))
|
52
|
+
when 'Hash'
|
53
|
+
(key, val) = destructure_hash_value(value)
|
54
|
+
obj.send("#{element}=", {}) if obj.send(element).nil?
|
55
|
+
obj.send(element)[key] = val
|
56
|
+
obj.save(validate: false)
|
57
|
+
else
|
58
|
+
obj.write_attribute(element, value)
|
59
|
+
obj.save
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_replace(obj, field, element, value)
|
64
|
+
case field.type.name
|
65
|
+
when 'Array'
|
66
|
+
obj.add_to_set(element, (value.is_a?(Array) ? {'$each' => value} : value))
|
67
|
+
when 'Hash'
|
68
|
+
(key, val) = destructure_hash_value(value)
|
69
|
+
obj.send("#{element}=", {}) if obj.send(element).nil?
|
70
|
+
obj.send(element)[key] = val
|
71
|
+
obj.save(validate: false)
|
72
|
+
else
|
73
|
+
obj.write_attribute(element, value)
|
74
|
+
obj.save
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def process_remove(obj, field, element, value = nil)
|
79
|
+
case field.type.name
|
80
|
+
when 'Array'
|
81
|
+
obj.pull_all(element, (value.is_a?(Array) ? value : [value]))
|
82
|
+
when 'Hash'
|
83
|
+
(key, val) = destructure_hash_value(value)
|
84
|
+
if obj.send(element).nil?
|
85
|
+
obj.send("#{element}=", {})
|
86
|
+
else
|
87
|
+
obj.send(element).delete(key)
|
88
|
+
end
|
89
|
+
obj.save(validate: false)
|
90
|
+
else
|
91
|
+
obj.write_attribute(element, nil)
|
92
|
+
obj.save
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def patch_ops_valid?(ops)
|
97
|
+
ops.inject(true) do |valid, (hunk, obj, element, field)|
|
98
|
+
return false unless valid && hunk && obj && element && field
|
99
|
+
return !hunk.value.nil? if [:add, :replace].include?(hunk.op)
|
100
|
+
return true if hunk.op == :remove
|
101
|
+
false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def destructure_hash_value(value)
|
106
|
+
(key, value) = value.split(/\=/, 2)
|
107
|
+
key.ends_with?('[]') ? [key.slice(0..-3), value.split(',')] : [key, value]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
def os(a); OpenStruct.new(a) end
|
5
|
+
|
6
|
+
describe JSON::Patch do
|
7
|
+
let(:hunk) { { 'add' => '/foo/bar', 'value' => 'hams'} }
|
8
|
+
subject {JSON::Patch.new([hunk])}
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
its(:hunks) { should eq([JSON::Hunk.new(hunk)])}
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#apply_to' do
|
15
|
+
let(:obj) { os(:foo => 1, :bar => os(:baz => 2)) }
|
16
|
+
it 'should call apply_patch on the target' do
|
17
|
+
obj.expects(:apply_patch).with(subject).returns(true)
|
18
|
+
subject.apply_to(obj).should == true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe JSON::Hunk do
|
24
|
+
subject {JSON::Hunk.new('add' => '/foo/bar', 'value' => 'hams')}
|
25
|
+
|
26
|
+
describe "#initialize" do
|
27
|
+
its(:op) { should eq(:add)}
|
28
|
+
its(:path) { should eq([:foo, :bar])}
|
29
|
+
its(:value) { should eq('hams')}
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#path=' do
|
33
|
+
it 'should split paths' do
|
34
|
+
subject.path = '/foo/bar'
|
35
|
+
subject.path.should == [:foo, :bar]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should set paths to an empty list when given nil' do
|
39
|
+
subject.path = nil
|
40
|
+
subject.path.should == []
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should raise an argument error if path without starting slash is passed' do
|
44
|
+
lambda { subject.path = 'foo/bar' }.should raise_error(ArgumentError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#resolve_path' do
|
49
|
+
let(:obj) { os(:foo => 1, :bar => os(:baz => 2)) }
|
50
|
+
it "should resolve paths" do
|
51
|
+
JSON::Hunk.new('add' => '/foo').resolve_path(obj).should == [obj, :foo]
|
52
|
+
JSON::Hunk.new('add' => '/bar/baz').resolve_path(obj).should == [obj.bar, :baz]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mongoid'
|
3
|
+
require 'mongoid/patchable'
|
4
|
+
require 'json/patch'
|
5
|
+
|
6
|
+
class PatchableDoc
|
7
|
+
include Mongoid::Document
|
8
|
+
include Mongoid::Patchable
|
9
|
+
field :foo, type: Array
|
10
|
+
field :bar, type: Hash
|
11
|
+
field :baz, type: Integer
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Mongoid::Patchable do
|
15
|
+
subject { PatchableDoc.new }
|
16
|
+
|
17
|
+
describe "#apply_patch" do
|
18
|
+
it 'should call process_add' do
|
19
|
+
subject.expects(:process_add).with(subject, subject.fields['foo'], :foo, 12345)
|
20
|
+
subject.apply_patch(JSON::Patch.new([{ 'add' => '/foo', 'value' => 12345}])).should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should call process_replace' do
|
24
|
+
subject.expects(:process_replace).with(subject, subject.fields['foo'], :foo, 12345)
|
25
|
+
subject.apply_patch(JSON::Patch.new([{ 'replace' => '/foo', 'value' => 12345}])).should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should call process_remove' do
|
29
|
+
subject.expects(:process_remove).with(subject, subject.fields['foo'], :foo, nil)
|
30
|
+
subject.apply_patch(JSON::Patch.new([{ 'remove' => '/foo'}])).should be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#process_add" do
|
35
|
+
it "should add a scalar element to an array" do
|
36
|
+
subject.expects(:add_to_set).with(:foo, 12345)
|
37
|
+
subject.send(:process_add, subject, subject.fields['foo'], :foo, 12345)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should add an array element to an array" do
|
41
|
+
subject.expects(:add_to_set).with(:foo, {'$each' => [12345]})
|
42
|
+
subject.send(:process_add, subject, subject.fields['foo'], :foo, [12345])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should add an element to a hash" do
|
46
|
+
subject.expects(:save)
|
47
|
+
subject.send(:process_add, subject, subject.fields['bar'], :bar, "rilo=kiley")
|
48
|
+
subject.bar.should == {'rilo' => 'kiley'}
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should set a scalar" do
|
52
|
+
subject.expects(:write_attribute).with(:baz, 12345)
|
53
|
+
subject.expects(:save)
|
54
|
+
subject.send(:process_add, subject, subject.fields['baz'], :baz, 12345)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#process_replace" do
|
59
|
+
it "should replace a scalar element in an array" do
|
60
|
+
subject.expects(:add_to_set).with(:foo, 12345)
|
61
|
+
subject.send(:process_replace, subject, subject.fields['foo'], :foo, 12345)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should replace an array element in an array" do
|
65
|
+
subject.expects(:add_to_set).with(:foo, {'$each' => [12345]})
|
66
|
+
subject.send(:process_replace, subject, subject.fields['foo'], :foo, [12345])
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should replace an element in a hash" do
|
70
|
+
subject.bar = {'rilo' => 'yelik'}
|
71
|
+
subject.expects(:save)
|
72
|
+
subject.send(:process_replace, subject, subject.fields['bar'], :bar, "rilo=kiley")
|
73
|
+
subject.bar.should == {'rilo' => 'kiley'}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should replace a scalar" do
|
77
|
+
subject.baz = 54321
|
78
|
+
subject.expects(:write_attribute).with(:baz, 12345)
|
79
|
+
subject.expects(:save)
|
80
|
+
subject.send(:process_replace, subject, subject.fields['baz'], :baz, 12345)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#process_remove" do
|
85
|
+
it "should remove a scalar element from an array" do
|
86
|
+
subject.expects(:pull_all).with(:foo, [12345])
|
87
|
+
subject.send(:process_remove, subject, subject.fields['foo'], :foo, 12345)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should remove a array element from an array" do
|
91
|
+
subject.expects(:pull_all).with(:foo, [12345])
|
92
|
+
subject.send(:process_remove, subject, subject.fields['foo'], :foo, [12345])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should remove an element from a hash" do
|
96
|
+
subject.bar = {'rilo' => 'kiley'}
|
97
|
+
subject.expects(:save)
|
98
|
+
subject.send(:process_remove, subject, subject.fields['bar'], :bar, "rilo")
|
99
|
+
subject.bar.should == {}
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should nil a scalar" do
|
103
|
+
subject.baz = 54321
|
104
|
+
subject.expects(:write_attribute).with(:baz, nil)
|
105
|
+
subject.expects(:save)
|
106
|
+
subject.send(:process_remove, subject, subject.fields['baz'], :baz)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#destructure_hash_value" do
|
111
|
+
it "should return array of key and value" do
|
112
|
+
subject.send(:destructure_hash_value, "foo=bar").should == ['foo', 'bar']
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return removed square brackets from key and array from csv string" do
|
116
|
+
subject.send(:destructure_hash_value, "foo[]=bar,baz").should == ['foo', ['bar', 'baz']]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_patch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Travis Vachon
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-01-07 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mocha
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bson_ext
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mongoid
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: gemfury
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id005
|
70
|
+
description: |
|
71
|
+
An implementation of JSON patch in Ruby.
|
72
|
+
|
73
|
+
http://tools.ietf.org/html/draft-pbryan-json-patch-01
|
74
|
+
|
75
|
+
Utilities for applying JSON patches to arbitary objects. To
|
76
|
+
participate in the patch protocol, classes can implement #apply_patch
|
77
|
+
|
78
|
+
email:
|
79
|
+
- travis@copious.com
|
80
|
+
executables: []
|
81
|
+
|
82
|
+
extensions: []
|
83
|
+
|
84
|
+
extra_rdoc_files: []
|
85
|
+
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .rspec
|
89
|
+
- .rvmrc
|
90
|
+
- Gemfile
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- json_patch.gemspec
|
94
|
+
- lib/json/patch.rb
|
95
|
+
- lib/json/patch/version.rb
|
96
|
+
- lib/mongoid/patchable.rb
|
97
|
+
- spec/json/patch_spec.rb
|
98
|
+
- spec/mongoid/patchable_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
homepage: ""
|
101
|
+
licenses: []
|
102
|
+
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: "0"
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: "0"
|
120
|
+
requirements: []
|
121
|
+
|
122
|
+
rubyforge_project: json_patch
|
123
|
+
rubygems_version: 1.8.11
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: JSON patch implementation in ruby
|
127
|
+
test_files:
|
128
|
+
- spec/json/patch_spec.rb
|
129
|
+
- spec/mongoid/patchable_spec.rb
|
130
|
+
- spec/spec_helper.rb
|