-
Notifications
You must be signed in to change notification settings - Fork 28
/
hana.rb
106 lines (84 loc) · 1.98 KB
/
hana.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
module Hana
VERSION = '1.0.0'
class Pointer
include Enumerable
def initialize path
@path = Pointer.parse path
end
def each
@path.each { |x| yield x }
end
def to_a; @path.dup; end
def eval object
Pointer.eval @path, object
end
def self.eval list, object
list.inject(object) { |o, part| o[(Array === o ? part.to_i : part)] }
end
def self.parse path
path.sub(/^\//, '').split(/(?<!\^)\//).map { |part|
part.gsub(/\^([\/^])/, '\1')
}
end
end
class Patch
class Exception < StandardError
end
def initialize is
@is = is
end
def apply doc
@is.each_with_object(doc) { |ins, doc|
send ins.keys.sort.first, ins, doc
}
end
private
def add ins, doc
list = Pointer.parse ins['add']
key = list.pop
obj = Pointer.eval list, doc
if Array === obj
obj.insert key.to_i, ins['value']
else
obj[key] = ins['value']
end
end
def move ins, doc
from = Pointer.parse ins['move']
to = Pointer.parse ins['to']
from_key = from.pop
to_key = to.pop
src = Pointer.eval(from, doc)
if Array === src
obj = src.delete_at from_key.to_i
else
obj = src.delete from_key
end
dest = Pointer.eval(to, doc)
if Array === dest
dest.insert to_key.to_i, obj
else
dest[to_key] = obj
end
end
def test ins, doc
expected = Pointer.new(ins['test']).eval doc
raise Exception unless expected == ins['value']
end
def replace ins, doc
list = Pointer.parse ins['replace']
key = list.pop
Pointer.eval(list, doc)[key] = ins['value']
end
def remove ins, doc
list = Pointer.parse ins['remove']
key = list.pop
obj = Pointer.eval list, doc
if Array === obj
obj.delete_at key.to_i
else
obj.delete key
end
end
end
end