selene 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Selene](http://corykaufman.com/images/selene.png)
|
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
|