divan 0.1.1

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/README ADDED
@@ -0,0 +1,3 @@
1
+ Divan, a Ruby CouchDB client for insane people
2
+
3
+ TODO: README, documentation and other details
data/Rakefile.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'init.rb'
3
+ require 'rake'
4
+
5
+ task :create_database do
6
+ if ENV['database']
7
+ Divan[ENV['database'].to_sym].create unless Divan[ENV['database'].to_sym].exists?
8
+ else
9
+ Divan.databases.each do |name, database|
10
+ database.create unless database.exists?
11
+ end
12
+ end
13
+ end
14
+
15
+ task :create_views do
16
+ if ENV['database']
17
+ if ENV['design']
18
+ Divan[ENV['design'].to_sym].create_views
19
+ else
20
+ Divan[ENV['database'].to_sym].create_views
21
+ end
22
+ else
23
+ Divan.databases.each do |name, database|
24
+ database.create_views
25
+ end
26
+ end
27
+ end
28
+
29
+ begin
30
+ require 'jeweler'
31
+ Jeweler::Tasks.new do |gem|
32
+ gem.name = "divan"
33
+ gem.summary = "A Ruby CouchDB Client for insane people"
34
+ gem.description = "This is a very simple CouchDB client that have few dependencies.\nThis client has a lot of interesting features, for example: easy access to CouchDB revisions.\n"
35
+ gem.email = "dalthon@aluno.ita.br"
36
+ gem.homepage = "http://github.com/efqdalton/divan"
37
+ gem.authors = ["Dalton Pinto"]
38
+ gem.files.exclude "config"
39
+ end
40
+ Jeweler::GemcutterTasks.new
41
+ rescue LoadError
42
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
43
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/init.rb ADDED
@@ -0,0 +1,14 @@
1
+ divan_path = File.expand_path('../lib', __FILE__)
2
+ $:.unshift(divan_path) if File.directory?(divan_path) && !$:.include?(divan_path)
3
+
4
+ require 'restclient'
5
+ require 'json'
6
+ require 'divan.rb'
7
+
8
+ Divan.load_database_configuration 'config/divan_config.yml'
9
+
10
+ # Lines below are used for debug purposes only
11
+ class POC < Divan::Models::ProofOfConcept
12
+ view_by :mod
13
+ view_by :value
14
+ end
data/lib/divan.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'divan/models/base'
2
+ require 'divan/base'
3
+ require 'divan/database'
4
+ require 'divan/utils'
5
+ require 'yaml'
6
+
7
+ module Divan
8
+ @@databases = {}
9
+
10
+ def self.Model(database_config_name)
11
+ Database.model_class(database_config_name)
12
+ rescue
13
+ Divan::Models::Base
14
+ end
15
+
16
+ def self.load_database_configuration(config_path)
17
+ YAML.load(File.read config_path).each do |name, params|
18
+ @@databases[name.to_sym] = Database.new name, params
19
+ end
20
+ end
21
+
22
+ def self.[](name)
23
+ @@databases[name.to_sym]
24
+ end
25
+
26
+ def self.databases
27
+ @@databases
28
+ end
29
+
30
+ class DatabaseNotFound < RuntimeError
31
+ attr_reader :database
32
+ def initialize(database)
33
+ @database = database
34
+ end
35
+ end
36
+
37
+ class DatabaseAlreadyCreated < RuntimeError
38
+ attr_reader :database
39
+ def initialize(database)
40
+ @database = database
41
+ end
42
+ end
43
+
44
+ class DocumentRevisionMissing < RuntimeError
45
+ attr_reader :document
46
+ def initialize(document)
47
+ @document = document
48
+ end
49
+ end
50
+
51
+ class DocumentNotFound < RuntimeError
52
+ attr_reader :document
53
+ def initialize(document)
54
+ @document = document
55
+ end
56
+ end
57
+
58
+ class DocumentConflict < RuntimeError
59
+ attr_reader :new_document, :current_document
60
+ def initialize(new_document)
61
+ @new_document = new_document
62
+ @current_document = new_document.class.find new_document.id
63
+ end
64
+ end
65
+ end
data/lib/divan/base.rb ADDED
@@ -0,0 +1,195 @@
1
+ module Divan
2
+ class Base < Models::Base
3
+ attr_accessor :id, :rev, :attributes, :last_request
4
+
5
+ def initialize(opts = {})
6
+ opts = opts.clone
7
+ @id = opts.delete(:id) || opts.delete(:_id) || Divan::Utils.uuid
8
+ @rev = opts.delete(:rev) || opts.delete(:_rev)
9
+ @attributes = opts
10
+ @attributes[self.class.type_field.to_sym] = self.class.type_name unless self.class.top_level_model?
11
+ self.class.properties.each{ |property| @attributes[property] ||= nil }
12
+ end
13
+
14
+ def [](key)
15
+ @attributes[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @attributes[key] = value
20
+ end
21
+
22
+ def validate
23
+ true
24
+ end
25
+ alias :"valid?" :validate
26
+
27
+ def database
28
+ self.class.database
29
+ end
30
+
31
+ def method_missing(method, *args, &block)
32
+ method = method.to_s
33
+ if method[-1..-1] == '='
34
+ attrib = method[0..-2].to_sym
35
+ return @attributes[attrib] = args.first
36
+ end
37
+ if( @attributes.keys.include?( (attrib = method[0..-1].to_sym) ) )
38
+ return @attributes[attrib]
39
+ end
40
+ raise NoMethodError, "undefined method '#{method}' for #{self}:#{self.class}"
41
+ end
42
+
43
+ def to_s
44
+ "\#<#{database.name.to_s.split('_').map{|str| "#{str[0..0].upcase}#{str[1..-1]}" }} #{@attributes.inspect.gsub("\\\"", "\"")}>"
45
+ end
46
+ alias :inspect :to_s
47
+
48
+ protected
49
+
50
+ def document_path(params = {})
51
+ Divan::Utils.formatted_path @id, params
52
+ end
53
+
54
+ def current_document_path(params = {})
55
+ params[:rev] = @rev if @rev
56
+ document_path params
57
+ end
58
+
59
+ class << self
60
+ attr_writer :type_name, :type_field
61
+ attr_reader :view_by_params, :database
62
+ attr_accessor :model_name
63
+
64
+ def inherited(subclass)
65
+ strs = subclass.name.match(/[^:]*\Z/)[0].split(/([A-Z][^A-Z]*)/)
66
+ strs.delete ''
67
+ subclass.model_name = strs.map{ |x| x.downcase }.join('_')
68
+ if database
69
+ subclass.database = database
70
+ subclass.top_level_model! if subclass.database.name == subclass.model_name
71
+ end
72
+ end
73
+
74
+ def type_name
75
+ @type_name ||= model_name
76
+ end
77
+
78
+ def type_field
79
+ @type_field ||= 'divan_doc_type'
80
+ end
81
+
82
+ def top_level_model!(true_or_false = true)
83
+ @top_level_model = true_or_false
84
+ end
85
+
86
+ def top_level_model?
87
+ @top_level_model ||= false
88
+ end
89
+
90
+ def properties
91
+ @properties ||= ( superclass.methods.include? :properties ) ? superclass.properties.clone : []
92
+ end
93
+
94
+ def property(*args)
95
+ properties.concat args.flatten
96
+ end
97
+
98
+ def database=(database)
99
+ undefine_views if( !@database.nil? && @database != database )
100
+ @database = database
101
+ define_view_all
102
+ define_views
103
+ @database
104
+ end
105
+
106
+ def model_name
107
+ @model_name ||= ( superclass.methods.include? :model_name ) ? superclass.model_name.clone : nil
108
+ end
109
+
110
+ [:before_save, :after_save, :before_create, :after_create, :before_validate, :after_validate].each do |method|
111
+ define_method method do |param|
112
+ eval "( @#{method}_callback ||= [] ) << param"
113
+ end
114
+
115
+ define_method "execute_#{method}_callback" do |instance|
116
+ eval <<-end_txt
117
+ @#{method}_callback ||= []
118
+ !!@#{method}_callback.each do |cb|
119
+ instance.send(cb) or break false if cb.is_a?(Symbol)
120
+ cb.call(instance) or break false if cb.is_a?(Proc)
121
+ end
122
+ end_txt
123
+ end
124
+
125
+ end
126
+
127
+ def define_view(param, functions)
128
+ @views ||= {}
129
+ @views[param.to_sym] = functions
130
+ end
131
+
132
+ def define_view!(param, functions)
133
+ database.views[model_name] ||= {}
134
+ database.views[model_name][param.to_sym] = functions
135
+ end
136
+
137
+ def define_view_all
138
+ if database && model_name == database.name
139
+ define_view :all, :map => "function(doc){ if(doc._id.slice(0, 7) != \"_design\"){ emit(null, doc) } }"
140
+ else
141
+ define_view :all, :map => "function(doc){ if(doc.#{type_field} == \"#{type_name}\"){ emit(null, doc) } }"
142
+ end
143
+ end
144
+
145
+ def query_view(view, key=nil, args={}, special={})
146
+ if key.is_a? Hash
147
+ special = args
148
+ args = key
149
+ else
150
+ special = args
151
+ args = {:key => key}
152
+ end
153
+
154
+ args = args.inject({}){ |hash,(k,v)| hash[k] = v.to_json; hash }
155
+ view_path = Divan::Utils.formatted_path "_design/#{model_name}/_view/#{view}", args.merge(special)
156
+ last_request = database.client[view_path].get
157
+ results = JSON.parse last_request, :symbolize_names => true
158
+ results[:rows].map do |row|
159
+ obj = self.new row[:value]
160
+ obj.last_request = last_request
161
+ obj
162
+ end
163
+ end
164
+
165
+ protected
166
+
167
+ def view_by(param)
168
+ @view_by_params ||= []
169
+ @view_by_params << param
170
+ define_view! "by_#{param}", :map => "function(doc) { emit(doc.#{param}, doc) }"
171
+ eval <<-end_txt
172
+ class << self
173
+ def all_by_#{param}(key, args={}, special={})
174
+ query_view :by_#{param}, key, args, special
175
+ end
176
+
177
+ def by_#{param}(key)
178
+ all_by_#{param}(:key => key, :limit => 1).first
179
+ end
180
+ end
181
+ end_txt
182
+ end
183
+
184
+ def define_views
185
+ database.views[model_name] ||= {}
186
+ database.views[model_name].merge! @views if @views
187
+ end
188
+
189
+ def undefine_views
190
+ database.views[model_name] = nil
191
+ end
192
+
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,95 @@
1
+ module Divan
2
+ module Models; end
3
+
4
+ class Database
5
+ attr_accessor :name, :host, :port, :database, :user, :password, :views
6
+ def initialize(name, options = {})
7
+ @name, @user, @password = name, options['user'], options['password']
8
+ #TODO: Add user & password support
9
+ @host = options['host'] || 'http://127.0.0.1'
10
+ @port = options['port'] || 5984
11
+ @database = options['database']
12
+ @views = {}
13
+ build_model_class
14
+ end
15
+
16
+ def exists?
17
+ begin
18
+ client.get
19
+ return true
20
+ rescue RestClient::ResourceNotFound
21
+ return false
22
+ end
23
+ end
24
+
25
+ def stats
26
+ begin
27
+ JSON.parse client.get, :symbolize_names => true
28
+ rescue RestClient::ResourceNotFound
29
+ raise Divan::DatabaseNotFound.new(self), "Database was not found"
30
+ end
31
+ end
32
+
33
+ def create
34
+ begin
35
+ client.put Hash.new
36
+ rescue RestClient::PreconditionFailed
37
+ raise Divan::DatabaseAlreadyCreated.new(self), "Database already created"
38
+ end
39
+ end
40
+
41
+ def delete
42
+ begin
43
+ client.delete
44
+ rescue RestClient::ResourceNotFound
45
+ raise Divan::DatabaseNotFound.new(self), "Database was not found"
46
+ end
47
+ end
48
+
49
+ def create_views
50
+ views.each do |name, views|
51
+ create_view(name)
52
+ end
53
+ end
54
+
55
+ def create_view(view_name)
56
+ if view_doc = model_class.find("_design/#{view_name}")
57
+ view_doc.views = views[view_name]
58
+ view_doc.save
59
+ else
60
+ model_class.create :id => "_design/#{view_name}", :language => 'javascript', :views => views[view_name]
61
+ end
62
+ end
63
+
64
+ def [](path, params={})
65
+ client[ Divan::Utils.formatted_path(path, params) ]
66
+ end
67
+
68
+ def client
69
+ @client ||= RestClient::Resource.new( basic_url, *([@user, @password].compact) )
70
+ end
71
+
72
+ def model_class
73
+ @model_class ||= eval model_class_full_name
74
+ end
75
+
76
+ protected
77
+ def basic_url
78
+ "#{host}:#{port}/#{database}/"
79
+ end
80
+
81
+ def model_class_full_name
82
+ "Divan::Models::#{name.to_s.split('_').map{|str| "#{str[0..0].upcase}#{str[1..-1]}" }}"
83
+ end
84
+
85
+ def build_model_class
86
+ Divan::Base.class_eval "class #{model_class_full_name} < Divan::Base ; end"
87
+ model_class.database = self
88
+ model_class.top_level_model!
89
+ end
90
+
91
+ def self.model_class(name)
92
+ eval "Divan::Models::#{name.to_s.split('_').map{|str| "#{str[0..0].upcase}#{str[1..-1]}" }}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,126 @@
1
+ module Divan
2
+ module Models
3
+ class Base
4
+
5
+ def save
6
+ self.class.execute_before_validate_callback(self) or return false
7
+
8
+ validate or return false
9
+
10
+ self.class.execute_after_validate_callback(self) or return false
11
+ self.class.execute_before_save_callback(self) or return false
12
+
13
+ execute_save
14
+
15
+ self.class.execute_after_save_callback(self)
16
+ @last_request
17
+ end
18
+
19
+ def delete
20
+ begin
21
+ @last_request = database.client[current_document_path].delete
22
+ rescue RestClient::ResourceNotFound
23
+ @last_request = nil
24
+ return @last_request
25
+ end
26
+ @rev = JSON.parse(@last_request, :symbolize_names => true )[:rev]
27
+ @last_request
28
+ end
29
+
30
+ def revision_ids
31
+ begin
32
+ @last_request = database.client[document_path :revs_info => true].get
33
+ JSON.parse(@last_request, :symbolize_names => true )[:_revs_info].find_all{ |hash| hash[:status] == 'available' }.map{ |hash| hash[:rev] }
34
+ rescue RestClient::ResourceNotFound
35
+ return []
36
+ end
37
+ end
38
+
39
+ def revision(index)
40
+ revision!(index)
41
+ rescue Divan::Divan::DocumentRevisionNotAvailable
42
+ nil
43
+ end
44
+
45
+ def revision!(index)
46
+ r = revision_ids.find{ |rev| rev[0..1].to_i == index}
47
+ r.nil? and raise Divan::Divan::DocumentRevisionNotAvailable.new(self), "Revision with index #{index} missing"
48
+ return self if r == @rev
49
+ self.class.find @id, :rev => r
50
+ end
51
+
52
+ protected
53
+
54
+ def execute_save
55
+ begin
56
+ save_attrs = @attributes.clone
57
+ save_attrs[:"_rev"] = @rev if @rev
58
+ @last_request = database.client[current_document_path].put save_attrs.to_json
59
+ @rev = JSON.parse(@last_request, :symbolize_names => true )[:rev]
60
+ rescue RestClient::Conflict
61
+ raise Divan::DocumentConflict.new(self), "Update race conflict"
62
+ end
63
+ end
64
+
65
+ class << self
66
+ def find(id, params = {})
67
+ begin
68
+ last_request = database.client[Divan::Utils.formatted_path id, params].get
69
+ rescue RestClient::ResourceNotFound
70
+ return nil
71
+ end
72
+ attributes = JSON.parse last_request, :symbolize_names => true
73
+ obj = self.new attributes
74
+ obj.last_request = last_request
75
+ obj
76
+ end
77
+
78
+ def find_all(params=nil)
79
+ query_view :all, params
80
+ end
81
+
82
+ def all
83
+ find_all
84
+ end
85
+
86
+ def delete_all(params = nil)
87
+ to_be_deleted = find_all(params).map do |object|
88
+ {:_id => object.id, :_rev => object.rev, :_deleted => true }
89
+ end
90
+ payload = { :docs => to_be_deleted }.to_json
91
+ database.client['_bulk_docs'].post payload, :content_type => :json, :accept => :json
92
+ to_be_deleted.size
93
+ end
94
+
95
+ def create(opts = {})
96
+ raise ArgumentError if( !opts.is_a?(Hash) && !opts.is_a?(Array) )
97
+ if opts.is_a? Hash
98
+ single_create opts
99
+ else
100
+ bulk_create opts
101
+ end
102
+ end
103
+
104
+ protected
105
+
106
+ def single_create(opts = {})
107
+ obj = self.new(opts)
108
+ obj.save
109
+ obj
110
+ end
111
+
112
+ def bulk_create(opts)
113
+ payload = { :docs => opts.map do |params|
114
+ params = params.clone
115
+ params[:_id] = params.delete(:id) || Divan::Utils.uuid
116
+ params[type_field.to_sym] = type_name unless top_level_model?
117
+ params
118
+ end }
119
+ last_request = database.client['_bulk_docs'].post( payload.to_json, :content_type => :json, :accept => :json )
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,18 @@
1
+ module Divan
2
+ module Utils
3
+ def self.uuid
4
+ values = [ rand(0x0010000), rand(0x0010000), rand(0x0010000), rand(0x0010000),
5
+ rand(0x0010000), rand(0x1000000), rand(0x1000000) ]
6
+ "%04x%04x%04x%04x%04x%06x%06x" % values
7
+ end
8
+
9
+ def self.formatted_path(path = nil, opts = {})
10
+ if opts.empty?
11
+ CGI.escape path.to_s
12
+ else
13
+ formatted_opts = opts.map{|k,v| "#{CGI.escape k.to_s}=#{URI.encode v.to_s}"}.join('&')
14
+ "#{path.to_s}?#{formatted_opts}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require "#{File.dirname(__FILE__)}/../init.rb"
3
+ require 'test/unit'
@@ -0,0 +1,198 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper.rb"
2
+
3
+ class InvalidatedModel < Divan::Models::ProofOfConcept
4
+ before_validate :indiferent_callback
5
+ after_validate lambda{ |obj| obj != nil }
6
+ after_validate :invalidate
7
+
8
+ def indiferent_callback
9
+ true
10
+ end
11
+
12
+ def invalidate
13
+ false
14
+ end
15
+ end
16
+
17
+ class ViewedModel < Divan::Models::ProofOfConcept
18
+ view_by :value
19
+ view_by :mod
20
+ end
21
+
22
+ class ProofOfConcept < Divan::Models::ProofOfConcept
23
+ property :first_name
24
+ end
25
+
26
+ class TestDivan < Test::Unit::TestCase
27
+ def test_dynamic_model
28
+ m = Divan::Model(:teste)
29
+ assert m.class, Divan::Models::Teste
30
+ assert m.database.name, 'test'
31
+ assert m.database.host, '127.0.0.1'
32
+ assert m.database.port, 12345
33
+ assert m.database.user, 'admin'
34
+ assert m.database.password, 'top1secret2pass'
35
+ end
36
+
37
+ def test_get_database_stats
38
+ database = Divan[:proof_of_concept]
39
+ assert_equal database.stats[:db_name], 'proof_of_concept'
40
+ end
41
+
42
+ def test_create_and_delete_database
43
+ database = Divan::Database.new :created_test_database, 'host' => 'http://127.0.0.1', 'database' => 'test_database'
44
+ delete_lambda = lambda{
45
+ assert database.delete['ok']
46
+ assert !database.exists?
47
+ }
48
+ create_lambda = lambda{
49
+ assert database.create['ok']
50
+ assert_equal database.stats[:db_name], 'test_database'
51
+ }
52
+ delete_lambda.call if database.exists?
53
+ create_lambda.call
54
+ delete_lambda.call
55
+ end
56
+
57
+ def test_database_not_found
58
+ database = Divan::Database.new :missing_database, 'host' => 'http://localhost', 'database' => 'mising_database'
59
+ assert_raise(Divan::DatabaseNotFound){ database.stats }
60
+ assert_raise(Divan::DatabaseNotFound){ database.delete }
61
+ end
62
+
63
+ def test_database_already_created
64
+ database = Divan::Database.new :already_created, 'host' => 'http://localhost', 'database' => 'already_created'
65
+ assert database.create['ok']
66
+ assert_raise(Divan::DatabaseAlreadyCreated){ database.create }
67
+ assert database.delete['ok'] # Only to ensure that database is deleted after this test
68
+ end
69
+
70
+ def test_saving_and_retrieving_simple_document_should_work
71
+ object = ProofOfConcept.new :simple_param => 'Working well!',
72
+ :hashed_params => { :is_a => 'Hash', :hash_size => 2 }
73
+ assert object.save
74
+ retrieved_object = ProofOfConcept.find object.id
75
+ assert retrieved_object
76
+ assert retrieved_object.rev
77
+ assert_equal object.id, retrieved_object.id
78
+ assert_equal object.attributes, retrieved_object.attributes
79
+ assert retrieved_object.delete['ok']
80
+ assert_not_equal object.rev, retrieved_object.rev
81
+ end
82
+
83
+ def test_retrieving_non_existent_document_should_return_nil
84
+ obj = ProofOfConcept.find '0'*32 # Probably this uuid don't exists in database
85
+ assert_nil obj
86
+ end
87
+
88
+ def test_updating_document
89
+ object = ProofOfConcept.new
90
+ object[:hashed_params] = {:is_a => 'Hash', :hash_size => 2}
91
+ object[:simple_param] = 'Working well!'
92
+ assert object.save
93
+ retrieved_object = ProofOfConcept.find object.id
94
+ assert retrieved_object
95
+ retrieved_object[:updated_attrib] = 'New attribute!'
96
+ assert retrieved_object.save['ok']
97
+ object[:lost_race] = 'I\'ll fail!'
98
+ assert_raise(Divan::DocumentConflict){ object.save }
99
+ end
100
+
101
+ def test_retrieving_deleted_object
102
+ object = ProofOfConcept.new
103
+ object[:hashed_params] = {:is_a => 'Hash', :hash_size => 2}
104
+ object[:simple_param] = 'Working well!'
105
+ assert object.save
106
+ assert object.delete
107
+ retrieved_object = ProofOfConcept.find object.id
108
+ assert_nil retrieved_object
109
+ end
110
+
111
+ def test_deleting_document_twice
112
+ object = ProofOfConcept.new
113
+ object[:hashed_params] = {:is_a => 'Hash', :hash_size => 2}
114
+ object[:simple_param] = 'Working well!'
115
+ assert object.save
116
+ assert object.delete
117
+ assert_nil object.delete
118
+ end
119
+
120
+ def test_save_deleted_document
121
+ object = ProofOfConcept.new
122
+ object[:hashed_params] = {:is_a => 'Hash', :hash_size => 2}
123
+ object[:simple_param] = 'Working well!'
124
+ assert object.save
125
+ assert object.delete
126
+ assert object.save
127
+ assert object.delete
128
+ end
129
+
130
+ def test_delete_all_from_database
131
+ assert ProofOfConcept.delete_all
132
+ 10.times do |n|
133
+ assert ProofOfConcept.new( :value => n ).save
134
+ end
135
+ # assert_equal Divan[:proof_of_concept].views.count, 5
136
+ assert Divan[:proof_of_concept].create_views
137
+ assert_equal ProofOfConcept.delete_all(:limit => 6), 6
138
+ assert_equal ProofOfConcept.all.first.class, ProofOfConcept
139
+ assert_equal ProofOfConcept.delete_all, 4
140
+ assert ProofOfConcept.find('_design/proof_of_concept')
141
+ assert_equal ProofOfConcept.all.count, 0
142
+ end
143
+
144
+ def test_bulk_create
145
+ assert ProofOfConcept.delete_all
146
+ params = 10.times.map do |n|
147
+ {:number => n, :double => 2*n}
148
+ end
149
+ assert ProofOfConcept.create params
150
+ assert_equal ProofOfConcept.delete_all, 10
151
+ end
152
+
153
+ def test_perform_view_by_query
154
+ assert ViewedModel.delete_all
155
+ assert Divan[:proof_of_concept].create_views
156
+ params = 10.times.map do |n|
157
+ {:mod => (n%2), :value => "#{n} mod 2"}
158
+ end
159
+ assert ViewedModel.create params
160
+ obj = ViewedModel.by_value '5 mod 2'
161
+ assert obj
162
+ assert_equal obj.mod, 1
163
+ assert_equal ViewedModel.all_by_mod(0).count, 5
164
+ assert_equal ViewedModel.find_all.count, 10
165
+ assert_equal ViewedModel.delete_all, 10
166
+ end
167
+
168
+ def test_before_validate_callback_avoids_save
169
+ object = InvalidatedModel.new
170
+ assert !object.save
171
+ assert_nil object.rev
172
+ end
173
+
174
+ def test_dynamic_access_to_attributes
175
+ object = ProofOfConcept.new :dynamic_attribute => 'Working'
176
+ assert object.dynamic_attribute, 'Working'
177
+ assert_equal( (object.dynamic_setter = "Well"), 'Well')
178
+ assert_equal object.dynamic_setter, 'Well'
179
+ end
180
+
181
+ def test_setting_properties
182
+ object = ProofOfConcept.new
183
+ assert_nil object.first_name
184
+ assert_raise(NoMethodError){ object.last_name }
185
+ end
186
+
187
+ def test_top_level_model
188
+ Divan::Models::ProofOfConcept.delete_all
189
+ ProofOfConcept.create :test => 123
190
+ Divan::Models::ProofOfConcept.create :test => 456
191
+ ViewedModel.create :test => 789
192
+ assert_equal Divan::Models::ProofOfConcept.find_all.count, 3
193
+ assert_equal ProofOfConcept.find_all.count, 3
194
+ assert_equal ViewedModel.find_all.count, 1
195
+ assert_equal ViewedModel.delete_all, 1
196
+ assert_equal ProofOfConcept.delete_all, 2
197
+ end
198
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: divan
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Dalton Pinto
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-18 00:00:00 -02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ This is a very simple CouchDB client that have few dependencies.
24
+ This client has a lot of interesting features, for example: easy access to CouchDB revisions.
25
+
26
+ email: dalthon@aluno.ita.br
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ files:
34
+ - README
35
+ - Rakefile.rb
36
+ - VERSION
37
+ - init.rb
38
+ - lib/divan.rb
39
+ - lib/divan/base.rb
40
+ - lib/divan/database.rb
41
+ - lib/divan/models/base.rb
42
+ - lib/divan/utils.rb
43
+ - test/test_helper.rb
44
+ - test/unit/divan.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/efqdalton/divan
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A Ruby CouchDB Client for insane people
79
+ test_files:
80
+ - test/test_helper.rb
81
+ - test/unit/divan.rb