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.
@@ -0,0 +1,230 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class FooTestModel
4
+ include Exegesis::Model
5
+ expose :ref, :as => :reference
6
+ end
7
+ class BarTestModel
8
+ include Exegesis::Model
9
+ end
10
+
11
+ class WithDefaultTestModel
12
+ include Exegesis::Model
13
+ default :foo => 'bar'
14
+ end
15
+
16
+ class ModelTestDocument
17
+ include Exegesis::Model
18
+ attr_accessor :database
19
+ end
20
+
21
+ class ExposeTestModel
22
+ include Exegesis::Model
23
+ expose :foo, :bar
24
+ expose :read_only, :writer => false
25
+ expose :custom_writer, :writer => lambda {|val| {'value' => val} }
26
+ expose :castee, :castees, :as => :given
27
+ expose :time, :times, :as => Time
28
+ expose :regex, :regexen, :as => Regexp
29
+ expose :other_doc, :other_docs, :as => :reference
30
+ end
31
+
32
+ class ExegesisModelTest < Test::Unit::TestCase
33
+
34
+ context "class definitions" do
35
+ context "default objects" do
36
+ expect { WithDefaultTestModel.new['foo'].will == 'bar' }
37
+ expect { WithDefaultTestModel.new({'foo' => 'baz'})['foo'].will == 'baz' }
38
+ end
39
+
40
+ context "exposing keys" do
41
+ context "regular declarations" do
42
+ before do
43
+ @obj = ExposeTestModel.new(:foo => 'bar', :bar => 'foo')
44
+ end
45
+ context "reading" do
46
+ expect { @obj.foo.will == 'bar' }
47
+ expect { @obj.bar.will == 'foo' }
48
+ end
49
+
50
+ context "writing" do
51
+ before do
52
+ @obj.bar = "bee"
53
+ end
54
+ expect { @obj.bar.will == "bee" }
55
+ end
56
+ end
57
+
58
+ context "with a custom writer" do
59
+ context "when false" do
60
+ before { @obj = ExposeTestModel.new(:read_only => 'value') }
61
+ expect { @obj.read_only.will == 'value' }
62
+ expect { lambda{@obj.read_only = "other value"}.will raise_error(NoMethodError) }
63
+ end
64
+
65
+ context "when lambda" do
66
+ before do
67
+ @obj = ExposeTestModel.new(:custom_writer => 'value')
68
+ @obj.custom_writer = 'other value'
69
+ @expected = {'value' => 'other value'}
70
+ end
71
+ expect { @obj.custom_writer.will == @expected }
72
+ end
73
+ end
74
+
75
+ context "when casting a value" do
76
+ context "when as given" do
77
+ before do
78
+ @obj = ExposeTestModel.new({
79
+ :castee => {'foo' => 'foo', 'class' => 'FooTestModel'},
80
+ :castees => [
81
+ {'foo' => 'foo', 'class' => 'FooTestModel'},
82
+ {'foo' => 'bar', 'class' => 'BarTestModel'}
83
+ ]
84
+ })
85
+ end
86
+
87
+ expect { @obj.castee.class.will == FooTestModel }
88
+ expect { @obj.castee['foo'].will == 'foo' }
89
+ expect { @obj.castee.parent.will == @obj }
90
+
91
+ expect { @obj.castees.class.will == Array }
92
+ expect { @obj.castees[0].class.will == FooTestModel }
93
+ expect { @obj.castees[0]['foo'].will == 'foo' }
94
+ expect { @obj.castees[0].parent.will == @obj }
95
+ expect { @obj.castees[1].class.will == BarTestModel }
96
+ expect { @obj.castees[1]['foo'].will == 'bar' }
97
+ expect { @obj.castees[1].parent.will == @obj }
98
+ end
99
+
100
+ context "when as time" do
101
+ before do
102
+ @obj = ExposeTestModel.new({:time => Time.now.to_json, :times => [Time.local(2009,3,1).to_json, Time.local(2009,2,1).to_json]})
103
+ end
104
+
105
+ expect { @obj.time.class.will == Time }
106
+ expect { @obj.time.to_f.will be_close(Time.now.to_f, 1) }
107
+
108
+ expect { @obj.times.class.will == Array }
109
+ expect { @obj.times[0].class.will == Time }
110
+ expect { @obj.times[0].will == Time.local(2009,3,1) }
111
+ expect { @obj.times[1].class.will == Time }
112
+ expect { @obj.times[1].will == Time.local(2009,2,1) }
113
+ end
114
+
115
+ context "when as non document class" do
116
+ before do
117
+ @obj = ExposeTestModel.new({
118
+ :regex => 'foo',
119
+ :regexen => ['foo', 'bar']
120
+ })
121
+ end
122
+
123
+ expect { @obj.regex.will == /foo/ }
124
+
125
+ expect { @obj.regexen.class.will == Array }
126
+ expect { @obj.regexen[0].will == /foo/ }
127
+ expect { @obj.regexen[1].will == /bar/ }
128
+ end
129
+
130
+ context "when as reference" do
131
+ context "with a database present" do
132
+ before do
133
+ reset_db
134
+ @obj = ExposeTestModel.new(:other_doc => "other_doc",
135
+ :other_docs => ["other_docs_1", "other_docs_2"])
136
+ @doc = ModelTestDocument.new
137
+ @doc.database = @db
138
+ @obj.parent = @doc
139
+ end
140
+
141
+ context "when the referenced document exists" do
142
+ before do
143
+ [ {'class' => 'ModelTestDocument', '_id' => 'other_doc'},
144
+ {'class' => 'ModelTestDocument', '_id' => 'other_docs_1'},
145
+ {'class' => 'ModelTestDocument', '_id' => 'other_docs_2'}
146
+ ].each {|doc| @db.put(doc.delete('_id'), doc) }
147
+ end
148
+
149
+ expect { @obj.other_doc['_rev'].will == @db.get('other_doc')['_rev'] }
150
+ expect { @obj.other_doc.class.will == ModelTestDocument }
151
+ expect { @obj.other_docs.class.will == Array }
152
+ expect { @obj.other_docs[0]['_rev'].will == @db.get('other_docs_1')['_rev'] }
153
+ expect { @obj.other_docs[0].class.will == ModelTestDocument }
154
+ expect { @obj.other_docs[1]['_rev'].will == @db.get('other_docs_2')['_rev'] }
155
+ expect { @obj.other_docs[1].class.will == ModelTestDocument }
156
+
157
+ context "caching" do
158
+ before do
159
+ @obj.other_doc # load it
160
+ doc = @db.get('other_doc')
161
+ doc.update('foo' => 'updated')
162
+ @db.put(doc['_id'], doc.attributes)
163
+ end
164
+
165
+ expect { @obj.other_doc['foo'].will be(nil) }
166
+ expect { @obj.other_doc(true)['foo'].will == 'updated' }
167
+ end
168
+ end
169
+
170
+ context "when the document is missing" do
171
+ expect { lambda{@obj.other_doc}.will raise_error(RestClient::ResourceNotFound) }
172
+ expect { lambda{@obj.other_docs}.will raise_error(RestClient::ResourceNotFound) }
173
+ end
174
+
175
+ context "when the model has a parent" do
176
+ before do
177
+ @obj.castee = {'class' => 'FooTestModel', 'ref' => 'other_doc'}
178
+ @db.put('other_doc', {'class' => 'ModelTestDocument', '_id' => 'other_doc'})
179
+ end
180
+
181
+ expect { @obj.castee.ref['_rev'].will == @db.get('other_doc')['_rev'] }
182
+ end
183
+ end
184
+
185
+ context "without any database present" do
186
+ before { @obj = ExposeTestModel.new(:other_doc => "some_doc_id") }
187
+ expect { lambda{@obj.other_doc}.will raise_error(ArgumentError) }
188
+ end
189
+ end
190
+
191
+ context "when the value is nil" do
192
+ before do
193
+ @obj = ExposeTestModel.new({:castee => nil, :castees => nil, :regexen => ['foo', nil]})
194
+ end
195
+ expect { @obj.castee.will be(nil) }
196
+ expect { @obj.castees.will be(nil) }
197
+ expect { @obj.regexen.will == [/foo/] }
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ context "instance methods" do
204
+ before do
205
+ @obj = ExposeTestModel.new({:read_only => 'bar'})
206
+ end
207
+ context "update" do
208
+ before do
209
+ @obj.update({:read_only => 'bee'})
210
+ end
211
+
212
+ expect { @obj.read_only.will == 'bee' }
213
+ end
214
+
215
+ context "update" do
216
+ context "with a writer" do
217
+ before do
218
+ @obj.update_attributes(:foo => 'foo')
219
+ end
220
+
221
+ expect { @obj.foo.will == "foo" }
222
+ end
223
+
224
+ context "without a writer" do
225
+ expect { lambda{@obj.update_attributes({:read_only => 'bee'})}.will raise_error(NoMethodError) }
226
+ end
227
+ end
228
+ end
229
+
230
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class ExegesisServerTest < Test::Unit::TestCase
4
+
5
+ before(:all) do
6
+ @db = 'http://localhost:5984/exegesis-test'
7
+ RestClient.delete @db rescue nil
8
+ RestClient.delete "#{@db}-2" rescue nil
9
+ RestClient.put @db, ''
10
+
11
+ @server = Exegesis::Server.new('http://localhost:5984')
12
+ end
13
+
14
+ context "listing databases" do
15
+ expect { @server.databases.include?('exegesis-test').will == true }
16
+ end
17
+
18
+ context "creating a database" do
19
+ before do
20
+ @response = @server.create_database('exegesis-test-2')
21
+ end
22
+
23
+ expect { @response['ok'].will == true }
24
+ end
25
+
26
+ end
data/test/test_helper.rb CHANGED
@@ -15,25 +15,29 @@ end
15
15
  $LOAD_PATH.unshift(File.dirname(__FILE__))
