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.
# File lib/icalendar/component.rb, line 46 def initialize(name) @name = name @components = Hash.new([]) @properties = {} @@logger.info("New #{@name[1,@name.size].capitalize}...") end
Make it protected so we can monitor usage…
# File lib/icalendar/component.rb, line 216 def Component.ical_component(*syms) hash_accessor :@components, *syms end
Define a set of methods defining a new property, which supports multiple values for the same property name.
# File lib/icalendar/component.rb, line 234 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
# File lib/icalendar/component.rb, line 251 def Component.ical_multiline_property(property, singular, plural) @@multiline_properties["#{property}"] = true ical_multi_property(property, singular, plural) end
Define a set of methods supporting a new property
# File lib/icalendar/component.rb, line 221 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
# File lib/icalendar/component.rb, line 259 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
# File lib/icalendar/component.rb, line 368 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
# File lib/icalendar/component.rb, line 315 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
# File lib/icalendar/component.rb, line 355 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
# File lib/icalendar/component.rb, line 399 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
# File lib/icalendar/component.rb, line 332 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
# File lib/icalendar/component.rb, line 300 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
# File lib/icalendar/component.rb, line 286 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
Add a sub-component to the current component object.
# File lib/icalendar/component.rb, line 55 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
# File lib/icalendar/component.rb, line 168 def add_sliced_text(add_to,escaped) escaped = escaped.split('') # split is unicdoe-aware when `$KCODE = 'u'` add_to << escaped.slice!(0,MAX_LINE_LENGTH).join << "\r\n " while escaped.length != 0 # shift(MAX_LINE_LENGTH) does not work with ruby 1.8.6 add_to.gsub!(/ *$/, '') 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.
# File lib/icalendar/component.rb, line 201 def custom_property(name, value) @properties[name] = value end
# File lib/icalendar/component.rb, line 162 def escape_chars(value) v = value.gsub("\\", "\\\\").gsub("\r\n", "\n").gsub("\r", "\n").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;") return v # return value end
# File lib/icalendar/component.rb, line 205 def multi_property?(name) @@multi_properties.has_key?(name.downcase) end
# File lib/icalendar/component.rb, line 209 def multiline_property?(name) @@multiline_properties.has_key?(name.downcase) end
Used to generate unique component ids
# File lib/icalendar/component.rb, line 98 def new_uid "#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}" end
Print this icalendar component
# File lib/icalendar/component.rb, line 114 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 the parameters for a specific property
# File lib/icalendar/component.rb, line 175 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
# File lib/icalendar/component.rb, line 128 def print_properties s = "" @properties.sort.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}" value = escape_chars(value) unless ["rrule", "categories"].include?(key) add_sliced_text(s, prelude + value) else prelude = "#{key.gsub(/_/, '-').upcase}" val.each do |v| params = print_parameters(v) value = ":#{v.to_ical}" value = escape_chars(value) unless key == "rrule" add_sliced_text(s, prelude + params + value) end end end s end
# File lib/icalendar/component.rb, line 77 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
# File lib/icalendar/component.rb, line 436 def respond_to?(method_name) if method_name.to_s.downcase =~ /x_.*/ true else super end end
Output in the icalendar format
# File lib/icalendar/component.rb, line 103 def to_ical print_component do s = "" @components.each do |key, comps| comps.each { |component| s << component.to_ical } end s end end
# File lib/icalendar/component.rb, line 415 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