rufus-doric 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +7 -0
- data/README.rdoc +149 -3
- data/TODO.txt +5 -0
- data/lib/rufus/doric/model.rb +80 -23
- data/lib/rufus/doric/value.rb +10 -0
- data/lib/rufus/doric/version.rb +1 -1
- data/rufus-doric.gemspec +4 -3
- data/test/ut_2_model_view.rb +9 -0
- data/test/ut_5_value.rb +16 -0
- data/test/ut_9_any.rb +115 -0
- metadata +4 -3
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
= rufus-doric CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-doric - 0.1.3 released 2010/04/06
|
6
|
+
|
7
|
+
- Rufus::Doric::Value now has h_shortcut
|
8
|
+
- some kind of full-text indexing (.text_index / .texts) for Model
|
9
|
+
- preventing infinite loop when doing Model.by_x and db is not created
|
10
|
+
|
11
|
+
|
5
12
|
== rufus-doric - 0.1.2 released 2010/03/23
|
6
13
|
|
7
14
|
- person.belongings() returns all the object whose person_id is person._id
|
data/README.rdoc
CHANGED
@@ -1,16 +1,162 @@
|
|
1
1
|
|
2
2
|
= rufus-doric
|
3
3
|
|
4
|
-
|
4
|
+
some Ruby lib at the intersection of Rails3, CouchDB and rufus-jig.
|
5
|
+
|
6
|
+
|
7
|
+
== how does rufus-doric determine which Couch database to use ?
|
8
|
+
|
9
|
+
At first it determines which CouchDB server to use via this code :
|
10
|
+
|
11
|
+
def self.couch_url
|
12
|
+
|
13
|
+
if defined?(Rails) # rails config/couch_url.txt
|
14
|
+
return File.read(Rails.root.join('config', 'couch_url.txt')).strip
|
15
|
+
end
|
16
|
+
if File.exist?('couch_url.txt') # current working directory
|
17
|
+
return File.read('couch_url.txt').strip
|
18
|
+
end
|
19
|
+
|
20
|
+
'http://127.0.0.1:5984' # the default
|
21
|
+
end
|
22
|
+
|
23
|
+
Then the database is determined by calling Rufus::Doric.db(name)
|
24
|
+
|
25
|
+
p Rufus::Doric.db('mydb', :url_only => true)
|
26
|
+
# => "http://127.0.0.1:5984/mydb_test"
|
27
|
+
|
28
|
+
p Rufus::Doric.db('mydb', :env => 'camelia', :url_only => true)
|
29
|
+
# => "http://127.0.0.1:5984/mydb_camelia"
|
30
|
+
|
31
|
+
p Rufus::Doric.db('mydb', :env => 'camelia')
|
32
|
+
# => #<Rufus::Jig::Couch:0x00000102325120 @http=#<Rufus::Jig::Http:0x00000102324908 @host="127.0.0.1", @port=5984, @path="/toot_test", ...>...>
|
33
|
+
|
34
|
+
In summary, you only have to create a couch_url.txt file that contains the http:://{host}:{port} of your couch server. The rest is taken care of.
|
5
35
|
|
6
36
|
|
7
37
|
== Rufus::Doric::Model usage
|
8
38
|
|
9
|
-
|
39
|
+
One document per instance.
|
40
|
+
|
41
|
+
class Item < Rufus::Doric::Model
|
42
|
+
|
43
|
+
db :doric
|
44
|
+
# in which db it goes (remember that an _env suffix is added)
|
45
|
+
|
46
|
+
doric_type :items
|
47
|
+
# 'doric_type' field
|
48
|
+
|
49
|
+
_id_field :name
|
50
|
+
# _id field is determined from field 'name'
|
51
|
+
|
52
|
+
h_accessor :name
|
53
|
+
h_accessor :supplier
|
54
|
+
|
55
|
+
validates :supplier, :presence => true
|
56
|
+
end
|
57
|
+
|
58
|
+
There is more, please look at the test/ directory to discover Model.
|
59
|
+
|
10
60
|
|
11
61
|
== Rufus::Doric::OneDocModel usage
|
12
62
|
|
13
|
-
|
63
|
+
I use this for 'users' models.
|
64
|
+
|
65
|
+
class User < Rufus::Doric::OneDocModel
|
66
|
+
|
67
|
+
db 'doric'
|
68
|
+
doc_id :users
|
69
|
+
|
70
|
+
h_accessor :locale
|
71
|
+
h_accessor :email
|
72
|
+
h_reader :password
|
73
|
+
|
74
|
+
validates :password, :presence => true
|
75
|
+
end
|
76
|
+
|
77
|
+
It places all the users in one document (whereas Rufus::Doric::Model has 1 document per instance/record).
|
78
|
+
|
79
|
+
Look at the test/ directory to learn more about OneDocModel.
|
80
|
+
|
81
|
+
|
82
|
+
== Rufus::Doric::Value usage
|
83
|
+
|
84
|
+
Value is about Couch documents containing a single 'value' field (apart from _id and _rev)
|
85
|
+
|
86
|
+
Given
|
87
|
+
|
88
|
+
class Tuples < Rufus::Doric::Value
|
89
|
+
|
90
|
+
doc_id :tuples
|
91
|
+
db :doric
|
92
|
+
|
93
|
+
def to_s
|
94
|
+
value.sort.join(' ')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
and a document 'tuples' in the database 'doric' :
|
99
|
+
|
100
|
+
{
|
101
|
+
"_id": "tuples",
|
102
|
+
"value": [ "alpha", "bravo", "charly" ]
|
103
|
+
}
|
104
|
+
|
105
|
+
this can be done :
|
106
|
+
|
107
|
+
p Tuples.load.to_s
|
108
|
+
# => "alpha bravo charly"
|
109
|
+
|
110
|
+
tuples = Tuples.load
|
111
|
+
tuples.value << 'borneo'
|
112
|
+
tuples.save!
|
113
|
+
|
114
|
+
p Tuples.load.to_s
|
115
|
+
# => "alpha borneo bravo charly"
|
116
|
+
|
117
|
+
Also :
|
118
|
+
|
119
|
+
tuples = Tuples.new(
|
120
|
+
'_id' => 'tuples', 'value' => %w[ alpha beta delta gamma ]).save!
|
121
|
+
|
122
|
+
p Tuples.load.to_s
|
123
|
+
# => "alpha beta delta gamma"
|
124
|
+
|
125
|
+
|
126
|
+
== Rufus::Doric::Value and h_shortcut
|
127
|
+
|
128
|
+
Most of the time, I use Rufus::Doric::Value to store a hash of things :
|
129
|
+
|
130
|
+
class Misc < Rufus::Doric::Value
|
131
|
+
|
132
|
+
doc_id :misc
|
133
|
+
db :doric
|
134
|
+
|
135
|
+
h_shortcut :product_lines
|
136
|
+
h_shortcut :purposes
|
137
|
+
end
|
138
|
+
|
139
|
+
with :
|
140
|
+
|
141
|
+
{
|
142
|
+
"_id": "misc",
|
143
|
+
"value": {
|
144
|
+
"product_lines" : [
|
145
|
+
"blue_coat", "ulticom", "znyx"
|
146
|
+
],
|
147
|
+
"purposes": [
|
148
|
+
"stock", "non_stock", "replace", "rma", "loan"
|
149
|
+
]
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
then in my app, I just do
|
154
|
+
|
155
|
+
p Misc.product_lines
|
156
|
+
# => [ "blue_coat", "ulticom", "znyx" ]
|
157
|
+
|
158
|
+
p Misc.purposes
|
159
|
+
# => [ "stock", "non_stock", "replace", "rma", "loan" ]
|
14
160
|
|
15
161
|
|
16
162
|
== 'fixtures' usage
|
data/TODO.txt
CHANGED
@@ -2,8 +2,13 @@
|
|
2
2
|
[o] one_doc_model : validation
|
3
3
|
[o] model : validation
|
4
4
|
[o] destroy/delete
|
5
|
+
[o] fix Amedeo's infinite loop (model.rb l438)
|
5
6
|
|
6
7
|
[x] lsof check (jig / patron)
|
7
8
|
|
8
9
|
[ ] all, by : limit and skip (pagination)
|
9
10
|
|
11
|
+
[ ] eventually : cache the text index
|
12
|
+
[ ] eventually : pagination for the text index
|
13
|
+
[ ] pagination for everybody in Model (DRY)
|
14
|
+
|
data/lib/rufus/doric/model.rb
CHANGED
@@ -115,6 +115,11 @@ module Doric
|
|
115
115
|
}
|
116
116
|
end
|
117
117
|
|
118
|
+
def self.text_index (*keys)
|
119
|
+
|
120
|
+
@text_index = keys
|
121
|
+
end
|
122
|
+
|
118
123
|
include WithH
|
119
124
|
include WithDb
|
120
125
|
|
@@ -369,6 +374,22 @@ module Doric
|
|
369
374
|
"_design/doric_#{name}"
|
370
375
|
end
|
371
376
|
|
377
|
+
# Well... Returns a map { 'word' => [ docid0, docid1 ] }
|
378
|
+
#
|
379
|
+
def self.texts (key=nil)
|
380
|
+
|
381
|
+
return nil unless @text_index
|
382
|
+
|
383
|
+
path = "#{design_path}/_view/text_index"
|
384
|
+
path = "#{path}?key=%22#{key}%22" if key
|
385
|
+
|
386
|
+
m = get_result(path, :text_index)
|
387
|
+
|
388
|
+
m = m['rows'].inject({}) { |h, r| (h[r['key']] ||= []) << r['id']; h }
|
389
|
+
|
390
|
+
key ? m[key] : m
|
391
|
+
end
|
392
|
+
|
372
393
|
protected
|
373
394
|
|
374
395
|
def self.put_design_doc (key=nil)
|
@@ -384,15 +405,42 @@ module Doric
|
|
384
405
|
'views' => {}
|
385
406
|
}
|
386
407
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
408
|
+
if key == :text_index
|
409
|
+
|
410
|
+
# I wish I could write keys.forEach(...) directly
|
411
|
+
|
412
|
+
# do no word removing, it depends on languages, and can be
|
413
|
+
# done on the client side
|
414
|
+
|
415
|
+
ddoc['views']['text_index'] = {
|
416
|
+
'map' => %{
|
417
|
+
function(doc) {
|
418
|
+
if (doc.doric_type == '#{@doric_type}') {
|
419
|
+
var keys = #{Rufus::Json.encode(@text_index)};
|
420
|
+
for (var key in doc) {
|
421
|
+
if (keys.indexOf(key) < 0) continue;
|
422
|
+
if (doc[key] == undefined) continue;
|
423
|
+
var words = doc[key].split(/[\s,;\.]/);
|
424
|
+
words.forEach(function (word) {
|
425
|
+
if (word != '') emit(word, null);
|
426
|
+
});
|
427
|
+
}
|
428
|
+
}
|
392
429
|
}
|
393
430
|
}
|
394
431
|
}
|
395
|
-
|
432
|
+
else
|
433
|
+
|
434
|
+
ddoc['views']["by_#{key}"] = {
|
435
|
+
'map' => %{
|
436
|
+
function(doc) {
|
437
|
+
if (doc.doric_type == '#{@doric_type}') {
|
438
|
+
emit(doc['#{key}'], null);
|
439
|
+
}
|
440
|
+
}
|
441
|
+
}
|
442
|
+
}
|
443
|
+
end
|
396
444
|
|
397
445
|
db.put(ddoc)
|
398
446
|
end
|
@@ -405,18 +453,7 @@ module Doric
|
|
405
453
|
"_design/doric/_view/by_doric_type?key=%22#{@doric_type}%22" +
|
406
454
|
"&include_docs=true"
|
407
455
|
|
408
|
-
result =
|
409
|
-
|
410
|
-
unless result
|
411
|
-
|
412
|
-
# insert design doc
|
413
|
-
|
414
|
-
r = put_design_doc
|
415
|
-
raise(
|
416
|
-
"failed to insert design_doc in db '#{db.name}'"
|
417
|
-
) if r == true
|
418
|
-
return get_all(opts)
|
419
|
-
end
|
456
|
+
result = get_result(path)
|
420
457
|
|
421
458
|
result['rows'].collect { |r| r['doc'] }
|
422
459
|
end
|
@@ -431,14 +468,34 @@ module Doric
|
|
431
468
|
|
432
469
|
path = "#{design_path}/_view/by_#{key}?key=#{v}&include_docs=true"
|
433
470
|
|
471
|
+
result = get_result(path, key)
|
472
|
+
|
473
|
+
result['rows'].collect { |r| self.new(r['doc']) }
|
474
|
+
end
|
475
|
+
|
476
|
+
# Ensures the necessary design_doc is loaded (if first query failed)
|
477
|
+
# and then returns the raw result.
|
478
|
+
#
|
479
|
+
# Will raise if the design_doc can't be inserted (probably the underlying
|
480
|
+
# db is missing).
|
481
|
+
#
|
482
|
+
def self.get_result (path, design_doc_key=nil)
|
483
|
+
|
434
484
|
result = db.get(path)
|
435
485
|
|
436
|
-
|
437
|
-
put_design_doc(key)
|
438
|
-
return by(key, val, opts)
|
439
|
-
end
|
486
|
+
return result if result
|
440
487
|
|
441
|
-
|
488
|
+
# insert design doc
|
489
|
+
|
490
|
+
r = put_design_doc(design_doc_key)
|
491
|
+
|
492
|
+
raise(
|
493
|
+
"failed to insert 'any' design_doc in db '#{db.name}'"
|
494
|
+
) if r == true
|
495
|
+
|
496
|
+
# re-get
|
497
|
+
|
498
|
+
get_result(path, design_doc_key)
|
442
499
|
end
|
443
500
|
end
|
444
501
|
end
|
data/lib/rufus/doric/value.rb
CHANGED
data/lib/rufus/doric/version.rb
CHANGED
data/rufus-doric.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rufus-doric}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["John Mettraux"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-04-06}
|
13
13
|
s.description = %q{
|
14
14
|
something at the intersection of Rails3, CouchDB and rufus-jig
|
15
15
|
}
|
@@ -53,7 +53,8 @@ something at the intersection of Rails3, CouchDB and rufus-jig
|
|
53
53
|
"test/ut_5_value.rb",
|
54
54
|
"test/ut_6_model_associations.rb",
|
55
55
|
"test/ut_7_looser_associations.rb",
|
56
|
-
"test/ut_8_belongings.rb"
|
56
|
+
"test/ut_8_belongings.rb",
|
57
|
+
"test/ut_9_any.rb"
|
57
58
|
]
|
58
59
|
s.homepage = %q{http://github.com/jmettraux/rufus-doric/}
|
59
60
|
s.rdoc_options = ["--charset=UTF-8"]
|
data/test/ut_2_model_view.rb
CHANGED
@@ -64,5 +64,14 @@ class UtModelTest < Test::Unit::TestCase
|
|
64
64
|
|
65
65
|
assert_not_nil Nada::Thing.db.get('_design/doric_nada__thing')
|
66
66
|
end
|
67
|
+
|
68
|
+
def test_no_infinite_loop_when_missing_db
|
69
|
+
|
70
|
+
Rufus::Doric.db('doric').delete('.')
|
71
|
+
|
72
|
+
assert_raise RuntimeError do
|
73
|
+
Nada::Thing.by_colour('blue').size
|
74
|
+
end
|
75
|
+
end
|
67
76
|
end
|
68
77
|
|
data/test/ut_5_value.rb
CHANGED
@@ -21,6 +21,14 @@ class Tuples < Rufus::Doric::Value
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
class Misc < Rufus::Doric::Value
|
25
|
+
|
26
|
+
doc_id :misc
|
27
|
+
db :doric
|
28
|
+
|
29
|
+
h_shortcut :product_lines
|
30
|
+
end
|
31
|
+
|
24
32
|
|
25
33
|
class UtValueTest < Test::Unit::TestCase
|
26
34
|
|
@@ -65,5 +73,13 @@ class UtValueTest < Test::Unit::TestCase
|
|
65
73
|
|
66
74
|
assert_equal 'alpha beta delta gamma', Tuples.load.to_s
|
67
75
|
end
|
76
|
+
|
77
|
+
def test_h_shortcut
|
78
|
+
|
79
|
+
Misc.new(
|
80
|
+
'_id' => 'misc', 'value' => { 'product_lines' => %w[ a b c ]}).save!
|
81
|
+
|
82
|
+
assert_equal %w[ a b c ], Misc.product_lines
|
83
|
+
end
|
68
84
|
end
|
69
85
|
|
data/test/ut_9_any.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# testing rufus-doric
|
4
|
+
#
|
5
|
+
# Thu Apr 1 10:32:57 JST 2010
|
6
|
+
#
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__), 'base')
|
9
|
+
|
10
|
+
require 'rufus/doric'
|
11
|
+
|
12
|
+
|
13
|
+
class Product < Rufus::Doric::Model
|
14
|
+
|
15
|
+
db :doric
|
16
|
+
doric_type :products
|
17
|
+
|
18
|
+
_id_field :serial_number
|
19
|
+
|
20
|
+
property :serial_number
|
21
|
+
property :name
|
22
|
+
property :brand
|
23
|
+
property :category
|
24
|
+
property :comment
|
25
|
+
|
26
|
+
text_index :serial_number, :name, :brand
|
27
|
+
end
|
28
|
+
|
29
|
+
class Whatever < Rufus::Doric::Model
|
30
|
+
|
31
|
+
db :doric
|
32
|
+
doric_type :whatevers
|
33
|
+
|
34
|
+
_id_field :name
|
35
|
+
|
36
|
+
property :name
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class UtAnyTest < Test::Unit::TestCase
|
41
|
+
|
42
|
+
def setup
|
43
|
+
|
44
|
+
Rufus::Doric.db('doric').delete('.')
|
45
|
+
Rufus::Doric.db('doric').put('.')
|
46
|
+
|
47
|
+
Rufus::Doric.db('doric').http.cache.clear
|
48
|
+
# CouchDB feeds the same etags for views, even after a db has
|
49
|
+
# been deleted and put back, so have to do that 'forgetting'
|
50
|
+
|
51
|
+
Product.new(
|
52
|
+
:serial_number => 'h2o',
|
53
|
+
:name => 'water',
|
54
|
+
:brand => 'earth',
|
55
|
+
:category => 'drink',
|
56
|
+
:comment => nil
|
57
|
+
).save!
|
58
|
+
Product.new(
|
59
|
+
:serial_number => '951',
|
60
|
+
:name => 'seamaster professional',
|
61
|
+
:brand => 'omega',
|
62
|
+
:category => 'watches',
|
63
|
+
:comment => 'want as well'
|
64
|
+
).save!
|
65
|
+
Product.new(
|
66
|
+
:serial_number => 'lv52',
|
67
|
+
:name => 'leather bag, for men',
|
68
|
+
:brand => 'lv',
|
69
|
+
:category => 'bags',
|
70
|
+
:comment => nil
|
71
|
+
).save!
|
72
|
+
Product.new(
|
73
|
+
:serial_number => '0kcal',
|
74
|
+
:name => 'zero',
|
75
|
+
:brand => 'coca-cola',
|
76
|
+
:category => 'drink',
|
77
|
+
:comment => nil
|
78
|
+
).save!
|
79
|
+
Product.new(
|
80
|
+
:serial_number => 'tell2',
|
81
|
+
:name => 'tell bag',
|
82
|
+
:brand => 'victorinox',
|
83
|
+
:category => 'bags',
|
84
|
+
:comment => nil
|
85
|
+
).save!
|
86
|
+
|
87
|
+
Whatever.new(
|
88
|
+
:name => 'whatever'
|
89
|
+
).save!
|
90
|
+
end
|
91
|
+
|
92
|
+
#def teardown
|
93
|
+
#end
|
94
|
+
|
95
|
+
def test_no_texts
|
96
|
+
|
97
|
+
assert_nil Whatever.texts
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_texts
|
101
|
+
|
102
|
+
index = Product.texts
|
103
|
+
|
104
|
+
#p index
|
105
|
+
|
106
|
+
assert_equal %w[ lv52 tell2 ], index['bag'].sort
|
107
|
+
assert_equal nil, index['products']
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_texts_key
|
111
|
+
|
112
|
+
assert_equal [ '951' ], Product.texts('omega')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 3
|
9
|
+
version: 0.1.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- John Mettraux
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-04-06 00:00:00 +09:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- test/ut_6_model_associations.rb
|
142
142
|
- test/ut_7_looser_associations.rb
|
143
143
|
- test/ut_8_belongings.rb
|
144
|
+
- test/ut_9_any.rb
|
144
145
|
has_rdoc: true
|
145
146
|
homepage: http://github.com/jmettraux/rufus-doric/
|
146
147
|
licenses: []
|