16
16
  require 'lib/exegesis'
17
17
 
18
+ class TestingDatabase
19
+ include Exegesis::Database
20
+ end
21
+
18
22
  class Test::Unit::TestCase
19
23
 
20
24
  def fixtures_path fixtures
21
25
  File.join(File.dirname(__FILE__), 'fixtures', fixtures)
22
26
  end
23
27
 
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)
28
+ def db_server
29
+ @db_server ||= Exegesis::Server.new('http://localhost:5984')
29
30
  end
30
31
 
31
- def teardown_db
32
- @db.delete! rescue nil
32
+ # todo: extract to some helper methods to include ala RR, etc
33
+ def reset_db(name=nil)
34
+ RestClient.delete "http://localhost:5984/#{db(name)}" rescue nil
35
+ db_server.create_database(db(name))
36
+ @db = TestingDatabase.new(db_server, db(name))
33
37
  end
34
38
 
35
39
  def db(name)
36
- "http://localhost:5984/exegesis-test#{name.nil? ? '' : "-#{name}"}"
40
+ "exegesis-test#{name.nil? ? '' : "-#{name}"}"
37
41
  end
38
42
 
39
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mattly-exegesis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Lyon
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-08 00:00:00 -08:00
12
+ date: 2009-04-04 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: jchris-couchrest
16
+ name: rest-client
17
17
  type: :runtime
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
@@ -22,32 +22,45 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.12.6
24
24
  version:
