Skip to content

Commit

Permalink
Merge pull request #232 from icalendar/custom-components
Browse files Browse the repository at this point in the history
Add custom component support
  • Loading branch information
rahearn committed May 31, 2020
2 parents a53a4dd + 4c4468e commit 07c9fed
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 12 deletions.
11 changes: 5 additions & 6 deletions icalendar.gemspec
Expand Up @@ -28,20 +28,19 @@ ActiveSupport is required for TimeWithZone support, but not required for general

s.add_dependency 'ice_cube', '~> 0.16'

s.add_development_dependency 'rake', '~> 12.0'
s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'bundler', '~> 2.0'

# test with all groups of tzinfo dependencies
# tzinfo 2.x
# s.add_development_dependency 'tzinfo', '~> 2.0'
# s.add_development_dependency 'tzinfo-data', '~> 1.2018'
# s.add_development_dependency 'tzinfo-data', '~> 1.2020'
# tzinfo 1.x
s.add_development_dependency 'activesupport', '~> 5.2'
s.add_development_dependency 'i18n', '~> 1.1'
s.add_development_dependency 'activesupport', '~> 6.0'
s.add_development_dependency 'i18n', '~> 1.8'
s.add_development_dependency 'tzinfo', '~> 1.2'
s.add_development_dependency 'tzinfo-data', '~> 1.2018'
s.add_development_dependency 'tzinfo-data', '~> 1.2020'
# tzinfo 0.x
# s.add_development_dependency 'i18n', '~> 0.7'
# s.add_development_dependency 'tzinfo', '~> 0.3'
# end tzinfo

Expand Down
20 changes: 16 additions & 4 deletions lib/icalendar/has_components.rb
Expand Up @@ -21,21 +21,33 @@ def add_component(c)
c
end

def add_custom_component(component_name, c)
c.parent = self
yield c if block_given?
(custom_components[component_name.downcase.gsub("-", "_")] ||= []) << c
c
end

def custom_component(component_name)
custom_components[component_name.downcase.gsub("-", "_")] || []
end

def method_missing(method, *args, &block)
method_name = method.to_s
if method_name =~ /^add_(x_\w+)$/
component_name = $1
custom = args.first || Component.new(component_name, component_name.upcase)
(custom_components[component_name] ||= []) << custom
yield custom if block_given?
custom
add_custom_component(component_name, custom, &block)
elsif method_name =~ /^x_/ && custom_component(method_name).size > 0
custom_component method_name
else
super
end
end

def respond_to_missing?(method_name, include_private = false)
method_name.to_s.start_with?('add_x_') || super
string_method = method_name.to_s
string_method.start_with?('add_x_') || custom_component(string_method).size > 0 || super
end

