calfilter 1.0.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/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2008-05-02
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ example.rb
6
+ lib/calfilter.rb
7
+ lib/calfilter/cgi.rb
8
+ lib/calfilter/tripit.rb
9
+ test/test_calfilter.rb
10
+ test/test_calfilter_cgi.rb
data/README.txt ADDED
@@ -0,0 +1,69 @@
1
+ = calfilter
2
+
3
+ * http://opensource.thinkrelevance.com/wiki/calfilter
4
+
5
+ == DESCRIPTION:
6
+
7
+ calfilter is a small library to assist in writing filtering
8
+ programs for icalendar files or streams. It can be used for
9
+ various purposes, including:
10
+
11
+ * removing items from icalendar feeds that are not interesting to you
12
+ * removing private information from your own calendar before publishing it to others
13
+ * reformatting a provided calendar to highlight particular information
14
+
15
+ == FEATURES/PROBLEMS:
16
+
17
+ * require 'calfilter/tripit' to add some methods specific to
18
+ tripit.com calendar feeds.
19
+
20
+ * require 'calfilter/cgi' to automatically turn your filter into
21
+ a CGI script. The CGI object is available as 'CalFilter::CGI'.
22
+
23
+ == SYNOPSIS:
24
+
25
+ require 'calfilter'
26
+
27
+ cals = filter_calendars_at('some_url') do |cal|
28
+ cal.keep(:events) # not journals, todos, or freebusys
29
+
30
+ cal.filter_events do |evt|
31
+ # Remove events I've marked private
32
+ evt.remove if evt.description =~ /PRIVATE/
33
+ # Don't reveal phone numbers of my contacts
34
+ evt.description.sub! /\d{3}-\d{4}/, '###-####'
35
+ end
36
+ end
37
+
38
+ == REQUIREMENTS:
39
+
40
+ calfilter depends on the icalendar gem.
41
+
42
+ == INSTALL:
43
+
44
+ sudo gem install calfilter
45
+
46
+ == LICENSE:
47
+
48
+ (The MIT License)
49
+
50
+ Copyright (c) 2008 Relevance, Inc.
51
+
52
+ Permission is hereby granted, free of charge, to any person obtaining
53
+ a copy of this software and associated documentation files (the
54
+ 'Software'), to deal in the Software without restriction, including
55
+ without limitation the rights to use, copy, modify, merge, publish,
56
+ distribute, sublicense, and/or sell copies of the Software, and to
57
+ permit persons to whom the Software is furnished to do so, subject to
58
+ the following conditions:
59
+
60
+ The above copyright notice and this permission notice shall be
61
+ included in all copies or substantial portions of the Software.
62
+
63
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
64
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
65
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
66
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
67
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
68
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
69
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/calfilter.rb'
6
+
7
+ class Hoe
8
+ def extra_deps
9
+ @extra_deps.reject do |x|
10
+ Array(x).first == 'hoe'
11
+ end
12
+ end
13
+ end
14
+
15
+ Hoe.new('calfilter', CalFilter::VERSION) do |p|
16
+ p.rubyforge_name = 'thinkrelevance'
17
+ p.developer('Glenn Vanderburg', 'glenn@thinkrelevance.com')
18
+ p.extra_deps = %w{icalendar}
19
+ end
20
+
21
+ # vim: syntax=Ruby
data/example.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ gem 'calfilter'
5
+ require 'calfilter'
6
+ require 'calfilter/cgi'
7
+
8
+ filter_calendars_at(CalFilter::CGI.query_string}) do |cal|
9
+ cal.keep(:events) # drop todos, journals, etc.
10
+
11
+ cal.filter_events do |evt|
12
+ evt.keep if evt.dtstart >= Date.today # ignore stuff in the past
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ require 'calfilter'
2
+ require 'cgi'
3
+ require 'stringio'
4
+
5
+ module CalFilter
6
+
7
+ class CGIWrapper
8
+ attr_reader :output_stream
9
+
10
+ def initialize(output_stream)
11
+ set_cgi_constant
12
+ CalFilter.output_stream = @output_stream = output_stream
13
+ end
14
+
15
+ def create_cgi_instance
16
+ CGI.new
17
+ end
18
+
19
+ def set_cgi_constant
20
+ CalFilter.const_set('CGI', create_cgi_instance) unless CalFilter.const_defined?('CGI')
21
+ end
22
+
23
+ def finish
24
+ CGI.out('text/calendar; charset=utf-8'){ output_stream.string }
25
+ end
26
+ end
27
+
28
+ def self.make_cgi_wrapper
29
+ CGIWrapper.new(StringIO.new)
30
+ end
31
+
32
+ CGIWRAPPER = make_cgi_wrapper
33
+
34
+ end
35
+
36
+ at_exit do
37
+ CalFilter::CGIWRAPPER.finish
38
+ end
@@ -0,0 +1,31 @@
1
+ require 'calfilter'
2
+
3
+ module CalFilter
4
+ module TripIt
5
+ EVENT_TYPES = {
6
+ # The keys are the $1 strings from description =~ /^\[(.*?)\]/m
7
+ nil => :trip,
8
+ 'Flight' => :flight,
9
+ 'Car Rental' => :car,
10
+ 'Hotel' => :hotel,
11
+ 'Directions' => :directions,
12
+ 'Activity' => :activity
13
+ }
14
+
15
+ def tripit_type(*args)
16
+ return :unknown unless __kind__ == 'event'
17
+ if args.empty?
18
+ description =~ /^\[(.+?)\]/m
19
+ TripIt::EVENT_TYPES[$1] || :unknown
20
+ else
21
+ args.include?(tripit_type)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ class ResourceWrapper
29
+ include TripIt
30
+ end
31
+ end
data/lib/calfilter.rb ADDED
@@ -0,0 +1,166 @@
1
+ %w{rubygems icalendar date open-uri}.each{|l| require l}
2
+
3
+ module CalFilter
4
+ VERSION = '1.0.0'
5
+
6
+ def self.output_stream
7
+ @output_stream
8
+ end
9
+
10
+ def self.output_stream=(stream)
11
+ @output_stream = stream
12
+ end
13
+
14
+ class FilterError < RuntimeError
15
+ end
16
+
17
+ class ResourceWrapper
18
+ def initialize(delegate, kind)
19
+ @delegate = delegate
20
+ @kind = kind
21
+ end
22
+
23
+ def __delegate__
24
+ @delegate
25
+ end
26
+
27
+ def __action__
28
+ @keep_or_remove || :default
29
+ end
30
+
31
+ def __kind__
32
+ @kind
33
+ end
34
+
35
+ def remove
36
+ __flag__ :remove
37
+ end
38
+
39
+ def keep
40
+ __flag__ :keep
41
+ end
42
+
43
+ def method_missing(symbol, *args, &block)
44
+ __delegate__.send(symbol, *args, &block)
45
+ end
46
+
47
+ protected
48
+
49
+ def __flag__(sym)
50
+ other_sym = (sym == :keep ? :remove : :keep)
51
+ raise FilterError, "Cannot both keep and remove the same #{@kind}", __caller__ if @keep_or_remove == other_sym
52
+ @keep_or_remove = sym
53
+ throw :bailout if sym == :remove
54
+ end
55
+
56
+ def __caller__
57
+ f = __FILE__
58
+ stack = caller
59
+ stack.each_with_index do |s, i|
60
+ return stack[i..-1] unless s =~ Regexp.new("^#{Regexp.escape(f)}:")
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ class CalendarWrapper < ResourceWrapper
67
+ RESOURCE_TYPES = %w{events freebusys journals todos}
68
+
69
+ def initialize(calendar)
70
+ super(calendar, 'calendar')
71
+ end
72
+
73
+ def remove(*args)
74
+ if args.empty?
75
+ super
76
+ else
77
+ __remove_elements__ :remove, args
78
+ end
79
+ end
80
+
81
+ def keep(*args)
82
+ if args.empty?
83
+ super
84
+ else
85
+ __remove_elements__ :keep, RESOURCE_TYPES.map{|t|t.to_sym} - args
86
+ end
87
+ end
88
+
89
+ def method_missing(symbol, *args, &block)
90
+ if symbol.to_s =~ /^filter_(.*)$/ && RESOURCE_TYPES.include?($1)
91
+ __filter_resource__($1, *args, &block)
92
+ else
93
+ super(symbol, *args, &block)
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def __remove_elements__(sym, to_remove)
100
+ other_sym = (sym == :keep ? :remove : :keep)
101
+ raise FilterError, "Cannot call both keep and remove for elements in the same calendar", __caller__ if @keep_or_remove_elements == other_sym
102
+ @keep_or_remove_elements = sym
103
+ to_remove.each{|sym| __delegate__.send(sym).clear}
104
+ end
105
+
106
+ def __filter_resource__(resource_type)
107
+ resources = __delegate__.send(resource_type.to_sym)
108
+ return resources unless block_given?
109
+ actions = resources.map do |res|
110
+ wrapper = CalFilter.wrap_resource(res, resource_type)
111
+ catch(:bailout) do
112
+ yield wrapper
113
+ end
114
+ wrapper.__action__
115
+ end
116
+ __delegate__.send("#{resource_type}=".to_sym, CalFilter::keep_or_delete_items(resources, resource_type, actions))
117
+ end
118
+
119
+ end
120
+
121
+ def self.wrap_calendar(cal)
122
+ CalendarWrapper.new(cal)
123
+ end
124
+
125
+ def self.wrap_resource(res, plural_resource_type)
126
+ # This works with the particular resource names in Icalendar:
127
+ singular_resource_type = plural_resource_type.sub(/s$/, '')
128
+ ResourceWrapper.new(res, singular_resource_type)
129
+ end
130
+
131
+ def self.keep_or_delete_items(items, type, actions)
132
+ if actions.include?(:keep) && actions.include?(:remove)
133
+ raise CalFilter::FilterError, "Cannot both keep and remove #{type} in the same group.", caller(2)
134
+ end
135
+ keep_action = (actions.include?(:keep) ? :keep : :default)
136
+ kept_items = []
137
+ items.each_with_index{|item, i| kept_items << item if actions[i] == keep_action}
138
+ kept_items
139
+ end
140
+
141
+ end
142
+
143
+ def filter_calendars_at(url, &block)
144
+ filter_calendars(open(url, 'r'), &block)
145
+ end
146
+
147
+ def filter_calendars(ics_data, &block)
148
+ filter_icalendars(Icalendar.parse(ics_data), &block)
149
+ end
150
+
151
+ def filter_icalendars(cals)
152
+ return cals unless block_given?
153
+ actions = cals.map do |cal|
154
+ wrapper = CalFilter.wrap_calendar(cal)
155
+ catch(:bailout) do
156
+ yield wrapper
157
+ end
158
+ wrapper.__action__
159
+ end
160
+ new_cals = CalFilter::keep_or_delete_items(cals, 'calendars', actions)
161
+ os = CalFilter.output_stream
162
+ unless os.nil?
163
+ new_cals.each{|cal| os.puts cal.to_ical}
164
+ end
165
+ new_cals
166
+ end
@@ -0,0 +1,102 @@
1
+ require 'rubygems'
2
+ require 'test/spec'
3
+ require 'mocha'
4
+
5
+ require 'calfilter'
6
+
7
+ describe "filtering calendars" do
8
+ it "should pass things straight through" do
9
+ expected_cals = [1, 2, 3]
10
+ actual_cals = filter_icalendars(expected_cals.dup)
11
+ assert_equal expected_cals.size, actual_cals.size
12
+ end
13
+
14
+ it "should delete a calendar when told" do
15
+ cals = filter_icalendars([1, 2, 3]){|cal| cal.remove if cal.__delegate__ == 2}
16
+ assert_equal [1, 3], cals
17
+ end
18
+
19
+ it "should keep a calendar when told" do
20
+ cals = filter_icalendars([1, 2, 3]){|cal| cal.keep if cal.__delegate__ == 2}
21
+ assert_equal [2], cals
22
+ end
23
+
24
+ it "should delete parts of a calendar when told" do
25
+ cals = filter_icalendars(%w{1 2 3}) do |cal|
26
+ events = mock(:clear => nil)
27
+ cal.__delegate__.stubs(:events).returns(events)
28
+ cal.remove(:events)
29
+ end
30
+ end
31
+
32
+ it "should keep parts of a calendar when told" do
33
+ cals = filter_icalendars(%w{1 2 3}) do |cal|
34
+ events = mock(:clear => nil)
35
+ cal.__delegate__.stubs(:events).returns(events)
36
+ cal.keep(:freebusys, :journals, :todos)
37
+ end
38
+ end
39
+
40
+ it "should complain if we both keep and remove a calendar" do
41
+ assert_raise(CalFilter::FilterError) do
42
+ cals = filter_icalendars([1]){|cal| cal.keep; cal.remove}
43
+ end
44
+ end
45
+
46
+ it "should complain if we keep one calendar and remove another" do
47
+ assert_raises(CalFilter::FilterError) do
48
+ cals = filter_icalendars([1, 2, 3]) do |cal|
49
+ case cal.__delegate__
50
+ when 1: cal.keep;
51
+ when 2: cal.remove;
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ it "should delegate unknown methods to the Calendar objects" do
58
+ results = []
59
+ cals = filter_icalendars([0, 1]){|cal| results << cal.zero?}
60
+ assert_equal [true, false], results
61
+ end
62
+
63
+ end
64
+
65
+ describe "filtering resources" do
66
+ before do
67
+ @cal = Icalendar::Calendar.new
68
+ @cal.event do
69
+ dtstart Date.new(2005, 04, 29)
70
+ dtend Date.new(2005, 04, 28)
71
+ summary "Meeting with the man."
72
+ description "Have a long lunch meeting and decide nothing..."
73
+ klass "PRIVATE"
74
+ end
75
+ @cal.event do
76
+ dtstart Date.new(2006, 04, 29)
77
+ dtend Date.new(2005, 04, 28)
78
+ summary "Project review"
79
+ description "Find a bunch of stuff to complain about and get complaining."
80
+ klass "PUBLIC"
81
+ end
82
+ end
83
+
84
+ xit "should leave resources alone if no block specified" do
85
+ filter_icalendars([@cal]) do |cal|
86
+ cal.__delegate__.expects(:events).returns(@cal.events)
87
+ cal.filter_events
88
+ end
89
+ end
90
+
91
+ xit "should delete a resource when told"
92
+ #@calw = CalFilter.wrap_resource(@cal.events, 'events')
93
+
94
+ xit "should keep a resource when told"
95
+
96
+ xit "should complain if we both keep and remove a resource"
97
+
98
+ xit "should complain if we keep one resource and remove another"
99
+
100
+ xit "should delegate unknown methods to the resource objects"
101
+
102
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'test/spec'
3
+ require 'mocha'
4
+
5
+ require 'calfilter'
6
+
7
+ def at_exit(&block)
8
+ $at_exit_block = block
9
+ end
10
+
11
+ describe "calfilter cgi scripts" do
12
+ before(:all) do
13
+ CalFilter::CGI = ''
14
+ require 'calfilter/cgi'
15
+ end
16
+
17
+ it "should initialize a CGIWrapper object" do
18
+ assert_not_nil CalFilter::CGIWrapper
19
+ end
20
+
21
+ it "should set CalFilter's output stream" do
22
+ assert_not_nil CalFilter::CGIWRAPPER.output_stream
23
+ assert_equal CalFilter::CGIWRAPPER.output_stream, CalFilter.output_stream
24
+ end
25
+
26
+ it "should finish the CGI on process exit" do
27
+ CalFilter::CGIWRAPPER.expects(:finish)
28
+ $at_exit_block.call
29
+ end
30
+
31
+ it "should write proper output when finishing" do
32
+ CalFilter::CGI.expects(:out).with('text/calendar; charset=utf-8')
33
+ CalFilter::CGIWRAPPER.finish
34
+ end
35
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: calfilter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Glenn Vanderburg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDRDCCAiygAwIBAgIBADANBgkqhkiG9w0BAQUFADBIMQ4wDAYDVQQDDAVnbGVu
14
+ bjEeMBwGCgmSJomT8ixkARkWDnRoaW5rcmVsZXZhbmNlMRYwFAYKCZImiZPyLGQB
15
+ GRYGY29tZ2x2MB4XDTA4MDMwNDIwNDIzNFoXDTA5MDMwNDIwNDIzNFowSDEOMAwG
16
+ A1UEAwwFZ2xlbm4xHjAcBgoJkiaJk/IsZAEZFg50aGlua3JlbGV2YW5jZTEWMBQG
17
+ CgmSJomT8ixkARkWBmNvbWdsdjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
18
+ ggEBALXdD5Y00ZMPO/dL8sj1IcfomhezB+9rsO/FoCnR9TJyaOGPBAJBEfyA3SkP
19
+ hpbCkkUDpNlRR2sQHrtqBJovpw2oX6zakrG8PnDQQPU1oo0tWkiDtPFQc9lgDJOA
20
+ S/nQiS2D9rusOmpj84KpC6DUN80M+ap9z04Xadobuv7Cer/dTOfs4skUBiN/aonA
21
+ qtaRNOlOPGyPGiCa8tUQd53fVml2hSKIgt/+HfBHHWZkHs9UQRsqd7GICFi5R24i
22
+ ZNXbaRzw4637eCcjBhlEes9KMJOwayZ1KXHWzOdSzRJUI6nT4AQjw3GCyuXBmCY0
23
+ LFrbGOLYuqfiv/zr+Z+qqyI3Ul0CAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8E
24
+ BAMCBLAwHQYDVR0OBBYEFOsd6jtw384ogcRecKkFdFpm9SQkMA0GCSqGSIb3DQEB
25
+ BQUAA4IBAQBokYH6Pe63OU1O9t4z6jEom2NDieUlLWD9PTshI4UXUjxd83mjmg/Y
26
+ Cer/yqFHqQEW6BLNRmH6l2lw2Rb8CZdlh7K8Q8F/94M+actty2UvMgTyVEtOnyh/
27
+ QU8nqEil5vl7ppCwDeSF32cebsQoimkgAamOxVZVe5ipP1+UIO+Eu92Jt5k0hrnd
28
+ 1Fz3W+wrffm45P2X/6Z36dXgZKn8dlHuYtsqgpQxBC92oE7UqHXvwAu3FFe+1EV7
29
+ LrV1pr2EyieDotJOooYNz/E+9ZeL14MBhDOVmPr4KHTTK4VwdxFtAKu2QK/NfAAl
30
+ zrNa6ECLh1VS/pV78rYbAO1ZXsg3l7bY
31
+ -----END CERTIFICATE-----
32
+
33
+ date: 2008-05-02 00:00:00 -05:00
34
+ default_executable:
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: icalendar
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: "calfilter is a small library to assist in writing filtering programs for icalendar files or streams. It can be used for various purposes, including: * removing items from icalendar feeds that are not interesting to you * removing private information from your own calendar before publishing it to others * reformatting a provided calendar to highlight particular information"
46
+ email:
47
+ - glenn@thinkrelevance.com
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ extra_rdoc_files:
53
+ - History.txt
54
+ - Manifest.txt
55
+ - README.txt
56
+ files:
57
+ - History.txt
58
+ - Manifest.txt
59
+ - README.txt
60
+ - Rakefile
61
+ - example.rb
62
+ - lib/calfilter.rb
63
+ - lib/calfilter/cgi.rb
64
+ - lib/calfilter/tripit.rb
65
+ - test/test_calfilter.rb
66
+ - test/test_calfilter_cgi.rb
67
+ has_rdoc: true
68
+ homepage: http://opensource.thinkrelevance.com/wiki/calfilter
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --main
72
+ - README.txt
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project: thinkrelevance
90
+ rubygems_version: 1.0.1
91
+ signing_key:
92
+ specification_version: 2
93
+ summary: calfilter is a small library to assist in writing filtering programs for icalendar files or streams
94
+ test_files:
95
+ - test/test_calfilter.rb
96
+ - test/test_calfilter_cgi.rb
metadata.gz.sig ADDED
@@ -0,0 +1 @@
1
+ u\�p S`S"y�+�a7��8i���0ԏ��B�(l3_�8�Ƚ��2p;���7v���ln�s;��ֵ;�l=�}���o�lށ�F%�̭XB�������d۾�;���!>�Yʿ�f;��1 � x��&�=.�EJ�W6@�#�+�L��S��y.�̿��q_T�ʙ2Ѭ$��\��\U�$�J�>>�����l|~@����vg��k-X$Z�i-��S ��;��j3�6=h