Rubocop and update files

This commit is contained in:
Brandon Robins
2017-12-28 20:30:17 -06:00
parent a5c476c82c
commit 5b7d67ee57
9 changed files with 323 additions and 186 deletions

View File

@@ -2,3 +2,17 @@ AllCops:
TargetRubyVersion: 2.3 TargetRubyVersion: 2.3
Exclude: Exclude:
- 'spec/dummy/**/*' - 'spec/dummy/**/*'
Metrics/ClassLength:
Exclude:
- 'lib/calligraphy/resource/resource.rb'
Metrics/AbcSize:
Exclude:
- 'lib/calligraphy/rails/mapper.rb'
Metrics/LineLength:
Exclude:
- 'lib/calligraphy/rails/mapper.rb'
Metrics/MethodLength:
Exclude:
- 'lib/calligraphy/rails/mapper.rb'
Style/ClassVars:
Enabled: False

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'calligraphy/rails/mapper' require 'calligraphy/rails/mapper'
require 'calligraphy/rails/web_dav_methods' require 'calligraphy/rails/web_dav_methods'
require 'calligraphy/rails/web_dav_preconditions' require 'calligraphy/rails/web_dav_preconditions'
@@ -24,6 +26,7 @@ require 'calligraphy/web_dav_request/proppatch'
require 'calligraphy/web_dav_request/put' require 'calligraphy/web_dav_request/put'
require 'calligraphy/web_dav_request/unlock' require 'calligraphy/web_dav_request/unlock'
#:nodoc:
module Calligraphy module Calligraphy
# Constants used throughout Calligraphy. # Constants used throughout Calligraphy.
DAV_NS = 'DAV:' DAV_NS = 'DAV:'
@@ -39,17 +42,17 @@ module Calligraphy
# HTTP methods allowed by the WebDavRequests controller. # HTTP methods allowed by the WebDavRequests controller.
mattr_accessor :allowed_http_methods mattr_accessor :allowed_http_methods
@@allowed_http_methods = %w( @@allowed_http_methods = %w[
options get put delete copy move options get put delete copy move
mkcol propfind proppatch lock unlock mkcol propfind proppatch lock unlock
) ]
# Proc responsible for returning the user's password, API key, # Proc responsible for returning the user's password, API key,
# or HA1 digest hash so that Rails can check user credentials. # or HA1 digest hash so that Rails can check user credentials.
# Should be overridden to handle your particular application's # Should be overridden to handle your particular application's
# user and/or authentication setup. # user and/or authentication setup.
mattr_accessor :digest_password_procedure mattr_accessor :digest_password_procedure
@@digest_password_procedure = Proc.new { |username| 'changeme!' } @@digest_password_procedure = proc { |_username| 'changeme!' }
# If Digest Authentication is enabled by default. # If Digest Authentication is enabled by default.
mattr_accessor :enable_digest_authentication mattr_accessor :enable_digest_authentication
@@ -61,15 +64,15 @@ module Calligraphy
# Maximum lock lifetime in seconds. # Maximum lock lifetime in seconds.
mattr_accessor :lock_timeout_period mattr_accessor :lock_timeout_period
@@lock_timeout_period = 86400 @@lock_timeout_period = 86_400
# The HTTP actions Calligraphy uses to create mappings between WebDAV # The HTTP actions Calligraphy uses to create mappings between WebDAV
# HTTP verbs and URLs and WebDAV controller actions. # HTTP verbs and URLs and WebDAV controller actions.
mattr_accessor :web_dav_actions mattr_accessor :web_dav_actions
@@web_dav_actions = %i( @@web_dav_actions = %i[
options get put delete copy move options get put delete copy move
mkcol propfind proppatch lock unlock mkcol propfind proppatch lock unlock
) ]
# Default way to set up Calligraphy. # Default way to set up Calligraphy.
# Run `rails generate calligraphy:install` to generate a # Run `rails generate calligraphy:install` to generate a

View File

