gridfs-rackdav 0.1.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mihael Konjević
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ ---
2
+ GridFS-RackDAV - Mongo GridFS resource for RackDAV
3
+ ---
4
+
5
+ ## Install
6
+
7
+ GridFS-RackDAV is hosted at Gemcutter:
8
+
9
+ $ sudo gem install gemcutter
10
+ $ sudo gem tumble
11
+ $ sudo gem install gridfs-rackdav
12
+
13
+ You should also have MongoDB installed.
14
+
15
+ ## Quickstart
16
+
17
+ Use simple rackup script for serving files from GridFS
18
+
19
+ @@ruby
20
+
21
+ require 'rubygems'
22
+ require 'rack_dav'
23
+ require 'gridfs-rackdav'
24
+
25
+ connection = Mongo::Connection.new('localhost').db('name-of-your-db')
26
+
27
+ use Rack::CommonLogger
28
+
29
+ run RackDAV::Handler.new({
30
+ :root => 'root_of_collection',
31
+ :connection => connection,
32
+ :resource_class => GridFSRackDAV::GridFSResource
33
+ })
34
+
35
+ ## Specs
36
+
37
+ GridFS-RackDAV resource passes all of original specs that are included with RackDAV project.
38
+
39
+ ## Copyright
40
+
41
+ Copyright (c) 2009 Mihael Konjević. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "gridfs-rackdav"
8
+ gem.summary = "GridFS resource adapter for RackDAV"
9
+ gem.description = "gridfs-rackdav enables you to use GridFS as backend for WebDAV collections with RackDAV application"
10
+ gem.email = "konjevic@gmail.com"
11
+ gem.homepage = "http://github.com/retro/gridfs-rackdav"
12
+ gem.authors = ["Mihael Konjević"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_dependency('georgi-rack_dav', '>= 0.1.1')
15
+ gem.add_dependency('mongo', '>= 0.1')
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ task :default => :spec
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "gridfs-rackdav #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{gridfs-rackdav}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mihael Konjevi\304\207"]
12
+ s.date = %q{2010-02-05}
13
+ s.description = %q{gridfs-rackdav enables you to use GridFS as backend for WebDAV collections with RackDAV application}
14
+ s.email = %q{konjevic@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "gridfs-rackdav.gemspec",
27
+ "lib/gridfs-rackdav.rb",
28
+ "lib/gridfs-rackdav/gridfs_model.rb",
29
+ "lib/gridfs-rackdav/gridfs_resource.rb",
30
+ "spec/gridfs-rackdav_spec.rb",
31
+ "spec/spec.opts",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/retro/gridfs-rackdav}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.5}
38
+ s.summary = %q{GridFS resource adapter for RackDAV}
39
+ s.test_files = [
40
+ "spec/gridfs-rackdav_spec.rb",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
50
+ s.add_runtime_dependency(%q<georgi-rack_dav>, [">= 0.1.1"])
51
+ s.add_runtime_dependency(%q<mongo>, [">= 0.1"])
52
+ else
53
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
54
+ s.add_dependency(%q<georgi-rack_dav>, [">= 0.1.1"])
55
+ s.add_dependency(%q<mongo>, [">= 0.1"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
59
+ s.add_dependency(%q<georgi-rack_dav>, [">= 0.1.1"])
60
+ s.add_dependency(%q<mongo>, [">= 0.1"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,8 @@
1
+ require 'mongo'
2
+ require 'mongo/gridfs'
3
+ require 'mime/types'
4
+ require 'rack_dav'
5
+
6
+
7
+ require 'gridfs-rackdav/gridfs_resource'
8
+ require 'gridfs-rackdav/gridfs_model'
@@ -0,0 +1,94 @@
1
+ module GridFSRackDAV
2
+ class GridFSModel
3
+ include Mongo
4
+ include GridFS
5
+
6
+ attr_reader :path, :collection, :root, :options
7
+
8
+ def initialize(resource_path, options)
9
+ @connection = options[:connection]
10
+ @collection = @connection.collection('fs.files')
11
+ self.root = options[:root]
12
+ self.path = resource_path
13
+ @options = options
14
+ end
15
+ def path=(path)
16
+ @path = ('/' + path).gsub(/\/+/, '/').strip
17
+ @path = self.root + @path if @path[0..root.length-1] != root
18
+ end
19
+ def root=(raw_root)
20
+ raw_root = '' if raw_root.nil?
21
+ raw_root = "/" + raw_root if raw_root != "/"
22
+ raw_root = raw_root[0..-2] if raw_root[-1,1] == '/'
23
+ @root = raw_root
24
+ end
25
+ def path_without_root
26
+ @path[root.length..-1]
27
+ end
28
+ def collection?
29
+ if item.nil?
30
+ path[-1,1] == '/'
31
+ else
32
+ item['filename'][-1,1] == '/'
33
+ end
34
+ end
35
+ def name
36
+ item.nil? ? path : item['filename']
37
+ end
38
+ def item
39
+ @item ||= @collection.find_one({:filename => /^(#{Regexp.escape(self.path)})\/?$/})
40
+ if @item.nil? and path == (self.root + '/').gsub(/\/+/, '/')
41
+ @item = self.write
42
+ end
43
+ @item
44
+ end
45
+
46
+ def children_names
47
+ children_names = []
48
+ if self.collection? && !item.nil?
49
+ @collection.find({:filename => /^(#{Regexp.escape(self.item['filename'])})[^\/]+(\/?)$/}).each do |r|
50
+ children_names << r['filename'] if r['filename'] != self.path
51
+ end
52
+ children_names
53
+ end
54
+ children_names
55
+ end
56
+
57
+ def children
58
+ if self.collection? && !item.nil?
59
+ @collection.find({:filename => /^(#{Regexp.escape(self.item['filename'])})[^\/]+(\/?)$/}).map do |c|
60
+ self.class.new(c['filename'], options)
61
+ end
62
+ end
63
+ end
64
+
65
+ def get_file_contents
66
+ GridStore.open(@connection, self.path, 'r') { |f| f.read }
67
+ end
68
+ def write(file_contents = '')
69
+ filename = ('/' + self.path).gsub(/\/+/, '/')
70
+ GridStore.open(@connection, filename, 'w') do |f|
71
+ f.content_type = MIME::Types.type_for(filename).first.to_s
72
+ f.content_type = 'text/html' if f.content_type.empty?
73
+ f.metadata = {
74
+ :ctime => Time.now.to_i,
75
+ :mtime => Time.now.to_i
76
+ }
77
+ f.write(file_contents)
78
+ end
79
+ @item = nil
80
+ item
81
+ end
82
+ def save
83
+ @collection.save(self.item)
84
+ end
85
+ def delete
86
+ if collection?
87
+ @collection.remove({:filename => /^(#{Regexp.escape(item['filename'])}).*/})
88
+ else
89
+ @collection.remove({:filename => /^(#{Regexp.escape(item['filename'])})\/?$/})
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,183 @@
1
+ module GridFSRackDAV
2
+ class GridFSResource < RackDAV::Resource
3
+ attr_reader :gridfs_model
4
+
5
+ DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>"
6
+
7
+ DIR_PAGE = <<-PAGE
8
+ <html><head>
9
+ <title>%s</title>
10
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
11
+ <style type='text/css'>
12
+ table { width:100%%; }
13
+ .name { text-align:left; }
14
+ .size, .mtime { text-align:right; }
15
+ .type { width:11em; }
16
+ .mtime { width:15em; }
17
+ </style>
18
+ </head><body>
19
+ <h1>%s</h1>
20
+ <hr />
21
+ <table>
22
+ <tr>
23
+ <th class='name'>Name</th>
24
+ <th class='size'>Size</th>
25
+ <th class='type'>Type</th>
26
+ <th class='mtime'>Last Modified</th>
27
+ </tr>
28
+ %s
29
+ </table>
30
+ <hr />
31
+ </body></html>
32
+ PAGE
33
+
34
+
35
+ def initialize(path, options)
36
+ raise 'You must provide MongoDB connection in options[:connection]' if options[:connection].nil?
37
+ raise 'You must provide root of collection in options[:root]' if options[:root].nil?
38
+ super(path, options)
39
+ @gridfs_model = GridFSModel.new(path, options)
40
+ end
41
+
42
+ # If this is a collection, return the child resources.
43
+ def children
44
+ @gridfs_model.children_names.map { |filename| self.class.new(filename, @options)}
45
+ end
46
+
47
+ # Is this resource a collection?
48
+ def collection?
49
+ @gridfs_model.collection?
50
+ end
51
+
52
+ # Does this recource exist?
53
+ def exist?
54
+ !@gridfs_model.item.nil?
55
+ end
56
+
57
+ # Return the creation time.
58
+ def creation_date
59
+ @gridfs_model.item['metadata'] ? Time.at(@gridfs_model.item['metadata']['mtime'].to_i) : Time.now
60
+ end
61
+
62
+ # Return the time of last modification.
63
+ def last_modified
64
+ @gridfs_model.item['metadata'] ? Time.at(@gridfs_model.item['metadata']['mtime'].to_i) : Time.now
65
+ end
66
+
67
+ # Set the time of last modification.
68
+ def last_modified=(time)
69
+ @gridfs_model.item['metadata']['mtime'] = Time.parse(time).to_i
70
+ @gridfs_model.save
71
+ end
72
+
73
+ # Return an Etag, an unique hash value for this resource.
74
+ def etag
75
+ Digest::MD5.hexdigest(
76
+ sprintf('%s-%x-%x', @gridfs_model.item['filename'], @gridfs_model.item['length'], @gridfs_model.item['metadata']['mtime'].to_i)
77
+ )
78
+ end
79
+
80
+ # Return the resource type.
81
+ #
82
+ # If this is a collection, return
83
+ # REXML::Element.new('D:collection')
84
+ def resource_type
85
+ if @gridfs_model.collection?
86
+ REXML::Element.new('D:collection')
87
+ end
88
+ end
89
+
90
+ # Return the mime type of this resource.
91
+ def content_type
92
+ @gridfs_model.item['contentType']
93
+ end
94
+
95
+ # Return the size in bytes for this resource.
96
+ def content_length
97
+ @gridfs_model.item['length']
98
+ end
99
+
100
+ # HTTP GET request.
101
+ #
102
+ # Write the content of the resource to the response.body.
103
+ # TODO: Write test for get method for collections
104
+ def get(request, response)
105
+ if @gridfs_model.collection?
106
+ files = []
107
+ if @gridfs_model.path != @options[:root] + '/'
108
+ files << DIR_FILE % [@gridfs_model.path_without_root.split('/')[0..-2].join('/') + "/", '../', "", "", ""]
109
+ end
110
+ @gridfs_model.children.each do |f|
111
+ time = Time.at(f.item['metadata']['mtime']).to_s if f.item['metadata'] and f.item['metadata']['mtime']
112
+ files << DIR_FILE % [f.path_without_root, File.basename(f.item['filename']), f.item['length'], f.item['contentType'], time]
113
+ end
114
+ response.body = DIR_PAGE % [@gridfs_model.path_without_root, @gridfs_model.path_without_root, files.join('')]
115
+ response.body.strip!
116
+
117
+ response['Content-Type'] = 'text/html'
118
+ response['Content-Length'] = response.body.size.to_s
119
+ else
120
+ response.body = @gridfs_model.get_file_contents
121
+ end
122
+ end
123
+
124
+ # HTTP PUT request.
125
+ #
126
+ # Save the content of the request.body.
127
+ def put(request, response)
128
+ write(request.body)
129
+ end
130
+
131
+ # HTTP POST request.
132
+ #
133
+ # Usually forbidden.
134
+ def post(request, response)
135
+ raise HTTPStatus::Forbidden
136
+ end
137
+
138
+ # HTTP DELETE request.
139
+ #
140
+ # Delete this resource.
141
+ def delete
142
+ @gridfs_model.delete
143
+ end
144
+
145
+ # HTTP COPY request.
146
+ #
147
+ # Copy this resource to given destination resource.
148
+ def copy(dest)
149
+ if collection?
150
+ dest.gridfs_model.path = dest.gridfs_model.path + '/'
151
+ dest.make_collection
152
+ else
153
+ dest.write(StringIO.new(@gridfs_model.get_file_contents)) unless @gridfs_model.item.nil?
154
+ end
155
+ end
156
+
157
+ # HTTP MOVE request.
158
+ #
159
+ # Move this resource to given destination resource.
160
+ def move(dest)
161
+ copy(dest)
162
+ delete
163
+ end
164
+
165
+ # HTTP MKCOL request.
166
+ #
167
+ # Create this resource as collection.
168
+ def make_collection
169
+ @gridfs_model.path = @gridfs_model.path + '/'
170
+ @gridfs_model.write
171
+ end
172
+
173
+ # Write to this resource from given IO.
174
+ def write(io)
175
+ file = ''
176
+ while part = io.read(8192)
177
+ file << part
178
+ end
179
+ @gridfs_model.write(file)
180
+ end
181
+
182
+ end
183
+ end
@@ -0,0 +1,275 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'rubygems'
3
+ require 'rack_dav'
4
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
5
+
6
+ describe RackDAV::Handler do
7
+ DOC_ROOT = 'rack_dav_test_collection'
8
+ METHODS = %w(GET PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE OPTIONS HEAD LOCK UNLOCK)
9
+
10
+ before do
11
+ @connection = Mongo::Connection.new('localhost').db('gridfs-rackdav')
12
+ @controller = RackDAV::Handler.new(:root => DOC_ROOT,
13
+ :connection => @connection,
14
+ :resource_class => GridFSRackDAV::GridFSResource
15
+ )
16
+ end
17
+
18
+ after do
19
+ @connection.collection('fs.files').drop
20
+ @connection.collection('fs.chunks').drop
21
+ end
22
+
23
+ attr_reader :response
24
+
25
+ def request(method, uri, options={})
26
+ options = {
27
+ 'HTTP_HOST' => 'localhost',
28
+ 'REMOTE_USER' => 'manni'
29
+ }.merge(options)
30
+ request = Rack::MockRequest.new(@controller)
31
+ @response = request.request(method, uri, options)
32
+ end
33
+
34
+ METHODS.each do |method|
35
+ define_method(method.downcase) do |*args|
36
+ request(method, *args)
37
+ end
38
+ end
39
+
40
+ def render
41
+ xml = Builder::XmlMarkup.new
42
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
43
+ xml.namespace('d') do
44
+ yield xml
45
+ end
46
+ xml.target!
47
+ end
48
+
49
+ def url_escape(string)
50
+ string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
51
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
52
+ end.tr(' ', '+')
53
+ end
54
+
55
+ def response_xml
56
+ REXML::Document.new(@response.body)
57
+ end
58
+
59
+ def multistatus_response(pattern)
60
+ @response.should be_multi_status
61
+ REXML::XPath::match(response_xml, "/multistatus/response", '' => 'DAV:').should_not be_empty
62
+ REXML::XPath::match(response_xml, "/multistatus/response" + pattern, '' => 'DAV:')
63
+ end
64
+
65
+ def propfind_xml(*props)
66
+ render do |xml|
67
+ xml.propfind('xmlns:d' => "DAV:") do
68
+ xml.prop do
69
+ props.each do |prop|
70
+ xml.tag! prop
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ it 'should return all options' do
78
+ options('/').should be_ok
79
+
80
+ METHODS.each do |method|
81
+ response.headers['allow'].should include(method)
82
+ end
83
+ end
84
+
85
+ it 'should return headers' do
86
+ put('/test.html', :input => '<html/>').should be_ok
87
+ head('/test.html').should be_ok
88
+ response.headers['etag'].should_not be_nil
89
+ response.headers['content-type'].should match(/html/)
90
+ response.headers['last-modified'].should_not be_nil
91
+ end
92
+
93
+ it 'should not find a nonexistent resource' do
94
+ get('/not_found').should be_not_found
95
+ end
96
+
97
+ it 'should not allow directory traversal' do
98
+ get('/../htdocs').should be_forbidden
99
+ end
100
+
101
+ it 'should create a resource and allow its retrieval' do
102
+ put('/test', :input => 'body').should be_ok
103
+ get('/test').should be_ok
104
+ response.body.should == 'body'
105
+ end
106
+
107
+ it 'should create and find a url with escaped characters' do
108
+ put(url_escape('/a b'), :input => 'body').should be_ok
109
+ get(url_escape('/a b')).should be_ok
110
+ response.body.should == 'body'
111
+ end
112
+
113
+ it 'should delete a single resource' do
114
+ put('/test', :input => 'body').should be_ok
115
+ delete('/test').should be_no_content
116
+ end
117
+
118
+ it 'should delete recursively' do
119
+ mkcol('/folder').should be_created
120
+ put('/folder/a', :input => 'body').should be_ok
121
+ put('/folder/b', :input => 'body').should be_ok
122
+
123
+ delete('/folder').should be_no_content
124
+ get('/folder').should be_not_found
125
+ get('/folder/a').should be_not_found
126
+ get('/folder/b').should be_not_found
127
+ end
128
+
129
+
130
+ it 'should not allow copy to another domain' do
131
+ put('/test', :input => 'body').should be_ok
132
+ copy('http://localhost/', 'HTTP_DESTINATION' => 'http://another/').should be_bad_gateway
133
+ end
134
+
135
+ it 'should not allow copy to the same resource' do
136
+ put('/test', :input => 'body').should be_ok
137
+ copy('/test', 'HTTP_DESTINATION' => '/test').should be_forbidden
138
+ end
139
+
140
+ it 'should not allow an invalid destination uri' do
141
+ put('/test', :input => 'body').should be_ok
142
+ copy('/test', 'HTTP_DESTINATION' => '%').should be_bad_request
143
+ end
144
+
145
+ it 'should copy a single resource' do
146
+ put('/test', :input => 'body').should be_ok
147
+ copy('/test', 'HTTP_DESTINATION' => '/copy').should be_created
148
+ get('/copy').body.should == 'body'
149
+ end
150
+
151
+ it 'should copy a resource with escaped characters' do
152
+ put(url_escape('/a b'), :input => 'body').should be_ok
153
+ copy(url_escape('/a b'), 'HTTP_DESTINATION' => url_escape('/a c')).should be_created
154
+ get(url_escape('/a c')).should be_ok
155
+ response.body.should == 'body'
156
+ end
157
+
158
+ it 'should deny a copy without overwrite' do
159
+ put('/test', :input => 'body').should be_ok
160
+ put('/copy', :input => 'copy').should be_ok
161
+ copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'F')
162
+
163
+ multistatus_response('/href').first.text.should == 'http://localhost/test'
164
+ multistatus_response('/status').first.text.should match(/412 Precondition Failed/)
165
+
166
+ get('/copy').body.should == 'copy'
167
+ end
168
+
169
+ it 'should allow a copy with overwrite' do
170
+ put('/test', :input => 'body').should be_ok
171
+ put('/copy', :input => 'copy').should be_ok
172
+ copy('/test', 'HTTP_DESTINATION' => '/copy', 'HTTP_OVERWRITE' => 'T').should be_no_content
173
+ get('/copy').body.should == 'body'
174
+ end
175
+
176
+ it 'should copy a collection' do
177
+ mkcol('/folder').should be_created
178
+ copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
179
+ propfind('/copy', :input => propfind_xml(:resourcetype))
180
+ multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
181
+ end
182
+
183
+ it 'should copy a collection resursively' do
184
+ mkcol('/folder').should be_created
185
+ put('/folder/a', :input => 'A').should be_ok
186
+ put('/folder/b', :input => 'B').should be_ok
187
+
188
+ copy('/folder', 'HTTP_DESTINATION' => '/copy').should be_created
189
+ propfind('/copy', :input => propfind_xml(:resourcetype))
190
+ multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
191
+
192
+ get('/copy/a').body.should == 'A'
193
+ get('/copy/b').body.should == 'B'
194
+ end
195
+
196
+ it 'should move a collection recursively' do
197
+ mkcol('/folder').should be_created
198
+ put('/folder/a', :input => 'A').should be_ok
199
+ put('/folder/b', :input => 'B').should be_ok
200
+
201
+ move('/folder', 'HTTP_DESTINATION' => '/move').should be_created
202
+ propfind('/move', :input => propfind_xml(:resourcetype))
203
+ multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
204
+
205
+ get('/move/a').body.should == 'A'
206
+ get('/move/b').body.should == 'B'
207
+ get('/folder/a').should be_not_found
208
+ get('/folder/b').should be_not_found
209
+ end
210
+
211
+ it 'should create a collection' do
212
+ mkcol('/folder').should be_created
213
+ propfind('/folder', :input => propfind_xml(:resourcetype))
214
+ multistatus_response('/propstat/prop/resourcetype/collection').should_not be_empty
215
+ end
216
+
217
+ it 'should not find properties for nonexistent resources' do
218
+ propfind('/non').should be_not_found
219
+ end
220
+
221
+ it 'should find all properties' do
222
+ xml = render do |xml|
223
+ xml.propfind('xmlns:d' => "DAV:") do
224
+ xml.allprop
225
+ end
226
+ end
227
+
228
+ propfind('http://localhost/', :input => xml)
229
+
230
+ multistatus_response('/href').first.text.strip.should == 'http://localhost/'
231
+
232
+ props = %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength)
233
+ props.each do |prop|
234
+ multistatus_response('/propstat/prop/' + prop).should_not be_empty
235
+ end
236
+ end
237
+
238
+ it 'should find named properties' do
239
+ put('/test.html', :input => '<html/>').should be_ok
240
+ propfind('/test.html', :input => propfind_xml(:getcontenttype, :getcontentlength))
241
+
242
+ multistatus_response('/propstat/prop/getcontenttype').first.text.should == 'text/html'
243
+ multistatus_response('/propstat/prop/getcontentlength').first.text.should == '7'
244
+ end
245
+
246
+ it 'should lock a resource' do
247
+ put('/test', :input => 'body').should be_ok
248
+
249
+ xml = render do |xml|
250
+ xml.lockinfo('xmlns:d' => "DAV:") do
251
+ xml.lockscope { xml.exclusive }
252
+ xml.locktype { xml.write }
253
+ xml.owner { xml.href "http://test.de/" }
254
+ end
255
+ end
256
+
257
+ lock('/test', :input => xml)
258
+
259
+ response.should be_ok
260
+
261
+ match = lambda do |pattern|
262
+ REXML::XPath::match(response_xml, "/prop/lockdiscovery/activelock" + pattern, '' => 'DAV:')
263
+ end
264
+
265
+ match[''].should_not be_empty
266
+
267
+ match['/locktype'].should_not be_empty
268
+ match['/lockscope'].should_not be_empty
269
+ match['/depth'].should_not be_empty
270
+ match['/owner'].should_not be_empty
271
+ match['/timeout'].should_not be_empty
272
+ match['/locktoken'].should_not be_empty
273
+ end
274
+
275
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'gridfs-rackdav'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gridfs-rackdav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - "Mihael Konjevi\xC4\x87"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-05 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: georgi-rack_dav
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: mongo
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0.1"
44
+ version:
45
+ description: gridfs-rackdav enables you to use GridFS as backend for WebDAV collections with RackDAV application
46
+ email: konjevic@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.md
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - VERSION
61
+ - gridfs-rackdav.gemspec
62
+ - lib/gridfs-rackdav.rb
63
+ - lib/gridfs-rackdav/gridfs_model.rb
64
+ - lib/gridfs-rackdav/gridfs_resource.rb
65
+ - spec/gridfs-rackdav_spec.rb
66
+ - spec/spec.opts
67
+ - spec/spec_helper.rb
68
+ has_rdoc: true
69
+ homepage: http://github.com/retro/gridfs-rackdav
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --charset=UTF-8
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.3.5
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: GridFS resource adapter for RackDAV
96
+ test_files:
97
+ - spec/gridfs-rackdav_spec.rb
98
+ - spec/spec_helper.rb