mattly-exegesis 0.0.6
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.rdoc +23 -0
- data/VERSION.yml +4 -0
- data/lib/exegesis.rb +44 -0
- data/lib/exegesis/design.rb +84 -0
- data/lib/exegesis/design/design_docs.rb +92 -0
- data/lib/exegesis/document.rb +149 -0
- data/test/design_doc_test.rb +120 -0
- data/test/design_test.rb +113 -0
- data/test/document_class_definitions_test.rb +267 -0
- data/test/document_instance_methods_test.rb +40 -0
- data/test/exegesis_test.rb +28 -0
- data/test/fixtures/designs/foos.js +12 -0
- data/test/test_helper.rb +39 -0
- metadata +79 -0
data/README.rdoc
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
= exegesis
|
|
2
|
+
|
|
3
|
+
* http://github.com/mattly/exegesis
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
An ODM (Object/Document Mapper) for Couchdb. Still very much a work in progress
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
* Encourages Per-"Account" databases. This is both a feature and a problem, since classes cannot know which database to pull from you cannot do f.e. "Article.find('foo')" as Article doesn't know what database to use.
|
|
12
|
+
* Does not provide it's own validators or callbacks. I have a solution in mind for this involving state machines.
|
|
13
|
+
|
|
14
|
+
== REQUIREMENTS:
|
|
15
|
+
|
|
16
|
+
* Johnson (and Spidermonkey, if you have CouchDB you have Spidermonkey)
|
|
17
|
+
|
|
18
|
+
For running the tests:
|
|
19
|
+
|
|
20
|
+
* Test::Unit (you got it)
|
|
21
|
+
* Context (http://github.com/jeremymcanally/context, can install from github gems)
|
|
22
|
+
* Matchy (http://github.com/jeremymcanally/matchy, github gem version out of date; clone, build & install for now)
|
|
23
|
+
* Zebra (http://github.com/giraffesoft/zerba, depends on jeremymcanally-matchy, which is out of date; clone, build & install for now)
|
data/VERSION.yml
ADDED
data/lib/exegesis.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
|
|
4
|
+
require 'couchrest'
|
|
5
|
+
require 'active_support/inflector'
|
|
6
|
+
|
|
7
|
+
$:.unshift File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
8
|
+
|
|
9
|
+
module Exegesis
|
|
10
|
+
autoload :Document, 'exegesis/document'
|
|
11
|
+
autoload :Design, 'exegesis/design'
|
|
12
|
+
|
|
13
|
+
extend self
|
|
14
|
+
|
|
15
|
+
def designs_directory= dir
|
|
16
|
+
@designs_directory = Pathname.new(dir)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def designs_directory
|
|
20
|
+
@designs_directory ||= Pathname.new(ENV["PWD"])
|
|
21
|
+
@designs_directory
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def design_file name
|
|
25
|
+
File.read(designs_directory + name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def database_template= template
|
|
29
|
+
@db_template = template
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def database_template
|
|
33
|
+
@db_template ||= "http://localhost:5984/%s"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def database_for name
|
|
37
|
+
database_template % name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def document_classes
|
|
41
|
+
@document_classes ||= Hash.new(Exegesis::Document)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
|
2
|
+
require 'design/design_docs'
|
|
3
|
+
|
|
4
|
+
module Exegesis
|
|
5
|
+
class Design
|
|
6
|
+
include Exegesis::Design::DesignDocs
|
|
7
|
+
|
|
8
|
+
attr_accessor :database
|
|
9
|
+
|
|
10
|
+
def initialize(db)
|
|
11
|
+
@database = db
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.use_design_doc_name name
|
|
15
|
+
@design_doc_name = name.to_s
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.design_doc_name
|
|
19
|
+
@design_doc_name ||= name.to_s.sub(/(Design)$/,'').downcase
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def design_doc_name
|
|
23
|
+
self.class.design_doc_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get(id)
|
|
27
|
+
doc = Exegesis::Document.instantiate database.get(id)
|
|
28
|
+
doc.database = self.database
|
|
29
|
+
doc
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def parse_opts(opts={})
|
|
33
|
+
if opts[:key]
|
|
34
|
+
case opts[:key]
|
|
35
|
+
when Range
|
|
36
|
+
range = opts.delete(:key)
|
|
37
|
+
opts.update({:startkey => range.first, :endkey => range.last})
|
|
38
|
+
when Array
|
|
39
|
+
if opts[:key].any?{|v| v.kind_of?(Range) }
|
|
40
|
+
key = opts.delete(:key)
|
|
41
|
+
opts[:startkey] = key.map {|v| v.kind_of?(Range) ? v.first : v }
|
|
42
|
+
opts[:endkey] = key.map {|v| v.kind_of?(Range) ? v.last : v }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
elsif opts[:keys] && opts[:keys].empty?
|
|
46
|
+
opts.delete(:keys)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def view view_name, opts={}
|
|
53
|
+
opts = parse_opts opts
|
|
54
|
+
return [] unless opts[:key] || opts[:startkey] || opts[:keys] || opts[:all]
|
|
55
|
+
opts.delete(:all)
|
|
56
|
+
database.view("#{design_doc_name}/#{view_name}", opts)['rows']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def docs_for view_name, opts={}
|
|
60
|
+
response = view view_name, opts.update({:include_docs => true})
|
|
61
|
+
response.map do |doc|
|
|
62
|
+
model = Exegesis::Document.instantiate doc['doc']
|
|
63
|
+
model.database = database
|
|
64
|
+
model
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def values_for view_name, opts={}
|
|
69
|
+
response = view view_name, opts
|
|
70
|
+
response.map {|row| row['value'] }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def keys_for view_name, opts={}
|
|
74
|
+
response = view view_name, opts
|
|
75
|
+
response.map {|row| row['key'] }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def ids_for view_name, opts={}
|
|
79
|
+
response = view view_name, opts
|
|
80
|
+
response.map {|row| row['id'] }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'johnson'
|
|
2
|
+
require 'digest/md5'
|
|
3
|
+
|
|
4
|
+
module Exegesis
|
|
5
|
+
class Design
|
|
6
|
+
module DesignDocs
|
|
7
|
+
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.extend ClassMethods
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module ClassMethods
|
|
13
|
+
def designs_directory dir=nil
|
|
14
|
+
if dir
|
|
15
|
+
@designs_directory = Pathname.new(dir)
|
|
16
|
+
else
|
|
17
|
+
@designs_directory || Exegesis.designs_directory
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def design_doc_path
|
|
22
|
+
designs_directory + "#{design_doc_name}.js"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def design_doc
|
|
26
|
+
js_doc = Johnson.evaluate("v = #{File.read(design_doc_path)}");
|
|
27
|
+
views = js_doc['views'].entries.inject({}) do |memo, (name, mapreduce)|
|
|
28
|
+
memo[name] = mapreduce.entries.inject({}) do |view, (role, func)|
|
|
29
|
+
view.update role => func.toString
|
|
30
|
+
end
|
|
31
|
+
memo
|
|
32
|
+
end
|
|
33
|
+
composite_views = declared_views.dup.update(views)
|
|
34
|
+
{ '_id' => "_design/#{design_doc_name}",
|
|
35
|
+
'language' => 'javascript',
|
|
36
|
+
'views' => composite_views
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def declared_views
|
|
41
|
+
@declared_views ||= {}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def view_by *keys
|
|
45
|
+
view_name = "by_#{keys.join('_and_')}"
|
|
46
|
+
doc_keys = keys.map {|k| "doc['#{k}']" }
|
|
47
|
+
declared_views[view_name] = {
|
|
48
|
+
'map' => %|function(doc) {
|
|
49
|
+
if (doc['.kind'] == '#{name.sub(/Design$/,'')}' && #{doc_keys.join(' && ')}) {
|
|
50
|
+
emit(#{keys.length == 1 ? doc_keys.first : "[#{doc_keys.join(', ')}]" }, null);
|
|
51
|
+
}
|
|
52
|
+
}|
|
|
53
|
+
}
|
|
54
|
+
define_method view_name do |*args|
|
|
55
|
+
docs_for view_name, *args
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def design_doc_hash
|
|
60
|
+
hash_for_design design_doc
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def hash_for_design design
|
|
64
|
+
funcs = design['views'].map do |name, view|
|
|
65
|
+
"//view/#{name}/#{view['map']}/#{view['reduce']}"
|
|
66
|
+
end
|
|
67
|
+
Digest::MD5.hexdigest(funcs.sort.join)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def design_doc_hash
|
|
73
|
+
design_doc.nil? ? '' : self.class.hash_for_design(design_doc)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def design_doc reload=false
|
|
77
|
+
@design_doc = nil if reload
|
|
78
|
+
@design_doc ||= database.get "_design/#{design_doc_name}" rescue nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def push_design!
|
|
82
|
+
return if design_doc_hash == self.class.design_doc_hash
|
|
83
|
+
if design_doc
|
|
84
|
+
design_doc.update(self.class.design_doc)
|
|
85
|
+
design_doc.save
|
|
86
|
+
else
|
|
87
|
+
database.save_doc(self.class.design_doc)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Exegesis
|
|
2
|
+
class Document < CouchRest::Document
|
|
3
|
+
|
|
4
|
+
def self.inherited subklass
|
|
5
|
+
Exegesis.document_classes[subklass.name] = subklass
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.instantiate hash={}
|
|
9
|
+
Exegesis.document_classes[hash['.kind']].new(hash)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.expose *attrs
|
|
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 }
|
|
27
|
+
end
|
|
28
|
+
if opts[:as]
|
|
29
|
+
if opts[:as] == :reference
|
|
30
|
+
define_method(attrib) do |*reload|
|
|
31
|
+
reload = false if reload.empty?
|
|
32
|
+
instance_variable_set("@#{attrib}", nil) if reload
|
|
33
|
+
return instance_variable_get("@#{attrib}") if instance_variable_get("@#{attrib}")
|
|
34
|
+
instance_variable_set("@#{attrib}", load_reference(self[attrib]))
|
|
35
|
+
end
|
|
36
|
+
else
|
|
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
|
|
45
|
+
else
|
|
46
|
+
define_method(attrib) { self[attrib] }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.default hash=nil
|
|
52
|
+
if hash
|
|
53
|
+
@default = hash
|
|
54
|
+
else
|
|
55
|
+
@default ||= superclass.respond_to?(:default) ? superclass.default : {}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.timestamps!
|
|
60
|
+
define_method :set_timestamps do
|
|
61
|
+
self['updated_at'] = Time.now
|
|
62
|
+
self['created_at'] ||= Time.now
|
|
63
|
+
end
|
|
64
|
+
expose 'updated_at', :as => Time, :writer => false
|
|
65
|
+
expose 'created_at', :as => Time, :writer => false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.unique_id meth
|
|
69
|
+
define_method :set_unique_id do
|
|
70
|
+
self['_id'] = self.send(meth)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
alias :_rev :rev
|
|
75
|
+
alias_method :document_save, :save
|
|
76
|
+
|
|
77
|
+
def save
|
|
78
|
+
set_timestamps if respond_to?(:set_timestamps)
|
|
79
|
+
if respond_to?(:set_unique_id) && id.nil?
|
|
80
|
+
@unique_id_attempt = 0
|
|
81
|
+
begin
|
|
82
|
+
self['_id'] = set_unique_id
|
|
83
|
+
document_save
|
|
84
|
+
rescue RestClient::RequestFailed => e
|
|
85
|
+
@unique_id_attempt += 1
|
|
86
|
+
retry
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
document_save
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def initialize keys={}
|
|
94
|
+
apply_default
|
|
95
|
+
super keys
|
|
96
|
+
self['.kind'] ||= self.class.to_s
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def update_attributes attrs={}
|
|
100
|
+
raise ArgumentError, 'must include a matching _rev attribute' unless rev == attrs.delete('_rev')
|
|
101
|
+
attrs.each_pair do |key, value|
|
|
102
|
+
self.send("#{key}=", value) rescue nil
|
|
103
|
+
attrs.delete(key)
|
|
104
|
+
end
|
|
105
|
+
save
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def apply_default
|
|
111
|
+
self.class.default.each do |key, value|
|
|
112
|
+
self[key] = value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def cast as, value
|
|
117
|
+
return nil if value.nil?
|
|
118
|
+
klass = if as == :given
|
|
119
|
+
if value.is_a?(Hash)
|
|
120
|
+
Exegesis.document_classes[value['.kind']]
|
|
121
|
+
else
|
|
122
|
+
nil #nfi what do to in this case; maybe just have it be a doc hash?
|
|
123
|
+
end
|
|
124
|
+
elsif as.is_a?(Class)
|
|
125
|
+
as
|
|
126
|
+
else
|
|
127
|
+
Exegesis.document_classes[nil] # whatever the default is? Hell idk.
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
with = klass == Time ? :parse : :new
|
|
131
|
+
casted = klass.send with, value
|
|
132
|
+
casted
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def load_reference ids
|
|
136
|
+
raise ArgumentError, "a database is required for loading a reference" unless database
|
|
137
|
+
if ids.is_a?(Array)
|
|
138
|
+
ids.map {|val| Exegesis::Document.instantiate(database.get(val)) }
|
|
139
|
+
else
|
|
140
|
+
Exegesis::Document.instantiate(database.get(ids))
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# $:.unshift File.dirname(__FILE__)
|
|
148
|
+
# require 'document/annotated_reference'
|
|
149
|
+
# require 'document/referencing'
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class FoosDesign < Exegesis::Design
|
|
4
|
+
view_by :foo
|
|
5
|
+
view_by :foo, :bar
|
|
6
|
+
end
|
|
7
|
+
class CustomDesignDirDesign < Exegesis::Design
|
|
8
|
+
designs_directory File.join(File.dirname(__FILE__), 'fixtures')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ComposingDesignDocTest < Test::Unit::TestCase
|
|
13
|
+
before(:all) { Exegesis.designs_directory = fixtures_path('designs') }
|
|
14
|
+
|
|
15
|
+
context "setting a custom designs directory" do
|
|
16
|
+
before do
|
|
17
|
+
@custom_design_dir = File.join(File.dirname(__FILE__), 'fixtures')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
expect { CustomDesignDirDesign.designs_directory.to_s.will == @custom_design_dir }
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context "composing design docs from local sources" do
|
|
25
|
+
before do
|
|
26
|
+
@design = FoosDesign.design_doc
|
|
27
|
+
@file = File.read(fixtures_path('designs/foos.js'))
|
|
28
|
+
@jsdoc = Johnson.evaluate("v=#{@file}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
expect { @design.has_key?('_id').will be(true) }
|
|
32
|
+
expect { @design['_id'].will == '_design/foos' }
|
|
33
|
+
expect { @design['views']['by_bar']['map'].will == @jsdoc['views']['by_bar']['map'].toString }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "composing a design doc from view_by declarations" do
|
|
37
|
+
before do
|
|
38
|
+
@design = FoosDesign.design_doc
|
|
39
|
+
@by_foo = @design['views']['by_foo']['map']
|
|
40
|
+
@by_foo_and_bar = @design['views']['by_foo_and_bar']['map']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
expect { @by_foo.will include("if (doc['.kind'] == 'Foos' && doc['foo'])") }
|
|
44
|
+
expect { @by_foo.will include("emit(doc['foo'], null);") }
|
|
45
|
+
|
|
46
|
+
expect { @by_foo_and_bar.will include("if (doc['.kind'] == 'Foos' && doc['foo'] && doc['bar'])") }
|
|
47
|
+
expect { @by_foo_and_bar.will include("emit([doc['foo'], doc['bar']], null)") }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "building a hash a design doc" do
|
|
51
|
+
before do
|
|
52
|
+
@design = {
|
|
53
|
+
'views' => {
|
|
54
|
+
'a' => {'map' => 'some value', 'reduce' => 'some value'},
|
|
55
|
+
'b' => {'map' => 'some value'}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
funcs = @design['views'].map{|name, view| "//view/#{name}/#{view['map']}/#{view['reduce']}" }.sort
|
|
59
|
+
@hashed = Digest::MD5.hexdigest(funcs.join)
|
|
60
|
+
end
|
|
61
|
+
expect { FoosDesign.hash_for_design(@design).will == @hashed }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context "syncronising with a databae" do
|
|
65
|
+
before do
|
|
66
|
+
reset_db
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context "pushing design doc when it doesn't exist yet" do
|
|
70
|
+
before do
|
|
71
|
+
foo = FoosDesign.new(@db)
|
|
72
|
+
foo.push_design!
|
|
73
|
+
@get_design = lambda { @db.get('_design/foos') }
|
|
74
|
+
@design = @get_design.call rescue nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
expect { @get_design.wont raise_error }
|
|
78
|
+
expect { @design['_rev'].will =~ /^[0-9]+$/ }
|
|
79
|
+
expect { @design['language'].will == 'javascript' }
|
|
80
|
+
expect { @design['views'].has_key?('by_bar').will be(true) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context "reading the existing design document" do
|
|
84
|
+
before do
|
|
85
|
+
@db.save_doc(FoosDesign.design_doc)
|
|
86
|
+
@foo = FoosDesign.new(@db)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
expect { lambda {@foo.design_doc}.wont raise_error }
|
|
90
|
+
expect { @foo.design_doc['_rev'].will =~ /^[0-9]+$/ }
|
|
91
|
+
expect { @foo.design_doc['views'].keys.will == FoosDesign.design_doc['views'].keys }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "updating an existing doc" do
|
|
95
|
+
|
|
96
|
+
context "when it hasn't changed" do
|
|
97
|
+
before do
|
|
98
|
+
@db.save_doc(FoosDesign.design_doc)
|
|
99
|
+
@foo = FoosDesign.new(@db)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
expect { lambda { @foo.push_design! }.wont change { @db.get('_design/foos')['_rev'] } }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
context "when it has chagned" do
|
|
106
|
+
before do
|
|
107
|
+
@db.save_doc({'_id' => '_design/foos',
|
|
108
|
+
'views' => {'a' => {'map' => 'function(doc) { emit(true, null)}'}}
|
|
109
|
+
})
|
|
110
|
+
@foo = FoosDesign.new(@db)
|
|
111
|
+
@pushing = lambda{ @foo.push_design! }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
expect { @pushing.will change { @db.get('_design/foos')['_rev'] } }
|
|
115
|
+
expect { @pushing.call; @foo.design_doc_hash.will == FoosDesign.design_doc_hash }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
end
|
data/test/design_test.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class FoosDesign < Exegesis::Design; end
|
|
4
|
+
class BarsDesign < Exegesis::Design
|
|
5
|
+
use_design_doc_name :something_else
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class TestForDesign < Exegesis::Document; end
|
|
9
|
+
|
|
10
|
+
class ExegesisDesignTest < Test::Unit::TestCase
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
reset_db
|
|
14
|
+
@doc = FoosDesign.new(@db)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
expect { @doc.database.will == @db }
|
|
18
|
+
expect { @doc.design_doc_name.will == "foos" }
|
|
19
|
+
|
|
20
|
+
expect { FoosDesign.design_doc_name.will == "foos" }
|
|
21
|
+
expect { BarsDesign.design_doc_name.will == "something_else" }
|
|
22
|
+
|
|
23
|
+
context "retrieving documents with #get" do
|
|
24
|
+
before do
|
|
25
|
+
@db.save_doc '_id' => 'foo', 'foo' => 'bar', '.kind' => 'TestForDesign'
|
|
26
|
+
@obj = @doc.get('foo')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
expect { @obj.will be_kind_of(TestForDesign) }
|
|
30
|
+
expect { @obj['foo'].will == 'bar' }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "retreiving views" do
|
|
34
|
+
before do
|
|
35
|
+
@raw_docs = [
|
|
36
|
+
{'_id' => 'bar', 'foo' => 'bar', 'bar' => 'bar', '.kind' => 'TestForDesign'},
|
|
37
|
+
{'_id' => 'baz', 'foo' => 'baz', 'bar' => 'baz', '.kind' => 'TestForDesign'},
|
|
38
|
+
{'_id' => 'foo', 'foo' => 'foo', 'bar' => 'foo', '.kind' => 'TestForDesign'}
|
|
39
|
+
]
|
|
40
|
+
@db.bulk_save @raw_docs
|
|
41
|
+
@db.save_doc({
|
|
42
|
+
'_id' => '_design/foos',
|
|
43
|
+
'views' => {
|
|
44
|
+
'test' => { 'map'=>'function(doc) {emit(doc.foo, doc.bar);}' },
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context "parsing options" do
|
|
50
|
+
context "when the key is a range" do
|
|
51
|
+
before { @opts = @doc.parse_opts(:key => 'bar'..'baz') }
|
|
52
|
+
|
|
53
|
+
expect { @opts[:key].will == nil }
|
|
54
|
+
expect { @opts[:startkey].will == 'bar' }
|
|
55
|
+
expect { @opts[:endkey].will == 'baz' }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "when the key is an array with a range in it" do
|
|
59
|
+
before { @opts = @doc.parse_opts(:key => ['published', '2008'..'2008/13']) }
|
|
60
|
+
|
|
61
|
+
expect { @opts[:key].will be(nil) }
|
|
62
|
+
expect { @opts[:startkey].will == ['published', '2008'] }
|
|
63
|
+
expect { @opts[:endkey].will == ['published', '2008/13'] }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "when a keys option is empty" do
|
|
67
|
+
before { @opts = @doc.parse_opts(:keys => []) }
|
|
68
|
+
|
|
69
|
+
expect { @opts[:keys].will be(nil) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "when no key, keys, startkey or all option is present" do
|
|
74
|
+
before { @response = @doc.view :test }
|
|
75
|
+
|
|
76
|
+
expect { @response.will == [] }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context "with an all key" do
|
|
80
|
+
before { @response = @doc.view :test, :all => true }
|
|
81
|
+
|
|
82
|
+
expect { @response.will == @raw_docs.map{|d| {'id' => d['_id'], 'key' => d['foo'], 'value' => d['bar']} } }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context "with docs" do
|
|
86
|
+
before { @response = @doc.docs_for :test, :key => 'foo' }
|
|
87
|
+
|
|
88
|
+
expect { @response.will be_kind_of(Array) }
|
|
89
|
+
expect { @response.size.will == 1 }
|
|
90
|
+
expect { @response.first.will be_kind_of(TestForDesign) }
|
|
91
|
+
expect { @response.first['foo'].will == 'foo' }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "for the view's data" do
|
|
95
|
+
before { @response = @doc.values_for :test, :all => true }
|
|
96
|
+
|
|
97
|
+
expect { @response.will == %w(bar baz foo) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context "for the view's matching keys" do
|
|
101
|
+
before { @response = @doc.keys_for :test, :key => 'bar'..'baz' }
|
|
102
|
+
|
|
103
|
+
expect { @response.will == %w(bar baz) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context "for the view's matching ids" do
|
|
107
|
+
before { @response = @doc.ids_for :test, :key => 'bar'..'foo'}
|
|
108
|
+
|
|
109
|
+
expect { @response.will == %w(bar baz foo) }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class Foo < Exegesis::Document; end
|
|
4
|
+
class Bar < Exegesis::Document; end
|
|
5
|
+
|
|
6
|
+
class WithDefault < Exegesis::Document
|
|
7
|
+
default :foo => 'bar'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class Exposer < Exegesis::Document
|
|
11
|
+
expose :foo, :bar
|
|
12
|
+
expose :read_only, :writer => false
|
|
13
|
+
expose :custom_writer, :writer => lambda {|val| {'value' => val} }
|
|
14
|
+
expose :castee, :castees, :as => :given
|
|
15
|
+
expose :time, :times, :as => Time
|
|
16
|
+
expose :regex, :regexen, :as => Regexp
|
|
17
|
+
expose :other_doc, :other_docs, :as => :reference
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Timestamper < Exegesis::Document
|
|
21
|
+
timestamps!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class UniqueSnowflake < Exegesis::Document
|
|
25
|
+
unique_id :set_id
|
|
26
|
+
def set_id
|
|
27
|
+
@unique_id_attempt.zero? ? "snowflake" : "snowflake-#{@unique_id_attempt}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class ExegesisDocumentClassDefinitionsTest < Test::Unit::TestCase
|
|
32
|
+
|
|
33
|
+
context "a bare Exegesis::Document" do
|
|
34
|
+
before do
|
|
35
|
+
reset_db
|
|
36
|
+
@obj = Foo.new
|
|
37
|
+
@obj.database = @db
|
|
38
|
+
@obj.save
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
expect { @obj['.kind'].will == "Foo" }
|
|
42
|
+
expect { @obj['foo'].will == nil }
|
|
43
|
+
expect { @obj.will_not respond_to(:foo) }
|
|
44
|
+
expect { @obj['created_at'].will == nil }
|
|
45
|
+
expect { @obj['updated_at'].will == nil }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "instantiating" do
|
|
49
|
+
expect { Exegesis::Document.instantiate({'.kind' => 'Foo'}).will be_kind_of(Foo) }
|
|
50
|
+
|
|
51
|
+
context "transitions" do
|
|
52
|
+
before do
|
|
53
|
+
@foo = Foo.new
|
|
54
|
+
@foo['.kind'] = 'Bar'
|
|
55
|
+
@bar = Exegesis::Document.instantiate(@foo)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
expect { @bar.will be_kind_of(Bar) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "default objects" do
|
|
63
|
+
expect { WithDefault.new['foo'].will == 'bar' }
|
|
64
|
+
expect { WithDefault.new({'foo' => 'baz'})['foo'].will == 'baz' }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context "exposing keys" do
|
|
68
|
+
context "regular declarations" do
|
|
69
|
+
before do
|
|
70
|
+
@obj = Exposer.new(:foo => 'bar', :bar => 'foo')
|
|
71
|
+
@writing = lambda { @obj.bar = "bee" }
|
|
72
|
+
end
|
|
73
|
+
expect { @obj.foo.will == 'bar' }
|
|
74
|
+
expect { @obj.bar.will == 'foo' }
|
|
75
|
+
expect { @writing.wont raise_error }
|
|
76
|
+
expect { @writing.call; @obj.bar.will == "bee" }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context "with a custom writer" do
|
|
80
|
+
context "when false" do
|
|
81
|
+
before { @obj = Exposer.new(:read_only => 'value') }
|
|
82
|
+
expect { @obj.read_only.will == 'value' }
|
|
83
|
+
expect { lambda{@obj.read_only = "other value"}.will raise_error(NoMethodError) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "when lambda" do
|
|
87
|
+
before do
|
|
88
|
+
@obj = Exposer.new(:custom_writer => 'value')
|
|
89
|
+
@obj.custom_writer = 'other value'
|
|
90
|
+
@expected = {'value' => 'other value'}
|
|
91
|
+
end
|
|
92
|
+
expect { @obj.custom_writer.will == @expected }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context "when casting a value" do
|
|
97
|
+
context "when as given" do
|
|
98
|
+
before do
|
|
99
|
+
@obj = Exposer.new({
|
|
100
|
+
:castee => {'foo' => 'foo', '.kind' => 'Foo'},
|
|
101
|
+
:castees => [{'foo' => 'foo', '.kind' => 'Foo'}, {'foo' => 'bar', '.kind' => 'Bar'}]
|
|
102
|
+
})
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
expect { @obj.castee.class.will == Foo }
|
|
106
|
+
expect { @obj.castee['foo'].will == 'foo' }
|
|
107
|
+
|
|
108
|
+
expect { @obj.castees.class.will == Array }
|
|
109
|
+
expect { @obj.castees[0].class.will == Foo }
|
|
110
|
+
expect { @obj.castees[0]['foo'].will == 'foo' }
|
|
111
|
+
expect { @obj.castees[1].class.will == Bar }
|
|
112
|
+
expect { @obj.castees[1]['foo'].will == 'bar' }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context "when as time" do
|
|
116
|
+
before do
|
|
117
|
+
@obj = Exposer.new({:time => Time.now.to_json, :times => [Time.local(2009,3,1).to_json, Time.local(2009,2,1).to_json]})
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
expect { @obj.time.class.will == Time }
|
|
121
|
+
expect { @obj.time.to_f.will be_close(Time.now.to_f, 1) }
|
|
122
|
+
|
|
123
|
+
expect { @obj.times.class.will == Array }
|
|
124
|
+
expect { @obj.times[0].class.will == Time }
|
|
125
|
+
expect { @obj.times[0].will == Time.local(2009,3,1) }
|
|
126
|
+
expect { @obj.times[1].class.will == Time }
|
|
127
|
+
expect { @obj.times[1].will == Time.local(2009,2,1) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context "when as non document class" do
|
|
131
|
+
before do
|
|
132
|
+
@obj = Exposer.new({
|
|
133
|
+
:regex => 'foo',
|
|
134
|
+
:regexen => ['foo', 'bar']
|
|
135
|
+
})
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
expect { @obj.regex.will == /foo/ }
|
|
139
|
+
|
|
140
|
+
expect { @obj.regexen.class.will == Array }
|
|
141
|
+
expect { @obj.regexen[0].will == /foo/ }
|
|
142
|
+
expect { @obj.regexen[1].will == /bar/ }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context "when as reference" do
|
|
146
|
+
context "with a database present" do
|
|
147
|
+
before do
|
|
148
|
+
reset_db
|
|
149
|
+
@obj = Exposer.new(:other_doc => "other_doc", :other_docs => ["other_docs_1", "other_docs_2"])
|
|
150
|
+
@obj.database = @db
|
|
151
|
+
end
|
|
152
|
+
context "when the document exists" do
|
|
153
|
+
before do
|
|
154
|
+
@db.bulk_save([
|
|
155
|
+
{'.kind' => 'Foo', '_id' => 'other_doc'},
|
|
156
|
+
{'.kind' => 'Foo', '_id' => 'other_docs_1'},
|
|
157
|
+
{'.kind' => 'Foo', '_id' => 'other_docs_2'}
|
|
158
|
+
])
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
expect { @obj.other_doc.rev.will == @db.get('other_doc')['_rev'] }
|
|
162
|
+
expect { @obj.other_doc.class.will == Foo }
|
|
163
|
+
expect { @obj.other_docs.class.will == Array }
|
|
164
|
+
expect { @obj.other_docs[0].rev.will == @db.get('other_docs_1')['_rev'] }
|
|
165
|
+
expect { @obj.other_docs[0].class.will == Foo }
|
|
166
|
+
expect { @obj.other_docs[1].rev.will == @db.get('other_docs_2')['_rev'] }
|
|
167
|
+
expect { @obj.other_docs[1].class.will == Foo }
|
|
168
|
+
|
|
169
|
+
context "caching" do
|
|
170
|
+
before do
|
|
171
|
+
@obj.other_doc
|
|
172
|
+
doc = @db.get('other_doc')
|
|
173
|
+
doc['foo'] = "updated"
|
|
174
|
+
doc.save
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
expect { @obj.other_doc['foo'].will be(nil) }
|
|
178
|
+
expect { @obj.other_doc(true)['foo'].will == 'updated' }
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
context "when the document is missing" do
|
|
183
|
+
expect { lambda{@obj.other_doc}.will raise_error(RestClient::ResourceNotFound) }
|
|
184
|
+
expect { lambda{@obj.other_docs}.will raise_error(RestClient::ResourceNotFound) }
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context "without a database present" do
|
|
189
|
+
before { @obj = Exposer.new(:other_doc => "some_doc_id") }
|
|
190
|
+
expect { lambda{@obj.other_doc}.will raise_error(ArgumentError) }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context "when the value is nil" do
|
|
195
|
+
before do
|
|
196
|
+
@obj = Exposer.new({:castee => nil, :castees => nil, :regexen => ['foo', nil]})
|
|
197
|
+
end
|
|
198
|
+
expect { @obj.castee.will be(nil) }
|
|
199
|
+
expect { @obj.castees.will be(nil) }
|
|
200
|
+
expect { @obj.regexen.will == [/foo/] }
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
context "with timestamps" do
|
|
206
|
+
before do
|
|
207
|
+
reset_db
|
|
208
|
+
@obj = Timestamper.new
|
|
209
|
+
@obj.database = @db
|
|
210
|
+
@obj.save
|
|
211
|
+
@obj = Timestamper.new(@db.get(@obj.id))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context "initial save" do
|
|
215
|
+
expect { @obj.created_at.to_f.will be_close(Time.now.to_f, 1) }
|
|
216
|
+
expect { @obj.updated_at.to_f.will be_close(Time.now.to_f, 1) }
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
context "when created_at already exists" do
|
|
220
|
+
before do
|
|
221
|
+
@obj.database = @db
|
|
222
|
+
@obj['created_at'] = Time.now
|
|
223
|
+
@obj.save
|
|
224
|
+
@obj = Timestamper.new(@db.get(@obj.id))
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
expect { @obj.created_at.to_f.will be_close(Time.now.to_f, 1) }
|
|
228
|
+
expect { @obj.updated_at.to_f.will be_close(Time.now.to_f, 1) }
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
context "with a custom unique_id setter" do
|
|
234
|
+
before do
|
|
235
|
+
reset_db
|
|
236
|
+
@obj = UniqueSnowflake.new
|
|
237
|
+
@obj.database = @db
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
context "when the id isn't in use yet" do
|
|
241
|
+
before do
|
|
242
|
+
@obj.save
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
expect { @obj.id.will == "snowflake" }
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
context "when there is an id in place already" do
|
|
249
|
+
before do
|
|
250
|
+
@obj['_id'] = 'foo'
|
|
251
|
+
@obj.save
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
expect { @obj.id.will == "foo" }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
context "when the desired id is already in use" do
|
|
258
|
+
before do
|
|
259
|
+
@db.save_doc({'_id' => 'snowflake', 'foo' => 'bar'})
|
|
260
|
+
@obj.save
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
expect { @obj.id.will == 'snowflake-1' }
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class DocInstance < Exegesis::Document
|
|
4
|
+
expose :foo
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class ExegesisDocumentInstanceMethodsTest < Test::Unit::TestCase
|
|
8
|
+
|
|
9
|
+
context "update_attributes" do
|
|
10
|
+
before do
|
|
11
|
+
reset_db
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "an existing doc" do
|
|
15
|
+
before do
|
|
16
|
+
@doc = DocInstance.new({'foo' => 'bar'})
|
|
17
|
+
@doc.database = @db
|
|
18
|
+
@doc.save
|
|
19
|
+
@old_rev = @doc.rev
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "without a matching rev" do
|
|
23
|
+
expect { lambda {@doc.update_attributes({'foo' => 'bee'})}.will raise_error(ArgumentError) }
|
|
24
|
+
expect { lambda {@doc.update_attributes({'foo' => 'bee', '_rev' => 'z'})}.will raise_error(ArgumentError) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "with a matching rev" do
|
|
28
|
+
before do
|
|
29
|
+
@doc.update_attributes({'_rev' => @doc.rev, 'foo' => 'bee', 'bar' => 'boo'})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
expect { @doc['foo'].will == 'bee' }
|
|
33
|
+
expect { @doc['bar'].will be(nil) }
|
|
34
|
+
expect { @doc.rev.wont == @old_rev }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper.rb')
|
|
2
|
+
|
|
3
|
+
class ExegesisTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
context "designs directory" do
|
|
6
|
+
context "setting custom" do
|
|
7
|
+
before do
|
|
8
|
+
@custom_design_dir = File.join(File.dirname(__FILE__), 'fixtures', 'designs')
|
|
9
|
+
Exegesis.designs_directory = @custom_design_dir
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
expect { Exegesis.designs_directory.to_s.will == @custom_design_dir }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "database template" do
|
|
19
|
+
before do
|
|
20
|
+
@template_string = "http://localhost:5984/appname-%s"
|
|
21
|
+
@account = "foo"
|
|
22
|
+
Exegesis.database_template = @template_string
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
expect { Exegesis.database_for(@account).will == @template_string % @account }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
|
|
4
|
+
require 'context' # with github: gem install jeremymcanally-context
|
|
5
|
+
require 'matchy' # best bet for now: clone from github and build yourself; when jeremy fixes matchy, jeremymcanally-matchy
|
|
6
|
+
require 'zebra' # until jeremy updates matchy, download and build yourself, after that, with github: gem install giraffesoft-zebra
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'ruby-debug'
|
|
10
|
+
Debugger.start
|
|
11
|
+
rescue
|
|
12
|
+
puts "no ruby-debug installed? REAlLY? ok, if that's how you roll..."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
16
|
+
require 'lib/exegesis'
|
|
17
|
+
|
|
18
|
+
class Test::Unit::TestCase
|
|
19
|
+
|
|
20
|
+
def fixtures_path fixtures
|
|
21
|
+
File.join(File.dirname(__FILE__), 'fixtures', fixtures)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# todo: extract to some helper methods to include ala RR, etc
|
|
25
|
+
def reset_db(name=nil)
|
|
26
|
+
@db = CouchRest.database db(name) rescue nil
|
|
27
|
+
@db.delete! rescue nil
|
|
28
|
+
@db = CouchRest.database! db(name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def teardown_db
|
|
32
|
+
@db.delete! rescue nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def db(name)
|
|
36
|
+
"http://localhost:5984/exegesis-test#{name.nil? ? '' : "-#{name}"}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mattly-exegesis
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.6
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matt Lyon
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-03-01 00:00:00 -08:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: jchris-couchrest
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - "="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 0.12.6
|
|
24
|
+
version:
|
|
25
|
+
description: TODO
|
|
26
|
+
email: matt@flowerpowered.com
|
|
27
|
+
executables: []
|
|
28
|
+
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
|
|
33
|
+
files:
|
|
34
|
+
- README.rdoc
|
|
35
|
+
- VERSION.yml
|
|
36
|
+
- lib/exegesis
|
|
37
|
+
- lib/exegesis/design
|
|
38
|
+
- lib/exegesis/design/design_docs.rb
|
|
39
|
+
- lib/exegesis/design.rb
|
|
40
|
+
- lib/exegesis/document.rb
|
|
41
|
+
- lib/exegesis.rb
|
|
42
|
+
- test/design_doc_test.rb
|
|
43
|
+
- test/design_test.rb
|
|
44
|
+
- test/document_class_definitions_test.rb
|
|
45
|
+
- test/document_instance_methods_test.rb
|
|
46
|
+
- test/exegesis_test.rb
|
|
47
|
+
- test/fixtures
|
|
48
|
+
- test/fixtures/designs
|
|
49
|
+
- test/fixtures/designs/foos.js
|
|
50
|
+
- test/test_helper.rb
|
|
51
|
+
has_rdoc: true
|
|
52
|
+
homepage: http://github.com/mattly/exegesis
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options:
|
|
55
|
+
- --inline-source
|
|
56
|
+
- --charset=UTF-8
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: "0"
|
|
64
|
+
version:
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: "0"
|
|
70
|
+
version:
|
|
71
|
+
requirements: []
|
|
72
|
+
|
|
73
|
+
rubyforge_project:
|
|
74
|
+
rubygems_version: 1.2.0
|
|
75
|
+
signing_key:
|
|
76
|
+
specification_version: 2
|
|
77
|
+
summary: TODO
|
|
78
|
+
test_files: []
|
|
79
|
+
|