calendav 0.4.0 → 0.5.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.md +8 -0
- data/README.md +73 -4
- data/lib/calendav/calendar.rb +2 -2
- data/lib/calendav/client.rb +10 -5
- data/lib/calendav/clients/calendars_client.rb +1 -1
- data/lib/calendav/clients/events_client.rb +4 -2
- data/lib/calendav/clients/todos_client.rb +73 -0
- data/lib/calendav/credentials/apple.rb +1 -1
- data/lib/calendav/credentials/fastmail.rb +1 -1
- data/lib/calendav/credentials/google.rb +1 -1
- data/lib/calendav/endpoint.rb +7 -5
- data/lib/calendav/error_handler.rb +1 -1
- data/lib/calendav/errors.rb +2 -2
- data/lib/calendav/event.rb +2 -2
- data/lib/calendav/parsers/todo_xml.rb +37 -0
- data/lib/calendav/requests/list_events.rb +13 -2
- data/lib/calendav/requests/list_todos.rb +49 -0
- data/lib/calendav/requests/make_calendar.rb +1 -0
- data/lib/calendav/todo.rb +60 -0
- data/lib/calendav.rb +4 -4
- metadata +7 -161
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f622fc58cda7b2c94bf7c6eff0056db4532e281bfd1e556a43f16ba8edac222
|
4
|
+
data.tar.gz: 075b5acb58221171aec66d8e9d0234410fa3f30cd0c790776ca0f023187f0d27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 596ecccf27b7cd0aeb697b4863f3c10c79e9ec8aaf2ca66786e2f31a94e5cf865a5835a8942a4c0189f7bfe040f5cde540ee7f10a09e0ec3cb693ae152923d09
|
7
|
+
data.tar.gz: 0ff81da9e901dfe16081a31a4df72319a6462ef939aa4aa83ebcb74792684e9b26b9999430159300192816e9ce9fdd31edbf04f57c6b0b28aa0cd85e9d9fe726
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## Unreleased
|
2
2
|
|
3
|
+
## 0.5.0 - 2025-08-02
|
4
|
+
|
5
|
+
* Support for todos (Josh Huckabee #8, #9)
|
6
|
+
* Improved documentation for development/testing environments (Josh Huckabee #7)
|
7
|
+
* Support for expanded recurring events (Sayooj Surendran #12)
|
8
|
+
* Support for OAuth authentication header (Ilya Nikitenkov #13)
|
9
|
+
* Updated tested Ruby versions to 3.1-3.4 (2.7 and 3.0 are no longer officially supported).
|
10
|
+
|
3
11
|
## 0.4.0 - 2023-02-27
|
4
12
|
|
5
13
|
* **Breaking**: calls to create/update events return event objects that only have a URL and etag populated (so: no calendar data), or nil if the request failed due to an etag precondition. This is instead of returning true on success or false on failure.
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ credentials = Calendav::Credentials::Standard.new(
|
|
23
23
|
host: "https://www.example.com/caldav",
|
24
24
|
username: "example",
|
25
25
|
password: "secret",
|
26
|
-
authentication: :basic_auth #
|
26
|
+
authentication: :basic_auth # :bearer_token and :oauth also supported
|
27
27
|
)
|
28
28
|
```
|
29
29
|
|
@@ -100,6 +100,16 @@ end
|
|
100
100
|
|
101
101
|
The returned events have a unique URL (just like calendars), an `etag` (which changes when the event changes), and `calendar_data`, which is stored in the [iCalendar](https://datatracker.ietf.org/doc/html/rfc5545) format. The event objects returned currently parse the summary out of the calendar data, but nothing else - further attributes may be made visible, but for full control it's recommended you use the [icalendar](https://github.com/icalendar/icalendar) gem.
|
102
102
|
|
103
|
+
To get recurring events, you can use the optional `expand_recurring_events` argument.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
events = client.events.list(
|
107
|
+
calendar_url, from: Time.new(2021, 1, 1), to: Time.new(2022, 1, 1),
|
108
|
+
expand_recurring_events: true
|
109
|
+
)
|
110
|
+
```
|
111
|
+
The recurrent events will be present in the `calendar_data`.
|
112
|
+
|
103
113
|
If you have an event's URL, you can fetch the details of just that event directly:
|
104
114
|
|
105
115
|
```ruby
|
@@ -151,6 +161,55 @@ client.events.delete(event_url)
|
|
151
161
|
client.events.delete(event_url, etag: event.etag)
|
152
162
|
```
|
153
163
|
|
164
|
+
### Todos
|
165
|
+
|
166
|
+
You can also use a calendar's URL to retrieve its todos.
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
todos = client.todos.list(calendar_url)
|
170
|
+
todos.each do |todo|
|
171
|
+
puts todo.url
|
172
|
+
puts todo.summary
|
173
|
+
puts todo.status
|
174
|
+
puts todo.due
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
Just like events, returned todos have a unique URL, an `etag` (which changes when the todo changes), and `calendar_data`. The todo objects returned currently parse the summary, status, and due date out of the calendar data.
|
179
|
+
|
180
|
+
If you have a todo's URL, you can fetch the details of just that todo directly:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
todo = client.todos.find(todo_url)
|
184
|
+
```
|
185
|
+
|
186
|
+
Creating todos, just like creating events, requires a unique identifier. You will also need to generate the iCalendar data - again, the [icalendar](https://github.com/icalendar/icalendar) gem is very helpful for this.
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
require "securerandom"
|
190
|
+
require "icalendar"
|
191
|
+
|
192
|
+
ics = Icalendar::Calendar.new
|
193
|
+
ics.todo do |todo|
|
194
|
+
# ...
|
195
|
+
end
|
196
|
+
ics.publish
|
197
|
+
|
198
|
+
identifier = "#{SecureRandom.uuid}.ics"
|
199
|
+
# The returned todo has just the URL and the etag, no calendar data:
|
200
|
+
todo_scaffold = client.todos.create(calendar.url, identifier, ics.to_ical)
|
201
|
+
```
|
202
|
+
|
203
|
+
Updating and deleting todos is similar to updating events:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
# Update the todo
|
207
|
+
client.todos.update(todo_url, ics.to_ical)
|
208
|
+
|
209
|
+
# Delete the todo
|
210
|
+
client.todos.delete(todo_url)
|
211
|
+
```
|
212
|
+
|
154
213
|
### Synchronising
|
155
214
|
|
156
215
|
If you are maintaining your own copy/history of events from a CalDAV server, then it's highly recommended you take advantage of the WebDAV-Sync protocol (should the calendar provider support it). Using Calendav, the suggested approach is:
|
@@ -198,7 +257,7 @@ While a lot of the core CalDAV functionality is covered and this gem is useful a
|
|
198
257
|
* Further iCalendar parsing for Event objects.
|
199
258
|
* Automated tests against FastMail and possibly other providers (Apple and Google are already covered).
|
200
259
|
* Locking/unlocking of events as per the WebCAL RFC.
|
201
|
-
* Support for
|
260
|
+
* Support for VJOURNAL, VFREEBUSY and any other components beyond VEVENT.
|
202
261
|
|
203
262
|
## Installation
|
204
263
|
|
@@ -232,11 +291,21 @@ The work done in previous Ruby CalDAV clients [RubyCaldav](https://github.com/di
|
|
232
291
|
|
233
292
|
## Development
|
234
293
|
|
235
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
294
|
+
After checking out the repo, run `bin/setup` to install dependencies and configure the required credentials. This will copy the `.env.example` file to a `.env` file. Make sure to configure the `.env` file with your own Apple and Google credentials.
|
295
|
+
|
296
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
236
297
|
|
237
298
|
## Tests
|
238
299
|
|
239
|
-
The test suite
|
300
|
+
The test suite depends on a locally running instance of a CalDAV server to work properly. We prefer [Radicale](https://radicale.org/v3.html) for this. You can run it locally with Docker and the provided configuration in `spec/data/radicale` with the following command:
|
301
|
+
|
302
|
+
docker run -v $PWD/spec/data/radicale:/var/radicale \
|
303
|
+
--name radicale \
|
304
|
+
--publish 8000:8000 \
|
305
|
+
--detach \
|
306
|
+
xlrl/radicale
|
307
|
+
|
308
|
+
With the CalDAV server running, you can now run `rake spec` to run the tests.
|
240
309
|
|
241
310
|
## Contributing
|
242
311
|
|
data/lib/calendav/calendar.rb
CHANGED
data/lib/calendav/client.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
7
|
-
require_relative "
|
3
|
+
require_relative "contextual_url"
|
4
|
+
require_relative "endpoint"
|
5
|
+
require_relative "clients/calendars_client"
|
6
|
+
require_relative "clients/events_client"
|
7
|
+
require_relative "clients/todos_client"
|
8
|
+
require_relative "requests/current_user_principal"
|
8
9
|
|
9
10
|
module Calendav
|
10
11
|
class Client
|
@@ -21,6 +22,10 @@ module Calendav
|
|
21
22
|
@events = Clients::EventsClient.new(self, endpoint, credentials)
|
22
23
|
end
|
23
24
|
|
25
|
+
def todos
|
26
|
+
@todos = Clients::TodosClient.new(self, endpoint, credentials)
|
27
|
+
end
|
28
|
+
|
24
29
|
def principal_url
|
25
30
|
@principal_url ||= begin
|
26
31
|
request = Requests::CurrentUserPrincipal.call
|
@@ -39,8 +39,10 @@ module Calendav
|
|
39
39
|
)
|
40
40
|
end
|
41
41
|
|
42
|
-
def list(calendar_url, from: nil, to: nil)
|
43
|
-
request = Requests::ListEvents.call(
|
42
|
+
def list(calendar_url, from: nil, to: nil, expand_recurring_events: false)
|
43
|
+
request = Requests::ListEvents.call(
|
44
|
+
from: from, to: to, expand_recurring_events: expand_recurring_events
|
45
|
+
)
|
44
46
|
|
45
47
|
endpoint
|
46
48
|
.report(request.to_xml, url: calendar_url, depth: 1)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../errors"
|
4
|
+
require_relative "../todo"
|
5
|
+
require_relative "../requests/list_todos"
|
6
|
+
|
7
|
+
module Calendav
|
8
|
+
module Clients
|
9
|
+
class TodosClient
|
10
|
+
def initialize(client, endpoint, credentials)
|
11
|
+
@client = client
|
12
|
+
@endpoint = endpoint
|
13
|
+
@credentials = credentials
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(calendar_url, todo_identifier, ics)
|
17
|
+
todo_url = merged_url(calendar_url, todo_identifier)
|
18
|
+
result = endpoint.put(ics, url: todo_url, content_type: :ics)
|
19
|
+
|
20
|
+
Todo.new(
|
21
|
+
url: result.headers["Location"] || todo_url,
|
22
|
+
etag: result.headers["ETag"]
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(todo_url, etag: nil)
|
27
|
+
endpoint.delete(url: todo_url, etag: etag)
|
28
|
+
rescue PreconditionError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def find(todo_url)
|
33
|
+
response = endpoint.get(url: todo_url)
|
34
|
+
|
35
|
+
Todo.new(
|
36
|
+
url: todo_url,
|
37
|
+
calendar_data: response.body.to_s,
|
38
|
+
etag: response.headers["ETag"]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def list(calendar_url)
|
43
|
+
request = Requests::ListTodos.call
|
44
|
+
|
45
|
+
endpoint
|
46
|
+
.report(request.to_xml, url: calendar_url, depth: 1)
|
47
|
+
.reject { |node| node.xpath(".//caldav:calendar-data").text.empty? }
|
48
|
+
.collect { |node| Todo.from_xml(calendar_url, node) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(todo_url, ics, etag: nil)
|
52
|
+
result = endpoint.put(
|
53
|
+
ics, url: todo_url, content_type: :ics, etag: etag
|
54
|
+
)
|
55
|
+
|
56
|
+
Todo.new(
|
57
|
+
url: todo_url,
|
58
|
+
etag: result.headers["ETag"]
|
59
|
+
)
|
60
|
+
rescue PreconditionError
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_reader :client, :endpoint, :credentials
|
67
|
+
|
68
|
+
def merged_url(calendar_url, todo_identifier)
|
69
|
+
"#{calendar_url.delete_suffix('/')}/#{todo_identifier}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/calendav/endpoint.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
3
|
+
require_relative "contextual_url"
|
4
|
+
require_relative "error_handler"
|
5
|
+
require_relative "parsers/response_xml"
|
6
6
|
|
7
7
|
module Calendav
|
8
8
|
class Endpoint
|
@@ -16,7 +16,7 @@ module Calendav
|
|
16
16
|
@timeout = timeout
|
17
17
|
end
|
18
18
|
|
19
|
-
def delete(url:, etag: nil)
|
19
|
+
def delete(url:, etag: nil) # rubocop:disable Naming/PredicateMethod
|
20
20
|
request(:delete, url: url, http: with_headers(etag: etag))
|
21
21
|
.status
|
22
22
|
.success?
|
@@ -79,12 +79,14 @@ module Calendav
|
|
79
79
|
|
80
80
|
attr_reader :credentials, :timeout
|
81
81
|
|
82
|
-
def authenticated
|
82
|
+
def authenticated # rubocop:disable Metrics/MethodLength
|
83
83
|
case credentials.authentication
|
84
84
|
when :basic_auth
|
85
85
|
HTTP.basic_auth(user: credentials.username, pass: credentials.password)
|
86
86
|
when :bearer_token
|
87
87
|
HTTP.auth("Bearer #{credentials.password}")
|
88
|
+
when :oauth
|
89
|
+
HTTP.auth("OAuth #{credentials.password}")
|
88
90
|
else
|
89
91
|
raise "Unexpected authentication approach: " \
|
90
92
|
"#{credentials.authentication}"
|
data/lib/calendav/errors.rb
CHANGED
@@ -7,7 +7,7 @@ module Calendav
|
|
7
7
|
attr_reader :xml, :original
|
8
8
|
|
9
9
|
def initialize(xml, original)
|
10
|
-
super
|
10
|
+
super(original.message)
|
11
11
|
|
12
12
|
@xml = xml
|
13
13
|
@original = original
|
@@ -18,7 +18,7 @@ module Calendav
|
|
18
18
|
attr_reader :response
|
19
19
|
|
20
20
|
def initialize(response)
|
21
|
-
super
|
21
|
+
super(response.status.to_s)
|
22
22
|
|
23
23
|
@response = response
|
24
24
|
end
|
data/lib/calendav/event.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calendav
|
4
|
+
module Parsers
|
5
|
+
class TodoXML
|
6
|
+
def self.call(...)
|
7
|
+
new(...).call
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(element)
|
11
|
+
@element = element
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
{
|
16
|
+
calendar_data: value(".//caldav:calendar-data"),
|
17
|
+
etag: value(".//dav:getetag")
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :element
|
24
|
+
|
25
|
+
def value(xpath)
|
26
|
+
node = element.xpath(xpath)
|
27
|
+
return nil if node.children.empty?
|
28
|
+
|
29
|
+
if node.children.any?(&:element?)
|
30
|
+
node.children.select(&:element?).collect(&:to_xml).join
|
31
|
+
else
|
32
|
+
node.children.text
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -11,9 +11,10 @@ module Calendav
|
|
11
11
|
new(...).call
|
12
12
|
end
|
13
13
|
|
14
|
-
def initialize(from:, to:)
|
14
|
+
def initialize(from:, to:, expand_recurring_events:)
|
15
15
|
@from = from
|
16
16
|
@to = to
|
17
|
+
@expand_recurring_events = expand_recurring_events
|
17
18
|
end
|
18
19
|
|
19
20
|
def call
|
@@ -21,7 +22,13 @@ module Calendav
|
|
21
22
|
xml["caldav"].public_send("calendar-query", NAMESPACES) do
|
22
23
|
xml["dav"].prop do
|
23
24
|
xml["dav"].getetag
|
24
|
-
|
25
|
+
if expand_recurring_events? && range?
|
26
|
+
xml["caldav"].public_send(:"calendar-data") do
|
27
|
+
xml["caldav"].expand(start: from, end: to)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
xml["caldav"].public_send(:"calendar-data")
|
31
|
+
end
|
25
32
|
end
|
26
33
|
xml["caldav"].filter do
|
27
34
|
xml["caldav"].public_send(:"comp-filter", name: "VCALENDAR") do
|
@@ -40,6 +47,10 @@ module Calendav
|
|
40
47
|
|
41
48
|
private
|
42
49
|
|
50
|
+
def expand_recurring_events?
|
51
|
+
@expand_recurring_events
|
52
|
+
end
|
53
|
+
|
43
54
|
def from
|
44
55
|
return nil if @from.nil?
|
45
56
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
|
5
|
+
require_relative "../namespaces"
|
6
|
+
|
7
|
+
module Calendav
|
8
|
+
module Requests
|
9
|
+
class ListTodos
|
10
|
+
def self.call(...)
|
11
|
+
new.call
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
Nokogiri::XML::Builder.new do |xml|
|
16
|
+
xml["caldav"].public_send("calendar-query", NAMESPACES) do
|
17
|
+
xml["dav"].prop do
|
18
|
+
xml["dav"].getetag
|
19
|
+
xml["caldav"].public_send(:"calendar-data")
|
20
|
+
end
|
21
|
+
xml["caldav"].filter do
|
22
|
+
xml["caldav"].public_send(:"comp-filter", name: "VCALENDAR") do
|
23
|
+
xml["caldav"].public_send(:"comp-filter", name: "VTODO")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def from
|
33
|
+
return nil if @from.nil?
|
34
|
+
|
35
|
+
@from.utc.iso8601.delete(":-")
|
36
|
+
end
|
37
|
+
|
38
|
+
def to
|
39
|
+
return nil if @to.nil?
|
40
|
+
|
41
|
+
@to.utc.iso8601.delete(":-")
|
42
|
+
end
|
43
|
+
|
44
|
+
def range?
|
45
|
+
to || from
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "contextual_url"
|
4
|
+
require_relative "parsers/todo_xml"
|
5
|
+
|
6
|
+
module Calendav
|
7
|
+
class Todo
|
8
|
+
ATTRIBUTES = %i[url calendar_data etag].freeze
|
9
|
+
|
10
|
+
def self.from_xml(host, node)
|
11
|
+
new(
|
12
|
+
{
|
13
|
+
url: ContextualURL.call(host, node.xpath("./dav:href").text)
|
14
|
+
}.merge(
|
15
|
+
Parsers::TodoXML.call(node)
|
16
|
+
)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(attributes = {})
|
21
|
+
@attributes = attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
ATTRIBUTES.each do |attribute|
|
25
|
+
define_method(attribute) { attributes[attribute] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
attributes.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
def summary
|
33
|
+
inner_todo.summary
|
34
|
+
end
|
35
|
+
|
36
|
+
def due
|
37
|
+
inner_todo.due
|
38
|
+
end
|
39
|
+
|
40
|
+
def status
|
41
|
+
inner_todo.status
|
42
|
+
end
|
43
|
+
|
44
|
+
def unloaded?
|
45
|
+
calendar_data.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :attributes
|
51
|
+
|
52
|
+
def inner_calendar
|
53
|
+
Icalendar::Calendar.parse(calendar_data).first
|
54
|
+
end
|
55
|
+
|
56
|
+
def inner_todo
|
57
|
+
@inner_todo = inner_calendar.todos.first
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/calendav.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
3
|
+
require_relative "calendav/credentials/apple"
|
4
|
+
require_relative "calendav/credentials/fastmail"
|
5
|
+
require_relative "calendav/credentials/google"
|
6
|
+
require_relative "calendav/client"
|
7
7
|
|
8
8
|
module Calendav
|
9
9
|
PROVIDERS = {
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: calendav
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: http
|
@@ -52,161 +51,6 @@ dependencies:
|
|
52
51
|
- - ">="
|
53
52
|
- !ruby/object:Gem::Version
|
54
53
|
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: dotenv
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: google-api-client
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: googleauth
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rake
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rspec
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rubocop
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - ">="
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - ">="
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: timecop
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: tzinfo
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">="
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0'
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - ">="
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: vcr
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '0'
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: vcr_assistant
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - ">="
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '0'
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - ">="
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '0'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: webmock
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - ">="
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: '0'
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - ">="
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: '0'
|
209
|
-
description:
|
210
54
|
email:
|
211
55
|
- pat@freelancing-gods.com
|
212
56
|
executables: []
|
@@ -221,6 +65,7 @@ files:
|
|
221
65
|
- lib/calendav/client.rb
|
222
66
|
- lib/calendav/clients/calendars_client.rb
|
223
67
|
- lib/calendav/clients/events_client.rb
|
68
|
+
- lib/calendav/clients/todos_client.rb
|
224
69
|
- lib/calendav/contextual_url.rb
|
225
70
|
- lib/calendav/credentials/apple.rb
|
226
71
|
- lib/calendav/credentials/fastmail.rb
|
@@ -236,14 +81,17 @@ files:
|
|
236
81
|
- lib/calendav/parsers/event_xml.rb
|
237
82
|
- lib/calendav/parsers/response_xml.rb
|
238
83
|
- lib/calendav/parsers/sync_xml.rb
|
84
|
+
- lib/calendav/parsers/todo_xml.rb
|
239
85
|
- lib/calendav/requests/calendar_home_set.rb
|
240
86
|
- lib/calendav/requests/current_user_principal.rb
|
241
87
|
- lib/calendav/requests/list_calendars.rb
|
242
88
|
- lib/calendav/requests/list_events.rb
|
89
|
+
- lib/calendav/requests/list_todos.rb
|
243
90
|
- lib/calendav/requests/make_calendar.rb
|
244
91
|
- lib/calendav/requests/sync_collection.rb
|
245
92
|
- lib/calendav/requests/update_calendar.rb
|
246
93
|
- lib/calendav/sync_collection.rb
|
94
|
+
- lib/calendav/todo.rb
|
247
95
|
homepage: https://github.com/pat/calendav
|
248
96
|
licenses:
|
249
97
|
- Hippocratic-2.1
|
@@ -252,7 +100,6 @@ metadata:
|
|
252
100
|
source_code_uri: https://github.com/pat/calendav
|
253
101
|
changelog_uri: https://github.com/pat/calendav/blob/main/CHANGELOG.md
|
254
102
|
rubygems_mfa_required: 'true'
|
255
|
-
post_install_message:
|
256
103
|
rdoc_options: []
|
257
104
|
require_paths:
|
258
105
|
- lib
|
@@ -267,8 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
114
|
- !ruby/object:Gem::Version
|
268
115
|
version: '0'
|
269
116
|
requirements: []
|
270
|
-
rubygems_version: 3.
|
271
|
-
signing_key:
|
117
|
+
rubygems_version: 3.6.9
|
272
118
|
specification_version: 4
|
273
119
|
summary: CalDAV client
|
274
120
|
test_files: []
|