rufus-doric 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt CHANGED
@@ -2,6 +2,13 @@
2
2
  = rufus-doric CHANGELOG.txt
3
3
 
4
4
 
5
+ == rufus-doric - 0.1.16 released 2010/07/12
6
+
7
+ - OneDocModel#attach
8
+ - removed duplicates for custom view results. Thanks Claudio.
9
+ - 'open' models
10
+
11
+
5
12
  == rufus-doric - 0.1.15 released 2010/06/22
6
13
 
7
14
  - view_by :name, :ignore_case => true
data/Rakefile CHANGED
@@ -79,7 +79,7 @@ end
79
79
  #
80
80
  # TO THE WEB
81
81
 
82
- task :upload_website => [ :clean, :rdoc ] do
82
+ task :upload_rdoc => [ :clean, :rdoc ] do
83
83
 
84
84
  account = 'jmettraux@rubyforge.org'
85
85
  webdir = '/var/www/gforge-projects/rufus'
data/TODO.txt CHANGED
@@ -9,12 +9,21 @@
9
9
  [o] view_by 'name', %{ emit(doc.nada) }
10
10
  [o] start and end key should be by_x(:start => a, :end => b)
11
11
  [o] credit Claudio
12
+ [o] case-insentive index (Claudio)
13
+ [o] open model
14
+ [o] custom views : remove duplicate docs from results (Claudio)
12
15
 
13
16
  [x] lsof check (jig / patron)
14
17
 
15
18
  [ ] eventually : cache the text index
16
19
 
17
20
  [ ] view_by 'x', 'emit' ==> view 'x', 'emit' ==> Model.x(key)...
21
+ [ ] view_by 'x', :condition => '{some javascript}' (a la SimplyStored)
18
22
 
19
- [ ] case-insentive index (Claudio)
23
+ [ ] open models, OK, what about on-the-fly views ?
24
+ do you really need views for those ?
25
+
26
+ [ ] internal _sum, _count and _stats
27
+ http://www.mikealrogers.com/archives/785
28
+ http://wiki.apache.org/couchdb/Built-In_Reduce_Functions
20
29
 
@@ -105,6 +105,13 @@ module Doric
105
105
  @_id_field
106
106
  end
107
107
 
108
+ # Flags a model as "open". New properties can be added on the fly.
109
+ #
110
+ def self.open
111
+
112
+ @open = true
113
+ end
114
+
108
115
  def self.attachment (attachment_name)
109
116
 
