hana 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = 'minitest/autorun'
7
+ at.find_directories = ARGV unless ARGV.empty?
8
+ end
File without changes
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2012-09-05
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,9 @@
1
+ .autotest
2
+ CHANGELOG.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/hana.rb
7
+ test/helper.rb
8
+ test/test_hana.rb
9
+ test/test_patch.rb
@@ -0,0 +1,58 @@
1
+ = hana
2
+
3
+ * http://github.com/tenderlove/hana
4
+
5
+ == DESCRIPTION:
6
+
7
+ Implementation of JSON Patch and JSON Pointer drafts.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Implements draft specs of the JSON Patch and JSON pointer spec:
12
+
13
+ * http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-01
14
+ * http://tools.ietf.org/html/draft-ietf-appsawg-json-patch-03
15
+
16
+ These are drafts, so it could change. This works against Ruby objects, so you
17
+ should load the JSON to Ruby, process it, then emit as JSON again.
18
+
19
+ == SYNOPSIS:
20
+
21
+ patch = Hana::Patch.new [
22
+ { 'add' => '/baz', 'value' => 'qux' }
23
+ ]
24
+
25
+ patch.apply('foo' => 'bar') # => {'baz' => 'qux', 'foo' => 'bar'}
26
+
27
+ == REQUIREMENTS:
28
+
29
+ * Ruby
30
+
31
+ == INSTALL:
32
+
33
+ * gem install hana
34
+
35
+ == LICENSE:
36
+
37
+ (The MIT License)
38
+
39
+ Copyright (c) 2012 Aaron Patterson
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining
42
+ a copy of this software and associated documentation files (the
43
+ 'Software'), to deal in the Software without restriction, including
44
+ without limitation the rights to use, copy, modify, merge, publish,
45
+ distribute, sublicense, and/or sell copies of the Software, and to
46
+ permit persons to whom the Software is furnished to do so, subject to
47
+ the following conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
55
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
56
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
57
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
58
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugins.delete :rubyforge
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :gemspec # `gem install hoe-gemspec`
9
+ Hoe.plugin :git # `gem install hoe-git`
10
+
11
+ Hoe.spec 'hana' do
12
+ developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
13
+ self.readme_file = 'README.rdoc'
14
+ self.history_file = 'CHANGELOG.rdoc'
15
+ self.extra_rdoc_files = FileList['*.rdoc']
16
+ end
17
+
18
+ # vim: syntax=ruby
@@ -0,0 +1,110 @@
1
+ module Hana
2
+ VERSION = '1.0.0'
3
+
4
+ class Pointer
5
+ include Enumerable
6
+
7
+ def initialize path
8
+ @path = Pointer.parse path
9
+ end
10
+
11
+ def each
12
+ @path.each { |x| yield x }
13
+ end
14
+
15
+ def to_a
16
+ @path.dup
17
+ end
18
+
19
+ def eval object
20
+ Pointer.eval @path, object
21
+ end
22
+
23
+ def self.eval list, object
24
+ list.inject(object) { |o, part| o[(Array === o ? part.to_i : part)] }
25
+ end
26
+
27
+ def self.parse path
28
+ path.sub(/^\//, '').split(/(?<!\^)\//).map { |part|
29
+ part.gsub(/\^([\/^])/, '\1')
30
+ }
31
+ end
32
+ end
33
+
34
+ class Patch
35
+ class Exception < StandardError
36
+ end
37
+
38
+ def initialize is
39
+ @is = is
40
+ end
41
+
42
+ def apply doc
43
+ doc = doc.dup
44
+ @is.each { |ins|
45
+ send ins.keys.sort.first, ins, doc
46
+ }
47
+ doc
48
+ end
49
+
50
+ private
51
+
52
+ def add ins, doc
53
+ list = Pointer.parse ins['add']
54
+ key = list.pop
55
+ obj = Pointer.eval list, doc
56
+
57
+ if Array === obj
58
+ obj.insert key.to_i, ins['value']
59
+ else
60
+ obj[key] = ins['value']
61
+ end
62
+ end
63
+
64
+ def move ins, doc
65
+ from = Pointer.parse ins['move']
66
+ to = Pointer.parse ins['to']
67
+ from_key = from.pop
68
+ to_key = to.pop
69
+
70
+ src = Pointer.eval(from, doc)
71
+
72
+ if Array === src
73
+ obj = src.delete_at from_key.to_i
74
+ else
75
+ obj = src.delete from_key
76
+ end
77
+
78
+ dest = Pointer.eval(to, doc)
79
+
80
+ if Array === dest
81
+ dest.insert(to_key.to_i, obj)
82
+ else
83
+ dest[to_key] = obj
84
+ end
85
+ end
86
+
87
+ def test ins, doc
88
+ expected = Pointer.new(ins['test']).eval doc
89
+ raise Exception unless expected == ins['value']
90
+ end
91
+
92
+ def replace ins, doc
93
+ list = Pointer.parse ins['replace']
94
+ key = list.pop
95
+ Pointer.eval(list, doc)[key] = ins['value']
96
+ end
97
+
98
+ def remove ins, doc
99
+ list = Pointer.parse ins['remove']
100
+ key = list.pop
101
+ obj = Pointer.eval list, doc
102
+
103
+ if Array === obj
104
+ obj.delete_at key.to_i
105
+ else
106
+ obj.delete key
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,7 @@
1
+ require 'minitest/autorun'
2
+ require 'hana'
3
+
4
+ module Hana
5
+ class TestCase < MiniTest::Unit::TestCase
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+
3
+ class TestHana < Hana::TestCase
4
+ def test_split_many
5
+ pointer = Hana::Pointer.new '/foo/bar/baz'
6
+ assert_equal %w{ foo bar baz }, pointer.to_a
7
+ end
8
+
9
+ def test_root
10
+ pointer = Hana::Pointer.new '/'
11
+ assert_equal [], pointer.to_a
12
+ end
13
+
14
+ def test_escape
15
+ pointer = Hana::Pointer.new '/f^/oo/bar'
16
+ assert_equal ['f/oo', 'bar'], pointer.to_a
17
+
18
+ pointer = Hana::Pointer.new '/f^^oo/bar'
19
+ assert_equal ['f^oo', 'bar'], pointer.to_a
20
+ end
21
+
22
+ def test_eval_hash
23
+ pointer = Hana::Pointer.new '/foo'
24
+ assert_equal 'bar', pointer.eval('foo' => 'bar')
25
+
26
+ pointer = Hana::Pointer.new '/foo/bar'
27
+ assert_equal 'baz', pointer.eval('foo' => { 'bar' => 'baz' })
28
+ end
29
+
30
+ def test_eval_array
31
+ pointer = Hana::Pointer.new '/foo/1'
32
+ assert_equal 'baz', pointer.eval('foo' => ['bar', 'baz'])
33
+
34
+ pointer = Hana::Pointer.new '/foo/0/bar'
35
+ assert_equal 'omg', pointer.eval('foo' => [{'bar' => 'omg'}, 'baz'])
36
+ end
37
+
38
+ def test_eval_number_as_key
39
+ pointer = Hana::Pointer.new '/foo/1'
40
+ assert_equal 'baz', pointer.eval('foo' => { '1' => 'baz' })
41
+ end
42
+ end
@@ -0,0 +1,153 @@
1
+ require 'helper'
2
+
3
+ module Hana
4
+ class TestPatch < TestCase
5
+ def test_add_member
6
+ patch = Hana::Patch.new [
7
+ { 'add' => '/baz', 'value' => 'qux' }
8
+ ]
9
+
10
+ result = patch.apply('foo' => 'bar')
11
+ assert_equal({'baz' => 'qux', 'foo' => 'bar'}, result)
12
+ end
13
+
14
+ def test_add_array
15
+ patch = Hana::Patch.new [
16
+ { "add" => "/foo/1", "value" => "qux" }
17
+ ]
18
+
19
+ result = patch.apply({ "foo" => [ "bar", "baz" ] })
20
+
21
+ assert_equal({ "foo" => [ "bar", "qux", "baz" ] }, result)
22
+ end
23
+
24
+ def test_remove_object_member
25
+ patch = Hana::Patch.new [ { "remove" => "/baz" } ]
26
+
27
+ result = patch.apply({ 'baz' => 'qux', 'foo' => 'bar' })
28
+
29
+ assert_equal({ "foo" => 'bar' }, result)
30
+ end
31
+
32
+ def test_remove_array_element
33
+ patch = Hana::Patch.new [ { "remove" => "/foo/1" } ]
34
+ result = patch.apply({ "foo" => [ "bar", "qux", "baz" ] })
35
+ assert_equal({ "foo" => [ "bar", "baz" ] }, result)
36
+ end
37
+
38
+ def test_replace_value
39
+ patch = Hana::Patch.new [ { "replace" => "/baz", "value" => "boo" } ]
40
+ result = patch.apply({ "baz" => "qux", "foo" => "bar" })
41
+ assert_equal({ "baz" => "boo", "foo" => "bar" }, result)
42
+ end
43
+
44
+ def test_moving_a_value
45
+ doc = {
46
+ "foo" => {
47
+ "bar" => "baz",
48
+ "waldo" => "fred"
49
+ },
50
+ "qux" => {
51
+ "corge" => "grault"
52
+ }
53
+ }
54
+
55
+ patch = [ { "move" => "/foo/waldo", 'to' => "/qux/thud" } ]
56
+
57
+ expected = {
58
+ "foo" => {
59
+ "bar" => "baz"
60
+ },
61
+ "qux" => {
62
+ "corge" => "grault",
63
+ "thud" => "fred"
64
+ }
65
+ }
66
+
67
+ patch = Hana::Patch.new patch
68
+ result = patch.apply doc
69
+ assert_equal expected, result
70
+ end
71
+
72
+ def test_move_an_array_element
73
+ # An example target JSON document:
74
+ doc = {
75
+ "foo" => [ "all", "grass", "cows", "eat" ]
76
+ }
77
+
78
+ # A JSON Patch document:
79
+ patch = [
80
+ { "move" => "/foo/1", "to" => "/foo/3" }
81
+ ]
82
+
83
+ # The resulting JSON document:
84
+ expected = {
85
+ "foo" => [ "all", "cows", "eat", "grass" ]
86
+ }
87
+
88
+ patch = Hana::Patch.new patch
89
+ result = patch.apply doc
90
+ assert_equal expected, result
91
+ end
92
+
93
+ def test_testing_a_value_success
94
+ # An example target JSON document:
95
+ doc = {
96
+ "baz" => "qux",
97
+ "foo" => [ "a", 2, "c" ]
98
+ }
99
+
100
+ # A JSON Patch document that will result in successful evaluation:
101
+ patch = [
102
+ { "test" => "/baz", "value" => "qux" },
103
+ { "test" => "/foo/1", "value" => 2 },
104
+ { "add" => "/bar", "value" => 2 },
105
+ ]
106
+
107
+ expected = {
108
+ "baz" => "qux",
109
+ "foo" => [ "a", 2, "c" ],
110
+ 'bar' => 2
111
+ }
112
+
113
+ patch = Hana::Patch.new patch
114
+ result = patch.apply doc
115
+ assert_equal expected, result
116
+ end
117
+
118
+ def test_testing_a_value_error
119
+ # An example target JSON document:
120
+ doc = { "baz" => "qux" }
121
+
122
+ # A JSON Patch document that will result in an error condition:
123
+ patch = [
124
+ { "test" => "/baz", "value" => "bar" }
125
+ ]
126
+
127
+ patch = Hana::Patch.new patch
128
+
129
+ assert_raises(Hana::Patch::Exception) do
130
+ patch.apply doc
131
+ end
132
+ end
133
+
134
+ def test_add_nested_member_object
135
+ # An example target JSON document:
136
+ doc = { "foo" => "bar" }
137
+ # A JSON Patch document:
138
+ patch = [
139
+ { "add" => "/child", "value" => { "grandchild" => { } } }
140
+ ]
141
+
142
+ # The resulting JSON document:
143
+ expected = {
144
+ "foo" => "bar",
145
+ "child" => { "grandchild" => { } }
146
+ }
147
+
148
+ patch = Hana::Patch.new patch
149
+ result = patch.apply doc
150
+ assert_equal expected, result
151
+ end
152
+ end
153
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hana
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Aaron Patterson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rdoc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.10'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.10'
46
+ - !ruby/object:Gem::Dependency
47
+ name: hoe
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ description: Implementation of JSON Patch and JSON Pointer drafts.
63
+ email:
64
+ - aaron@tenderlovemaking.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files:
68
+ - CHANGELOG.rdoc
69
+ - Manifest.txt
70
+ - README.rdoc
71
+ files:
72
+ - .autotest
73
+ - CHANGELOG.rdoc
74
+ - Manifest.txt
75
+ - README.rdoc
76
+ - Rakefile
77
+ - lib/hana.rb
78
+ - test/helper.rb
79
+ - test/test_hana.rb
80
+ - test/test_patch.rb
81
+ - .gemtest
82
+ homepage: http://github.com/tenderlove/hana
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options:
86
+ - --main
87
+ - README.rdoc
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project: hana
104
+ rubygems_version: 1.8.24
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Implementation of JSON Patch and JSON Pointer drafts.
108
+ test_files:
109
+ - test/test_hana.rb
110
+ - test/test_patch.rb