open-uri-and-write 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +13 -0
- data/README.md +96 -40
- data/lib/open-uri-and-write.rb +6 -234
- data/lib/open-uri-and-write/credentials_store.rb +23 -0
- data/lib/open-uri-and-write/dir_extensions.rb +46 -0
- data/lib/open-uri-and-write/file_extensions.rb +52 -0
- data/lib/open-uri-and-write/handle.rb +106 -0
- data/lib/open-uri-and-write/kernel_extensions.rb +28 -0
- data/lib/open-uri-and-write/usernames.rb +51 -0
- data/lib/open-uri-and-write/version.rb +1 -1
- data/spec/integration/open-uri-and-write-spec.rb +28 -3
- metadata +19 -12
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2012 Thomas Flemming
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
CHANGED
@@ -3,9 +3,10 @@ OpenUriAndWrite
|
|
3
3
|
|
4
4
|
OpenUriAndWrite is an easy to use wrapper for Net::Dav, making it as easy to write to WebDAV enabled webservers as local files.
|
5
5
|
|
6
|
-
|
6
|
+
Examples
|
7
|
+
--------
|
7
8
|
|
8
|
-
It is possible to open an http
|
9
|
+
It is possible to open an http/https URL and write to it as though it were a local file:
|
9
10
|
|
10
11
|
```ruby
|
11
12
|
open("http://www.ruby-lang.org/open_uri_and_write.html","w") {|f|
|
@@ -13,7 +14,7 @@ It is possible to open an http, https URL and write to it as though it were a fi
|
|
13
14
|
}
|
14
15
|
```
|
15
16
|
|
16
|
-
|
17
|
+
With method chaining it gets more compact:
|
17
18
|
|
18
19
|
```ruby
|
19
20
|
open("http://www.ruby-lang.org/open_uri_and_write.html","w").puts "<h1>OpenUriAndWrite</h1>"
|
@@ -25,46 +26,31 @@ Files can be deleted as local files:
|
|
25
26
|
File.delete("http://www.ruby-lang.org/open_uri_and_write.html")
|
26
27
|
```
|
27
28
|
|
28
|
-
Directories are created the same way as local files
|
29
|
+
Directories are created the same way as local files:
|
29
30
|
|
30
31
|
```ruby
|
31
32
|
Dir.mkdir("http://www.ruby-lang.org/open_uri_and_write")
|
32
33
|
```
|
33
34
|
|
34
|
-
|
35
|
+
Authentication
|
36
|
+
--------------
|
37
|
+
By default the scripts prompts the user for username and password. The username and hostname are stored in the file ~/.open-uri-and-write-usernames, so the next time only the password has to be typed in. On OSX the password is stored encrypted in the keychain.
|
35
38
|
|
36
|
-
|
39
|
+
Credentials can also supplied as environment variables or options.
|
37
40
|
|
38
|
-
|
39
|
-
File.open("http://www.ruby-lang.org/open_uri_and_write.html","w").proppatch('<o:Author>Douglas Groncki</o:Author>')
|
40
|
-
props = Dir.propfind("http://www.ruby-lang.org") # Returns XML
|
41
|
-
```
|
42
|
-
|
43
|
-
# Interoperability with OpenURI
|
44
|
-
|
45
|
-
To not interfer with the 'open-uri' standard library, the 'open-uri-and-write' gem is only active in file modes 'w','a','w+' and 'a+':
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
open("http://www.ruby-lang.org/open_uri_and_write.html","w").puts("<h1>HTML</h1>") # open-uri-and-write
|
49
|
-
```
|
50
|
-
|
51
|
-
If not any filemode is supplied, 'open-uri' is used:
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
puts open("http://www.ruby-lang.org").read() # open-uri
|
55
|
-
```
|
56
|
-
|
57
|
-
# Authentication
|
58
|
-
|
59
|
-
By default 'open-uri-and-write' will prompt for username and password.
|
41
|
+
Default behaviour if no username or password is set:
|
60
42
|
|
61
43
|
```
|
62
44
|
$ ruby webdav_test.rb
|
63
45
|
Username for www.example.com: scott
|
64
46
|
Password for 'scott@www.example.com: *****
|
47
|
+
Username and hostname stored in /Users/thomasf/.open-uri-and-write-usernames
|
48
|
+
|
49
|
+
$ ruby webdav_test.rb
|
50
|
+
Password for 'scott@www.example.com: *****
|
65
51
|
```
|
66
52
|
|
67
|
-
|
53
|
+
Supplying credentials with the DAVUSER and DAVPASS environment variables:
|
68
54
|
|
69
55
|
```
|
70
56
|
$ export DAVUSER=scott
|
@@ -72,19 +58,20 @@ Credentials can be supplied with the DAVUSER and DAVPASS environment variables.
|
|
72
58
|
$ ruby webdav_test.rb
|
73
59
|
```
|
74
60
|
|
61
|
+
Setting username and password in ruby:
|
62
|
+
|
75
63
|
```ruby
|
76
64
|
ENV['DAVUSER'] = 'scott'
|
77
65
|
ENV['DAVPASS'] = 'tiger'
|
78
66
|
```
|
79
67
|
|
80
|
-
|
81
|
-
Another option is to supply username and password as arguments to open():
|
68
|
+
Another option is to supply username and password as arguments to open:
|
82
69
|
|
83
70
|
```ruby
|
84
71
|
file = open('https://www.example.com/', 'w', :username => 'scott', :password => 'tiger')
|
85
72
|
```
|
86
73
|
|
87
|
-
On OS X passwords typed in by user will be stored encrypted in the Keychain and reused later.
|
74
|
+
On OS X passwords typed in by the user will be stored encrypted in the Keychain and reused later.
|
88
75
|
|
89
76
|
```
|
90
77
|
$ export DAVUSER=scott
|
@@ -93,24 +80,93 @@ On OS X passwords typed in by user will be stored encrypted in the Keychain and
|
|
93
80
|
Password for 'scott@www.example.com' stored on OS X KeyChain.
|
94
81
|
```
|
95
82
|
|
96
|
-
The next time this script is executed, it will not prompt for password.
|
83
|
+
The next time this script is executed, it will not prompt for username and password.
|
84
|
+
|
85
|
+
Proppatch and Propfind
|
86
|
+
----------------------
|
97
87
|
|
98
|
-
|
88
|
+
In difference to files and directories on local filesystems, files and directories on WebDAV servers can have many custom properties. Properties can be read with til propfindare set as a xml snippet with proppatch() and accessed with propfind().
|
99
89
|
|
100
|
-
|
90
|
+
```ruby
|
91
|
+
file = File.open('http://www.ruby-lang.org/open_uri_and_write.html','w')
|
92
|
+
file.proppatch('<D:Author>Thomas Flemming</D:Author>')
|
93
|
+
properties_as_xml = Dir.propfind("http://www.ruby-lang.org")
|
94
|
+
```
|
95
|
+
|
96
|
+
Interoperability with OpenURI
|
97
|
+
-----------------------------
|
101
98
|
|
102
|
-
|
99
|
+
If no filemode is specified when using open on url, standard 'open-uri' will be used.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
puts open("http://www.ruby-lang.org").read() # Use 'open-uri'
|
103
|
+
open('http://www.ruby-lang.org/my_page.html','w').puts("<h1>HTML</h1>") # Use 'open-uri-and-write'
|
104
|
+
```
|
103
105
|
|
106
|
+
To not interfer with the 'open-uri' standard library, the 'open-uri-and-write' gem is only active in file modes 'w','a','w+','a+' and 'r+'.
|
107
|
+
|
108
|
+
Supported file access modes
|
109
|
+
===========================
|
110
|
+
|
111
|
+
Unspported file access modes
|
112
|
+
============================
|
113
|
+
|
114
|
+
* r Read-only mode. The file pointer is placed at the beginning of the file. This is the default mode.
|
115
|
+
|
116
|
+
* r+ Read-write mode. The file pointer will be at the beginning of the file.
|
117
|
+
|
118
|
+
* w Write-only mode. Overwrites the file if the file exists. If the file does not exist, creates a new file for writing.
|
119
|
+
|
120
|
+
* w+ Read-write mode. Overwrites the existing file if the file exists. If the file does not exist, creates a new file for reading and writing.
|
121
|
+
|
122
|
+
* a Write-only mode. The file pointer is at the end of the file if the file exists. That is, the file is in the append mode. If the file does not exist, it creates a new file for writing.
|
123
|
+
|
124
|
+
* a+ Read and write mode. The file pointer is at the end of the file if the file exists. The file opens in the append mode. If the file does not exist, it creates a new file for reading and writing.
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
Install
|
129
|
+
-------
|
130
|
+
|
131
|
+
```
|
132
|
+
$ gem install open-uri-and-write
|
133
|
+
```
|
134
|
+
|
135
|
+
For OSX users this will store password on the keychain.
|
136
|
+
|
137
|
+
```
|
138
|
+
$ gem install keychain_services
|
139
|
+
```
|
140
|
+
|
141
|
+
Note that if you have stored a misspelled password on the OSX Keychain, then you will have to delete it manually with Keychain Access application.
|
142
|
+
|
143
|
+
Testing
|
144
|
+
-------
|
104
145
|
To run all tests:
|
105
146
|
|
106
147
|
```
|
107
148
|
$ rake spec
|
108
149
|
```
|
109
150
|
|
110
|
-
The tests will start a webserver at startup, and close it down before finishing.
|
151
|
+
The tests will start a webserver with webdav at startup, and close it down before finishing.
|
152
|
+
|
153
|
+
Future work
|
154
|
+
-----------
|
155
|
+
This is work in progress. You can write files and crate directories, but there's still work to do on reading directories and at the time one filemodes "r", "w" and "a" is supported.
|
156
|
+
|
157
|
+
More protocols like FTP, SCP and Amazon S3 would be useful.
|
158
|
+
|
159
|
+
Credits
|
160
|
+
-------
|
161
|
+
|
162
|
+
* Tanaka Akira for the inspirational ['open-uri'](https://github.com/ruby/ruby/blob/trunk/lib/open-uri.rb) standard ruby library.
|
163
|
+
* Miron Cuperman for the ['net/dav'](https://github.com/devrandom/net_dav) gem used to access webdav servers.
|
164
|
+
* Chris Roberts and the rest of the DAV4Rack for the WebDAV implementation in ruby used for testing this gem.
|
111
165
|
|
112
|
-
|
166
|
+
Author
|
167
|
+
------
|
113
168
|
|
114
|
-
|
115
|
-
* Miron Cuperman for the 'net/dav' gem used to access webdav servers.
|
169
|
+
Thomas Flemming
|
116
170
|
|
171
|
+
* [@thomasfl](https://twitter.com/#!/thomasfl)
|
172
|
+
* [http://github.com/thomasfl/](http://github.com/thomasfl/)
|
data/lib/open-uri-and-write.rb
CHANGED
@@ -4,237 +4,9 @@ require 'open-uri'
|
|
4
4
|
require 'net/dav'
|
5
5
|
require 'highline/import'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(url, rest)
|
15
|
-
super("")
|
16
|
-
@url = url
|
17
|
-
@uri = URI.parse(url)
|
18
|
-
if(rest.size > 0)
|
19
|
-
if(rest.first.to_s[/^[wa]/])
|
20
|
-
@dav = Net::DAV.new(@url)
|
21
|
-
options = rest[1]
|
22
|
-
if(options && options[:username] && options[:password])
|
23
|
-
@dav.credentials(options[:username], options[:password])
|
24
|
-
else
|
25
|
-
set_credentials
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
if(rest.first.to_s[/^a/])
|
30
|
-
write(@dav.get(@url)) # write to stringIO
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def set_credentials
|
37
|
-
if(ENV['DAVUSER'])
|
38
|
-
username = ENV['DAVUSER']
|
39
|
-
# elsif(ENV['USER'])
|
40
|
-
# username = ENV['USER']
|
41
|
-
else
|
42
|
-
username = ask("Username for #{@uri.host}: ")
|
43
|
-
end
|
44
|
-
|
45
|
-
if(ENV['DAVPASS'])
|
46
|
-
password = ENV['DAVPASS']
|
47
|
-
else
|
48
|
-
osx = (RUBY_PLATFORM =~ /darwin/)
|
49
|
-
osx_keychain = false
|
50
|
-
if(osx)
|
51
|
-
begin
|
52
|
-
require 'osx_keychain'
|
53
|
-
osx_keychain = true
|
54
|
-
rescue LoadError
|
55
|
-
end
|
56
|
-
end
|
57
|
-
if(osx_keychain)then
|
58
|
-
keychain = OSXKeychain.new
|
59
|
-
password = keychain[@uri.host, username ]
|
60
|
-
if(!password)
|
61
|
-
password = ask("Password for '#{username}@#{@uri.host}: ") {|q| q.echo = "*"}
|
62
|
-
keychain[@uri.host, username] = password
|
63
|
-
puts "Password for '#{username}@#{@uri.host}' stored on OS X KeyChain."
|
64
|
-
end
|
65
|
-
|
66
|
-
else
|
67
|
-
password = ask("Password for '#{username}@#{@uri.host}: ") {|q| q.echo = "*"}
|
68
|
-
end
|
69
|
-
end
|
70
|
-
@dav.credentials(username, password)
|
71
|
-
end
|
72
|
-
|
73
|
-
def puts(string)
|
74
|
-
super(string)
|
75
|
-
@dav.put_string(@url, string)
|
76
|
-
end
|
77
|
-
|
78
|
-
def read
|
79
|
-
@dav.get(@url)
|
80
|
-
end
|
81
|
-
|
82
|
-
def proppatch(xml_snippet)
|
83
|
-
@dav.proppatch(@uri, xml_snippet)
|
84
|
-
end
|
85
|
-
|
86
|
-
def propfind
|
87
|
-
@dav.propfind(@url)
|
88
|
-
end
|
89
|
-
|
90
|
-
def close
|
91
|
-
@dav.put_string(@url, string)
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
# Kernel extensions
|
97
|
-
# Careful monkeypatching
|
98
|
-
module Kernel
|
99
|
-
private
|
100
|
-
alias open_uri_and_write_original open # :nodoc:
|
101
|
-
|
102
|
-
def open(name, *rest, &block) # :doc:
|
103
|
-
if name.respond_to?(:open)
|
104
|
-
name.open(*rest, &block)
|
105
|
-
elsif name.respond_to?(:to_s) and
|
106
|
-
name[/^(https?):\/\//] and
|
107
|
-
rest.size > 0 and
|
108
|
-
rest.first.to_s[/^[rwa]/] and
|
109
|
-
not (rest.first.to_s == 'r' or rest.first.to_s == 'rb')
|
110
|
-
webdav_agent = WebDavAgent.new(name, rest)
|
111
|
-
if(block)
|
112
|
-
yield webdav_agent
|
113
|
-
else
|
114
|
-
return webdav_agent
|
115
|
-
end
|
116
|
-
else
|
117
|
-
open_uri_and_write_original(name, *rest, &block)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
module_function :open
|
122
|
-
end
|
123
|
-
|
124
|
-
# Store credentials for later use in sesssion.
|
125
|
-
class WebDavCredentialsPool
|
126
|
-
|
127
|
-
def self.get_connection_for_url(url)
|
128
|
-
hostname = URI.parse(url).host.to_s
|
129
|
-
if(!$_webdav_credentials_pool)
|
130
|
-
$_webdav_credentials_pool = { }
|
131
|
-
end
|
132
|
-
if($_webdav_credentials_pool[hostname])
|
133
|
-
return $_webdav_credentials_pool[hostname]
|
134
|
-
else(!$_webdav_credentials_pool[hostname])
|
135
|
-
agent = WebDavAgent.new(url, ['w'])
|
136
|
-
$_webdav_credentials_pool[hostname] = agent.dav
|
137
|
-
return agent.dav
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
end
|
142
|
-
|
143
|
-
|
144
|
-
# More monkeypatching
|
145
|
-
class Dir
|
146
|
-
|
147
|
-
class << self
|
148
|
-
alias original_mkdir mkdir
|
149
|
-
alias original_rmddir rmdir
|
150
|
-
end
|
151
|
-
|
152
|
-
def self.mkdir(name)
|
153
|
-
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
154
|
-
dav = WebDavCredentialsPool.get_connection_for_url(name)
|
155
|
-
dav.mkdir(name)
|
156
|
-
else
|
157
|
-
self.original_mkdir(name)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def self.rmdir(name)
|
162
|
-
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
163
|
-
dav = WebDavCredentialsPool.get_connection_for_url(name)
|
164
|
-
dav.delete(name)
|
165
|
-
else
|
166
|
-
self.original_rmdir(name)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def self.propfind(name)
|
171
|
-
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
172
|
-
dav = WebDavCredentialsPool.get_connection_for_url(name)
|
173
|
-
dav.propfind(name)
|
174
|
-
else
|
175
|
-
# TODO Throw illegal action exception
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def self.proppatch(name,xml_snippet)
|
180
|
-
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
181
|
-
dav = WebDavCredentialsPool.get_connection_for_url(name)
|
182
|
-
dav.propfind(name, xml_snippet)
|
183
|
-
else
|
184
|
-
# TODO Throw illegal action exception
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
end
|
189
|
-
|
190
|
-
# Even more monkeypatching
|
191
|
-
class File
|
192
|
-
|
193
|
-
class << self
|
194
|
-
alias original_delete delete
|
195
|
-
alias original_open open
|
196
|
-
alias original_exists? exists?
|
197
|
-
end
|
198
|
-
|
199
|
-
def self.exists?(name)
|
200
|
-
if(name[/https?:\/\//])
|
201
|
-
dav = WebDavCredentialsPool.get_connection_for_url(name)
|
202
|
-
dav.exists?(name)
|
203
|
-
else
|
204
|
-
self.original_exists?(name)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def self.delete(names)
|
209
|
-
filenames = []
|
210
|
-
if(names.class == String)
|
211
|
-
filenames << names
|
212
|
-
elsif(names.class = Array)
|
213
|
-
filenames = names
|
214
|
-
end
|
215
|
-
filenames.each do |filename|
|
216
|
-
if(filename[/^(https?):\/\//])
|
217
|
-
dav = WebDavCredentialsPool.get_connection_for_url(filename)
|
218
|
-
dav.delete(filename)
|
219
|
-
else
|
220
|
-
self.original_delete(filename)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def self.open(name, *rest, &block)
|
226
|
-
if name.respond_to?(:open)
|
227
|
-
name.open(*rest, &block)
|
228
|
-
elsif name.respond_to?(:to_s) and name[/^(https?):\/\//] and rest.size > 0 and rest.first.to_s[/^w/]
|
229
|
-
webdav_agent = WebDavAgent.new(name, rest)
|
230
|
-
if(block)
|
231
|
-
yield webdav_agent
|
232
|
-
else
|
233
|
-
return webdav_agent
|
234
|
-
end
|
235
|
-
else
|
236
|
-
self.original_open(name, *rest, &block)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
end
|
7
|
+
require 'open-uri-and-write/handle'
|
8
|
+
require 'open-uri-and-write/usernames'
|
9
|
+
require 'open-uri-and-write/credentials_store'
|
10
|
+
require 'open-uri-and-write/file_extensions'
|
11
|
+
require 'open-uri-and-write/dir_extensions'
|
12
|
+
require 'open-uri-and-write/kernel_extensions'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Store credentials for later use in sesssion.
|
2
|
+
|
3
|
+
module OpenUriAndWrite
|
4
|
+
|
5
|
+
class CredentialsStore
|
6
|
+
|
7
|
+
def self.get_connection_for_url(url)
|
8
|
+
hostname = URI.parse(url).host.to_s
|
9
|
+
if(!$_webdav_credentials_pool)
|
10
|
+
$_webdav_credentials_pool = { }
|
11
|
+
end
|
12
|
+
if($_webdav_credentials_pool[hostname])
|
13
|
+
return $_webdav_credentials_pool[hostname]
|
14
|
+
else(!$_webdav_credentials_pool[hostname])
|
15
|
+
agent = OpenUriAndWrite::Handle.new(url, ['w'])
|
16
|
+
$_webdav_credentials_pool[hostname] = agent.dav
|
17
|
+
return agent.dav
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Extensions and modifications (monkeypatching) to the Dir class:
|
2
|
+
|
3
|
+
class Dir
|
4
|
+
|
5
|
+
class << self
|
6
|
+
alias original_mkdir mkdir
|
7
|
+
alias original_rmddir rmdir
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.mkdir(name, *args)
|
11
|
+
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
12
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(name)
|
13
|
+
dav.mkdir(name)
|
14
|
+
else
|
15
|
+
self.original_mkdir(name, *args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.rmdir(name)
|
20
|
+
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
21
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(name)
|
22
|
+
dav.delete(name)
|
23
|
+
else
|
24
|
+
self.original_rmdir(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.propfind(name)
|
29
|
+
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
30
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(name)
|
31
|
+
dav.propfind(name)
|
32
|
+
else
|
33
|
+
raise IOError.new(true), "Illegal action: Can't do propfind #{name}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.proppatch(name,xml_snippet)
|
38
|
+
if name.respond_to?(:to_s) and name[/^(https?):\/\//]
|
39
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(name)
|
40
|
+
dav.propfind(name, xml_snippet)
|
41
|
+
else
|
42
|
+
raise IOError.new(true), "Illegal action: Can't do proppatch on #{name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Extensions and modifications (monkeypatching) to the File class:
|
2
|
+
|
3
|
+
class File
|
4
|
+
|
5
|
+
class << self
|
6
|
+
alias original_delete delete
|
7
|
+
alias original_open open
|
8
|
+
alias original_exists? exists?
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.exists?(name)
|
12
|
+
if(name[/https?:\/\//])
|
13
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(name)
|
14
|
+
dav.exists?(name)
|
15
|
+
else
|
16
|
+
self.original_exists?(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.delete(names)
|
21
|
+
filenames = []
|
22
|
+
if(names.class == String)
|
23
|
+
filenames << names
|
24
|
+
elsif(names.class = Array)
|
25
|
+
filenames = names
|
26
|
+
end
|
27
|
+
filenames.each do |filename|
|
28
|
+
if(filename[/^(https?):\/\//])
|
29
|
+
dav = OpenUriAndWrite::CredentialsStore.get_connection_for_url(filename)
|
30
|
+
dav.delete(filename)
|
31
|
+
else
|
32
|
+
self.original_delete(filename)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.open(name, *rest, &block)
|
38
|
+
if name.respond_to?(:open)
|
39
|
+
name.open(*rest, &block)
|
40
|
+
elsif name.respond_to?(:to_s) and name[/^(https?):\/\//] and rest.size > 0 and rest.first.to_s[/^w/]
|
41
|
+
webdav_agent = OpenUriAndWrite::Handle.new(name, rest)
|
42
|
+
if(block)
|
43
|
+
yield webdav_agent
|
44
|
+
else
|
45
|
+
return webdav_agent
|
46
|
+
end
|
47
|
+
else
|
48
|
+
self.original_open(name, *rest, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# OpenUriAndWrite file handler. Does authentication upon initialize
|
2
|
+
|
3
|
+
module OpenUriAndWrite
|
4
|
+
|
5
|
+
class Handle < StringIO
|
6
|
+
|
7
|
+
alias_method :original_puts, :puts
|
8
|
+
attr_accessor :dav, :filemode
|
9
|
+
|
10
|
+
def initialize(url, rest)
|
11
|
+
super("")
|
12
|
+
@url = url
|
13
|
+
@uri = URI.parse(url)
|
14
|
+
if(rest.size > 0)
|
15
|
+
@filemode = rest.first.to_s
|
16
|
+
if(@filemode[/^[rwa]/])
|
17
|
+
@dav = Net::DAV.new(@url)
|
18
|
+
options = rest[1]
|
19
|
+
if(options && options[:username] && options[:password])
|
20
|
+
@dav.credentials(options[:username], options[:password])
|
21
|
+
else
|
22
|
+
set_credentials
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if(@filemode[/^a/])
|
27
|
+
write(@dav.get(@url)) # Write to StringIO
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_credentials
|
34
|
+
if(ENV['DAVUSER'])
|
35
|
+
username = ENV['DAVUSER']
|
36
|
+
else
|
37
|
+
usernames = OpenUriAndWrite::Usernames.new()
|
38
|
+
username = usernames.username_for_host(@uri.host)
|
39
|
+
if(not(username))
|
40
|
+
username = ask("Username for #{@uri.host}: ")
|
41
|
+
usernames.save_username_and_host(username,@uri.host)
|
42
|
+
STDOUT.puts "Username and hostname stored in #{usernames.usernamesfile}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
password = nil
|
47
|
+
if(ENV['DAVPASS'])
|
48
|
+
password = ENV['DAVPASS']
|
49
|
+
else
|
50
|
+
osx = (RUBY_PLATFORM =~ /darwin/)
|
51
|
+
osx_keychain = false
|
52
|
+
if(osx)
|
53
|
+
begin
|
54
|
+
require 'keychain'
|
55
|
+
osx_keychain = true
|
56
|
+
rescue LoadError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if(osx_keychain)then
|
60
|
+
item = Keychain.items.find { |item| item.label =~ /#{@uri.host}/ }
|
61
|
+
if(item)
|
62
|
+
password = item.password
|
63
|
+
end
|
64
|
+
|
65
|
+
if(!password)
|
66
|
+
password = ask("Password for '#{username}@#{@uri.host}: ") {|q| q.echo = "*"}
|
67
|
+
Keychain.add_internet_password(@uri.host, '', username, '', password)
|
68
|
+
STDOUT.puts "Password for '#{username}@#{@uri.host}' stored on OSX KeyChain."
|
69
|
+
end
|
70
|
+
|
71
|
+
else
|
72
|
+
password = ask("Password for '#{username}@#{@uri.host}: ") {|q| q.echo = "*"}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@dav.credentials(username, password)
|
76
|
+
end
|
77
|
+
|
78
|
+
def puts(string)
|
79
|
+
if(@filemode[/^r/])
|
80
|
+
raise IOError.new(true), "not opened for writing"
|
81
|
+
end
|
82
|
+
|
83
|
+
super(string)
|
84
|
+
@dav.put_string(@url, string)
|
85
|
+
end
|
86
|
+
|
87
|
+
def read
|
88
|
+
@dav.get(@url)
|
89
|
+
end
|
90
|
+
|
91
|
+
def proppatch(xml_snippet)
|
92
|
+
@dav.proppatch(@uri, xml_snippet)
|
93
|
+
end
|
94
|
+
|
95
|
+
def propfind
|
96
|
+
@dav.propfind(@url)
|
97
|
+
end
|
98
|
+
|
99
|
+
def close
|
100
|
+
@dav.put_string(@url, string)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Kernel extensions
|
2
|
+
# Careful monkeypatching
|
3
|
+
module Kernel
|
4
|
+
private
|
5
|
+
alias open_uri_and_write_original open # :nodoc:
|
6
|
+
|
7
|
+
def open(name, *rest, &block) # :doc:
|
8
|
+
if name.respond_to?(:open)
|
9
|
+
name.open(*rest, &block)
|
10
|
+
elsif name.respond_to?(:to_s) and
|
11
|
+
name[/^(https?):\/\//] and
|
12
|
+
rest.size > 0 and
|
13
|
+
rest.first.to_s[/^[rwa]/]
|
14
|
+
webdav_agent = OpenUriAndWrite::Handle.new(name, rest)
|
15
|
+
if(block)
|
16
|
+
yield webdav_agent
|
17
|
+
else
|
18
|
+
return webdav_agent
|
19
|
+
end
|
20
|
+
else
|
21
|
+
open_uri_and_write_original(name, *rest, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function :open
|
26
|
+
end
|
27
|
+
|
28
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# For storing usernames and hostnames in ~/.open-uri-and-write-usernames
|
2
|
+
|
3
|
+
module OpenUriAndWrite
|
4
|
+
|
5
|
+
class Usernames
|
6
|
+
|
7
|
+
def homedir
|
8
|
+
if(Dir.respond_to?("home"))
|
9
|
+
Dir.home
|
10
|
+
else
|
11
|
+
File.expand_path("~")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def usernamesfile
|
16
|
+
homedir + "/.open-uri-and-write-usernames"
|
17
|
+
end
|
18
|
+
|
19
|
+
def read_usernames_and_hosts
|
20
|
+
usernames = {}
|
21
|
+
if(File.exist?(usernamesfile))
|
22
|
+
open(usernamesfile).readlines.each do |line|
|
23
|
+
username, host = line.split(':')
|
24
|
+
usernames[host.strip] = username.strip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
return usernames
|
28
|
+
end
|
29
|
+
|
30
|
+
def save_username_and_host(username,host)
|
31
|
+
usernames = read_usernames_and_hosts
|
32
|
+
usernames[host] = username
|
33
|
+
store_username_and_host(usernames)
|
34
|
+
end
|
35
|
+
|
36
|
+
def store_username_and_host(usernames)
|
37
|
+
file = open(usernamesfile, "w")
|
38
|
+
usernames.keys.each do |key|
|
39
|
+
file.puts "#{usernames[key]}:#{key}"
|
40
|
+
end
|
41
|
+
file.close
|
42
|
+
end
|
43
|
+
|
44
|
+
def username_for_host(host)
|
45
|
+
usernames = read_usernames_and_hosts
|
46
|
+
return usernames[host]
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -128,11 +128,34 @@ describe "OpenUriAndWrite" do
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
it "should not use 'open-uri' to read file in 'r' filemode" do
|
132
|
+
timestamp = Time.now.to_s
|
133
|
+
webdav_url = @base_uri + 'webdav_r_filemode_test.txt'
|
134
|
+
file = open(webdav_url,'w')
|
135
|
+
file.puts timestamp
|
136
|
+
file.class.should == OpenUriAndWrite::Handle
|
137
|
+
file.close
|
138
|
+
|
139
|
+
file = open(webdav_url)
|
140
|
+
file.class.should == StringIO # StringIO means 'open-uri' gem is beeing used
|
141
|
+
file.read.strip.should == timestamp
|
142
|
+
|
143
|
+
file = open(webdav_url,'r')
|
144
|
+
file.class.should == OpenUriAndWrite::Handle
|
145
|
+
file.read.strip.should == timestamp
|
146
|
+
begin
|
147
|
+
file.puts("this should not be written")
|
148
|
+
fail
|
149
|
+
rescue IOError => ioError
|
150
|
+
end
|
151
|
+
file.close
|
152
|
+
end
|
153
|
+
|
131
154
|
# TODO Test all modes:
|
132
155
|
|
133
|
-
# r
|
134
|
-
#
|
135
|
-
#
|
156
|
+
# OK r
|
157
|
+
# Read-only mode. The file pointer is placed at the beginning of
|
158
|
+
# the file. This is the default mode.
|
136
159
|
#
|
137
160
|
# r+
|
138
161
|
# Read-write mode. The file pointer will be at the beginning of the file.
|
@@ -162,6 +185,8 @@ describe "OpenUriAndWrite" do
|
|
162
185
|
# r+, w+, and a+ all do read-write. w+ truncates the file. a+ appends.
|
163
186
|
# w+ and a+ both create the file if it does not exist.)
|
164
187
|
|
188
|
+
# TODO: Store username and hostname in .open-uri-and-write-hosts
|
189
|
+
|
165
190
|
# # TODO test authentication
|
166
191
|
|
167
192
|
after(:each) do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: open-uri-and-write
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: highline
|
16
|
-
requirement: &
|
16
|
+
requirement: &70128528057900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.6.9
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70128528057900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: net_dav
|
27
|
-
requirement: &
|
27
|
+
requirement: &70128537727960 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.5.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70128537727960
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70128537727120 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.5.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70128537727120
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: pry
|
49
|
-
requirement: &
|
49
|
+
requirement: &70128537726640 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.9.8.4
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70128537726640
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: dav4rack
|
60
|
-
requirement: &
|
60
|
+
requirement: &70128537726080 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: 0.2.10
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70128537726080
|
69
69
|
description: Use normal file operations to write files to WebDAV enabled web servers.
|
70
70
|
email:
|
71
71
|
- thomas.flemming@gmail.com
|
@@ -74,10 +74,17 @@ extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
76
|
- .gitignore
|
77
|
+
- LICENSE
|
77
78
|
- README.md
|
78
79
|
- Rakefile
|
79
80
|
- autotest.rb
|
80
81
|
- lib/open-uri-and-write.rb
|
82
|
+
- lib/open-uri-and-write/credentials_store.rb
|
83
|
+
- lib/open-uri-and-write/dir_extensions.rb
|
84
|
+
- lib/open-uri-and-write/file_extensions.rb
|
85
|
+
- lib/open-uri-and-write/handle.rb
|
86
|
+
- lib/open-uri-and-write/kernel_extensions.rb
|
87
|
+
- lib/open-uri-and-write/usernames.rb
|
81
88
|
- lib/open-uri-and-write/version.rb
|
82
89
|
- open-uri-and-write.gemspec
|
83
90
|
- spec/integration/dav4rack_testserver.rb
|