110
117
  class_eval %{
@@ -294,36 +301,16 @@ module Doric
294
301
  end
295
302
  end
296
303
 
304
+ # Attaches a document to this model.
305
+ #
306
+ # o.attach('icon.jpg', File.read('path/to/file.jpg'))
307
+ #
308
+ # No need to save! after an attachment, but the model/instance has to be
309
+ # up to date (ie, latest _rev).
310
+ #
297
311
  def attach (attname, data, opts={})
298
312
 
299
- extname = File.extname(attname)
300
- basename = File.basename(attname, extname)
301
- mime = ::MIME::Types.type_for(attname).first
302
-
303
- if data.is_a?(File)
304
- mime = ::MIME::Types.type_for(data.path).first
305
- data = data.read
306
- elsif data.is_a?(Array)
307
- data, mime = data
308
- mime = ::MIME::Types[mime].first
309
- end
310
-
311
- raise ArgumentError.new("couldn't determine mime type") unless mime
312
-
313
- attname = "#{attname}.#{mime.extensions.first}" if extname == ''
314
-
315
- if @h['_rev'] # document has already been saved
316
-
317
- db.attach(
318
- @h['_id'], @h['_rev'], attname, data, :content_type => mime.to_s)
319
-
320
- else # document hasn't yet been saved, inline attachment...
321
-
322
- (@h['_attachments'] ||= {})[attname] = {
323
- 'content_type' => mime.to_s,
324
- 'data' => Base64.encode64(data).gsub(/[\r\n]/, '')
325
- }
326
- end
313
+ do_attach(@h, attname, data, opts)
327
314
  end
328
315
 
329
316
  # Reads an attachment
@@ -344,6 +331,8 @@ module Doric
344
331
  db.get("#{@h['_id']}/#{attname}")
345
332
  end
346
333
 
334
+ # Removes an attachment.
335
+ #
347
336
  def detach (attname)
348
337
 
349
338
  raise ArgumentError.new("model not yet saved") unless @h['_rev']
@@ -351,6 +340,18 @@ module Doric
351
340
  db.delete("#{@h['_id']}/#{attname}?rev=#{@h['_rev']}")
352
341
  end
353
342
 
343
+ # If this model is open, will remove a property (key and value).
344
+ # If the model is not open, will raise an error.
345
+ #
346
+ def remove (key)
347
+
348
+ raise(
349
+ "model #{self.class.name} is not open, cannot remove properties"
350
+ ) unless self.class.instance_variable_get(:@open)
351
+
352
+ @h.delete(key.to_s)
353
+ end
354
+
354
355
  #--
355
356
  # methods required by ActiveModel (see test/unit/ut_3_model_lint.rb)
356
357
  #++
@@ -454,64 +455,17 @@ module Doric
454
455
  result['rows'].collect { |r| Rufus::Doric.instantiate(r['doc']) }
455
456
  end
456
457
 
457
- # All the association magic occur here, except for #belongings
458
+ # The association and the 'open' magic occurs here, except for #belongings
458
459
  #
459
460
  def method_missing (m, *args)
460
461
 
461
- mm = m.to_s
462
- sm = mm.singularize
463
- multiple = (mm != sm)
464
-
465
- klass = sm.camelize
466
- klass = (self.class.const_get(klass) rescue nil)
467
-
468
- #return super unless klass
469
-
470
- id_method = multiple ? "#{sm}_ids" : "#{mm}_id"
471
-
472
- unless klass
473
-
474
- return super unless self.respond_to?(id_method)
475
-
476
- i = self.send(id_method)
477
-
478
- if multiple
479
-
480
- return [] unless i
462
+ success, result = open_method_missing(m.to_s, args)
463
+ return result if success
481
464
 
482
- return i.collect { |ii|
483
- Rufus::Doric.instantiate(db.get(ii))
484
- }.select { |e|
485
- e != nil
486
- }
487
- end
488
-
489
- return Rufus::Doric.instantiate(db.get(i))
490
- end
465
+ success, result = association_method_missing(m.to_s, args)
466
+ return result if success
491
467
 
492
- if multiple
493
-
494
- if self.respond_to?(id_method)
495
-
496
- ids = self.send(id_method)
497
- return [] unless ids
498
-
499
- ids.collect { |i| klass.find(i) }
500
-
501
- else
502
-
503
- by_method = "by_#{self.class.doric_type.singularize}_id"
504
- klass.send(by_method, self._id)
505
- end
506
-
507
- else
508
-
509
- return super unless self.respond_to?(id_method)
510
-
511
- id = self.send(id_method)
512
-
513
- id ? klass.find(id) : nil
514
- end
468
+ super
515
469
  end
516
470
 
517
471
  def hash
@@ -589,6 +543,81 @@ module Doric
589
543
  "#{self.class.doric_type}__#{s}"
590
544
  end
591
545
 
546
+ def open_method_missing (m, args)
547
+
548
+ return false unless self.class.instance_variable_get(:@open)
549
+
550
+ key = m.match(/^(.+)=$/)
551
+
552
+ if key && args.length == 1
553
+ @h[key[1]] = args.first
554
+ return [ true, args.first ]
555
+ end
556
+ if ( ! key) && args.length == 0
557
+ return [ true, @h[m] ]
558
+ end
559
+
560
+ false
561
+ end
562
+
563
+ def association_method_missing (m, args)
564
+
565
+ sm = m.singularize
566
+ multiple = (m != sm)
567
+
568
+ klass = sm.camelize
569
+ klass = (self.class.const_get(klass) rescue nil)
570
+
571
+ id_method = multiple ? "#{sm}_ids" : "#{m}_id"
572
+
573
+ unless klass
574
+
575
+ return false unless self.respond_to?(id_method)
576
+
577
+ i = self.send(id_method)
578
+
579
+ if multiple
580
+
581
+ return [ true, [] ] unless i
582
+
583
+ return [
584
+ true,
585
+ i.collect { |ii|
586
+ Rufus::Doric.instantiate(db.get(ii))
587
+ }.select { |e|
588
+ e != nil
589
+ }
590
+ ]
591
+ end
592
+
593
+ return [ true, Rufus::Doric.instantiate(db.get(i)) ]
594
+ end
595
+
596
+ if multiple
597
+
598
+ if self.respond_to?(id_method)
599
+
600
+ ids = self.send(id_method)
601
+
602
+ [ true, ids ? ids.collect { |i| klass.find(i) } : [] ]
603
+
604
+ else
605
+
606
+ by_method = "by_#{self.class.doric_type.singularize}_id"
607
+
608
+ [ true, klass.send(by_method, self._id) ]
609
+ end
610
+
611
+ else
612
+
613
+ return false unless self.respond_to?(id_method)
614
+
615
+ id = self.send(id_method)
616
+
617
+ [ true, id ? klass.find(id) : nil ]
618
+ end
619
+ end
620
+
592
621
  def self.func (body, type=:map)
593
622
 
594
623
  if type == :map
@@ -755,7 +784,7 @@ module Doric
755
784
  r = result['rows'].first
756
785
  r ? r['value'] : nil
757
786
  else
758
- result['rows'].collect { |r| self.new(r['doc']) }
787
+ result['rows'].collect { |r| self.new(r['doc']) }.uniq
759
788
  end
760
789
  end
761
790
 
@@ -126,6 +126,42 @@ module Doric
126
126
 
127
127
  self.class.db
128
128
  end
129
+
130
+ protected
131
+
132
+ def do_attach (doc, attname, data, opts={})
133
+
134
+ extname = File.extname(attname)
135
+ basename = File.basename(attname, extname)
136
+ mime = ::MIME::Types.type_for(attname).first
137
+
138
+ if data.is_a?(File)
139
+ mime = ::MIME::Types.type_for(data.path).first
140
+ data = data.read
141
+ elsif data.is_a?(Array)
142
+ data, mime = data
143
+ mime = ::MIME::Types[mime].first
144
+ end
145
+
146
+ mime ||= (::MIME::Types[opts[:content_type]] || []).first
147
+
148
+ raise ArgumentError.new("couldn't determine mime type") unless mime
149
+
150
+ attname = "#{attname}.#{mime.extensions.first}" if extname == ''
151
+
152
+ if doc['_rev'] # document has already been saved
153
+
154
+ db.attach(
155
+ doc['_id'], doc['_rev'], attname, data, :content_type => mime.to_s)
156
+
157
+ else # document hasn't yet been saved, inline attachment...
158
+
159
+ (doc['_attachments'] ||= {})[attname] = {
160
+ 'content_type' => mime.to_s,
161
+ 'data' => Base64.encode64(data).gsub(/[\r\n]/, '')
162
+ }
163
+ end
164
+ end
129
165
  end
130
166
 
131
167
  #--
@@ -94,6 +94,18 @@ module Doric
94
94
  delete
95
95
  end
96
96
 
97
+ # Given a model named User, some usage examples :
98
+ #
99
+ # u = User.find('john')
100
+ # u.attach(File.read('avatar.png', :content_type => 'image/png'
101
+ # u.attach([ File.read('avatar.png', 'image/png' ])
102
+ #
103
+ def attach (data, opts={})
104
+
105
+ #def do_attach (doc, attname, data, opts={})
106
+ do_attach(db.get(self.class.doc_id), _id, data, opts)
107
+ end
108
+
97
109
  #--
98
110
  # class methods
99
111
  #++
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Rufus
3
3
  module Doric
4
- VERSION = '0.1.15'
4
+ VERSION = '0.1.16'
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.15"
8
+ s.version = "0.1.16"
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-06-22}
12
+ s.date = %q{2010-07-12}
13
13
  s.description = %q{
14
14
  something at the intersection of Rails3, CouchDB and rufus-jig
15
15
  }
