marklogic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +10 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +17 -0
  8. data/Guardfile +45 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +31 -0
  11. data/Rakefile +6 -0
  12. data/lib/marklogic.rb +21 -0
  13. data/lib/marklogic/app_server.rb +60 -0
  14. data/lib/marklogic/application.rb +244 -0
  15. data/lib/marklogic/collection.rb +265 -0
  16. data/lib/marklogic/connection.rb +308 -0
  17. data/lib/marklogic/consts.rb +35 -0
  18. data/lib/marklogic/cursor.rb +238 -0
  19. data/lib/marklogic/database.rb +205 -0
  20. data/lib/marklogic/database_settings.rb +13 -0
  21. data/lib/marklogic/database_settings/element_word_lexicon.rb +28 -0
  22. data/lib/marklogic/database_settings/geospatial_element_child_index.rb +41 -0
  23. data/lib/marklogic/database_settings/geospatial_element_index.rb +38 -0
  24. data/lib/marklogic/database_settings/geospatial_element_pair_index.rb +42 -0
  25. data/lib/marklogic/database_settings/geospatial_path_index.rb +37 -0
  26. data/lib/marklogic/database_settings/index.rb +27 -0
  27. data/lib/marklogic/database_settings/range_element_index.rb +77 -0
  28. data/lib/marklogic/database_settings/range_field_index.rb +37 -0
  29. data/lib/marklogic/database_settings/range_path_index.rb +37 -0
  30. data/lib/marklogic/exceptions.rb +5 -0
  31. data/lib/marklogic/forest.rb +47 -0
  32. data/lib/marklogic/loggable.rb +46 -0
  33. data/lib/marklogic/object_id.rb +46 -0
  34. data/lib/marklogic/persistence.rb +29 -0
  35. data/lib/marklogic/queries.rb +18 -0
  36. data/lib/marklogic/queries/and_not_query.rb +14 -0
  37. data/lib/marklogic/queries/and_query.rb +14 -0
  38. data/lib/marklogic/queries/base_query.rb +40 -0
  39. data/lib/marklogic/queries/boost_query.rb +14 -0
  40. data/lib/marklogic/queries/collection_query.rb +14 -0
  41. data/lib/marklogic/queries/container_query.rb +15 -0
  42. data/lib/marklogic/queries/directory_query.rb +20 -0
  43. data/lib/marklogic/queries/document_fragment_query.rb +13 -0
  44. data/lib/marklogic/queries/document_query.rb +14 -0
  45. data/lib/marklogic/queries/geospatial_query.rb +44 -0
  46. data/lib/marklogic/queries/locks_fragment_query.rb +13 -0
  47. data/lib/marklogic/queries/near_query.rb +31 -0
  48. data/lib/marklogic/queries/not_in_query.rb +14 -0
  49. data/lib/marklogic/queries/not_query.rb +13 -0
  50. data/lib/marklogic/queries/or_query.rb +24 -0
  51. data/lib/marklogic/queries/properties_fragment_query.rb +13 -0
  52. data/lib/marklogic/queries/range_query.rb +67 -0
  53. data/lib/marklogic/queries/value_query.rb +44 -0
  54. data/lib/marklogic/queries/word_query.rb +38 -0
  55. data/lib/marklogic/version.rb +3 -0
  56. data/marklogic.gemspec +23 -0
  57. data/spec/marklogic/app_server_spec.rb +21 -0
  58. data/spec/marklogic/application_spec.rb +105 -0
  59. data/spec/marklogic/collection_spec.rb +154 -0
  60. data/spec/marklogic/connection_spec.rb +128 -0
  61. data/spec/marklogic/cursor_spec.rb +219 -0
  62. data/spec/marklogic/database_settings/element_word_lexicon_spec.rb +21 -0
  63. data/spec/marklogic/database_settings/geospatial_element_child_index_spec.rb +26 -0
  64. data/spec/marklogic/database_settings/geospatial_element_index_spec.rb +24 -0
  65. data/spec/marklogic/database_settings/geospatial_element_pair_index_spec.rb +27 -0
  66. data/spec/marklogic/database_settings/geospatial_path_index_spec.rb +23 -0
  67. data/spec/marklogic/database_settings/range_element_index_spec.rb +34 -0
  68. data/spec/marklogic/database_settings/range_field_index_spec.rb +23 -0
  69. data/spec/marklogic/database_settings/range_path_index_spec.rb +23 -0
  70. data/spec/marklogic/database_spec.rb +108 -0
  71. data/spec/marklogic/forest_spec.rb +30 -0
  72. data/spec/marklogic/queries/and_not_query_spec.rb +13 -0
  73. data/spec/marklogic/queries/and_query_spec.rb +31 -0
  74. data/spec/marklogic/queries/boost_query_spec.rb +13 -0
  75. data/spec/marklogic/queries/collection_query_spec.rb +16 -0
  76. data/spec/marklogic/queries/container_query_spec.rb +11 -0
  77. data/spec/marklogic/queries/directory_query_spec.rb +21 -0
  78. data/spec/marklogic/queries/document_fragment_query_spec.rb +11 -0
  79. data/spec/marklogic/queries/document_query_spec.rb +16 -0
  80. data/spec/marklogic/queries/locks_fragement_query_spec.rb +11 -0
  81. data/spec/marklogic/queries/near_query_spec.rb +62 -0
  82. data/spec/marklogic/queries/not_in_query_spec.rb +13 -0
  83. data/spec/marklogic/queries/not_query_spec.rb +11 -0
  84. data/spec/marklogic/queries/or_query_spec.rb +32 -0
  85. data/spec/marklogic/queries/properties_fragment_query_spec.rb +11 -0
  86. data/spec/marklogic/queries/range_query_spec.rb +71 -0
  87. data/spec/marklogic/queries/value_query_spec.rb +68 -0
  88. data/spec/marklogic/queries/word_query_spec.rb +53 -0
  89. data/spec/spec_helper.rb +68 -0
  90. metadata +186 -0
