ruby/lib/rss/maker/base.rb

527 строки
11 KiB
Ruby

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 = __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
if @max_size >= 0
sort_if_need[0...@max_size]
else
sort_if_need[0..@max_size]
end
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