@@ -59,6 +59,7 @@ something at the intersection of Rails3, CouchDB and rufus-jig
59
59
  "test/ut_19_neutralize_id.rb",
60
60
  "test/ut_1_model.rb",
61
61
  "test/ut_20_model_view_by_ignore_case.rb",
62
+ "test/ut_21_open_model.rb",
62
63
  "test/ut_2_model_view.rb",
63
64
  "test/ut_3_model_lint.rb",
64
65
  "test/ut_4_one_doc_model.rb",
@@ -25,6 +25,11 @@ class Team < Rufus::Doric::Model
25
25
  view 'tysec2', %{
26
26
  emit([ doc.type, doc.security_level ], null);
27
27
  }
28
+
29
+ view 'duplicates', %{
30
+ emit(doc.type, null);
31
+ emit(doc.type, null);
32
+ }
28
33
  end
29
34
 
30
35
  #class City < Rufus::Doric::Model
@@ -99,5 +104,15 @@ class UtModelCustomViewTest < Test::Unit::TestCase
99
104
 
100
105
  assert_equal 1, Team.by_tysec('rifle__b').size
101
106
  end
107
+
108
+ def test_remove_duplicates
109
+
110
+ Team.new(
111
+ 'type' => 'cleaning',
112
+ 'security_level' => 'b'
113
+ ).save!
114
+
115
+ assert_equal 1, Team.duplicates('cleaning').size
116
+ end
102
117
  end
