lib/imw/resource.rb in imw-0.2.7 vs lib/imw/resource.rb in imw-0.2.8
- old
+ new
@@ -1,38 +1,9 @@
-require 'addressable/uri'
+require 'imw/utils/has_uri'
module IMW
- # Define this constant in your configuration file to add your own
- # URI handlers to IMW.
- USER_DEFINED_HANDLERS = [] unless defined?(USER_DEFINED_HANDLERS)
-
- # Register a new resource handler which dynamically extends a new
- # IMW::Resource with the given module +mod+.
- #
- # +handler+ must be one of
- #
- # 1. Regexp
- # 2. Proc
- # 3. +true+
- #
- # In case (1), if the regular expression matches the resource's URI
- # then the module (+mod+) will be used to extend the resource.
- #
- # In case (2), if the Proc returns a value other than +false+ or
- # +nil+ then the module will be used.
- #
- # In case (3), the module will be used.
- #
- # @param [String, Module] mod
- # @param [Regexp, Proc, true] handler
- def self.register_handler mod, handler
- raise IMW::ArgumentError.new("Module must be either a Module or String") unless mod.is_a?(Module) || mod.is_a?(String)
- raise IMW::ArgumentError.new("Handler must be either a Regexp, Proc, or true") unless handler.is_a?(Regexp) || handler.is_a?(Proc) || handler == true
- self::USER_DEFINED_HANDLERS << [mod, handler]
- end
-
# A resource can be anything addressable via a URI. Examples
# include local files, remote files, webpages, &c.
#
# The IMW::Resource class takes a URI as input and then dynamically
# extends itself with appropriate modules from IMW. As an example,
@@ -52,11 +23,11 @@
# configuration file.
#
# The modules extending a particular IMW::Resource instance can be
# listed as follows
#
- # my_archive.resource_modules #=> [IMW::Local::Base, IMW::Local::File, IMW::Local::Compressible, IMW::Archives::Tarbz2]
+ # my_archive.modules #=> [IMW::Local::Base, IMW::Local::File, IMW::Local::Compressible, IMW::Archives::Tarbz2]
#
# By default, resources are opened for reading. Passing in the
# appropriate <tt>:mode</tt> option changes this:
#
# IMW::Resource.new('/path/to/my_new_file', :mode => 'w')
@@ -72,165 +43,89 @@
#
# You can also instantiate an IMW::Resource using IMW.open, which
# accepts all the same arguments as IMW::Resource.new.
class Resource
- # The URI object associated with this resource.
- attr_reader :uri
-
# The mode in which to access this resource.
attr_accessor :mode
# A copy of the options passed to this resource on initialization.
attr_accessor :resource_options
# Create a new resource representing +uri+.
#
- # IMW will automatically extend the resulting IMW::Resourcen
- # instance with modules appropriate to the given URI.
+ # IMW will automatically extend the resulting IMW::Resource
+ # instance with modules appropriate for the given URI:
#
# r = IMW::Resource.new("http://www.infochimps.com")
- # r.resource_modules
+ # r.modules
# => [IMW::Schemes::Remote::Base, IMW::Schemes::Remote::RemoteFile, IMW::Schemes::HTTP, IMW::Formats::Html]
#
# You can prevent this altogether by passing in
# <tt>:no_modules</tt>:
#
- # r = IMW::Resource.new("http://www.infochimps.com")
- # r.resource_modules
- # => [IMW::Schemes::Remote::Base, IMW::Schemes::Remote::RemoteFile, IMW::Schemes::HTTP, IMW::Formats::Html]
+ # r = IMW::Resource.new("http://www.infochimps.com", :no_modules => true)
+ # r.modules
+ # => []
#
# And you can exert more fine-grained control with the
# <tt>:use_modules</tt> and <tt>:skip_modules</tt> options, see
- # IMW::Resource.extend_resource! for details.
+ # IMW::Resource.extend_instance! for details.
#
# @param [String, Addressable::URI] uri
# @param [Hash] options
# @option options [true, false] no_modules
# @option options [String] mode the mode to open the resource in (will be ignored when inapplicable)
+ # @option options [IMW::Metadata::Record, Array] schema the schema of this resource
# @return [IMW::Resource]
def initialize uri, options={}
self.uri = uri
self.resource_options = options
self.mode = options[:mode] || 'r'
- extend_appropriately!(options) unless options[:no_modules]
+ self.schema = options[:schema] if options[:schema]
+ extend_appropriately!(options)
end
- # Return the modules this resource has been extended by.
- #
- # @return [Array] the modules this resource has been extended by.
- def resource_modules
- @resource_modules ||= []
- end
+ # Provides resources with a wrapped Addressable::URI object.
+ include IMW::Utils::HasURI
- # Works just like Object#extend except it keeps track of the
- # modules it has extended, see Resource#resource_modules.
- def extend mod
- resource_modules << mod
- super mod
+ # Provides resources with a schema.
+ include IMW::Metadata::Schematized
+
+ # Gives IMW::Resource instances with the ability to dynamically
+ # extend themselves with modules chosen from a set of handlers
+ # stored by the IMW::Resource class.
+ include IMW::Utils::DynamicallyExtendable
+ [IMW::Schemes::HANDLERS, IMW::CompressedFiles::HANDLERS, IMW::Archives::HANDLERS, IMW::Formats::HANDLERS].each do |handlers|
+ register_handlers *handlers
end
-
- # Extend this resource with modules by passing it through a
- # collection of handlers defined by IMW::Resource.handlers.
- #
- # Accepts the same options as Resource.extend_resource!.
- def extend_appropriately! options={}
- self.class.extend_resource!(self, options)
- end
-
- # Set the URI of this resource by parsing the given +uri+ (if
- # necessary).
- #
- # @param [String, Addressable::URI] uri the uri to parse
- def uri= uri
- if uri.is_a?(Addressable::URI)
- @uri = uri
- else
- begin
- @uri = Addressable::URI.parse(uri.to_s)
- rescue URI::InvalidURIError
- @uri = Addressable::URI.parse(URI.encode(uri.to_s))
- @encoded_uri = true
- end
- end
- end
-
- # The scheme of this resource. Will be +nil+ for local resources.
- #
- # @return [String]
- def scheme
- @scheme ||= uri.scheme
- end
-
- # The directory name of this resource's path.
- #
- # @return [String]
- def dirname
- @dirname ||= File.dirname(path)
- end
-
- # The basename of this resource's path.
- #
- # @return [String]
- def basename
- @basename ||= File.basename(path)
- end
-
- # Returns the extension (INCLUDING the '.') of this resource's
- # path. Redefine this in an including class for which this is
- # weird ('.tar.gz' I'm talking to you...)
- #
- # @return [String]
- def extname
- @extname ||= File.extname(path)
- end
-
- # Returns the extension (WITHOUT the '.') of this resource's path.
- #
- # @return [String]
- def extension
- @extension ||= extname[1..-1] || ''
- end
-
- # Returns the basename of the file with its extension removed
- #
- # IMW.open('/path/to/some_file.tar.gz').name # => some_file
- #
- # @return [String]
- def name
- @name ||= extname ? basename[0,basename.length - extname.length] : basename
- end
-
- # Returns the user associated with the host of this URI.
- #
- # @return [String]
- def user
- @user ||= uri.user
- end
-
- def to_s
- uri.to_s
- end
-
+
# Raise an error unless this resource exists.
#
# @param [String] message an optional message to include
def should_exist!(message=nil)
- raise IMW::Error.new([message, "No path defined for #{self.inspect} extended by #{resource_modules.join(' ')}"].compact.join(', ')) unless respond_to?(:path)
- raise IMW::Error.new([message, "No exist? method defined for #{self.inspect} extended by #{resource_modules.join(' ')}"].compact.join(', ')) unless respond_to?(:exist?)
- raise IMW::PathError.new([message, "#{path} does not exist"].compact.join(', ')) unless exist?
+ raise IMW::Error.new([message, "No path defined for #{self.inspect} extended by #{modules.join(' ')}"].compact.join(', ')) unless respond_to?(:path)
+ raise IMW::Error.new([message, "No exist? method defined for #{self.inspect} extended by #{modules.join(' ')}"].compact.join(', ')) unless respond_to?(:exist?)
+ raise IMW::PathError.new([message, "#{path} does not exist"].compact.join(', ')) unless exist?
self
end
+ # Close this resource.
+ #
+ # Modules should hook into super() as they need to redefine this
+ # method.
+ def close
+ end
+
# Open a copy of this resource.
#
# This is useful when wanting to reset file handles. Though -- be
# warned -- it does not close any file handles itself...
#
# @return [IMW::Resource] the new (old) resource
def reopen
- IMW.open(self.uri.to_s)
+ IMW.open(uri.to_s)
end
# If +method+ begins with the strings +is+, +on+, or +via+ and
# ends with a question mark then we interpret it as a question
# this resource doesn't know how to answer -- so we have it answer
@@ -255,94 +150,11 @@
def method_missing method, *args
if args.empty? && method.to_s =~ /(is|on|via)_.*\?$/
# querying for a boolean response so answer false
return false
else
- raise IMW::NoMethodError, "undefined method `#{method}' for #{self}, extended by #{resource_modules.join(', ')}"
+ raise IMW::NoMethodError, "undefined method `#{method}' for #{self}, extended by #{modules.join(', ')}"
end
end
- # Iterate through IMW::Resource.handlers and extend the given
- # +resource+ with modules whose handler conditions match the
- # resource.
- #
- # Passing in <tt>:use_modules</tt> or <tt>:skip_modules</tt>
- # allows overriding the default behavior of handlers.
- #
- # @param [IMW::Resource] resource the resource to extend
- # @param [Hash] options
- # @option options [Array<String,Module>] use_modules a list of modules used regardless of handlers
- # @option options [Array<String,Module>] skip_modules a list of modules not to be used regardless of handlers
- # @return [IMW::Resource] the extended resource
- def self.extend_resource! resource, options={}
- options.reverse_merge!(:use_modules => [], :skip_modules => [])
- handlers.each do |mod_name, handler|
- case handler
- when Regexp then extend_resource_with_mod_or_string!(resource, mod_name, options[:skip_modules]) if handler =~ resource.uri.to_s
- when Proc then extend_resource_with_mod_or_string!(resource, mod_name, options[:skip_modules]) if handler.call(resource)
- when TrueClass then extend_resource_with_mod_or_string!(resource, mod_name, options[:skip_modules])
- else
- raise IMW::TypeError("A handler must be Regexp, Proc, or true")
- end
- end
- options[:use_modules].each { |mod_name| extend_resource_with_mod_or_string!(resource, mod_name, options[:skip_modules]) }
- resource
- end
-
- # A list of handlers to match against each new resource.
- #
- # When an IMW::Resource is instantiated it eventually calls
- # IMW::Resource.extend_resource! which will iterate through the
- # handlers in IMW::Resource.handlers, extending the resource with
- # modules whose handler conditions are satisfied.
- #
- # A handler is just an Array with two elements. The first should be
- # a module or a string identifying a module.
- #
- # If the second element is a Regexp, the corresponding module will
- # be used if the regexp matches the resource's URI (as a string)
- #
- # If the second element is a Proc, it will be called with the
- # resource as its only argument and if it returns true then the
- # module will be used.
- #
- # You can define your own handlers by appending them to
- # IMW::Resource::USER_DEFINED_HANDLERS in your <tt>.imwrc</tt>
- # file.
- #
- # The order in which handlers appear is significant --
- # IMW::CompressedFiles::HANDLERS must be _before_
- # IMW::Archives::HANDLERS, for example, because of (say)
- # <tt>.tar.bz2</tt> files.
- #
- # @return [Array]
- def self.handlers
- # order is important!
- #
- #
- #
- #CompressedFiles must come before
- # Archives because of tar.bz2 type files
- IMW::Schemes::HANDLERS + IMW::CompressedFiles::HANDLERS + IMW::Archives::HANDLERS + IMW::Formats::HANDLERS + USER_DEFINED_HANDLERS
- end
-
- protected
- # Extend +resource+ with +mod_or_string+. Will work hard to try
- # and interpret +mod_or_string+ as a module if it's a string.
- #
- # @param [IMW::Resource] resource the resource to extend
- #
- # @param [Module, String] mod_or_string the module or string
- # representing a module to extend the resource with
- #
- # @param [Array<Module,String>] skip_modules modules to exclude
- def self.extend_resource_with_mod_or_string! resource, mod_or_string, skip_modules
- return if skip_modules.include?(mod_or_string)
- if mod_or_string.is_a?(Module)
- resource.extend(mod_or_string)
- else
- m = IMW.class_eval(mod_or_string)
- resource.extend(m) unless skip_modules.include?(m)
- end
- end
end
end