marklogic 0.0.1

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.
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