103
118
 
@@ -20,6 +20,7 @@ class Handkerchief < Rufus::Doric::Model
20
20
  h_accessor :colour
21
21
  h_accessor :owner
22
22
 
23
+ view_by :colour
23
24
  view_by :owner, :ignore_case => true
24
25
  end
25
26
 
@@ -41,18 +42,37 @@ class UtModelViewTest < Test::Unit::TestCase
41
42
 
42
43
  def test_view_by_owner
43
44
 
45
+ load_data
46
+
47
+ assert_equal 1, Handkerchief.by_owner('pepe').size
48
+ assert_equal 2, Handkerchief.by_owner('jefe').size
49
+
50
+ assert_equal 0, Handkerchief.by_owner('Pepe').size
51
+ assert_equal 0, Handkerchief.by_owner('Jefe').size
52
+ end
53
+
54
+ def test_view_by_colour
55
+
56
+ load_data
57
+
58
+ assert_equal 1, Handkerchief.by_colour('brown').size
59
+ assert_equal 1, Handkerchief.by_colour('Brown').size
60
+ assert_equal 1, Handkerchief.by_colour('browN').size
61
+ end
62
+
63
+ protected
64
+
65
+ def load_data
66
+
44
67
  Handkerchief.new(
45
- :brand => 'valentino', :colour => 'blue', :owner => 'Pepe'
68
+ :brand => 'valentino', :colour => 'browN', :owner => 'Pepe'
46
69
  ).save!
47
70
  Handkerchief.new(
48
71
  :brand => 'lv', :colour => 'brown', :owner => 'Jefe'
49
72
  ).save!
50
73
  Handkerchief.new(
51
- :brand => 'chanel', :colour => 'b&w', :owner => 'jefe'
74
+ :brand => 'chanel', :colour => 'Brown', :owner => 'jefe'
52
75
  ).save!
