divan 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/.gitignore +5 -0
- data/README.rdoc +60 -0
- data/VERSION +1 -1
- data/init.rb +4 -1
- data/lib/divan.rb +5 -2
- data/lib/divan/base.rb +44 -25
- data/lib/divan/database.rb +8 -0
- data/lib/divan/models/base.rb +50 -8
- data/lib/divan/models/revision.rb +62 -0
- data/lib/divan/utils.rb +5 -0
- data/test/unit/divan.rb +64 -1
- metadata +8 -6
- data/README +0 -3
data/README.rdoc
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
== Divan, are you insane?
|
2
|
+
|
3
|
+
* Do you need to have CouchDB <b>easy access to revisions</b>?
|
4
|
+
* *Sometimes* do you need to use just <b>one kind of document per database</b>? And sometimes don't?
|
5
|
+
* Do you need to use CouchDB <b>without care about activesupport</b> dependencies?
|
6
|
+
* Do you need to access <b>HTTP headers</b> of the request that returned your document object?
|
7
|
+
|
8
|
+
So you are *insane*! Divan is a comfortable place for you to rest.
|
9
|
+
|
10
|
+
== Look how it's easy!
|
11
|
+
|
12
|
+
# Model class
|
13
|
+
class InsanePerson < Divan::Models::InsanePerson
|
14
|
+
view_by :name
|
15
|
+
view_by :doctor
|
16
|
+
end
|
17
|
+
|
18
|
+
# Simple Usage
|
19
|
+
patient = InsanePeople.new :name => 'Hannibal', :doctor => 'House'
|
20
|
+
patient.problems = ['Sleepness', 'Headache', 'Alucinations']
|
21
|
+
patient.save
|
22
|
+
|
23
|
+
# Acessing HTTP header
|
24
|
+
patient.last_request.headers[:content_type]
|
25
|
+
|
26
|
+
# Easy accesso to revisions
|
27
|
+
patient.alive = true
|
28
|
+
patient.ttl = 7
|
29
|
+
10.times.do
|
30
|
+
if patient.ttl > 0
|
31
|
+
patient.ttl -= 1
|
32
|
+
else
|
33
|
+
patient.alive = false
|
34
|
+
break
|
35
|
+
end
|
36
|
+
patient.save
|
37
|
+
end
|
38
|
+
patient.revision(2).rollback # Be carefull, he's back!
|
39
|
+
|
40
|
+
# Configuration file
|
41
|
+
insane_person:
|
42
|
+
host: http://127.0.0.1
|
43
|
+
port: 5984
|
44
|
+
database: insane_person
|
45
|
+
|
46
|
+
== FAQ
|
47
|
+
|
48
|
+
1. Why are you not using active_model?
|
49
|
+
Because ActiveModel depends on active_support, and sometimes we want a model library that could
|
50
|
+
be used with Rails 2, or any other project could have conflicted dependencies.
|
51
|
+
ActiveModel is a great library, but it'snt a silver bullet for model libraries.
|
52
|
+
2. Who needs access to HTTP headers?
|
53
|
+
Sometimes it's interesting to know exactly the time that each document is retuned by database,
|
54
|
+
you can do it calling method last_request in each document.
|
55
|
+
3. Why dont you use CouchRest?
|
56
|
+
With CouchRest isn't easy to have access to HTTP headers
|
57
|
+
|
58
|
+
== TODO
|
59
|
+
|
60
|
+
A better README, documentation and other details
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/init.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require "#{File.dirname(__FILE__)}/lib/divan.rb"
|
2
2
|
|
3
3
|
#Lines below are used for debug purposes only
|
4
4
|
Divan.load_database_configuration 'config/divan_config.yml'
|
5
5
|
|
6
|
+
class ProofOfConcept < Divan::Models::ProofOfConcept
|
7
|
+
end
|
8
|
+
|
6
9
|
class POC < Divan::Models::ProofOfConcept
|
7
10
|
view_by :mod
|
8
11
|
view_by :value
|
data/lib/divan.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
divan_path = File.
|
1
|
+
divan_path = File.dirname(__FILE__)
|
2
2
|
$:.unshift(divan_path) if File.directory?(divan_path) && !$:.include?(divan_path)
|
3
3
|
|
4
4
|
require 'restclient'
|
@@ -64,7 +64,10 @@ module Divan
|
|
64
64
|
attr_reader :new_document, :current_document
|
65
65
|
def initialize(new_document)
|
66
66
|
@new_document = new_document
|
67
|
-
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_document
|
70
|
+
@current_document ||= new_document.class.find new_document.id
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
data/lib/divan/base.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Divan
|
2
2
|
class Base < Models::Base
|
3
|
-
attr_accessor :id, :rev, :attributes, :last_request
|
3
|
+
attr_accessor :id, :rev, :attributes, :meta_attributes, :last_request
|
4
4
|
|
5
5
|
def initialize(opts = {})
|
6
6
|
opts = opts.clone
|
7
7
|
@id = opts.delete(:id) || opts.delete(:_id) || Divan::Utils.uuid
|
8
8
|
@rev = opts.delete(:rev) || opts.delete(:_rev)
|
9
|
+
@meta_attributes = opts.find_all{ |k,v| k.to_s[0..0] == '_' }.inject({}) do |hash, (key, value)|
|
10
|
+
hash[key.to_s[1..-1].to_sym] = opts.delete key
|
11
|
+
hash
|
12
|
+
end
|
9
13
|
@attributes = opts
|
10
14
|
@attributes[self.class.type_field.to_sym] = self.class.type_name unless self.class.top_level_model?
|
11
15
|
self.class.properties.each{ |property| @attributes[property] ||= nil }
|
@@ -66,8 +70,9 @@ module Divan
|
|
66
70
|
strs.delete ''
|
67
71
|
subclass.model_name = strs.map{ |x| x.downcase }.join('_')
|
68
72
|
if database
|
73
|
+
subclass.top_level_model! if database.name == subclass.model_name
|
74
|
+
subclass.define_view_all
|
69
75
|
subclass.database = database
|
70
|
-
subclass.top_level_model! if subclass.database.name == subclass.model_name
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
@@ -87,6 +92,10 @@ module Divan
|
|
87
92
|
@top_level_model ||= false
|
88
93
|
end
|
89
94
|
|
95
|
+
def views
|
96
|
+
@views ||= {}
|
97
|
+
end
|
98
|
+
|
90
99
|
def properties
|
91
100
|
@properties ||= ( superclass.methods.include? :properties ) ? superclass.properties.clone : []
|
92
101
|
end
|
@@ -98,7 +107,6 @@ module Divan
|
|
98
107
|
def database=(database)
|
99
108
|
undefine_views if( !@database.nil? && @database != database )
|
100
109
|
@database = database
|
101
|
-
define_view_all
|
102
110
|
define_views
|
103
111
|
@database
|
104
112
|
end
|
@@ -129,31 +137,31 @@ module Divan
|
|
129
137
|
@views[param.to_sym] = functions
|
130
138
|
end
|
131
139
|
|
132
|
-
def define_view!(param, functions)
|
133
|
-
database.views[model_name] ||= {}
|
134
|
-
database.views[model_name][param.to_sym] = functions
|
135
|
-
end
|
136
|
-
|
137
140
|
def define_view_all
|
138
|
-
if
|
139
|
-
define_view :all, :map => "function(doc){ if(doc._id
|
141
|
+
if top_level_model?
|
142
|
+
define_view :all, :map => "function(doc){ if(doc._id[0] != \"_\"){ emit(null, doc) } }"
|
140
143
|
else
|
141
144
|
define_view :all, :map => "function(doc){ if(doc.#{type_field} == \"#{type_name}\"){ emit(null, doc) } }"
|
142
|
-
end
|
145
|
+
end
|
143
146
|
end
|
144
147
|
|
145
|
-
def query_view(view,
|
146
|
-
|
147
|
-
|
148
|
-
|
148
|
+
def query_view(view, args={})
|
149
|
+
args ||= {}
|
150
|
+
args = args.clone
|
151
|
+
[:key, :startkey, :endkey].each do |k|
|
152
|
+
args[k] = args[k].to_json if args[k]
|
153
|
+
end
|
154
|
+
|
155
|
+
if args[:keys]
|
156
|
+
keys = args.delete(:keys).to_json
|
157
|
+
view_path = Divan::Utils.formatted_path "_design/#{model_name}/_view/#{view}", args
|
158
|
+
last_request = database.client[view_path].post keys
|
149
159
|
else
|
150
|
-
|
151
|
-
|
160
|
+
args[:key] = nil.to_json if args[:key].nil? && args[:startkey].nil? && args[:endkey].nil?
|
161
|
+
view_path = Divan::Utils.formatted_path "_design/#{model_name}/_view/#{view}", args
|
162
|
+
last_request = database.client[view_path].get
|
152
163
|
end
|
153
164
|
|
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
165
|
results = JSON.parse last_request, :symbolize_names => true
|
158
166
|
results[:rows].map do |row|
|
159
167
|
obj = self.new row[:value]
|
@@ -167,11 +175,22 @@ module Divan
|
|
167
175
|
def view_by(param)
|
168
176
|
@view_by_params ||= []
|
169
177
|
@view_by_params << param
|
170
|
-
|
178
|
+
if top_level_model?
|
179
|
+
define_view "by_#{param}", :map => "function(doc) { emit(doc.#{param}, doc) }"
|
180
|
+
else
|
181
|
+
define_view "by_#{param}", :map => "function(doc) { if(doc.#{type_field} == \"#{type_name}\"){ emit(doc.#{param}, doc) } }"
|
182
|
+
end
|
171
183
|
eval <<-end_txt
|
172
184
|
class << self
|
173
|
-
def all_by_#{param}(
|
174
|
-
|
185
|
+
def all_by_#{param}(args=nil)
|
186
|
+
unless args.is_a? Hash
|
187
|
+
if args.is_a? Array
|
188
|
+
args = { :keys => args }
|
189
|
+
else
|
190
|
+
args = { :key => args }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
query_view :by_#{param}, args
|
175
194
|
end
|
176
195
|
|
177
196
|
def by_#{param}(key)
|
@@ -179,11 +198,11 @@ module Divan
|
|
179
198
|
end
|
180
199
|
end
|
181
200
|
end_txt
|
201
|
+
define_views
|
182
202
|
end
|
183
203
|
|
184
204
|
def define_views
|
185
|
-
database.views[model_name]
|
186
|
-
database.views[model_name].merge! @views if @views
|
205
|
+
database.views[model_name] = views
|
187
206
|
end
|
188
207
|
|
189
208
|
def undefine_views
|
data/lib/divan/database.rb
CHANGED
@@ -30,6 +30,14 @@ module Divan
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
def compact
|
34
|
+
begin
|
35
|
+
client['_compact'].post Hash.new, :'content-type' => 'application/json'
|
36
|
+
rescue RestClient::ResourceNotFound
|
37
|
+
raise Divan::DatabaseNotFound.new(self), "Database was not found"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
def create
|
34
42
|
begin
|
35
43
|
client.put Hash.new
|
data/lib/divan/models/base.rb
CHANGED
@@ -2,7 +2,7 @@ module Divan
|
|
2
2
|
module Models
|
3
3
|
class Base
|
4
4
|
|
5
|
-
def save
|
5
|
+
def save(strategy=nil, &block)
|
6
6
|
self.class.execute_before_validate_callback(self) or return false
|
7
7
|
|
8
8
|
validate or return false
|
@@ -10,7 +10,7 @@ module Divan
|
|
10
10
|
self.class.execute_after_validate_callback(self) or return false
|
11
11
|
self.class.execute_before_save_callback(self) or return false
|
12
12
|
|
13
|
-
execute_save
|
13
|
+
execute_save strategy, &block
|
14
14
|
|
15
15
|
self.class.execute_after_save_callback(self)
|
16
16
|
@last_request
|
@@ -38,27 +38,58 @@ module Divan
|
|
38
38
|
|
39
39
|
def revision(index)
|
40
40
|
revision!(index)
|
41
|
-
rescue Divan::
|
41
|
+
rescue Divan::DocumentRevisionMissing
|
42
42
|
nil
|
43
43
|
end
|
44
44
|
|
45
45
|
def revision!(index)
|
46
|
-
r = revision_ids.find{ |rev| rev[0..1].to_i == index}
|
47
|
-
r.nil? and raise Divan::
|
46
|
+
r = revision_ids.find{ |rev| rev[0..1].to_i == index }
|
47
|
+
r.nil? and raise Divan::DocumentRevisionMissing.new(self), "Revision with index #{index} missing"
|
48
48
|
return self if r == @rev
|
49
49
|
self.class.find @id, :rev => r
|
50
50
|
end
|
51
51
|
|
52
52
|
protected
|
53
53
|
|
54
|
-
def execute_save
|
54
|
+
def execute_save(strategy=nil, &block)
|
55
|
+
previous_request = @last_request
|
55
56
|
begin
|
56
57
|
save_attrs = @attributes.clone
|
57
58
|
save_attrs[:"_rev"] = @rev if @rev
|
59
|
+
save_attrs.delete(:id)
|
60
|
+
save_attrs.delete(:_id)
|
58
61
|
@last_request = database.client[current_document_path].put save_attrs.to_json
|
59
62
|
@rev = JSON.parse(@last_request, :symbolize_names => true )[:rev]
|
60
63
|
rescue RestClient::Conflict
|
61
|
-
|
64
|
+
if strategy && self.class.strategies[strategy.to_sym]
|
65
|
+
run_strategy_in_block &self.class.strategies[strategy.to_sym]
|
66
|
+
elsif block_given?
|
67
|
+
run_strategy_in_block &block
|
68
|
+
else
|
69
|
+
raise Divan::DocumentConflict.new(self), "Update race conflict"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def run_strategy_in_function(strategy)
|
75
|
+
conflict_doc = self.class.find(id)
|
76
|
+
@rev = conflict_doc.rev
|
77
|
+
if send( "#{strategy}_strategy", self.class.find(id) )
|
78
|
+
execute_save strategy
|
79
|
+
else
|
80
|
+
@attributes = conflict_doc.attributes
|
81
|
+
@last_request = conflict_doc.last_request
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def run_strategy_in_block(&block)
|
86
|
+
conflict_doc = self.class.find(id)
|
87
|
+
@rev = conflict_doc.rev
|
88
|
+
if yield(self, conflict_doc)
|
89
|
+
self.execute_save(nil, &block)
|
90
|
+
else
|
91
|
+
@attributes = conflict_doc.attributes
|
92
|
+
@last_request = conflict_doc.last_request
|
62
93
|
end
|
63
94
|
end
|
64
95
|
|
@@ -101,8 +132,16 @@ module Divan
|
|
101
132
|
end
|
102
133
|
end
|
103
134
|
|
135
|
+
def strategies
|
136
|
+
@strategies ||= superclass.respond_to?(:strategies) ? superclass.strategies.clone : {}
|
137
|
+
end
|
138
|
+
|
104
139
|
protected
|
105
140
|
|
141
|
+
def strategy(name, &block)
|
142
|
+
strategies[name.to_sym] = block
|
143
|
+
end
|
144
|
+
|
106
145
|
def single_create(opts = {})
|
107
146
|
obj = self.new(opts)
|
108
147
|
obj.save
|
@@ -118,9 +157,12 @@ module Divan
|
|
118
157
|
end }
|
119
158
|
last_request = database.client['_bulk_docs'].post( payload.to_json, :content_type => :json, :accept => :json )
|
120
159
|
end
|
121
|
-
|
122
160
|
end
|
123
161
|
|
162
|
+
strategy(:first_wins) { |here, in_database| false }
|
163
|
+
strategy(:last_wins) { |here, in_database| true }
|
164
|
+
strategy(:merge) { |here, in_database| here.attributes = in_database.attributes.merge here.attributes }
|
165
|
+
|
124
166
|
end
|
125
167
|
end
|
126
168
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Divan
|
2
|
+
module Models
|
3
|
+
class Revision < Models::Base
|
4
|
+
attr_reader :revisioned_doc, :revisioned_at, :revisioned_by
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@revisioned_doc = @meta_attributes.delete :revision_of
|
9
|
+
@revisioned_at = @meta_attributes.delete :revisioned_at
|
10
|
+
@revisioned_by = @meta_attributes.delete :revisioned_by
|
11
|
+
end
|
12
|
+
|
13
|
+
def rollback
|
14
|
+
revisioned_doc.attributes = @attributes.clone
|
15
|
+
revisioned_doc.save :last_wins
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_reader :revisioned_class
|
20
|
+
|
21
|
+
def create_by_revisioned_doc(rev_doc, rev_by = nil)
|
22
|
+
self.new rev_doc.params
|
23
|
+
self.id = URI.encode "_revision/#{rev_doc.rev}"
|
24
|
+
@revisioned_doc = rev_doc
|
25
|
+
# parsed_time = Date._parse rev_doc.last_request.headers[:date]
|
26
|
+
# @revisioned_at = Time.gm *[:year, :mon, :mday, :hour, :min, :sec].collect{ |k| parsed_time[k] }
|
27
|
+
@revisioned_at = Divan::Utils.parse_time rev_doc.last_request.headers[:date]
|
28
|
+
@revisioned_by = rev_by
|
29
|
+
self.save
|
30
|
+
end
|
31
|
+
|
32
|
+
def revisioned_class=(rev_class)
|
33
|
+
@revisioned_class = rev_class
|
34
|
+
self.database = rev_class.database
|
35
|
+
self.model_name = rev_class.model_name + '_revision'
|
36
|
+
end
|
37
|
+
alias :revision_of :'revisioned_class='
|
38
|
+
|
39
|
+
def type_name
|
40
|
+
revisioned_class.type_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def type_field
|
44
|
+
revisioned_class.type_field
|
45
|
+
end
|
46
|
+
|
47
|
+
def top_level_model!(*args)
|
48
|
+
raise "Can't modify revision top level"
|
49
|
+
end
|
50
|
+
|
51
|
+
def top_level_model?
|
52
|
+
revisioned_class.top_level_model?
|
53
|
+
end
|
54
|
+
|
55
|
+
def inherithed(subclass)
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/divan/utils.rb
CHANGED
@@ -6,6 +6,11 @@ module Divan
|
|
6
6
|
"%04x%04x%04x%04x%04x%06x%06x" % values
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.parse_time(string)
|
10
|
+
parsed_time = Date._parse string
|
11
|
+
Time.gm *[:year, :mon, :mday, :hour, :min, :sec].collect{ |k| parsed_time[k] }
|
12
|
+
end
|
13
|
+
|
9
14
|
def self.formatted_path(path = nil, opts = {})
|
10
15
|
if opts.empty?
|
11
16
|
CGI.escape path.to_s
|
data/test/unit/divan.rb
CHANGED
@@ -20,9 +20,14 @@ class ViewedModel < Divan::Models::ProofOfConcept
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class ProofOfConcept < Divan::Models::ProofOfConcept
|
23
|
+
strategy(:add_conflicted_field_and_keep_this) { |here, in_db| here.conflict = "conflicted!"}
|
24
|
+
|
23
25
|
property :first_name
|
24
26
|
end
|
25
27
|
|
28
|
+
Divan::Models::ProofOfConcept.database.create unless Divan::Models::ProofOfConcept.database.exists?
|
29
|
+
Divan::Models::ProofOfConcept.database.create_views
|
30
|
+
|
26
31
|
class TestDivan < Test::Unit::TestCase
|
27
32
|
def test_dynamic_model
|
28
33
|
m = Divan::Model(:teste)
|
@@ -132,7 +137,6 @@ class TestDivan < Test::Unit::TestCase
|
|
132
137
|
10.times do |n|
|
133
138
|
assert ProofOfConcept.new( :value => n ).save
|
134
139
|
end
|
135
|
-
# assert_equal Divan[:proof_of_concept].views.count, 5
|
136
140
|
assert Divan[:proof_of_concept].create_views
|
137
141
|
assert_equal ProofOfConcept.delete_all(:limit => 6), 6
|
138
142
|
assert_equal ProofOfConcept.all.first.class, ProofOfConcept
|
@@ -142,6 +146,7 @@ class TestDivan < Test::Unit::TestCase
|
|
142
146
|
end
|
143
147
|
|
144
148
|
def test_bulk_create
|
149
|
+
assert ProofOfConcept.top_level_model?
|
145
150
|
assert ProofOfConcept.delete_all
|
146
151
|
params = 10.times.map do |n|
|
147
152
|
{:number => n, :double => 2*n}
|
@@ -195,4 +200,62 @@ class TestDivan < Test::Unit::TestCase
|
|
195
200
|
assert_equal ViewedModel.delete_all, 1
|
196
201
|
assert_equal ProofOfConcept.delete_all, 2
|
197
202
|
end
|
203
|
+
|
204
|
+
def test_first_wins_save_strategy
|
205
|
+
first = ProofOfConcept.create :test => 123
|
206
|
+
last = ProofOfConcept.new :id => first.id, :test => 321
|
207
|
+
assert last.save(:first_wins)
|
208
|
+
assert_equal ProofOfConcept.find(first.id).test, 123
|
209
|
+
assert_equal last.test, 123
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_last_wins_save_strategy
|
213
|
+
first = ProofOfConcept.create :test => 123
|
214
|
+
last = ProofOfConcept.new :id => first.id, :test => 321
|
215
|
+
assert last.save(:last_wins)
|
216
|
+
assert_equal ProofOfConcept.find(first.id).test, 321
|
217
|
+
assert_equal last.test, 321
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_merge_save_strategy
|
221
|
+
first = ProofOfConcept.create :test_one => 1, :test => 'Working'
|
222
|
+
last = ProofOfConcept.new :id => first.id, :test_one => 123, :test_two => 321
|
223
|
+
expected_attributes = first.attributes.merge last.attributes
|
224
|
+
assert last.save(:merge)
|
225
|
+
assert_equal ProofOfConcept.find(first.id).attributes, expected_attributes
|
226
|
+
assert_equal last.attributes, expected_attributes
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_custom_save_strategy
|
230
|
+
first = ProofOfConcept.create :amount => 100
|
231
|
+
last = ProofOfConcept.new :id => first.id, :amount => 200
|
232
|
+
expected_attributes = first.attributes.merge last.attributes
|
233
|
+
assert last.save(){ |here, in_database| here.amount += in_database.amount }
|
234
|
+
assert_equal ProofOfConcept.find(first.id).amount, 300
|
235
|
+
assert_equal last.amount, 300
|
236
|
+
assert first.save(){ |here, in_database| here.amount > in_database.amount }
|
237
|
+
assert_equal ProofOfConcept.find(first.id).amount, 300
|
238
|
+
assert_equal first.amount, 300
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_finding_an_older_revision
|
242
|
+
older = ProofOfConcept.create :save_counts => 0
|
243
|
+
newer = ProofOfConcept.find older.id
|
244
|
+
newer.save_counts += 1
|
245
|
+
assert newer.save
|
246
|
+
older_back = ProofOfConcept.find older.id, :rev => older.rev
|
247
|
+
assert older_back
|
248
|
+
assert_equal older_back.rev, older.rev
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_named_custom_strategy
|
252
|
+
first = ProofOfConcept.create :test => 123
|
253
|
+
last = ProofOfConcept.new :id => first.id, :test => 321
|
254
|
+
assert last.save(:add_conflicted_field_and_keep_this)
|
255
|
+
assert_equal ProofOfConcept.find(first.id).test, 321
|
256
|
+
assert_equal last.test, 321
|
257
|
+
assert_equal ProofOfConcept.find(first.id).conflict, 'conflicted!'
|
258
|
+
viewed = ViewedModel.new :id => first.id, :will => 'raise errors'
|
259
|
+
assert_raise(Divan::DocumentConflict){ viewed.save }
|
260
|
+
end
|
198
261
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: divan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Dalton Pinto
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-25 00:00:00 -02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -56,9 +56,10 @@ executables: []
|
|
56
56
|
extensions: []
|
57
57
|
|
58
58
|
extra_rdoc_files:
|
59
|
-
- README
|
59
|
+
- README.rdoc
|
60
60
|
files:
|
61
|
-
-
|
61
|
+
- .gitignore
|
62
|
+
- README.rdoc
|
62
63
|
- Rakefile.rb
|
63
64
|
- VERSION
|
64
65
|
- init.rb
|
@@ -66,6 +67,7 @@ files:
|
|
66
67
|
- lib/divan/base.rb
|
67
68
|
- lib/divan/database.rb
|
68
69
|
- lib/divan/models/base.rb
|
70
|
+
- lib/divan/models/revision.rb
|
69
71
|
- lib/divan/utils.rb
|
70
72
|
- test/test_helper.rb
|
71
73
|
- test/unit/divan.rb
|
data/README
DELETED