selene 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitattributes +1 -0
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +1 -4
- data/README.md +4 -1
- data/Rakefile +24 -1
- data/lib/selene.rb +4 -9
- data/lib/selene/alarm_builder.rb +17 -0
- data/lib/selene/calendar_builder.rb +25 -0
- data/lib/selene/daylight_savings_time_builder.rb +21 -0
- data/lib/selene/event_builder.rb +31 -0
- data/lib/selene/parser.rb +69 -0
- data/lib/selene/standard_time_builder.rb +16 -0
- data/lib/selene/time_zone_builder.rb +24 -0
- data/lib/selene/version.rb +1 -1
- data/selene.gemspec +7 -3
- data/test/fixtures/meetup.ics +131 -0
- data/test/fixtures/meetup.json +197 -0
- data/test/selene/calendar_builder_test.rb +56 -0
- data/test/selene/daylight_savings_time_builder_test.rb +16 -0
- data/test/selene/event_builder_test.rb +88 -0
- data/test/selene/parser_test.rb +40 -0
- data/test/selene/time_zone_builder_test.rb +14 -0
- data/test/test_helper.rb +34 -0
- metadata +85 -12
- data/spec/fixtures/mobile-monday.ics +0 -76
- data/spec/selene.rb +0 -14
- data/spec/spec_helper.rb +0 -6
data/.gitattributes
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.ics text eol=crlf
|
data/.gitignore
CHANGED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@selene --create
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Selene
|
2
2
|
|
3
|
-
Selene is
|
3
|
+
Selene is an iCalendar parser for Ruby. It takes a string in iCalendar format (RFC 5545) and outputs a hash.
|
4
|
+
|
5
|
+

