reagent-dockit 0.1 → 0.1.1
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/README +4 -0
- data/Rakefile +9 -1
- data/lib/dockit.rb +13 -20
- data/lib/dockit/event.rb +23 -0
- data/lib/event.rb +22 -0
- data/lib/event_collection.rb +38 -0
- data/lib/export.rb +76 -85
- data/lib/templates/event.ics.erb +21 -0
- data/lib/templates/event_collection.ics.erb +11 -0
- data/lib/user.rb +35 -0
- metadata +20 -3
data/README
CHANGED
@@ -28,10 +28,14 @@ default:
|
|
28
28
|
password: password
|
29
29
|
remote_uri: http://mail.example.com/exchange/john.doe@example.com/
|
30
30
|
local_port: 2000
|
31
|
+
timezone: US/Eastern
|
31
32
|
|
32
33
|
The resulting url for subscribing in iCal when running this daemon on the localhost would be:
|
33
34
|
http://localhost:2000/john.doe.ics
|
34
35
|
|
36
|
+
Timezones are include US/ Eastern,Pacific,Mountain or any other standard format.
|
37
|
+
See iCal's options for timezones for a complete listing.
|
38
|
+
|
35
39
|
Starting the Daemon
|
36
40
|
-----------------
|
37
41
|
Given the yaml file defined example, the command would be:
|
data/Rakefile
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake/gempackagetask'
|
3
|
+
require 'spec/rake/spectask'
|
3
4
|
|
5
|
+
desc "build the gem"
|
4
6
|
task :gem do
|
5
7
|
system('gem build dockit.gemspec')
|
6
8
|
end
|
7
9
|
|
8
10
|
task :install => [:package] do
|
9
11
|
sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
|
10
|
-
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Spec::Rake::SpecTask.new do |t|
|
15
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :spec
|
data/lib/dockit.rb
CHANGED
@@ -1,42 +1,35 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rack'
|
3
3
|
require 'yaml'
|
4
|
+
require 'activesupport'
|
4
5
|
|
5
6
|
require "export"
|
6
|
-
|
7
|
-
class String
|
8
|
-
def blank?
|
9
|
-
self.nil? || self.strip.empty?
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class NilClass
|
14
|
-
def blank?
|
15
|
-
true
|
16
|
-
end
|
17
|
-
end
|
7
|
+
require "user"
|
18
8
|
|
19
9
|
class Dockit
|
20
10
|
include Export
|
21
11
|
|
12
|
+
attr_accessor :user, :pid
|
13
|
+
|
22
14
|
def initialize(config)
|
23
15
|
# Get configuration information
|
24
16
|
config_file = YAML.load_file(File.join(ENV['HOME'], ".dockit"))
|
25
|
-
|
17
|
+
|
18
|
+
user_config = config_file && config_file.has_key?(config) ?
|
26
19
|
config_file[config] : config_file['default']
|
27
20
|
|
28
|
-
raise "No
|
21
|
+
raise "No .dockit file found with user section: #{config}!" unless user_config
|
22
|
+
|
23
|
+
@user = User.new(user_config)
|
29
24
|
end
|
30
25
|
|
31
26
|
def run
|
32
|
-
|
33
|
-
|
34
|
-
pid = fork do
|
35
|
-
Rack::Handler::Mongrel.run(rack_process(@user), :Port => local_port)
|
27
|
+
@pid = fork do
|
28
|
+
Rack::Handler::Mongrel.run(rack_process(@user), :Port => @user.local_port)
|
36
29
|
end
|
37
30
|
|
38
|
-
Process.detach(pid)
|
31
|
+
Process.detach(@pid)
|
39
32
|
|
40
|
-
puts "Use sudo kill #{pid} to stop this process."
|
33
|
+
puts "Use sudo kill #{@pid} to stop this process."
|
41
34
|
end
|
42
35
|
end
|
data/lib/dockit/event.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rexchange'
|
2
|
+
|
3
|
+
class Event
|
4
|
+
|
5
|
+
@@configuration = nil
|
6
|
+
|
7
|
+
def self.configuration=(configuration)
|
8
|
+
@@configuration = configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.all
|
12
|
+
endpoint = "#{@@configuration[:url]}/#{@@configuration[:email]}"
|
13
|
+
events = []
|
14
|
+
|
15
|
+
RExchange.open(endpoint, @@configuration[:email], @@configuration[:password]) do |mailbox|
|
16
|
+
mailbox.calendar.each do |appointment|
|
17
|
+
events << Event.new(appointment)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
events
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/event.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'event_collection'
|
2
|
+
|
3
|
+
class Event
|
4
|
+
def initialize(event)
|
5
|
+
@event = event
|
6
|
+
end
|
7
|
+
|
8
|
+
def dformat(t)
|
9
|
+
t.strftime('%Y%m%dT%H%M%S')
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(methodname, *args)
|
13
|
+
@event.send(methodname, *args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_ics
|
17
|
+
filename = File.join(File.dirname(__FILE__), "templates", "event.ics.erb")
|
18
|
+
file = IO.read(filename)
|
19
|
+
|
20
|
+
ERB.new(file).result(binding)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rexchange'
|
2
|
+
|
3
|
+
class EventCollection
|
4
|
+
def initialize
|
5
|
+
@collection = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def size
|
9
|
+
@collection.size
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(event)
|
13
|
+
@collection << event if event
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_ics
|
17
|
+
filename = File.join(File.dirname(__FILE__), "templates", "event_collection.ics.erb")
|
18
|
+
file = IO.read(filename)
|
19
|
+
ERB.new(file).inspect
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.user=(user)
|
23
|
+
@user = user ? user : @user || User.new({})
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.user
|
27
|
+
@user ||= User.new({})
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.all(&block)
|
31
|
+
self.user=(nil) unless @user
|
32
|
+
RExchange.open(@user.remote_uri, @user.email, @user.password) do |mailbox|
|
33
|
+
mailbox.calendar.each do |event|
|
34
|
+
@collection << Event.new(event)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/export.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'chronic'
|
1
|
+
require 'event_collection'
|
3
2
|
|
4
3
|
module Export
|
5
4
|
def rack_process(user)
|
@@ -12,93 +11,85 @@ module Export
|
|
12
11
|
end
|
13
12
|
|
14
13
|
def exchange(user)
|
15
|
-
|
16
|
-
|
17
|
-
email = "#{username}@#{domain}"
|
18
|
-
password = user.fetch("password", 'password')
|
19
|
-
remote_uri = user.fetch("remote_uri", "http://localhost/#{email}/")
|
14
|
+
file = File.new("#{user.username}.ics", "w")
|
15
|
+
raise "Failed to create file #{user.username}.ics" unless file
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
17
|
+
EventCollection.user = user
|
18
|
+
file.puts(EventCollection.all.to_ics)
|
19
|
+
file.close
|
20
|
+
|
21
|
+
# RExchange::open(user.remote_uri, user.email, user.password) do |mailbox|
|
22
|
+
#
|
23
|
+
# # create ics file, clearing any old one
|
24
|
+
# file = File.new("#{user.username}.ics", "w")
|
25
|
+
# raise "Failed to create file #{user.username}.ics" unless file
|
26
|
+
#
|
27
|
+
# file.puts "BEGIN:VCALENDAR"
|
28
|
+
# file.puts "CALSCALE:GREGORIAN"
|
29
|
+
# file.puts "PRODID:-//Apple Computer\, Inc//iCal 2.0//EN"
|
30
|
+
# file.puts "VERSION:2.0"
|
31
|
+
# file.puts "X-WR-CALNAME:#{user.title}"
|
32
|
+
#
|
33
|
+
# # itemcount = 0
|
34
|
+
#
|
35
|
+
# # Loop through all calendar events, making VEVENTS
|
36
|
+
# mailbox.calendar.each do |event|
|
37
|
+
#
|
38
|
+
# file.puts "BEGIN:VEVENT"
|
39
|
+
#
|
40
|
+
# # Set some properties for this event
|
41
|
+
# file.puts "DTSTAMP;TZID=#{user.timezone}:#{dformat(event.created_at)}"
|
42
|
+
# file.puts "DTSTART;TZID=#{user.timezone}:#{dformat(event.start_at)}"
|
43
|
+
# file.puts "DTEND;TZID=#{user.timezone}:#{dformat(event.end_at)}"
|
44
|
+
# file.puts "LAST-MODIFIED;TZID=#{user.timezone}:#{dformat(event.modified_at)}"
|
45
|
+
#
|
46
|
+
# file.puts "SUMMARY:" + (event.subject.blank? ? "Unknown Subject" : event.subject)
|
47
|
+
#
|
48
|
+
# description = event.body.blank? ? "" : event.body.gsub!("\n","\\n").gsub("^\n","")
|
49
|
+
# # ?? add event.href+"?Cmd=accept" or "decline" or "tentative"
|
50
|
+
# # description += event.href+"?Cmd=accept"
|
51
|
+
# # description += event.href+"?Cmd=decline"
|
52
|
+
# # description += event.href+"?Cmd=tentative"
|
53
|
+
#
|
54
|
+
# file.puts "DESCRIPTION:" + description
|
55
|
+
#
|
56
|
+
# file.puts "UID:" + event.uid if event.uid
|
57
|
+
#
|
58
|
+
# if event.reminder_offset && event.reminder_offset.to_i > 60
|
59
|
+
# # ouput valarm details. reminder_offset is in seconds
|
60
|
+
# ro_min = event.reminder_offset.to_i / 60
|
61
|
+
# file.puts "BEGIN:VALARM"
|
62
|
+
# file.puts "TRIGGER:-PT#{ro_min}M"
|
63
|
+
# # item.puts "DESCRIPTION:Påminnelse om aktivitet"
|
64
|
+
# file.puts "ACTION:DISPLAY"
|
65
|
+
# file.puts "END:VALARM"
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# #add event to calendar
|
69
|
+
# file.puts "END:VEVENT"
|
70
|
+
#
|
71
|
+
# # itemcount += 1
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# # puts mailbox.inspect
|
75
|
+
# # puts
|
76
|
+
# # puts mailbox.methods
|
77
|
+
#
|
78
|
+
# # Loop through all todos, creating VTODOS
|
79
|
+
# # Loop through all journals, creating VJOURNAL
|
80
|
+
# # Loop through all free/busy time, creating VFREEBUSY
|
81
|
+
# # event.busy_status
|
82
|
+
#
|
83
|
+
# #close ical file
|
84
|
+
# file.puts "END:VCALENDAR"
|
85
|
+
# file.close
|
86
|
+
#
|
87
|
+
# # p "Done! Wrote #{itemcount} appointment items"
|
88
|
+
#
|
89
|
+
# end
|
92
90
|
end
|
93
91
|
|
94
92
|
def dformat(t)
|
95
|
-
# month = s[4..6]
|
96
|
-
# day = s[8..9].to_i
|
97
|
-
# hour = s[11..12].to_i
|
98
|
-
# minute = s[14..15].to_i
|
99
|
-
# second = s[17..18].to_i
|
100
|
-
# year = s[23..27].to_i
|
101
|
-
# Time.local(year, month, day, hour, minute, second).strftime('%Y%m%dT%H%M%S')
|
102
93
|
t.strftime('%Y%m%dT%H%M%S')
|
103
94
|
end
|
104
95
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
BEGIN:VEVENT
|
2
|
+
DTSTAMP;TZID=<%= EventCollection.user.timezone %>:<%= dformat(@event.created_at) %>
|
3
|
+
DTSTART;TZID=<%= EventCollection.user.timezone %>:<%= dformat(@event.start_at) %>
|
4
|
+
DTEND;TZID=<%= EventCollection.user.timezone %>:<%= dformat(@event.end_at) %>
|
5
|
+
LAST-MODIFIED;TZID=<%= EventCollection.user.timezone %>:<%= dformat(@event.modified_at) %>
|
6
|
+
|
7
|
+
SUMMARY:<%= (@event.subject.blank? ? "Unknown Subject" : @event.subject) %>
|
8
|
+
<% description = @event.body.blank? ? "" : @event.body.gsub("\n","\\n").gsub("^\n","") %>
|
9
|
+
|
10
|
+
DESCRIPTION: <%= description %>
|
11
|
+
UID:<%= @event.uid if @event.uid %>
|
12
|
+
|
13
|
+
<% if @event.reminder_offset && @event.reminder_offset.to_i > 60 %>
|
14
|
+
<% ro_min = @event.reminder_offset.to_i / 60 %>
|
15
|
+
BEGIN:VALARM
|
16
|
+
TRIGGER:-PT<%= ro_min %>M
|
17
|
+
ACTION:DISPLAY
|
18
|
+
END:VALARM
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
END:VEVENT
|
data/lib/user.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class User
|
2
|
+
attr_accessor :username,
|
3
|
+
:domain,
|
4
|
+
:email,
|
5
|
+
:password,
|
6
|
+
:timezone,
|
7
|
+
:remote_uri,
|
8
|
+
:local_port,
|
9
|
+
:title
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
config.symbolize_keys!
|
13
|
+
|
14
|
+
self.username = config.fetch(:username, ENV['USER'])
|
15
|
+
self.domain = config.fetch(:domain, 'localhost')
|
16
|
+
self.email = "#{self.username}"
|
17
|
+
self.email << "@#{self.domain}" unless self.domain.blank?
|
18
|
+
self.password = config.fetch(:password, "password")
|
19
|
+
self.timezone = config.fetch(:timezone, "US/Eastern")
|
20
|
+
self.remote_uri = config.fetch(:remote_uri, "http://localhost/#{self.email}/")
|
21
|
+
self.local_port = config.fetch(:local_port, 2000)
|
22
|
+
self.title = config.fetch(:title, self.username)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_hash
|
26
|
+
{:username => self.username,
|
27
|
+
:domain => self.domain,
|
28
|
+
:email => self.email,
|
29
|
+
:password => self.password,
|
30
|
+
:timezone => self.timezone,
|
31
|
+
:remote_uri => self.remote_uri,
|
32
|
+
:local_port => self.local_port,
|
33
|
+
:title => self.title}
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reagent-dockit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Pitale @ Viget Labs
|
@@ -31,13 +31,22 @@ dependencies:
|
|
31
31
|
version: 0.3.0
|
32
32
|
version:
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: activesupport
|
35
35
|
version_requirement:
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0
|
40
|
+
version: "0"
|
41
|
+
version:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: erb
|
44
|
+
version_requirement:
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
41
50
|
version:
|
42
51
|
description: A rack application daemon that creates an iCal subscription file from exchange calendars.
|
43
52
|
email: tony.pitale@viget.com
|
@@ -57,6 +66,14 @@ files:
|
|
57
66
|
- Manifest
|
58
67
|
- lib/dockit.rb
|
59
68
|
- lib/export.rb
|
69
|
+
- lib/dockit
|
70
|
+
- lib/dockit/event.rb
|
71
|
+
- lib/event_collection.rb
|
72
|
+
- lib/event.rb
|
73
|
+
- lib/user.rb
|
74
|
+
- lib/templates
|
75
|
+
- lib/templates/event_collection.ics.erb
|
76
|
+
- lib/templates/event.ics.erb
|
60
77
|
has_rdoc: false
|
61
78
|
homepage: http://www.viget.com/extend/
|
62
79
|
post_install_message:
|