Cal Invite
[v0.3.2] - 2026-10-22
No changes to the library code; this release is documentation and CI.
-
API docs layout fixed. The README’s wide banner image ran under the “On This Page” sidebar on the published docs. RDoc now loads
assets/rdoc-overrides.css, which keeps images inside the content column. -
Docs deploy on their own. The documentation workflow runs on every push to
mainthat changes a file the docs render, and can be started by hand from the Actions tab on any branch. Release tags still publish the docs as before. -
Faster CI for doc-only changes. Changes that touch only Markdown files or
assets/skip the test matrix. Tag pushes always run the full pipeline. -
Docs checked on pull requests. A PR that changes a file the docs render now runs the RDoc build, so a broken build fails before merge. Nothing is deployed from a PR.
-
Workflow lint and wider test matrix.
actionlintruns on the workflows, and the test matrix adds Ruby 3.4. -
CI tidy-ups. Superseded pull request runs are cancelled. Documentation deploys queue instead of cancelling each other. Workflow permissions are narrowed, and the test job has a 20-minute timeout.
[v0.3.1] - 2026-10-22
No changes to the library code; this release is packaging and documentation.
-
Smaller gem. The package no longer includes
assets/(README images) or the unusedlib/tasks/rdoc.rake: 52 KB instead of 973 KB. -
API reference fixed. The published docs at dashapi-ai.github.io/cal-invite/ were missing
lib/cal_invite.rband every provider class (Google,Outlook,Office365,Yahoo,Ics,Ical,IcsContent,IcsDownload,BaseProvider), because the docs workflow’slib/**/*.rbglob only matched one directory level. The workflow now runsrake rdocand also publishes the README, CONFIGURATION.md, CACHING.md and CHANGELOG.md. -
New CACHING.md on caching
CalInviteoutput in your app (plain Ruby, Rails 8.1, Sinatra, Hanami, Grape), including HTTP caching for.icsfeeds. The example app gains a cached.icsdownload (MeetingsController#show). -
README rewritten for 0.3.0, with accurate examples that run in plain Ruby.
-
Gem metadata: the RubyGems changelog link pointed at a non-existent
masterbranch; it now points atmain, and a documentation link was added.
[v0.3.0] - 2026-10-22
Upgrading from 0.2.x
These changes can break existing callers:
-
Unknown
Eventattributes raise.Event.newandEvent#update_attributesraiseArgumentError(Unknown attribute(s): locaton) for keys that aren’tEventattributes, instead of silently ignoring them. If you pass a whole params hash, slice it first. A rejectedupdate_attributesleaves the event unchanged. (#10) -
timezoneis 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', raisesArgumentError. Such values previously produced invalid.ics(aTZID=with noVTIMEZONE, times shifted by the offset) and could inject parameters. (#7) -
Time order is validated.
ArgumentErrorwhenend_timeisn’t afterstart_time(all-day events may end on the same date), or amulti_day_sessionsentry is missing a time or ends before it starts. (#9) -
Provider names are validated.
generate_calendar_urlraisesArgumentErrorlisting the supported providers for anything outsideCalInvite::Providers::SUPPORTED_PROVIDERS(:ics_contentstill works as an alias of:ics). Previously e.g.:stringresolved Ruby’sStringand failed confusingly. (#11) -
config.timezoneis now honored as the defaultEvent#timezonewhentimezone:isn’t passed, as documented. If you set it to something other than'UTC', your events now render in that zone. An explicittimezone:still wins. (#22) -
No more blanket
active_support/core_ext. Requiringcal_inviteno longer monkey-patches core classes (Integer#hours,Object#blank?,String#squish, …) in non-Rails apps. Plain-Ruby code that used those only becausecal_inviteloaded them mustrequire "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)
Removed
-
Output caching.
generate_calendar_urlbuilds its output fresh on every call and never touches a cache store;update_attributesno longer wipes every event’s cache entries. The cache ignoredcache_prefix, almost never hit, froze.icsDTSTAMP, and cost more than regenerating. Output is unchanged. To cache anyway, do it in your app; CONFIGURATION.md has an example. (#14) -
Cache helpers.
CalInvite.fetch_from_cache,write_to_cache,read_from_cache,clear_cache!,clear_event_cache!,clear_provider_cache!andCalInvite::Cachingare gone. They had never worked (every call raisedNoMethodError).CACHING.mdis gone too. (#32)
Deprecated (removed in 0.4.0)
-
config.cache_store=,cache_prefix=,cache_expires_in=have no effect. They printwarning: [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) -
::BaseProvider. The provider base class is nowCalInvite::Providers::BaseProvider, so the gem no longer owns a top-level constant that can collide with your app’s.::BaseProviderremains as an alias (not defined at all if your app has its ownBaseProvider). Ruby printsconstant ::BaseProvider is deprecatedwhen deprecation warnings are enabled (e.g.RUBYOPT=-W:deprecated). (#13)
Security
-
Fix content-line/parameter injection in
.icsoutput (: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 differentMETHOD:). URL providers were not affected. (#5) -
TEXT values are escaped per RFC 5545 (backslashes were previously left unescaped and lone
\rpassed through). -
CN=display names are quoted and escaped per RFC 6868. -
URL:is no longer TEXT-escaped (it’s a URI; commas and semicolons in links were being corrupted). -
Values that can’t be escaped raise
ArgumentErrorif they contain a line break.visibilitymust be:public/:private/:confidential,geonumeric, andsequencean integer. -
SECURITY.md: real policy (supported versions, private reporting via GitHub Security Advisories).
Added
-
organizer:accepts a plain email string (organizer: "jane@example.com"), likeattendees; a string previously crashed.icsgeneration withTypeError. String-keyed hashes are read too. (#12) -
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/:office365show the overall span;:ics/:ical/:yahoostill emit one entry per session. Explicit times win. (#24)
Fixed
-
.icscontent 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) -
'UTC'events emitZ-suffixedDTSTART/DTENDinstead ofTZID=UTCwith noVTIMEZONE. (#7) -
:icalandIcsContentnow produce exactly the same content as:ics.IcsContentpreviously emitted UTC-only times with noVTIMEZONE, ignorednotes, and didn’t support all-day events.:icsall-day events without times default to today, as:icaldid. (#4) -
Non-UTC timezones under tzinfo 1.x (Rails/ActiveSupport 6.0):
:ics/:icalraisedNoMethodError(to_local) and silently omittedVTIMEZONE. (#17) -
Event.newno longer mutates the attributes hash passed in (it used to delete several keys from it, so reusing the hash dropped them). (#8)
Docs
-
README “Testing” section covers
rake test,bin/console, and the Example app’s local-path Gemfile workflow.CLAUDE.mdmerged intoAGENTS.md.
[v0.2.1] - 2026-08-10
-
:googleprovider: honorEvent#attendees/show_attendees(add=),Event#timezone(ctz=),Event#rrule(recur=), andEvent#busy(crm=BUSY/AVAILABLE) in the generated Google Calendar URL — these were already modeled onEventfor the ics providers but never wired into:google. -
:outlook/:office365providers: honorEvent#busyviafreebusy=. -
:outlook/:office365providers: fix stale deep-link URLs. Switched from the legacyoutlook.live.com/calendar/0/action/composeandoutlook.office.com/owa/hosts to the currently documentedoutlook.live.com/calendar/deeplink/composeandoutlook.office.com/calendar/deeplink/compose, and added the requiredrru=addeventparam. -
CI: fix Rails 6 + Ruby 3.3/3.4 test failures (missing
logger/mutex_m/bigdecimalrequires), fix RDoc doc generation (stale.rdoc_optionscrashing the darkfish generator and silently excluding all oflib/), and fix a GitHub Pages deploy deadlock (duplicateconcurrency: group: "pages"betweenmain.ymland the reusabledocumentation.yml).
[v0.2.0] - 2026-08-09
-
Add
Event#organizerand amethod:option (:publish/:request/:cancel) togenerate_calendar_url, so ics/ical output can carryMETHOD:REQUEST/METHOD:CANCEL+ORGANIZER/SEQUENCE/STATUS, matching what mail clients (Gmail, Outlook, Apple Mail) need to render an attached.icsas an RSVP-capable meeting invite (or a real cancellation) rather than a plain file -
ATTENDEElines now includeCUTYPE/ROLE/PARTSTAT -
IcsDownload.headers/.wrap_for_downloadacceptmethod:to add the matchingmethod=REQUESTContent-Type parameter -
Fix a
superclass mismatchcrash when both the:icsprovider andIcsDownload/IcsContentwere loaded (dead duplicateIcs/Icalclass definitions inics_content.rb) -
Fix: stable
UID.Event#uidis now a real attribute (auto-generated and memoized perEventinstance, or pass your own) instead of the ics/ical/ics_content providers each minting a fresh random UID on everygeneratecall — required for mail/calendar clients to recognize a:requestupdate or:cancelas referring to a previously sent invite rather than a new, unrelated event. See newEvent#sequence(defaults to0) and CONFIGURATION.md’s “Updating and cancelling invites”. -
Fix: real
VTIMEZONE.:ics/:icalnow emit a completeVTIMEZONEcomponent (STANDARD/DAYLIGHTobservances with correct offsets andRRULEs, derived from TZInfo’s transition data) for any recognized IANA timezone, instead of an empty/absent block.DTSTART;TZID=.../DTEND;TZID=...are also now correctly converted from UTC to that zone’s local wall-clock time — previously the UTC clock digits were emitted verbatim under a non-UTCTZID, mislabeling the time.'UTC'continues to use plainZ-suffixed timestamps with noVTIMEZONE, as before. -
Add
tzinfoas an explicit gem dependency (previously relied on it only transitively viaactivesupport). -
Event#attendeesentries can now be{ email:, name:, partstat: }hashes (plain email strings still work) —ATTENDEElines gainCN=and a settablePARTSTAT(:accepted/:declined/:tentative/:needs_action/:delegated) instead of alwaysNEEDS-ACTION. -
Add
method: :reply, for building an attendee’sMETHOD:REPLYback to an organizer (omitsRSVP=TRUE, honors each attendee’spartstat:). -
Add
Event#geo(GEO:property),Event#reminders(VALARMblocks),Event#busy(TRANSP:),Event#visibility(CLASS:),Event#rrule(rawRRULE:recurrence value), andEvent#calendar_name(X-WR-CALNAMEon theVCALENDAR) — all optional, all emitted by:ics/:ical/IcsContent. -
Add
method: :counter/:decline_counter, generatingMETHOD:COUNTER/METHOD:DECLINECOUNTERfor an attendee proposing a new time and an organizer rejecting it, respectively. (Note::decline_countercorrectly renders asMETHOD:DECLINECOUNTER— one word per RFC 5545 — not a literal upcase of the Ruby symbol; fixed via a newBaseProvider#method_valueused everywhere aMETHOD:/method=value is emitted, includingIcsDownload.headers.) -
Add
Event#importance(:low/:normal/:high→ standardPRIORITY:+ Outlook’sX-MICROSOFT-CDO-IMPORTANCE:) andEvent#allow_counter(defaulttrue;false→ Outlook’sX-MICROSOFT-DISALLOW-COUNTER:TRUE, hiding its “Propose New Time” action) andX-MICROSOFT-CDO-BUSYSTATUSalongside the existingTRANSP:— genuinely real, protocol-level, Outlook-specific properties (confirmed against Microsoft’s documented.icsextensions), safe to always emit since RFC 5545 requires unrecognizedX-properties be ignored by other clients. -
CONFIGURATION.md: rewrite “Guest permissions” — Google/Microsoft’s guest-permission toggles are confirmed API-only (no
.icsproperty, no parameter on Google’s own “add to calendar” render URL either), but the section now documents the real Outlook-specific levers above as the closest addable equivalent. Rewrite “Attendee-proposed reschedules (COUNTER)” now that generation is implemented, with the caveat that mainstream mail clients still don’t reliably expose a “propose new time” UI to trigger one from. Add “Tracking RSVPs” (inboundMETHOD:REPLYwebhook vs. provider-API alternative, with the Gmail reliability caveat) for the RSVP-tracking gap, which remains genuinely outbound-only. -
Example/calendar_app: add a workingMeeting/MeetingAttendeepersistence example (migration + models +MeetingsController#create/#reschedule/#cancel+MeetingMailer) demonstrating theuid/sequencelifecycle end-to-end, plus an illustrativeEventRepliesControllershowing how to parse an inboundMETHOD:REPLYemail. The example app’s Gemfile still tracks the published gem — this demo needs 0.2.0, so run it locally againstpath: "../.."until that’s released. -
Add per-attendee
rsvp:override ({ email:, name:, partstat:, rsvp: }) soRSVP=TRUEcan be suppressed even undermethod: :request— needed for a “registration confirmed” style invite where the recipient is alreadypartstat: :acceptedand nothing is actually being requested. Fixed a real bug found while adding it: the existing symbol/string dual-key lookup pattern (attendee[:key] || attendee["key"]) silently discards an explicitfalsevalue (false || xevaluates toxin Ruby, notfalse), which would have madersvp: falsea no-op; replaced withBaseProvider#attendee_hash_value, used for all attendee-hash reads. -
CONFIGURATION.md: add “Replicating a ticketing-platform confirmation email” — a full worked example (matching a real Luma-style confirmation email byte-for-byte in structure, brand names genericized) covering the
partstat: :accepted, rsvp: falseattendee pattern, the full ActionMailer attachment setup, whyFrom/Reply-To/ORGANIZERcan legitimately all differ, and an explicit note that Apple Wallet.pkpasspasses are an unrelated technology (PassKit, not iCalendar) and out of scope for this gem.
[Released]
[v0.1.7] - 2025-11-09
-
Upgrading to support Rails 8.1
-
Support gems upgrades
[v0.1.6] - 2024-12-20
-
Adding Mailer example
[v0.1.5] - 2024-12-19
-
Should deploy the doc to a GitHub page
-
Fixing a version number in the CHANGELOG file
[v0.1.4] - 2024-12-19
-
Add support to RDoc
[v0.1.3] - 2024-12-19
-
Apple iCal and ics file can now be either generated or wrap to be downloaded
-
Add Caching management support
-
Add CACHING.md for more details about how to use caching in a Rails app
-
Updating the README file to reflect latest usage changes
-
Minor bug fixes and improvments.
[v0.1.2] - 2024-12-17
Warning: This version should not be used
-
Add support to Microsoft office 365 calendar invite URL
-
Update the README
-
Add an example
-
Better testing
[v0.1.1] - 2024-12-17
Warning: This version should not be used
Fixing a bug in the gemspec file
[Unreleased]
[v0.1.0] - 2024-12-17
First public release of the Calendar Invite gem
Support the calendar invites for the following calendars:
-
Microsoft Outlook
-
Google Calendar
-
Yahoo Calendar
Support Apple iCal
Support other calendars via .ics