|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -20,6 +22,7 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
```ruby
|
22
24
|
ical = Selene.parse(File.read('calendar.ics'))
|
25
|
+
```
|
23
26
|
|
24
27
|
## Contributing
|
25
28
|
|
data/Rakefile
CHANGED
@@ -1 +1,24 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'debugger'
|
3
|
+
require 'json'
|
4
|
+
require 'pp'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'selene'
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << 'test'
|
10
|
+
t.test_files = FileList['test/**/*_test.rb']
|
11
|
+
end
|
12
|
+
|
13
|
+
task :meetup do
|
14
|
+
require 'selene'
|
15
|
+
require 'json'
|
16
|
+
require 'pp'
|
17
|
+
require 'debugger'
|
18
|
+
|
19
|
+
File.open('test/fixtures/meetup.json', 'wb') do |file|
|
20
|
+
feed = Selene.parse(File.read('test/fixtures/meetup.ics'))
|
21
|
+
pp feed
|
22
|
+
file.write(JSON.pretty_generate(feed))
|
23
|
+
end
|
24
|
+
end
|
data/lib/selene.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require 'selene/parser'
|
2
|
+
require 'selene/version'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
FEED = {
|
6
|
-
:calendars => []
|
7
|
-
}
|
4
|
+
module Selene
|
8
5
|
|
9
6
|
def self.parse(string)
|
10
|
-
|
11
|
-
|
12
|
-
end
|
7
|
+
Parser.parse(string)
|
13
8
|
end
|
14
9
|
|
15
10
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Selene
|
2
|
+
class CalendarBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@component = Hash.new { |component, property| component[property] = [] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(name, params, value)
|
9
|
+
@component[name.downcase] = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def append(builder)
|
13
|
+
case builder
|
14
|
+
when EventBuilder
|
15
|
+
@component['events'] << builder.component
|
16
|
+
when TimeZoneBuilder
|
17
|
+
@component['time_zones'] << builder.component
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def component
|
22
|
+
@component
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Selene
|
2
|
+
class DaylightSavingsTimeBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@component = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def component
|
9
|
+
@component
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(name, params, value)
|
13
|
+
@component[name.downcase] = case name
|
14
|
+
when 'RRULE'
|
15
|
+
Hash[value.split(';').map { |vs| k, v = vs.split('=', 2); [k.downcase, v] }]
|
16
|
+
else
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Selene
|
2
|
+
class EventBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@component = Hash.new { |component, property| component[property] = [] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def component
|
9
|
+
@component
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(name, params, value)
|
13
|
+
component[name.downcase] = case name
|
14
|
+
when 'DTSTAMP', 'DTSTART', 'DTEND'
|
15
|
+
Parser.parse_date_and_time(name, params, value)
|
16
|
+
when 'GEO'
|
17
|
+
value.split(';')
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def append(builder)
|
24
|
+
case builder
|
25
|
+
when AlarmBuilder
|
26
|
+
@component['alarms'] << builder.component
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'selene/alarm_builder'
|
2
|
+
require 'selene/calendar_builder'
|
3
|
+
require 'selene/daylight_savings_time_builder'
|
4
|
+
require 'selene/event_builder'
|
5
|
+
require 'selene/standard_time_builder'
|
6
|
+
require 'selene/time_zone_builder'
|
7
|
+
|
8
|
+
module Selene
|
9
|
+
module Parser
|
10
|
+
|
11
|
+
def self.builder(component)
|
12
|
+
case component
|
13
|
+
when 'VCALENDAR' then CalendarBuilder
|
14
|
+
when 'VTIMEZONE' then TimeZoneBuilder
|
15
|
+
when 'DAYLIGHT' then DaylightSavingsTimeBuilder
|
16
|
+
when 'STANDARD' then StandardTimeBuilder
|
17
|
+
when 'VEVENT' then EventBuilder
|
18
|
+
when 'VALARM' then AlarmBuilder
|
19
|
+
else raise "Unknown component #{component}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse(string)
|
24
|
+
{ 'calendars' => [] }.tap do |feed|
|
25
|
+
stack = []
|
26
|
+
split_content_lines(string).each_with_index do |content_line, i|
|
27
|
+
line = parse_content_line(content_line)
|
28
|
+
if line[:name].upcase == 'BEGIN'
|
29
|
+
stack << builder(line[:value]).new
|
30
|
+
elsif line[:name].upcase == 'END'
|
31
|
+
builder = stack.pop
|
32
|
+
if !stack.empty?
|
33
|
+
stack[-1].append(builder)
|
34
|
+
else
|
35
|
+
feed['calendars'] << builder.component
|
36
|
+
end
|
37
|
+
else
|
38
|
+
stack[-1].parse(line[:name], line[:params], line[:value])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.parse_content_line(string)
|
45
|
+
string =~ /([^:\;]+)(?:\;([^:]*))?:(.*)/i
|
46
|
+
{ :name => $1, :params => parse_params($2), :value => $3 }
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.parse_date_and_time(name, params, value)
|
50
|
+
params && params != {} ? [value, params] : value
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.parse_params(raw_params)
|
54
|
+
return unless raw_params
|
55
|
+
{}.tap do |params|
|
56
|
+
raw_params.scan(/[^\;]+/i).map do |param|
|
57
|
+
param =~ /([^=]+)=(.*)/
|
58
|
+
params[$1.downcase] = $2
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.split_content_lines(string)
|
64
|
+
line_break = string.scan(/\r\n?|\n/).first || "\r\n"
|
65
|
+
string.gsub(/#{line_break}\s/, '').split(line_break)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Selene
|
2
|
+
class TimeZoneBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@component = Hash.new { |component, property| component[property] = [] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def component
|
9
|
+
@component
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(name, params, value)
|
13
|
+
@component[name.downcase] = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def append(builder)
|
17
|
+
case builder
|
18
|
+
when DaylightSavingsTimeBuilder
|
19
|
+
@component['daylight'] << builder.component
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/selene/version.rb
CHANGED
data/selene.gemspec
CHANGED
@@ -8,12 +8,16 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Selene::VERSION
|
9
9
|
gem.authors = ["Cory Kaufman-Schofield"]
|
10
10
|
gem.email = ["cory@corykaufman.com"]
|
11
|
-
gem.description = %q{Selene is
|
12
|
-
gem.summary = %q{Selene is
|
13
|
-
gem.homepage = ""
|
11
|
+
gem.description = %q{Selene is an iCalendar parser for Ruby}
|
12
|
+
gem.summary = %q{Selene is an iCalendar parser for Ruby}
|
13
|
+
gem.homepage = "https://github.com/allspiritseve/selene"
|
14
14
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency 'debugger'
|
21
|
+
gem.add_development_dependency 'minitest-colorize'
|
22
|
+
gem.add_development_dependency 'rake'
|
19
23
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:-//Meetup//RemoteApi//EN
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
METHOD:PUBLISH
|
6
|
+
X-ORIGINAL-URL:http://www.meetup.com/DetroitRuby/events/ical/DetroitRuby/
|
7
|
+
|
8
|
+
X-WR-CALNAME:Events - DetroitRuby
|
9
|
+
BEGIN:VTIMEZONE
|
10
|
+
TZID:America/New_York
|
11
|
+
TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York
|
12
|
+
X-LIC-LOCATION:America/New_York
|
13
|
+
BEGIN:DAYLIGHT
|
14
|
+
TZOFFSETFROM:-0500
|
15
|
+
TZOFFSETTO:-0400
|
16
|
+
TZNAME:EDT
|
17
|
+
DTSTART:19700308T020000
|
18
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
19
|
+
END:DAYLIGHT
|
20
|
+
BEGIN:STANDARD
|
21
|
+
TZOFFSETFROM:-0400
|
22
|
+
TZOFFSETTO:-0500
|
23
|
+
TZNAME:EST
|
24
|
+
DTSTART:19701101T020000
|
25
|
+
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
26
|
+
END:STANDARD
|
27
|
+
END:VTIMEZONE
|
28
|
+
BEGIN:VEVENT
|
29
|
+
DTSTAMP:20121231T093631Z
|
30
|
+
DTSTART;TZID=America/New_York:20130110T183000
|
31
|
+
DTEND;TZID=America/New_York:20130110T213000
|
32
|
+
STATUS:CONFIRMED
|
33
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
34
|
+
DESCRIPTION:DetroitRuby\nThursday\, January 10 at 6:30 PM\n\nIts wild\, i
|
35
|
+
ts crazy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of c
|
36
|
+
hanges for this year for DetroitRuby! We are hoping to do away with ...\
|
37
|
+
n\nDetails: http://www.meetup.com/DetroitRuby/events/93346412/
|
38
|
+
CLASS:PUBLIC
|
39
|
+
CREATED:20120106T161509Z
|
40
|
+
GEO:42.33;-83.05
|
41
|
+
LOCATION:Compuware Building (One Campus Martius\, Detroit\, MI 48226)
|
42
|
+
URL:http://www.meetup.com/DetroitRuby/events/93346412/
|
43
|
+
LAST-MODIFIED:20120106T161509Z
|
44
|
+
UID:event_qgkxkcyrcbnb@meetup.com
|
45
|
+
END:VEVENT
|
46
|
+
BEGIN:VEVENT
|
47
|
+
DTSTAMP:20121231T093631Z
|
48
|
+
DTSTART;TZID=America/New_York:20130214T183000
|
49
|
+
DTEND;TZID=America/New_York:20130214T213000
|
50
|
+
STATUS:CONFIRMED
|
51
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
52
|
+
DESCRIPTION:DetroitRuby\nThursday\, February 14 at 6:30 PM\n\nIts wild\,
|
53
|
+
its crazy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of
|
54
|
+
changes for this year for DetroitRuby! We are hoping to do away with ...
|
55
|
+
\n\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrdbsb/
|
56
|
+
CLASS:PUBLIC
|
57
|
+
CREATED:20120106T161509Z
|
58
|
+
GEO:42.33;-83.05
|
59
|
+
URL:http://www.meetup.com/DetroitRuby/events/qgkxkcyrdbsb/
|
60
|
+
LAST-MODIFIED:20120106T161509Z
|
61
|
+
UID:event_qgkxkcyrdbsb@meetup.com
|
62
|
+
END:VEVENT
|
63
|
+
BEGIN:VEVENT
|
64
|
+
DTSTAMP:20121231T093631Z
|
65
|
+
DTSTART;TZID=America/New_York:20130314T183000
|
66
|
+
DTEND;TZID=America/New_York:20130314T213000
|
67
|
+
STATUS:CONFIRMED
|
68
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
69
|
+
DESCRIPTION:DetroitRuby\nThursday\, March 14 at 6:30 PM\n\nIts wild\, its
|
70
|
+
crazy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of cha
|
71
|
+
nges for this year for DetroitRuby! We are hoping to do away with ...\n\
|
72
|
+
nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrfbsb/
|
73
|
+
CLASS:PUBLIC
|
74
|
+
CREATED:20120106T161509Z
|
75
|
+
GEO:42.33;-83.05
|
76
|
+
URL:http://www.meetup.com/DetroitRuby/events/qgkxkcyrfbsb/
|
77
|
+
LAST-MODIFIED:20120106T161509Z
|
78
|
+
UID:event_qgkxkcyrfbsb@meetup.com
|
79
|
+
END:VEVENT
|
80
|
+
BEGIN:VEVENT
|
81
|
+
DTSTAMP:20121231T093631Z
|
82
|
+
DTSTART;TZID=America/New_York:20130411T183000
|
83
|
+
DTEND;TZID=America/New_York:20130411T213000
|
84
|
+
STATUS:CONFIRMED
|
85
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
86
|
+
DESCRIPTION:DetroitRuby\nThursday\, April 11 at 6:30 PM\n\nIts wild\, its
|
87
|
+
crazy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of cha
|
88
|
+
nges for this year for DetroitRuby! We are hoping to do away with ...\n\
|
89
|
+
nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrgbpb/
|
90
|
+
CLASS:PUBLIC
|
91
|
+
CREATED:20120106T161509Z
|
92
|
+
GEO:42.33;-83.05
|
93
|
+
URL:http://www.meetup.com/DetroitRuby/events/qgkxkcyrgbpb/
|
94
|
+
LAST-MODIFIED:20120106T161509Z
|
95
|
+
UID:event_qgkxkcyrgbpb@meetup.com
|
96
|
+
END:VEVENT
|
97
|
+
BEGIN:VEVENT
|
98
|
+
DTSTAMP:20121231T093631Z
|
99
|
+
DTSTART;TZID=America/New_York:20130509T183000
|
100
|
+
DTEND;TZID=America/New_York:20130509T213000
|
101
|
+
STATUS:CONFIRMED
|
102
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
103
|
+
DESCRIPTION:DetroitRuby\nThursday\, May 9 at 6:30 PM\n\nIts wild\, its cr
|
104
|
+
azy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of change
|
105
|
+
s for this year for DetroitRuby! We are hoping to do away with ...\n\nDe
|
106
|
+
tails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrhbmb/
|
107
|
+
CLASS:PUBLIC
|
108
|
+
CREATED:20120106T161509Z
|
109
|
+
GEO:42.33;-83.05
|
110
|
+
URL:http://www.meetup.com/DetroitRuby/events/qgkxkcyrhbmb/
|
111
|
+
LAST-MODIFIED:20120106T161509Z
|
112
|
+
UID:event_qgkxkcyrhbmb@meetup.com
|
113
|
+
END:VEVENT
|
114
|
+
BEGIN:VEVENT
|
115
|
+
DTSTAMP:20121231T093631Z
|
116
|
+
DTSTART;TZID=America/New_York:20130613T183000
|
117
|
+
DTEND;TZID=America/New_York:20130613T213000
|
118
|
+
STATUS:CONFIRMED
|
119
|
+
SUMMARY:DetroitRuby: 2012 Plan + Lightning talks
|
120
|
+
DESCRIPTION:DetroitRuby\nThursday\, June 13 at 6:30 PM\n\nIts wild\, its
|
121
|
+
crazy\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of chan
|
122
|
+
ges for this year for DetroitRuby! We are hoping to do away with ...\n\n
|
123
|
+
Details: http://www.meetup.com/DetroitRuby/events/qgkxkcyrjbrb/
|
124
|
+
CLASS:PUBLIC
|
125
|
+
CREATED:20120106T161509Z
|
126
|
+
GEO:42.33;-83.05
|
127
|
+
URL:http://www.meetup.com/DetroitRuby/events/qgkxkcyrjbrb/
|
128
|
+
LAST-MODIFIED:20120106T161509Z
|
129
|
+
UID:event_qgkxkcyrjbrb@meetup.com
|
130
|
+
END:VEVENT
|
131
|
+
END:VCALENDAR
|
@@ -0,0 +1,197 @@
|
|
1
|
+
{
|
2
|
+
"calendars": [
|
3
|
+
{
|
4
|
+
"version": "2.0",
|
5
|
+
"prodid": "-//Meetup//RemoteApi//EN",
|
6
|
+
"calscale": "GREGORIAN",
|
7
|
+
"method": "PUBLISH",
|
8
|
+
"x-original-url": "http://www.meetup.com/DetroitRuby/events/ical/DetroitRuby/",
|
9
|
+
"x-wr-calname": "Events - DetroitRuby",
|
10
|
+
"time_zones": [
|
11
|
+
{
|
12
|
+
"tzid": "America/New_York",
|
13
|
+
"tzurl": "http://tzurl.org/zoneinfo-outlook/America/New_York",
|
14
|
+
"x-lic-location": "America/New_York",
|
15
|
+
"daylight": [
|
16
|
+
{
|
17
|
+
"tzoffsetfrom": "-0500",
|
18
|
+
"tzoffsetto": "-0400",
|
19
|
+
"tzname": "EDT",
|
20
|
+
"dtstart": "19700308T020000",
|
21
|
+
"rrule": {
|
22
|
+
"freq": "YEARLY",
|
23
|
+
"bymonth": "3",
|
24
|
+
"byday": "2SU"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
]
|
28
|
+
}
|
29
|
+
],
|
30
|
+
"events": [
|
31
|
+
{
|
32
|
+
"dtstamp": "20121231T093631Z",
|
33
|
+
"dtstart": [
|
34
|
+
"20130110T183000",
|
35
|
+
{
|
36
|
+
"tzid": "America/New_York"
|
37
|
+
}
|
38
|
+
],
|
39
|
+
"dtend": [
|
40
|
+
"20130110T213000",
|
41
|
+
{
|
42
|
+
"tzid": "America/New_York"
|
43
|
+
}
|
44
|
+
],
|
45
|
+
"status": "CONFIRMED",
|
46
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
47
|
+
"description": "DetroitRuby\\nThursday\\, January 10 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/93346412/",
|
48
|
+
"class": "PUBLIC",
|
49
|
+
"created": "20120106T161509Z",
|
50
|
+
"geo": [
|
51
|
+
"42.33",
|
52
|
+
"-83.05"
|
53
|
+
],
|
54
|
+
"location": "Compuware Building (One Campus Martius\\, Detroit\\, MI 48226)",
|
55
|
+
"url": "http://www.meetup.com/DetroitRuby/events/93346412/",
|
56
|
+
"last-modified": "20120106T161509Z",
|
57
|
+
"uid": "event_qgkxkcyrcbnb@meetup.com"
|
58
|
+
},
|
59
|
+
{
|
60
|
+
"dtstamp": "20121231T093631Z",
|
61
|
+
"dtstart": [
|
62
|
+
"20130214T183000",
|
63
|
+
{
|
64
|
+
"tzid": "America/New_York"
|
65
|
+
}
|
66
|
+
],
|
67
|
+
"dtend": [
|
68
|
+
"20130214T213000",
|
69
|
+
{
|
70
|
+
"tzid": "America/New_York"
|
71
|
+
}
|
72
|
+
],
|
73
|
+
"status": "CONFIRMED",
|
74
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
75
|
+
"description": "DetroitRuby\\nThursday\\, February 14 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrdbsb/",
|
76
|
+
"class": "PUBLIC",
|
77
|
+
"created": "20120106T161509Z",
|
78
|
+
"geo": [
|
79
|
+
"42.33",
|
80
|
+
"-83.05"
|
81
|
+
],
|
82
|
+
"url": "http://www.meetup.com/DetroitRuby/events/qgkxkcyrdbsb/",
|
83
|
+
"last-modified": "20120106T161509Z",
|
84
|
+
"uid": "event_qgkxkcyrdbsb@meetup.com"
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"dtstamp": "20121231T093631Z",
|
88
|
+
"dtstart": [
|
89
|
+
"20130314T183000",
|
90
|
+
{
|
91
|
+
"tzid": "America/New_York"
|
92
|
+
}
|
93
|
+
],
|
94
|
+
"dtend": [
|
95
|
+
"20130314T213000",
|
96
|
+
{
|
97
|
+
"tzid": "America/New_York"
|
98
|
+
}
|
99
|
+
],
|
100
|
+
"status": "CONFIRMED",
|
101
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
102
|
+
"description": "DetroitRuby\\nThursday\\, March 14 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrfbsb/",
|
103
|
+
"class": "PUBLIC",
|
104
|
+
"created": "20120106T161509Z",
|
105
|
+
"geo": [
|
106
|
+
"42.33",
|
107
|
+
"-83.05"
|
108
|
+
],
|
109
|
+
"url": "http://www.meetup.com/DetroitRuby/events/qgkxkcyrfbsb/",
|
110
|
+
"last-modified": "20120106T161509Z",
|
111
|
+
"uid": "event_qgkxkcyrfbsb@meetup.com"
|
112
|
+
},
|
113
|
+
{
|
114
|
+
"dtstamp": "20121231T093631Z",
|
115
|
+
"dtstart": [
|
116
|
+
"20130411T183000",
|
117
|
+
{
|
118
|
+
"tzid": "America/New_York"
|
119
|
+
}
|
120
|
+
],
|
121
|
+
"dtend": [
|
122
|
+
"20130411T213000",
|
123
|
+
{
|
124
|
+
"tzid": "America/New_York"
|
125
|
+
}
|
126
|
+
],
|
127
|
+
"status": "CONFIRMED",
|
128
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
129
|
+
"description": "DetroitRuby\\nThursday\\, April 11 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrgbpb/",
|
130
|
+
"class": "PUBLIC",
|
131
|
+
"created": "20120106T161509Z",
|
132
|
+
"geo": [
|
133
|
+
"42.33",
|
134
|
+
"-83.05"
|
135
|
+
],
|
136
|
+
"url": "http://www.meetup.com/DetroitRuby/events/qgkxkcyrgbpb/",
|
137
|
+
"last-modified": "20120106T161509Z",
|
138
|
+
"uid": "event_qgkxkcyrgbpb@meetup.com"
|
139
|
+
},
|
140
|
+
{
|
141
|
+
"dtstamp": "20121231T093631Z",
|
142
|
+
"dtstart": [
|
143
|
+
"20130509T183000",
|
144
|
+
{
|
145
|
+
"tzid": "America/New_York"
|
146
|
+
}
|
147
|
+
],
|
148
|
+
"dtend": [
|
149
|
+
"20130509T213000",
|
150
|
+
{
|
151
|
+
"tzid": "America/New_York"
|
152
|
+
}
|
153
|
+
],
|
154
|
+
"status": "CONFIRMED",
|
155
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
156
|
+
"description": "DetroitRuby\\nThursday\\, May 9 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrhbmb/",
|
157
|
+
"class": "PUBLIC",
|
158
|
+
"created": "20120106T161509Z",
|
159
|
+
"geo": [
|
160
|
+
"42.33",
|
161
|
+
"-83.05"
|
162
|
+
],
|
163
|
+
"url": "http://www.meetup.com/DetroitRuby/events/qgkxkcyrhbmb/",
|
164
|
+
"last-modified": "20120106T161509Z",
|
165
|
+
"uid": "event_qgkxkcyrhbmb@meetup.com"
|
166
|
+
},
|
167
|
+
{
|
168
|
+
"dtstamp": "20121231T093631Z",
|
169
|
+
"dtstart": [
|
170
|
+
"20130613T183000",
|
171
|
+
{
|
172
|
+
"tzid": "America/New_York"
|
173
|
+
}
|
174
|
+
],
|
175
|
+
"dtend": [
|
176
|
+
"20130613T213000",
|
177
|
+
{
|
178
|
+
"tzid": "America/New_York"
|
179
|
+
}
|
180
|
+
],
|
181
|
+
"status": "CONFIRMED",
|
182
|
+
"summary": "DetroitRuby: 2012 Plan + Lightning talks",
|
183
|
+
"description": "DetroitRuby\\nThursday\\, June 13 at 6:30 PM\\n\\nIts wild\\, its crazy\\, its DetroitRuby 2012! Hey everyone Happy New year! A lot of changes for this year for DetroitRuby! We are hoping to do away with ...\\n\\nDetails: http://www.meetup.com/DetroitRuby/events/qgkxkcyrjbrb/",
|
184
|
+
"class": "PUBLIC",
|
185
|
+
"created": "20120106T161509Z",
|
186
|
+
"geo": [
|
187
|
+
"42.33",
|
188
|
+
"-83.05"
|
189
|
+
],
|
190
|
+
"url": "http://www.meetup.com/DetroitRuby/events/qgkxkcyrjbrb/",
|
191
|
+
"last-modified": "20120106T161509Z",
|
192
|
+
"uid": "event_qgkxkcyrjbrb@meetup.com"
|
193
|
+
}
|
194
|
+
]
|
195
|
+
}
|
196
|
+
]
|
197
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Selene
|
4
|
+
class CalendarBuilderTest < TestCase
|
5
|
+
|
6
|
+
def builder
|
7
|
+
@builder ||= CalendarBuilder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def event_builder
|
11
|
+
@event_builder ||= EventBuilder.new.extend(Stubbable)
|
12
|
+
end
|
13
|
+
|
14
|
+
def time_zone_builder
|
15
|
+
@time_zone_builder ||= TimeZoneBuilder.new.extend(Stubbable)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parse_prodid
|
19
|
+
builder.parse('PRODID', '', '-//Meetup//RemoteApi//EN')
|
20
|
+
assert_equal builder.component['prodid'], '-//Meetup//RemoteApi//EN'
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_parse_version
|
24
|
+
builder.parse('VERSION', '', '2.0')
|
25
|
+
assert_equal @builder.component['version'], '2.0'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_parse_calscale
|
29
|
+
builder.parse('CALSCALE', '', 'Gregorian')
|
30
|
+
assert_equal builder.component['calscale'], 'Gregorian'
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_parse_method
|
34
|
+
builder.parse('METHOD', '', 'Publish')
|
35
|
+
assert_equal builder.component['method'], 'Publish'
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_parse_x_prop
|
39
|
+
builder.parse('X-ORIGINAL-URL', '', 'http://www.google.com')
|
40
|
+
assert_equal builder.component['x-original-url'], 'http://www.google.com'
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_append_event_builder
|
44
|
+
event_builder.stub :component, { 'summary' => "Bluth's Best Party" }
|
45
|
+
builder.append(event_builder)
|
46
|
+
assert_equal builder.component['events'].first['summary'], "Bluth's Best Party"
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_append_time_zone_builder
|
50
|
+
time_zone_builder.stub :component, { 'tzid' => 'America/Detroit' }
|
51
|
+
builder.append(time_zone_builder)
|
52
|
+
assert_equal builder.component['time_zones'].first, { 'tzid' => 'America/Detroit' }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Selene
|
4
|
+
class DaylightSavingsTimeBuilderTest < TestCase
|
5
|
+
|
6
|
+
def builder
|
7
|
+
@builder ||= DaylightSavingsTimeBuilder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_parses_rrule
|
11
|
+
builder.parse('RRULE', '', 'FREQ=YEARLY;BYMONTH=3;BYDAY=2SU')
|
12
|
+
assert_equal builder.component['rrule'], { 'freq' => 'YEARLY', 'bymonth' => '3', 'byday' => '2SU' }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Selene
|
4
|
+
class EventBuilderTest < TestCase
|
5
|
+
|
6
|
+
def builder
|
7
|
+
@builder ||= EventBuilder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_parses_dtstart
|
11
|
+
expected = ['20130110T183000', { 'tzid' => 'America/New_York' }]
|
12
|
+
builder.parse('DTSTART', { 'tzid' => 'America/New_York' }, '20130110T183000')
|
13
|
+
assert_equal builder.component['dtstart'], expected
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_parses_dtstamp
|
17
|
+
builder.parse('DTSTAMP', nil, '20121231T093631Z')
|
18
|
+
assert_equal builder.component['dtstamp'], '20121231T093631Z'
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_parses_dtstart_without_tzid
|
22
|
+
builder.parse('DTSTART', nil, '20130110T183000')
|
23
|
+
assert_equal builder.component['dtstart'], '20130110T183000'
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_parses_dtend
|
27
|
+
expected = ['20130110T183000', { 'tzid' => 'America/New_York' }]
|
28
|
+
builder.parse('DTEND', { 'tzid' => 'America/New_York' }, '20130110T183000')
|
29
|
+
assert_equal builder.component['dtend'], expected
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_parses_dtstart_without_tzid
|
33
|
+
builder.parse('DTEND', nil, '20130110T183000')
|
34
|
+
assert_equal builder.component['dtend'], '20130110T183000'
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_parses_status
|
38
|
+
builder.parse('STATUS', nil, 'CONFIRMED')
|
39
|
+
assert_equal builder.component['status'], 'CONFIRMED'
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_parses_summary
|
43
|
+
builder.parse('SUMMARY', nil, 'DetroitRuby: 2012 Plan + Lightning talks')
|
44
|
+
assert_equal builder.component['summary'], 'DetroitRuby: 2012 Plan + Lightning talks'
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_parses_description
|
48
|
+
builder.parse('DESCRIPTION', nil, 'DetroitRuby\nThursday\, January 10 at 6:30 PM\n\n')
|
49
|
+
assert_equal builder.component['description'], 'DetroitRuby\nThursday\, January 10 at 6:30 PM\n\n'
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_parses_class
|
53
|
+
builder.parse('CLASS', nil, 'PUBLIC')
|
54
|
+
assert_equal builder.component['class'], 'PUBLIC'
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_parses_created
|
58
|
+
builder.parse('CREATED', nil, '20120106T161509Z')
|
59
|
+
assert_equal builder.component['created'], '20120106T161509Z'
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_parses_geo
|
63
|
+
builder.parse('GEO', nil, '42.33;-83.05')
|
64
|
+
assert_equal builder.component['geo'], ['42.33', '-83.05']
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_parses_location
|
68
|
+
builder.parse('LOCATION', nil, 'Compuware Building (One Campus Martius\, Detroit\, MI 48226)')
|
69
|
+
assert_equal builder.component['location'], 'Compuware Building (One Campus Martius\, Detroit\, MI 48226)'
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_parses_url
|
73
|
+
builder.parse('URL', nil, 'http://www.meetup.com/DetroitRuby/events/93346412/')
|
74
|
+
assert_equal builder.component['url'], 'http://www.meetup.com/DetroitRuby/events/93346412/'
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_parses_last_modified
|
78
|
+
builder.parse('LAST-MODIFIED', nil, '20120106T161509Z')
|
79
|
+
assert_equal builder.component['last-modified'], '20120106T161509Z'
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_parses_uid
|
83
|
+
builder.parse('UID', nil, 'event_qgkxkcyrcbnb@meetup.com')
|
84
|
+
assert_equal builder.component['uid'], 'event_qgkxkcyrcbnb@meetup.com'
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Selene
|
5
|
+
class ParserTest < TestCase
|
6
|
+
|
7
|
+
def test_lines_are_unfolded_before_splitting
|
8
|
+
assert_equal Selene::Parser.split_content_lines("This is a\r\n test").first, "This is a test"
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_parses_blank_string
|
12
|
+
assert_equal Selene::Parser.parse(""), { 'calendars' => [] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_parses_content_line
|
16
|
+
assert_equal Selene::Parser.parse_content_line('VERSION:2.0'), { :name => 'VERSION', :params => nil, :value => '2.0' }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_parses_content_line_with_url
|
20
|
+
expected = { :name => 'TZURL', :params => nil, :value => 'http://www.meetup.com/DetroitRuby/events/ical/DetroitRuby/' }
|
21
|
+
assert_equal Selene::Parser.parse_content_line('TZURL:http://www.meetup.com/DetroitRuby/events/ical/DetroitRuby/'), expected
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_parses_content_line_with_param
|
25
|
+
expected = { :name => 'DTSTART', :params => { 'tzid' => 'America/New_York' }, :value => '20130110T183000' }
|
26
|
+
assert_equal Selene::Parser.parse_content_line('DTSTART;TZID=America/New_York:20130110T183000'), expected
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sanity tests just to make sure the thing works
|
30
|
+
|
31
|
+
def test_parses_simple_calendar
|
32
|
+
assert_equal Selene::Parser.parse("BEGIN:VCALENDAR\r\nSUMMARY:Meetups\r\nEND:VCALENDAR"), { 'calendars' => [{ 'summary' => 'Meetups' }] }
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_parses_meetup_calendar
|
36
|
+
expected = JSON.parse(fixture('meetup.json'))
|
37
|
+
assert_equal Selene::Parser.parse(fixture('meetup.ics')), expected
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'debugger'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/colorize'
|
5
|
+
require 'minitest/mock'
|
6
|
+
require 'selene'
|
7
|
+
|
8
|
+
module Selene
|
9
|
+
class TestCase < MiniTest::Unit::TestCase
|
10
|
+
|
11
|
+
def fixture(filename)
|
12
|
+
File.read(File.join(File.dirname(__FILE__), 'fixtures', filename))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Stubbable
|
19
|
+
|
20
|
+
# stub :method => 'value'
|
21
|
+
# stub :method, 'value'
|
22
|
+
# stub :method { 'value' }
|
23
|
+
|
24
|
+
def stub(hash_or_method, value_or_block = nil)
|
25
|
+
if hash_or_method.is_a?(Hash)
|
26
|
+
hash_or_method.each { |method, value| stub method, Proc.new { value } }
|
27
|
+
elsif !value_or_block.is_a?(Proc)
|
28
|
+
stub hash_or_method, Proc.new { value_or_block }
|
29
|
+
else
|
30
|
+
define_singleton_method hash_or_method, value_or_block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: selene
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,28 +9,90 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
12
|
+
date: 2013-01-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: debugger
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest-colorize
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Selene is an iCalendar parser for Ruby
|
15
63
|
email:
|
16
64
|
- cory@corykaufman.com
|
17
65
|
executables: []
|
18
66
|
extensions: []
|
19
67
|
extra_rdoc_files: []
|
20
68
|
files:
|
69
|
+
- .gitattributes
|
21
70
|
- .gitignore
|
22
71
|
- .rspec
|
72
|
+
- .rvmrc
|
23
73
|
- Gemfile
|
24
74
|
- LICENSE.txt
|
25
75
|
- README.md
|
26
76
|
- Rakefile
|
27
77
|
- lib/selene.rb
|
78
|
+
- lib/selene/alarm_builder.rb
|
79
|
+
- lib/selene/calendar_builder.rb
|
80
|
+
- lib/selene/daylight_savings_time_builder.rb
|
81
|
+
- lib/selene/event_builder.rb
|
82
|
+
- lib/selene/parser.rb
|
83
|
+
- lib/selene/standard_time_builder.rb
|
84
|
+
- lib/selene/time_zone_builder.rb
|
28
85
|
- lib/selene/version.rb
|
29
86
|
- selene.gemspec
|
30
|
-
-
|
31
|
-
-
|
32
|
-
-
|
33
|
-
|
87
|
+
- test/fixtures/meetup.ics
|
88
|
+
- test/fixtures/meetup.json
|
89
|
+
- test/selene/calendar_builder_test.rb
|
90
|
+
- test/selene/daylight_savings_time_builder_test.rb
|
91
|
+
- test/selene/event_builder_test.rb
|
92
|
+
- test/selene/parser_test.rb
|
93
|
+
- test/selene/time_zone_builder_test.rb
|
94
|
+
- test/test_helper.rb
|
95
|
+
homepage: https://github.com/allspiritseve/selene
|
34
96
|
licenses: []
|
35
97
|
post_install_message:
|
36
98
|
rdoc_options: []
|
@@ -42,19 +104,30 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
104
|
- - ! '>='
|
43
105
|
- !ruby/object:Gem::Version
|
44
106
|
version: '0'
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
hash: 3007603395114085871
|
45
110
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
111
|
none: false
|
47
112
|
requirements:
|
48
113
|
- - ! '>='
|
49
114
|
- !ruby/object:Gem::Version
|
50
115
|
version: '0'
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
hash: 3007603395114085871
|
51
119
|
requirements: []
|
52
120
|
rubyforge_project:
|
53
121
|
rubygems_version: 1.8.24
|
54
122
|
signing_key:
|
55
123
|
specification_version: 3
|
56
|
-
summary: Selene is
|
124
|
+
summary: Selene is an iCalendar parser for Ruby
|
57
125
|
test_files:
|
58
|
-
-
|
59
|
-
-
|
60
|
-
-
|
126
|
+
- test/fixtures/meetup.ics
|
127
|
+
- test/fixtures/meetup.json
|
128
|
+
- test/selene/calendar_builder_test.rb
|
129
|
+
- test/selene/daylight_savings_time_builder_test.rb
|
130
|
+
- test/selene/event_builder_test.rb
|
131
|
+
- test/selene/parser_test.rb
|
132
|
+
- test/selene/time_zone_builder_test.rb
|
133
|
+
- test/test_helper.rb
|
@@ -1,76 +0,0 @@
|
|
1
|
-
BEGIN:VCALENDAR
|
2
|
-
VERSION:2.0
|
3
|
-
PRODID:-//Meetup//RemoteApi//EN
|
4
|
-
CALSCALE:GREGORIAN
|
5
|
-
METHOD:PUBLISH
|
6
|
-
X-ORIGINAL-URL:http://www.meetup.com/Mobile-Monday-Detroit/events/ical/Mo
|
7
|
-
bile+Monday+Detroit/
|
8
|
-
X-WR-CALNAME:Events - Mobile Monday Detroit
|
9
|
-
BEGIN:VTIMEZONE
|
10
|
-
TZID:America/New_York
|
11
|
-
TZURL:http://tzurl.org/zoneinfo-outlook/America/New_York
|
12
|
-
X-LIC-LOCATION:America/New_York
|
13
|
-
BEGIN:DAYLIGHT
|
14
|
-
TZOFFSETFROM:-0500
|
15
|
-
TZOFFSETTO:-0400
|
16
|
-
TZNAME:EDT
|
17
|
-
DTSTART:19700308T020000
|
18
|
-
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
19
|
-
END:DAYLIGHT
|
20
|
-
BEGIN:STANDARD
|
21
|
-
TZOFFSETFROM:-0400
|
22
|
-
TZOFFSETTO:-0500
|
23
|
-
TZNAME:EST
|
24
|
-
DTSTART:19701101T020000
|
25
|
-
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
26
|
-
END:STANDARD
|
27
|
-
END:VTIMEZONE
|
28
|
-
BEGIN:VEVENT
|
29
|
-
DTSTAMP:20121222T185423Z
|
30
|
-
DTSTART;TZID=America/New_York:20130114T173000
|
31
|
-
DTEND;TZID=America/New_York:20130114T203000
|
32
|
-
STATUS:CONFIRMED
|
33
|
-
SUMMARY:MoMoDetroit - taking a break in January
|
34
|
-
DESCRIPTION:Mobile Monday Detroit\nMonday\, January 14 at 5:30 PM\n\nMobi
|
35
|
-
le Monday Detroit will not be meeting in January 2013. We'll be taking a
|
36
|
-
month off during which time our organizing committee will be working on
|
37
|
-
p...\n\nDetails: http://www.meetup.com/Mobile-Monday-Detroit/events/939
|
38
|
-
23552/
|
39
|
-
CLASS:PUBLIC
|
40
|
-
CREATED:20121203T215142Z
|
41
|
-
GEO:42.33;-83.05
|
42
|
-
URL:http://www.meetup.com/Mobile-Monday-Detroit/events/93923552/
|
43
|
-
LAST-MODIFIED:20121203T215142Z
|
44
|
-
UID:event_93923552@meetup.com
|
45
|
-
END:VEVENT
|
46
|
-
BEGIN:VEVENT
|
47
|
-
DTSTAMP:20121222T185423Z
|
48
|
-
DTSTART;TZID=America/New_York:20130211T173000
|
49
|
-
DTEND;TZID=America/New_York:20130211T200000
|
50
|
-
STATUS:CONFIRMED
|
51
|
-
SUMMARY:To be announced
|
52
|
-
DESCRIPTION:Mobile Monday Detroit\nMonday\, February 11 at 5:30 PM\n\nDet
|
53
|
-
ails: http://www.meetup.com/Mobile-Monday-Detroit/events/93923892/
|
54
|
-
CLASS:PUBLIC
|
55
|
-
CREATED:20121203T215431Z
|
56
|
-
GEO:42.33;-83.05
|
57
|
-
URL:http://www.meetup.com/Mobile-Monday-Detroit/events/93923892/
|
58
|
-
LAST-MODIFIED:20121203T215431Z
|
59
|
-
UID:event_93923892@meetup.com
|
60
|
-
END:VEVENT
|
61
|
-
BEGIN:VEVENT
|
62
|
-
DTSTAMP:20121222T185423Z
|
63
|
-
DTSTART;TZID=America/New_York:20130311T173000
|
64
|
-
DTEND;TZID=America/New_York:20130311T200000
|
65
|
-
STATUS:CONFIRMED
|
66
|
-
SUMMARY:To be announced
|
67
|
-
DESCRIPTION:Mobile Monday Detroit\nMonday\, March 11 at 5:30 PM\n\nDetail
|
68
|
-
s: http://www.meetup.com/Mobile-Monday-Detroit/events/93924042/
|
69
|
-
CLASS:PUBLIC
|
70
|
-
CREATED:20121203T215538Z
|
71
|
-
GEO:42.33;-83.05
|
72
|
-
URL:http://www.meetup.com/Mobile-Monday-Detroit/events/93924042/
|
73
|
-
LAST-MODIFIED:20121203T215538Z
|
74
|
-
UID:event_93924042@meetup.com
|
75
|
-
END:VEVENT
|
76
|
-
END:VCALENDAR
|
data/spec/selene.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
-
|
3
|
-
describe Selene do
|
4
|
-
|
5
|
-
describe "when parsing" do
|
6
|
-
let(:actual) { File.read('fixtures/mobile-monday.ics') }
|
7
|
-
let(:expected) { File.read('fixtures/mobile-monday.json') }
|
8
|
-
|
9
|
-
it "parses calendar" do
|
10
|
-
Selene.parse(actual).should == JSON.parse(expected)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
end
|