mattly-exegesis 0.2.6 → 0.2.8

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.
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 2
4
- :patch: 7
4
+ :patch: 8
@@ -22,10 +22,6 @@ module Exegesis
22
22
 
23
23
  extend self
24
24
 
25
- def model_classes
26
- @model_classes ||= {}
27
- end
28
-
29
25
  # extracted from Extlib
30
26
  #
31
27
  # Constantize tries to find a declared constant with the name specified
@@ -44,6 +40,11 @@ module Exegesis
44
40
  Object.module_eval("::#{$1}", __FILE__, __LINE__)
45
41
  end
46
42
 
43
+ # turns class/module names into valid database names
44
+ def nameify(name)
45
+ name.gsub(/([a-z])([A-Z])/) { "#{$1}_#{$2.downcase}"}.gsub(/[A-Z]/) {|m| m.downcase }.gsub(/::/,'/')
46
+ end
47
+
47
48
  def instantiate(doc, database)
48
49
  doc = constantize(doc['class']).new(doc)
49
50
  doc.database = database if doc.respond_to?(:database=)
@@ -1,66 +1,33 @@
1
1
  require 'pathname'
2
2
  module Exegesis
3
3
  module Database
4
+ autoload :Designs, 'exegesis/database/designs'
5
+ autoload :Documents, 'exegesis/database/documents'
6
+ autoload :Singleton, 'exegesis/database/singleton'
7
+ autoload :Rest, 'exegesis/database/rest'
4
8
 
5
9
  VALID_NAME_PATTERN = '[-a-z0-9_\$\(\)\+\/]+'
