hyperdoc 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -2
- data/{test/check_all.rb → checks/all.rb} +0 -0
- data/checks/core/check_array.rb +154 -0
- data/checks/core/check_document.rb +51 -0
- data/checks/core/check_hash.rb +50 -0
- data/{test → checks}/setup.rb +2 -0
- data/hyperdoc.gemspec +2 -2
- data/library/hyperdoc.rb +21 -0
- data/library/hyperdoc/{link.rb → agent.rb} +0 -0
- data/library/hyperdoc/array.rb +158 -0
- data/library/hyperdoc/box.rb +5 -0
- data/library/hyperdoc/{nop.rb → couch/database.rb} +0 -0
- data/library/hyperdoc/{schema.rb → couch/document.rb} +0 -0
- data/library/hyperdoc/{selector.rb → couch/view.rb} +0 -0
- data/library/hyperdoc/document.rb +68 -0
- data/library/hyperdoc/factory.rb +44 -0
- data/library/hyperdoc/hash.rb +69 -0
- data/library/hyperdoc/meta.rb +14 -0
- data/library/hyperdoc/type.rb +86 -0
- data/rakefile +6 -13
- metadata +22 -16
- data/library/hyperdoc/value.rb +0 -0
data/README.md
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
hyperdoc
|
2
2
|
========
|
3
3
|
|
4
|
-
hypertextual documents using plain old data structures
|
4
|
+
This library helps build hypertextual documents using plain old data structures which are designed for use in a REST friendly ecosystem. I'll be adding more here as I port the ideas over from the old repo. This is also part of a the upcoming open sourced General Assembly platform.
|
5
|
+
|
6
|
+
NOTE: This release is completely unoptimized! Work is being done to reduce the complexity of the behaviors Hyperdoc's exhibit before things are tuned.
|
5
7
|
|
6
8
|
License
|
7
9
|
-------
|
8
10
|
|
11
|
+
Thanks goes out to constantcontact/dash for allowing me to open source an *very early* prototype of these ideas. While it has evolved quite a bit since then, the core ideas have remained the same.
|
12
|
+
|
9
13
|
While it might be controversial, I'm going to give CC a shot for code. The [CC FAQ](http://wiki.creativecommons.org/Frequently_Asked_Questions#Can_I_apply_a_Creative_Commons_license_to_software.3F) does not usually recommend it, though their reasons seem based around problems that a similar MIT style license does not address either. GPL compatibility is not a concern here either since I'd rather not see this relicensed under a more viral contract.
|
10
14
|
|
11
|
-
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">hyperdoc</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://generalassemb.ly/" property="cc:attributionName" rel="cc:attributionURL">General Assembly</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.
|
15
|
+
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">hyperdoc</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://generalassemb.ly/" property="cc:attributionName" rel="cc:attributionURL">General Assembly</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.
|
File without changes
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require_relative '../setup'
|
2
|
+
|
3
|
+
class ArrayTest < TestCase
|
4
|
+
|
5
|
+
def test_compact
|
6
|
+
obj = Hyperdoc.new('arr' => [1, nil, 2, nil, 3])
|
7
|
+
arr = obj.arr
|
8
|
+
compacted = arr.compact
|
9
|
+
assert_equal [1,2,3], compacted.meta.value
|
10
|
+
assert_equal arr.meta.parent, compacted.meta.parent
|
11
|
+
assert_equal arr.meta.key, compacted.meta.key
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_compact_bang
|
15
|
+
arr = Hyperdoc.new([nil, 1, Hyperdoc.new(nil), 2, nil])
|
16
|
+
assert_equal arr, arr.compact!
|
17
|
+
assert_equal [1,2], arr.meta.value
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_each
|
21
|
+
arr = Hyperdoc.new([{'a' => 'b'}, [3, 4]])
|
22
|
+
arr.each do |obj|
|
23
|
+
assert Hyperdoc::Type === obj
|
24
|
+
end
|
25
|
+
arr = Hyperdoc.new([0, 1])
|
26
|
+
arr.each do |obj|
|
27
|
+
assert !(Hyperdoc::Type === obj)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_first
|
32
|
+
arr = Hyperdoc.new([{'a' => 42}])
|
33
|
+
assert_equal 42, arr.first.a
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_last
|
37
|
+
arr = Hyperdoc.new([0, 1, 2, 42])
|
38
|
+
assert_equal 42, arr.last
|
39
|
+
arr = Hyperdoc.new([0, 1, {}])
|
40
|
+
assert_equal 2, arr.last.meta.key
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_map
|
44
|
+
arr = Hyperdoc.new([1,2,3])
|
45
|
+
assert_equal [-1,-2,-3], arr.map {|x| -x}.meta.value
|
46
|
+
arr = Hyperdoc.new([{'a' => 'b'}])
|
47
|
+
assert_equal ['a=b'], arr.map {|x| "a=#{x.a}"}.meta.value
|
48
|
+
obj = Hyperdoc.new({'arr' => [1,2,3]})
|
49
|
+
mapped_array = obj.arr.map {|x| -x}
|
50
|
+
assert_equal obj, mapped_array.meta.parent.box
|
51
|
+
assert_equal 'arr', mapped_array.meta.key
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_map_bang
|
55
|
+
arr = Hyperdoc.new([{'a' => 1}, {'a' => 2}])
|
56
|
+
assert_equal arr, arr.map! {|x| x.a}
|
57
|
+
assert_equal [1,2], arr.meta.value
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_push
|
61
|
+
arr = Hyperdoc.new([1,2,3])
|
62
|
+
arr.push(4)
|
63
|
+
arr << 5 << 6
|
64
|
+
assert_equal [1,2,3,4,5,6], arr.meta.value
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_reduce
|
68
|
+
arr = Hyperdoc.new([0,1,2])
|
69
|
+
assert_equal 3, arr.reduce(:+)
|
70
|
+
assert_equal 3, arr.inject(:+)
|
71
|
+
assert_equal 0, arr.reduce(-3, :+)
|
72
|
+
assert_equal 3, arr.reduce {|x,y| x + y}
|
73
|
+
assert_equal 0, arr.reduce(-3) {|x,y| x + y}
|
74
|
+
assert_raise(ArgumentError) {arr.inject}
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_reject
|
78
|
+
obj = Hyperdoc.new('arr' => (1..10).to_a)
|
79
|
+
arr = obj.arr
|
80
|
+
odds = arr.reject {|x| x % 2 == 0}
|
81
|
+
assert_equal [1,3,5,7,9], odds.meta.value
|
82
|
+
assert_equal arr.meta.parent, odds.meta.parent
|
83
|
+
assert_equal arr.meta.key, odds.meta.key
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_reject_bang
|
87
|
+
arr = Hyperdoc.new([1,2,3,4])
|
88
|
+
assert_equal arr, arr.reject! {|x| x % 2 == 0}
|
89
|
+
assert_equal [1,3], arr.meta.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_select
|
93
|
+
obj = Hyperdoc.new('arr' => (1..10).to_a)
|
94
|
+
arr = obj.arr
|
95
|
+
evens = arr.select {|x| x % 2 == 0}
|
96
|
+
assert_equal [2,4,6,8,10], evens.meta.value
|
97
|
+
assert_equal arr.meta.parent, evens.meta.parent
|
98
|
+
assert_equal arr.meta.key, evens.meta.key
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_select_bang
|
102
|
+
arr = Hyperdoc.new([1,2,3,4])
|
103
|
+
assert_equal arr, arr.select! {|x| x % 2 == 0}
|
104
|
+
assert_equal [2,4], arr.meta.value
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_sort
|
108
|
+
obj = Hyperdoc.new('arr' => [1,3,2,5,4])
|
109
|
+
arr = obj.arr
|
110
|
+
sorted = arr.sort
|
111
|
+
assert_equal [1,2,3,4,5], sorted.meta.value
|
112
|
+
assert_equal arr.meta.parent, sorted.meta.parent
|
113
|
+
assert_equal arr.meta.key, sorted.meta.key
|
114
|
+
sorted = arr.sort {|x, y| y <=> x}
|
115
|
+
assert_equal [5,4,3,2,1], sorted.meta.value
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_sort_bang
|
119
|
+
arr = Hyperdoc.new([3,4,1,2])
|
120
|
+
assert_equal arr, arr.sort!
|
121
|
+
assert_equal [1,2,3,4], arr.meta.value
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_sort_by
|
125
|
+
obj = Hyperdoc.new('arr' => [1,2,3,4])
|
126
|
+
arr = obj.arr
|
127
|
+
sorted = arr.sort_by {|x| -x}
|
128
|
+
assert_equal [4,3,2,1], sorted.meta.value
|
129
|
+
assert_equal arr.meta.parent, sorted.meta.parent
|
130
|
+
assert_equal arr.meta.key, sorted.meta.key
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_sort_by_bang
|
134
|
+
arr = Hyperdoc.new([1,2,3,4])
|
135
|
+
assert_equal arr, arr.sort_by! {|x| -x}
|
136
|
+
assert_equal [4,3,2,1], arr.meta.value
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_uniq
|
140
|
+
obj = Hyperdoc.new('arr' => [1,1,2,2,3,3])
|
141
|
+
arr = obj.arr
|
142
|
+
uniq = arr.uniq
|
143
|
+
assert_equal [1,2,3], uniq.meta.value
|
144
|
+
assert_equal arr.meta.parent, uniq.meta.parent
|
145
|
+
assert_equal arr.meta.key, uniq.meta.key
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_uniq_bang
|
149
|
+
arr = Hyperdoc.new([1, 1, 2, 1, 1])
|
150
|
+
assert_equal arr, arr.uniq!
|
151
|
+
assert_equal [1,2], arr.meta.value
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative '../setup'
|
2
|
+
|
3
|
+
class BoxTest < TestCase
|
4
|
+
|
5
|
+
def test_match_delegation
|
6
|
+
assert Hyperdoc.new('abc') =~ /abc/
|
7
|
+
assert Hyperdoc.new('abc') !~ /xyz/
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_to_s_delegation
|
11
|
+
assert_equal '?'.to_s, Hyperdoc.new('?').to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_comparison
|
15
|
+
assert Hyperdoc.new(a: 42) == {a: 42}
|
16
|
+
assert Hyperdoc.new(42) > 41
|
17
|
+
assert_equal 0, Hyperdoc.new(42) <=> Hyperdoc.new(42)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_value_extraction
|
21
|
+
assert_equal [42], Hyperdoc.new([42]).meta.value
|
22
|
+
assert_equal 42, Hyperdoc.new(42)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class CustomBaseTest < TestCase
|
28
|
+
|
29
|
+
class X
|
30
|
+
include Hyperdoc::Type
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_hash_construction
|
34
|
+
obj = X.new(a: 42)
|
35
|
+
assert Hyperdoc::Type === obj
|
36
|
+
assert_equal 42, obj.a
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_array_construciton
|
40
|
+
obj = X.new([42])
|
41
|
+
assert Hyperdoc::Type === obj
|
42
|
+
assert_equal 42, obj.first
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_value_construction
|
46
|
+
assert_equal 42, X.new(42)
|
47
|
+
assert_equal nil, X.new(nil)
|
48
|
+
assert_equal "42", X.new("42")
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative '../setup'
|
2
|
+
|
3
|
+
class AccessBehaviorTest < TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@hash = Hyperdoc.new('a' => 42, b: 43)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_accessor_synthesis
|
10
|
+
assert @hash.a == 42
|
11
|
+
assert @hash.respond_to?(:a)
|
12
|
+
assert @hash.b == 43
|
13
|
+
assert @hash.respond_to?(:b)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_missing_accessor_synthesis
|
17
|
+
assert @hash.blahblah.nil?
|
18
|
+
assert @hash.respond_to?(:blahblah)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_query_synthesis
|
22
|
+
assert @hash.a?
|
23
|
+
assert @hash.respond_to?(:a?)
|
24
|
+
assert @hash.b?
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_missing_query_synthesis
|
28
|
+
assert !@hash.blahblah?
|
29
|
+
assert @hash.respond_to?(:blahblah?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_assignment_synthesis
|
33
|
+
@hash.answer = 42
|
34
|
+
assert_equal 42, @hash.answer
|
35
|
+
assert @hash.respond_to?(:answer=)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class BoxingBehaviorTest < TestCase
|
41
|
+
|
42
|
+
def test_assignment_should_unbox
|
43
|
+
hash = {'b' => 42}
|
44
|
+
object = Hyperdoc.new({})
|
45
|
+
object.a = Hyperdoc.new(hash)
|
46
|
+
object.a = object.a # assign a boxed value which should become unboxed
|
47
|
+
assert_equal hash.class, object.a.meta.value.class
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/{test → checks}/setup.rb
RENAMED
data/hyperdoc.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = 'hyperdoc'
|
3
|
-
gem.version = '0.1.
|
3
|
+
gem.version = '0.1.1'
|
4
4
|
gem.date = '2012-05-18'
|
5
5
|
gem.summary = "hypertextual documents using plain old data structures"
|
6
|
-
gem.authors = ['strmpnk (Brian Mitchell)', 'John Paul Ashenfelter']
|
6
|
+
gem.authors = ['strmpnk (Brian Mitchell)', 'John Paul Ashenfelter', 'Robert Gleeson']
|
7
7
|
gem.email = 'brian@generalassemb.ly'
|
8
8
|
gem.homepage = 'https://github.com/generalassembly/hyperdoc'
|
9
9
|
gem.require_paths = %w[library]
|
data/library/hyperdoc.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hyperdoc
|
2
|
+
|
3
|
+
# value model
|
4
|
+
autoload :Array, 'hyperdoc/array'
|
5
|
+
autoload :Box, 'hyperdoc/box'
|
6
|
+
autoload :Factory, 'hyperdoc/factory'
|
7
|
+
autoload :Hash, 'hyperdoc/hash'
|
8
|
+
autoload :Type, 'hyperdoc/type'
|
9
|
+
|
10
|
+
# document model
|
11
|
+
autoload :Agent, 'hyperdoc/agent'
|
12
|
+
autoload :Meta, 'hyperdoc/meta'
|
13
|
+
|
14
|
+
# drivers
|
15
|
+
autoload :Couch, 'hyperdoc/couch'
|
16
|
+
|
17
|
+
def self.new(value)
|
18
|
+
Hyperdoc::Box.new(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
File without changes
|
data/library/hyperdoc/array.rb
CHANGED
@@ -0,0 +1,158 @@
|
|
1
|
+
module Hyperdoc::Array
|
2
|
+
|
3
|
+
def [](key)
|
4
|
+
box(key, meta.value[key])
|
5
|
+
end
|
6
|
+
|
7
|
+
def []=(key, value)
|
8
|
+
value = unbox(value)
|
9
|
+
meta.value[key] = unbox(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def compact
|
13
|
+
rebox meta.value.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
def compact!
|
17
|
+
meta.value.reject! {|x| x.nil?}
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
meta.value.each_with_index do |value, i|
|
23
|
+
yield box(i, value)
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def first
|
29
|
+
box(0, meta.value.first)
|
30
|
+
end
|
31
|
+
|
32
|
+
def last
|
33
|
+
box(meta.value.size - 1, meta.value.last)
|
34
|
+
end
|
35
|
+
|
36
|
+
def map
|
37
|
+
arr = []
|
38
|
+
each do |box|
|
39
|
+
arr << unbox(yield(box))
|
40
|
+
end
|
41
|
+
rebox(arr)
|
42
|
+
end
|
43
|
+
|
44
|
+
def map!
|
45
|
+
meta.value.map!.with_index do |x, i|
|
46
|
+
unbox(yield box(i, x))
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def push(item)
|
52
|
+
meta.value.push unbox(item)
|
53
|
+
self
|
54
|
+
end
|
55
|
+
alias_method :<<, :push
|
56
|
+
|
57
|
+
def reduce(*args, &block)
|
58
|
+
case args.size
|
59
|
+
when 0
|
60
|
+
initial = false
|
61
|
+
block_given? or raise ArgumentError,
|
62
|
+
"no block given"
|
63
|
+
when 1
|
64
|
+
initial = block_given?
|
65
|
+
block = args[0].to_proc unless initial
|
66
|
+
when 2
|
67
|
+
initial = true
|
68
|
+
block = args[1].to_proc
|
69
|
+
else
|
70
|
+
raise ArgumentError,
|
71
|
+
"Too many arguments: #{args.size} for 0 to 2"
|
72
|
+
end
|
73
|
+
memo = initial ? args[0] : nil
|
74
|
+
meta.value.each_with_index do |value, i|
|
75
|
+
# set an initial memo on first loop unless we were passed one
|
76
|
+
memo = (!initial && i == 0) ?
|
77
|
+
box(i, value) :
|
78
|
+
block.call(memo, box(i, value))
|
79
|
+
end
|
80
|
+
memo
|
81
|
+
end
|
82
|
+
alias inject reduce
|
83
|
+
|
84
|
+
def reject
|
85
|
+
selected = meta.value.reject.with_index do |x, i|
|
86
|
+
yield box(i, x)
|
87
|
+
end
|
88
|
+
rebox(selected)
|
89
|
+
end
|
90
|
+
|
91
|
+
def reject!
|
92
|
+
meta.value.reject!.with_index do |x, i|
|
93
|
+
yield box(i, x)
|
94
|
+
end
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def size
|
99
|
+
meta.value.size
|
100
|
+
end
|
101
|
+
|
102
|
+
def select
|
103
|
+
selected = meta.value.select.with_index do |x, i|
|
104
|
+
yield box(i, x)
|
105
|
+
end
|
106
|
+
rebox(selected)
|
107
|
+
end
|
108
|
+
|
109
|
+
def select!
|
110
|
+
meta.value.select!.with_index do |x, i|
|
111
|
+
yield box(i, x)
|
112
|
+
end
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
def sort
|
117
|
+
if block_given?
|
118
|
+
# box everything once and then sort the boxes
|
119
|
+
sorted = meta.value.map.with_index do |v, i|
|
120
|
+
box(i, v)
|
121
|
+
end.sort do |a,b|
|
122
|
+
yield a,b
|
123
|
+
end
|
124
|
+
else
|
125
|
+
sorted = meta.value.sort
|
126
|
+
end
|
127
|
+
rebox(sorted)
|
128
|
+
end
|
129
|
+
|
130
|
+
def sort!
|
131
|
+
meta.value.sort!
|
132
|
+
self
|
133
|
+
end
|
134
|
+
|
135
|
+
def sort_by
|
136
|
+
sorted = meta.value.sort_by.with_index do |x, i|
|
137
|
+
yield box(i, x)
|
138
|
+
end
|
139
|
+
rebox(sorted)
|
140
|
+
end
|
141
|
+
|
142
|
+
def sort_by!
|
143
|
+
meta.value.sort_by!.with_index do |x, i|
|
144
|
+
yield box(i, x)
|
145
|
+
end
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
def uniq
|
150
|
+
rebox meta.value.uniq
|
151
|
+
end
|
152
|
+
|
153
|
+
def uniq!
|
154
|
+
meta.value.uniq!
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
data/library/hyperdoc/box.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Hyperdoc::Document
|
2
|
+
include Comparable
|
3
|
+
include Hyperdoc::Generator
|
4
|
+
|
5
|
+
def self.generate(types)
|
6
|
+
return self if types.empty?
|
7
|
+
@cache[types]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.inherited(subclass)
|
11
|
+
# inherit schema
|
12
|
+
# setup caches for subclass
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.inspect
|
16
|
+
return name if name
|
17
|
+
base = self
|
18
|
+
base = base.superclass until base.name
|
19
|
+
behaviors = ancestors[1..-1] - base.ancestors
|
20
|
+
behaviors.empty? ?
|
21
|
+
"#{base}":
|
22
|
+
"#{base}(#{behaviors.join(',')})"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.on(selectors)
|
26
|
+
selectors.each do |code, type|
|
27
|
+
schema.register(code, type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
public
|
32
|
+
|
33
|
+
undef_method :=~
|
34
|
+
undef_method :nil?
|
35
|
+
undef_method :to_s
|
36
|
+
|
37
|
+
def <=>(other)
|
38
|
+
value <=> unbox(other)
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
key ?
|
43
|
+
"#<#{self.class.inxpect}##{full_path.join('.')}: #{value.inspect}>":
|
44
|
+
"#<#{self.class.inspect}: #{value.inspect}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_accessor :key, :parent, :schema
|
50
|
+
attr_reader :value
|
51
|
+
|
52
|
+
def box()
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(value)
|
57
|
+
@value = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def rebox()
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def unbox()
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hyperdoc::Factory
|
2
|
+
|
3
|
+
def inspect
|
4
|
+
return name if name
|
5
|
+
base = self
|
6
|
+
base = base.superclass until base.name
|
7
|
+
behaviors = ancestors[1..-1] - base.ancestors
|
8
|
+
behaviors.empty? ?
|
9
|
+
"#{base}":
|
10
|
+
"#{base}(#{behaviors.join(',')})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def new(value)
|
14
|
+
# NOTE: this is unoptimized. Specialization will get more complex
|
15
|
+
# once selectors are added back. We'll delay optimizing this till
|
16
|
+
# after that feature is complete.
|
17
|
+
sub = Class.new(self)
|
18
|
+
type = value_type(value)
|
19
|
+
return value if type.nil?
|
20
|
+
sub.class_eval do
|
21
|
+
include type
|
22
|
+
undef_method :=~
|
23
|
+
end
|
24
|
+
sub.allocate.tap {|inst|
|
25
|
+
inst.meta.value = inst.send(:unbox, value)
|
26
|
+
inst.meta.box = inst
|
27
|
+
yield inst if block_given?
|
28
|
+
inst.send(:initialize)}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def value_type(value)
|
34
|
+
case value
|
35
|
+
when ::Hash
|
36
|
+
Hyperdoc::Hash
|
37
|
+
when ::Array
|
38
|
+
Hyperdoc::Array
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/library/hyperdoc/hash.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Hyperdoc::Hash
|
2
|
+
|
3
|
+
def [](key)
|
4
|
+
if meta.value.has_key?(key)
|
5
|
+
# Check natural type
|
6
|
+
box(key, meta.value[key])
|
7
|
+
elsif String === key
|
8
|
+
# Check symbol form
|
9
|
+
sym_key = key.to_sym
|
10
|
+
box(key, meta.value[sym_key])
|
11
|
+
else
|
12
|
+
# Check string form
|
13
|
+
str_key = key.to_s
|
14
|
+
box(key, meta.value[str_key])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
str_key = key.to_s
|
20
|
+
sym_key = key.to_sym
|
21
|
+
if meta.value.has_key?(sym_key)
|
22
|
+
if meta.value.has_key?(str_key)
|
23
|
+
meta.value[key] = unbox(value)
|
24
|
+
else
|
25
|
+
meta.value[sym_key] = unbox(value)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
meta.value[str_key] = unbox(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_key?(key)
|
33
|
+
return true if meta.value.has_key?(key)
|
34
|
+
if String === key
|
35
|
+
meta.value.has_key?(key.to_sym)
|
36
|
+
else
|
37
|
+
meta.value.has_key?(key.to_s)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def access_call(symbol, key)
|
44
|
+
cached_call(symbol) {box(key, self[key])}
|
45
|
+
end
|
46
|
+
|
47
|
+
def assign_call(symbol, key)
|
48
|
+
cached_call(symbol) {|v| self[key] = v}
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(symbol, *args, &blk)
|
52
|
+
case symbol
|
53
|
+
when /(.*)=$/
|
54
|
+
key = $1
|
55
|
+
assign_call(symbol, key).call(*args, &blk)
|
56
|
+
when /(.*)\?$/
|
57
|
+
key = $1
|
58
|
+
query_call(symbol, key).call(*args, &blk)
|
59
|
+
else
|
60
|
+
key = symbol.to_s
|
61
|
+
access_call(symbol, key).call(*args, &blk)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def query_call(symbol, key)
|
66
|
+
cached_call(symbol) {has_key?(key) && !!box(key, self[key])}
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/library/hyperdoc/meta.rb
CHANGED
@@ -0,0 +1,86 @@
|
|
1
|
+
=begin
|
2
|
+
# Overview
|
3
|
+
|
4
|
+
Hyperdoc::Type is included in classes which intend on constructing
|
5
|
+
document instances. It transforms the new() interface into a sort of
|
6
|
+
factory which generates a class with the appropriate sort of
|
7
|
+
combination of modules based on what it's wrapping. This is don't
|
8
|
+
rather than subclassing for precisely that sort of late-bound behavior.
|
9
|
+
|
10
|
+
## Why Hyperdoc::Type and not some other name?
|
11
|
+
|
12
|
+
While it was originally going to be implemented directly on the
|
13
|
+
Hyperdoc module itself, this was a problem for constant lookup since
|
14
|
+
Hyperdoc implements many common constant names like Array and Hash, which
|
15
|
+
leads to a sort of ambiguity. This protects us somewhat by not creating
|
16
|
+
unintended ambiguity for those that include Hyperdoc::Type.
|
17
|
+
|
18
|
+
Type itself was chosen because it helps describe what it is. It's a sort of
|
19
|
+
Hyperdoc and will read well: Hyperdoc::Type === … Though we'll probably add
|
20
|
+
a simple implementation for Hyperdoc.=== which will delegate to
|
21
|
+
Hyperdoc::Type
|
22
|
+
|
23
|
+
=end
|
24
|
+
|
25
|
+
module Hyperdoc::Type
|
26
|
+
include Comparable
|
27
|
+
|
28
|
+
def self.append_features(_class)
|
29
|
+
Class === _class or raise ArgumentError,
|
30
|
+
"Hyperdoc::Type can only be included directly into a class, not a module"
|
31
|
+
super
|
32
|
+
_class.extend Hyperdoc::Factory
|
33
|
+
end
|
34
|
+
|
35
|
+
public
|
36
|
+
|
37
|
+
def <=>(other)
|
38
|
+
meta.value <=> unbox(other)
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
meta.path ?
|
43
|
+
"#<#{self.class.inspect}##{meta.path}: #{meta.inspect}>":
|
44
|
+
"#<#{self.class.inspect}: #{meta.inspect}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
def meta
|
48
|
+
@hyperdoc_meta ||= Hyperdoc::Meta.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s(*a)
|
52
|
+
meta.value.to_s(*a)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def box(key, value)
|
58
|
+
Hyperdoc::Box.new(value) { |box|
|
59
|
+
box.meta.key = key
|
60
|
+
box.meta.parent = meta
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def rebox(value)
|
65
|
+
self.dup.tap { |box|
|
66
|
+
box.meta.value = value
|
67
|
+
box.meta.box = box
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def unbox(object)
|
72
|
+
Hyperdoc::Type === object ? unbox(object.meta.value) : object
|
73
|
+
end
|
74
|
+
|
75
|
+
def cached_call(symbol, &proc)
|
76
|
+
unless self.respond_to?(symbol, true)
|
77
|
+
self.class.class_eval {define_method(symbol, &proc)}
|
78
|
+
end
|
79
|
+
proc
|
80
|
+
end
|
81
|
+
|
82
|
+
def scrub_call(symbol)
|
83
|
+
self.class.class_eval {undef_method symbol}
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/rakefile
CHANGED
@@ -1,23 +1,16 @@
|
|
1
1
|
task default: [:test, :build]
|
2
2
|
|
3
|
-
task compile: 'lib/dash/selector.kpeg.rb'
|
4
|
-
|
5
3
|
desc "check tests"
|
6
|
-
task :test
|
7
|
-
ruby ENV['ONLY'] || '
|
4
|
+
task :test do
|
5
|
+
ruby ENV['ONLY'] || 'checks/all.rb'
|
8
6
|
end
|
9
7
|
|
10
8
|
desc "build dash gem"
|
11
|
-
task :build
|
12
|
-
sh 'gem build
|
9
|
+
task :build do
|
10
|
+
sh 'gem build hyperdoc.gemspec'
|
13
11
|
end
|
14
12
|
|
15
13
|
desc "clean build artifacts"
|
16
14
|
task :clean do
|
17
|
-
rm '
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
file 'lib/dash/selector.kpeg.rb' => 'lib/dash/selector.kpeg' do
|
22
|
-
sh "kpeg --stand-alone --force --output lib/dash/selector.kpeg.rb lib/dash/selector.kpeg"
|
23
|
-
end
|
15
|
+
rm 'hyperdoc-*.gem'
|
16
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyperdoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- strmpnk (Brian Mitchell)
|
9
9
|
- John Paul Ashenfelter
|
10
|
+
- Robert Gleeson
|
10
11
|
autorequire:
|
11
12
|
bindir: bin
|
12
13
|
cert_chain: []
|
@@ -14,7 +15,7 @@ date: 2012-05-18 00:00:00.000000000 Z
|
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: celluloid
|
17
|
-
requirement: &
|
18
|
+
requirement: &70245880712420 !ruby/object:Gem::Requirement
|
18
19
|
none: false
|
19
20
|
requirements:
|
20
21
|
- - ! '>='
|
@@ -22,10 +23,10 @@ dependencies:
|
|
22
23
|
version: '0'
|
23
24
|
type: :runtime
|
24
25
|
prerelease: false
|
25
|
-
version_requirements: *
|
26
|
+
version_requirements: *70245880712420
|
26
27
|
- !ruby/object:Gem::Dependency
|
27
28
|
name: excon
|
28
|
-
requirement: &
|
29
|
+
requirement: &70245880711820 !ruby/object:Gem::Requirement
|
29
30
|
none: false
|
30
31
|
requirements:
|
31
32
|
- - ! '>='
|
@@ -33,10 +34,10 @@ dependencies:
|
|
33
34
|
version: '0'
|
34
35
|
type: :runtime
|
35
36
|
prerelease: false
|
36
|
-
version_requirements: *
|
37
|
+
version_requirements: *70245880711820
|
37
38
|
- !ruby/object:Gem::Dependency
|
38
39
|
name: yajl-ruby
|
39
|
-
requirement: &
|
40
|
+
requirement: &70245880711180 !ruby/object:Gem::Requirement
|
40
41
|
none: false
|
41
42
|
requirements:
|
42
43
|
- - ! '>='
|
@@ -44,10 +45,10 @@ dependencies:
|
|
44
45
|
version: '0'
|
45
46
|
type: :runtime
|
46
47
|
prerelease: false
|
47
|
-
version_requirements: *
|
48
|
+
version_requirements: *70245880711180
|
48
49
|
- !ruby/object:Gem::Dependency
|
49
50
|
name: kpeg
|
50
|
-
requirement: &
|
51
|
+
requirement: &70245880710740 !ruby/object:Gem::Requirement
|
51
52
|
none: false
|
52
53
|
requirements:
|
53
54
|
- - ! '>='
|
@@ -55,29 +56,34 @@ dependencies:
|
|
55
56
|
version: '0'
|
56
57
|
type: :development
|
57
58
|
prerelease: false
|
58
|
-
version_requirements: *
|
59
|
+
version_requirements: *70245880710740
|
59
60
|
description:
|
60
61
|
email: brian@generalassemb.ly
|
61
62
|
executables: []
|
62
63
|
extensions: []
|
63
64
|
extra_rdoc_files: []
|
64
65
|
files:
|
66
|
+
- checks/all.rb
|
67
|
+
- checks/core/check_array.rb
|
68
|
+
- checks/core/check_document.rb
|
69
|
+
- checks/core/check_hash.rb
|
70
|
+
- checks/setup.rb
|
65
71
|
- hyperdoc.gemspec
|
72
|
+
- library/hyperdoc/agent.rb
|
66
73
|
- library/hyperdoc/array.rb
|
67
74
|
- library/hyperdoc/box.rb
|
75
|
+
- library/hyperdoc/couch/database.rb
|
76
|
+
- library/hyperdoc/couch/document.rb
|
77
|
+
- library/hyperdoc/couch/view.rb
|
68
78
|
- library/hyperdoc/couch.rb
|
79
|
+
- library/hyperdoc/document.rb
|
80
|
+
- library/hyperdoc/factory.rb
|
69
81
|
- library/hyperdoc/hash.rb
|
70
|
-
- library/hyperdoc/link.rb
|
71
82
|
- library/hyperdoc/meta.rb
|
72
|
-
- library/hyperdoc/
|
73
|
-
- library/hyperdoc/schema.rb
|
74
|
-
- library/hyperdoc/selector.rb
|
75
|
-
- library/hyperdoc/value.rb
|
83
|
+
- library/hyperdoc/type.rb
|
76
84
|
- library/hyperdoc.rb
|
77
85
|
- rakefile
|
78
86
|
- README.md
|
79
|
-
- test/check_all.rb
|
80
|
-
- test/setup.rb
|
81
87
|
homepage: https://github.com/generalassembly/hyperdoc
|
82
88
|
licenses: []
|
83
89
|
post_install_message:
|
data/library/hyperdoc/value.rb
DELETED
File without changes
|