calligraphy 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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