divan 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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