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