53
-
54
- assert_equal 1, Handkerchief.by_owner('pepe').size
55
- assert_equal 2, Handkerchief.by_owner('jefe').size
56
76
  end
57
77
  end
58
78
 
@@ -0,0 +1,85 @@
1
+
2
+ #
3
+ # testing rufus-doric
4
+ #
5
+ # Wed Jun 30 15:05:56 JST 2010
6
+ #
7
+
8
+ require File.join(File.dirname(__FILE__), 'base')
9
+
10
+ require 'rufus/doric'
11
+
12
+
13
+ class Audit < Rufus::Doric::Model
14
+
15
+ db :doric
16
+ doric_type :audits
17
+
18
+ open
19
+
20
+ _id_field { "audit_#{name}" }
21
+ property :name
22
+ end
23
+
24
+ class Participant < Rufus::Doric::Model
25
+
26
+ db :doric
27
+ doric_type :audits
28
+
29
+ _id_field { "participant_#{name}" }
30
+ property :name
31
+ end
32
+
33
+
34
+ class UtOpenModelTest < Test::Unit::TestCase
35
+
36
+ def setup
37
+
38
+ Rufus::Doric.db('doric').delete('.')
39
+ Rufus::Doric.db('doric').put('.')
40
+
41
+ Rufus::Doric.db('doric').http.cache.clear
42
+ # CouchDB feeds the same etags for views, even after a db has
43
+ # been deleted and put back, so have to do that 'forgetting'
44
+ end
45
+
46
+ #def teardown
47
+ #end
48
+
49
+ def test_openness
50
+
51
+ a = Audit.new(:name => 'nada')
52
+ a.save!
53
+
54
+ assert_equal nil, a.clerk
55
+ assert_equal nil, a.whatever
56
+
57
+ a = Audit.all.first
58
+ a.clerk = 'Wilfried'
59
+ a.save!
60
+
61
+ assert_equal 'Wilfried', a.clerk
62
+ end
63
+
64
+ def test_remove
65
+
66
+ a = Audit.new(:name => 'berlusconi')
67
+ a.h['country'] = 'x'
68
+
69
+ a.remove(:country)
70
+
71
+ assert_equal %w[ doric_type name ], a.h.keys.sort
72
+ end
73
+
74
+ def test_remove_on_a_closed_model
75
+
76
+ Participant.new(:name => 'Joseph').save!
77
+
78
+ pa = Participant.all.first
79
+
80
+ assert_raise RuntimeError do
81
+ pa.remove(:nationality)
82
+ end
83
+ end
84
+ end
85
+
@@ -94,9 +94,15 @@ class UtOneDocModelTest < Test::Unit::TestCase
94
94
  assert_equal [], User.all
95
95
  end
96
96
 
97
- # def test_missing_db_declaration
98
- #
99
- # Site.new(:name => 'Eur', :streets => [:one, :two, :three]).save!
100
- # end
97
+ def test_attach
98
+
99
+ u = User.find('john')
100
+ u.attach(File.read(__FILE__), :content_type => 'text/plain')
101
+
102
+ assert_match /justin/, u.db.get('users/john.txt')
103
+
104
+ u = User.find('john')
105
+ assert_equal 'john.txt', u.attachment
106
+ end
101
107
  end
102
108
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 15
9
- version: 0.1.15
8
+ - 16
9
+ version: 0.1.16
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-06-22 00:00:00 +09:00
17
+ date: 2010-07-12 00:00:00 +09:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -146,6 +146,7 @@ files:
146
146
  - test/ut_19_neutralize_id.rb
147
147
  - test/ut_1_model.rb
148
148
  - test/ut_20_model_view_by_ignore_case.rb
149
+ - test/ut_21_open_model.rb
149
150
  - test/ut_2_model_view.rb
150
151
  - test/ut_3_model_lint.rb
151
152
  - test/ut_4_one_doc_model.rb