cmis-ruby 0.3.1 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2ac859a88499ba57eb289daec0db713d00f666b
4
- data.tar.gz: 2063b514d8cae8f4cb153db41dd3df2e047617d2
3
+ metadata.gz: 9c5f9d4df93396fe4735ee09d4d97db95c48df45
4
+ data.tar.gz: 8938574723b4af67c631415610fcf4f71c4a1987
5
5
  SHA512:
6
- metadata.gz: d42ed7b59b590e0ceb931a5a1b8f2fc0d766413b064453d93587560756afa38afa55f8e77f20d1eec7d5a370e5674065634890d21e0f5bede8f3cc30f2a4c410
7
- data.tar.gz: 61e99957d26901a28f3aed5c9107951a4287973633a64cb61d548f929190815380e24c21b253aceab50ebf0267f11cefe9e41aec998a7642d96fc5ae907dc56e
6
+ metadata.gz: db9eec57e4879bcd1ef91cac2f717754e3ac376995cc11fe4f4c3af91dba94b3f777af086447e785eeb4d3887e3ae0247daea4be8f0fbd44e45737b3f42db71a
7
+ data.tar.gz: d8e06feb0c924488abe759cf1b7525f7d3e1ee33dde2e77e5bee374164d4542bda06b057705dcc974eb806a3f39998db52e0a640a071bdbf3d4ca59ce19f8f2f
@@ -1,3 +1,6 @@
1
+ require 'cmis/core_ext/array'
2
+ require 'cmis/core_ext/hash'
3
+
1
4
  require 'cmis/connection'
2
5
  require 'cmis/exceptions'
3
6
  require 'cmis/server'
@@ -6,6 +9,7 @@ require 'cmis/object_factory'
6
9
  require 'cmis/query_result'
7
10
  require 'cmis/query'
8
11
  require 'cmis/children'
12
+ require 'cmis/relationships'
9
13
  require 'cmis/repository'
10
14
  require 'cmis/object'
11
15
  require 'cmis/document'
@@ -1,10 +1,10 @@
1
1
  require 'active_support'
2
+ require 'json'
2
3
  require 'typhoeus'
3
4
  require 'net/http/post/multipart'
4
5
 
5
6
  module CMIS
6
7
  class Connection
7
-
8
8
  def initialize(service_url, username, password, headers)
9
9
  @service_url = service_url
10
10
  @username = username
@@ -15,123 +15,181 @@ module CMIS
15
15
  end
16
16
 
17
17
  def execute!(params = {}, options = {})
18
- options.stringify_keys!
19
- query = options['query'] || {}
20
- headers = @headers.merge(options['headers'] || {})
18
+ query, headers = parse_options(options)
19
+
20
+ Request.new(service_url: @service_url,
21
+ url_cache: @url_cache,
22
+ params: params,
23
+ query: query,
24
+ headers: headers,
25
+ username: @username,
26
+ password: @password).run
27
+ end
21
28
 
22
- url = get_url(params.delete(:repositoryId), params[:objectId])
29
+ private
23
30
 
24
- params = transform_hash(params)
31
+ def parse_options(options)
32
+ options.symbolize_keys!
33
+ query = options[:query] || {}
34
+ headers = @headers
35
+ headers.merge!(options[:headers]) if options[:headers]
36
+ [ query, headers ]
37
+ end
25
38
 
26
- if params[:cmisaction]
27
- method = params[:content] ? 'multipart_post' : 'post'
28
- body = params
29
- else
30
- method = 'get'
31
- body = nil
32
- query.merge!(params)
39
+ class Request
40
+ def initialize(options)
41
+ @service_url = options[:service_url]
42
+ @url_cache = options[:url_cache]
43
+ @params = massage(options[:params])
44
+ @repository_id = @params.delete(:repositoryId)
45
+ @query = options[:query]
46
+ @headers = options[:headers]
47
+ @username = options[:username]
48
+ @password = options[:password]
33
49
  end
34
50
 
