webdav 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +12 -0
- data/README.md +128 -4
- data/lib/WebDAV/MultiStatus.rb +37 -15
- data/lib/WebDAV/VERSION.rb +1 -1
- data/test/WebDAV/MultiStatus_test.rb +134 -5
- data/webdav.gemspec +4 -3
- metadata +22 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 112d0cec672b4fba93b1b1b356ba14d148982ae94b9063c7df8ff0116ab2f775
|
|
4
|
+
data.tar.gz: 0e94db83b43f2dd4efed30a214931086d29278fb5eab5dc3cc0b4cecf1fdb051
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1684e452204e2e57250698d6acd09d7603bd51fcaeea1ddb679edf7bb2d80ae7310ad122ffb3917b5d9fa1bfe0e6e4952e4c79c5db74068a554a73c4d793a165
|
|
7
|
+
data.tar.gz: 686bf1a9d4729235642d694379663ee5e9711291d5d524013a22ce05c10aac66b4235477cb8085e5208316830793f4cc58ef2ae6c26308ec7bb13ceacad70fc9
|
data/CHANGELOG
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 20260522
|
|
4
|
+
|
|
5
|
+
0.2.0: Rewrite MultiStatus#parse; align dependencies with http.rb 1.0.0; document WebDAV concepts.
|
|
6
|
+
|
|
7
|
+
1. ~ WebDAV::MultiStatus#parse: Walk propstat blocks individually; preserve per-propstat status; detect response-level status (for COPY/MOVE/DELETE); preserve namespace URIs on properties. Now private; resources remains the public accessor.
|
|
8
|
+
2. ~ WebDAV::MultiStatus resource shape: Properties move from flat hash keyed by local name to nested hash keyed by namespace URI then local name. Resources now have :propstats (array of {properties:, status:}) and :status (response-level, may be nil) instead of a single :properties and :status pair.
|
|
9
|
+
3. ~ test/WebDAV/MultiStatus_test.rb: Update existing tests for new shape; + tests for per-propstat status, response-level status, namespace preservation, and same-local-name collision across namespaces.
|
|
10
|
+
4. ~ README.md: Document new MultiStatus#resources shape, with paired wire-XML and parsed-Ruby examples for both PROPFIND and collection-cascade Multi-Status; + WebDAV::Response wire example; + Concepts section (properties vs content, collections and the trailing slash, why 207 Multi-Status exists, namespaces).
|
|
11
|
+
5. ~ webdav.gemspec: required_ruby_version />= 2.7/>= 3.2/; http.rb dependency /[]/['~> 1.0']/; minitest pin /'~> 5.27'/'~> 6.0'/; + minitest-mock (minitest 6 split it out).
|
|
12
|
+
6. ~ WebDAV::VERSION: /0.1.2/0.2.0/
|
|
13
|
+
7. ~ CHANGELOG: + 0.2.0 entry
|
|
14
|
+
|
|
3
15
|
## 20260520
|
|
4
16
|
|
|
5
17
|
0.1.2: Fix Response#success? to return true only for 2xx responses.
|
data/README.md
CHANGED
|
@@ -30,7 +30,14 @@ dav = WebDAV.new('https://dav.example.com/files/', username: 'user', password: '
|
|
|
30
30
|
response = dav.propfind('/', depth: '1')
|
|
31
31
|
response.resources.each do |resource|
|
|
32
32
|
puts resource[:href]
|
|
33
|
-
|
|
33
|
+
resource[:propstats].each do |propstat|
|
|
34
|
+
puts propstat[:status]
|
|
35
|
+
propstat[:properties].each do |namespace, properties|
|
|
36
|
+
properties.each do |name, value|
|
|
37
|
+
puts " #{namespace} #{name} = #{value}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
34
41
|
end
|
|
35
42
|
```
|
|
36
43
|
|
|
@@ -92,6 +99,27 @@ end
|
|
|
92
99
|
```
|
|
93
100
|
|
|
94
101
|
|
|
102
|
+
## Concepts
|
|
103
|
+
|
|
104
|
+
WebDAV extends HTTP with a few ideas that don't have direct REST analogues. The ones below explain why the API is shaped the way it is.
|
|
105
|
+
|
|
106
|
+
### Properties vs content
|
|
107
|
+
|
|
108
|
+
A WebDAV resource has two faces. **Content** is what GET returns — the bytes of the file. **Properties** are metadata associated with the resource: display name, creation date, lock state, content type, and any custom properties the server defines. The same URL identifies both, but different verbs reach them — GET/PUT for content, PROPFIND/PROPPATCH for properties.
|
|
109
|
+
|
|
110
|
+
### Collections and the trailing slash
|
|
111
|
+
|
|
112
|
+
A **collection** is WebDAV's directory: a resource that contains other resources. MKCOL creates one. By convention, collection URLs end in `/` and ordinary resources don't; servers that care about the distinction will redirect or 404 if you get it wrong. The distinction matters because COPY, MOVE, and DELETE on a collection cascade to its children — which is also why those verbs can return 207 Multi-Status when children succeed and fail independently.
|
|
113
|
+
|
|
114
|
+
### Why 207 Multi-Status exists
|
|
115
|
+
|
|
116
|
+
HTTP assumes one request maps to one status. WebDAV breaks that assumption: PROPFIND on a folder asks about many resources at once; COPY of a tree may succeed on some children and fail on others. The 207 Multi-Status response code says "the request touched many things; here are per-thing outcomes." The XML body carries one `<d:response>` per affected resource. This gem returns those as `WebDAV::MultiStatus`; the `resources` accessor exposes the per-resource detail (see [Responses](#responses)).
|
|
117
|
+
|
|
118
|
+
### Namespaces
|
|
119
|
+
|
|
120
|
+
WebDAV properties are XML elements, and XML elements belong to namespaces. The core RFC 4918 properties live in the `DAV:` namespace. Extensions — CalDAV (`urn:ietf:params:xml:ns:caldav`), CardDAV, Exchange, ownCloud, custom server vocabularies — each define their own. Properties from different namespaces can share local names (`<d:displayname>` and `<x:displayname>` are different properties), so the parser preserves namespace URIs as the outer key in the properties hash.
|
|
121
|
+
|
|
122
|
+
|
|
95
123
|
## Methods
|
|
96
124
|
|
|
97
125
|
WebDAV extends HTTP with additional methods for distributed authoring. This gem provides all the methods defined in RFC 4918 ("HTTP Extensions for Web Distributed Authoring and Versioning") and the REPORT method from RFC 3253 ("Versioning Extensions to WebDAV"), which is essential for CalDAV and CardDAV queries.
|
|
@@ -149,12 +177,108 @@ All methods return either a `WebDAV::Response` or a `WebDAV::MultiStatus`.
|
|
|
149
177
|
- `content_type`
|
|
150
178
|
- `success?`
|
|
151
179
|
|
|
180
|
+
A `GET` response on the wire:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
HTTP/1.1 200 OK
|
|
184
|
+
Content-Type: text/plain
|
|
185
|
+
Content-Length: 13
|
|
186
|
+
ETag: "5d41402a"
|
|
187
|
+
|
|
188
|
+
Hello, world.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Parses to:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
response.code # => 200
|
|
195
|
+
response.message # => "OK"
|
|
196
|
+
response.body # => "Hello, world."
|
|
197
|
+
response.etag # => "\"5d41402a\""
|
|
198
|
+
response.content_type # => "text/plain"
|
|
199
|
+
response.success? # => true
|
|
200
|
+
```
|
|
201
|
+
|
|
152
202
|
`WebDAV::MultiStatus` additionally provides:
|
|
153
203
|
|
|
154
204
|
- `resources` — an array of hashes, each with:
|
|
155
|
-
- `href`
|
|
156
|
-
- `properties`
|
|
157
|
-
- `status`
|
|
205
|
+
- `href` — the resource URL
|
|
206
|
+
- `propstats` — an array of `{properties:, status:}` hashes (PROPFIND / PROPPATCH / REPORT). May be empty when the response carries a response-level status instead.
|
|
207
|
+
- `status` — the response-level status string (COPY / MOVE / DELETE). `nil` when the response has propstats instead.
|
|
208
|
+
|
|
209
|
+
Within a propstat, `properties` is a nested hash keyed first by XML namespace URI, then by local name. For example, a CalDAV calendar property appears as `propstat[:properties]['urn:ietf:params:xml:ns:caldav']['calendar-data']` and a DAV property as `propstat[:properties]['DAV:']['getetag']`. Keeping the namespace explicit prevents collisions between properties from different namespaces that share a local name.
|
|
210
|
+
|
|
211
|
+
A PROPFIND response — properties grouped by namespace, status per propstat, response-level status `nil`. The wire XML:
|
|
212
|
+
|
|
213
|
+
```xml
|
|
214
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
215
|
+
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
216
|
+
<d:response>
|
|
217
|
+
<d:href>/calendar/event.ics</d:href>
|
|
218
|
+
<d:propstat>
|
|
219
|
+
<d:prop>
|
|
220
|
+
<d:getetag>"abc123"</d:getetag>
|
|
221
|
+
<c:calendar-data>BEGIN:VCALENDAR...</c:calendar-data>
|
|
222
|
+
</d:prop>
|
|
223
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
224
|
+
</d:propstat>
|
|
225
|
+
<d:propstat>
|
|
226
|
+
<d:prop>
|
|
227
|
+
<d:getctag/>
|
|
228
|
+
</d:prop>
|
|
229
|
+
<d:status>HTTP/1.1 404 Not Found</d:status>
|
|
230
|
+
</d:propstat>
|
|
231
|
+
</d:response>
|
|
232
|
+
</d:multistatus>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Parses to:
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
[
|
|
239
|
+
{
|
|
240
|
+
href: '/calendar/event.ics',
|
|
241
|
+
propstats: [
|
|
242
|
+
{
|
|
243
|
+
properties: {
|
|
244
|
+
'DAV:' => {'getetag' => '"abc123"'},
|
|
245
|
+
'urn:ietf:params:xml:ns:caldav' => {'calendar-data' => 'BEGIN:VCALENDAR...'}
|
|
246
|
+
},
|
|
247
|
+
status: 'HTTP/1.1 200 OK'
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
properties: {'DAV:' => {'getctag' => ''}},
|
|
251
|
+
status: 'HTTP/1.1 404 Not Found'
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
status: nil
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
A COPY / MOVE / DELETE on a collection where a child resource failed — the server returns 207 Multi-Status with one `<d:response>` per affected child, each carrying a response-level status rather than propstats. Single-resource lifecycle operations don't go through this path; they return a plain `WebDAV::Response` with the status code as the whole story. The wire XML:
|
|
260
|
+
|
|
261
|
+
```xml
|
|
262
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
263
|
+
<d:multistatus xmlns:d="DAV:">
|
|
264
|
+
<d:response>
|
|
265
|
+
<d:href>/dir/file.txt</d:href>
|
|
266
|
+
<d:status>HTTP/1.1 403 Forbidden</d:status>
|
|
267
|
+
</d:response>
|
|
268
|
+
</d:multistatus>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Parses to:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
[
|
|
275
|
+
{
|
|
276
|
+
href: '/dir/file.txt',
|
|
277
|
+
propstats: [],
|
|
278
|
+
status: 'HTTP/1.1 403 Forbidden'
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
```
|
|
158
282
|
|
|
159
283
|
|
|
160
284
|
## Errors
|
data/lib/WebDAV/MultiStatus.rb
CHANGED
|
@@ -9,26 +9,48 @@ class WebDAV
|
|
|
9
9
|
class MultiStatus < Response
|
|
10
10
|
attr_reader :resources
|
|
11
11
|
|
|
12
|
-
def parse
|
|
13
|
-
doc = REXML::Document.new(body)
|
|
14
|
-
resources = []
|
|
15
|
-
doc.elements.each('//d:response') do |resp|
|
|
16
|
-
href = resp.elements['.//d:href']&.text
|
|
17
|
-
properties = {}
|
|
18
|
-
resp.elements.each('.//d:prop/*') do |prop|
|
|
19
|
-
properties[prop.name] = prop.text || prop.to_s
|
|
20
|
-
end
|
|
21
|
-
status = resp.elements['.//d:status']&.text
|
|
22
|
-
resources << {href: href, properties: properties, status: status}
|
|
23
|
-
end
|
|
24
|
-
resources
|
|
25
|
-
end
|
|
26
|
-
|
|
27
12
|
private
|
|
28
13
|
|
|
29
14
|
def initialize(response)
|
|
30
15
|
super
|
|
31
16
|
@resources = parse
|
|
32
17
|
end
|
|
18
|
+
|
|
19
|
+
def parse
|
|
20
|
+
doc = REXML::Document.new(body)
|
|
21
|
+
doc.elements.collect('//d:response'){|response_element| parse_response(response_element)}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parse_response(response_element)
|
|
25
|
+
{
|
|
26
|
+
href: response_element.elements['d:href']&.text,
|
|
27
|
+
propstats: parse_propstats(response_element),
|
|
28
|
+
status: parse_response_status(response_element)
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def parse_propstats(response_element)
|
|
33
|
+
response_element.elements.collect('d:propstat'){|propstat_element| parse_propstat(propstat_element)}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def parse_propstat(propstat_element)
|
|
37
|
+
{
|
|
38
|
+
properties: parse_properties(propstat_element.elements['d:prop']),
|
|
39
|
+
status: propstat_element.elements['d:status']&.text
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_properties(prop_element)
|
|
44
|
+
return {} unless prop_element
|
|
45
|
+
prop_element.elements.to_a.each_with_object({}) do |property_element, result|
|
|
46
|
+
result[property_element.namespace] ||= {}
|
|
47
|
+
result[property_element.namespace][property_element.name] = property_element.text || property_element.to_s
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse_response_status(response_element)
|
|
52
|
+
return nil if response_element.elements['d:propstat']
|
|
53
|
+
response_element.elements['d:status']&.text
|
|
54
|
+
end
|
|
33
55
|
end
|
|
34
56
|
end
|
data/lib/WebDAV/VERSION.rb
CHANGED
|
@@ -67,13 +67,142 @@ describe WebDAV::MultiStatus do
|
|
|
67
67
|
_(response.resources[1][:href]).must_equal '/dav/calendars/user/test/calendar2/'
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
it "extracts properties from each
|
|
71
|
-
_(response.resources[0][:properties]['displayname']).must_equal 'Work'
|
|
72
|
-
_(response.resources[1][:properties]['displayname']).must_equal 'Personal'
|
|
70
|
+
it "extracts properties from each propstat, keyed by namespace then local name" do
|
|
71
|
+
_(response.resources[0][:propstats][0][:properties]['DAV:']['displayname']).must_equal 'Work'
|
|
72
|
+
_(response.resources[1][:propstats][0][:properties]['DAV:']['displayname']).must_equal 'Personal'
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
it "extracts status
|
|
76
|
-
_(response.resources[0][:status]).must_equal 'HTTP/1.1 200 OK'
|
|
75
|
+
it "extracts per-propstat status" do
|
|
76
|
+
_(response.resources[0][:propstats][0][:status]).must_equal 'HTTP/1.1 200 OK'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "leaves response-level status nil when propstats are present" do
|
|
80
|
+
_(response.resources[0][:status]).must_be_nil
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "with multiple propstats per response" do
|
|
85
|
+
let(:mixed_status_xml) do
|
|
86
|
+
<<~XML
|
|
87
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
88
|
+
<d:multistatus xmlns:d="DAV:">
|
|
89
|
+
<d:response>
|
|
90
|
+
<d:href>/calendar/</d:href>
|
|
91
|
+
<d:propstat>
|
|
92
|
+
<d:prop>
|
|
93
|
+
<d:displayname>Work</d:displayname>
|
|
94
|
+
</d:prop>
|
|
95
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
96
|
+
</d:propstat>
|
|
97
|
+
<d:propstat>
|
|
98
|
+
<d:prop>
|
|
99
|
+
<d:getctag/>
|
|
100
|
+
</d:prop>
|
|
101
|
+
<d:status>HTTP/1.1 404 Not Found</d:status>
|
|
102
|
+
</d:propstat>
|
|
103
|
+
</d:response>
|
|
104
|
+
</d:multistatus>
|
|
105
|
+
XML
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
let(:mixed_response){WebDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: mixed_status_xml))}
|
|
109
|
+
|
|
110
|
+
it "preserves each propstat as a separate entry" do
|
|
111
|
+
_(mixed_response.resources[0][:propstats].length).must_equal 2
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "keeps the 200 propstat's properties under its own status" do
|
|
115
|
+
successful_propstat = mixed_response.resources[0][:propstats][0]
|
|
116
|
+
_(successful_propstat[:status]).must_equal 'HTTP/1.1 200 OK'
|
|
117
|
+
_(successful_propstat[:properties]['DAV:']['displayname']).must_equal 'Work'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "keeps the 404 propstat's properties under its own status" do
|
|
121
|
+
missing_propstat = mixed_response.resources[0][:propstats][1]
|
|
122
|
+
_(missing_propstat[:status]).must_equal 'HTTP/1.1 404 Not Found'
|
|
123
|
+
_(missing_propstat[:properties]['DAV:']).must_include 'getctag'
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe "with response-level status (COPY/MOVE/DELETE)" do
|
|
128
|
+
let(:response_level_xml) do
|
|
129
|
+
<<~XML
|
|
130
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
131
|
+
<d:multistatus xmlns:d="DAV:">
|
|
132
|
+
<d:response>
|
|
133
|
+
<d:href>/dir/file.txt</d:href>
|
|
134
|
+
<d:status>HTTP/1.1 403 Forbidden</d:status>
|
|
135
|
+
</d:response>
|
|
136
|
+
</d:multistatus>
|
|
137
|
+
XML
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
let(:response_level_response){WebDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: response_level_xml))}
|
|
141
|
+
|
|
142
|
+
it "populates the response-level status" do
|
|
143
|
+
_(response_level_response.resources[0][:status]).must_equal 'HTTP/1.1 403 Forbidden'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "leaves propstats empty" do
|
|
147
|
+
_(response_level_response.resources[0][:propstats]).must_equal []
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe "with multiple namespaces" do
|
|
152
|
+
let(:multi_namespace_xml) do
|
|
153
|
+
<<~XML
|
|
154
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
155
|
+
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
156
|
+
<d:response>
|
|
157
|
+
<d:href>/calendar/event.ics</d:href>
|
|
158
|
+
<d:propstat>
|
|
159
|
+
<d:prop>
|
|
160
|
+
<d:getetag>"abc123"</d:getetag>
|
|
161
|
+
<c:calendar-data>BEGIN:VCALENDAR</c:calendar-data>
|
|
162
|
+
</d:prop>
|
|
163
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
164
|
+
</d:propstat>
|
|
165
|
+
</d:response>
|
|
166
|
+
</d:multistatus>
|
|
167
|
+
XML
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
let(:multi_namespace_response){WebDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: multi_namespace_xml))}
|
|
171
|
+
|
|
172
|
+
it "groups DAV properties under the DAV: namespace" do
|
|
173
|
+
_(multi_namespace_response.resources[0][:propstats][0][:properties]['DAV:']['getetag']).must_equal '"abc123"'
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "groups CalDAV properties under the CalDAV namespace" do
|
|
177
|
+
_(multi_namespace_response.resources[0][:propstats][0][:properties]['urn:ietf:params:xml:ns:caldav']['calendar-data']).must_equal 'BEGIN:VCALENDAR'
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
describe "with the same local name in different namespaces" do
|
|
182
|
+
let(:collision_xml) do
|
|
183
|
+
<<~XML
|
|
184
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
185
|
+
<d:multistatus xmlns:d="DAV:" xmlns:x="http://example.com/custom">
|
|
186
|
+
<d:response>
|
|
187
|
+
<d:href>/file.txt</d:href>
|
|
188
|
+
<d:propstat>
|
|
189
|
+
<d:prop>
|
|
190
|
+
<d:displayname>From DAV</d:displayname>
|
|
191
|
+
<x:displayname>From custom</x:displayname>
|
|
192
|
+
</d:prop>
|
|
193
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
194
|
+
</d:propstat>
|
|
195
|
+
</d:response>
|
|
196
|
+
</d:multistatus>
|
|
197
|
+
XML
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
let(:collision_response){WebDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: collision_xml))}
|
|
201
|
+
|
|
202
|
+
it "keeps both without collision" do
|
|
203
|
+
properties = collision_response.resources[0][:propstats][0][:properties]
|
|
204
|
+
_(properties['DAV:']['displayname']).must_equal 'From DAV'
|
|
205
|
+
_(properties['http://example.com/custom']['displayname']).must_equal 'From custom'
|
|
77
206
|
end
|
|
78
207
|
end
|
|
79
208
|
|
data/webdav.gemspec
CHANGED
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
spec.homepage = 'https://github.com/thoran/webdav'
|
|
23
23
|
spec.license = 'MIT'
|
|
24
24
|
|
|
25
|
-
spec.required_ruby_version = '>= 2
|
|
25
|
+
spec.required_ruby_version = '>= 3.2'
|
|
26
26
|
spec.require_paths = ['lib']
|
|
27
27
|
|
|
28
28
|
spec.files = [
|
|
@@ -37,12 +37,13 @@ Gem::Specification.new do |spec|
|
|
|
37
37
|
].flatten
|
|
38
38
|
|
|
39
39
|
spec.dependencies = [
|
|
40
|
-
['http.rb'],
|
|
40
|
+
['http.rb', '~> 1.0'],
|
|
41
41
|
['rexml']
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
spec.development_dependencies = [
|
|
45
|
-
['minitest', '~>
|
|
45
|
+
['minitest', '~> 6.0'],
|
|
46
|
+
['minitest-mock'],
|
|
46
47
|
['rake']
|
|
47
48
|
]
|
|
48
49
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: webdav
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
@@ -13,16 +13,16 @@ dependencies:
|
|
|
13
13
|
name: http.rb
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
18
|
+
version: '1.0'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
25
|
+
version: '1.0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rexml
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -43,14 +43,28 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '6.0'
|
|
47
47
|
type: :development
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '6.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: minitest-mock
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: rake
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -104,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
104
118
|
requirements:
|
|
105
119
|
- - ">="
|
|
106
120
|
- !ruby/object:Gem::Version
|
|
107
|
-
version: '2
|
|
121
|
+
version: '3.2'
|
|
108
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
123
|
requirements:
|
|
110
124
|
- - ">="
|