From 8640f16308dce0b013af0470b926956e676ccc1a Mon Sep 17 00:00:00 2001 From: matz Date: Wed, 28 Jan 2004 03:46:13 +0000 Subject: * lib/rss: rss library imported. [ruby-dev:22726] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5566 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rss/rss.rb | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 lib/rss/rss.rb (limited to 'lib/rss/rss.rb') diff --git a/lib/rss/rss.rb b/lib/rss/rss.rb new file mode 100644 index 0000000000..0acf7803e5 --- /dev/null +++ b/lib/rss/rss.rb @@ -0,0 +1,536 @@ +require "time" + +require "rss/utils" +require "rss/converter" + +module RSS + + VERSION = "0.0.7" + + class Error < StandardError; end + + class OverlappedPrefixError < Error + attr_reader :prefix + def initialize(prefix) + @prefix = prefix + end + end + + class InvalidRSSError < Error; end + + class MissingTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is missing in tag <#{parent}>") + end + end + + class TooMuchTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is too much in tag <#{parent}>") + end + end + + class MissingAttributeError < InvalidRSSError + attr_reader :tag, :attribute + def initialize(tag, attribute) + @tag, @attribute = tag, attribute + super("attribute <#{attribute}> is missing in tag <#{tag}>") + end + end + + class UnknownTagError < InvalidRSSError + attr_reader :tag, :uri + def initialize(tag, uri) + @tag, @uri = tag, uri + super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>") + end + end + + class NotExceptedTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is not expected in tag <#{parent}>") + end + end + + class NotAvailableValueError < InvalidRSSError + attr_reader :tag, :value + def initialize(tag, value) + @tag, @value = tag, value + super("value <#{value}> of tag <#{tag}> is not available.") + end + end + + class UnknownConversionMethodError < Error + attr_reader :to, :from + def initialize(to, from) + @to = from + @from = from + super("can't convert to #{to} from #{from}.") + end + end + # for backward compatibility + UnknownConvertMethod = UnknownConversionMethodError + + class ConversionError < Error + attr_reader :string, :to, :from + def initialize(string, to, from) + @string = string + @to = from + @from = from + super("can't convert #{@string} to #{to} from #{from}.") + end + end + + module BaseModel + + include Utils + + def install_have_child_element(name) + attr_accessor name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + "\#{indent}\#{@#{n}.to_s(convert)}" + else + '' + end +EOC + end + end + alias_method(:install_have_attribute_element, :install_have_child_element) + + def install_have_children_element(name, postfix="s") + def_children_accessor(name, postfix) + add_have_children_element(name) + install_element(name, postfix) do |n, elem_name| + <<-EOC + rv = '' + @#{n}.each do |x| + rv << "\#{indent}\#{x.to_s(convert)}" + end + rv +EOC + end + end + + def install_text_element(name) + self::ELEMENTS << name + attr_writer name + convert_attr_reader name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}) + if convert and @converter + rv << @converter.convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + end + + def install_date_element(name, type, disp_name=name) + self::ELEMENTS << name + + # accessor + convert_attr_reader name + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.kind_of?(Time) + @#{name} = new_value + else + if @do_validate + begin + @#{name} = Time.send('#{type}', new_value) + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + elsif /\\A\\s*\\z/ !~ new_value.to_s + @#{name} = Time.parse(new_value) + else + @#{name} = nil + end + end + + # Is it need? + if @#{name} + class << @#{name} + alias_method(:_to_s, :to_s) unless respond_to?(:_to_s) + alias_method(:to_s, :#{type}) + end + end + + end +EOC + + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}.#{type}) + if convert and @converter + rv << @converter.convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + + end + + private + def install_element(name, postfix="") + elem_name = name.sub('_', ':') + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}_element#{postfix}(convert=true, indent='') + #{yield(name, elem_name)} + end + private :#{name}_element#{postfix} +EOC + end + + def convert_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{attr} + if @converter + @converter.convert(@#{attr}) + else + @#{attr} + end + end +EOC + end + end + + def def_children_accessor(accessor_name, postfix="s") + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{accessor_name}#{postfix} + @#{accessor_name} + end + + def #{accessor_name}(*args) + if args.empty? + @#{accessor_name}.first + else + @#{accessor_name}.send("[]", *args) + end + end + + def #{accessor_name}=(*args) + if args.size == 1 + @#{accessor_name}.push(args[0]) + else + @#{accessor_name}.send("[]=", *args) + end + end + alias_method(:set_#{accessor_name}, :#{accessor_name}=) +EOC + end + + end + + URI = "http://purl.org/rss/1.0/" + + class Element + + extend BaseModel + include Utils + + class << self + + def inherited(klass) + klass.module_eval(<<-EOC) + public + + TAG_NAME = name.split('::').last.downcase + + + @@must_call_validators = {::RSS::URI => ''} + + def self.must_call_validators + @@must_call_validators + end + + def self.install_must_call_validator(prefix, uri) + @@must_call_validators[uri] = prefix + end + + @@model = [] + + def self.model + @@model + end + + def self.install_model(tag, occurs=nil) + if m = @@model.find {|t, o| t == tag} + m[1] = occurs + else + @@model << [tag, occurs] + end + end + + @@get_attributes = [] + + def self.get_attributes() + @@get_attributes + end + + def self.install_get_attribute(name, uri, required=true) + attr_writer name + convert_attr_reader name + @@get_attributes << [name, uri, required] + end + + @@have_content = false + + def self.content_setup + attr_writer :content + convert_attr_reader :content + @@have_content = true + end + + def self.have_content? + @@have_content + end + + @@have_children_elements = [] + + def self.have_children_elements + @@have_children_elements + end + + def self.add_have_children_element(variable_name) + @@have_children_elements << variable_name + end + + EOC + end + + def required_prefix + nil + end + + def required_uri + nil + end + + def install_ns(prefix, uri) + if self::NSPOOL.has_key?(prefix) + raise OverlappedPrefixError.new(prefix) + end + self::NSPOOL[prefix] = uri + end + + end + + attr_accessor :do_validate + + def initialize(do_validate=true) + @do_validate = do_validate + initialize_have_children_elements + end + + def tag_name + self.class::TAG_NAME + end + + def converter=(converter) + @converter = converter + children.each do |child| + child.converter = converter unless child.nil? + end + end + + def validate + validate_attribute + __validate + end + + def validate_for_stream(tags) + __validate(tags, false) + end + + private + def initialize_have_children_elements + self.class.have_children_elements.each do |variable_name| + instance_eval("@#{variable_name} = []") + end + end + + # not String class children. + def children + [] + end + + # default #validate() argument. + def _tags + [] + end + + def _attrs + [] + end + + def __validate(tags=_tags, recursive=true) + if recursive + children.compact.each do |child| + child.validate + end + end + must_call_validators = self.class::must_call_validators + tags = tag_filter(tags.dup) + p tags if $DEBUG + self.class::NSPOOL.each do |prefix, uri| + if tags.has_key?(uri) and !must_call_validators.has_key?(uri) + meth = "#{prefix}_validate" + send(meth, tags[uri]) if respond_to?(meth, true) + end + end + must_call_validators.each do |uri, prefix| + send("#{prefix}_validate", tags[uri]) + end + end + + def validate_attribute + _attrs.each do |a_name, required| + if required and send(a_name).nil? + raise MissingAttributeError.new(self.class::TAG_NAME, a_name) + end + end + end + + def other_element(convert, indent='') + rv = '' + private_methods.each do |meth| + if /\A([^_]+)_[^_]+_elements?\z/ =~ meth and + self.class::NSPOOL.has_key?($1) + res = send(meth, convert) + rv << "#{indent}#{res}\n" if /\A\s*\z/ !~ res + end + end + rv + end + + def _validate(tags, model=self.class.model) + count = 1 + do_redo = false + not_shift = false + tag = nil + + model.each_with_index do |elem, i| + + if $DEBUG + p "before" + p tags + p elem + end + + if not_shift + not_shift = false + else + begin + tag = tags.shift + rescue NameError + end + end + + if $DEBUG + p "mid" + p count + end + + case elem[1] + when '?' + if count > 2 + raise TooMuchTagError.new(elem[0], tag_name) + else + if elem[0] == tag + do_redo = true + else + not_shift = true + end + end + when '*' + if elem[0] == tag + do_redo = true + else + not_shift = true + end + when '+' + if elem[0] == tag + do_redo = true + else + if count > 1 + not_shift = true + else + raise MissingTagError.new(elem[0], tag_name) + end + end + else + if elem[0] == tag + begin + if model[i+1][0] != elem[0] and tags.first == elem[0] + raise TooMuchTagError.new(elem[0], tag_name) + end + rescue NameError # for model[i+1][0] and tags.first + end + else + raise MissingTagError.new(elem[0], tag_name) + end + end + + if $DEBUG + p "after" + p not_shift + p do_redo + p tag + end + + if do_redo + do_redo = false + count += 1 + redo + else + count = 1 + end + + end + + if !tags.nil? and !tags.empty? + raise NotExceptedTagError.new(tag, tag_name) + end + + end + + def tag_filter(tags) + rv = {} + tags.each do |tag| + rv[tag[0]] = [] unless rv.has_key?(tag[0]) + rv[tag[0]].push(tag[1]) + end + rv + end + + end + +end -- cgit v1.2.3