35
- response = perform_request(method: method, url: url,
36
- body: body, query: query, headers: headers)
37
-
38
- content_type = if response.respond_to?(:content_type)
39
- response.content_type
40
- else
41
- response.headers['Content-Type']
51
+ def run
52
+ case method
53
+ when 'get'
54
+ typhoeus_request
55
+ when 'post'
56
+ typhoeus_request
57
+ when 'multipart_post'
58
+ multipart_post
59
+ end
42
60
  end
43
61
 
44
- result = response.body
45
- result = JSON.parse(result) if content_type =~ /application\/json/
46
- result = result.with_indifferent_access if result.is_a? Hash
47
-
48
- check_for_exception!(response.code.to_i, result)
49
-
50
- result
51
- end
52
-
53
- private
62
+ private
63
+
64
+ def typhoeus_request
65
+ options = {
66
+ method: method,
67
+ body: body,
68
+ params: query,
69
+ headers: @headers,
70
+ followlocation: true
71
+ }
72
+ options[:userpwd] = "#{@username}:#{@password}" if @username
73
+ response = Typhoeus::Request.new(url, options).run
74
+ Response.new(response.headers['Content-Type'], response.body).parse!
75
+ end
54
76
 
55
- def check_for_exception!(code, result)
56
- unless (200...300).include?(code)
57
- if result.is_a?(Hash) && result['exception']
58
- exception_class = "CMIS::Exceptions::#{result['exception'].camelize}"
59
- raise exception_class.constantize, "#{result['message']}"
77
+ def multipart_post
78
+ uri = URI.parse(url)
79
+ req = Net::HTTP::Post::Multipart.new(uri.path, body)
80
+ @headers.each { |key, value| req[key] = value }
81
+ req.basic_auth @username, @password if @username
82
+ opts = if uri.scheme == 'https'
83
+ { use_ssl: true , verify_mode: OpenSSL::SSL::VERIFY_NONE }
84
+ else
85
+ {}
86
+ end
87
+ Net::HTTP.start(uri.host, uri.port, opts) do |http|
88
+ response = http.request(req)
89
+ Response.new(response['Content-Type'], response.body).parse!
60
90
  end
61
91
  end
62
- end
63
92
 
64
- def get_url(repository_id, cmis_object_id)
65
- if repository_id.nil?
66
- @service_url
67
- else
68
- urls = repository_urls(repository_id)
69
- if cmis_object_id
70
- urls[:root_folder_url]
93
+ def url
94
+ if @repository_id.nil?
95
+ @service_url
71
96
  else
72
- urls[:repository_url]
97
+ urls = repository_urls(@repository_id)
98
+ if @params[:objectId]
99
+ urls[:root_folder_url]
100
+ else
101
+ urls[:repository_url]
102
+ end
73
103
  end
74
104
  end
75
- end
76
105
 
77
- def repository_urls(repository_id)
78
- if @url_cache[repository_id].nil?
79
- repository_infos = JSON.parse(perform_request(url: @service_url).body)
80
- raise Exceptions::ObjectNotFound, "repositoryId: #{repository_id}" unless repository_infos.has_key?(repository_id)
81
- repository_info = repository_infos[repository_id]
82
- @url_cache[repository_id] = { repository_url: repository_info['repositoryUrl'],
83
- root_folder_url: repository_info['rootFolderUrl'] }
106
+ def method
107
+ if @params[:cmisaction]
108
+ if @params[:content]
109
+ 'multipart_post'
110
+ else
111
+ 'post'
112
+ end
113
+ else
114
+ 'get'
115
+ end
84
116
  end
85
- @url_cache[repository_id]
86
- end
87
117
 
88
- def transform_hash(hash)
89
- hash.reject! { |_, v| v.nil? }
118
+ def body
119
+ @params if @params[:cmisaction]
120
+ end
90
121
 
91
- if content_hash = hash[:content]
92
- hash[:content] = UploadIO.new(content_hash[:stream], content_hash[:mime_type], content_hash[:filename])
122
+ def query
123
+ if @params[:cmisaction]
124
+ @query
125
+ else
126
+ @params.merge(@query)
127
+ end
93
128
  end