@@ -1,93 +1,97 @@
module ActionDispatch::Routing # frozen_string_literal: true
module ActionDispatch
module Routing
class Mapper class Mapper
#:nodoc:
module HttpHelpers module HttpHelpers
# Define a Calligraphy route that only recognizes HTTP COPY. # Define a Calligraphy route that only recognizes HTTP COPY.
# copy 'bacon', to: 'food#bacon' # copy 'bacon', to: 'food#bacon'
def copy(*args, &block) def copy(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :copy, args, &block map_method :copy, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP HEAD. # Define a Calligraphy route that only recognizes HTTP HEAD.
# head 'bacon', to: 'food#bacon' # head 'bacon', to: 'food#bacon'
def head(*args, &block) def head(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :head, args, &block map_method :head, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP LOCK. # Define a Calligraphy route that only recognizes HTTP LOCK.
# lock 'bacon', to: 'food#bacon' # lock 'bacon', to: 'food#bacon'
def lock(*args, &block) def lock(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :lock, args, &block map_method :lock, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP MKCOL. # Define a Calligraphy route that only recognizes HTTP MKCOL.
# mkcol 'bacon', to: 'food#bacon' # mkcol 'bacon', to: 'food#bacon'
def mkcol(*args, &block) def mkcol(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :mkcol, args, &block map_method :mkcol, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP MOVE. # Define a Calligraphy route that only recognizes HTTP MOVE.
# move 'bacon', to: 'food#bacon' # move 'bacon', to: 'food#bacon'
def move(*args, &block) def move(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :move, args, &block map_method :move, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP OPTIONS. # Define a Calligraphy route that only recognizes HTTP OPTIONS.
# options 'bacon', to: 'food#bacon' # options 'bacon', to: 'food#bacon'
def options(*args, &block) def options(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :options, args, &block map_method :options, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PROPFIND. # Define a Calligraphy route that only recognizes HTTP PROPFIND.
# propfind 'bacon', to: 'food#bacon' # propfind 'bacon', to: 'food#bacon'
def propfind(*args, &block) def propfind(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :propfind, args, &block map_method :propfind, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PROPPATCH. # Define a Calligraphy route that only recognizes HTTP PROPPATCH.
# proppatch 'bacon', to: 'food#bacon' # proppatch 'bacon', to: 'food#bacon'
def proppatch(*args, &block) def proppatch(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :proppatch, args, &block map_method :proppatch, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP UNLOCK. # Define a Calligraphy route that only recognizes HTTP UNLOCK.
# unlock 'bacon', to: 'food#bacon' # unlock 'bacon', to: 'food#bacon'
def unlock(*args, &block) def unlock(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :unlock, args, &block map_method :unlock, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP DELETE. # Define a Calligraphy route that only recognizes HTTP DELETE.
# web_dav_delete 'broccoli', to: 'food#broccoli' # web_dav_delete 'broccoli', to: 'food#broccoli'
def web_dav_delete(*args, &block) def web_dav_delete(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :delete, args, &block map_method :delete, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP GET. # Define a Calligraphy route that only recognizes HTTP GET.
# web_dav_get 'bacon', to: 'food#bacon' # web_dav_get 'bacon', to: 'food#bacon'
def web_dav_get(*args, &block) def web_dav_get(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :get, args, &block map_method :get, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PUT. # Define a Calligraphy route that only recognizes HTTP PUT.
# web_dav_put 'bacon', to: 'food#bacon' # web_dav_put 'bacon', to: 'food#bacon'
def web_dav_put(*args, &block) def web_dav_put(*args, &block)
args = set_web_dav_args args args = web_dav_args args
map_method :put, args, &block map_method :put, args, &block
end end
private private
def set_web_dav_args(args) def web_dav_args(args)
options = { options = {
controller: 'calligraphy/rails/web_dav_requests', controller: 'calligraphy/rails/web_dav_requests',
action: 'invoke_method' action: 'invoke_method'
@@ -96,8 +100,12 @@ module ActionDispatch::Routing
end end
end end
#:nodoc:
module Resources module Resources
#:nodoc:
class Resource class Resource
# Returns the available WebDAV HTTP verbs based on Calligraphy
# configuration and Rails routing options.
def web_dav_actions def web_dav_actions
if @only if @only
Array(@only).map(&:to_sym) Array(@only).map(&:to_sym)
@@ -115,8 +123,8 @@ module ActionDispatch::Routing
# #
# calligraphy_resource :photos # calligraphy_resource :photos
# #
# creates eleven different routes in your application, all mapping to the # creates eleven different routes in your application, all mapping to
# WebDavRequests controller: # the WebDavRequests controller:
# #
# OPTIONS /photos/*resource # OPTIONS /photos/*resource
# GET /photos/*resource # GET /photos/*resource
@@ -138,9 +146,8 @@ module ActionDispatch::Routing
with_scope_level(:resource) do with_scope_level(:resource) do
options = apply_action_options options options = apply_action_options options
singleton_resoure = ActionDispatch::Routing::Mapper::SingletonResource
resource_scope(singleton_resoure.new resources.pop, api_only?, @scope[:shallow], options) do resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
yield if block_given? yield if block_given?
concerns(options[:concerns]) if options[:concerns] concerns(options[:concerns]) if options[:concerns]
@@ -157,8 +164,8 @@ module ActionDispatch::Routing
# Rails already defines GET, PUT, and DELETE actions which we don't # Rails already defines GET, PUT, and DELETE actions which we don't
# want to override. Instead, we map WebDAV GET, PUT, and DELETE # want to override. Instead, we map WebDAV GET, PUT, and DELETE
# HTTP actions to 'web_dav_' prefixed methods. # HTTP actions to 'web_dav_' prefixed methods.
if [:get, :put, :delete].include? action if %i[get put delete].include? action
send "web_dav_#{action.to_s}", '*resource' send "web_dav_#{action}", '*resource'
else else
send action, '*resource' send action, '*resource'
end end
@@ -167,3 +174,4 @@ module ActionDispatch::Routing
end end
end end
end end
end

View File

@@ -26,7 +26,7 @@ module Calligraphy
def can_copy?(options) def can_copy?(options)
copy_options = { can_copy: false, ancestor_exist: false, locked: false } copy_options = { can_copy: false, ancestor_exist: false, locked: false }
overwrite = is_true? options[:overwrite] overwrite = true? options[:overwrite]
destination = options[:destination].tap { |s| s.slice! @mount_point } destination = options[:destination].tap { |s| s.slice! @mount_point }
copy_options[:ancestor_exist] = File.exist? parent_path(destination) copy_options[:ancestor_exist] = File.exist? parent_path(destination)
@@ -62,9 +62,10 @@ module Calligraphy
File.directory? @src_path File.directory? @src_path
end end
# Creates a duplicate of the resource in `options[:destination]`.
def copy(options) def copy(options)
destination = options[:destination].tap { |s| s.slice! @mount_point } destination = options[:destination].tap { |s| s.slice! @mount_point }
preserve_existing = is_false? options[:overwrite] preserve_existing = false? options[:overwrite]
to_path = join_paths @root_dir, destination to_path = join_paths @root_dir, destination
to_path_exists = File.exist? to_path to_path_exists = File.exist? to_path

View File

@@ -1,143 +1,248 @@
# frozen_string_literal: true
module Calligraphy module Calligraphy
# Resource base class.
#
# All custom resource classes should be inherited from Resource and should
# implement the relevant methods needed for the desired level of WebDAV
# support.
class Resource class Resource
attr_accessor :client_nonce, :contents, :updated_at attr_accessor :client_nonce, :contents, :updated_at
attr_reader :full_request_path, :mount_point, :request_body, :request_path, :root_dir attr_reader :full_request_path, :mount_point, :request_body, :request_path,
:root_dir
def initialize(resource: nil, req: nil, mount: nil, root_dir: nil) def initialize(resource: nil, req: nil, mount: nil, root_dir: nil)
@full_request_path = req&.original_url @full_request_path = req&.original_url
@mount_point = mount || req&.path&.tap { |s| s.slice! resource } @mount_point = mount || req&.path&.tap { |s| s.slice! resource }
@request_body = req&.body&.read || '' @request_body = req&.body&.read || ''
@request_path = mount.nil? ? resource : resource.split(mount)[-1] @request_path = mount.nil? ? resource : resource.split(mount)[-1]
@root_dir = root_dir
end end
# Responsible for returning a boolean value indicating if an ancestor
# exists for the resource.
#
# Used in COPY and MKCOL requests.
def ancestor_exist? def ancestor_exist?
raise NotImplementedError raise NotImplementedError
end end
def can_copy?(options) # Responsible for returning a boolean value indicating if the resource
# can be copied.
#
# Used in COPY and MOVE (which inherits from COPY) requests.
def can_copy?(_options)
raise NotImplementedError raise NotImplementedError
end end
# Responsible for returning a boolean value indicating if the resource
# is a collection.
#
# Used in DELETE, MKCOL, MOVE, and PUT requests.
def collection? def collection?
raise NotImplementedError raise NotImplementedError
end end
def copy(options) # Responsible for creating a duplicate of the resource in
# `options[:destination]` (see section 9.8 of RFC4918).
#
# Used in COPY and MOVE (which inherits from COPY) requests.
def copy(_options)
raise NotImplementedError raise NotImplementedError
end end
# Responsible for creating a new collection based on the resource (see
# section 9.3 of RFC4918).
#
# Used in MKCOL requests.
def create_collection def create_collection
raise NotImplementedError raise NotImplementedError
end end
# A DAV-compliant resource can advertise several classes of compliance.
# `dav_compliance` is responsible for returning the classes of WebDAV
# compliance that the resource supports (see section 18 of RFC4918).
#
# Used in OPTIONS requests.
def dav_compliance def dav_compliance
'1, 2, 3' '1, 2, 3'
end end
# Responsible for deleting a resource collection (see section 9.6 of
# RFC4918).
#
# Used in DELETE and MOVE requests.
def delete_collection def delete_collection
raise NotImplementedError raise NotImplementedError
end end
# Responsible for returning unique identifier used to create an etag.
#
# Used in precondition validation, as well as GET, HEAD, and PROPFIND
# requests.
def etag def etag
raise NotImplementedError raise NotImplementedError
end end
# Responsible for indicating if the resource already exists.
#
# Used in DELETE, LOCK, MKCOL, and MOVE requests.
def exists? def exists?
raise NotImplementedError raise NotImplementedError
end end
def lock(nodes, depth='infinity') # Responsible for creating a lock on the resource (see section 9.10 of
# RFC4918).
#
# Used in LOCK requests.
def lock(_nodes, _depth = 'infinity')
raise NotImplementedError raise NotImplementedError
end end
# Responsible for indicating if a resource lock is exclusive.
#
# Used in LOCK requests.
def lock_is_exclusive? def lock_is_exclusive?
raise NotImplementedError raise NotImplementedError
end end
def lock_tokens # Responsible for indicating if a resource is current locked.
raise NotImplementedError #
end # Used in LOCK requests.
def locked? def locked?
raise NotImplementedError raise NotImplementedError
end end
def locked_to_user?(headers=nil) # Responsible for indicating if a resource is locked to the current user.
#
# Used in DELETE, LOCK, MOVE, PROPPATCH, and PUT requests.
def locked_to_user?(_headers = nil)
raise NotImplementedError raise NotImplementedError
end end
def propfind(nodes) # Responsible for handling the retrieval of properties defined on the
# resource (see section 9.1 of RFC4918).
#
# Used in PROPFIND requests.
def propfind(_nodes)
raise NotImplementedError raise NotImplementedError
end end
def proppatch(nodes) # Responsible for handling the addition and/or removal of properties
# defined on the resource through a PROPPATCH request (see section 9.2 of
# RFC4918).
#
# Used in PROPPATCH requests.
def proppatch(_nodes)
raise NotImplementedError raise NotImplementedError
end end
# Responsible for setting and returning the contents of a resource
# if it is readable (see section 9.4 of RFC4918).
#
# Used in GET requests.
def read def read
raise NotImplementedError raise NotImplementedError
end end
# Responsible for indicating if a resource is readable.
#
# Used in GET and HEAD requests.
def readable? def readable?
exists? && !collection? exists? && !collection?
end end
# Responsible for refreshing locks (see section 9.10.2 of RFC4918).
#
# Used in LOCK requests.
def refresh_lock def refresh_lock
raise NotImplementedError raise NotImplementedError
end end
def unlock(token) # Responsible for unlocking a resource lock (see section 9.11 of RFC4918).
#
# Used in UNLOCK requests.
def unlock(_token)
raise NotImplementedError raise NotImplementedError
end end
def write(contents=@request_body.to_s) # Responsible for writing contents to a resource (see section 9.7 of
# RFC4918).
#
# Used in PUT requests.
def write(_contents = @request_body.to_s)
raise NotImplementedError raise NotImplementedError
end end
private private
# DAV property which can be retrieved by a PROPFIND request. `creationdate`
# records the time and date the resource was created (see section 15.1 of
# RFC4918).
def creationdate def creationdate
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request. `displayname`
# returns a name for the resource that is suitable for presentation to the
# user (see section 15.2 of RFC4918).
def displayname def displayname
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `getcontentlanguage` returns the Content-Language header value (see
# section 15.3 of RFC4918).
def getcontentlanguage def getcontentlanguage
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `getcontentlength` returns the Content-Length header value (see section
# 15.4 of RFC4918).
def getcontentlength def getcontentlength
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `getcontenttype` returns the Content-Type header value (see section
# 15.5 of RFC4918).
def getcontenttype def getcontenttype
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `getetag` returns the ETag header value (see section 15.6 of RFC4918).
def getetag def getetag
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `getlastmodified` returns the Last-Modified header value (see section
# 15.7 of RFC4918).
def getlastmodified def getlastmodified
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `lockdiscovery` describes the active locks on a resource (see section
# 15.8 of RFC4918).
def lockdiscovery def lockdiscovery
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `resourcetype` specifies the nature of the resource (see section 15.9 of
# RFC4918).
def resourcetype def resourcetype
raise NotImplementedError raise NotImplementedError
end end
# DAV property which can be retrieved by a PROPFIND request.
# `supportedlock` provides a listing of the lock capabilities supported by
# the resource (see section 15.10 of RFC4918).
def supportedlock def supportedlock
raise NotImplementedError raise NotImplementedError
end end
def get_custom_property(prop)
raise NotImplementedError
end
end end
end end

View File

@@ -1,13 +1,16 @@
module Calligraphy # frozen_string_literal: true
module Utils
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE']
def is_true?(val) module Calligraphy
# Miscellaneous convenience methods.
module Utils
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].freeze
def true?(val)
TRUE_VALUES.include? val TRUE_VALUES.include? val
end end
def is_false?(val) def false?(val)
FALSE_VALUES.include? val FALSE_VALUES.include? val
end end
@@ -26,13 +29,14 @@ module Calligraphy
def map_array_of_hashes(arr_hashes) def map_array_of_hashes(arr_hashes)
[].tap do |output_array| [].tap do |output_array|
arr_hashes.each do |hash| arr_hashes.each do |hash|
output_array.push hash.map { |k, v| v } output_array.push(hash.map { |_k, v| v })
end end
end end
end end
def extract_lock_token(if_header) def extract_lock_token(if_header)
if_header.scan(Calligraphy::LOCK_TOKEN_REGEX)&.flatten[0] token = if_header.scan(Calligraphy::LOCK_TOKEN_REGEX)
token.flatten.first if token.is_a? Array
end end
def lockentry_hash(scope, type) def lockentry_hash(scope, type)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Calligraphy module Calligraphy
VERSION = '0.2.1' VERSION = '0.2.1'
end end

View File

@@ -7,7 +7,7 @@ module Calligraphy
def execute def execute
return :locked if @resource.locked_to_user? @headers return :locked if @resource.locked_to_user? @headers
if @resource.is_true? options[:overwrite] if @resource.true? options[:overwrite]
previous_resource_existed = overwrite_destination previous_resource_existed = overwrite_destination
end end

View File

@@ -4,12 +4,12 @@ RSpec.describe 'Resource' do
context 'base method' do context 'base method' do
resource_methods_without_inputs = %w( resource_methods_without_inputs = %w(
ancestor_exist? collection? create_collection delete_collection etag ancestor_exist? collection? create_collection delete_collection etag
exists? lock_is_exclusive? lock_tokens locked? read readable? refresh_lock exists? lock_is_exclusive? locked? read readable? refresh_lock
creationdate displayname getcontentlanguage getcontentlength getcontenttype creationdate displayname getcontentlanguage getcontentlength getcontenttype
getetag getlastmodified lockdiscovery resourcetype supportedlock getetag getlastmodified lockdiscovery resourcetype supportedlock
) )
resource_methods_with_inputs = %w( resource_methods_with_inputs = %w(
can_copy? copy lock locked_to_user? propfind proppatch unlock write get_custom_property can_copy? copy lock locked_to_user? propfind proppatch unlock write
) )
resource_methods_without_inputs.each do |method| resource_methods_without_inputs.each do |method|