module OEmbed module Formatter # These are methods that are shared by the OEmbed::Formatter sub-classes # (i.e. OEmbed::Formatter:JSON and OEmbed::Formatter::XML). module Base # Returns true if there is a valid backend. Otherwise, raises OEmbed::FormatNotSupported def supported? !!backend end # Parses a String or IO and convert it into an Object def decode(value) backend.decode(value) end # Given either a String (the name of the backend to use) or an Object (which # must respond to the decode method), sets the current backend. Raises a LoadError # if the given backend cannot be loaded (e.g. an invalid String name, or the # decode method doesn't work properly). # OEmbed::Formatter::XML.backend = 'REXML' # OEmbed::Formatter::JSON.backend = MyCustomJsonDecoder.new def backend=(new_backend) new_backend_obj = case new_backend when String already_required = false begin already_required = self::Backends.const_defined?(new_backend, false) rescue ArgumentError # we're dealing with ruby < 1.9 where const_defined? only takes 1 argument, but behaves the way we want it to. already_required = self::Backends.const_defined?(new_backend) rescue NameError # no backends have been loaded yet already_required = false end require "oembed/formatter/#{backend_path}/#{new_backend.downcase}" unless already_required self::Backends.const_get(new_backend) else new_backend end test_backend(new_backend_obj) @backend = new_backend_obj rescue raise LoadError, "There was an error setting the backend: #{new_backend.inspect} - #{$!.message}" end # Perform a set of operations using a backend other than the current one. # OEmbed::Formatter::XML.with_backend('XmlSimple') do # OEmbed::Formatter::XML.decode(xml_value) # end def with_backend(new_backend) old_backend, self.backend = backend, new_backend yield ensure self.backend = old_backend end private # Makes sure the given backend can correctly parse values using the decode # method. def test_backend(new_backend) raise LoadError, "The given backend must respond to the decode method: #{new_backend.inspect}" unless new_backend.respond_to?(:decode) expected = { "version"=>1.0, "string"=>"test", "int"=>42, "html"=>"Cool's\n the \"word\"!", } actual = new_backend.decode(test_value) # For the test to be true the actual output Hash should have the # exact same list of keys _and_ the values should be the same # if we ignoring typecasting. if( actual.keys.sort != expected.keys.sort || actual.detect { |key, value| value.to_s != expected[key].to_s } ) msg = new_backend.decode_fail_msg rescue nil msg ||= "The given backend failed to decode the test string correctly" raise LoadError, "#{msg}: #{new_backend.inspect}" end end # Must return a String representing the sub-directory where in-library # backend rb files live (e.g. 'json/backends') def backend_path raise "This method must be defined by a format-specific OEmbed::Formatter sub-class." end # Must return a String that when parsed by a backend returns the following ruby Hash # { # "version"=>1.0, # "string"=>"test", # "int"=>42, # "html"=>"Cool's\n the \"word\"!", # } def test_value raise "This method must be defined by a format-specific OEmbed::Formatter sub-class." end end # SharedMethods end end