94
129
 
95
- if props = hash.delete(:properties)
96
- props.each_with_index do |(id, value), index|
97
- value = value.to_time if value.is_a?(Date) or value.is_a?(DateTime)
98
- value = (value.to_f * 1000).to_i if value.is_a?(Time)
99
- if value.is_a?(Array)
100
- hash.merge!("propertyId[#{index}]" => id)
101
- value.each_with_index do |v, idx|
102
- hash.merge!("propertyValue[#{index}][#{idx}]" => value[idx])
130
+ # TODO: Extract functionality
131
+ def massage(hash)
132
+ hash.compact
133
+
134
+ if content_hash = hash[:content]
135
+ hash[:content] = UploadIO.new(content_hash[:stream],
136
+ content_hash[:mime_type],
137
+ content_hash[:filename])
138
+ end
139
+
140
+ if props = hash.delete(:properties)
141
+ props.each_with_index do |(id, value), index|
142
+ value = value.to_time if value.is_a?(Date) or value.is_a?(DateTime)
143
+ value = (value.to_f * 1000).to_i if value.is_a?(Time)
144
+ if value.is_a?(Array)
145
+ hash.merge!("propertyId[#{index}]" => id)
146
+ value.each_with_index do |v, idx|
147
+ hash.merge!("propertyValue[#{index}][#{idx}]" => value[idx])
148
+ end
149
+ else
150
+ hash.merge!("propertyId[#{index}]" => id,
151
+ "propertyValue[#{index}]" => value)
103
152
  end
104
- else
105
- hash.merge!("propertyId[#{index}]" => id,
106
- "propertyValue[#{index}]" => value)
107
153
  end
108
154
  end
155
+ hash
109
156
  end
110
- hash
111
- end
112
157
 
113
- def perform_request(options)
114
- if options[:method] == 'multipart_post'
115
- multipart_post(options)
116
- else
117
- typhoeus_request(options)
158
+ def repository_urls(repository_id)
159
+ if @url_cache[repository_id].nil?
160
+
161
+ options = { method: 'get' }
162
+ options[:userpwd] = "#{@username}:#{@password}" if @username
163
+ response = Typhoeus::Request.new(@service_url, options).run
164
+ repository_infos = JSON.parse(response.body)
165
+
166
+ unless repository_infos.has_key?(repository_id)
167
+ raise Exceptions::ObjectNotFound, "repositoryId: #{repository_id}"
168
+ end
169
+
170
+ repository_info = repository_infos[repository_id]
171
+ @url_cache[repository_id] = { repository_url: repository_info['repositoryUrl'],
172
+ root_folder_url: repository_info['rootFolderUrl'] }
173
+ end
174
+ @url_cache[repository_id]
118
175
  end
119
176
  end
120
177
 
121
- def typhoeus_request(options)
122
- options[:params] = options.delete(:query)
123
- options[:followlocation] = true
124
- options[:userpwd] = "#{@username}:#{@password}" if @username
125
- Typhoeus::Request.new(options.delete(:url), options).run
126
- end
178
+ class Response
179
+ def initialize(content_type, body)
180
+ @content_type = content_type
181
+ @body = body
182
+ end
183
+
184
+ def parse!
185
+ return @body unless @content_type =~ /application\/json/
127
186
 
128
- def multipart_post(options)
129
- url = URI.parse(options[:url])
130
- req = Net::HTTP::Post::Multipart.new(url.path, options[:body])
131
- options[:headers].each { |key, value| req[key] = value }
132
- req.basic_auth @username, @password if @username
133
- opts = url.scheme == 'https' ? { use_ssl: true , verify_mode: OpenSSL::SSL::VERIFY_NONE } : {}
134
- Net::HTTP.start(url.host, url.port, opts) { |http| http.request(req) }
187
+ result = JSON.parse(@body)
188
+ if result.is_a?(Hash) && ex = result['exception']
189
+ raise "CMIS::Exceptions::#{ex.camelize}".constantize, result['message']
190
+ end
191
+ result.with_indifferent_access
192
+ end
135
193
  end
