require 'forwardable' require 'rss/rss' module RSS module Maker module Base def self.append_features(klass) super klass.module_eval(<<-EOC, __FILE__, __LINE__) OTHER_ELEMENTS = [] NEED_INITIALIZE_VARIABLES = [] def self.inherited(subclass) subclass.const_set("OTHER_ELEMENTS", []) subclass.const_set("NEED_INITIALIZE_VARIABLES", []) subclass.module_eval(<<-EOEOC, __FILE__, __LINE__) def self.other_elements OTHER_ELEMENTS + super end def self.need_initialize_variables NEED_INITIALIZE_VARIABLES + super end EOEOC end def self.add_other_element(variable_name) OTHER_ELEMENTS << variable_name end def self.other_elements OTHER_ELEMENTS end def self.add_need_initialize_variable(variable_name, init_value="nil") NEED_INITIALIZE_VARIABLES << [variable_name, init_value] end def self.need_initialize_variables NEED_INITIALIZE_VARIABLES end def self.def_array_element(name) include Enumerable extend Forwardable def_delegators("@\#{name}", :<<, :[], :[]=, :first, :last) def_delegators("@\#{name}", :push, :pop, :shift, :unshift) def_delegators("@\#{name}", :each, :size) add_need_initialize_variable(name, "[]") end EOC end def initialize(maker) @maker = maker initialize_variables end def have_required_values? true end private def initialize_variables self.class.need_initialize_variables.each do |variable_name, init_value| instance_eval("@#{variable_name} = #{init_value}", __FILE__, __LINE__) end end def setup_other_elements(rss) self.class.other_elements.each do |element| __send__("setup_#{element}", rss, current_element(rss)) end end def current_element(rss) rss end def setup_values(target) set = false if have_required_values? variables.each do |var| setter = "#{var}=" if target.respond_to?(setter) value = self.__send__(var) if value target.__send__(setter, value) set = true end end end end set end def variables self.class.need_initialize_variables.find_all do |name, init| "nil" == init end.collect do |name, init| name end end def variable_is_set? variables.find {|var| !__send__(var).nil?} end def not_set_required_variables required_variable_names.find_all do |var| __send__(var).nil? end end def required_variables_are_set? required_variable_names.each do |var| return false if __send__(var).nil? end true end end class RSSBase include Base class << self def make(&block) new.make(&block) end end %w(xml_stylesheets channel image items textinput).each do |element| attr_reader element add_need_initialize_variable(element, "make_#{element}") module_eval(<<-EOC, __FILE__, __LINE__) private def setup_#{element}(rss) @#{element}.to_rss(rss) end def make_#{element} self.class::#{Utils.to_class_name(element)}.new(self) end EOC end attr_reader :rss_version attr_accessor :version, :encoding, :standalone def initialize(rss_version) super(self) @rss_version = rss_version @version = "1.0" @encoding = "UTF-8" @standalone = nil end def make if block_given? yield(self) to_rss else nil end end def to_rss rss = make_rss setup_xml_stylesheets(rss) setup_elements(rss) setup_other_elements(rss) if rss.channel rss else nil end end private remove_method :make_xml_stylesheets def make_xml_stylesheets XMLStyleSheets.new(self) end end class XMLStyleSheets include Base def_array_element("xml_stylesheets") def to_rss(rss) @xml_stylesheets.each do |xss| xss.to_rss(rss) end end def new_xml_stylesheet xss = XMLStyleSheet.new(@maker) @xml_stylesheets << xss xss end class XMLStyleSheet include Base ::RSS::XMLStyleSheet::ATTRIBUTES.each do |attribute| attr_accessor attribute add_need_initialize_variable(attribute) end def to_rss(rss) xss = ::RSS::XMLStyleSheet.new guess_type_if_need(xss) set = setup_values(xss) if set rss.xml_stylesheets << xss end end def have_required_values? @href and @type end private def guess_type_if_need(xss) if @type.nil? xss.href = @href @type = xss.type end end end end class ChannelBase include Base %w(cloud categories skipDays skipHours).each do |element| attr_reader element add_other_element(element) add_need_initialize_variable(element, "make_#{element}") module_eval(<<-EOC, __FILE__, __LINE__) private def setup_#{element}(rss, current) @#{element}.to_rss(rss, current) end def make_#{element} self.class::#{Utils.to_class_name(element)}.new(@maker) end EOC end %w(about title link description language copyright managingEditor webMaster rating docs date lastBuildDate generator ttl).each do |element| attr_accessor element add_need_initialize_variable(element) end alias_method(:pubDate, :date) alias_method(:pubDate=, :date=) def current_element(rss) rss.channel end class SkipDaysBase include Base def_array_element("days") def new_day day = self.class::Day.new(@maker) @days << day day end def current_element(rss) rss.channel.skipDays end class DayBase include Base %w(content).each do |element| attr_accessor element add_need_initialize_variable(element) end def current_element(rss) rss.channel.skipDays.last end end end class SkipHoursBase include Base def_array_element("hours") def new_hour hour = self.class::Hour.new(@maker) @hours << hour hour end def current_element(rss) rss.channel.skipHours end class HourBase include Base %w(content).each do |element| attr_accessor element add_need_initialize_variable(element) end def current_element(rss) rss.channel.skipHours.last end end end class CloudBase include Base %w(domain port path registerProcedure protocol).each do |element| attr_accessor element add_need_initialize_variable(element) end def current_element(rss) rss.channel.cloud end end class CategoriesBase include Base def_array_element("categories") def new_category category = self.class::Category.new(@maker) @categories << category category end class CategoryBase include Base %w(domain content).each do |element| attr_accessor element add_need_initialize_variable(element) end end end end class ImageBase include Base %w(title url width height description).each do |element| attr_accessor element add_need_initialize_variable(element) end def link @maker.channel.link end def current_element(rss) rss.image end end class ItemsBase include Base def_array_element("items") attr_accessor :do_sort, :max_size def initialize(maker) super @do_sort = false @max_size = -1 end def normalize sort_if_need[0..@max_size] end def current_element(rss) rss.items end def new_item item = self.class::Item.new(@maker) @items << item item end private def sort_if_need if @do_sort.respond_to?(:call) @items.sort do |x, y| @do_sort.call(x, y) end elsif @do_sort @items.sort do |x, y| y <=> x end else @items end end class ItemBase include Base %w(guid enclosure source categories).each do |element| attr_reader element add_other_element(element) add_need_initialize_variable(element, "make_#{element}") module_eval(<<-EOC, __FILE__, __LINE__) private def setup_#{element}(rss, current) @#{element}.to_rss(rss, current) end def make_#{element} self.class::#{Utils.to_class_name(element)}.new(@maker) end EOC end %w(title link description date author comments).each do |element| attr_accessor element add_need_initialize_variable(element) end alias_method(:pubDate, :date) alias_method(:pubDate=, :date=) def <=>(other) if date and other.date date <=> other.date elsif date 1 elsif other.date -1 else 0 end end def current_element(rss) rss.items.last end class GuidBase include Base %w(isPermaLink content).each do |element| attr_accessor element add_need_initialize_variable(element) end end class EnclosureBase include Base %w(url length type).each do |element| attr_accessor element add_need_initialize_variable(element) end end class SourceBase include Base %w(url content).each do |element| attr_accessor element add_need_initialize_variable(element) end end CategoriesBase = ChannelBase::CategoriesBase end end class TextinputBase include Base %w(title description name link).each do |element| attr_accessor element add_need_initialize_variable(element) end def current_element(rss) rss.textinput end end end end