calligraphy 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +64 -0
- data/lib/calligraphy.rb +58 -0
- data/lib/calligraphy/copy.rb +37 -0
- data/lib/calligraphy/delete.rb +17 -0
- data/lib/calligraphy/file_resource.rb +487 -0
- data/lib/calligraphy/get.rb +12 -0
- data/lib/calligraphy/lock.rb +52 -0
- data/lib/calligraphy/mkcol.rb +20 -0
- data/lib/calligraphy/move.rb +31 -0
- data/lib/calligraphy/propfind.rb +18 -0
- data/lib/calligraphy/proppatch.rb +20 -0
- data/lib/calligraphy/put.rb +12 -0
- data/lib/calligraphy/rails/mapper.rb +120 -0
- data/lib/calligraphy/rails/web_dav_requests_controller.rb +208 -0
- data/lib/calligraphy/resource.rb +93 -0
- data/lib/calligraphy/unlock.rb +15 -0
- data/lib/calligraphy/utils.rb +38 -0
- data/lib/calligraphy/version.rb +3 -0
- data/lib/calligraphy/web_dav_request.rb +31 -0
- data/lib/calligraphy/xml/builder.rb +147 -0
- data/lib/calligraphy/xml/namespace.rb +10 -0
- data/lib/calligraphy/xml/node.rb +23 -0
- data/lib/calligraphy/xml/utils.rb +18 -0
- data/spec/spec_helper.rb +46 -0
- metadata +97 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
module Calligraphy
|
2
|
+
class Resource
|
3
|
+
attr_accessor :client_nonce, :contents, :updated_at
|
4
|
+
attr_reader :full_request_path, :mount_point, :request_body, :request_path, :root_dir
|
5
|
+
|
6
|
+
def initialize(resource: nil, req: nil, mount: nil, root_dir: nil)
|
7
|
+
@full_request_path = req&.original_url
|
8
|
+
@mount_point = mount || req&.path&.tap { |s| s.slice! resource }
|
9
|
+
@request_body = req&.body&.read || ''
|
10
|
+
@request_path = mount.nil? ? resource : resource.split(mount)[-1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def ancestor_exist?
|
14
|
+
raise NotImplemented
|
15
|
+
end
|
16
|
+
|
17
|
+
def can_copy?(options)
|
18
|
+
raise NotImplemented
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection?
|
22
|
+
raise NotImplemented
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy(options)
|
26
|
+
raise NotImplemented
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_collection
|
30
|
+
raise NotImplemented
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_collection
|
34
|
+
raise NotImplemented
|
35
|
+
end
|
36
|
+
|
37
|
+
def etag
|
38
|
+
raise NotImplemented
|
39
|
+
end
|
40
|
+
|
41
|
+
def exists?
|
42
|
+
raise NotImplemented
|
43
|
+
end
|
44
|
+
|
45
|
+
def lock(nodes, depth='infinity')
|
46
|
+
raise NotImplemented
|
47
|
+
end
|
48
|
+
|
49
|
+
def lock_is_exclusive?
|
50
|
+
raise NotImplemented
|
51
|
+
end
|
52
|
+
|
53
|
+
def lock_tokens
|
54
|
+
raise NotImplemented
|
55
|
+
end
|
56
|
+
|
57
|
+
def locked?
|
58
|
+
raise NotImplemented
|
59
|
+
end
|
60
|
+
|
61
|
+
def locked_to_user?(headers=nil)
|
62
|
+
raise NotImplemented
|
63
|
+
end
|
64
|
+
|
65
|
+
def propfind(nodes)
|
66
|
+
raise NotImplemented
|
67
|
+
end
|
68
|
+
|
69
|
+
def proppatch(nodes)
|
70
|
+
raise NotImplemented
|
71
|
+
end
|
72
|
+
|
73
|
+
def read
|
74
|
+
raise NotImplemented
|
75
|
+
end
|
76
|
+
|
77
|
+
def readable?
|
78
|
+
exists? && !collection?
|
79
|
+
end
|
80
|
+
|
81
|
+
def refresh_lock
|
82
|
+
raise NotImplemented
|
83
|
+
end
|
84
|
+
|
85
|
+
def unlock(token)
|
86
|
+
raise NotImplemented
|
87
|
+
end
|
88
|
+
|
89
|
+
def write(contents=@request_body.to_s)
|
90
|
+
raise NotImplemented
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Calligraphy
|
2
|
+
class Unlock < WebDavRequest
|
3
|
+
def request
|
4
|
+
return :bad_request if @headers['Lock-Token'].nil?
|
5
|
+
|
6
|
+
@resource.unlock lock_token_header
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def lock_token_header
|
12
|
+
@headers['Lock-Token'].gsub Calligraphy::LOCK_TOKEN_ANGLE_REGEX, ''
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Calligraphy
|
2
|
+
module Utils
|
3
|
+
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
|
4
|
+
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE']
|
5
|
+
|
6
|
+
def is_true?(val)
|
7
|
+
TRUE_VALUES.include? val
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_false?(val)
|
11
|
+
FALSE_VALUES.include? val
|
12
|
+
end
|
13
|
+
|
14
|
+
def join_paths(*paths)
|
15
|
+
paths.join '/'
|
16
|
+
end
|
17
|
+
|
18
|
+
def split_and_pop(path:, separator: '/')
|
19
|
+
path.split(separator)[0..-2]
|
20
|
+
end
|
21
|
+
|
22
|
+
def obj_exists_and_is_not_type?(obj:, type:)
|
23
|
+
obj.nil? ? false : obj != type
|
24
|
+
end
|
25
|
+
|
26
|
+
def map_array_of_hashes(arr_hashes)
|
27
|
+
[].tap do |output_array|
|
28
|
+
arr_hashes.each do |hash|
|
29
|
+
output_array.push hash.map { |k, v| v }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_lock_token(if_header)
|
35
|
+
if_header.scan(Calligraphy::LOCK_TOKEN_REGEX)&.flatten[0]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Calligraphy
|
2
|
+
class WebDavRequest
|
3
|
+
attr_accessor :resource, :response
|
4
|
+
attr_reader :headers, :request
|
5
|
+
|
6
|
+
def initialize(headers:, request:, response:, resource:)
|
7
|
+
@headers = headers
|
8
|
+
@request = request
|
9
|
+
@response = response
|
10
|
+
@resource = resource
|
11
|
+
end
|
12
|
+
|
13
|
+
def request
|
14
|
+
raise NotImplemented
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def body
|
20
|
+
@resource.request_body
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_xml_content_type
|
24
|
+
@response.content_type = 'application/xml'
|
25
|
+
end
|
26
|
+
|
27
|
+
def xml_builder
|
28
|
+
Calligraphy::XML::Builder.new server_protocol: @request.env['SERVER_PROTOCOL']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Calligraphy::XML
|
2
|
+
class Builder
|
3
|
+
SUPPORTED_NS_TAGS = %w(
|
4
|
+
creationdate displayname exclusive getcontentlanguage getcontentlength
|
5
|
+
getcontenttype getetag getlastmodified href lockdiscovery lockscope
|
6
|
+
locktype owner write
|
7
|
+
)
|
8
|
+
|
9
|
+
attr_reader :dav_ns, :default_ns, :server_protocol
|
10
|
+
|
11
|
+
def initialize(dav_ns: 'D', server_protocol: 'HTTP/1.1')
|
12
|
+
@dav_ns = dav_ns
|
13
|
+
@default_ns = { "xmlns:#{@dav_ns}" => 'DAV:' }
|
14
|
+
@server_protocol = server_protocol
|
15
|
+
end
|
16
|
+
|
17
|
+
def lock_res(activelock_properties)
|
18
|
+
build :prop do |xml|
|
19
|
+
xml.lockdiscovery do
|
20
|
+
activelock_properties.each do |properties|
|
21
|
+
activelock xml, properties
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def propfind_res(path, properties)
|
28
|
+
multistatus do |xml|
|
29
|
+
href xml, path
|
30
|
+
propstat xml, properties[:found], :ok
|
31
|
+
propstat xml, properties[:not_found], :not_found
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def proppatch_res(path, actions)
|
36
|
+
multistatus do |xml|
|
37
|
+
href xml, path
|
38
|
+
propstat xml, actions[:set]
|
39
|
+
propstat xml, actions[:remove]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def build(tag)
|
46
|
+
Nokogiri::XML::Builder.new do |xml|
|
47
|
+
xml[@dav_ns].send(tag, @default_ns) do
|
48
|
+
yield xml
|
49
|
+
end
|
50
|
+
end.to_xml
|
51
|
+
end
|
52
|
+
|
53
|
+
def activelock(xml, property_set)
|
54
|
+
xml.activelock do
|
55
|
+
property_set.each do |property|
|
56
|
+
property_drilldown xml, property
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def href(xml, path)
|
62
|
+
xml.href path
|
63
|
+
end
|
64
|
+
|
65
|
+
def multistatus
|
66
|
+
build :multistatus do |xml|
|
67
|
+
xml.response do
|
68
|
+
yield xml
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def prop(xml, property_set)
|
74
|
+
xml.prop do
|
75
|
+
property_set.each do |property|
|
76
|
+
property_drilldown xml, property
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def propstat(xml, property_set, status=:ok)
|
82
|
+
return unless property_set.length > 0
|
83
|
+
|
84
|
+
xml.propstat do
|
85
|
+
prop xml, property_set
|
86
|
+
status xml, status
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def resourcetype(xml, property)
|
91
|
+
if property.children.text == 'collection'
|
92
|
+
xml[@dav_ns].resourcetype do
|
93
|
+
xml.send 'collection'
|
94
|
+
end
|
95
|
+
else
|
96
|
+
xml[@dav_ns].resourcetype
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def status(xml, status)
|
101
|
+
xml.status status_message status
|
102
|
+
end
|
103
|
+
|
104
|
+
# NOTE: `xml[@dav_ns].send timeout` results in Timeout being called, so
|
105
|
+
# we have this timeout method for convenience
|
106
|
+
def timeout(xml, property)
|
107
|
+
xml[@dav_ns].timeout do
|
108
|
+
xml.text property.text
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def property_drilldown(xml, property)
|
113
|
+
if property.is_a? Array
|
114
|
+
property.each do |prop|
|
115
|
+
property_drilldown xml, prop
|
116
|
+
end
|
117
|
+
elsif property.children && property.text.nil?
|
118
|
+
xml.send property.name do
|
119
|
+
property.children.each do |child|
|
120
|
+
property_drilldown xml, child
|
121
|
+
end
|
122
|
+
end
|
123
|
+
elsif property.name == 'resourcetype'
|
124
|
+
resourcetype xml, property
|
125
|
+
elsif property.name == 'timeout'
|
126
|
+
timeout xml, property
|
127
|
+
elsif SUPPORTED_NS_TAGS.include? property.name
|
128
|
+
xml[@dav_ns].send property.name do
|
129
|
+
xml.text property.text
|
130
|
+
end
|
131
|
+
elsif property.namespace && property.namespace.href
|
132
|
+
xml.send property.name, xmlns: property.namespace.href do
|
133
|
+
xml.text property.text
|
134
|
+
end
|
135
|
+
else
|
136
|
+
xml.send property.name, property.text do
|
137
|
+
xml.parent.namespace = nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def status_message(status)
|
143
|
+
status_code = Rack::Utils.status_code status
|
144
|
+
[@server_protocol, status_code, Rack::Utils::HTTP_STATUS_CODES[status_code]].join ' '
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Calligraphy::XML
|
2
|
+
class Node
|
3
|
+
attr_accessor :children, :name, :namespace, :text
|
4
|
+
|
5
|
+
def initialize(node=nil)
|
6
|
+
unless node.nil?
|
7
|
+
@name = node.name
|
8
|
+
@text = node.text unless node.text.empty?
|
9
|
+
|
10
|
+
if node.namespace
|
11
|
+
@namespace = Calligraphy::XML::Namespace.new node.namespace
|
12
|
+
end
|
13
|
+
|
14
|
+
if node.children&.length > 0
|
15
|
+
@children = []
|
16
|
+
node.children.each do |child|
|
17
|
+
@children.push Calligraphy::XML::Node.new child
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Calligraphy::XML
|
2
|
+
module Utils
|
3
|
+
def xml_for(body:, node:)
|
4
|
+
xml = Nokogiri::XML body
|
5
|
+
return :bad_request unless xml.errors.empty?
|
6
|
+
|
7
|
+
namespace = nil
|
8
|
+
xml.root.namespace_definitions.each do |n|
|
9
|
+
namespace = "#{n.prefix}|" if n&.href == Calligraphy::DAV_NS && !n.prefix.nil?
|
10
|
+
end
|
11
|
+
namespace
|
12
|
+
|
13
|
+
node = node.split(' ').map! { |n| namespace + n }.join(' ') if namespace
|
14
|
+
|
15
|
+
xml.css(node).children
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.expect_with :rspec do |expectations|
|
3
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
4
|
+
end
|
5
|
+
|
6
|
+
config.mock_with :rspec do |mocks|
|
7
|
+
mocks.verify_partial_doubles = true
|
8
|
+
end
|
9
|
+
|
10
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
11
|
+
|
12
|
+
config.filter_run_when_matching :focus
|
13
|
+
|
14
|
+
# Allows RSpec to persist some state between runs in order to support
|
15
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
16
|
+
# you configure your source control system to ignore this file.
|
17
|
+
# config.example_status_persistence_file_path = "spec/examples.txt"
|
18
|
+
|
19
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
20
|
+
# recommended. For more details, see:
|
21
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
22
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
23
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
24
|
+
config.disable_monkey_patching!
|
25
|
+
|
26
|
+
config.warnings = true
|
27
|
+
|
28
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
29
|
+
# file, and it's useful to allow more verbose output when running an
|
30
|
+
# individual spec file.
|
31
|
+
if config.files_to_run.one?
|
32
|
+
# Use the documentation formatter for detailed output,
|
33
|
+
# unless a formatter has already been configured
|
34
|
+
# (e.g. via a command-line flag).
|
35
|
+
config.default_formatter = "doc"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print the 10 slowest examples and example groups at the
|
39
|
+
# end of the spec run, to help surface which specs are running
|
40
|
+
# particularly slow.
|
41
|
+
config.profile_examples = 10
|
42
|
+
|
43
|
+
config.order = :random
|
44
|
+
|
45
|
+
Kernel.srand config.seed
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: calligraphy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brandon Robins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: WebDAV framework and extension for Rails 5
|
42
|
+
email: brandon@onebnottwo.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- lib/calligraphy.rb
|
50
|
+
- lib/calligraphy/copy.rb
|
51
|
+
- lib/calligraphy/delete.rb
|
52
|
+
- lib/calligraphy/file_resource.rb
|
53
|
+
- lib/calligraphy/get.rb
|
54
|
+
- lib/calligraphy/lock.rb
|
55
|
+
- lib/calligraphy/mkcol.rb
|
56
|
+
- lib/calligraphy/move.rb
|
57
|
+
- lib/calligraphy/propfind.rb
|
58
|
+
- lib/calligraphy/proppatch.rb
|
59
|
+
- lib/calligraphy/put.rb
|
60
|
+
- lib/calligraphy/rails/mapper.rb
|
61
|
+
- lib/calligraphy/rails/web_dav_requests_controller.rb
|
62
|
+
- lib/calligraphy/resource.rb
|
63
|
+
- lib/calligraphy/unlock.rb
|
64
|
+
- lib/calligraphy/utils.rb
|
65
|
+
- lib/calligraphy/version.rb
|
66
|
+
- lib/calligraphy/web_dav_request.rb
|
67
|
+
- lib/calligraphy/xml/builder.rb
|
68
|
+
- lib/calligraphy/xml/namespace.rb
|
69
|
+
- lib/calligraphy/xml/node.rb
|
70
|
+
- lib/calligraphy/xml/utils.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
homepage: http://www.github.com/eanlain/calligraphy
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.6.14
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: WebDAV framework and extension for Rails 5
|
96
|
+
test_files:
|
97
|
+
- spec/spec_helper.rb
|