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(name) ⇒ 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_exists? ⇒ Boolean
Does the parent resource exist?.
-
#post(request, response) ⇒ Object
HTTP POST request.
-
#property_names ⇒ Object
Available properties.
-
#put(request, response) ⇒ Object
HTTP PUT request.
-
#remove_property(name) ⇒ Object
- name
-
Property name Remove the property from the resource.
-
#resource_type ⇒ Object
Return the resource type.
-
#set_property(name, value) ⇒ Object
- name
- String - Property name value
-
New value Set the property to the given value.
-
#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
329 330 331 |
# File 'lib/dav4rack/resource.rb', line 329 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.
431 432 433 434 435 436 437 438 |
# File 'lib/dav4rack/resource.rb', line 431 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
383 384 385 386 387 388 389 390 391 |
# File 'lib/dav4rack/resource.rb', line 383 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.
122 123 124 |
# File 'lib/dav4rack/resource.rb', line 122 def children NotImplemented end |
#collection? ⇒ Boolean
Is this resource a collection?
127 128 129 |
# File 'lib/dav4rack/resource.rb', line 127 def collection? NotImplemented end |
#content_length ⇒ Object
Return the size in bytes for this resource.
174 175 176 |
# File 'lib/dav4rack/resource.rb', line 174 def content_length raise NotImplemented end |
#content_type ⇒ Object
Return the mime type of this resource.
169 170 171 |
# File 'lib/dav4rack/resource.rb', line 169 def content_type raise NotImplemented end |
#copy(dest, overwrite = false) ⇒ Object
HTTP COPY request.
Copy this resource to given destination resource.
209 210 211 |
# File 'lib/dav4rack/resource.rb', line 209 def copy(dest, overwrite=false) NotImplemented end |
#creation_date ⇒ Object
Return the creation time.
142 143 144 |
# File 'lib/dav4rack/resource.rb', line 142 def creation_date raise NotImplemented end |
#delete ⇒ Object
HTTP DELETE request.
Delete this resource.
202 203 204 |
# File 'lib/dav4rack/resource.rb', line 202 def delete NotImplemented end |
#descendants ⇒ Object
Return list of descendants
409 410 411 412 413 414 415 416 |
# File 'lib/dav4rack/resource.rb', line 409 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
339 340 341 |
# File 'lib/dav4rack/resource.rb', line 339 def display_name name end |
#etag ⇒ Object
Return an Etag, an unique hash value for this resource.
158 159 160 |
# File 'lib/dav4rack/resource.rb', line 158 def etag raise NotImplemented end |
#exist? ⇒ Boolean
Does this resource exist?
132 133 134 |
# File 'lib/dav4rack/resource.rb', line 132 def exist? NotImplemented end |
#get(request, response) ⇒ Object
HTTP GET request.
Write the content of the resource to the response.body.
181 182 183 |
# File 'lib/dav4rack/resource.rb', line 181 def get(request, response) NotImplemented end |
#get_property(name) ⇒ Object
- name
-
String - Property name
Returns the value of the given property
350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/dav4rack/resource.rb', line 350 def get_property(name) case 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 end end |
#index_page ⇒ Object
Index page template for GETs on collection
419 420 421 422 423 424 425 |
# File 'lib/dav4rack/resource.rb', line 419 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
452 453 454 455 456 |
# File 'lib/dav4rack/resource.rb', line 452 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.
147 148 149 |
# File 'lib/dav4rack/resource.rb', line 147 def last_modified raise NotImplemented end |
#last_modified=(time) ⇒ Object
Set the time of last modification.
152 153 154 155 |
# File 'lib/dav4rack/resource.rb', line 152 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)
235 236 237 238 239 240 241 242 243 244 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 |
# File 'lib/dav4rack/resource.rb', line 235 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.
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/dav4rack/resource.rb', line 273 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.
323 324 325 |
# File 'lib/dav4rack/resource.rb', line 323 def make_collection NotImplemented end |
#move(dest, overwrite = false) ⇒ Object
HTTP MOVE request.
Move this resource to given destination resource.
216 217 218 |
# File 'lib/dav4rack/resource.rb', line 216 def move(dest, overwrite=false) NotImplemented end |
#name ⇒ Object
Name of the resource
334 335 336 |
# File 'lib/dav4rack/resource.rb', line 334 def name File.basename(path) end |
#parent ⇒ Object
Return parent of this resource
394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/dav4rack/resource.rb', line 394 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_exists? ⇒ Boolean
Does the parent resource exist?
137 138 139 |
# File 'lib/dav4rack/resource.rb', line 137 def parent_exists? parent.exist? end |
#post(request, response) ⇒ Object
HTTP POST request.
Usually forbidden.
195 196 197 |
# File 'lib/dav4rack/resource.rb', line 195 def post(request, response) NotImplemented end |
#property_names ⇒ Object
Available properties
344 345 346 |
# File 'lib/dav4rack/resource.rb', line 344 def property_names %w(creationdate displayname getlastmodified getetag resourcetype getcontenttype getcontentlength) end |
#put(request, response) ⇒ Object
HTTP PUT request.
Save the content of the request.body.
188 189 190 |
# File 'lib/dav4rack/resource.rb', line 188 def put(request, response) NotImplemented end |
#remove_property(name) ⇒ Object
- name
-
Property name
Remove the property from the resource
376 377 378 |
# File 'lib/dav4rack/resource.rb', line 376 def remove_property(name) Forbidden end |
#resource_type ⇒ Object
Return the resource type. Generally only used to specify resource is a collection.
164 165 166 |
# File 'lib/dav4rack/resource.rb', line 164 def resource_type :collection if collection? end |
#set_property(name, value) ⇒ Object
- name
-
String - Property name
- value
-
New value
Set the property to the given value
365 366 367 368 369 370 371 372 |
# File 'lib/dav4rack/resource.rb', line 365 def set_property(name, value) case 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) end end |
#unlock(token) ⇒ Object
- token
-
Lock token
Remove the given lock
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/dav4rack/resource.rb', line 300 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
440 441 442 |
# File 'lib/dav4rack/resource.rb', line 440 def use_compat_mkcol_response? @options[:compat_mkcol] || @options[:compat_all] end |
#use_ms_compat_creationdate? ⇒ Boolean
Returns true if using an MS client
445 446 447 448 449 |
# File 'lib/dav4rack/resource.rb', line 445 def use_ms_compat_creationdate? if(@options[:compat_ms_mangled_creationdate] || @options[:compat_all]) is_ms_client? end end |