rufus-doric 0.1.2 → 0.1.3
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 +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: []
|