hana 1.0.0

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.
@@ -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