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.
@@ -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,3 @@
1
+ module Calligraphy
2
+ VERSION = '0.2.0'
3
+ 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,10 @@
1
+ module Calligraphy::XML
2
+ class Namespace
3
+ attr_accessor :href, :prefix
4
+
5
+ def initialize(namespace)
6
+ @href = namespace.href if namespace.href
7
+ @prefix = namespace.prefix if namespace.prefix
8
+ end
9
+ end
10
+ 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
@@ -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