cal-invite 0.2.0 → 0.3.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/.rdoc_options +1 -2
- data/AGENTS.md +13 -8
- data/CHANGELOG.md +56 -0
- data/CONFIGURATION.md +28 -32
- data/README.md +32 -20
- data/SECURITY.md +39 -10
- data/assets/social/cal-invite-github.png +0 -0
- data/lib/cal_invite/configuration.rb +20 -11
- data/lib/cal_invite/event.rb +87 -114
- data/lib/cal_invite/ical_timezone.rb +64 -18
- data/lib/cal_invite/providers/base_provider.rb +302 -264
- data/lib/cal_invite/providers/google.rb +4 -0
- data/lib/cal_invite/providers/ical.rb +7 -152
- data/lib/cal_invite/providers/ics.rb +68 -13
- data/lib/cal_invite/providers/ics_content.rb +7 -129
- data/lib/cal_invite/providers/office365.rb +6 -2
- data/lib/cal_invite/providers/outlook.rb +7 -3
- data/lib/cal_invite/providers.rb +8 -0
- data/lib/cal_invite/version.rb +1 -1
- data/lib/cal_invite.rb +1 -8
- metadata +3 -8
- data/.DS_Store +0 -0
- data/CACHING.md +0 -148
- data/CLAUDE.md +0 -49
- data/lib/.DS_Store +0 -0
- data/lib/cal_invite/.DS_Store +0 -0
- data/lib/cal_invite/caching.rb +0 -74
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a320d848302c940f549394fa7d2d76c2160415efa3eb4ef007e3613c9ef9196
|
|
4
|
+
data.tar.gz: 25bd69ff530c5b2479c0ac5f23862618089384c53994a10eaec3c8e3b59b8ce3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd3569a38b9f741d27dfe5964b98223e425539e94bca9e3a6c90b5f1baa1a2d390af7935d39c3fb04ab1272bf9d501ab3759b867d8926bc287f2019ece5021bf
|
|
7
|
+
data.tar.gz: fc43475e14f1eb253319de3d671288a261fe296b006e4807b388cfbc96707738e36f15ed0a4e1e0698afed32d5a67cd0adf3fcb36bb19fee6c8b001ec72b536c
|
data/.rdoc_options
CHANGED
data/AGENTS.md
CHANGED
|
@@ -21,26 +21,31 @@ Tests are Minitest-based, files live under `test/cal_invite/`, named `*_test.rb`
|
|
|
21
21
|
|
|
22
22
|
## Architecture
|
|
23
23
|
|
|
24
|
-
- **`CalInvite` module** (`lib/cal_invite.rb`) — top-level entry point. Holds a singleton `CalInvite.configuration` (a `Configuration` instance) set via `CalInvite.configure { |c| ... }`.
|
|
24
|
+
- **`CalInvite` module** (`lib/cal_invite.rb`) — top-level entry point. Holds a singleton `CalInvite.configuration` (a `Configuration` instance) set via `CalInvite.configure { |c| ... }`.
|
|
25
25
|
|
|
26
|
-
- **`CalInvite::Event`** (`lib/cal_invite/event.rb`) — the central object. Holds all event attributes (title, start/end time, location, url, attendees, timezone, notes, multi-day sessions, all-day flag). `generate_calendar_url(provider)` is the main API: it validates the event,
|
|
26
|
+
- **`CalInvite::Event`** (`lib/cal_invite/event.rb`) — the central object. Holds all event attributes (title, start/end time, location, url, attendees, timezone, notes, multi-day sessions, all-day flag). `generate_calendar_url(provider)` is the main API: it validates the event, resolves `CalInvite::Providers::<CapitalizedProvider>`, instantiates it with `self`, and calls `#generate`. The gem deliberately caches **nothing** (output caching and the cache helpers were removed in 0.3.0; see CONFIGURATION.md's `generate_calendar_url` section for why) — don't reintroduce it. Location and virtual-meeting URL are intentionally separate fields (not merged) so each provider can format them appropriately.
|
|
27
27
|
|
|
28
28
|
- **`CalInvite::Providers`** (`lib/cal_invite/providers.rb`) — namespace with `SUPPORTED_PROVIDERS = %i[google ical outlook yahoo ics office365]`. Provider classes are autoloaded from `lib/cal_invite/providers/*.rb`. To add a new provider: create a class extending `BaseProvider`, implement `#generate`, register it here with `autoload`, and add its symbol to `SUPPORTED_PROVIDERS`.
|
|
29
29
|
|
|
30
|
-
- **`BaseProvider`** (`lib/cal_invite/providers/base_provider.rb`) — abstract base class
|
|
30
|
+
- **`CalInvite::Providers::BaseProvider`** (`lib/cal_invite/providers/base_provider.rb`) — abstract base class. A deprecated top-level `::BaseProvider` alias (defined in `providers.rb` via `Object.deprecate_constant`, skipped if the host app already has a `BaseProvider`) remains until 0.4.0 — don't reference it from gem code. Defines the `#generate` contract (must raise `NotImplementedError` if not overridden) plus shared helpers used by subclasses: URL-building (`url_encode`, `format_description`, `format_location`, `format_url`, `format_description_with_url`, `add_optional_params`) and iCalendar property formatting (`attendees_list`, `attendee_emails`, `attendee_hash_value`, `attendee_line`, `organizer_line`, `method_value`, `status_line`, `geo_line`, `transp_line`, `busystatus_line`, `class_line`, `importance_lines`, `disallow_counter_line`, `rrule_line`, `valarm_lines`, `local_wall_time`, `vtimezone_lines`). `attendee_hash_value(attendee, key)` is the correct way to read an attendee-hash field (tries symbol then string key) — don't reintroduce `attendee[:key] || attendee["key"]`, which silently drops explicit `false` values. Each concrete provider (`Google`, `Outlook`, `Office365`, `Yahoo`, `Ical`, `Ics`) builds a provider-specific URL or content string from the `Event` using these helpers.
|
|
31
31
|
|
|
32
|
-
- **`Ics` / `IcsContent` providers** (`ics.rb`, `ics_content.rb`) — generate actual `.ics` file content (RFC 5545) rather than a URL, for direct download flows (see README's `send_data` controller example). `IcsDownload` is
|
|
32
|
+
- **`Ics` / `IcsContent` providers** (`ics.rb`, `ics_content.rb`) — generate actual `.ics` file content (RFC 5545) rather than a URL, for direct download flows (see README's `send_data` controller example). `Ics` is the **only** `.ics` generator: `Ical` and `IcsContent` are empty subclasses kept for their public names, so make content changes in `ics.rb` only. `IcsDownload` (HTTP header/filename helpers) lives in `ics_content.rb` alongside `IcsContent` (not an alias). All ics-family providers accept a `method:` (`:publish`/`:request`/`:cancel`/`:reply`/`:counter`/`:decline_counter`) passed through `Event#generate_calendar_url`. Use `BaseProvider#method_value` (not `method.to_s.upcase`) when emitting the `METHOD:` line — `:decline_counter` must render as `METHOD:DECLINECOUNTER` (one word) per RFC 5545, unlike its Ruby symbol. `:request` plus `Event#organizer` produces an RFC 5545 meeting request that mail clients render with RSVP actions; `:cancel` (reusing the same `Event#uid`, with `Event#sequence` bumped) produces a real cancellation; `:reply`/`:counter`/`:decline_counter` all omit `RSVP=TRUE` on `ATTENDEE` lines (none of them request a further response). `Event#uid` is stable (auto-generated and memoized per instance, or pass your own) — required so a later `:request`/`:cancel` is recognized as updating the original invite rather than creating a new one. `Event#attendees` entries can be plain email strings or `{ email:, name:, partstat:, rsvp: }` hashes (`partstat:` drives `PARTSTAT=` on the `ATTENDEE` line, default `NEEDS-ACTION`; `rsvp:` overrides the method-derived `RSVP=TRUE`/absent default — needed for e.g. a registration-confirmation invite where the attendee is already `:accepted` and `rsvp: false` suppresses it under `method: :request`). Optional `Event` fields `geo`, `reminders`, `busy`, `visibility`, `rrule`, `calendar_name` map to `GEO:`, `VALARM`, `TRANSP:`, `CLASS:`, `RRULE:`, and `X-WR-CALNAME` respectively; `importance`/`allow_counter` map to Outlook's non-standard `X-MICROSOFT-CDO-IMPORTANCE`/`X-MICROSOFT-DISALLOW-COUNTER` (plus standard `PRIORITY:` and `X-MICROSOFT-CDO-BUSYSTATUS` alongside `TRANSP:`) — safe to always emit since unrecognized `X-` properties are required to be ignored by other clients. See `CONFIGURATION.md`.
|
|
33
|
+
- **Timezones** — `:ics`/`:ical` build a real `VTIMEZONE` (via `CalInvite::IcalTimezone`, backed by `tzinfo`) for any recognized IANA `timezone` other than `'UTC'`, and correctly convert `start_time`/`end_time` (always UTC) to that zone's local wall-clock time for `DTSTART;TZID=...`. `'UTC'` uses plain `Z`-suffixed timestamps with no `VTIMEZONE`. `Event#validate!` rejects any other `timezone` (`IcalTimezone.valid?`), so providers can rely on it being UTC or a real IANA zone.
|
|
33
34
|
|
|
34
|
-
- **`
|
|
35
|
-
|
|
36
|
-
- **`Configuration`** (`lib/cal_invite/configuration.rb`) — holds `cache_store`, `cache_prefix` (default `'cal_invite'`), `cache_expires_in` (default 24 hours), `webhook_secret`, `timezone` (default `'UTC'`). `cache_store=` accepts `:memory_store`, `:null_store`, or any object implementing `read`/`write`/`delete`.
|
|
35
|
+
- **`Configuration`** (`lib/cal_invite/configuration.rb`) — holds `webhook_secret` and `timezone` (default `'UTC'`; the default `Event#timezone` when `timezone:` isn't passed). `cache_store`/`cache_prefix`/`cache_expires_in` are deprecated no-ops kept until 0.4.0: their setters warn (`uplevel: 2`, so the warning points at the caller's initializer) but still store the value so app code reading them keeps working.
|
|
37
36
|
|
|
38
37
|
## Conventions
|
|
39
38
|
|
|
40
39
|
- Always pass times to `Event.new` in UTC; use the `timezone` attribute only to control display/formatting in the generated invite.
|
|
40
|
+
- **Treat every `Event` attribute as untrusted input** (host apps pass end-user strings: titles, display names, ...). In `.ics` output: TEXT values go through `Ics#escape_text`; parameter values (e.g. `CN=`) through `BaseProvider#quoted_param` (RFC 6868); enumerated/numeric values are whitelisted or coerced (`class_line`, `geo_line`, `sequence_line`); and `Ics#ensure_single_line_values!` raises on any CR/LF left in a content line — never bypass it or interpolate a raw attribute into a property/parameter. URL providers must pass every value through `url_encode`. Add a case to `test/cal_invite/ics_security_test.rb` for any new property.
|
|
41
41
|
- `location` (physical) and `url` (virtual meeting link) are kept as distinct fields throughout the provider layer — don't conflate them when adding provider logic.
|
|
42
|
-
- All-day events skip `start_time`/`end_time` validation; multi-day events use the separate `multi_day_sessions` array instead of `start_time`/`end_time`.
|
|
42
|
+
- All-day events skip `start_time`/`end_time` validation; multi-day events use the separate `multi_day_sessions` array instead of `start_time`/`end_time`. `Event#start_time`/`#end_time` are readers that fall back to the earliest session start / latest session end when not set explicitly (computed on read, so they follow `update_attributes`), which is what single-range URL providers use.
|
|
43
43
|
- YARD-style doc comments (`@param`, `@return`, `@example`) are used throughout `lib/` and feed the RDoc-generated docs published to GitHub Pages (`.github/workflows/documentation.yml`) — keep them accurate when changing public method signatures.
|
|
44
|
+
- CalInvite is outbound-only: it renders `.ics` content/URLs and has no concept of inbound RSVP replies. It also can't express Google/Microsoft Calendar-API-only concepts like guest permissions (`guestsCanInviteOthers` etc.) since those have no `.ics`/URL representation at all — but it does generate the genuinely protocol-level equivalents that exist: `METHOD:COUNTER`/`DECLINECOUNTER` and Outlook's `X-MICROSOFT-*` extensions (`allow_counter`, `importance`, `busy`). See CONFIGURATION.md's "Tracking RSVPs" and "Guest permissions" sections for the full breakdown of what's real vs. not addable, and why.
|
|
45
|
+
|
|
46
|
+
## Example app (`Example/calendar_app`)
|
|
47
|
+
|
|
48
|
+
Rails 8.1 app demonstrating the gem. Its Gemfile depends on the **published** `cal-invite` gem (never commit it pointing at `path: "../.."`) — when testing example-app changes that rely on unreleased gem code, temporarily uncomment the `path: "../.."` line locally, `bundle install`, test, then revert the Gemfile and re-run `bundle install` against rubygems before committing. `Meeting`/`MeetingAttendee` (with a migration) is a working reference implementation of the `uid`/`sequence` persistence pattern CONFIGURATION.md describes — see `app/models/meeting.rb`, `app/controllers/meetings_controller.rb` (create/reschedule/cancel), and `app/controllers/event_replies_controller.rb` (illustrative inbound `METHOD:REPLY` parsing, not wired to a real inbound-email provider).
|
|
44
49
|
|
|
45
50
|
## PRs & commits
|
|
46
51
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Cal Invite
|
|
2
2
|
|
|
3
|
+
## [v0.3.0] - 2026-10-22
|
|
4
|
+
|
|
5
|
+
### Upgrading from 0.2.x
|
|
6
|
+
|
|
7
|
+
These changes can break existing callers:
|
|
8
|
+
|
|
9
|
+
- **Unknown `Event` attributes raise.** `Event.new` and `Event#update_attributes` raise `ArgumentError` (`Unknown attribute(s): locaton`) for keys that aren't `Event` attributes, instead of silently ignoring them. If you pass a whole params hash, slice it first. A rejected `update_attributes` leaves the event unchanged. (#10)
|
|
10
|
+
- **`timezone` is validated.** It must be `'UTC'` or an IANA identifier known to tzinfo (e.g. `'America/New_York'`); anything else, including raw offsets like `'+01:00'`, raises `ArgumentError`. Such values previously produced invalid `.ics` (a `TZID=` with no `VTIMEZONE`, times shifted by the offset) and could inject parameters. (#7)
|
|
11
|
+
- **Time order is validated.** `ArgumentError` when `end_time` isn't after `start_time` (all-day events may end on the same date), or a `multi_day_sessions` entry is missing a time or ends before it starts. (#9)
|
|
12
|
+
- **Provider names are validated.** `generate_calendar_url` raises `ArgumentError` listing the supported providers for anything outside `CalInvite::Providers::SUPPORTED_PROVIDERS` (`:ics_content` still works as an alias of `:ics`). Previously e.g. `:string` resolved Ruby's `String` and failed confusingly. (#11)
|
|
13
|
+
- **`config.timezone` is now honored** as the default `Event#timezone` when `timezone:` isn't passed, as documented. If you set it to something other than `'UTC'`, your events now render in that zone. An explicit `timezone:` still wins. (#22)
|
|
14
|
+
- **No more blanket `active_support/core_ext`.** Requiring `cal_invite` no longer monkey-patches core classes (`Integer#hours`, `Object#blank?`, `String#squish`, ...) in non-Rails apps. Plain-Ruby code that used those only because `cal_invite` loaded them must `require "active_support/core_ext"` (or the specific file). Rails apps are unaffected. This also lets the gem load with ActiveSupport 6.0 on Ruby 4.0. (#15)
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
- **Output caching.** `generate_calendar_url` builds its output fresh on every call and never touches a cache store; `update_attributes` no longer wipes every event's cache entries. The cache ignored `cache_prefix`, almost never hit, froze `.ics` `DTSTAMP`, and cost more than regenerating. Output is unchanged. To cache anyway, do it in your app; CONFIGURATION.md has an example. (#14)
|
|
19
|
+
- **Cache helpers.** `CalInvite.fetch_from_cache`, `write_to_cache`, `read_from_cache`, `clear_cache!`, `clear_event_cache!`, `clear_provider_cache!` and `CalInvite::Caching` are gone. They had never worked (every call raised `NoMethodError`). `CACHING.md` is gone too. (#32)
|
|
20
|
+
|
|
21
|
+
### Deprecated (removed in 0.4.0)
|
|
22
|
+
|
|
23
|
+
- **`config.cache_store=`, `cache_prefix=`, `cache_expires_in=`** have no effect. They print `warning: [cal_invite] Configuration#cache_store= is deprecated and has no effect ...` pointing at your initializer line, but still store the value, so code that reads them keeps working. Delete them from your initializer. (#32)
|
|
24
|
+
- **`::BaseProvider`.** The provider base class is now `CalInvite::Providers::BaseProvider`, so the gem no longer owns a top-level constant that can collide with your app's. `::BaseProvider` remains as an alias (not defined at all if your app has its own `BaseProvider`). Ruby prints `constant ::BaseProvider is deprecated` when deprecation warnings are enabled (e.g. `RUBYOPT=-W:deprecated`). (#13)
|
|
25
|
+
|
|
26
|
+
### Security
|
|
27
|
+
|
|
28
|
+
- **Fix content-line/parameter injection in `.ics` output** (`:ics`, `:ical`, `IcsContent`). A CR or LF in event attributes (title, description, attendee/organizer names and emails, `uid`, `rrule`, `visibility`, `geo`, ...), or a `"` in a display name, could add arbitrary properties to a generated invite (e.g. extra attendees, a different `METHOD:`). URL providers were not affected. (#5)
|
|
29
|
+
- TEXT values are escaped per RFC 5545 (backslashes were previously left unescaped and lone `\r` passed through).
|
|
30
|
+
- `CN=` display names are quoted and escaped per RFC 6868.
|
|
31
|
+
- `URL:` is no longer TEXT-escaped (it's a URI; commas and semicolons in links were being corrupted).
|
|
32
|
+
- Values that can't be escaped raise `ArgumentError` if they contain a line break. `visibility` must be `:public`/`:private`/`:confidential`, `geo` numeric, and `sequence` an integer.
|
|
33
|
+
- `SECURITY.md`: real policy (supported versions, private reporting via GitHub Security Advisories).
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `organizer:` accepts a plain email string (`organizer: "jane@example.com"`), like `attendees`; a string previously crashed `.ics` generation with `TypeError`. String-keyed hashes are read too. (#12)
|
|
38
|
+
- Multi-day events no longer need top-level `start_time`/`end_time`: they default to the earliest session start and latest session end, so the README's multi-day example works. `:google`/`:outlook`/`:office365` show the overall span; `:ics`/`:ical`/`:yahoo` still emit one entry per session. Explicit times win. (#24)
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `.ics` content lines longer than 75 octets are folded (RFC 5545 §3.1), never splitting a multi-byte UTF-8 character. Long lines could be mangled or rejected by Outlook and mail gateways. (#6)
|
|
43
|
+
- `'UTC'` events emit `Z`-suffixed `DTSTART`/`DTEND` instead of `TZID=UTC` with no `VTIMEZONE`. (#7)
|
|
44
|
+
- `:ical` and `IcsContent` now produce exactly the same content as `:ics`. `IcsContent` previously emitted UTC-only times with no `VTIMEZONE`, ignored `notes`, and didn't support all-day events. `:ics` all-day events without times default to today, as `:ical` did. (#4)
|
|
45
|
+
- Non-UTC timezones under tzinfo 1.x (Rails/ActiveSupport 6.0): `:ics`/`:ical` raised `NoMethodError` (`to_local`) and silently omitted `VTIMEZONE`. (#17)
|
|
46
|
+
- `Event.new` no longer mutates the attributes hash passed in (it used to delete several keys from it, so reusing the hash dropped them). (#8)
|
|
47
|
+
|
|
48
|
+
### Docs
|
|
49
|
+
|
|
50
|
+
- README "Testing" section covers `rake test`, `bin/console`, and the Example app's local-path Gemfile workflow. `CLAUDE.md` merged into `AGENTS.md`.
|
|
51
|
+
|
|
52
|
+
## [v0.2.1] - 2026-08-10
|
|
53
|
+
|
|
54
|
+
- `:google` provider: honor `Event#attendees`/`show_attendees` (`add=`), `Event#timezone` (`ctz=`), `Event#rrule` (`recur=`), and `Event#busy` (`crm=BUSY`/`AVAILABLE`) in the generated Google Calendar URL — these were already modeled on `Event` for the ics providers but never wired into `:google`.
|
|
55
|
+
- `:outlook`/`:office365` providers: honor `Event#busy` via `freebusy=`.
|
|
56
|
+
- `:outlook`/`:office365` providers: fix stale deep-link URLs. Switched from the legacy `outlook.live.com/calendar/0/action/compose` and `outlook.office.com/owa/` hosts to the currently documented `outlook.live.com/calendar/deeplink/compose` and `outlook.office.com/calendar/deeplink/compose`, and added the required `rru=addevent` param.
|
|
57
|
+
- CI: fix Rails 6 + Ruby 3.3/3.4 test failures (missing `logger`/`mutex_m`/`bigdecimal` requires), fix RDoc doc generation (stale `.rdoc_options` crashing the darkfish generator and silently excluding all of `lib/`), and fix a GitHub Pages deploy deadlock (duplicate `concurrency: group: "pages"` between `main.yml` and the reusable `documentation.yml`).
|
|
58
|
+
|
|
3
59
|
## [v0.2.0] - 2026-08-09
|
|
4
60
|
|
|
5
61
|
- Add `Event#organizer` and a `method:` option (`:publish`/`:request`/`:cancel`) to `generate_calendar_url`, so ics/ical output can carry `METHOD:REQUEST`/`METHOD:CANCEL` + `ORGANIZER`/`SEQUENCE`/`STATUS`, matching what mail clients (Gmail, Outlook, Apple Mail) need to render an attached `.ics` as an RSVP-capable meeting invite (or a real cancellation) rather than a plain file
|
data/CONFIGURATION.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Complete reference for every configurable option in CalInvite: `Event` attributes, providers, the `generate_calendar_url` call, ICS/email-invite delivery, and global `CalInvite.configure` settings.
|
|
4
4
|
|
|
5
|
-
For caching-specific detail, see [CACHING.md](CACHING.md).
|
|
6
|
-
|
|
7
5
|
## Table of Contents
|
|
8
6
|
|
|
9
7
|
- [Event attributes](#event-attributes)
|
|
@@ -22,23 +20,23 @@ For caching-specific detail, see [CACHING.md](CACHING.md).
|
|
|
22
20
|
|
|
23
21
|
## Event attributes
|
|
24
22
|
|
|
25
|
-
All attributes are set via `CalInvite::Event.new(attributes)` or `event.update_attributes(attributes)
|
|
23
|
+
All attributes are set via `CalInvite::Event.new(attributes)` or `event.update_attributes(attributes)` (symbol or string keys). Any key not listed below raises `ArgumentError` (`Unknown attribute(s): ...`); `update_attributes` checks all keys before changing anything.
|
|
26
24
|
|
|
27
25
|
| Attribute | Type | Required | Default | Notes |
|
|
28
26
|
|------------------------|-----------------|----------------------------------------|---------|-------|
|
|
29
27
|
| `title` | `String` | yes | — | Non-blank. |
|
|
30
28
|
| `start_time` | `Time` | yes, unless `all_day` or using `multi_day_sessions` | — | Always pass UTC. |
|
|
31
|
-
| `end_time` | `Time` | yes, unless `all_day` or using `multi_day_sessions` | — | Always pass UTC. |
|
|
29
|
+
| `end_time` | `Time` | yes, unless `all_day` or using `multi_day_sessions` | — | Always pass UTC. Must be after `start_time` (`ArgumentError` otherwise); all-day events may end on the same date they start. |
|
|
32
30
|
| `description` | `String` | no | `nil` | Plain text; combined with `notes` in provider output. |
|
|
33
31
|
| `location` | `String` | no | `nil` | Physical location only. Kept separate from `url` so each provider formats it correctly. |
|
|
34
32
|
| `url` | `String` | no | `nil` | Virtual meeting link (Zoom, Meet, Teams, etc). Kept separate from `location`. |
|
|
35
33
|
| `attendees` | `Array<String, Hash>` | no | `nil` | Email strings, or `{ email:, name:, partstat:, rsvp: }` hashes for a display name (`CN=`), a specific RSVP status, and/or an explicit `RSVP=` override. Only emitted if `show_attendees` is `true`. `partstat` is one of `:accepted`, `:declined`, `:tentative`, `:needs_action` (default), `:delegated`. `rsvp:` defaults to `true` for `:request`/`:cancel`/`:publish` and `false` for `:reply`/`:counter`/`:decline_counter`; set it explicitly to override (e.g. `rsvp: false` on an already-`:accepted` attendee in a registration-confirmation invite — see [Replicating a ticketing-platform confirmation email](#replicating-a-ticketing-platform-confirmation-email)). |
|
|
36
34
|
| `show_attendees` | `Boolean` | no | `false` | Gate for including `attendees` in generated output. |
|
|
37
|
-
| `organizer` | `Hash`
|
|
38
|
-
| `timezone` | `String` | no | `'UTC'` | Controls display/formatting only — does not affect how `start_time`/`end_time` are interpreted.
|
|
35
|
+
| `organizer` | `String`/`Hash` | no, but required for `method: :request` | `nil` | `"jane@example.com"` or `{ name: "Jane Doe", email: "jane@example.com" }`. `name` is optional. See [Email meeting invites](#email-meeting-invites-rsvp-capable). |
|
|
36
|
+
| `timezone` | `String` | no | `CalInvite.configuration.timezone` (`'UTC'`) | Controls display/formatting only — does not affect how `start_time`/`end_time` are interpreted. Must be `'UTC'` or an IANA identifier (e.g. `'America/New_York'`), which produces a correctly converted `DTSTART;TZID=...` plus a full `VTIMEZONE` block; anything else raises `ArgumentError`. See [Timezones and VTIMEZONE](#timezones-and-vtimezone). |
|
|
39
37
|
| `notes` | `String` | no | `nil` | Appended to the description. |
|
|
40
38
|
| `all_day` | `Boolean` | no | `false` | When `true`, `start_time`/`end_time` validation is skipped. |
|
|
41
|
-
| `multi_day_sessions` | `Array<Hash>` | no | `[]` | `[{ start_time:, end_time: }, ...]`. Used instead of `start_time`/`end_time` for multi-session events. |
|
|
39
|
+
| `multi_day_sessions` | `Array<Hash>` | no | `[]` | `[{ start_time:, end_time: }, ...]`. Used instead of `start_time`/`end_time` for multi-session events. Each session needs both times, with `end_time` after `start_time`. `:ics`/`:ical` emit one `VEVENT` per session and `:yahoo` one URL per session (newline-separated); `:google`/`:outlook`/`:office365` can only express one range, so they use `start_time`/`end_time`, which default to the earliest session start and latest session end (explicit values win). |
|
|
42
40
|
| `uid` | `String` | no | randomly generated, memoized per `Event` instance | Stable RFC 5545 identifier. **Must** be reused across calls when you send an update (`:request`) or cancellation (`:cancel`) for a previously sent invite — see [Updating and cancelling invites](#updating-and-cancelling-invites). |
|
|
43
41
|
| `sequence` | `Integer` | no | `0` | RFC 5545 SEQUENCE. Increment it yourself each time you resend a `:request`/`:cancel` for the same `uid`. |
|
|
44
42
|
| `geo` | `Array<Float>`, `Hash` | no | `nil` | `[37.4595, -122.1418]` or `{ lat:, lng: }`. Emits `GEO:lat;lng` — lets Apple/Google Maps deep-link from the invite. |
|
|
@@ -79,16 +77,16 @@ Pass one of these symbols to `generate_calendar_url`:
|
|
|
79
77
|
|
|
80
78
|
| Symbol | Output | Notes |
|
|
81
79
|
|-------------|----------------------------------|-------|
|
|
82
|
-
| `:google` | Google Calendar URL | |
|
|
83
|
-
| `:outlook` | Outlook (outlook.live.com) URL | |
|
|
84
|
-
| `:office365`| Outlook 365 URL | |
|
|
80
|
+
| `:google` | Google Calendar URL | Honors `attendees`/`show_attendees` (`add=`), `timezone` (`ctz=`), `rrule` (`recur=`), `busy` (`crm=BUSY`/`AVAILABLE`). |
|
|
81
|
+
| `:outlook` | Outlook (outlook.live.com) URL | Honors `attendees`/`show_attendees` (`to=`), `busy` (`freebusy=`). |
|
|
82
|
+
| `:office365`| Outlook 365 URL | Honors `attendees`/`show_attendees` (`to=`), `busy` (`freebusy=`). |
|
|
85
83
|
| `:yahoo` | Yahoo Calendar URL | |
|
|
86
84
|
| `:ical` | `.ics` content (`METHOD:PUBLISH` or `:REQUEST`) | Apple iCal / any iCalendar-compatible app |
|
|
87
85
|
| `:ics` | `.ics` content (`METHOD:PUBLISH` or `:REQUEST`) | Generic RFC 5545 file |
|
|
88
86
|
|
|
89
87
|
`CalInvite::Providers::SUPPORTED_PROVIDERS` holds the full list programmatically.
|
|
90
88
|
|
|
91
|
-
`CalInvite::Providers::IcsContent` is also available directly (not via `generate_calendar_url`) if you want raw `.ics` content without
|
|
89
|
+
`CalInvite::Providers::IcsContent` is also available directly (not via `generate_calendar_url`) if you want raw `.ics` content without `Event#generate_calendar_url`'s validation — see [ICS content and downloads](#ics-content-and-downloads).
|
|
92
90
|
|
|
93
91
|
## `generate_calendar_url`
|
|
94
92
|
|
|
@@ -98,20 +96,26 @@ event.generate_calendar_url(provider, method: :publish)
|
|
|
98
96
|
|
|
99
97
|
| Param | Type | Default | Notes |
|
|
100
98
|
|-----------|----------|------------|-------|
|
|
101
|
-
| `provider`| `Symbol` | required | One of the provider symbols above. |
|
|
99
|
+
| `provider`| `Symbol` | required | One of the provider symbols above (a String also works). Anything else raises `ArgumentError` listing the supported providers. `:ics_content` is accepted as a legacy alias of `:ics`. |
|
|
102
100
|
| `method` | `Symbol` | `:publish` | `:publish`, `:request`, `:cancel`, `:reply`, `:counter`, or `:decline_counter`. Only honored by `:ics`/`:ical` — ignored by URL-based providers. `:request` requires `organizer` to be set on the event; see below. `:cancel` requires reusing the original `uid` — see [Updating and cancelling invites](#updating-and-cancelling-invites). `:reply`/`:counter`/`:decline_counter` omit `RSVP=TRUE` on `ATTENDEE` lines — see [Attendee RSVP status and METHOD:REPLY](#attendee-rsvp-status-and-methodreply) and [Attendee-proposed reschedules (COUNTER)](#attendee-proposed-reschedules-counter). Note: `:decline_counter` renders as `METHOD:DECLINECOUNTER` (one word, per RFC 5545). |
|
|
103
101
|
|
|
104
|
-
|
|
102
|
+
Output is generated fresh on every call. CalInvite doesn't cache anything: building the output takes microseconds, and a cached `.ics` would keep a stale `DTSTAMP`. If you want caching anyway, do it in your app, keyed on whatever identifies the content and changes with it:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
Rails.cache.fetch([meeting, meeting.sequence, :ics]) do
|
|
106
|
+
meeting.to_cal_invite_event.generate_calendar_url(:ics, method: :request)
|
|
107
|
+
end
|
|
108
|
+
```
|
|
105
109
|
|
|
106
110
|
## ICS content and downloads
|
|
107
111
|
|
|
108
112
|
Two ways to get raw `.ics` content:
|
|
109
113
|
|
|
110
114
|
```ruby
|
|
111
|
-
# Via Event (goes through validation
|
|
115
|
+
# Via Event (goes through validation)
|
|
112
116
|
content = event.generate_calendar_url(:ics, method: :publish)
|
|
113
117
|
|
|
114
|
-
# Directly via the provider (
|
|
118
|
+
# Directly via the provider (skips Event#validate!)
|
|
115
119
|
content = CalInvite::Providers::Ics.new(event, method: :publish).generate
|
|
116
120
|
```
|
|
117
121
|
|
|
@@ -331,10 +335,8 @@ A working reference implementation of this whole pattern — a `Meeting` model
|
|
|
331
335
|
persisting `uid`/`sequence`, and controller actions that send/reschedule/cancel
|
|
332
336
|
— lives in [`Example/calendar_app`](Example/calendar_app): see
|
|
333
337
|
`app/models/meeting.rb` and `app/controllers/meetings_controller.rb`. It
|
|
334
|
-
depends on the attendee-hash/`uid`/`sequence` features documented here,
|
|
335
|
-
|
|
336
|
-
tracks the published gem, so point it at `path: "../.."` locally if you want
|
|
337
|
-
to run this demo before that release is out.
|
|
338
|
+
depends on the attendee-hash/`uid`/`sequence` features documented here, which
|
|
339
|
+
shipped in 0.2.0, so it runs against the published gem.
|
|
338
340
|
|
|
339
341
|
## Tracking RSVPs: what the gem does and doesn't do
|
|
340
342
|
|
|
@@ -506,20 +508,17 @@ For any `timezone` other than `'UTC'` that's a recognized IANA/Olson identifier
|
|
|
506
508
|
timestamps with no `VTIMEZONE` block, which every calendar client understands
|
|
507
509
|
unambiguously.
|
|
508
510
|
|
|
509
|
-
|
|
510
|
-
`
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
511
|
+
Any other value — including raw offset strings like `'+01:00'` — raises
|
|
512
|
+
`ArgumentError` from `Event.new`/`update_attributes`: there's no timezone
|
|
513
|
+
database entry to convert against or build a `VTIMEZONE` from, and emitting a
|
|
514
|
+
`TZID` with no matching `VTIMEZONE` is invalid RFC 5545. Use the IANA name for
|
|
515
|
+
the place instead (e.g. `'Europe/Paris'`), or `'UTC'`.
|
|
514
516
|
|
|
515
517
|
## Global configuration (`CalInvite.configure`)
|
|
516
518
|
|
|
517
519
|
```ruby
|
|
518
520
|
# config/initializers/cal_invite.rb
|
|
519
521
|
CalInvite.configure do |config|
|
|
520
|
-
config.cache_store = Rails.cache # or :memory_store, :null_store, or a custom read/write/delete object
|
|
521
|
-
config.cache_prefix = 'my_app_cal_invite'
|
|
522
|
-
config.cache_expires_in = 3600 # seconds
|
|
523
522
|
config.webhook_secret = ENV['CAL_INVITE_WEBHOOK_SECRET']
|
|
524
523
|
config.timezone = 'UTC'
|
|
525
524
|
end
|
|
@@ -527,10 +526,7 @@ end
|
|
|
527
526
|
|
|
528
527
|
| Option | Type | Default | Notes |
|
|
529
528
|
|---------------------|-----------------------------------------|----------------|-------|
|
|
530
|
-
| `cache_store` | `:memory_store`, `:null_store`, or any object implementing `read`/`write`/`delete` | `nil` (caching disabled) | See [CACHING.md](CACHING.md) for full detail. |
|
|
531
|
-
| `cache_prefix` | `String` | `'cal_invite'` | Namespaces cache keys. |
|
|
532
|
-
| `cache_expires_in` | `Integer` (seconds) | `86400` (24h) | |
|
|
533
529
|
| `webhook_secret` | `String` | `nil` | Reserved for webhook signature verification in consuming apps. |
|
|
534
|
-
| `timezone` | `String` | `'UTC'` |
|
|
530
|
+
| `timezone` | `String` | `'UTC'` | Default `timezone` for events created without one; an explicit per-event `timezone:` wins. Validated like any event timezone, so an unknown value makes `Event.new` raise `ArgumentError`. |
|
|
535
531
|
|
|
536
|
-
|
|
532
|
+
`cache_store`, `cache_prefix` and `cache_expires_in` are deprecated and have no effect. Setting one prints a warning, and they'll be removed in 0.4.0, so delete them from your initializer.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 📅 CalInvite
|
|
2
2
|
|
|
3
|
-
A Ruby gem for generating calendar invitations across multiple calendar platforms with
|
|
3
|
+
A Ruby gem for generating calendar invitations across multiple calendar platforms with webhook support.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/rb/cal-invite)
|
|
6
6
|

|
|
@@ -217,25 +217,9 @@ end
|
|
|
217
217
|
- Attendees are properly formatted with RSVP options
|
|
218
218
|
- Virtual meeting URLs are properly separated from physical locations
|
|
219
219
|
|
|
220
|
-
## Caching
|
|
220
|
+
## Caching
|
|
221
221
|
|
|
222
|
-
CalInvite
|
|
223
|
-
|
|
224
|
-
```ruby
|
|
225
|
-
# config/initializers/cal_invite.rb
|
|
226
|
-
CalInvite.configure do |config|
|
|
227
|
-
# Use Rails cache by default
|
|
228
|
-
config.cache_store = Rails.cache
|
|
229
|
-
|
|
230
|
-
# Optional: Set cache prefix
|
|
231
|
-
config.cache_prefix = 'my_app_cal_invite'
|
|
232
|
-
|
|
233
|
-
# Optional: Set expiration time (in seconds)
|
|
234
|
-
config.cache_expires_in = 3600 # 1 hour
|
|
235
|
-
end
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
For detailed information about configuring caching in Rails applications and available options, see our [Caching Guide](https://github.com/DashAPI-ai/cal-invite/blob/master/CACHING.md). For every other configuration option (`Event` attributes, providers, email invites), see [CONFIGURATION.md](CONFIGURATION.md).
|
|
222
|
+
CalInvite doesn't cache anything. Generated URLs and `.ics` content are cheap to build and always fresh. The old `cache_store`/`cache_prefix`/`cache_expires_in` settings are deprecated no-ops (removed in 0.4.0). See [CONFIGURATION.md](CONFIGURATION.md#generate_calendar_url) for caching at your own layer, and for every other option.
|
|
239
223
|
|
|
240
224
|
## Development
|
|
241
225
|
|
|
@@ -249,13 +233,41 @@ Add test(s) as necessary.
|
|
|
249
233
|
|
|
250
234
|
Run all the tests before submitting: `bundle exec rake test`
|
|
251
235
|
|
|
236
|
+
There are three ways to exercise the gem while working on a change:
|
|
237
|
+
|
|
238
|
+
**1. Unit tests** (Minitest, under `test/cal_invite/`) — use these to lock a change in:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
bundle exec rake test # full suite (+ SimpleCov report in coverage/)
|
|
242
|
+
bundle exec rake test TEST=test/cal_invite/event_test.rb # a single file
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**2. Interactive console** — `bin/console` opens IRB with your local `lib/` loaded, handy for eyeballing generated URLs and `.ics` output:
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
event = CalInvite::Event.new(
|
|
249
|
+
title: "Demo",
|
|
250
|
+
start_time: Time.utc(2026, 11, 1, 15),
|
|
251
|
+
end_time: Time.utc(2026, 11, 1, 16),
|
|
252
|
+
timezone: "America/New_York",
|
|
253
|
+
organizer: { email: "me@example.com", name: "Me" },
|
|
254
|
+
attendees: ["guest@example.com"],
|
|
255
|
+
show_attendees: true
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
puts event.generate_calendar_url(:ics, method: :request) # raw .ics content
|
|
259
|
+
event.generate_calendar_url(:google) # provider URL
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**3. Example Rails app** (`Example/calendar_app`) — for end-to-end checks in a real browser or mail client (downloading the `.ics`, RSVP buttons, etc.). Its Gemfile points at the published gem; to test unreleased changes, temporarily swap `gem "cal-invite"` for the commented-out `gem "cal-invite", path: "../../"` line and run `bundle install`. Revert the Gemfile and re-run `bundle install` before committing.
|
|
263
|
+
|
|
252
264
|
## Contributing
|
|
253
265
|
|
|
254
266
|
Bug reports and pull requests are welcome on GitHub at https://github.com/DashAPI-ai/cal-invite. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/DashAPI-ai/cal-invite/blob/master/CODE_OF_CONDUCT.md).
|
|
255
267
|
|
|
256
268
|
## Documentation
|
|
257
269
|
|
|
258
|
-
The documentation is spread across the README, [CONFIGURATION.md](CONFIGURATION.md),
|
|
270
|
+
The documentation is spread across the README, [CONFIGURATION.md](CONFIGURATION.md), and the doc folder.
|
|
259
271
|
|
|
260
272
|
The documentation can be generated using `bundle exec rake rdoc`
|
|
261
273
|
|
data/SECURITY.md
CHANGED
|
@@ -2,20 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported Versions
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Security fixes are released for the latest minor version only. Please upgrade to
|
|
6
|
+
the most recent release before reporting.
|
|
7
7
|
|
|
8
8
|
| Version | Supported |
|
|
9
9
|
| ------- | ------------------ |
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
| 4.0.x | :white_check_mark: |
|
|
13
|
-
| < 4.0 | :x: |
|
|
10
|
+
| 0.2.x | :white_check_mark: |
|
|
11
|
+
| < 0.2 | :x: |
|
|
14
12
|
|
|
15
13
|
## Reporting a Vulnerability
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
**Please do not open a public issue for security problems.**
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
Report vulnerabilities privately through GitHub:
|
|
18
|
+
[Report a vulnerability](https://github.com/DashAPI-ai/cal-invite/security/advisories/new)
|
|
19
|
+
(Security tab → "Report a vulnerability").
|
|
20
|
+
|
|
21
|
+
Please include:
|
|
22
|
+
|
|
23
|
+
- the affected version(s) and provider(s) (`:ics`, `:google`, ...)
|
|
24
|
+
- a minimal `CalInvite::Event` that reproduces the issue, and the output you got
|
|
25
|
+
- the impact you expect (e.g. injected `.ics` properties, broken URL encoding)
|
|
26
|
+
|
|
27
|
+
What to expect:
|
|
28
|
+
|
|
29
|
+
- an acknowledgement within 5 business days;
|
|
30
|
+
- an assessment, and a fix timeline if the report is accepted, within 14 days;
|
|
31
|
+
- a patched release plus a published GitHub Security Advisory (with credit, if
|
|
32
|
+
you'd like it) once a fix is available. If the report is declined, we'll
|
|
33
|
+
explain why.
|
|
34
|
+
|
|
35
|
+
## Scope
|
|
36
|
+
|
|
37
|
+
CalInvite turns `CalInvite::Event` attributes into calendar URLs and `.ics`
|
|
38
|
+
content. Attribute values are treated as untrusted:
|
|
39
|
+
|
|
40
|
+
- **URL providers** (`:google`, `:outlook`, `:office365`, `:yahoo`) URL-encode
|
|
41
|
+
every value.
|
|
42
|
+
- **`.ics` providers** (`:ics`, `:ical`, `IcsContent`) escape TEXT values per
|
|
43
|
+
RFC 5545, quote and escape parameter values (e.g. `CN=`) per RFC 6868, and raise
|
|
44
|
+
`ArgumentError` rather than emit a value that would break out of its content
|
|
45
|
+
line.
|
|
46
|
+
|
|
47
|
+
Anything that lets attribute values alter the structure of generated output
|
|
48
|
+
(extra properties, components, parameters, or URL query parameters) is in scope.
|
|
49
|
+
How the host application stores, sends, or serves the generated content (email
|
|
50
|
+
delivery, HTTP headers you set yourself, etc.) is out of scope.
|
|
Binary file
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# lib/cal_invite/configuration.rb
|
|
4
4
|
# Configuration class for the CalInvite gem.
|
|
5
|
-
# Handles all configurable options
|
|
5
|
+
# Handles all configurable options: timezone and webhook secret.
|
|
6
6
|
#
|
|
7
|
-
# @attr_reader [ActiveSupport::Cache::Store, nil] cache_store
|
|
8
|
-
# @attr_reader [String] cache_prefix
|
|
9
|
-
# @attr_reader [Integer] cache_expires_in
|
|
7
|
+
# @attr_reader [ActiveSupport::Cache::Store, nil] cache_store Deprecated, unused; removed in 0.4.0
|
|
8
|
+
# @attr_reader [String] cache_prefix Deprecated, unused; removed in 0.4.0
|
|
9
|
+
# @attr_reader [Integer] cache_expires_in Deprecated, unused; removed in 0.4.0
|
|
10
10
|
# @attr_reader [String, nil] webhook_secret The secret key for webhook verification
|
|
11
11
|
# @attr_reader [String] timezone The default timezone for events
|
|
12
12
|
module CalInvite
|
|
@@ -17,19 +17,18 @@ module CalInvite
|
|
|
17
17
|
def initialize
|
|
18
18
|
@cache_store = nil
|
|
19
19
|
@cache_prefix = 'cal_invite'
|
|
20
|
-
@cache_expires_in = 24
|
|
20
|
+
@cache_expires_in = 24 * 60 * 60
|
|
21
21
|
@webhook_secret = nil
|
|
22
22
|
@timezone = 'UTC'
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
#
|
|
25
|
+
# @deprecated CalInvite caches nothing, so this setting has no effect.
|
|
26
|
+
# It warns, and will be removed in 0.4.0.
|
|
26
27
|
#
|
|
27
28
|
# @param store [Symbol, #read, #write, #delete] The cache store to use
|
|
28
29
|
# @raise [ArgumentError] If an unsupported cache store is provided
|
|
29
|
-
#
|
|
30
|
-
# @example Set memory store
|
|
31
|
-
# config.cache_store = :memory_store
|
|
32
30
|
def cache_store=(store)
|
|
31
|
+
warn_deprecated_cache_setting(:cache_store)
|
|
33
32
|
@cache_store = case store
|
|
34
33
|
when :memory_store
|
|
35
34
|
ActiveSupport::Cache::MemoryStore.new
|
|
@@ -46,17 +45,19 @@ module CalInvite
|
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
#
|
|
48
|
+
# @deprecated Has no effect; warns, and will be removed in 0.4.0.
|
|
50
49
|
#
|
|
51
50
|
# @param prefix [#to_s] The prefix to use for cache keys
|
|
52
51
|
def cache_prefix=(prefix)
|
|
52
|
+
warn_deprecated_cache_setting(:cache_prefix)
|
|
53
53
|
@cache_prefix = prefix.to_s
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
#
|
|
56
|
+
# @deprecated Has no effect; warns, and will be removed in 0.4.0.
|
|
57
57
|
#
|
|
58
58
|
# @param duration [#to_i] The duration in seconds
|
|
59
59
|
def cache_expires_in=(duration)
|
|
60
|
+
warn_deprecated_cache_setting(:cache_expires_in)
|
|
60
61
|
@cache_expires_in = duration.to_i
|
|
61
62
|
end
|
|
62
63
|
|
|
@@ -73,5 +74,13 @@ module CalInvite
|
|
|
73
74
|
def timezone=(tz)
|
|
74
75
|
@timezone = tz.to_s
|
|
75
76
|
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def warn_deprecated_cache_setting(name)
|
|
81
|
+
warn "[cal_invite] Configuration##{name}= is deprecated and has no effect: " \
|
|
82
|
+
"CalInvite no longer caches anything. Remove it; it will be removed in 0.4.0.",
|
|
83
|
+
uplevel: 2
|
|
84
|
+
end
|
|
76
85
|
end
|
|
77
86
|
end
|