Class: IMW::Resource

Inherits:
Object
  • Object
show all
Defined in:
lib/imw/resource.rb

Overview

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, calling

my_archive = IMW::Resource.new('/path/to/my/archive.tar.bz2')

would return an IMW::Resource extended by 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 imw/resources directory and accessible via 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::Local::Base, IMW::Local::File, IMW::Local::Compressible, IMW::Archives::Tarbz2]

By default, resources are opened for reading. Passing in the appropriate :mode option changes this:

IMW::Resource.new('/path/to/my_new_file', :mode => 'w')

If the :skip_modules option is passed in then the resource will not extend itself with any modules and will essentially only retain the bare functionality of a URI. This can 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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri, options = {}) ⇒ IMW::Resource

Create a new resource representing uri.

IMW will automatically extend the resulting IMW::Resourcen instance with modules appropriate to the given URI.

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]

You can prevent this altogether by passing in :no_modules:

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]

And you can exert more fine-grained control with the :use_modules and :skip_modules options, see IMW::Resource.extend_resource! for details.

Parameters:

Options Hash (options):

  • no_modules (true, false)
  • mode (String)

    the mode to open the resource in (will be ignored when inapplicable)



79
80
81
82
83
# File 'lib/imw/resource.rb', line 79

def initialize uri, options={}
  self.uri = uri
  @mode    = options[:mode] || 'r'
  extend_appropriately!(options) unless options[:no_modules]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

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 false.

As an example, consider the following loop:

IMW.open('/tmp').all_contents.each do |obj|
  if obj.is_archive?
    # ... do something
  end
end

When obj is initialized and it _isn’t_ an archive, then it doesn’t know about the is_archive? method – but it should therefore answer false anyway.

This lets a basic text file answer questions about whether it’s an archive (or on S3, or accessed via some user-defined scheme, &c.) without needing to know anything about archives (or S3 or the user-defined scheme).



214
215
216
217
218
219
220
221
# File 'lib/imw/resource.rb', line 214

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(', ')}"
  end
end

Instance Attribute Details

#modeObject (readonly)

Returns the value of attribute mode.



52
53
54
# File 'lib/imw/resource.rb', line 52

def mode
  @mode
end

#uriObject

Returns the value of attribute uri.



52
53
54
# File 'lib/imw/resource.rb', line 52

def uri
  @uri
end

Class Method Details

.extend_resource!(resource, options = {}) ⇒ IMW::Resource

Iterate through IMW::Resource.handlers and extend the given resource with modules whose handler conditions match the resource.

Passing in :use_modules or :skip_modules allows overriding the default behavior of handlers.

Parameters:

  • resource (IMW::Resource)

    the resource to extend

  • options (Hash) (defaults to: {})

Options Hash (options):

  • use_modules (Array<String,Module>)

    a list of modules used regardless of handlers

  • skip_modules (Array<String,Module>)

    a list of modules not to be used regardless of handlers

Returns:



235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/imw/resource.rb', line 235

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

.handlersArray

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 .imwrc file.

The order in which handlers appear is significant – IMW::CompressedFiles::HANDLERS must be before IMW::Archives::HANDLERS, for example, because of (say) .tar.bz2 files.

Returns:



277
278
279
280
281
282
283
284
285
# File 'lib/imw/resource.rb', line 277

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

Instance Method Details

#basenameString

The basename of this resource’s path.

Returns:



141
142
143
# File 'lib/imw/resource.rb', line 141

def basename
  @basename ||= File.basename(path)
end

#dirnameString

The directory name of this resource’s path.

Returns:



134
135
136
# File 'lib/imw/resource.rb', line 134

def dirname
  @dirname  ||= File.dirname(path)
end

#extend(mod) ⇒ Object

Works just like Object#extend except it keeps track of the modules it has extended, see Resource#resource_modules.



94
95
96
97
# File 'lib/imw/resource.rb', line 94

def extend mod
  resource_modules << mod
  super mod
end

#extend_appropriately!(options = {}) ⇒ Object

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!.



103
104
105
# File 'lib/imw/resource.rb', line 103

def extend_appropriately! options={}
  self.class.extend_resource!(self, options)
end

#extensionString

Returns the extension (WITHOUT the ‘.’) of this resource’s path.

Returns:



157
158
159
# File 'lib/imw/resource.rb', line 157

def extension
  @extension ||= extname[1..-1] || ''
end

#extnameString

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…)

Returns:



150
151
152
# File 'lib/imw/resource.rb', line 150

def extname
  @extname ||= File.extname(path)
end

#nameString

Returns the basename of the file with its extension removed

IMW.open('/path/to/some_file.tar.gz').name # => some_file

Returns:



166
167
168
# File 'lib/imw/resource.rb', line 166

def name
  @name ||= extname ? basename[0,basename.length - extname.length] : basename
end

#reopenIMW::Resource

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…

Returns:



189
190
191
# File 'lib/imw/resource.rb', line 189

def reopen
  IMW.open(self.uri.to_s)
end

#resource_modulesArray

Return the modules this resource has been extended by.

Returns:

  • (Array)

    the modules this resource has been extended by.



88
89
90
# File 'lib/imw/resource.rb', line 88

def resource_modules
  @resource_modules ||= []
end

#schemeString

The scheme of this resource. Will be nil for local resources.

Returns:



127
128
129
# File 'lib/imw/resource.rb', line 127

def scheme
  @scheme ||= uri.scheme
end

#should_exist!(message = nil) ⇒ Object

Raise an error unless this resource exists.

Parameters:

  • message (String) (defaults to: nil)

    an optional message to include

Raises:



177
178
179
180
181
# File 'lib/imw/resource.rb', line 177

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?
end

#to_sObject



170
171
172
# File 'lib/imw/resource.rb', line 170

def to_s
  uri.to_s
end