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 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
- something at the intersection of Rails3, CouchDB and rufus-jig.
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
- see tests
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
- see tests
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
+
@@ -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
- ddoc['views']["by_#{key}"] = {
388
- 'map' => %{
389
- function(doc) {
390
- if (doc.doric_type == '#{@doric_type}') {
391
- emit(doc['#{key}'], null);
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 = db.get(path)
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
- unless result
437
- put_design_doc(key)
438
- return by(key, val, opts)
439
- end
486
+ return result if result
440
487
 
441
- result['rows'].collect { |r| self.new(r['doc']) }
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
@@ -40,6 +40,16 @@ module Doric
40
40
  @doc_id
41
41
  end
42
42
 
43
+ def self.h_shortcut (*keys)
44
+ keys.each do |k|
45
+ self.instance_eval %{
46
+ def #{k}
47
+ load.value[#{k.to_s.inspect}]
48
+ end
49
+ }
50
+ end
51
+ end
52
+
43
53
  include WithDb
44
54
 
45
55
  #
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Rufus
3
3
  module Doric
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
6
6
  end
7
7
 
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.2"
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-03-23}
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"]
@@ -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
- - 2
9
- version: 0.1.2
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-03-23 00:00:00 +09:00
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: []