136
194
  end
137
195
  end
@@ -0,0 +1,14 @@
1
+ class Array
2
+ def with_indifferent_access
3
+ map do |value|
4
+ case value
5
+ when Hash
6
+ value.with_indifferent_access
7
+ when Array
8
+ value.with_indifferent_access
9
+ else
10
+ value
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def compact
3
+ delete_if { |_, v| v.nil? }
4
+ end
5
+ end
@@ -41,10 +41,7 @@ module CMIS
41
41
  result = raw['succinctProperties']
42
42
  elsif raw['properties']
43
43
  result = raw['properties'].reduce({}) do |h, (k, v)|
44
- val = v['value']
45
- val = v['value'].first if v['value'].is_a?(Array) and v['cardinality'] == 'single'
46
- val = Time.at(val / 1000) if val and v['type'] == 'datetime'
47
- h.merge(k => val)
44
+ h.merge(k => sanitize(v))
48
45
  end
49
46
  else
50
47
  result = {}
@@ -52,5 +49,21 @@ module CMIS
52
49
 
53
50
  result.with_indifferent_access
54
51
  end
52
+
53
+ def sanitize(prop)
54
+ value = prop['value']
55
+
56
+ # Sometimes (when?) single values come in an array
57
+ if value.is_a?(Array) && prop['cardinality'] == 'single'
58
+ value = value.first
59
+ end
60
+
61
+ if !!value && prop['type'] == 'datetime'
62
+ # CMIS sends millis since epoch
63
+ value = Time.at(value / 1000.0)
64
+ end
65
+
66
+ value
67
+ end
55
68
  end
56
69
  end
@@ -50,13 +50,8 @@ module CMIS
50
50
  objectId: cmis_object_id }, opts)
51
51
  end
52
52
 
53
- def relationships(direction = :either, opts = {})
54
- result = connection.execute!({ cmisselector: 'relationships',
55
- repositoryId: repository.id,
56
- objectId: cmis_object_id,
57
- relationshipDirection: direction }, opts)
58
-
59
- result['objects'].map { |r| Relationship.new(r, repository) }
53
+ def relationships(opts = {})
54
+ Relationships.new(self, opts)
60
55
  end
61
56
 
62
57
  def policies(opts = {})
@@ -12,5 +12,9 @@ module CMIS
12
12
  def target(opts = {})
13
13
  repository.object(target_id, opts)
14
14
  end
15
+
16
+ def create(opts = {})
17
+ repository.create_relationship(self, opts)
18
+ end
15
19
  end
16
20
  end
