ical_builder 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Marshall Roch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ = ical_builder
2
+
3
+ Description goes here.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Marshall Roch. See LICENSE for details.
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ical_builder"
8
+ gem.summary = %Q{An iCalendar generator in Ruby, modelled after Jim Weirich's Builder}
9
+ gem.email = "marshall@mroch.com"
10
+ gem.homepage = "http://github.com/mroch/ical_builder"
11
+ gem.authors = ["Marshall Roch"]
12
+ gem.add_dependency 'blankslate'
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ require 'rake/rdoctask'
19
+ Rake::RDocTask.new do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'ical_builder'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README*')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/*_test.rb'
31
+ test.verbose = false
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/*_test.rb'
39
+ test.verbose = true
40
+ end
41
+ rescue LoadError
42
+ task :rcov do
43
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
44
+ end
45
+ end
46
+
47
+
48
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 2
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ical_builder}
8
+ s.version = "0.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Marshall Roch"]
12
+ s.date = %q{2011-03-28}
13
+ s.email = %q{marshall@mroch.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION.yml",
23
+ "ical_builder.gemspec",
24
+ "init.rb",
25
+ "lib/ical_builder.rb",
26
+ "lib/ical_template.rb",
27
+ "rails/init.rb",
28
+ "test/ical_builder_test.rb",
29
+ "test/test_helper.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/mroch/ical_builder}
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.6.2}
34
+ s.summary = %q{An iCalendar generator in Ruby, modelled after Jim Weirich's Builder}
35
+ s.test_files = [
36
+ "test/ical_builder_test.rb",
37
+ "test/test_helper.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ s.specification_version = 3
42
+
43
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
44
+ s.add_runtime_dependency(%q<blankslate>, [">= 0"])
45
+ else
46
+ s.add_dependency(%q<blankslate>, [">= 0"])
47
+ end
48
+ else
49
+ s.add_dependency(%q<blankslate>, [">= 0"])
50
+ end
51
+ end
52
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'ical_builder'
2
+ require 'ical_template'
@@ -0,0 +1,94 @@
1
+ require 'blankslate'
2
+
3
+ module Builder
4
+ class Ical < BlankSlate
5
+ def initialize(options = {})
6
+ @target = options[:target] || ''
7
+ end
8
+
9
+ def to_s
10
+ __fold(@target.to_s)
11
+ end
12
+
13
+ def <<(str)
14
+ @target << str
15
+ end
16
+
17
+ # RRULE is a special case since it needs to take a hash, but FREQ must
18
+ # always be first.
19
+ def rrule(*args)
20
+ if args[0].is_a?(Hash)
21
+ freq = __value({ :freq => args[0].delete(:freq) })
22
+ others = args[0].empty? ? nil : __value(args[0])
23
+ args[0] = [freq, others].compact.join(';')
24
+ end
25
+ method_missing(:rrule, *args)
26
+ end
27
+
28
+ def method_missing(sym, *args, &block)
29
+ value = args.shift
30
+ if block
31
+ @target << "BEGIN:#{__property(sym)}\r\n"
32
+ block.call(self)
33
+ @target << "END:#{__property(sym)}\r\n"
34
+ else
35
+ @target << "#{__property(sym)}#{__parameters(args)}:#{__value(value)}\r\n"
36
+ end
37
+ end
38
+
39
+ private
40
+ def __property(sym)
41
+ sym.to_s.upcase.gsub(/_/, '-')
42
+ end
43
+
44
+ # 4.2 Property Parameters
45
+ def __parameters(args)
46
+ return '' if args.nil? || args.empty?
47
+
48
+ ';' + args.collect { |arg|
49
+ case arg
50
+ when Hash
51
+ arg.collect { |k, v| "#{__property(k)}=#{__value(v)}" }.sort.join(';')
52
+ else
53
+ arg.to_s
54
+ end
55
+ }.join(';')
56
+ end
57
+
58
+ # 4.3 Property Value Data Types
59
+ def __value(val)
60
+ case val
61
+ when Array
62
+ # 4.1.1
63
+ val.collect { |v| __value(v) }.join(',')
64
+ when Hash
65
+ # 4.1.1
66
+ val.collect { |k, v| "#{__property(k)}=#{__value(v)}" }.sort.join(';')
67
+ when true, false
68
+ # 4.3.2
69
+ val.to_s.upcase
70
+ when Date
71
+ # 4.3.4
72
+ val.strftime('%Y%m%d')
73
+ when Time
74
+ # 4.3.5
75
+ val.strftime('%Y%m%dT%H%M%S' + (val.utc? ? 'Z' : ''))
76
+ else
77
+ val.to_s
78
+ end
79
+ end
80
+
81
+ # 4.1 Content Lines
82
+ def __fold(str)
83
+ str.split("\r\n").collect { |line| __fold_line(line) }.join("\r\n") + "\r\n"
84
+ end
85
+
86
+ def __fold_line(line)
87
+ if line.length > 75
88
+ line[0..74] + "\r\n" + __fold_line(" " + line[75..-1])
89
+ else
90
+ line
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,17 @@
1
+ module Builder
2
+ class IcalPlugin < ActionView::TemplateHandler
3
+ include ActionView::TemplateHandlers::Compilable if defined?(ActionView::TemplateHandlers::Compilable)
4
+
5
+ def compile(template)
6
+ "_set_controller_content_type(Mime::ICS);" +
7
+ "cal = ::Builder::Ical.new;" +
8
+ "self.output_buffer = cal.to_s;" +
9
+ template.source +
10
+ ";cal.to_s;"
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+ ActionView::Template.register_template_handler(:ical, Builder::IcalPlugin)
17
+ ActionView::Template.exempt_from_layout(:ical)
@@ -0,0 +1 @@
1
+ Kernel.load File.join(File.dirname(__FILE__), '..', 'init.rb')
@@ -0,0 +1,140 @@
1
+ require 'test_helper'
2
+
3
+ class IcalBuilderTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @builder = Builder::Ical.new
7
+ end
8
+
9
+ should "handle boolean property values (4.3.2)" do
10
+ @builder.gibberish(true)
11
+ assert_equal "GIBBERISH:TRUE\r\n", @builder.to_s
12
+ end
13
+
14
+ should "handle date property values (4.3.4)" do
15
+ @builder.gibberish(Date.new(2009, 9, 11))
16
+ assert_equal "GIBBERISH:20090911\r\n", @builder.to_s
17
+ end
18
+
19
+ should "handle date-time property values (4.3.5)" do
20
+ # Local time after 12pm
21
+ @builder.gibberish(Time.local(2009, 9, 11, 13, 45, 22))
22
+ assert_equal "GIBBERISH:20090911T134522\r\n", @builder.to_s
23
+
24
+ # Local time before 12pm
25
+ @builder = Builder::Ical.new
26
+ @builder.gibberish(Time.local(2009, 9, 11, 1, 45, 22))
27
+ assert_equal "GIBBERISH:20090911T014522\r\n", @builder.to_s
28
+
29
+ # UTC after 12pm
30
+ @builder = Builder::Ical.new
31
+ @builder.gibberish(Time.utc(2009, 9, 11, 13, 45, 22))
32
+ assert_equal "GIBBERISH:20090911T134522Z\r\n", @builder.to_s
33
+
34
+ # UTC before 12pm
35
+ @builder = Builder::Ical.new
36
+ @builder.gibberish(Time.utc(2009, 9, 11, 1, 45, 22))
37
+ assert_equal "GIBBERISH:20090911T014522Z\r\n", @builder.to_s
38
+ end
39
+
40
+ should "handle lists of values (4.1.1)" do
41
+ @builder.gibberish(['foo', 'bar'])
42
+ assert_equal "GIBBERISH:foo,bar\r\n", @builder.to_s
43
+ end
44
+
45
+ should "handle lists of values with only one item (4.1.1)" do
46
+ @builder.gibberish(['foo'])
47
+ assert_equal "GIBBERISH:foo\r\n", @builder.to_s
48
+ end
49
+
50
+ should "allow newlines (4.3.11)" do
51
+ @builder.text("Foo bar\nBaz bliffl")
52
+ assert_equal "TEXT:Foo bar\nBaz bliffl\r\n", @builder.to_s
53
+ end
54
+
55
+ should "handle values with multiple parts (4.1.1)" do
56
+ @builder.gibberish({ :foo => 'bar', :bymonth => 11, :byday => '1SU' })
57
+ assert_equal "GIBBERISH:BYDAY=1SU;BYMONTH=11;FOO=bar\r\n", @builder.to_s
58
+ end
59
+
60
+ should "handle values and properties with multiple values" do
61
+ @builder.gibberish({ :freq => 'YEARLY', :bymonth => 11, :byday => '1SU' }, { :foo => 'bar', :baz => 'bliffl' })
62
+ assert_equal "GIBBERISH;BAZ=bliffl;FOO=bar:BYDAY=1SU;BYMONTH=11;FREQ=YEARLY\r\n", @builder.to_s
63
+ end
64
+
65
+ should "format parameters without double-quotes correctly" do
66
+ @builder.organizer('MAILTO:jsmith@host.com', :cn => 'John Smith')
67
+ assert_equal "ORGANIZER;CN=John Smith:MAILTO:jsmith@host.com\r\n", @builder.to_s
68
+ end
69
+
70
+ should "format parameters with double-quotes correctly" do
71
+ @builder.organizer('MAILTO:jsmith@host.com', :cn => '"John Smith"')
72
+ assert_equal "ORGANIZER;CN=\"John Smith\":MAILTO:jsmith@host.com\r\n", @builder.to_s
73
+ end
74
+
75
+ should "format parameters with dashes correctly" do
76
+ @builder.attendee('MAILTO: jdoe@host.com', :delegated_from => '"MAILTO:jsmith@host.com"')
77
+ assert_equal \
78
+ "ATTENDEE;DELEGATED-FROM=\"MAILTO:jsmith@host.com\":MAILTO: jdoe@host.com\r\n",
79
+ @builder.to_s
80
+ end
81
+
82
+ should "format parameters with multiple values correctly" do
83
+ @builder.attendee(
84
+ 'MAILTO:janedoe@host.com',
85
+ :member => ['"MAILTO:projectA@host.com"', '"MAILTO:projectB@host.com"']
86
+ )
87
+ assert_equal \
88
+ "ATTENDEE;MEMBER=\"MAILTO:projectA@host.com\",\"MAILTO:projectB@host.com\":MAILT\r\n O:janedoe@host.com\r\n",
89
+ @builder.to_s
90
+ end
91
+
92
+ should "append arbitrary text" do
93
+ @builder.foo("bar")
94
+ @builder << "ARBITRARY-PROPERTY:\"Lorem ipsum dolor sit amet\"\r\n"
95
+ assert_equal "FOO:bar\r\nARBITRARY-PROPERTY:\"Lorem ipsum dolor sit amet\"\r\n", @builder.to_s
96
+ end
97
+
98
+ should "not fold lines less than 75 characters long" do
99
+ @builder.text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
100
+ assert_equal "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r\n", @builder.to_s
101
+ end
102
+
103
+ should "not fold lines exactly 75 characters long" do
104
+ @builder.text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam.")
105
+ assert_equal "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam.\r\n", @builder.to_s
106
+ end
107
+
108
+ should "not fold lines exactly 75 characters long even if they end in whitespace" do
109
+ @builder.text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam ")
110
+ assert_equal "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam \r\n", @builder.to_s
111
+ end
112
+
113
+ should "fold lines longer than 75 characters" do
114
+ @builder.description("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eget elit tellus. In hac habitasse platea dictumst. Vestibulum tincidunt velit id erat interdum id tristique diam blandit. Praesent nullam.")
115
+ assert_equal \
116
+ "DESCRIPTION:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eg\r\n et elit tellus. In hac habitasse platea dictumst. Vestibulum tincidunt vel\r\n it id erat interdum id tristique diam blandit. Praesent nullam.\r\n",
117
+ @builder.to_s
118
+ end
119
+
120
+ should "fold lines longer than 75 characters even if only whitespace" do
121
+ @builder.text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam. ")
122
+ assert_equal "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nullam.\r\n \r\n", @builder.to_s
123
+ end
124
+
125
+ should "fold arbitrary text" do
126
+ @builder << "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eget elit tellus. In hac habitasse platea dictumst.\r\n"
127
+ assert_equal "TEXT:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eget elit\r\n tellus. In hac habitasse platea dictumst.\r\n", @builder.to_s
128
+ end
129
+
130
+ should "always place FREQ first in RRULE values" do
131
+ @builder.rrule({ :freq => 'YEARLY', :interval => 1, :byday => '1SU' })
132
+ assert_equal "RRULE:FREQ=YEARLY;BYDAY=1SU;INTERVAL=1\r\n", @builder.to_s
133
+ end
134
+
135
+ should "format RRULEs properly even with only FREQ" do
136
+ @builder.rrule({ :freq => 'YEARLY' })
137
+ assert_equal "RRULE:FREQ=YEARLY\r\n", @builder.to_s
138
+ end
139
+
140
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'ical_builder'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ical_builder
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 1
10
+ version: 0.2.1
11
+ platform: ruby
12
+ authors:
13
+ - Marshall Roch
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-28 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: blankslate
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description:
36
+ email: marshall@mroch.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - LICENSE
46
+ - README.rdoc
47
+ - Rakefile
48
+ - VERSION.yml
49
+ - ical_builder.gemspec
50
+ - init.rb
51
+ - lib/ical_builder.rb
52
+ - lib/ical_template.rb
53
+ - rails/init.rb
54
+ - test/ical_builder_test.rb
55
+ - test/test_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/mroch/ical_builder
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: An iCalendar generator in Ruby, modelled after Jim Weirich's Builder
90
+ test_files:
91
+ - test/ical_builder_test.rb
92
+ - test/test_helper.rb