caldav.rb 0.1.0 → 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 +13 -0
- data/README.md +57 -13
- data/lib/CalDAV/Calendar.rb +67 -0
- data/lib/CalDAV/Extensions/Apple.rb +34 -0
- data/lib/CalDAV/Extensions/CalendarServer.rb +35 -0
- data/lib/CalDAV/Objects/Core.rb +9 -0
- data/lib/CalDAV/Objects.rb +11 -0
- data/lib/CalDAV/Resource.rb +4 -0
- data/lib/CalDAV/VERSION.rb +1 -1
- data/lib/caldav.rb +16 -4
- data/test/CalDAV/Calendar_test.rb +169 -0
- data/test/CalDAV/Extensions/Apple_test.rb +30 -0
- data/test/CalDAV/Extensions/CalendarServer_test.rb +30 -0
- data/test/CalDAV/Objects/Core_test.rb +15 -0
- data/test/CalDAV/Objects_test.rb +34 -0
- data/test/CalDAV/Resource_test.rb +5 -0
- data/test/{caldav_test.rb → CalDAV_test.rb} +5 -2
- data/test/integration_test.rb +6 -5
- metadata +13 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 78f2d9632906e82c83ef2a9f7368fedd3a6e6ec473b1bf5225963ca3e298fef0
|
|
4
|
+
data.tar.gz: fd60c2113bc0059685560b89052edcfd70c2e85bda4359e2be03b48a49196688
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4b935fdde691594b182f27fe4aed10e43017a986b1d4de04ec47443c7090d0485714b2ec424ddf760de9363d484e7be899a9d8750757e99d5919273bef401ba
|
|
7
|
+
data.tar.gz: d26b9a87746ea1e8abdfc2c20f31c641e6980a78d95ccceb0eb1ba1f3228a3540b5b6e5e0ebd5c4447426d74226b236dded24893e3b0212ef8480ff1d74a6a70
|
data/CHANGELOG
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 20260811
|
|
4
|
+
|
|
5
|
+
0.2.0: Begin Layer 2 (Objects) — Calendar value objects and object-returning discovery.
|
|
6
|
+
|
|
7
|
+
1. + CalDAV::Calendar, a value object (path, display_name, ctag, color, description, timezone, supported_components), with .from_resource mapping a CalDAV::Resource, and .all(client:, home:) discovering a home's calendars as Calendars — the eodhd model-class-method form; the client stays the low-level raw path.
|
|
8
|
+
2. + the object-layer entry points: require 'CalDAV/Objects' (default, with extensions) and require 'CalDAV/Objects/Core' (objects without the extension accessors).
|
|
9
|
+
3. + a composable vendor-extension layer: lib/CalDAV/Extensions/CalendarServer.rb (getctag) and Apple.rb (calendar-color). Each is a module that prepends onto the client to add its namespace and property to the calendar-discovery request and mixes a reader into CalDAV::Resource, composing via super so vendors stack rather than clobber. Layer 1 stays strictly RFC 4791 (empty base hooks); the object layer loads both by default, 'CalDAV/Objects/Core' loads neither, and a consumer may require a single vendor. from_resource reads ctag/color opportunistically.
|
|
10
|
+
4. + CalDAV::Resource#display_name (DAV:displayname).
|
|
11
|
+
5. ~ CalDAV#calendars: returns CalDAV::Resource objects rather than href strings, from a fuller PROPFIND (displayname, calendar-description, calendar-timezone, supported-calendar-component-set). Callers wanting raw read the resources directly; Calendar.all maps them.
|
|
12
|
+
|
|
13
|
+
|
|
3
14
|
## 20260623
|
|
4
15
|
|
|
5
16
|
0.1.0: Complete the RFC 4791 §5.2 navigation surface.
|
|
@@ -14,6 +25,7 @@
|
|
|
14
25
|
8. ~ README navigation-accessor list.
|
|
15
26
|
9. ~ VERSION: /0.0.1/0.1.0/
|
|
16
27
|
|
|
28
|
+
|
|
17
29
|
## 20260621
|
|
18
30
|
|
|
19
31
|
0.0.1: Lowercase load file; require VERSION; MultiStatus tests.
|
|
@@ -24,6 +36,7 @@
|
|
|
24
36
|
4. + CalDAV::MultiStatus unit test, covering the two overrides over WebDAV::MultiStatus: parse_response (wraps each response as a CalDAV::Resource, in order) and parse_properties (a pretty-printed nested property is kept as serialised markup rather than collapsing to inter-tag whitespace).
|
|
25
37
|
5. ~ VERSION: /0.0.0/0.0.1/
|
|
26
38
|
|
|
39
|
+
|
|
27
40
|
## 20260620
|
|
28
41
|
|
|
29
42
|
0.0.0: Initial release.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# caldav.rb
|
|
2
2
|
|
|
3
|
-
A CalDAV client library for Ruby, built on [webdav](https://github.com/thoran/webdav) and implementing RFC 4791.
|
|
3
|
+
A CalDAV client library for Ruby, built on [webdav](https://github.com/thoran/webdav) and implementing RFC 4791. It ships **Layer 1 (Protocol)** and the beginning of **Layer 2 (Objects)** — Calendar objects — of a planned three-layer ecosystem.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
@@ -18,11 +18,11 @@ gem 'caldav.rb'
|
|
|
18
18
|
|
|
19
19
|
## Concepts
|
|
20
20
|
|
|
21
|
-
caldav.rb is designed in three layers, each a strict superset of the one below.
|
|
21
|
+
caldav.rb is designed in three layers, each a strict superset of the one below. Layer 1 is complete; Layer 2 has begun with Calendar objects; its Event and Principal objects, and all of Layer 3, are future releases.
|
|
22
22
|
|
|
23
|
-
- **Layer 1 — Protocol
|
|
24
|
-
- **Layer 2 — Objects** (
|
|
25
|
-
- **Layer 3 — Queryable** (future, `require '
|
|
23
|
+
- **Layer 1 — Protocol.** The CalDAV verbs, multistatus responses, and namespace-aware navigation accessors. The equivalent of a raw protocol library: it returns CalDAV-typed responses but does not parse iCalendar payloads or construct domain objects. For events, read `resource.calendar_data` and parse it with the `icalendar` gem until Layer 2's `CalDAV::Event` lands.
|
|
24
|
+
- **Layer 2 — Objects** (`require 'CalDAV/Objects'`). Value objects over the protocol layer. `CalDAV::Calendar` and object-returning discovery (`CalDAV::Calendar.all`) ship now, with vendor properties (getctag, calendar-color) available and separable (`require 'CalDAV/Objects/Core'` to omit them). `CalDAV::Event` (with iCalendar parsing) and `CalDAV::Principal` are still to come.
|
|
25
|
+
- **Layer 3 — Queryable** (future, `require 'CalDAV/Queryable'`). A [Namo](https://github.com/thoran/namo)-backed `CalDAV::Query::Calendar` exposing events as queryable rows with derived columns.
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
## Usage
|
|
@@ -33,12 +33,43 @@ require 'caldav.rb'
|
|
|
33
33
|
caldav = CalDAV.new('https://caldav.example.com/dav/', username: 'user', password: 'pass')
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
The object layer (`require 'CalDAV/Objects'`) is the high-level API; today it covers calendar discovery. Everything else below is the protocol layer it is built on — which you can always use directly for raw access.
|
|
37
|
+
|
|
36
38
|
### Discovery
|
|
37
39
|
|
|
40
|
+
With the object layer, listing calendars returns `CalDAV::Calendar` objects — `Calendar.all` walks principal → home-set → calendars for you:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require 'CalDAV/Objects'
|
|
44
|
+
CalDAV::Calendar.all(client: caldav).each{|calendar| puts calendar.display_name}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Each `Calendar` exposes its collection's properties:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
calendar = CalDAV::Calendar.all(client: caldav).first
|
|
51
|
+
calendar.path # "/dav/calendars/user/work/"
|
|
52
|
+
calendar.description # the calendar-description string
|
|
53
|
+
calendar.timezone # the raw VTIMEZONE string, if the server sets one
|
|
54
|
+
calendar.supported_components # the raw supported-component-set (unparsed markup)
|
|
55
|
+
calendar.ctag # change tag for cheap sync-detection (vendor extension)
|
|
56
|
+
calendar.color # the calendar colour (vendor extension)
|
|
57
|
+
calendar.to_s # display name, or path when unnamed
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`ctag` and `color` come from the vendor extensions, loaded by default. Require the core objects instead to omit them — the readers stay, but return `nil`:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require 'CalDAV/Objects/Core' # strictly RFC 4791 — no vendor extensions
|
|
64
|
+
CalDAV::Calendar.all(client: caldav).first.ctag # => nil
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Drop to the protocol layer when you want the raw resources, or aren't loading the object layer — the same walk, step by step:
|
|
68
|
+
|
|
38
69
|
```ruby
|
|
39
70
|
principal = caldav.current_user_principal
|
|
40
71
|
home = caldav.calendar_home_set(principal)
|
|
41
|
-
caldav.calendars(home).each{|
|
|
72
|
+
caldav.calendars(home).each{|resource| puts resource.href} # CalDAV::Resource objects
|
|
42
73
|
```
|
|
43
74
|
|
|
44
75
|
Each discovery helper defaults its argument to the result of the previous step, so `caldav.calendars` alone walks principal → home-set → calendars.
|
|
@@ -77,18 +108,30 @@ caldav.mkcalendar('/calendars/user/new/', body: mkcalendar_xml)
|
|
|
77
108
|
|
|
78
109
|
## Methods
|
|
79
110
|
|
|
80
|
-
### Protocol
|
|
111
|
+
### Layer 1 — Protocol (`require 'caldav.rb'`)
|
|
112
|
+
|
|
113
|
+
Raw CalDAV-typed responses and navigation resources; no domain objects.
|
|
114
|
+
|
|
115
|
+
#### Protocol verbs (RFC 4791)
|
|
81
116
|
|
|
82
117
|
- `mkcalendar(path, body:)` — §5.3.1. Create a calendar collection. Returns a `WebDAV::Response`.
|
|
83
118
|
- `calendar_query(path, body:, depth:)` — §7.8. REPORT with a `<c:calendar-query>` body. Returns a `CalDAV::MultiStatus`.
|
|
84
119
|
- `calendar_multiget(path, body:, depth:)` — §7.9. REPORT with a `<c:calendar-multiget>` body. Returns a `CalDAV::MultiStatus`.
|
|
85
120
|
- `freebusy_query(path, body:, depth:)` — §7.10. REPORT with a `<c:free-busy-query>` body. Returns a raw `WebDAV::Response` carrying VFREEBUSY iCalendar data — **not** a multistatus. This asymmetry is inherent to the CalDAV spec.
|
|
86
121
|
|
|
87
|
-
|
|
122
|
+
#### Discovery
|
|
88
123
|
|
|
89
124
|
- `current_user_principal(path = base URL path)` — returns the principal URL string. PROPFINDs the base URL's path by default; pass a path to start discovery elsewhere.
|
|
90
125
|
- `calendar_home_set(principal = current_user_principal)` — returns the calendar-home-set URL string.
|
|
91
|
-
- `calendars(home = calendar_home_set)` — returns
|
|
126
|
+
- `calendars(home = calendar_home_set)` — returns the calendar-collection `CalDAV::Resource`s.
|
|
127
|
+
|
|
128
|
+
### Layer 2 — Objects (`require 'CalDAV/Objects'`)
|
|
129
|
+
|
|
130
|
+
Value objects built over Layer 1. The default require also loads the vendor extensions (`getctag`, `calendar-color`); `require 'CalDAV/Objects/Core'` omits them.
|
|
131
|
+
|
|
132
|
+
- `CalDAV::Calendar.all(client:, home: nil)` — discovers a home's calendars and returns `CalDAV::Calendar` objects. `home` defaults to the client's discovered calendar-home-set.
|
|
133
|
+
- `CalDAV::Calendar.from_resource(resource)` — maps a single `CalDAV::Resource` to a `CalDAV::Calendar`.
|
|
134
|
+
- `CalDAV::Calendar` readers — `path`, `display_name`, `description`, `timezone`, `supported_components`, `ctag`, `color`; `ctag` and `color` are `nil` unless the vendor extensions are loaded. `to_s` gives the display name or path.
|
|
92
135
|
|
|
93
136
|
|
|
94
137
|
## Responses
|
|
@@ -96,6 +139,7 @@ caldav.mkcalendar('/calendars/user/new/', body: mkcalendar_xml)
|
|
|
96
139
|
The REPORT verbs return `CalDAV::MultiStatus`, a type-preserving subclass of `WebDAV::MultiStatus` whose `resources` are `CalDAV::Resource` objects. Each `CalDAV::Resource` adds CalDAV-namespace navigation accessors over the underlying webdav resource:
|
|
97
140
|
|
|
98
141
|
- `href` — the resource URL
|
|
142
|
+
- `display_name` — the human-readable name from `<d:displayname>`
|
|
99
143
|
- `calendar_data` — the iCalendar string from `<c:calendar-data>`
|
|
100
144
|
- `calendar_description` — `<c:calendar-description>`
|
|
101
145
|
- `supported_calendar_component_set` — `<c:supported-calendar-component-set>`
|
|
@@ -113,13 +157,13 @@ These are strictly navigation: they return raw strings and values, never parsed
|
|
|
113
157
|
|
|
114
158
|
## Limitations
|
|
115
159
|
|
|
116
|
-
|
|
160
|
+
The client reads but does not write, and the object layer is only beginning. Known boundaries:
|
|
117
161
|
|
|
118
|
-
- **Read-only.**
|
|
119
|
-
- **No iCalendar parsing.** Use the `icalendar` gem on `resource.calendar_data` if you need parsed events now.
|
|
162
|
+
- **Read-only.** No creating, updating or deleting yet — conditional writes need `If-Match`, which webdav's `put` does not yet expose. Writes are a later increment, beyond Layer 2's read-only objects, not part of them.
|
|
163
|
+
- **No iCalendar parsing.** Use the `icalendar` gem on `resource.calendar_data` if you need parsed events now, until Layer 2's `CalDAV::Event` lands.
|
|
120
164
|
- **Basic auth only.** No OAuth.
|
|
121
165
|
- **Tested against one real CalDAV server.** Other servers should work but are unverified.
|
|
122
|
-
- **No sync-collection.**
|
|
166
|
+
- **No sync-collection.** `getctag` gives coarse per-collection change-detection now; fine-grained sync-collection (RFC 6578) is deferred.
|
|
123
167
|
|
|
124
168
|
|
|
125
169
|
## Dependencies
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# CalDAV/Calendar.rb
|
|
2
|
+
# CalDAV::Calendar
|
|
3
|
+
|
|
4
|
+
require 'webdav'
|
|
5
|
+
|
|
6
|
+
class CalDAV < WebDAV
|
|
7
|
+
class Calendar
|
|
8
|
+
attr_reader\
|
|
9
|
+
:path,
|
|
10
|
+
:display_name,
|
|
11
|
+
:ctag,
|
|
12
|
+
:color,
|
|
13
|
+
:description,
|
|
14
|
+
:timezone,
|
|
15
|
+
:supported_components
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
# Discovers the calendars in a home collection and returns them as
|
|
19
|
+
# Calendars — the high-level, object-returning entry point. The CalDAV
|
|
20
|
+
# client stays the low-level path for anyone wanting the raw resources.
|
|
21
|
+
# home defaults to the client's discovered calendar-home-set.
|
|
22
|
+
def all(client:, home: nil)
|
|
23
|
+
load(client: client, home: home)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Builds a Calendar from a CalDAV::Resource (as returned by a calendar-home
|
|
27
|
+
# PROPFIND). Strictly a mapping over the resource's navigation accessors — no
|
|
28
|
+
# parsing. ctag and color are read opportunistically: populated only when
|
|
29
|
+
# the extension accessors are loaded (require 'CalDAV/Objects' pulls them
|
|
30
|
+
# in; 'CalDAV/Objects/Core' does not), and nil otherwise.
|
|
31
|
+
def from_resource(resource)
|
|
32
|
+
new(
|
|
33
|
+
path: resource.href,
|
|
34
|
+
display_name: resource.display_name,
|
|
35
|
+
ctag: (resource.getctag if resource.respond_to?(:getctag)),
|
|
36
|
+
color: (resource.calendar_color if resource.respond_to?(:calendar_color)),
|
|
37
|
+
description: resource.calendar_description,
|
|
38
|
+
timezone: resource.calendar_timezone,
|
|
39
|
+
supported_components: resource.supported_calendar_component_set
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def load(client:, home: nil)
|
|
46
|
+
resources = home ? client.calendars(home) : client.calendars
|
|
47
|
+
resources.collect{|resource| from_resource(resource)}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
display_name || path
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def initialize(path:, display_name: nil, ctag: nil, color: nil, description: nil, timezone: nil, supported_components: nil)
|
|
58
|
+
@path = path
|
|
59
|
+
@display_name = display_name
|
|
60
|
+
@ctag = ctag
|
|
61
|
+
@color = color
|
|
62
|
+
@description = description
|
|
63
|
+
@timezone = timezone
|
|
64
|
+
@supported_components = supported_components
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CalDAV/Extensions/Apple.rb
|
|
2
|
+
# CalDAV::Extensions::Apple
|
|
3
|
+
|
|
4
|
+
# The apple calendar-color property. A non-RFC-4791 vendor extension: it prepends
|
|
5
|
+
# its namespace and property onto the calendar-discovery request and mixes its
|
|
6
|
+
# reader into Resource. Composes with other extensions via super. Loaded with the
|
|
7
|
+
# object layer; omitted by 'CalDAV/Objects/Core'.
|
|
8
|
+
|
|
9
|
+
require_relative '../../caldav'
|
|
10
|
+
|
|
11
|
+
class CalDAV < WebDAV
|
|
12
|
+
module Extensions
|
|
13
|
+
module Apple
|
|
14
|
+
NAMESPACE = 'http://apple.com/ns/ical/'
|
|
15
|
+
|
|
16
|
+
private def calendars_request_namespaces
|
|
17
|
+
%(#{super} xmlns:ic="#{NAMESPACE}")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private def calendars_request_properties
|
|
21
|
+
"#{super}\n <ic:calendar-color/>"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Reader
|
|
25
|
+
def calendar_color
|
|
26
|
+
property(CalDAV::Extensions::Apple::NAMESPACE, 'calendar-color')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
prepend Extensions::Apple
|
|
33
|
+
Resource.include Extensions::Apple::Reader
|
|
34
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# CalDAV/Extensions/CalendarServer.rb
|
|
2
|
+
# CalDAV::Extensions::CalendarServer
|
|
3
|
+
|
|
4
|
+
# The calendarserver getctag property (calendar-collection sync token). A
|
|
5
|
+
# non-RFC-4791 vendor extension: it prepends its namespace and property onto the
|
|
6
|
+
# calendar-discovery request and mixes its reader into Resource. Composes with
|
|
7
|
+
# other extensions via super. Loaded with the object layer; omitted by
|
|
8
|
+
# 'CalDAV/Objects/Core'.
|
|
9
|
+
|
|
10
|
+
require_relative '../../caldav'
|
|
11
|
+
|
|
12
|
+
class CalDAV < WebDAV
|
|
13
|
+
module Extensions
|
|
14
|
+
module CalendarServer
|
|
15
|
+
NAMESPACE = 'http://calendarserver.org/ns/'
|
|
16
|
+
|
|
17
|
+
private def calendars_request_namespaces
|
|
18
|
+
%(#{super} xmlns:cs="#{NAMESPACE}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private def calendars_request_properties
|
|
22
|
+
"#{super}\n <cs:getctag/>"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Reader
|
|
26
|
+
def getctag
|
|
27
|
+
property(CalDAV::Extensions::CalendarServer::NAMESPACE, 'getctag')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
prepend Extensions::CalendarServer
|
|
34
|
+
Resource.include Extensions::CalendarServer::Reader
|
|
35
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# CalDAV/Objects/Core.rb
|
|
2
|
+
# CalDAV object layer, core (no extension accessors)
|
|
3
|
+
|
|
4
|
+
# The object layer without the extension-namespace accessors — a strictly
|
|
5
|
+
# RFC 4791 object surface. `require 'CalDAV/Objects'` loads this plus the
|
|
6
|
+
# extensions; require this path directly to opt out of them.
|
|
7
|
+
|
|
8
|
+
require_relative '../../caldav'
|
|
9
|
+
require_relative '../Calendar'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CalDAV/Objects.rb
|
|
2
|
+
# CalDAV Objects layer (Layer 2), default: with vendor extensions
|
|
3
|
+
|
|
4
|
+
# Value objects built over the Layer 1 protocol/navigation surface. Opt-in via
|
|
5
|
+
# `require 'CalDAV/Objects'`. This default form also loads the vendor extensions
|
|
6
|
+
# (calendarserver getctag, apple calendar-color); require 'CalDAV/Objects/Core'
|
|
7
|
+
# for the objects without them, a strictly RFC 4791 consumer.
|
|
8
|
+
|
|
9
|
+
require_relative './Objects/Core'
|
|
10
|
+
require_relative './Extensions/Apple'
|
|
11
|
+
require_relative './Extensions/CalendarServer'
|
data/lib/CalDAV/Resource.rb
CHANGED
data/lib/CalDAV/VERSION.rb
CHANGED
data/lib/caldav.rb
CHANGED
|
@@ -37,8 +37,10 @@ class CalDAV < WebDAV
|
|
|
37
37
|
handle_response(response)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# Discovery helpers.
|
|
41
|
-
#
|
|
40
|
+
# Discovery helpers. current_user_principal and calendar_home_set return href
|
|
41
|
+
# strings; calendars returns CalDAV::Resource objects (the navigation surface),
|
|
42
|
+
# which the object layer (CalDAV::Calendar.all, via require 'CalDAV/Objects')
|
|
43
|
+
# maps into value objects.
|
|
42
44
|
|
|
43
45
|
def current_user_principal(path = discovery_path)
|
|
44
46
|
resource = propfind_as_caldav(path, body: current_user_principal_body, depth: '0').resources.first
|
|
@@ -52,7 +54,7 @@ class CalDAV < WebDAV
|
|
|
52
54
|
|
|
53
55
|
def calendars(home = calendar_home_set)
|
|
54
56
|
resources = propfind_as_caldav(home, body: calendars_body, depth: '1').resources
|
|
55
|
-
resources.select{|resource| resource.is_calendar?}
|
|
57
|
+
resources.select{|resource| resource.is_calendar?}
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
private
|
|
@@ -126,11 +128,21 @@ class CalDAV < WebDAV
|
|
|
126
128
|
def calendars_body
|
|
127
129
|
<<~XML
|
|
128
130
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
129
|
-
<d:propfind xmlns:d="DAV:">
|
|
131
|
+
<d:propfind xmlns:d="DAV:" xmlns:c="#{NAMESPACE}"#{calendars_request_namespaces}>
|
|
130
132
|
<d:prop>
|
|
133
|
+
<d:displayname/>
|
|
131
134
|
<d:resourcetype/>
|
|
135
|
+
<c:calendar-description/>
|
|
136
|
+
<c:calendar-timezone/>
|
|
137
|
+
<c:supported-calendar-component-set/>#{calendars_request_properties}
|
|
132
138
|
</d:prop>
|
|
133
139
|
</d:propfind>
|
|
134
140
|
XML
|
|
135
141
|
end
|
|
142
|
+
|
|
143
|
+
# Root of the calendar-discovery extension chain. Extension modules prepend
|
|
144
|
+
# onto these and super, so their contributions compose rather than clobber;
|
|
145
|
+
# empty here keeps the request strictly RFC 4791.
|
|
146
|
+
def calendars_request_namespaces; ''; end
|
|
147
|
+
def calendars_request_properties; ''; end
|
|
136
148
|
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# test/CalDAV/Calendar_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
require_relative '../../lib/CalDAV/Objects'
|
|
5
|
+
|
|
6
|
+
describe CalDAV::Calendar do
|
|
7
|
+
let(:calendar) do
|
|
8
|
+
CalDAV::Calendar.new(
|
|
9
|
+
path: '/dav/calendars/user/test/work/',
|
|
10
|
+
display_name: 'Work',
|
|
11
|
+
ctag: 'abc123',
|
|
12
|
+
color: '#FF0000',
|
|
13
|
+
description: 'Work calendar',
|
|
14
|
+
supported_components: 'VEVENT'
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#path" do
|
|
19
|
+
it "returns the path" do
|
|
20
|
+
_(calendar.path).must_equal '/dav/calendars/user/test/work/'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#display_name" do
|
|
25
|
+
it "returns the display name" do
|
|
26
|
+
_(calendar.display_name).must_equal 'Work'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#ctag" do
|
|
31
|
+
it "returns the ctag" do
|
|
32
|
+
_(calendar.ctag).must_equal 'abc123'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "#color" do
|
|
37
|
+
it "returns the color" do
|
|
38
|
+
_(calendar.color).must_equal '#FF0000'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "#to_s" do
|
|
43
|
+
it "returns the display name" do
|
|
44
|
+
_(calendar.to_s).must_equal 'Work'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "falls back to path when display_name is nil" do
|
|
48
|
+
c = CalDAV::Calendar.new(path: '/dav/work/')
|
|
49
|
+
_(c.to_s).must_equal '/dav/work/'
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe ".from_resource" do
|
|
54
|
+
let(:multistatus) do
|
|
55
|
+
CalDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: <<~XML))
|
|
56
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
57
|
+
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:ic="http://apple.com/ns/ical/">
|
|
58
|
+
<d:response>
|
|
59
|
+
<d:href>/calendars/user/work/</d:href>
|
|
60
|
+
<d:propstat>
|
|
61
|
+
<d:prop>
|
|
62
|
+
<d:displayname>Work</d:displayname>
|
|
63
|
+
<d:resourcetype><d:collection/><c:calendar/></d:resourcetype>
|
|
64
|
+
<cs:getctag>abc123</cs:getctag>
|
|
65
|
+
<ic:calendar-color>#FF0000</ic:calendar-color>
|
|
66
|
+
<c:calendar-description>Work calendar</c:calendar-description>
|
|
67
|
+
<c:calendar-timezone>BEGIN:VTIMEZONE</c:calendar-timezone>
|
|
68
|
+
</d:prop>
|
|
69
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
70
|
+
</d:propstat>
|
|
71
|
+
</d:response>
|
|
72
|
+
</d:multistatus>
|
|
73
|
+
XML
|
|
74
|
+
end
|
|
75
|
+
let(:built){CalDAV::Calendar.from_resource(multistatus.resources.first)}
|
|
76
|
+
|
|
77
|
+
it "maps href to path" do
|
|
78
|
+
_(built.path).must_equal '/calendars/user/work/'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "maps displayname to display_name" do
|
|
82
|
+
_(built.display_name).must_equal 'Work'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "maps calendar-description to description" do
|
|
86
|
+
_(built.description).must_equal 'Work calendar'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "maps calendar-timezone to timezone" do
|
|
90
|
+
_(built.timezone).must_equal 'BEGIN:VTIMEZONE'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "maps getctag to ctag (extension, loaded by default)" do
|
|
94
|
+
_(built.ctag).must_equal 'abc123'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "maps calendar-color to color (extension, loaded by default)" do
|
|
98
|
+
_(built.color).must_equal '#FF0000'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "leaves ctag and color nil when those properties are absent" do
|
|
102
|
+
body = <<~XML
|
|
103
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
104
|
+
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
105
|
+
<d:response>
|
|
106
|
+
<d:href>/calendars/user/plain/</d:href>
|
|
107
|
+
<d:propstat>
|
|
108
|
+
<d:prop><d:displayname>Plain</d:displayname></d:prop>
|
|
109
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
110
|
+
</d:propstat>
|
|
111
|
+
</d:response>
|
|
112
|
+
</d:multistatus>
|
|
113
|
+
XML
|
|
114
|
+
plain = CalDAV::Calendar.from_resource(CalDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: body)).resources.first)
|
|
115
|
+
_(plain.ctag).must_be_nil
|
|
116
|
+
_(plain.color).must_be_nil
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe ".all" do
|
|
121
|
+
let(:caldav){CalDAV.new('https://caldav.example.com/', username: 'u', password: 'p')}
|
|
122
|
+
let(:home_response) do
|
|
123
|
+
MockResponse.new(code: '207', message: 'Multi-Status', body: <<~XML)
|
|
124
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
125
|
+
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:ic="http://apple.com/ns/ical/">
|
|
126
|
+
<d:response>
|
|
127
|
+
<d:href>/dav/calendars/user/abc/</d:href>
|
|
128
|
+
<d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
|
|
129
|
+
</d:response>
|
|
130
|
+
<d:response>
|
|
131
|
+
<d:href>/dav/calendars/user/abc/work/</d:href>
|
|
132
|
+
<d:propstat>
|
|
133
|
+
<d:prop>
|
|
134
|
+
<d:displayname>Work</d:displayname>
|
|
135
|
+
<d:resourcetype><d:collection/><c:calendar/></d:resourcetype>
|
|
136
|
+
<cs:getctag>ctag-1</cs:getctag>
|
|
137
|
+
<ic:calendar-color>#FF0000</ic:calendar-color>
|
|
138
|
+
<c:calendar-description>Work calendar</c:calendar-description>
|
|
139
|
+
</d:prop>
|
|
140
|
+
<d:status>HTTP/1.1 200 OK</d:status>
|
|
141
|
+
</d:propstat>
|
|
142
|
+
</d:response>
|
|
143
|
+
</d:multistatus>
|
|
144
|
+
XML
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "returns Calendar objects for the calendar collections only" do
|
|
148
|
+
caldav.stub(:request, home_response) do
|
|
149
|
+
calendars = CalDAV::Calendar.all(client: caldav, home: '/dav/calendars/user/abc/')
|
|
150
|
+
_(calendars.length).must_equal 1
|
|
151
|
+
_(calendars.first).must_be_kind_of CalDAV::Calendar
|
|
152
|
+
_(calendars.first.path).must_equal '/dav/calendars/user/abc/work/'
|
|
153
|
+
_(calendars.first.display_name).must_equal 'Work'
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "maps the extension properties when the extensions are loaded" do
|
|
158
|
+
caldav.stub(:request, home_response) do
|
|
159
|
+
work = CalDAV::Calendar.all(client: caldav, home: '/dav/calendars/user/abc/').first
|
|
160
|
+
_(work.ctag).must_equal 'ctag-1'
|
|
161
|
+
_(work.color).must_equal '#FF0000'
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "requires a client" do
|
|
166
|
+
_{CalDAV::Calendar.all(home: '/dav/calendars/user/abc/')}.must_raise ArgumentError
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# test/CalDAV/Extensions/Apple_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../../helper'
|
|
4
|
+
require 'CalDAV/Objects' # loads the vendor extensions
|
|
5
|
+
|
|
6
|
+
describe "CalDAV::Extensions::Apple" do
|
|
7
|
+
let(:resource) do
|
|
8
|
+
body = <<~XML
|
|
9
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
10
|
+
<d:multistatus xmlns:d="DAV:" xmlns:ic="http://apple.com/ns/ical/">
|
|
11
|
+
<d:response>
|
|
12
|
+
<d:href>/calendars/user/work/</d:href>
|
|
13
|
+
<d:propstat><d:prop><ic:calendar-color>#FF0000</ic:calendar-color></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
|
|
14
|
+
</d:response>
|
|
15
|
+
</d:multistatus>
|
|
16
|
+
XML
|
|
17
|
+
CalDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: body)).resources.first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "reads calendar-color from the apple namespace" do
|
|
21
|
+
_(resource.calendar_color).must_equal '#FF0000'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "contributes its namespace and property to the calendars request" do
|
|
25
|
+
caldav = CalDAV.new('https://caldav.example.com/', username: 'u', password: 'p')
|
|
26
|
+
body = caldav.send(:calendars_body)
|
|
27
|
+
_(body).must_include 'xmlns:ic="http://apple.com/ns/ical/"'
|
|
28
|
+
_(body).must_include '<ic:calendar-color/>'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# test/CalDAV/Extensions/CalendarServer_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../../helper'
|
|
4
|
+
require 'CalDAV/Objects' # loads the vendor extensions
|
|
5
|
+
|
|
6
|
+
describe "CalDAV::Extensions::CalendarServer" do
|
|
7
|
+
let(:resource) do
|
|
8
|
+
body = <<~XML
|
|
9
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
10
|
+
<d:multistatus xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/">
|
|
11
|
+
<d:response>
|
|
12
|
+
<d:href>/calendars/user/work/</d:href>
|
|
13
|
+
<d:propstat><d:prop><cs:getctag>abc123</cs:getctag></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
|
|
14
|
+
</d:response>
|
|
15
|
+
</d:multistatus>
|
|
16
|
+
XML
|
|
17
|
+
CalDAV::MultiStatus.new(MockResponse.new(code: '207', message: 'Multi-Status', body: body)).resources.first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "reads getctag from the calendarserver namespace" do
|
|
21
|
+
_(resource.getctag).must_equal 'abc123'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "contributes its namespace and property to the calendars request" do
|
|
25
|
+
caldav = CalDAV.new('https://caldav.example.com/', username: 'u', password: 'p')
|
|
26
|
+
body = caldav.send(:calendars_body)
|
|
27
|
+
_(body).must_include 'xmlns:cs="http://calendarserver.org/ns/"'
|
|
28
|
+
_(body).must_include '<cs:getctag/>'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# test/CalDAV/Objects/Core_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../../helper'
|
|
4
|
+
require 'CalDAV/Objects/Core'
|
|
5
|
+
|
|
6
|
+
# The core path loads the object layer without the extension accessors. Their
|
|
7
|
+
# absence can't be asserted in-process — requires are global, and another test
|
|
8
|
+
# loads the full 'CalDAV/Objects' — so this covers only that the path itself
|
|
9
|
+
# resolves and yields the objects; the opt-out is a structural property of
|
|
10
|
+
# Core.rb not requiring Resource/Extensions.
|
|
11
|
+
describe "CalDAV/Objects/Core" do
|
|
12
|
+
it "loads the object layer through the core require path" do
|
|
13
|
+
_(defined?(CalDAV::Calendar)).must_equal 'constant'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# test/CalDAV/Objects_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../helper'
|
|
4
|
+
|
|
5
|
+
# Exercise the Layer 2 entry point through its real public require path, so this
|
|
6
|
+
# guards the path itself (the CalDAV-cased, portable form) — not just the file's
|
|
7
|
+
# contents. lib is on the load path under rake's TestTask, as it is for a
|
|
8
|
+
# consumer of the installed gem.
|
|
9
|
+
require 'CalDAV/Objects'
|
|
10
|
+
|
|
11
|
+
describe "CalDAV/Objects" do
|
|
12
|
+
it "loads the object layer through the require path" do
|
|
13
|
+
_(defined?(CalDAV::Calendar)).must_equal 'constant'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "pulls in the Layer 1 base it builds on" do
|
|
17
|
+
_(CalDAV.ancestors).must_include(WebDAV)
|
|
18
|
+
_(defined?(CalDAV::Resource)).must_equal 'constant'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "loads the extension accessors by default" do
|
|
22
|
+
_(CalDAV::Resource.method_defined?(:getctag)).must_equal true
|
|
23
|
+
_(CalDAV::Resource.method_defined?(:calendar_color)).must_equal true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "composes every loaded extension into the calendars request" do
|
|
27
|
+
caldav = CalDAV.new('https://caldav.example.com/', username: 'u', password: 'p')
|
|
28
|
+
body = caldav.send(:calendars_body)
|
|
29
|
+
_(body).must_include 'xmlns:cs="http://calendarserver.org/ns/"'
|
|
30
|
+
_(body).must_include '<cs:getctag/>'
|
|
31
|
+
_(body).must_include 'xmlns:ic="http://apple.com/ns/ical/"'
|
|
32
|
+
_(body).must_include '<ic:calendar-color/>'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -12,6 +12,7 @@ describe CalDAV::Resource do
|
|
|
12
12
|
<d:propstat>
|
|
13
13
|
<d:prop>
|
|
14
14
|
<d:resourcetype><d:collection/><c:calendar/></d:resourcetype>
|
|
15
|
+
<d:displayname>Work</d:displayname>
|
|
15
16
|
<c:calendar-description>Work calendar</c:calendar-description>
|
|
16
17
|
<c:supported-calendar-data><c:calendar-data content-type="text/calendar" version="2.0"/></c:supported-calendar-data>
|
|
17
18
|
<c:max-resource-size>10485760</c:max-resource-size>
|
|
@@ -59,6 +60,10 @@ describe CalDAV::Resource do
|
|
|
59
60
|
_(calendar_resource.href).must_equal '/calendars/user/work/'
|
|
60
61
|
end
|
|
61
62
|
|
|
63
|
+
it "reads displayname" do
|
|
64
|
+
_(calendar_resource.display_name).must_equal 'Work'
|
|
65
|
+
end
|
|
66
|
+
|
|
62
67
|
it "reads calendar-description" do
|
|
63
68
|
_(calendar_resource.calendar_description).must_equal 'Work calendar'
|
|
64
69
|
end
|
|
@@ -173,9 +173,12 @@ describe CalDAV do
|
|
|
173
173
|
XML
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
-
it "returns only the calendar
|
|
176
|
+
it "returns only the calendar-collection resources" do
|
|
177
177
|
caldav.stub(:request, response) do
|
|
178
|
-
|
|
178
|
+
result = caldav.calendars('/dav/calendars/user/abc/')
|
|
179
|
+
_(result.length).must_equal 1
|
|
180
|
+
_(result.first).must_be_kind_of CalDAV::Resource
|
|
181
|
+
_(result.first.href).must_equal '/dav/calendars/user/abc/work/'
|
|
179
182
|
end
|
|
180
183
|
end
|
|
181
184
|
end
|
data/test/integration_test.rb
CHANGED
|
@@ -23,7 +23,7 @@ describe 'CalDAV integration' do
|
|
|
23
23
|
XML
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
it "discovers principal
|
|
26
|
+
it "discovers principal and home-set as strings, and calendars as resources" do
|
|
27
27
|
with_caldav_cassette('discovery') do
|
|
28
28
|
principal = caldav.current_user_principal
|
|
29
29
|
_(principal).must_be_kind_of String
|
|
@@ -33,9 +33,10 @@ describe 'CalDAV integration' do
|
|
|
33
33
|
_(home).wont_include '<'
|
|
34
34
|
calendars = caldav.calendars(home)
|
|
35
35
|
_(calendars).must_be_kind_of Array
|
|
36
|
-
calendars.each do |
|
|
37
|
-
_(
|
|
38
|
-
_(href).
|
|
36
|
+
calendars.each do |resource|
|
|
37
|
+
_(resource).must_be_kind_of CalDAV::Resource
|
|
38
|
+
_(resource.href).must_be_kind_of String
|
|
39
|
+
_(resource.href).wont_include '<'
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
end
|
|
@@ -44,7 +45,7 @@ describe 'CalDAV integration' do
|
|
|
44
45
|
with_caldav_cassette('calendar_query') do
|
|
45
46
|
calendar = caldav.calendars.first
|
|
46
47
|
skip 'no calendars on this account' unless calendar
|
|
47
|
-
result = caldav.calendar_query(calendar, body: events_query_body)
|
|
48
|
+
result = caldav.calendar_query(calendar.href, body: events_query_body)
|
|
48
49
|
_(result).must_be_kind_of CalDAV::MultiStatus
|
|
49
50
|
result.resources.each{|resource| _(resource).must_be_kind_of CalDAV::Resource}
|
|
50
51
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: caldav.rb
|
|
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
|
|
@@ -105,15 +105,25 @@ files:
|
|
|
105
105
|
- README.md
|
|
106
106
|
- Rakefile
|
|
107
107
|
- caldav.rb.gemspec
|
|
108
|
+
- lib/CalDAV/Calendar.rb
|
|
109
|
+
- lib/CalDAV/Extensions/Apple.rb
|
|
110
|
+
- lib/CalDAV/Extensions/CalendarServer.rb
|
|
108
111
|
- lib/CalDAV/MultiStatus.rb
|
|
112
|
+
- lib/CalDAV/Objects.rb
|
|
113
|
+
- lib/CalDAV/Objects/Core.rb
|
|
109
114
|
- lib/CalDAV/Resource.rb
|
|
110
115
|
- lib/CalDAV/VERSION.rb
|
|
111
116
|
- lib/Net/HTTP/Mkcalendar.rb
|
|
112
117
|
- lib/caldav.rb
|
|
118
|
+
- test/CalDAV/Calendar_test.rb
|
|
119
|
+
- test/CalDAV/Extensions/Apple_test.rb
|
|
120
|
+
- test/CalDAV/Extensions/CalendarServer_test.rb
|
|
113
121
|
- test/CalDAV/MultiStatus_test.rb
|
|
122
|
+
- test/CalDAV/Objects/Core_test.rb
|
|
123
|
+
- test/CalDAV/Objects_test.rb
|
|
114
124
|
- test/CalDAV/Resource_test.rb
|
|
125
|
+
- test/CalDAV_test.rb
|
|
115
126
|
- test/Net/HTTP/Mkcalendar_test.rb
|
|
116
|
-
- test/caldav_test.rb
|
|
117
127
|
- test/helper.rb
|
|
118
128
|
- test/integration_test.rb
|
|
119
129
|
- test/vcr_helper.rb
|
|
@@ -136,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
136
146
|
- !ruby/object:Gem::Version
|
|
137
147
|
version: '0'
|
|
138
148
|
requirements: []
|
|
139
|
-
rubygems_version: 4.0.
|
|
149
|
+
rubygems_version: 4.0.18
|
|
140
150
|
specification_version: 4
|
|
141
151
|
summary: A Ruby CalDAV client library.
|
|
142
152
|
test_files: []
|