cmis-ruby 0.3.1 → 0.3.4

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