@@ -0,0 +1,101 @@
1
+ require 'active_support/core_ext/hash/slice'
2
+
3
+ module CMIS
4
+ class Relationships
5
+ # Options: from, page_size
6
+ def initialize(object, options = {})
7
+ @object = object
8
+ @options = options.stringify_keys!
9
+
10
+ init_options
11
+ end
12
+
13
+ # Options: limit
14
+ def each_relationship(options = {}, &block)
15
+ return enum_for(:each_relationship, options) unless block_given?
16
+
17
+ init_options
18
+ limit = parse_limit(options)
19
+ counter = 0
20
+
21
+ while has_next?
22
+ results.each do |object|
23
+ break unless counter < limit
24
+ yield object
25
+ counter = counter.next
26
+ end
27
+ end
28
+ end
29
+
30
+ # Options: limit
31
+ def each_page(options = {}, &block)
32
+ return enum_for(:each_page, options) unless block_given?
33
+
34
+ init_options
35
+ limit = parse_limit(options)
36
+ counter = 0
37
+
38
+ while has_next?
39
+ break unless counter < limit
40
+ yield r = results
41
+ counter += r.size
42
+ end
43
+ end
44
+
45
+ def results
46
+ result = do_get_relationships
47
+
48
+ @skip_count += result.results.size
49
+ @has_next = result.has_more_items
50
+ @total = result.num_items
51
+
52
+ result.results
53
+ end
54
+
55
+ def has_next?
56
+ @has_next
57
+ end
58
+
59
+ def total
60
+ @total ||= do_get_relationships.num_items
61
+ end
62
+
63
+ private
64
+
65
+ def init_options
66
+ @max_items = @options['page_size'] || 10
67
+ @skip_count = @options['from'] || 0
68
+ @direction = @options['direction'] || :either
69
+ @include_subtypes = @options['include_subtypes']
70
+ @type_id = @options['type_id']
71
+ @has_next = true
72
+
73
+ @opts = @options.slice('query', 'headers')
74
+ end
75
+
76
+ def parse_limit(options)
77
+ options.stringify_keys!
78
+ limit = options['limit'] || 10
79
+ limit = BigDecimal::INFINITY if limit == :all
80
+ raise 'Not a valid limit' unless limit.is_a? Numeric
81
+ limit
82
+ end
83
+
84
+ def do_get_relationships
85
+ result = @object.connection.execute!({ cmisselector: 'relationships',
86
+ repositoryId: @object.repository.id,
87
+ objectId: @object.cmis_object_id,
88
+ maxItems: @max_items,
89
+ skipCount: @skip_count,
90
+ relationshipDirection: @direction,
91
+ includeSubRelationshipTypes: @include_subtypes,
92
+ typeId: @type_id }, @opts)
93
+
94
+ results = result['objects'].map do |r|
95
+ ObjectFactory.create(r, @object.repository)
96
+ end
97
+
98
+ QueryResult.new(results, result['numItems'], result['hasMoreItems'])
99
+ end
100
+ end
101
+ end
@@ -1,5 +1,3 @@
1
- require 'active_support'
2
-
3
1
  module CMIS
4
2
  class Repository
5
3
  attr_reader :connection
@@ -118,14 +116,7 @@ module CMIS
118
116
  QUOTE = "\'"
119
117
 
120
118
  def normalize(value)
121
- case value
122
- when DateTime
123
- value = value.strftime('%Y-%m-%dT%H:%M:%S.%L')
124
- "TIMESTAMP '#{value}'"
125
- when Time
126
- value = value.strftime('%Y-%m-%dT%H:%M:%S.%L')
127
- "TIMESTAMP '#{value}'"
128
- when Date
119
+ if value.respond_to?(:strftime)
129
120
  value = value.strftime('%Y-%m-%dT%H:%M:%S.%L')
130
121
  "TIMESTAMP '#{value}'"
131
122
  else
@@ -1,3 +1,3 @@
1
1
  module CMIS
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.4'
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'cmis-ruby'
2
- require 'json'
3
2
 
4
3
  META = CMIS::Server.new.repository('meta')
5
4
 
@@ -43,7 +43,7 @@ describe CMIS::Object do
43
43
  doc = create_document
44
44
  rels = doc.relationships
45
45
  rels.should_not be_nil
46
- rels.each do |r|
46
+ rels.each_relationship do |r|
47
47
  r.should be_a_kind_of CMIS::Relationship
48
48
  end
49
49
  doc.delete
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmis-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenneth Geerts
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-02-27 00:00:00.000000000 Z
12
+ date: 2014-03-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: typhoeus
@@ -69,6 +69,8 @@ files:
69
69
  - lib/cmis-ruby.rb
70
70
  - lib/cmis/children.rb
71
71
  - lib/cmis/connection.rb
72
+ - lib/cmis/core_ext/array.rb
73
+ - lib/cmis/core_ext/hash.rb
72
74
  - lib/cmis/document.rb
73
75
  - lib/cmis/exceptions.rb
74
76
  - lib/cmis/folder.rb
@@ -81,6 +83,7 @@ files:
81
83
  - lib/cmis/query.rb
82
84
  - lib/cmis/query_result.rb
83
85
  - lib/cmis/relationship.rb
86
+ - lib/cmis/relationships.rb
84
87
  - lib/cmis/repository.rb
85
88
  - lib/cmis/server.rb
86
89
  - lib/cmis/type.rb