lib/imw/resource.rb in imw-0.2.0 vs lib/imw/resource.rb in imw-0.2.1
- old
+ new
@@ -1,34 +1,37 @@
require 'addressable/uri'
-require 'imw/resources'
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)
+
# 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::Resources. As
- # an example, calling
+ # extends itself with appropriate modules from IMW. As an example,
+ # calling
#
# my_archive = IMW::Resource.new('/path/to/my/archive.tar.bz2')
#
# would return an IMW::Resource extended by
- # IMW::Resources::Archives::Tarbz2 (among other modules) which
+ # IMW::Archives::Tarbz2 (among other modules) which
# therefore has methods for extracting, listing, and appending to
# the archive.
#
# Modules are so extended based on handlers defined in the
# <tt>imw/resources</tt> directory and accessible via
- # IMW::Resources#handlers. You can define your own handlers by
- # defining the constant IMW::Resources::USER_DEFINED_HANDLERS in
- # your configuration file.
+ # IMW::Resource.handlers. You can define your own handlers by
+ # defining the constant IMW::Resource::USER_DEFINED_HANDLERS in your
+ # configuration file.
#
# The modules extending a particular IMW::Resource instance can be
# listed as follows
#
- # my_archive.resource_modules #=> [IMW::Resources::LocalObj, IMW::Resources::LocalFile, IMW::Resources::Compressible, IMW::Resources::Archives::Tarbz2]
+ # my_archive.resource_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')
@@ -39,10 +42,13 @@
# be useful when subclassing IMW::Resource or dealing with a very
# strange kind of resource.
#
# Read the documentation for modules in IMW::Resources to learn more
# about the various behaviors an IMW::Resource can acquire.
+ #
+ # You can also instantiate an IMW::Resource using IMW.open, which
+ # accepts all the same arguments as IMW::Resource.new.
class Resource
attr_reader :uri, :mode
def initialize uri, options={}
@@ -64,13 +70,13 @@
resource_modules << mod
super mod
end
# Extend this resource with modules by passing it through a
- # collection of handlers defined by IMW::Resources#handlers
+ # collection of handlers defined by IMW::Resource.handlers.
def extend_appropriately!
- IMW::Resources.extend_resource!(self)
+ self.class.extend_resource!(self)
end
# Set the URI of this resource by parsing the given +uri+ (if
# necessary).
#
@@ -184,7 +190,99 @@
return false
else
raise IMW::NoMethodError, "undefined method `#{method}' for #{self}, extended by #{resource_modules.join(', ')}"
end
end
+
+ # Iterate through IMW::Resource.handlers and extend the given
+ # +resource+ with modules whose handler conditions match the
+ # resource.
+ #
+ # @param [IMW::Resource] resource the resource to extend
+ # @return [IMW::Resource] the extended resource
+ def self.extend_resource! resource
+ handlers.each do |mod_name, handler|
+ case handler
+ when Regexp then extend_resource_with_mod_or_string!(resource, mod_name) if handler =~ resource.uri.to_s
+ when Proc then extend_resource_with_mod_or_string!(resource, mod_name) if handler.call(resource)
+ when TrueClass then extend_resource_with_mod_or_string!(resource, mod_name)
+ else
+ raise IMW::TypeError("A handler must be Regexp, Proc, or true")
+ end
+ end
+ 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
+ def self.extend_resource_with_mod_or_string! resource, mod_or_string
+ if mod_or_string.is_a?(Module)
+ resource.extend(mod_or_string)
+ else
+ # Given a string "Mod::SubMod::SubSubMod" first split it into
+ # its parts ["Mod", "SubMod", "SubSubMod"] and then begin
+ # class_eval'ing them in order so that each is class_eval'd in
+ # the scope of the one before it.
+ #
+ # There is almost certainly a better way to do this.
+ # mod_names = mod_or_string.to_s.split('::')
+ # mods = []
+ # mod_names.each_with_index do |name, index|
+ # if index == 0
+ # mods << IMW.class_eval(name)
+ # else
+ # begin
+ # mods << class_eval(name)
+ # rescue NameError
+ # mods << mods[index - 1].class_eval(name)
+ # end
+ # end
+ # end
+ # resource.extend(mods.last)
+ resource.extend(IMW.class_eval(mod_or_string))
+ end
+ end
end
end