10
+ URI_PATTERN = /\A(https?:\/\/[-0-9a-z\.]+(?::\d+))\/(#{Exegesis::Database::VALID_NAME_PATTERN})\Z/
11
+ NAME_PATTERN = /\A#{Exegesis::Database::VALID_NAME_PATTERN}\Z/
6
12
 
7
13
  def self.included base
8
14
  base.send :attr_accessor, :server, :uri
9
15
  base.send :include, InstanceMethods
10
- base.extend ClassMethods
11
- end
12
-
13
- module ClassMethods
14
- def designs_directory dir=nil
15
- if dir
16
- @designs_directory = Pathname.new(dir)
17
- else
18
- @designs_directory ||= Pathname.new('designs')
19
- end
20
- end
21
-
22
- # declare a design document for this database. Creates a new class and yields a given block to the class to
23
- # configure the design document and declare views; See Class methods for Exegesis::Design
24
- def design design_name, opts={}, &block
25
- klass_name = "#{design_name.to_s.capitalize}Design"
26
- klass = const_set(klass_name, Class.new(Exegesis::Design))
27
- klass.design_directory = opts[:directory] || self.designs_directory + design_name.to_s
28
- klass.design_name = opts[:name] || design_name.to_s
29
- klass.compose_canonical
30
- klass.class_eval &block
31
- define_method design_name do
32
- @exegesis_designs ||= {}
33
- @exegesis_designs[design_name] ||= klass.new(self)
34
- end
35
- end
36
-
37
- def named_document document_name, opts={}, &block
38
- klass_name = document_name.to_s.capitalize.gsub(/_(\w)/) { $1.capitalize }
39
- klass = const_set(klass_name, Class.new(Exegesis::GenericDocument))
40
- klass.unique_id { document_name.to_s }
41
- klass.class_eval &block if block
42
- define_method document_name do
43
- @exegesis_named_documents ||= {}
44
- @exegesis_named_documents[document_name] ||= begin
45
- get(document_name.to_s)
46
- rescue RestClient::ResourceNotFound
47
- doc = klass.new({}, self)
48
- doc.save
49
- doc
50
- end
51
- end
52
- end
16
+ base.extend Exegesis::Database::Designs
17
+ base.extend Exegesis::Database::Documents
53
18
  end
54
19
 
55
20
  module InstanceMethods
21
+ include Exegesis::Database::Rest
22
+
56
23
  # Create a Database adapter for the given server and database name. Will raise
57
24
  # RestClient::ResourceNotFound if the database does not exist.
58
25
  def initialize server, database_name=nil
59
26
  if database_name.nil?
60
- if server.match(/\A(https?:\/\/[-0-9a-z\.]+(?::\d+))\/(#{Exegesis::Database::VALID_NAME_PATTERN})\Z/)
27
+ if server.match(URI_PATTERN)
61
28
  @server = Exegesis::Server.new($1)
62
29
  database_name = $2
63
- elsif server.match(/\A#{Exegesis::Database::VALID_NAME_PATTERN}\Z/)
30
+ elsif server.match(NAME_PATTERN)
64
31
  @server = Exegesis::Server.new #localhost
65
32
  database_name = server
66
33
  else
@@ -78,56 +45,6 @@ module Exegesis
78
45
  end
79
46
  alias :inspect :to_s
80
47
 
81
- # performs a raw GET request against the database
82
- def raw_get id, options={}
83
- keys = options.delete(:keys)
84
- id = Exegesis::Http.escape_id id
85
- url = Exegesis::Http.format_url "#{@uri}/#{id}", options
86
- if id.match(%r{^_design/.*/_view/.*$}) && keys
87
- Exegesis::Http.post url, {:keys => keys}.to_json
88
- else
89
- Exegesis::Http.get url
90
- end
91
- end
92
-
93
- # GETs a document with the given id from the database
94
- def get id, opts={}
95
- if id.kind_of?(Array)
96
- collection = opts.delete(:collection) # nil or true for yes, false for no
97
- r = post '_all_docs?include_docs=true', {"keys"=>id}
98
- r['rows'].map {|d| Exegesis.instantiate d['doc'], self }
99
- else
100
- Exegesis.instantiate raw_get(id), self
101
- end
102
- end
103
-
104
- # saves a document or collection thereof
105
- def save docs
106
- if docs.is_a?(Array)
107
- post "_bulk_docs", { 'docs' => docs }
108
- else
109
- result = docs['_id'].nil? ? post(docs) : put(docs['_id'], docs)
110
- if result['ok']
111
- docs['_id'] = result['id']
112
- docs['_rev'] = result['rev']
113
- end
114
- docs
115
- end
116
- end
117
-
118
- # PUTs the body to the given id in the database
119
- def put id, body, headers={}
120
- Exegesis::Http.put "#{@uri}/#{id}", (body || '').to_json, headers
121
- end
122
-
123
- # POSTs the body to the database
124
- def post url, body={}, headers={}
125
- if body.is_a?(Hash) && body.empty?
126
- body = url
127
- url = ''
128
- end
129
- Exegesis::Http.post "#{@uri}/#{url}", (body || '').to_json, headers
130
- end
131
48
  end
132
49
  end
133
50
  end
@@ -0,0 +1,31 @@
1
+ # tests for this module are in test/design_test.rb
2
+ module Exegesis
3
+ module Database
4
+ module Designs
5
+
6
+ # set the directory where the designs will be, relative to ENV['PWD']
7
+ def designs_directory dir=nil
8
+ if dir
9
+ @designs_directory = Pathname.new(dir)
10
+ else
11
+ @designs_directory ||= Pathname.new('designs')
12
+ end
13
+ end
14
+
15
+ # declare a design document for this database. Creates a new class and yields a given block to the class to
16
+ # configure the design document and declare views; See Class methods for Exegesis::Design
17
+ def design design_name, opts={}, &block
18
+ klass_name = "#{design_name.to_s.capitalize}Design"
19
+ klass = const_set(klass_name, Class.new(Exegesis::Design))
20
+ klass.design_directory = opts[:directory] || self.designs_directory + design_name.to_s
21
+ klass.design_name = opts[:name] || design_name.to_s
22
+ klass.compose_canonical
23
+ klass.class_eval &block if block_given?
24
+ define_method design_name do
25
+ @exegesis_designs ||= {}
26
+ @exegesis_designs[design_name] ||= klass.new(self)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module Exegesis
2
+ module Database
3
+ module Documents
4
+
5
+ # creates a one-off document
6
+ def document document_name, opts={}, &block
7
+ klass_name = document_name.to_s.capitalize.gsub(/_(\w)/) { $1.capitalize }
8
+ klass = const_set(klass_name, Class.new(Exegesis::GenericDocument))
9
+ klass.unique_id { document_name.to_s }
10
+ klass.class_eval &block if block_given?
11
+ define_method document_name do
12
+ @exegesis_named_documents ||= {}
13
+ @exegesis_named_documents[document_name] ||= begin
14
+ get(document_name.to_s)
15
+ rescue RestClient::ResourceNotFound
16
+ doc = klass.new({}, self)
17
+ doc.save
18
+ doc
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,62 @@
1
+ module Exegesis
2
+ module Database
3
+ module Rest
4
+ # performs a raw GET request against the database
5
+ def raw_get(id, options={})
6
+ keys = options.delete(:keys)
7
+ id = Exegesis::Http.escape_id id
8
+ url = Exegesis::Http.format_url "#{@uri}/#{id}", options
9
+ if id.match(%r{^_design/.*/_view/.*$}) && keys
10
+ Exegesis::Http.post url, {:keys => keys}.to_json
11
+ else
12
+ Exegesis::Http.get url
13
+ end
14
+ end
15
+
16
+ # GETs a document with the given id from the database
17
+ def get(id, opts={})
18
+ if id.kind_of?(Array)
19
+ collection = opts.delete(:collection) # nil or true for yes, false for no
20
+ r = post '_all_docs?include_docs=true', {"keys"=>id}
21
+ r['rows'].map {|d| Exegesis.instantiate d['doc'], self }
22
+ else
23
+ Exegesis.instantiate raw_get(id), self
24
+ end
25
+ end
26
+
27
+ # saves a document or collection thereof
28
+ def save(docs)
29
+ if docs.is_a?(Array)
30
+ post "_bulk_docs", { 'docs' => docs }
31
+ else
32
+ result = docs['_id'] ? put(docs['_id'], docs) : post(docs)
33
+ if result['ok']
34
+ docs['_id'] = result['id']
35
+ docs['_rev'] = result['rev']
36
+ end
37
+ docs
38
+ end
39
+ end
40
+
41
+ # PUTs the body to the given id in the database
42
+ def put(id, body, headers={})
43
+ Exegesis::Http.put "#{@uri}/#{id}", (body || '').to_json, headers
44
+ end
45
+
46
+ # POSTs the body to the database
47
+ def post(url, body={}, headers={})
48
+ if body.is_a?(Hash) && body.empty?
49
+ body = url
50
+ url = ''
51
+ end
52
+ Exegesis::Http.post "#{@uri}/#{url}", (body || '').to_json, headers
53
+ end
54
+
55
+ # DELETE the doc from the database. requires a hash with _id and _rev keys
56
+ def delete(doc={})
57
+ raise ArgumentError, "doc must have both '_id' and '_rev' keys" unless doc['_id'] && doc['_rev']
58
+ Exegesis::Http.delete "#{@uri}/#{doc['_id']}?rev=#{doc['_rev']}"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,36 @@
1
+ module Exegesis
2
+ module Database
3
+ module Singleton
4
+
5
+ include Exegesis::Database::InstanceMethods
6
+ include Exegesis::Database::Designs
7
+ include Exegesis::Database::Documents
8
+
9
+ def uri(addr=nil)
10
+ if addr
11
+ if addr.match(Exegesis::Database::URI_PATTERN)
12
+ @server = Exegesis::Server.new($1)
13
+ @uri = "#{@server.uri}/#{$2}"
14
+ elsif addr.match(Exegesis::Database::NAME_PATTERN)
15
+ @server = Exegesis::Server.new
16
+ @uri = "#{@server.uri}/#{addr}"
17
+ else
18
+ raise ArgumentError, "does not match a valid database name/uri pattern"
19
+ end
20
+ begin
21
+ @server.get @uri
22
+ rescue RestClient::ResourceNotFound
23
+ @server.put @uri
24
+ end
25
+ @uri
26
+ else
27
+ @uri || uri(Exegesis.nameify(name))
28
+ end
29
+ end
30
+
31
+ def server
32
+ @server
33
+ end
34
+ end
35
+ end
36
+ end
@@ -14,6 +14,18 @@ module Exegesis
14
14
  end
15
15
 
16
16
  module ClassMethods
17
+ def database(db=nil)
18
+ if db
19
+ if db.is_a?(Exegesis::Database::Singleton) || db.is_a?(Exegesis::Database)
20
+ @database = db
21
+ else
22
+ raise ArgumentError, "please supply either an Exegesis::Database or Exegesis::Database::Singleton"
23
+ end
24
+ else
25
+ @database
26
+ end
27
+ end
28
+
17
29
  def timestamps!
18
30
  define_method :set_timestamps do
19
31
  @attributes['updated_at'] = Time.now
@@ -37,7 +49,7 @@ module Exegesis
37
49
  module InstanceMethods
38
50
  def initialize hash={}, db=nil
39
51
  super hash
40
- @database = db
52
+ @database = db || self.class.database
41
53
  end
42
54
 
43
55
  def uri
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
2
+
3
+ class DatabaseDocumentsTest
4
+ include Exegesis::Database
5
+
6
+ document :settings do
7
+ expose :things
8
+ end
9
+ document :no_block
10
+ end
11
+
12
+ describe Exegesis::Database::Documents do
13
+
14
+ before do
15
+ reset_db
16
+ @db = DatabaseDocumentsTest.new('exegesis-test')
17
+ end
18
+
19
+ describe "with named documents" do
20
+ describe "that doesn't exist yet" do
21
+ before do
22
+ @db.settings
23
+ end
24
+
25
+ expect { @db.settings.must_be_kind_of DatabaseDocumentsTest::Settings }
26
+ expect { @db.settings.rev.must_match /1-\d{7,12}/ }
27
+ expect { @db.settings.must_respond_to :things }
28
+ expect { @db.get('settings').must_be_kind_of DatabaseDocumentsTest::Settings }
29
+ end
30
+
31
+ describe "that does exist" do
32
+ before do
33
+ @doc = @db.save({'_id' => 'settings', 'things' => %w(foo bar baz), 'class' => 'DatabaseDocumentsTest::Settings'})
34
+ end
35
+
36
+ expect { @db.settings.rev.must_equal @doc['_rev'] }
37
+ expect { @db.settings.rev.must_match /1-\d{7,12}/ }
38
+ expect { @db.settings.things.must_equal %w(foo bar baz) }
39
+ end
40
+
41
+ describe "when the declaration does not have a block" do
42
+ expect { @db.no_block.must_be_kind_of DatabaseDocumentsTest::NoBlock }
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
2
+
3
+ Exegesis::Http.delete("#{@server.uri}/exegesis-singleton-test") rescue nil
4
+
5
+ module SingletonDbTest
6
+ extend self
7
+
8
+ COUCH = 'http://localhost:5984'
9
+ DB = 'exegesis-singleton-test'
10
+ URI = "#{COUCH}/#{DB}"
11
+
12
+ extend Exegesis::Database::Singleton
13
+ uri DB
14
+
15
+ design :design_doc
16
+ document :named_doc
17
+ end
18
+
19
+ module SingletonDbDefaultUriTest
20
+ extend self
21
+ extend Exegesis::Database::Singleton
22
+ end
23
+
24
+ describe Exegesis::Database::Singleton do
25
+
26
+ describe "database setup" do
27
+ expect { SingletonDbTest.uri.must_equal SingletonDbTest::URI }
28
+ expect { Exegesis::Http.get(SingletonDbTest::URI)["db_name"].must_equal SingletonDbTest::DB }
29
+ expect { lambda{SingletonDbTest.uri('foo.db')}.must_raise ArgumentError }
30
+
31
+ expect { SingletonDbDefaultUriTest.uri.must_equal "http://localhost:5984/singleton_db_default_uri_test" }
32
+ end
33
+
34
+ describe "database REST methods" do
35
+ before do
36
+ Exegesis::Http.delete(SingletonDbTest::URI) rescue nil
37
+ Exegesis::Http.put(SingletonDbTest::URI)
38
+ @test_doc = {"_id" => "test", "foo" => "bar"}
39
+ response = Exegesis::Http.put("#{SingletonDbTest::URI}/test", @test_doc.to_json)
40
+ @test_doc['_rev'] = response['rev']
41
+ end
42
+ expect { SingletonDbTest.get("test")['foo'].must_equal @test_doc['foo'] }
43
+ expect { SingletonDbTest.post({"foo" => "bar"})["id"].must_match /[0-9a-f]{32}/ }
44
+ expect { SingletonDbTest.put("foo", {"_id" => "foo"})["id"].must_equal "foo" }
45
+ expect { SingletonDbTest.delete(@test_doc)["ok"].must_equal true }
46
+ end
47
+
48
+ describe "database class methods" do
49
+ expect { SingletonDbTest.design_doc.must_be_kind_of Exegesis::Design }
50
+ expect { SingletonDbTest.named_doc.must_be_kind_of Exegesis::GenericDocument }
51
+ end
52
+ end
@@ -3,20 +3,12 @@ require File.join(File.dirname(__FILE__), 'test_helper.rb')
3
3
  class DatabaseTest
4
4
  include Exegesis::Database
5
5
  end
6
+
6
7
  class CustomDesignDirDatabaseTest
7
8
  include Exegesis::Database
8
9
  designs_directory 'app/designs'
9
10
  end
10
- class NamedDocumentDatabaseTest
11
- include Exegesis::Database
12
- named_document :settings do
13
- expose :things
14
- end
15
- end
16
- class NamedDocumentWithoutBlockDatabaseTest
17
- include Exegesis::Database
18
- named_document :blah
19
- end
11
+
20
12
  class DatabaseTestDocument
21
13
  include Exegesis::Document
22
14
  end
@@ -179,40 +171,4 @@ describe Exegesis::Database do
179
171
  expect { CustomDesignDirDatabaseTest.designs_directory.must_equal Pathname.new('app/designs') }
180
172
  end
181
173
 
182
- describe "with a named document" do
183
- describe "that doesn't exist yet" do
184
- before do
185
- reset_db
186
- @db = NamedDocumentDatabaseTest.new('exegesis-test')
187
- @db.settings
188
- end
189
-
190
- expect { @db.settings.must_be_kind_of NamedDocumentDatabaseTest::Settings }
191
- expect { @db.settings.rev.must_match /1-\d{7,12}/ }
192
- expect { @db.settings.must_respond_to :things }
193
- expect { @db.get('settings').must_be_kind_of NamedDocumentDatabaseTest::Settings }
194
- end
195
-
196
- describe "that does exist" do
197
- before do
198
- reset_db
199
- @db = NamedDocumentDatabaseTest.new('exegesis-test')
200
- @doc = @db.save({'_id' => 'settings', 'things' => %w(foo bar baz), 'class' => 'NamedDocumentDatabaseTest::Settings'})
201
- end
202
-
203
- expect { @db.settings.rev.must_equal @doc['_rev'] }
204
- expect { @db.settings.rev.must_match /1-\d{7,12}/ }
205
- expect { @db.settings.things.must_equal %w(foo bar baz) }
206
- end
207
-
208
- describe "when the declaration does not have a block" do
209
- before do
210
- reset_db
211
- @db = NamedDocumentWithoutBlockDatabaseTest.new('exegesis-test')
212
- end
213
-
214
- expect { @db.blah.must_be_kind_of NamedDocumentWithoutBlockDatabaseTest::Blah }
215
- end
216
- end
217
-
218
- end
174
+ end
@@ -23,9 +23,29 @@ class UniqueIdBlockTestDocument
23
23
  unique_id {|doc, attempt| attempt.zero? ? doc['pk'] : "#{doc['pk']}-#{attempt}" }
24
24
  end
25
25
 
26
+ module DocumentSingletonDatabaseTest
27
+ extend self
28
+ extend Exegesis::Database::Singleton
29
+ end
30
+ class SingletonDatabaseDocument
31
+ include Exegesis::Document
32
+ database DocumentSingletonDatabaseTest
33
+ end
34
+
26
35
  describe Exegesis::Document do
27
36
 
28
37
  describe "class definitions" do
38
+ describe "with database declarations" do
39
+ before do
40
+ @doc = SingletonDatabaseDocument.new
41
+ end
42
+
43
+ expect { SingletonDatabaseDocument.database.must_equal DocumentSingletonDatabaseTest }
44
+ expect { @doc.database.must_equal DocumentSingletonDatabaseTest }
45
+
46
+ expect { lambda{SingletonDatabaseDocument.database("foo")}.must_raise(ArgumentError) }
47
+ end
48
+
29
49
  describe "with timestamps" do
30
50
  before do
31
51
  reset_db
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.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Lyon
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-12 00:00:00 -07:00
12
+ date: 2009-06-12 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -38,6 +38,10 @@ files:
38
38
  - VERSION.yml
39
39
  - lib/exegesis.rb
40
40
  - lib/exegesis/database.rb
41
+ - lib/exegesis/database/designs.rb
42
+ - lib/exegesis/database/documents.rb
43
+ - lib/exegesis/database/rest.rb
44
+ - lib/exegesis/database/singleton.rb
41
45
  - lib/exegesis/design.rb
42
46
  - lib/exegesis/document.rb
43
47
  - lib/exegesis/document/attachment.rb
@@ -49,6 +53,8 @@ files:
49
53
  - lib/exegesis/utils/http.rb
50
54
  - lib/monkeypatches/time.rb
51
55
  - test/attachments_test.rb
56
+ - test/database/documents_test.rb
57
+ - test/database/singleton_test.rb
52
58
  - test/database_test.rb
53
59
  - test/design_test.rb
54
60
  - test/document_collection_test.rb
@@ -91,6 +97,8 @@ specification_version: 3
91
97
  summary: A Document <> Object Mapper for CouchDB Documents
92
98
  test_files:
93
99
  - test/attachments_test.rb
100
+ - test/database/documents_test.rb
101
+ - test/database/singleton_test.rb
94
102
  - test/database_test.rb
95
103
  - test/design_test.rb
96
104
  - test/document_collection_test.rb