25
- description: TODO
25
+ description: A Document <> Object Mapper for CouchDB Documents
26
26
  email: matt@flowerpowered.com
27
27
  executables: []
28
28
 
29
29
  extensions: []
30
30
 
31
- extra_rdoc_files: []
32
-
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ - LICENSE
33
34
  files:
34
35
  - README.rdoc
35
36
  - VERSION.yml
36
37
  - lib/exegesis
37
- - lib/exegesis/design
38
- - lib/exegesis/design/design_docs.rb
38
+ - lib/exegesis/database.rb
39
39
  - lib/exegesis/design.rb
40
40
  - lib/exegesis/document.rb
41
+ - lib/exegesis/model.rb
42
+ - lib/exegesis/server.rb
43
+ - lib/exegesis/utils
44
+ - lib/exegesis/utils/http.rb
41
45
  - lib/exegesis.rb
42
- - test/design_doc_test.rb
46
+ - lib/monkeypatches
47
+ - lib/monkeypatches/time.rb
48
+ - test/database_test.rb
43
49
  - test/design_test.rb
44
- - test/document_class_definitions_test.rb
45
- - test/document_instance_methods_test.rb
46
- - test/exegesis_test.rb
50
+ - test/document_test.rb
47
51
  - test/fixtures
48
52
  - test/fixtures/designs
49
53
  - test/fixtures/designs/foos.js
54
+ - test/fixtures/designs/tags
55
+ - test/fixtures/designs/tags/views
56
+ - test/fixtures/designs/tags/views/by_tag
57
+ - test/fixtures/designs/tags/views/by_tag/map.js
58
+ - test/fixtures/designs/tags/views/by_tag/reduce.js
59
+ - test/http_test.rb
60
+ - test/model_test.rb
61
+ - test/server_test.rb
50
62
  - test/test_helper.rb
63
+ - LICENSE
51
64
  has_rdoc: true
52
65
  homepage: http://github.com/mattly/exegesis
53
66
  post_install_message:
@@ -1,92 +0,0 @@
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