You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

439 lines
13 KiB

=begin
Copyright (C) 2005 Jeff Rose
This library is free software; you can redistribute it and/or modify it
under the same terms as the ruby language itself, see the file COPYING for
details.
=end
module Icalendar
require 'socket'
MAX_LINE_LENGTH = 75
class Geo < Icalendar::Base
attr_accessor :latitude, :longitude
alias :lat :latitude
alias :long :longitude
def initialize(lat, long)
@lat = lat
@long = long
end
def to_ical
"#{@lat.to_ical};#{@long.to_ical}"
end
end
# The body of the iCalendar object consists of a sequence of calendar
# properties and one or more calendar components. The calendar
# properties are attributes that apply to the calendar as a whole. The
# calendar components are collections of properties that express a
# particular calendar semantic. For example, the calendar component can
# specify an Event, a Todo, a Journal entry, Timezone information, or
# Freebusy time information, or an Alarm.
class Component < Icalendar::Base
meta_include HashAttrs
attr_reader :name
attr_accessor :properties
@@multi_properties = {}
@@multiline_properties = {}
def initialize(name)
@name = name
@components = Hash.new([])
@properties = {}
@@logger.info("New #{@name[1,@name.size].capitalize}...")
end
# Add a sub-component to the current component object.
def add_component(component)
key = (component.class.to_s.downcase + 's').gsub('icalendar::', '').to_sym
unless @components.has_key? key
@components[key] = []
end
@components[key] << component
end
# Add a component to the calendar.
alias add add_component
# Add an event to the calendar.
alias add_event add_component
# Add a todo item to the calendar.
alias add_todo add_component
# Add a journal item to the calendar.
alias add_journal add_component
def remove_component(component)
key = (component.class.to_s.downcase + 's').gsub('icalendar::', '').to_sym
if @components.has_key? key
@components[key].delete(component)
end
end
# Remove a component from the calendar.
alias remove remove_component
# Remove an event from the calendar.
alias remove_event remove_component
# Remove a todo item from the calendar.
alias remove_todo remove_component
# Remove a journal item from the calendar.
alias remove_journal remove_component
# Used to generate unique component ids
def new_uid
"#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}"
end
# Output in the icalendar format
def to_ical
print_component do
s = ""
@components.each_value do |comps|
comps.each { |component| s << component.to_ical }
end
s
end
end
# Print this icalendar component
def print_component
# Begin a new component
"BEGIN:#{@name.upcase}\r\n" +
# Then the properties
print_properties +
# sub components
yield +
# End of this component
"END:#{@name.upcase}\r\n"
end
# Print this components properties
def print_properties
s = ""
@properties.each do |key,val|
# Take out underscore for property names that conflicted
# with built-in words.
if key =~ /ip_.*/
key = key[3..-1]
end
# Property name
unless multiline_property?(key)
prelude = "#{key.gsub(/_/, '-').upcase}" +
# Possible parameters
print_parameters(val)
# Property value
value = ":#{val.to_ical}"
escaped = prelude + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
s << escaped << "\r\n"
s.gsub!(/ *$/, '')
else
prelude = "#{key.gsub(/_/, '-').upcase}"
val.each do |v|
params = print_parameters(v)
value = ":#{v.to_ical}"
escaped = prelude + params + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
s << escaped << "\r\n"
s.gsub!(/ *$/, '')
end
end
end
s
end
# Print the parameters for a specific property
def print_parameters(val)
s = ""
return s unless val.respond_to?(:ical_params) and not val.ical_params.nil?
val.ical_params.each do |key,val|
s << ";#{key}"
val = [ val ] unless val.is_a?(Array)
# Possible parameter values
unless val.empty?
s << "="
sep = "" # First entry comes after = sign, but then we need commas
val.each do |pval|
if pval.respond_to? :to_ical
s << sep << pval.to_ical
sep = ","
end
end
end
end
s
end
# TODO: Look into the x-property, x-param stuff...
# This would really only be needed for subclassing to add additional
# properties to an application using the API.
def custom_property(name, value)
@properties[name] = value
end
def multi_property?(name)
@@multi_properties.has_key?(name.downcase)
end
def multiline_property?(name)
@@multiline_properties.has_key?(name.downcase)
end
# Make it protected so we can monitor usage...
protected
def Component.ical_component(*syms)
hash_accessor :@components, *syms
end
# Define a set of methods supporting a new property
def Component.ical_property(property, alias_name = nil, prop_name = nil)
property = "#{property}".strip.downcase
alias_name = "#{alias_name}".strip.downcase unless alias_name.nil?
# If a prop_name was given then we use that for the actual storage
property = "#{prop_name}".strip.downcase unless prop_name.nil?
generate_getter(property, alias_name)
generate_setter(property, alias_name)
generate_query(property, alias_name)
end
# Define a set of methods defining a new property, which
# supports multiple values for the same property name.
def Component.ical_multi_property(property, singular, plural)
property = "#{property}".strip.downcase.gsub(/-/, '_')
plural = "#{plural}".strip.downcase
# Set this key so the parser knows to use an array for
# storing this property type.
@@multi_properties["#{property}"] = true
generate_multi_getter(property, plural)
generate_multi_setter(property, plural)
generate_multi_query(property, plural)
generate_multi_adder(property, singular)
generate_multi_remover(property, singular)
end
# Define a set of methods defining a new property, which
# supports multiple values in multiple lines with same property name
def Component.ical_multiline_property(property, singular, plural)
@@multiline_properties["#{property}"] = true
ical_multi_property(property, singular, plural)
end
private
def Component.generate_getter(property, alias_name)
unless instance_methods.include? property
code = <<-code
def #{property}(val = nil, params = nil)
return @properties["#{property}"] if val.nil?
unless val.respond_to?(:to_ical)
raise(NotImplementedError, "Value of type (" + val.class.to_s + ") does not support to_ical method!")
end
unless params.nil?
# Extend with the parameter methods only if we have to...
unless val.respond_to?(:ical_params)
val.class.class_eval { attr_accessor :ical_params }
end
val.ical_params = params
end
@properties["#{property}"] = val
end
code
class_eval code, "component.rb", 219
alias_method("#{alias_name}", "#{property}") unless alias_name.nil?
end
end
def Component.generate_setter(property, alias_name)
setter = property + '='
unless instance_methods.include? setter
code = <<-code
def #{setter}(val)
#{property}(val)
end
code
class_eval code, "component.rb", 233
alias_method("#{alias_name}=", "#{property+'='}") unless alias_name.nil?
end
end
def Component.generate_query(property, alias_name)
query = "#{property}?"
unless instance_methods.include? query
code = <<-code
def #{query}
@properties.has_key?("#{property.downcase}")
end
code
class_eval code, "component.rb", 226
alias_method("#{alias_name}\?", "#{query}") unless alias_name.nil?
end
end
def Component.generate_multi_getter(property, plural)
# Getter for whole array
unless instance_methods.include? plural
code = <<-code
def #{plural}(a = nil)
if a.nil?
@properties["#{property}"] || []
else
self.#{plural}=(a)
end
end
code
class_eval code, "component.rb", 186
end
end
def Component.generate_multi_setter(property, plural)
# Setter for whole array
unless instance_methods.include? plural+'+'
code = <<-code
def #{plural}=(a)
if a.respond_to?(:to_ary)
a.to_ary.each do |val|
unless val.respond_to?(:to_ical)
raise(NotImplementedError, "Property values do not support to_ical method!")
end
end
@properties["#{property}"] = a.to_ary
else
raise ArgumentError, "#{plural} is a multi-property that must be an array! Use the add_[property] method to add single entries."
end
end
code
class_eval code, "component.rb", 198
end
end
def Component.generate_multi_query(property, plural)
# Query for any of these properties
unless instance_methods.include? plural+'?'
code = <<-code
def #{plural}?
@properties.has_key?("#{property}")
end
code
class_eval code, "component.rb", 210
end
end
def Component.generate_multi_adder(property, singular)
adder = "add_"+singular.to_s
# Add another item to this properties array
unless instance_methods.include? adder
code = <<-code
def #{adder}(val, params = {})
unless val.respond_to?(:to_ical)
raise(NotImplementedError, "Property value object does not support to_ical method!")
end
unless params.nil?
# Extend with the parameter methods only if we have to...
unless val.respond_to?(:ical_params)
val.class.class_eval { attr_accessor :ical_params }
end
val.ical_params = params
end
if @properties.has_key?("#{property}")
@properties["#{property}"] << val
else
@properties["#{property}"] = [val]
end
end
code
class_eval code, "component.rb", 289
alias_method("add_#{property.downcase}", "#{adder}")
end
end
def Component.generate_multi_remover(property, singular)
# Remove an item from this properties array
unless instance_methods.include? "remove_#{singular}"
code = <<-code
def remove_#{singular}(a)
if @properties.has_key?("#{property}")
@properties["#{property}"].delete(a)
end
end
code
class_eval code, "component.rb", 303
alias_method("remove_#{property.downcase}", "remove_#{singular}")
end
end
def method_missing(method_name, *args)
@@logger.debug("Inside method_missing...")
method_name = method_name.to_s.downcase
unless method_name =~ /x_.*/
raise NoMethodError, "Method Name: #{method_name}"
end
# x-properties are accessed with underscore but stored with a dash so
# they output correctly and we don't have to special case the
# output code, which would require checking every property.
if args.size > 0 # Its a setter
# Pull off the possible equals
@properties[method_name[/x_[^=]*/].gsub('x_', 'x-')] = args.first
else # Or its a getter
return @properties[method_name.gsub('x_', 'x-')]
end
end
public
def respond_to?(method_name)
unless method_name.to_s.downcase =~ /x_.*/
super
end
true
end
end # class Component
end