@@ -0,0 +1,265 @@
1
+ require 'securerandom'
2
+
3
+ module MarkLogic
4
+ class Collection
5
+
6
+ attr_accessor :collection
7
+ attr_reader :database
8
+
9
+ alias_method :name, :collection
10
+ def initialize(name, database)
11
+ @collection = name
12
+ @database = database
13
+ @operators = %w{GT LT GE LE EQ NE ASC DESC}
14
+ end
15
+
16
+ def count
17
+ MarkLogic::Cursor.new(self).count
18
+ end
19
+
20
+ def load(id)
21
+ url = "/v1/documents?uri=#{gen_uri(id)}&format=json"
22
+ response = @database.connection.get(url)
23
+ raise Exception.new("Invalid response: #{response.code.to_i}, #{response.body}") unless response.code.to_i == 200
24
+ JSON.parse(response.body)
25
+ end
26
+
27
+ def save(doc)
28
+ if (doc.is_a?(Array))
29
+ docs = {}
30
+ doc.each do |d|
31
+ docs[doc_uri(d)] = JSON.generate(d)
32
+ end
33
+ body = build_multipart_body(docs)
34
+ response = @database.connection.post_multipart("/v1/documents", body)
35
+ raise Exception.new("Invalid response: #{response.code.to_i}, #{response.body}\n") unless response.code.to_i == 200
36
+ else
37
+ uri = doc_uri(doc)
38
+ url = "/v1/documents?uri=#{uri}&format=json&collection=#{collection}"
39
+ json = JSON.generate(doc)
40
+ response = @database.connection.put(url, json)
41
+ raise Exception.new("Invalid response: #{response.code.to_i}, #{response.body}\n") unless [201, 204].include? response.code.to_i
42
+ doc[:_id] || doc[:id] || doc['_id'] || doc['id']
43
+ end
44
+ end
45
+
46
+ def update(selector, document, opts={})
47
+ find(selector).each do |doc|
48
+ document.each do |key, value|
49
+ case key
50
+ when "$set"
51
+ value.each do |kk, vv|
52
+ doc[kk.to_s] = vv
53
+ end
54
+ when "$inc"
55
+ value.each do |kk, vv|
56
+ prev = doc[kk.to_s] || 0
57
+ doc[kk.to_s] = prev + vv
58
+ end
59
+ when "$unset"
60
+ value.keys.each do |kk|
61
+ doc.delete(kk.to_s)
62
+ end
63
+ when "$push"
64
+ value.each do |kk, vv|
65
+ if doc.has_key?(kk.to_s)
66
+ doc[kk.to_s].push(vv)
67
+ else
68
+ doc[kk.to_s] = [vv]
69
+ end
70
+ end
71
+ when "$pushAll"
72
+ value.each do |kk, vv|
73
+ if doc.has_key?(kk.to_s)
74
+ doc[kk.to_s] = doc[kk.to_s] + vv
75
+ else
76
+ doc[kk.to_s] = vv
77
+ end
78
+ end
79
+ end
80
+ save(doc)
81
+ end
82
+ end
83
+ end
84
+
85
+ alias_method :create, :save
86
+ alias_method :insert, :save
87
+
88
+ def remove(query = nil, options = {})
89
+ if query.nil? || (query.is_a?(Hash) && query.empty?)
90
+ drop
91
+ else
92
+ if query.class == Hash
93
+ query = from_criteria(query)
94
+ elsif query.nil?
95
+ query = Queries::AndQuery.new()
96
+ end
97
+
98
+ xqy = %Q{cts:search(fn:collection("#{collection}"), #{query.to_xqy}, ("unfiltered")) / xdmp:node-delete(.)}
99
+ response = @database.connection.run_query(xqy, "xquery")
100
+ raise Exception.new("Invalid response: #{response.code.to_i}, #{response.body}") unless response.code.to_i == 200
101
+ end
102
+ end
103
+
104
+ def drop
105
+ url = "/v1/search?collection=#{collection}"
106
+ response =@database.connection.delete(url)
107
+ raise Exception.new("Invalid response: #{response.code.to_i}, #{response.body}") unless [204].include? response.code.to_i
108
+ end
109
+
110
+ def find_one(query = nil, options = {})
111
+ opts = options.merge(:per_page => 1)
112
+ find(query, opts).next
113
+ end
114
+
115
+ def find(query = nil, options = {})
116
+ if query.class == Hash
117
+ query = from_criteria(query)
118
+ elsif query.nil?
119
+ query = Queries::AndQuery.new()
120
+ end
121
+ options[:query] = query
122
+ cursor = MarkLogic::Cursor.new(self, options)
123
+
124
+ if block_given?
125
+ yield cursor
126
+ nil
127
+ else
128
+ cursor
129
+ end
130
+ end
131
+
132
+ def build_query(name, operator, value, query_options = {})
133
+ if database.has_range_index?(name) && (query_options.has_key?(:case_sensitive) == false || query_options[:case_sensitive] == true)
134
+ index = database.range_index(name)
135
+ type = index.scalar_type
136
+ Queries::RangeQuery.new(name, operator, type, value, query_options)
137
+ elsif operator != 'EQ'
138
+ raise MissingIndexError.new("Missing index on #{name}")
139
+ elsif value.nil?
140
+ Queries::OrQuery.new([
141
+ Queries::ValueQuery.new(name, value, query_options),
142
+ Queries::NotQuery.new(Queries::ContainerQuery.new(name, Queries::AndQuery.new))
143
+ ])
144
+ elsif operator == 'EQ'
145
+ Queries::ValueQuery.new(name, value, query_options)
146
+ end
147
+ end
148
+
149
+ # Builds a MarkLogic Query from Mongo Style Criteria
150
+ #
151
+ # @param [Hash] criteria The Criteria to use when searching
152
+ #
153
+ # @example Build a query from criteria
154
+ #
155
+ # # Query on age == 3
156
+ # collection.from_criteria({ 'age' => { '$eq' => 3 } })
157
+ #
158
+ # # Query on age < 3
159
+ # collection.from_criteria({ 'age' => { '$lt' => 3 } })
160
+ #
161
+ # # Query on age <= 3
162
+ # collection.from_criteria({ 'age' => { '$le' => 3 } })
163
+ #
164
+ # # Query on age > 3
165
+ # collection.from_criteria({ 'age' => { '$gt' => 3 } })
166
+ #
167
+ # # Query on age >= 3
168
+ # collection.from_criteria({ 'age' => { '$ge' => 3 } })
169
+ #
170
+ # # Query on age != 3
171
+ # collection.from_criteria({ 'age' => { '$ne' => 3 } })
172
+ #
173
+ # @since 0.0.1
174
+ def from_criteria(criteria)
175
+ queries = []
176
+
177
+ criteria.each do |k, v|
178
+ name, operator, index_type, value = nil
179
+ query_options = {}
180
+
181
+ if (v.is_a?(Hash))
182
+ name = k.to_s
183
+ query_options.merge!(v.delete(:options) || {})
184
+
185
+ sub_queries = []
186
+ v.each do |kk, vv|
187
+ operator = kk.to_s.gsub('$', '').upcase || "EQ"
188
+ if @operators.include?(operator)
189
+ value = vv
190
+ value = value.to_s if value.is_a?(MarkLogic::ObjectId)
191
+ sub_queries << build_query(name, operator, value, query_options)
192
+ elsif value.is_a?(Hash)
193
+ child_queries = value.map do |kk, vv|
194
+ build_query(kk, vv, query_options)
195
+ end
196
+ sub_queries << Queries::ContainerQuery.new(name, Queries::AndQuery.new(child_queries))
197
+ end
198
+ end
199
+
200
+ if sub_queries.length > 1
201
+ queries << Queries::AndQuery.new(sub_queries)
202
+ elsif sub_queries.length == 1
203
+ queries << sub_queries[0]
204
+ end
205
+ else
206
+ name = k.to_s
207
+ value = v
208
+ operator = "EQ"
209
+ queries << build_query(name, operator, value, query_options)
210
+ end
211
+ end
212
+
213
+ if queries.length > 1
214
+ MarkLogic::Queries::AndQuery.new(*queries)
215
+ elsif queries.length == 1
216
+ queries[0]
217
+ end
218
+ end
219
+
220
+ private
221
+
222
+ def doc_uri(doc)
223
+ id = doc[:_id] || doc['_id']
224
+ if id.nil?
225
+ id = SecureRandom.hex
226
+ doc[:_id] = id
227
+ end
228
+ gen_uri(id)
229
+ end
230
+
231
+ def gen_uri(id)
232
+ if id.is_a?(Hash)
233
+ id_str = id.hash.to_s
234
+ else
235
+ id_str = id.to_s
236
+ end
237
+ %Q{/#{collection}/#{id_str}.json}
238
+ end
239
+
240
+ def build_multipart_body(docs, boundary = "BOUNDARY")
241
+ tmp = ""
242
+
243
+ # collection
244
+ metadata = JSON.generate({ collections: [ collection ]})
245
+ tmp << %Q{--#{boundary}\r\n}
246
+ tmp << %Q{Content-Type: application/json\r\n}
247
+ tmp << %Q{Content-Disposition: inline; category=metadata\r\n}
248
+ tmp << %Q{Content-Length: #{metadata.size}\r\n\r\n}
249
+ tmp << metadata
250
+ tmp << %Q{\r\n}
251
+
252
+ docs.each do |uri, doc|
253
+ # doc
254
+ tmp << %Q{--#{boundary}\r\n}
255
+ tmp << %Q{Content-Type: application/json\r\n}
256
+ tmp << %Q{Content-Disposition: attachment; filename="#{uri}"; category=content; format=json\r\n}
257
+ tmp << %Q{Content-Length: #{doc.size}\r\n\r\n}
258
+ tmp << doc
259
+ tmp << %Q{\r\n}
260
+ end
261
+ tmp << "--#{boundary}--"
262
+ tmp
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,308 @@
1
+ require 'net/http'
2
+ require 'date'
3
+ require 'json'
4
+ require 'digest'
5
+
6
+ module Net
7
+ module HTTPHeader
8
+ @@nonce_count = -1
9
+ CNONCE = Digest::MD5.hexdigest "%x" % (Time.now.to_i + rand(65535))
10
+
11
+ def create_digest_auth(user, password, response)
12
+ # based on http://segment7.net/projects/ruby/snippets/digest_auth.rb
13
+ @@nonce_count += 1
14
+
15
+ response['www-authenticate'] =~ /^(\w+) (.*)/
16
+
17
+ params = {}
18
+ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
19
+
20
+ digest_auth(user, password, params)
21
+ end
22
+
23
+ def digest_auth(user, password, params)
24
+
25
+ a_1 = "#{user}:#{params['realm']}:#{password}"
26
+ a_2 = "#{@method}:#{@path}"
27
+ request_digest = ''
28
+ request_digest << Digest::MD5.new.update(a_1).hexdigest
29
+ request_digest << ':' << params['nonce']
30
+ request_digest << ':' << ('%08x' % @@nonce_count)
31
+ request_digest << ':' << CNONCE
32
+ request_digest << ':' << params['qop']
33
+ request_digest << ':' << Digest::MD5.new.update(a_2).hexdigest
34
+
35
+ header = []
36
+ header << "Digest username=\"#{user}\""
37
+ header << "realm=\"#{params['realm']}\""
38
+
39
+ header << "qop=#{params['qop']}"
40
+
41
+ header << "algorithm=MD5"
42
+ header << "uri=\"#{@path}\""
43
+ header << "nonce=\"#{params['nonce']}\""
44
+ header << "nc=#{'%08x' % @@nonce_count}"
45
+ header << "cnonce=\"#{CNONCE}\""
46
+ header << "response=\"#{Digest::MD5.hexdigest(request_digest)}\""
47
+
48
+ @header['Authorization'] = header
49
+ params
50
+ end
51
+
52
+ def basic_auth(user, password)
53
+ encoded = Base64.encode64("#{user}:#{password}").chomp
54
+
55
+ @header['Authorization'] = ["Basic #{encoded}"]
56
+ end
57
+ end
58
+ end
59
+
60
+ module MarkLogic
61
+ class Connection
62
+ include MarkLogic::Loggable
63
+
64
+ attr_accessor :admin, :manage, :app_services, :username, :password, :host, :port, :request_retries
65
+
66
+ def self.configure(options = {})
67
+ @@__host_name = options[:host] if options[:host]
68
+ @@__app_services_port = options[:app_services_port] if options[:app_services_port]
69
+ @@__admin_port = options[:admin_port] if options[:admin_port]
70
+ @@__manage_port = options[:manage_port] if options[:manage_port]
71
+ @@__default_user = options[:default_user] if options[:default_user]
72
+ @@__default_password = options[:default_password] if options[:default_password]
73
+ end
74
+
75
+ def self.default_user
76
+ @@__default_user ||= "admin"
77
+ end
78
+
79
+ def self.default_password
80
+ @@__default_password ||= "admin"
81
+ end
82
+
83
+ def self.host_name
84
+ @@__host_name ||= "localhost"
85
+ end
86
+
87
+ def self.app_services_port
88
+ @@__app_services_port ||= 8000
89
+ end
90
+
91
+ def self.admin_port
92
+ @@__admin_port ||= 8001
93
+ end
94
+
95
+ def self.manage_port
96
+ @@__manage_port ||= 8002
97
+ end
98
+
99
+ def self.admin_connection(username = self.default_user, password = self.default_password)
100
+ @@__admin_connection ||= Connection.new(self.host_name, self.admin_port, username, password)
101
+ end
102
+
103
+ def self.manage_connection(username = self.default_user, password = self.default_password)
104
+ @@__manage_connection ||= Connection.new(self.host_name, self.manage_port, username, password)
105
+ end
106
+
107
+ def self.app_services_connection(username = self.default_user, password = self.default_password)
108
+ @@__app_services_connection ||= Connection.new(self.host_name, self.app_services_port, username, password)
109
+ end
110
+
111
+ def host
112
+ @host
113
+ end
114
+
115
+ def initialize(host, port, username = nil, password = nil, options = {})
116
+ @host = host
117
+ @port = port
118
+ @username = username || self.class.default_user
119
+ @password = password || self.class.default_password
120
+ @request_retries = options[:request_retries] || 3
121
+ @http = Net::HTTP.new(host, port)
122
+ end
123
+
124
+ def run_query(query, type = "javascript", options = {})
125
+ params = {
126
+ type.to_sym => query
127
+ }
128
+ params[:dbname] = options[:db] if options[:db]
129
+ response = post('/eval', params)
130
+ # :xquery => options[:query],
131
+ # :locale => LOCALE,
132
+ # :tzoffset => "-18000",
133
+ # :dbname => options[:db]
134
+ end
135
+
136
+ def head(url, headers = {})
137
+ request(url, 'head', headers)
138
+ end
139
+
140
+ def get(url, headers = {})
141
+ request(url, 'get', headers)
142
+ end
143
+
144
+ def put(url, body = nil, headers = {})
145
+ request(url, 'put', headers, body)
146
+ end
147
+
148
+ def post(url, params = nil, headers = {})
149
+ request(url, 'post', headers, nil, params)
150
+ end
151
+
152
+ def post_json(url, params = nil, headers = {})
153
+ request(url, 'post', headers, ::JSON.generate(params))
154
+ end
155
+
156
+ def post_multipart(url, body = nil, headers = {}, boundary = "BOUNDARY")
157
+ headers['Content-Type'] = %Q{multipart/mixed; boundary=#{boundary}}
158
+ headers['Accept'] = %Q{application/json}
159
+ request(url, 'post', headers, body)
160
+ end
161
+
162
+ def delete(url, headers = {}, body = nil)
163
+ request(url, 'delete', headers, body)
164
+ end
165
+
166
+ def wait_for_restart(body)
167
+ json = JSON.parse(body)
168
+ ts_value = json["restart"]["last-startup"][0]["value"]
169
+ timestamp = DateTime.iso8601(ts_value).to_time
170
+ new_timestamp = timestamp
171
+
172
+ code = nil
173
+ logger.debug "Waiting for restart"
174
+ until code == 200 and new_timestamp > timestamp
175
+ begin
176
+ rr = get(%Q{/admin/v1/timestamp})
177
+ code = rr.code.to_i
178
+ bb = rr.body
179
+ new_timestamp = DateTime.iso8601(bb).to_time if code == 200
180
+ rescue
181
+ end
182
+ end
183
+ logger.debug "Restart Complete"
184
+ end
185
+
186
+ def ==(other)
187
+ @host == other.host &&
188
+ @port == other.port &&
189
+ @username == other.username &&
190
+ @password == other.password
191
+ end
192
+
193
+ private
194
+
195
+ def default_headers
196
+ {
197
+ 'Connection' => 'keep-alive',
198
+ 'Keep-Alive' => '30',
199
+ 'User-Agent' => 'MarkLogic',
200
+ 'Content-type' => 'application/json'
201
+ }
202
+ end
203
+
204
+ def split_multipart(response)
205
+ if response.read_body
206
+ body = response.body
207
+
208
+ if body.length == 0
209
+ response.body = nil
210
+ return
211
+ end
212
+
213
+ content_type = response['Content-Type']
214
+ if (content_type and content_type.match(/multipart\/mixed.*/))
215
+ boundary = $1 if content_type =~ /^.*boundary=(.*)$/
216
+
217
+ body.sub!(Regexp.new("[\r\n]+--#{boundary}--[\r\n]+$", Regexp::MULTILINE), "")
218
+ body.sub!(Regexp.new("^[\r\n]+--#{boundary}.+?[\r\n]+", Regexp::MULTILINE), "")
219
+
220
+ values = []
221
+ body.split(Regexp.new(%Q{[\r\n]+--#{boundary}[\r\n]+}, Regexp::MULTILINE)).each do |item|
222
+ splits = item.split(/\r\n\r\n/m)
223
+ metas = splits[0]
224
+ raw_value = splits[1]
225
+
226
+ value_content_type = type = xpath = nil
227
+
228
+ metas.split(/\r\n/m).each do |meta|
229
+ if meta.match(/^Content-Type:.*/m)
230
+ value_content_type = $1 if meta =~ /Content-Type:\s+(.*)$/
231
+ elsif meta.match(/^X-Primitive:.*/)
232
+ type = $1 if meta =~ /X-Primitive:\s+(.*)$/
233
+ elsif meta.match(/^X-Path:.*/)
234
+ xpath = $1 if meta =~ /X-Path:\s+(.*)$/
235
+ end
236
+ end
237
+
238
+ if (value_content_type == "application/json") then
239
+ value = JSON.parse(raw_value)
240
+ else
241
+ case type
242
+ when "integer"
243
+ value = raw_value.to_i
244
+ when "boolean"
245
+ value = raw_value == "true"
246
+ when "decimal"
247
+ value = raw_value.to_f
248
+ else
249
+ value = raw_value
250
+ end
251
+ end
252
+ values.push(value)
253
+ end
254
+
255
+ if (values.length == 1)
256
+ values = values[0]
257
+ end
258
+ output = values
259
+ else
260
+ output = body
261
+ end
262
+ response.body = output
263
+ end
264
+ end
265
+
266
+ def request(url, verb = 'get', headers = {}, body = nil, params = nil)
267
+ tries ||= request_retries
268
+
269
+ logger.debug "Retry #{request_retries - tries} of #{request_retries} for:\n#{body}" unless (tries == request_retries)
270
+ all_headers = {}
271
+
272
+ # configure headers
273
+ default_headers.merge(headers).each do |k, v|
274
+ all_headers[k] = v
275
+ end
276
+
277
+ request = Net::HTTP.const_get(verb.capitalize).new(url, all_headers)
278
+
279
+ # Send the auth info if we have it
280
+ if @auth
281
+ request.digest_auth(@username, @password, @auth)
282
+ end
283
+
284
+ request.set_form_data(params) if (params)
285
+ request.body = body if (body)
286
+
287
+ response = @http.request request
288
+
289
+ if (response.code.to_i == 401 and @username and @password)
290
+ auth_method = $1.downcase if response['www-authenticate'] =~ /^(\w+) (.*)/
291
+ if (auth_method == "basic")
292
+ request.basic_auth(@username, @password)
293
+ elsif (auth_method == "digest")
294
+ @auth = request.create_digest_auth(@username, @password, response)
295
+ end
296
+
297
+ response = @http.request request
298
+ end
299
+
300
+ # puts("#{response.code} : #{verb.upcase} => ://#{@host}:#{@port}#{url} :: #{body} #{params}")
301
+
302
+ split_multipart(response)
303
+ response
304
+ rescue Net::ReadTimeout => e
305
+ retry unless (tries -= 1).zero?
306
+ end
307
+ end
308
+ end