mattly-exegesis 0.0.10 → 0.2.0
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/LICENSE +22 -0
- data/README.rdoc +51 -7
- data/VERSION.yml +2 -2
- data/lib/exegesis.rb +22 -29
- data/lib/exegesis/database.rb +109 -0
- data/lib/exegesis/design.rb +123 -52
- data/lib/exegesis/document.rb +72 -132
- data/lib/exegesis/model.rb +142 -0
- data/lib/exegesis/server.rb +28 -0
- data/lib/exegesis/utils/http.rb +38 -0
- data/lib/monkeypatches/time.rb +5 -0
- data/test/database_test.rb +161 -0
- data/test/design_test.rb +154 -74
- data/test/document_test.rb +159 -0
- data/test/fixtures/designs/tags/views/by_tag/map.js +8 -0
- data/test/fixtures/designs/tags/views/by_tag/reduce.js +3 -0
- data/test/http_test.rb +79 -0
- data/test/model_test.rb +230 -0
- data/test/server_test.rb +26 -0
- data/test/test_helper.rb +12 -8
- metadata +25 -12
- data/lib/exegesis/design/design_docs.rb +0 -92
- data/test/design_doc_test.rb +0 -120
- data/test/document_class_definitions_test.rb +0 -284
- data/test/document_instance_methods_test.rb +0 -40
- data/test/exegesis_test.rb +0 -28
data/lib/exegesis/document.rb
CHANGED
|
@@ -1,153 +1,93 @@
|
|
|
1
1
|
module Exegesis
|
|
2
|
-
|
|
2
|
+
module Document
|
|
3
3
|
|
|
4
|
-
def self.
|
|
5
|
-
|
|
4
|
+
def self.included base
|
|
5
|
+
base.send :include, Exegesis::Model
|
|
6
|
+
base.extend ClassMethods
|
|
7
|
+
base.send :include, InstanceMethods
|
|
8
|
+
base.send :attr_accessor, :database
|
|
6
9
|
end
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
opts = if attrs.last.is_a?(Hash)
|
|
14
|
-
attrs.pop
|
|
15
|
-
else
|
|
16
|
-
{}
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
[attrs].flatten.each do |attrib|
|
|
20
|
-
attrib = "#{attrib}"
|
|
21
|
-
if opts.has_key?(:writer)
|
|
22
|
-
if opts[:writer]
|
|
23
|
-
define_method("#{attrib}=") {|val| self[attrib] = opts[:writer].call(val) }
|
|
24
|
-
end
|
|
25
|
-
else
|
|
26
|
-
define_method("#{attrib}=") {|val| self[attrib] = val }
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def timestamps!
|
|
13
|
+
define_method :set_timestamps do
|
|
14
|
+
@attributes['updated_at'] = Time.now
|
|
15
|
+
@attributes['created_at'] ||= Time.now
|
|
27
16
|
end
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
define_method(attrib) do
|
|
38
|
-
self[attrib] = if self[attrib].is_a?(Array)
|
|
39
|
-
self[attrib].map {|val| cast opts[:as], val }.compact
|
|
40
|
-
else
|
|
41
|
-
cast opts[:as], self[attrib]
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
17
|
+
expose 'updated_at', :as => Time, :writer => false
|
|
18
|
+
expose 'created_at', :as => Time, :writer => false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def unique_id meth=nil, &block
|
|
22
|
+
if block
|
|
23
|
+
@unique_id_method = block
|
|
24
|
+
elsif meth
|
|
25
|
+
@unique_id_method = meth
|
|
45
26
|
else
|
|
46
|
-
|
|
27
|
+
@unique_id_method ||= nil
|
|
47
28
|
end
|
|
48
29
|
end
|
|
49
30
|
end
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@default ||= superclass.respond_to?(:default) ? superclass.default : {}
|
|
32
|
+
module InstanceMethods
|
|
33
|
+
def initialize hash={}, db=nil
|
|
34
|
+
super hash
|
|
35
|
+
@database = db
|
|
56
36
|
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
define_method :set_timestamps do
|
|
61
|
-
self['updated_at'] = Time.now
|
|
62
|
-
self['created_at'] ||= Time.now
|
|
37
|
+
|
|
38
|
+
def == other
|
|
39
|
+
self.id == other.id
|
|
63
40
|
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def self.unique_id meth
|
|
69
|
-
define_method :set_unique_id do
|
|
70
|
-
self['_id'] = self.send(meth)
|
|
41
|
+
|
|
42
|
+
def id
|
|
43
|
+
@attributes['_id']
|
|
71
44
|
end
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@unique_id_attempt = 0
|
|
84
|
-
begin
|
|
85
|
-
self['_id'] = set_unique_id
|
|
86
|
-
document_save
|
|
87
|
-
rescue RestClient::RequestFailed => e
|
|
88
|
-
@unique_id_attempt += 1
|
|
89
|
-
retry
|
|
45
|
+
|
|
46
|
+
def rev
|
|
47
|
+
@attributes['_rev']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def save
|
|
51
|
+
set_timestamps if respond_to?(:set_timestamps)
|
|
52
|
+
if self.class.unique_id && id.nil?
|
|
53
|
+
save_with_custom_unique_id
|
|
54
|
+
else
|
|
55
|
+
save_document
|
|
90
56
|
end
|
|
91
|
-
else
|
|
92
|
-
document_save
|
|
93
57
|
end
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
self['.kind'] ||= self.class.to_s
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def update_attributes attrs={}
|
|
103
|
-
raise ArgumentError, 'must include a matching _rev attribute' unless rev == attrs.delete('_rev')
|
|
104
|
-
attrs.each_pair do |key, value|
|
|
105
|
-
self.send("#{key}=", value) rescue nil
|
|
106
|
-
attrs.delete(key)
|
|
58
|
+
|
|
59
|
+
def update_attributes attrs={}
|
|
60
|
+
raise ArgumentError, 'must include a matching _rev attribute' unless rev == attrs.delete('_rev')
|
|
61
|
+
super attrs
|
|
62
|
+
save
|
|
107
63
|
end
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
self.class.default.each do |key, value|
|
|
115
|
-
self[key] = value
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def save_document
|
|
68
|
+
raise ArgumentError, "canont save without a database" unless database
|
|
69
|
+
database.save self.attributes
|
|
116
70
|
end
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
71
|
+
|
|
72
|
+
def save_with_custom_unique_id
|
|
73
|
+
attempt = 0
|
|
74
|
+
value = ''
|
|
75
|
+
begin
|
|
76
|
+
@attributes['_id'] = if self.class.unique_id.is_a?(Proc)
|
|
77
|
+
self.class.unique_id.call(self, attempt)
|
|
78
|
+
else
|
|
79
|
+
self.send(self.class.unique_id, attempt)
|
|
80
|
+
end
|
|
81
|
+
save_document
|
|
82
|
+
rescue RestClient::RequestFailed => e
|
|
83
|
+
oldvalue = value
|
|
84
|
+
value = @attributes['_id']
|
|
85
|
+
raise RestClient::RequestFailed if oldvalue == value || attempt > 100
|
|
86
|
+
attempt += 1
|
|
87
|
+
retry
|
|
126
88
|
end
|
|
127
|
-
elsif as.is_a?(Class)
|
|
128
|
-
as
|
|
129
|
-
else
|
|
130
|
-
Exegesis.document_classes[nil] # whatever the default is? Hell idk.
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
with = klass == Time ? :parse : :new
|
|
134
|
-
casted = klass.send with, value
|
|
135
|
-
casted.parent = self if casted.respond_to?(:parent)
|
|
136
|
-
casted
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def load_reference ids
|
|
140
|
-
raise ArgumentError, "a database is required for loading a reference" unless database || (parent && parent.database)
|
|
141
|
-
if ids.is_a?(Array)
|
|
142
|
-
ids.map {|val| Exegesis::Document.instantiate((database || parent && parent.database).get(val)) }
|
|
143
|
-
else
|
|
144
|
-
Exegesis::Document.instantiate((database || parent && parent.database).get(ids))
|
|
145
89
|
end
|
|
146
90
|
end
|
|
147
91
|
|
|
148
92
|
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# $:.unshift File.dirname(__FILE__)
|
|
152
|
-
# require 'document/annotated_reference'
|
|
153
|
-
# require 'document/referencing'
|
|
93
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
module Exegesis
|
|
2
|
+
module Model
|
|
3
|
+
|
|
4
|
+
def self.included base
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
base.send :include, InstanceMethods
|
|
7
|
+
Exegesis.model_classes[base.name] = base
|
|
8
|
+
base.send :attr_accessor, :attributes, :references, :parent
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def expose *attrs
|
|
13
|
+
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
|
14
|
+
[attrs].flatten.each do |attrib|
|
|
15
|
+
attrib = attrib.to_s
|
|
16
|
+
if opts[:writer]
|
|
17
|
+
define_writer(attrib) {|val| @attributes[attrib] = opts[:writer].call(val) }
|
|
18
|
+
elsif !opts.has_key?(:writer)
|
|
19
|
+
define_writer(attrib) {|val| @attributes[attrib] = val }
|
|
20
|
+
end
|
|
21
|
+
if opts[:as] == :reference
|
|
22
|
+
define_reference attrib
|
|
23
|
+
elsif opts[:as]
|
|
24
|
+
define_caster attrib, opts[:as]
|
|
25
|
+
else
|
|
26
|
+
define_method(attrib) { @attributes[attrib] }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# sets a default hash object for the attributes of the instances of the class if an argument given,
|
|
32
|
+
# else retrieves the default
|
|
33
|
+
def default hash=nil
|
|
34
|
+
if hash
|
|
35
|
+
@default = {}
|
|
36
|
+
hash.each {|key, value| @default[key.to_s] = value }
|
|
37
|
+
else
|
|
38
|
+
@default ||= {}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def define_writer attrib, &block
|
|
44
|
+
define_method("#{attrib}=", block)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def define_reference attrib
|
|
48
|
+
define_method(attrib) do |*reload|
|
|
49
|
+
reload = false if reload.empty?
|
|
50
|
+
@references ||= {}
|
|
51
|
+
@references[attrib] = nil if reload
|
|
52
|
+
@references[attrib] ||= load_reference(@attributes[attrib])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def define_caster attrib, as
|
|
57
|
+
define_method(attrib) do
|
|
58
|
+
@attributes[attrib] = if @attributes[attrib].is_a?(Array)
|
|
59
|
+
@attributes[attrib].map {|val| cast as, val }.compact
|
|
60
|
+
else
|
|
61
|
+
cast as, @attributes[attrib]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module InstanceMethods
|
|
69
|
+
def initialize hash={}
|
|
70
|
+
apply_default
|
|
71
|
+
hash.each {|key, value| @attributes[key.to_s] = value }
|
|
72
|
+
@attributes['class'] = self.class.name
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# works like Hash#update on the attributes hash, bypassing any writers
|
|
76
|
+
def update hash={}
|
|
77
|
+
hash.each {|key, value| @attributes[key.to_s] = value }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# update the attributes in the model using writers. If no writer is defined for a given
|
|
81
|
+
# key it will raise NoMethodError
|
|
82
|
+
def update_attributes hash={}
|
|
83
|
+
hash.each do |key, value|
|
|
84
|
+
self.send("#{key}=", value)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# retrieves the attribte
|
|
89
|
+
def [] key
|
|
90
|
+
@attributes[key]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# directly sets the attribute, avoiding any writers or lack thereof.
|
|
94
|
+
def []= key, value
|
|
95
|
+
@attributes[key] = value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# returns the instance's database, or its parents database if it has a parent.
|
|
99
|
+
# If neither, returns false.
|
|
100
|
+
# This is overwritten in classes including Exegesis::Document by an attr_accessor.
|
|
101
|
+
def database
|
|
102
|
+
parent && parent.database
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
def apply_default
|
|
107
|
+
@attributes = self.class.default.dup
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def cast as, value
|
|
111
|
+
return nil if value.nil?
|
|
112
|
+
klass = if as == :given && value.is_a?(Hash)
|
|
113
|
+
Exegesis.model_classes[value['class']]
|
|
114
|
+
elsif as.is_a?(Class)
|
|
115
|
+
as
|
|
116
|
+
else
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
casted = if klass.nil?
|
|
121
|
+
value # if no class, just return the value
|
|
122
|
+
elsif klass == Time # Time is a special case; the ONLY special case.
|
|
123
|
+
Time.parse value
|
|
124
|
+
else
|
|
125
|
+
klass.new value
|
|
126
|
+
end
|
|
127
|
+
casted.parent = self if casted.respond_to?(:parent)
|
|
128
|
+
casted
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def load_reference ids
|
|
132
|
+
raise ArgumentError, "a database is required for loading a reference" unless database
|
|
133
|
+
if ids.is_a?(Array)
|
|
134
|
+
ids.map {|val| database.get(val) }
|
|
135
|
+
else
|
|
136
|
+
database.get(ids)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Exegesis
|
|
2
|
+
class Server
|
|
3
|
+
include Exegesis::Http
|
|
4
|
+
|
|
5
|
+
attr_accessor :uri, :version
|
|
6
|
+
|
|
7
|
+
# Creates a new instance of Exegesis::Server. Defaults to http://localhost:5984 and
|
|
8
|
+
# verifies the existance of the database.
|
|
9
|
+
def initialize address='http://localhost:5984'
|
|
10
|
+
@uri = address
|
|
11
|
+
@version = get(@uri)['version']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# returns an array of all the databases on the server
|
|
15
|
+
def databases
|
|
16
|
+
get "#{@uri}/_all_dbs"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# creates a database with the given name on the server
|
|
20
|
+
def create_database name
|
|
21
|
+
put "#{@uri}/#{name}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def inspect
|
|
25
|
+
"#<Exegesis::Server #{@uri}>"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
module Exegesis
|
|
3
|
+
module Http
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def format_url url, params={}
|
|
7
|
+
if params && !params.empty?
|
|
8
|
+
query = params.map do |key, value|
|
|
9
|
+
value = value.to_json if [:key, :startkey, :endkey, :keys].include?(key)
|
|
10
|
+
"#{key}=#{CGI.escape(value.to_s)}"
|
|
11
|
+
end.join('&')
|
|
12
|
+
url = "#{url}?#{query}"
|
|
13
|
+
end
|
|
14
|
+
url
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def escape_id id
|
|
18
|
+
/^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get url
|
|
22
|
+
JSON.parse(RestClient.get(url), :max_nesting => false)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def post url, body=''
|
|
26
|
+
JSON.parse(RestClient.post(url, (body || '').to_json))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def put url, body=''
|
|
30
|
+
JSON.parse(RestClient.put(url, (body || '').to_json))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def delete url
|
|
34
|
+
JSON.parse(RestClient.delete(url))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class DatabaseTest
|
|
4
|
+
include Exegesis::Database
|
|
5
|
+
end
|
|
6
|
+
class CustomDesignDirDatabaseTest
|
|
7
|
+
include Exegesis::Database
|
|
8
|
+
designs_directory 'app/designs'
|
|
9
|
+
end
|
|
10
|
+
class DatabaseTestDocument
|
|
11
|
+
include Exegesis::Document
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class ExegesisDatabaseTest < Test::Unit::TestCase
|
|
15
|
+
before(:all) do
|
|
16
|
+
@server = Exegesis::Server.new('http://localhost:5984')
|
|
17
|
+
RestClient.delete("#{@server.uri}/exegesis-test") rescue nil
|
|
18
|
+
RestClient.delete("#{@server.uri}/exegesis-test-nonexistent") rescue nil
|
|
19
|
+
RestClient.put("#{@server.uri}/exegesis-test", '')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "initializing" do
|
|
23
|
+
context "with server and name arguments" do
|
|
24
|
+
before do
|
|
25
|
+
@db =DatabaseTest.new(@server, 'exegesis-test')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
expect { @db.is_a?(DatabaseTest).will == true }
|
|
29
|
+
expect { @db.uri.will == "#{@server.uri}/exegesis-test"}
|
|
30
|
+
|
|
31
|
+
context "when the database does not exist" do
|
|
32
|
+
before do
|
|
33
|
+
@action = lambda { DatabaseTest.new(@server, 'exegesis-test-nonexistent') }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
expect { @action.will raise_error(RestClient::ResourceNotFound) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context "with a url argument" do
|
|
41
|
+
before do
|
|
42
|
+
@db = DatabaseTest.new('http://localhost:5984/exegesis-test')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
expect { @db.is_a?(DatabaseTest).will == true }
|
|
46
|
+
expect { @db.uri.will == 'http://localhost:5984/exegesis-test' }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context "with a name argument" do
|
|
50
|
+
before do
|
|
51
|
+
@db = DatabaseTest.new('exegesis-test')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
expect { @db.is_a?(DatabaseTest).will == true }
|
|
55
|
+
expect { @db.uri.will == "http://localhost:5984/exegesis-test" }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "retrieving documents by id" do
|
|
60
|
+
before do
|
|
61
|
+
@db = DatabaseTest.new @server, 'exegesis-test'
|
|
62
|
+
RestClient.put "#{@db.uri}/test-document", {'key'=>'value', 'class'=>'DatabaseTestDocument'}.to_json rescue nil
|
|
63
|
+
@doc = @db.get('test-document')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
after do
|
|
67
|
+
RestClient.delete("#{@db.uri}/test-document?rev=#{@doc['_rev']}") rescue nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
expect { @doc.is_a?(DatabaseTestDocument).will == true }
|
|
71
|
+
expect { @doc.id.will == 'test-document' }
|
|
72
|
+
expect { @doc['key'].will == 'value' }
|
|
73
|
+
|
|
74
|
+
context "retrieving the raw document" do
|
|
75
|
+
before do
|
|
76
|
+
@doc = @db.raw_get('test-document')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
expect { @doc.is_a?(Hash).will == true }
|
|
80
|
+
expect { @doc['_id'].will == 'test-document' }
|
|
81
|
+
expect { @doc['key'].will == 'value' }
|
|
82
|
+
expect { @doc['class'].will == 'DatabaseTestDocument' }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "saving docs" do
|
|
87
|
+
before do
|
|
88
|
+
reset_db
|
|
89
|
+
@db = DatabaseTest.new('exegesis-test')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context "a single doc" do
|
|
93
|
+
before { @doc = {'foo' => 'bar'} }
|
|
94
|
+
|
|
95
|
+
context "without an id" do
|
|
96
|
+
before do
|
|
97
|
+
@db.save(@doc)
|
|
98
|
+
@rdoc = @db.get(@doc['_id'])
|
|
99
|
+
end
|
|
100
|
+
expect { @doc['_rev'].will == @rdoc['_rev'] }
|
|
101
|
+
expect { @rdoc['foo'].will == @doc['foo'] }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "with an id" do
|
|
105
|
+
before { @doc['_id'] = 'test-document' }
|
|
106
|
+
|
|
107
|
+
context "when the document doesn't exist yet" do
|
|
108
|
+
before do
|
|
109
|
+
@db.save(@doc)
|
|
110
|
+
@rdoc = @db.get('test-document')
|
|
111
|
+
end
|
|
112
|
+
expect { @doc['_rev'].will == @rdoc['_rev'] }
|
|
113
|
+
expect { @rdoc['foo'].will == @doc['foo'] }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context "when the document exists already" do
|
|
117
|
+
before do
|
|
118
|
+
response = @db.post(@doc)
|
|
119
|
+
@doc['_id'] = response['id']
|
|
120
|
+
@doc['_rev'] = response['rev']
|
|
121
|
+
@doc['foo'] = 'bee'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
expect { lambda { @db.save(@doc) }.wont raise_error }
|
|
125
|
+
|
|
126
|
+
context "without a valid rev" do
|
|
127
|
+
before { @doc.delete('_rev') }
|
|
128
|
+
expect { lambda { @db.save(@doc) }.will raise_error }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "multiple docs" do
|
|
135
|
+
before do
|
|
136
|
+
@updated = @db.post({'_id' => 'updated', 'key' => 'original'})
|
|
137
|
+
@deleted = @db.post({'_id' => 'deleted', 'key' => 'original'})
|
|
138
|
+
@saving = lambda {
|
|
139
|
+
@db.save([
|
|
140
|
+
{'_id' => 'new', 'key' => 'new'},
|
|
141
|
+
{'_id' => 'updated', 'key' => 'new', '_rev' => @updated['rev']},
|
|
142
|
+
{'_id' => 'deleted', '_rev' => @deleted['rev'], '_deleted' => true }
|
|
143
|
+
])
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context "without conflicts" do
|
|
148
|
+
before { @saving.call }
|
|
149
|
+
expect { @db.get('new')['key'].will == 'new' }
|
|
150
|
+
expect { @db.get('updated')['key'].will == 'new' }
|
|
151
|
+
expect { lambda {@db.get('deleted')}.will raise_error(RestClient::ResourceNotFound) }
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context "setting the designs directory" do
|
|
157
|
+
expect { DatabaseTest.designs_directory.will == Pathname.new('designs') }
|
|
158
|
+
expect { CustomDesignDirDatabaseTest.designs_directory.will == Pathname.new('app/designs') }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end
|