Class: DAV4Rack::Resource
- Inherits:
-
Object
- Object
- DAV4Rack::Resource
- Includes:
- HTTPStatus
- Defined in:
- lib/dav4rack/resource.rb
Direct Known Subclasses
Constant Summary collapse
- @@blocks =
{}
Constants included from HTTPStatus
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#propstat_relative_path ⇒ Object
readonly
Returns the value of attribute propstat_relative_path.
-
#public_path ⇒ Object
readonly
Returns the value of attribute public_path.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
-
#response ⇒ Object
readonly
Returns the value of attribute response.
-
#root_xml_attributes ⇒ Object
readonly
Returns the value of attribute root_xml_attributes.
-
#user ⇒ Object
Returns the value of attribute user.
Class Method Summary collapse
-
.method_missing(*args, &block) ⇒ Object
This lets us define a bunch of before and after blocks that are either called before all methods on the resource, or only specific methods on the resource.
Instance Method Summary collapse
-
#==(other) ⇒ Object
- other
-
Resource Returns if current resource is equal to other resource.
-
#allows_redirect? ⇒ Boolean
Does client allow GET redirection TODO: Get a comprehensive list in here.
-
#child(name) ⇒ Object
- name
- Name of child Create a new child with the given name NOTE
-
Include trailing ‘/’ if child is collection.
-
#children ⇒ Object
If this is a collection, return the child resources.
-
#collection? ⇒ Boolean
Is this resource a collection?.
-
#content_length ⇒ Object
Return the size in bytes for this resource.
-
#content_type ⇒ Object
Return the mime type of this resource.
-
#copy(dest, overwrite = false) ⇒ Object
HTTP COPY request.
-
#creation_date ⇒ Object
Return the creation time.
-
#delete ⇒ Object
HTTP DELETE request.
-
#descendants ⇒ Object
Return list of descendants.
-
#display_name ⇒ Object
Name of the resource to be displayed to the client.
-
#etag ⇒ Object
Return an Etag, an unique hash value for this resource.
-
#exist? ⇒ Boolean
Does this resource exist?.
-
#get(request, response) ⇒ Object
HTTP GET request.
-
#get_property(element) ⇒ Object
- name
-
String - Property name Returns the value of the given property.
-
#index_page ⇒ Object
Index page template for GETs on collection.
-
#initialize(public_path, path, request, response, options) ⇒ Resource
constructor
- public_path
- Path received via request path
- Internal resource path (Only different from public path when using root_uri’s for webdav) request
- Rack::Request options
-
Any options provided for this resource Creates a new instance of the resource.
-
#is_ms_client? ⇒ Boolean
Basic user agent testing for MS authored client.
-
#last_modified ⇒ Object
Return the time of last modification.
-
#last_modified=(time) ⇒ Object
Set the time of last modification.
-
#lock(args) ⇒ Object
- args
-
Hash of lock arguments Request for a lock on the given resource.
-
#lock_check(lock_scope = nil) ⇒ Object
- lock_scope
-
scope of lock Check if resource is locked.
-
#make_collection ⇒ Object
Create this resource as collection.
-
#method_missing(*args) ⇒ Object
This allows us to call before and after blocks.
-
#move(dest, overwrite = false) ⇒ Object
HTTP MOVE request.
-
#name ⇒ Object
Name of the resource.
-
#parent ⇒ Object
Return parent of this resource.
-
#parent_collection? ⇒ Boolean
Is the parent resource a collection?.
-
#parent_exists? ⇒ Boolean
Does the parent resource exist?.
-
#post(request, response) ⇒ Object
HTTP POST request.
-
#properties ⇒ Object
Available properties.
-
#put(request, response) ⇒ Object
HTTP PUT request.
-
#remove_property(element) ⇒ Object
- name
-
Property name Remove the property from the resource.
-
#resource_type ⇒ Object
Return the resource type.
-
#set_property(element, value) ⇒ Object
- name
- String - Property name value
-
New value Set the property to the given value.
-
#supports_locking? ⇒ Boolean
Returns if resource supports locking.
-
#unlock(token) ⇒ Object
- token
-
Lock token Remove the given lock.
- #use_compat_mkcol_response? ⇒ Boolean
-
#use_ms_compat_creationdate? ⇒ Boolean
Returns true if using an MS client.
Constructor Details
#initialize(public_path, path, request, response, options) ⇒ Resource
- public_path
-
Path received via request
- path
-
Internal resource path (Only different from public path when using root_uri’s for webdav)
- request
-
Rack::Request
- options
-
Any options provided for this resource
Creates a new instance of the resource. NOTE: path and public_path will only differ if the root_uri has been set for the resource. The
controller will strip out the starting path so the resource can easily determine what
it is working on. For example:
request -> /my/webdav/directory/actual/path
public_path -> /my/webdav/directory/actual/path
path -> /actual/path
NOTE: Customized Resources should not use initialize for setup. Instead
use the #setup method
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/dav4rack/resource.rb', line 66 def initialize(public_path, path, request, response, ) @skip_alias = [ :authenticate, :authentication_error_msg, :authentication_realm, :path, :options, :public_path, :request, :response, :user, :user=, :setup ] @public_path = public_path.dup @path = path.dup @propstat_relative_path = !!.delete(:propstat_relative_path) @root_xml_attributes = .delete(:root_xml_attributes) || {} @request = request @response = response unless(.has_key?(:lock_class)) require 'dav4rack/lock_store' @lock_class = LockStore else @lock_class = [:lock_class] raise NameError.new("Unknown lock type constant provided: #{@lock_class}") unless @lock_class.nil? || defined?(@lock_class) end @options = .dup @max_timeout = [:max_timeout] || 86400 @default_timeout = [:default_timeout] || 60 @user = @options[:user] || request.ip setup if respond_to?(:setup) public_methods(false).each do |method| next if @skip_alias.include?(method.to_sym) || method[0,4] == 'DAV_' || method[0,5] == '_DAV_' self.class.class_eval "alias :'_DAV_#{method}' :'#{method}'" self.class.class_eval "undef :'#{method}'" end @runner = lambda do |class_sym, kind, method_name| [:'__all__', method_name.to_sym].each do |sym| if(@@blocks[class_sym] && @@blocks[class_sym][kind] && @@blocks[class_sym][kind][sym]) @@blocks[class_sym][kind][sym].each do |b| args = [self, sym == :'__all__' ? method_name : nil].compact b.call(*args) end end end end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(*args) ⇒ Object
This allows us to call before and after blocks
109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/dav4rack/resource.rb', line 109 def method_missing(*args) result = nil orig = args.shift class_sym = self.class.name.to_sym m = orig.to_s[0,5] == '_DAV_' ? orig : "_DAV_#{orig}" # If hell is doing the same thing over and over and expecting a different result this is a hell preventer raise NoMethodError.new("Undefined method: #{orig} for class #{self}.") unless respond_to?(m) @runner.call(class_sym, :before, orig) result = send m, *args @runner.call(class_sym, :after, orig) result end |
Instance Attribute Details
#options ⇒ Object (readonly)
Returns the value of attribute options.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def @options end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def path @path end |
#propstat_relative_path ⇒ Object (readonly)
Returns the value of attribute propstat_relative_path.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def propstat_relative_path @propstat_relative_path end |
#public_path ⇒ Object (readonly)
Returns the value of attribute public_path.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def public_path @public_path end |
#request ⇒ Object (readonly)
Returns the value of attribute request.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def request @request end |
#response ⇒ Object (readonly)
Returns the value of attribute response.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def response @response end |
#root_xml_attributes ⇒ Object (readonly)
Returns the value of attribute root_xml_attributes.
19 20 21 |
# File 'lib/dav4rack/resource.rb', line 19 def root_xml_attributes @root_xml_attributes end |
#user ⇒ Object
Returns the value of attribute user.
21 22 23 |
# File 'lib/dav4rack/resource.rb', line 21 def user @user end |
Class Method Details
.method_missing(*args, &block) ⇒ Object
This lets us define a bunch of before and after blocks that are either called before all methods on the resource, or only specific methods on the resource
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/dav4rack/resource.rb', line 29 def method_missing(*args, &block) class_sym = self.name.to_sym @@blocks[class_sym] ||= {:before => {}, :after => {}} m = args.shift parts = m.to_s.split('_') type = parts.shift.to_s.to_sym method = parts.empty? ? nil : parts.join('_').to_sym if(@@blocks[class_sym][type] && block_given?) if(method) @@blocks[class_sym][type][method] ||= [] @@blocks[class_sym][type][method] << block else @@blocks[class_sym][type][:'__all__'] ||= [] @@blocks[class_sym][type][:'__all__'] << block end else raise NoMethodError.new("Undefined method #{m} for class #{self}") end end |
Instance Method Details
#==(other) ⇒ Object
- other
-
Resource
Returns if current resource is equal to other resource
339 340 341 |
# File 'lib/dav4rack/resource.rb', line 339 def ==(other) path == other.path end |
#allows_redirect? ⇒ Boolean
Does client allow GET redirection TODO: Get a comprehensive list in here. TODO: Allow this to be dynamic so users can add regexes to match if they know of a client that can be supported that is not listed.
447 448 449 450 451 452 453 454 |
# File 'lib/dav4rack/resource.rb', line 447 def allows_redirect? [ %r{cyberduck}i, %r{konqueror}i ].any? do |regexp| (request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp end end |
#child(name) ⇒ Object
- name
-
Name of child
Create a new child with the given name
- NOTE
-
Include trailing ‘/’ if child is collection
399 400 401 402 403 404 405 406 407 |
# File 'lib/dav4rack/resource.rb', line 399 def child(name) new_public = public_path.dup new_public = new_public + '/' unless new_public[-1,1] == '/' new_public = '/' + new_public unless new_public[0,1] == '/' new_path = path.dup new_path = new_path + '/' unless new_path[-1,1] == '/' new_path = '/' + new_path unless new_path[0,1] == '/' self.class.new("#{new_public}#{name}", "#{new_path}#{name}", request, response, .merge(:user => @user)) end |
#children ⇒ Object
If this is a collection, return the child resources.
127 128 129 |
# File 'lib/dav4rack/resource.rb', line 127 def children NotImplemented end |
#collection? ⇒ Boolean
Is this resource a collection?
132 133 134 |
# File 'lib/dav4rack/resource.rb', line 132 def collection? NotImplemented end |
#content_length ⇒ Object
Return the size in bytes for this resource.
184 185 186 |
# File 'lib/dav4rack/resource.rb', line 184 def content_length raise NotImplemented end |
#content_type ⇒ Object
Return the mime type of this resource.
179 180 181 |
# File 'lib/dav4rack/resource.rb', line 179 def content_type raise NotImplemented end |
#copy(dest, overwrite = false) ⇒ Object
HTTP COPY request.
Copy this resource to given destination resource.
219 220 221 |
# File 'lib/dav4rack/resource.rb', line 219 def copy(dest, overwrite=false) NotImplemented end |
#creation_date ⇒ Object
Return the creation time.
152 153 154 |
# File 'lib/dav4rack/resource.rb', line 152 def creation_date raise NotImplemented end |
#delete ⇒ Object
HTTP DELETE request.
Delete this resource.
212 213 214 |
# File 'lib/dav4rack/resource.rb', line 212 def delete NotImplemented end |
#descendants ⇒ Object
Return list of descendants
425 426 427 428 429 430 431 432 |
# File 'lib/dav4rack/resource.rb', line 425 def descendants list = [] children.each do |child| list << child list.concat(child.descendants) end list end |
#display_name ⇒ Object
Name of the resource to be displayed to the client
349 350 351 |
# File 'lib/dav4rack/resource.rb', line 349 def display_name name end |
#etag ⇒ Object
Return an Etag, an unique hash value for this resource.
168 169 170 |
# File 'lib/dav4rack/resource.rb', line 168 def etag raise NotImplemented end |
#exist? ⇒ Boolean
Does this resource exist?
137 138 139 |
# File 'lib/dav4rack/resource.rb', line 137 def exist? NotImplemented end |
#get(request, response) ⇒ Object
HTTP GET request.
Write the content of the resource to the response.body.
191 192 193 |
# File 'lib/dav4rack/resource.rb', line 191 def get(request, response) NotImplemented end |
#get_property(element) ⇒ Object
- name
-
String - Property name
Returns the value of the given property
362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/dav4rack/resource.rb', line 362 def get_property(element) raise NotImplemented if (element[:ns_href] != 'DAV:') case element[:name] when 'resourcetype' then resource_type when 'displayname' then display_name when 'creationdate' then use_ms_compat_creationdate? ? creation_date.httpdate : creation_date.xmlschema when 'getcontentlength' then content_length.to_s when 'getcontenttype' then content_type when 'getetag' then etag when 'getlastmodified' then last_modified.httpdate else raise NotImplemented end end |
#index_page ⇒ Object
Index page template for GETs on collection
435 436 437 438 439 440 441 |
# File 'lib/dav4rack/resource.rb', line 435 def index_page '<html><head> <title>%s</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /></head> <body> <h1>%s</h1> <hr /> <table> <tr> <th class="name">Name</th> <th class="size">Size</th> <th class="type">Type</th> <th class="mtime">Last Modified</th> </tr> %s </table> <hr /> </body></html>' end |
#is_ms_client? ⇒ Boolean
Basic user agent testing for MS authored client
468 469 470 471 472 |
# File 'lib/dav4rack/resource.rb', line 468 def is_ms_client? [%r{microsoft-webdav}i, %r{microsoft office}i].any? do |regexp| (request.respond_to?(:user_agent) ? request.user_agent : request.env['HTTP_USER_AGENT']).to_s =~ regexp end end |
#last_modified ⇒ Object
Return the time of last modification.
157 158 159 |
# File 'lib/dav4rack/resource.rb', line 157 def last_modified raise NotImplemented end |
#last_modified=(time) ⇒ Object
Set the time of last modification.
162 163 164 165 |
# File 'lib/dav4rack/resource.rb', line 162 def last_modified=(time) # Is this correct? raise NotImplemented end |
#lock(args) ⇒ Object
- args
-
Hash of lock arguments
Request for a lock on the given resource. A valid lock should lock all descendents. Failures should be noted and returned as an exception using LockFailure. Valid args keys: :timeout -> requested timeout
:depth -> lock depth
:scope -> lock scope
:type -> lock type
:owner -> lock owner
Should return a tuple: [lock_time, locktoken] where lock_time is the given timeout NOTE: See section 9.10 of RFC 4918 for guidance about how locks should be generated and the expected responses (www.webdav.org/specs/rfc4918.html#rfc.section.9.10)
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/dav4rack/resource.rb', line 245 def lock(args) unless(@lock_class) NotImplemented else unless(parent_exists?) Conflict else lock_check(args[:scope]) lock = @lock_class.explicit_locks(@path).find{|l| l.scope == args[:scope] && l.kind == args[:type] && l.user == @user} unless(lock) token = UUIDTools::UUID.random_create.to_s lock = @lock_class.generate(@path, @user, token) lock.scope = args[:scope] lock.kind = args[:type] lock.owner = args[:owner] lock.depth = args[:depth].is_a?(Symbol) ? args[:depth] : args[:depth].to_i if(args[:timeout]) lock.timeout = args[:timeout] <= @max_timeout && args[:timeout] > 0 ? args[:timeout] : @max_timeout else lock.timeout = @default_timeout end lock.save if lock.respond_to? :save end begin lock_check(args[:type]) rescue DAV4Rack::LockFailure => lock_failure lock.destroy raise lock_failure rescue HTTPStatus::Status => status status end [lock.remaining_timeout, lock.token] end end end |
#lock_check(lock_scope = nil) ⇒ Object
- lock_scope
-
scope of lock
Check if resource is locked. Raise DAV4Rack::LockFailure if locks are in place.
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/dav4rack/resource.rb', line 283 def lock_check(lock_scope=nil) return unless @lock_class if(@lock_class.explicitly_locked?(@path)) raise Locked if @lock_class.explicit_locks(@path).find_all{|l|l.scope == 'exclusive' && l.user != @user}.size > 0 elsif(@lock_class.implicitly_locked?(@path)) if(lock_scope.to_s == 'exclusive') locks = @lock_class.implicit_locks(@path) failure = DAV4Rack::LockFailure.new("Failed to lock: #{@path}") locks.each do |lock| failure.add_failure(@path, Locked) end raise failure else locks = @lock_class.implict_locks(@path).find_all{|l| l.scope == 'exclusive' && l.user != @user} if(locks.size > 0) failure = LockFailure.new("Failed to lock: #{@path}") locks.each do |lock| failure.add_failure(@path, Locked) end raise failure end end end end |
#make_collection ⇒ Object
Create this resource as collection.
333 334 335 |
# File 'lib/dav4rack/resource.rb', line 333 def make_collection NotImplemented end |
#move(dest, overwrite = false) ⇒ Object
HTTP MOVE request.
Move this resource to given destination resource.
226 227 228 |
# File 'lib/dav4rack/resource.rb', line 226 def move(dest, overwrite=false) NotImplemented end |
#name ⇒ Object
Name of the resource
344 345 346 |
# File 'lib/dav4rack/resource.rb', line 344 def name File.basename(path) end |
#parent ⇒ Object
Return parent of this resource
410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/dav4rack/resource.rb', line 410 def parent unless(@path.to_s.empty?) self.class.new( File.split(@public_path).first, File.split(@path).first, @request, @response, @options.merge( :user => @user ) ) end end |
#parent_collection? ⇒ Boolean
Is the parent resource a collection?
147 148 149 |
# File 'lib/dav4rack/resource.rb', line 147 def parent_collection? parent.collection? end |
#parent_exists? ⇒ Boolean
Does the parent resource exist?
142 143 144 |
# File 'lib/dav4rack/resource.rb', line 142 def parent_exists? parent.exist? end |
#post(request, response) ⇒ Object
HTTP POST request.
Usually forbidden.
205 206 207 |
# File 'lib/dav4rack/resource.rb', line 205 def post(request, response) NotImplemented end |
#properties ⇒ Object
Available properties
354 355 356 357 358 |
# File 'lib/dav4rack/resource.rb', line 354 def properties %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength).collect do |prop| {:name => prop, :ns_href => 'DAV:'} end end |
#put(request, response) ⇒ Object
HTTP PUT request.
Save the content of the request.body.
198 199 200 |
# File 'lib/dav4rack/resource.rb', line 198 def put(request, response) NotImplemented end |
#remove_property(element) ⇒ Object
- name
-
Property name
Remove the property from the resource
392 393 394 |
# File 'lib/dav4rack/resource.rb', line 392 def remove_property(element) Forbidden end |
#resource_type ⇒ Object
Return the resource type. Generally only used to specify resource is a collection.
174 175 176 |
# File 'lib/dav4rack/resource.rb', line 174 def resource_type :collection if collection? end |
#set_property(element, value) ⇒ Object
- name
-
String - Property name
- value
-
New value
Set the property to the given value
379 380 381 382 383 384 385 386 387 388 |
# File 'lib/dav4rack/resource.rb', line 379 def set_property(element, value) raise NotImplemented if (element[:ns_href] != 'DAV:') case element[:name] when 'resourcetype' then self.resource_type = value when 'getcontenttype' then self.content_type = value when 'getetag' then self.etag = value when 'getlastmodified' then self.last_modified = Time.httpdate(value) else raise NotImplemented end end |
#supports_locking? ⇒ Boolean
Returns if resource supports locking
122 123 124 |
# File 'lib/dav4rack/resource.rb', line 122 def supports_locking? false #true end |
#unlock(token) ⇒ Object
- token
-
Lock token
Remove the given lock
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/dav4rack/resource.rb', line 310 def unlock(token) unless(@lock_class) NotImplemented else token = token.slice(1, token.length - 2) if(token.nil? || token.empty?) BadRequest else lock = @lock_class.find_by_token(token) if(lock.nil? || lock.user != @user) Forbidden elsif(lock.path !~ /^#{Regexp.escape(@path)}.*$/) Conflict else lock.destroy NoContent end end end end |
#use_compat_mkcol_response? ⇒ Boolean
456 457 458 |
# File 'lib/dav4rack/resource.rb', line 456 def use_compat_mkcol_response? @options[:compat_mkcol] || @options[:compat_all] end |
#use_ms_compat_creationdate? ⇒ Boolean
Returns true if using an MS client
461 462 463 464 465 |
# File 'lib/dav4rack/resource.rb', line 461 def use_ms_compat_creationdate? if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all]) is_ms_client? end end |