rufus-cloche 0.1.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.
- data/CHANGELOG.txt +6 -0
- data/CREDITS.txt +4 -0
- data/README.rdoc +91 -0
- data/test/test.rb +152 -0
- metadata +62 -0
data/CHANGELOG.txt
ADDED
data/CREDITS.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
= rufus-cloche
|
3
|
+
|
4
|
+
A very stupid local JSON store.
|
5
|
+
|
6
|
+
It's built on top of yajl-ruby and File.lock. Defaults to 'json' (or 'json_pure') if yajl-ruby is not present (it's probably just a "gem install yajl-ruby" away.
|
7
|
+
|
8
|
+
Strives to be process-safe and thread-safe.
|
9
|
+
|
10
|
+
The philosophy here is : every document has a revision number. You have to provide the latest revision number in order to update sucessfully a document (else the 'put' will fail and you'll return the latest version of the document (as a new starting point)).
|
11
|
+
|
12
|
+
A cloche assumes your documents have a 'type' and it stores documents in separate dirs, one per type.
|
13
|
+
|
14
|
+
|
15
|
+
== usage
|
16
|
+
|
17
|
+
Basic operations :
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'rufus-cloche' # gem install rufus-cloche
|
21
|
+
|
22
|
+
c = Rufus::Cloche.new(:dir => 'my_cloche')
|
23
|
+
|
24
|
+
doc = { '_id' => 'invitation05', 'type' => 'letter', 'to' => 'The Duke' }
|
25
|
+
# documents are Hash instances, with an '_id' and a 'type' key.
|
26
|
+
|
27
|
+
d = c.put(doc)
|
28
|
+
|
29
|
+
p d
|
30
|
+
# => nil
|
31
|
+
# #put returned nil, the 'insertion' was successful
|
32
|
+
|
33
|
+
p doc['_rev']
|
34
|
+
# => 0
|
35
|
+
# Cloche added the '_rev' key and set its value to 0.
|
36
|
+
|
37
|
+
# ... meanwhile, someone in another process upated the document
|
38
|
+
# we're trying to put an updated version ...
|
39
|
+
|
40
|
+
doc['body'] = "Dear Monty, it's been a while since El Alamein..."
|
41
|
+
|
42
|
+
d = c.put(doc)
|
43
|
+
|
44
|
+
p d
|
45
|
+
# => { "_id" => "invitation05", "type"=>"letter", "to" => "The Count", "_rev" => 1 }
|
46
|
+
#
|
47
|
+
# the update failed, the method returned the current version of the document
|
48
|
+
|
49
|
+
It's OK to get a document, but it's sometimes better to get many of them :
|
50
|
+
|
51
|
+
docs = get_many('letter')
|
52
|
+
# returns an array containing *all* the documents with the type 'letter'
|
53
|
+
|
54
|
+
docs = get_many('letter', /^invitation/)
|
55
|
+
# returns all the documents whose _id begins with "invitation"
|
56
|
+
|
57
|
+
docs = get_many('letter', /\/2009\//)
|
58
|
+
# returns all the 2009 letters (assuming their _id contains the string "/2009/"
|
59
|
+
|
60
|
+
|
61
|
+
== mailing list
|
62
|
+
|
63
|
+
On the rufus-ruby list :
|
64
|
+
|
65
|
+
http://groups.google.com/group/rufus-ruby
|
66
|
+
|
67
|
+
|
68
|
+
== issue tracker
|
69
|
+
|
70
|
+
http://github.com/jmettraux/rufus-cloche/issues
|
71
|
+
|
72
|
+
|
73
|
+
== irc
|
74
|
+
|
75
|
+
irc.freenode.net #ruote
|
76
|
+
|
77
|
+
|
78
|
+
== the rest of Rufus
|
79
|
+
|
80
|
+
http://rufus.rubyforge.org
|
81
|
+
|
82
|
+
|
83
|
+
== authors
|
84
|
+
|
85
|
+
* John Mettraux, jmettraux@gmail.com, http://jmettraux.wordpress.com
|
86
|
+
|
87
|
+
|
88
|
+
== license
|
89
|
+
|
90
|
+
MIT
|
91
|
+
|
data/test/test.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# testing cloche
|
4
|
+
#
|
5
|
+
# Fri Nov 13 09:09:58 JST 2009
|
6
|
+
#
|
7
|
+
|
8
|
+
ROOT = File.join(File.dirname(__FILE__), '..')
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
require File.join(ROOT, %w[ lib rufus cloche.rb ])
|
12
|
+
|
13
|
+
class ClocheTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@c_dir = File.join(ROOT, 'tcloche')
|
17
|
+
FileUtils.rm_rf(@c_dir) rescue nil
|
18
|
+
@c = Rufus::Cloche.new(:dir => @c_dir)
|
19
|
+
end
|
20
|
+
#def teardown
|
21
|
+
#end
|
22
|
+
|
23
|
+
def test_put
|
24
|
+
|
25
|
+
r = @c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
26
|
+
|
27
|
+
assert_nil r
|
28
|
+
|
29
|
+
h = fetch('person', 'john')
|
30
|
+
assert_equal 0, h['_rev']
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_depth
|
34
|
+
|
35
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
36
|
+
|
37
|
+
assert_equal(
|
38
|
+
"test/../tcloche/person/hn/john.json",
|
39
|
+
Dir[File.join(@c_dir, %w[ ** *.json ])].first)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_small_id
|
43
|
+
|
44
|
+
r = @c.put({ '_id' => '0', 'type' => 'person', 'eyes' => 'green' })
|
45
|
+
|
46
|
+
assert_nil r
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_put_insufficient_doc
|
50
|
+
|
51
|
+
assert_raise ArgumentError do
|
52
|
+
@c.put({ '_id' => 'john', 'eyes' => 'shut' })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_put_fail
|
57
|
+
|
58
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
59
|
+
r = @c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'brown' })
|
60
|
+
|
61
|
+
assert_equal 0, r['_rev']
|
62
|
+
|
63
|
+
h = fetch('person', 'john')
|
64
|
+
assert_equal 'green', h['eyes']
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_re_put
|
68
|
+
|
69
|
+
@c.put(
|
70
|
+
{ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
71
|
+
|
72
|
+
r = @c.put(
|
73
|
+
{ '_id' => 'john', 'type' => 'person', 'eyes' => 'blue', '_rev' => 0 })
|
74
|
+
|
75
|
+
assert_nil r
|
76
|
+
|
77
|
+
h = fetch('person', 'john')
|
78
|
+
assert_equal 1, h['_rev']
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_delete_missing
|
82
|
+
|
83
|
+
r = @c.delete({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
84
|
+
|
85
|
+
assert_not_nil r
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_delete
|
89
|
+
|
90
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
91
|
+
|
92
|
+
r = @c.delete({ '_id' => 'john', 'type' => 'person', '_rev' => 0 })
|
93
|
+
|
94
|
+
assert_nil r
|
95
|
+
assert_equal false, File.exist?(File.join(ROOT, 'person', 'john'))
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_delete_fail
|
99
|
+
|
100
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
101
|
+
|
102
|
+
r = @c.delete({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
103
|
+
|
104
|
+
assert_not_nil r
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_get_many
|
108
|
+
|
109
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
110
|
+
@c.put({ '_id' => 'jami', 'type' => 'person', 'eyes' => 'blue' })
|
111
|
+
@c.put({ '_id' => 'minehiko', 'type' => 'person', 'eyes' => 'brown' })
|
112
|
+
@c.put({ '_id' => 'hiro', 'type' => 'person', 'eyes' => 'brown' })
|
113
|
+
@c.put({ '_id' => 'chicko-chan', 'type' => 'animal', 'eyes' => 'black' })
|
114
|
+
|
115
|
+
assert_equal(
|
116
|
+
%w[ blue brown brown green ],
|
117
|
+
@c.get_many('person').collect { |e| e['eyes'] }.sort)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_get_many_with_key_match
|
121
|
+
|
122
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
123
|
+
@c.put({ '_id' => 'jami', 'type' => 'person', 'eyes' => 'blue' })
|
124
|
+
@c.put({ '_id' => 'minehiko', 'type' => 'person', 'eyes' => 'brown' })
|
125
|
+
@c.put({ '_id' => 'hiro', 'type' => 'person', 'eyes' => 'brown' })
|
126
|
+
@c.put({ '_id' => 'chicko-chan', 'type' => 'animal', 'eyes' => 'black' })
|
127
|
+
|
128
|
+
assert_equal 2, @c.get_many('person', /^j/).size
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_get_many_key_order
|
132
|
+
|
133
|
+
@c.put({ '_id' => 'john', 'type' => 'person', 'eyes' => 'green' })
|
134
|
+
@c.put({ '_id' => 'jami', 'type' => 'person', 'eyes' => 'blue' })
|
135
|
+
@c.put({ '_id' => 'minehiko', 'type' => 'person', 'eyes' => 'brown' })
|
136
|
+
@c.put({ '_id' => 'hiro', 'type' => 'person', 'eyes' => 'brown' })
|
137
|
+
@c.put({ '_id' => 'chicko-chan', 'type' => 'animal', 'eyes' => 'black' })
|
138
|
+
|
139
|
+
assert_equal(
|
140
|
+
%w[ hiro jami john minehiko ],
|
141
|
+
@c.get_many('person').collect { |e| e['_id'] })
|
142
|
+
end
|
143
|
+
|
144
|
+
protected
|
145
|
+
|
146
|
+
def fetch (type, key)
|
147
|
+
|
148
|
+
s = File.read(File.join(ROOT, 'tcloche', type, key[-2, 2], "#{key}.json"))
|
149
|
+
Rufus::Cloche.json_decode(s)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rufus-cloche
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Mettraux
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-16 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: "\n\
|
17
|
+
A very stupid JSON hash store.\n\n\
|
18
|
+
It's built on top of yajl-ruby and File.lock. Defaults to 'json' (or 'json_pure') if yajl-ruby is not present (it's probably just a \"gem install yajl-ruby\" away.\n\n\
|
19
|
+
Strives to be process-safe and thread-safe.\n "
|
20
|
+
email: jmettraux@gmail.com
|
21
|
+
executables: []
|
22
|
+
|
23
|
+
extensions: []
|
24
|
+
|
25
|
+
extra_rdoc_files:
|
26
|
+
- README.rdoc
|
27
|
+
- CHANGELOG.txt
|
28
|
+
- CREDITS.txt
|
29
|
+
files:
|
30
|
+
- README.rdoc
|
31
|
+
- CHANGELOG.txt
|
32
|
+
- CREDITS.txt
|
33
|
+
has_rdoc: true
|
34
|
+
homepage: http://github.com/jmettraux/rufus-cloche/
|
35
|
+
licenses: []
|
36
|
+
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project: rufus
|
57
|
+
rubygems_version: 1.3.5
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: a very stupid JSON hash store
|
61
|
+
test_files:
|
62
|
+
- test/test.rb
|