gridfs-rackdav 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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