module ClassMethods
Expand Down
4 changes: 2 additions & 2 deletions lib/icalendar/parser.rb
Expand Up @@ -106,14 +106,14 @@ def parse_component(component)
timezone_store.store(component) if klass_name == 'Timezone'
break
elsif fields[:name] == 'begin'
klass_name = fields[:value].gsub(/\AV/, '').downcase.capitalize
klass_name = fields[:value].gsub(/\AV/, '').gsub("-", "_").downcase.capitalize
Icalendar.logger.debug "Adding component #{klass_name}"
if Icalendar.const_defined? klass_name
component.add_component parse_component(Icalendar.const_get(klass_name).new)
elsif Icalendar::Timezone.const_defined? klass_name
component.add_component parse_component(Icalendar::Timezone.const_get(klass_name).new)
else
component.add_component parse_component(Component.new klass_name.downcase, fields[:value])
component.add_custom_component klass_name, parse_component(Component.new klass_name.downcase, fields[:value])
end
else
parse_property component, fields
Expand Down
158 changes: 158 additions & 0 deletions spec/fixtures/custom_component.ics
@@ -0,0 +1,158 @@
BEGIN:VCALENDAR
PRODID:-//Atlassian Confluence//Calendar Plugin 1.0//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:Grimsell testkalender
X-WR-CALDESC:
X-WR-TIMEZONE:Europe/Stockholm
X-MIGRATED-FOR-USER-KEY:true
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Europe/Stockholm
TZURL:http://tzurl.org/zoneinfo/Europe/Stockholm
SEQUENCE:9206
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:STANDARD
TZOFFSETFROM:+011212
TZOFFSETTO:+010014
TZNAME:SET
DTSTART:18790101T000000
RDATE:18790101T000000
END:STANDARD
BEGIN:STANDARD
TZOFFSETFROM:+010014
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19000101T000000
RDATE:19000101T000000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19160514T230000
RDATE:19160514T230000
RDATE:19800406T020000
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19161001T010000
RDATE:19161001T010000
RDATE:19800928T030000
RDATE:19810927T030000
RDATE:19820926T030000
RDATE:19830925T030000
RDATE:19840930T030000
RDATE:19850929T030000
RDATE:19860928T030000
RDATE:19870927T030000
RDATE:19880925T030000
RDATE:19890924T030000
RDATE:19900930T030000
RDATE:19910929T030000
RDATE:19920927T030000
RDATE:19930926T030000
RDATE:19940925T030000
RDATE:19950924T030000
END:STANDARD
BEGIN:STANDARD
TZOFFSETFROM:+0100
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19800101T000000
RDATE:19800101T000000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20151203T104347Z
SUMMARY:Kaffepaus
UID:20151203T101856Z-1543254179@intranet.idainfront.se
DESCRIPTION:
ORGANIZER;X-CONFLUENCE-USER-KEY=ff80818141e4d3b60141e4d4b75700a2;CN=Magnu
s Grimsell;CUTYPE=INDIVIDUAL:mailto:magnus.grimsell@idainfront.se
CREATED:20151203T101856Z
LAST-MODIFIED:20151203T101856Z
SEQUENCE:1
X-CONFLUENCE-SUBCALENDAR-TYPE:other
TRANSP:OPAQUE
STATUS:CONFIRMED
DTSTART:20151203T090000Z
DTEND:20151203T091500Z
END:VEVENT
BEGIN:X-EVENT-SERIES
SUMMARY:iipax - Sprints
DESCRIPTION:
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
URL:jira://8112ef9a-343e-30e6-b469-4f993bf0371d?projectKey=II&dateFieldNa
me=sprint
END:X-EVENT-SERIES
BEGIN:VEVENT
DTSTAMP:20151203T104348Z
DTSTART;TZID=Europe/Stockholm:20130115T170800
DTEND;TZID=Europe/Stockholm:20130204T170800
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-3@jira.idainfront.se
X-GREENHOPPER-SPRINT-CLOSED:false
X-GREENHOPPER-SPRINT-EDITABLE:true
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
oToBoard.jspa?sprintId=3
SEQUENCE:0
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
TRANSP:OPAQUE
STATUS:CONFIRMED
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=3

SUMMARY:iipax - Callisto-6
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20151203T104348Z
DTSTART;TZID=Europe/Stockholm:20130219T080000
DTEND;TZID=Europe/Stockholm:20130319T095900
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-5@jira.idainfront.se
X-GREENHOPPER-SPRINT-CLOSED:false
X-GREENHOPPER-SPRINT-EDITABLE:true
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
oToBoard.jspa?sprintId=5
SEQUENCE:0
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
TRANSP:OPAQUE
STATUS:CONFIRMED
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=5

SUMMARY:iipax - Bulbasaur
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20151203T104348Z
DTSTART;TZID=Europe/Stockholm:20130326T105900
DTEND;TZID=Europe/Stockholm:20130419T105900
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-7@jira.idainfront.se
X-GREENHOPPER-SPRINT-CLOSED:true
X-GREENHOPPER-SPRINT-EDITABLE:true
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
oToBoard.jspa?sprintId=7
SEQUENCE:0
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
TRANSP:OPAQUE
STATUS:CONFIRMED
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=7

SUMMARY:iipax - Ivysaur
END:VEVENT
END:VCALENDAR
10 changes: 10 additions & 0 deletions spec/parser_spec.rb
Expand Up @@ -66,6 +66,16 @@
expect(event.dtstart.utc).to eq Time.parse("20180104T150000Z")
end
end
context 'custom_component.ics' do
let(:fn) { 'custom_component.ics' }

it 'correctly handles custom named components' do
parsed = subject.parse
calendar = parsed.first
expect(calendar.custom_component('x_event_series').size).to eq 1
expect(calendar.custom_component('X-EVENT-SERIES').size).to eq 1
end
end
end

describe '#parse with bad line' do
Expand Down
9 changes: 9 additions & 0 deletions spec/roundtrip_spec.rb
Expand Up @@ -135,6 +135,15 @@
ical = subject.parse.first.to_ical
expect(ical).to include 'CUSTOMFIELD:Not properly noted as custom with X- prefix.'
end

context 'custom components' do
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'custom_component.ics') }

it 'can output the custom component' do
ical = subject.parse.first.to_ical
expect(ical).to include 'BEGIN:X-EVENT-SERIES'
end
end
end
end
end

0 comments on commit 07c9fed

Please sign in to comment.