rufus-cloche 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|