arbetsformedlingen 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.ruby-style-guide.yml +270 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -0
- data/arbetsformedlingen.gemspec +10 -9
- data/bin/console +1 -0
- data/lib/arbetsformedlingen/api/client.rb +48 -30
- data/lib/arbetsformedlingen/api/ledigtarbete_client.rb +6 -1
- data/lib/arbetsformedlingen/api/matchning_client.rb +16 -10
- data/lib/arbetsformedlingen/api/request.rb +10 -13
- data/lib/arbetsformedlingen/api/response.rb +71 -0
- data/lib/arbetsformedlingen/api/results/ad_result.rb +36 -18
- data/lib/arbetsformedlingen/api/results/matchning_result.rb +42 -3
- data/lib/arbetsformedlingen/api/results/soklista_result.rb +16 -2
- data/lib/arbetsformedlingen/api/soap_request.rb +11 -10
- data/lib/arbetsformedlingen/api/values/ad_result_values.rb +6 -1
- data/lib/arbetsformedlingen/api/values/create_ad_page.rb +3 -1
- data/lib/arbetsformedlingen/api/values/matchning_result_values.rb +9 -1
- data/lib/arbetsformedlingen/api/values/soklista_values.rb +17 -1
- data/lib/arbetsformedlingen/api/ws_occupation_client.rb +26 -1
- data/lib/arbetsformedlingen/codes/country_code.rb +3 -1
- data/lib/arbetsformedlingen/codes/drivers_license_code.rb +6 -3
- data/lib/arbetsformedlingen/codes/experience_required_code.rb +3 -1
- data/lib/arbetsformedlingen/codes/municipality_code.rb +3 -1
- data/lib/arbetsformedlingen/codes/occupation_code.rb +3 -1
- data/lib/arbetsformedlingen/codes/salary_type_code.rb +3 -1
- data/lib/arbetsformedlingen/key_struct.rb +5 -3
- data/lib/arbetsformedlingen/models/application_method.rb +3 -1
- data/lib/arbetsformedlingen/models/company.rb +2 -2
- data/lib/arbetsformedlingen/models/document.rb +4 -2
- data/lib/arbetsformedlingen/models/dry/predicates.rb +2 -0
- data/lib/arbetsformedlingen/models/dry/types.rb +5 -3
- data/lib/arbetsformedlingen/models/model.rb +2 -0
- data/lib/arbetsformedlingen/models/packet.rb +3 -1
- data/lib/arbetsformedlingen/models/packet_xml_builder.rb +8 -3
- data/lib/arbetsformedlingen/models/position.rb +5 -3
- data/lib/arbetsformedlingen/models/publication.rb +3 -1
- data/lib/arbetsformedlingen/models/qualification.rb +3 -1
- data/lib/arbetsformedlingen/models/salary.rb +3 -1
- data/lib/arbetsformedlingen/models/schedule.rb +4 -2
- data/lib/arbetsformedlingen/soap_builder.rb +3 -1
- data/lib/arbetsformedlingen/version.rb +3 -1
- metadata +43 -25
@@ -1,13 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'arbetsformedlingen/api/request'
|
2
4
|
require 'arbetsformedlingen/api/values/create_ad_page'
|
3
5
|
|
4
6
|
module Arbetsformedlingen
|
5
7
|
module API
|
8
|
+
# API client for ledigtarbete
|
6
9
|
class LedigtarbeteClient
|
10
|
+
# Base URL for ledigtarbete
|
7
11
|
BASE_URL = 'http://api.arbetsformedlingen.se/ledigtarbete'.freeze
|
8
12
|
|
13
|
+
# HTTP headers
|
9
14
|
HEADERS = {
|
10
|
-
'Content-type' => 'text/xml'
|
15
|
+
'Content-type' => 'text/xml',
|
11
16
|
}.freeze
|
12
17
|
|
13
18
|
# Post ad to API
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'time'
|
3
5
|
require 'arbetsformedlingen/api/request'
|
@@ -5,13 +7,16 @@ require 'arbetsformedlingen/api/results/matchning_result'
|
|
5
7
|
|
6
8
|
module Arbetsformedlingen
|
7
9
|
module API
|
10
|
+
# API client for matchning
|
8
11
|
class MatchningClient
|
9
12
|
attr_reader :request
|
10
13
|
|
14
|
+
# Initialize client
|
11
15
|
def initialize(request: Request.new)
|
12
16
|
@request = request
|
13
17
|
end
|
14
18
|
|
19
|
+
# rubocop:disable Metrics/LineLength
|
15
20
|
# Find matching ads from API
|
16
21
|
# @return [MatchningResult] the result.
|
17
22
|
# @param area_id [String] Area ID.
|
@@ -36,6 +41,7 @@ module Arbetsformedlingen
|
|
36
41
|
# client.ads(keywrods: 'ruby', page: 3, page_size: 10)
|
37
42
|
# @example Get ads with keyword and organsiation numer
|
38
43
|
# client.ads(keywrods: 'ruby', organization_number: org_no)
|
44
|
+
# rubocop:enable Metrics/LineLength
|
39
45
|
def ads(
|
40
46
|
# one of these must be present
|
41
47
|
county_id: nil,
|
@@ -55,7 +61,7 @@ module Arbetsformedlingen
|
|
55
61
|
|
56
62
|
one_of_required = [county_id, municipality_id, occupation_id, keywords]
|
57
63
|
if one_of_required.all?(&:nil?)
|
58
|
-
error_message = 'One of: county_id, municipality_id, occupation_id, keywords is required'
|
64
|
+
error_message = 'One of: county_id, municipality_id, occupation_id, keywords is required' # rubocop:disable Metrics/LineLength
|
59
65
|
raise ArgumentError, error_message
|
60
66
|
end
|
61
67
|
|
@@ -77,12 +83,12 @@ module Arbetsformedlingen
|
|
77
83
|
anstallningstyp: santize_employment_type_query(employment_type),
|
78
84
|
yrkesomradeid: occupation_field_id,
|
79
85
|
sokdatum: normalize_date_to_iso8601(published_after),
|
80
|
-
organisationsnummer: organization_number
|
86
|
+
organisationsnummer: organization_number,
|
81
87
|
}
|
82
88
|
|
83
89
|
response = request.get('matchning', query: query)
|
84
90
|
|
85
|
-
MatchningResult.build(response
|
91
|
+
MatchningResult.build(response)
|
86
92
|
end
|
87
93
|
|
88
94
|
private
|
@@ -99,11 +105,11 @@ module Arbetsformedlingen
|
|
99
105
|
end
|
100
106
|
|
101
107
|
def santize_employment_type_query(employment_type)
|
102
|
-
#
|
103
|
-
#
|
108
|
+
# Sökkriterier anställningstyp.
|
109
|
+
# Värdena ska ligga mellan 1 och 3.
|
104
110
|
# 1 är XXX (EJ DOKUMENTERAT)
|
105
|
-
# 2
|
106
|
-
# 3
|
111
|
+
# 2 är somarjobb / feriejobb
|
112
|
+
# 3 är utlandsjobb
|
107
113
|
|
108
114
|
# TODO: The question is what we do if an invalid parameter is passed
|
109
115
|
# should we crash?
|
@@ -113,15 +119,15 @@ module Arbetsformedlingen
|
|
113
119
|
|
114
120
|
def santize_keywords_query(keywords)
|
115
121
|
#
|
116
|
-
#
|
117
|
-
# mellanslag (
|
122
|
+
# Sökord kan separeras eller kombineras med något av följande exempel:
|
123
|
+
# mellanslag (" ")
|
118
124
|
#
|
119
125
|
# [Example]
|
120
126
|
# /matchning?nyckelord="bagare""test"
|
121
127
|
# /matchning?nyckelord="bagare"OR"test" /matchning?nyckelord="automatisk"AND"test"
|
122
128
|
|
123
129
|
# Valid characters
|
124
|
-
#
|
130
|
+
# abcdefghijklmnopqrstuvwxyzåäö0123456789: ,.-"
|
125
131
|
|
126
132
|
# TODO: What do we do if invalid characters are passed? Crash?
|
127
133
|
keywords
|
@@ -1,19 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'net/http'
|
3
5
|
require 'json'
|
6
|
+
require 'arbetsformedlingen/api/response'
|
4
7
|
|
5
8
|
module Arbetsformedlingen
|
6
9
|
module API
|
10
|
+
# API request object
|
7
11
|
class Request
|
8
|
-
Response = KeyStruct.new(:code, :body, :json)
|
9
|
-
|
10
12
|
attr_reader :locale, :base_url
|
11
13
|
|
14
|
+
# Initialize request
|
12
15
|
def initialize(base_url: '', locale: 'sv')
|
13
16
|
@base_url = base_url
|
14
17
|
@locale = locale
|
15
18
|
end
|
16
19
|
|
20
|
+
# Perform GEt request
|
21
|
+
# @param [String] url to be fetched
|
22
|
+
# @param [Hash] query params
|
23
|
+
# @return [Response] response object
|
17
24
|
def get(url, query: {})
|
18
25
|
uri = URI("#{base_url}#{url}?#{URI.encode_www_form(query.to_a)}")
|
19
26
|
|
@@ -25,17 +32,7 @@ module Arbetsformedlingen
|
|
25
32
|
|
26
33
|
response = http.request(request)
|
27
34
|
|
28
|
-
Response.new(
|
29
|
-
code: response.code,
|
30
|
-
body: response.read_body,
|
31
|
-
json: parse_json(response.read_body)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
def parse_json(string)
|
36
|
-
JSON.parse(string.to_s)
|
37
|
-
rescue JSON::ParserError => _e
|
38
|
-
{}
|
35
|
+
Response.new(response)
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'nokogiri'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
module Arbetsformedlingen
|
9
|
+
module API
|
10
|
+
# API response object
|
11
|
+
class Response
|
12
|
+
# Initialize response
|
13
|
+
def initialize(response)
|
14
|
+
@response = response
|
15
|
+
@json = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# True if response is 200
|
19
|
+
# @return [Boolean] true if response code is 200
|
20
|
+
def success?
|
21
|
+
response.code == '200'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Response body
|
25
|
+
# @return [String] the response body
|
26
|
+
def body
|
27
|
+
response.read_body
|
28
|
+
end
|
29
|
+
|
30
|
+
# Response JSON
|
31
|
+
# @return [Hash] response json - empty if JSON is invalid
|
32
|
+
def json
|
33
|
+
@json ||= parse_json(body)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Response XML
|
37
|
+
# @return [Nokogiri::XML::Document] response - empty is XML is invalid
|
38
|
+
def xml
|
39
|
+
@xml ||= parse_xml(body)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Delegate missing values to response
|
43
|
+
def method_missing(method_name, *arguments, &block)
|
44
|
+
if response.respond_to?(method_name)
|
45
|
+
response.public_send(method_name, *arguments, &block)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return true if missing method can be delegated
|
52
|
+
def respond_to_missing?(method_name, include_private = false)
|
53
|
+
response.respond_to?(method_name) || super
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :response
|
59
|
+
|
60
|
+
def parse_json(string)
|
61
|
+
JSON.parse(string.to_s)
|
62
|
+
rescue JSON::ParserError => _e
|
63
|
+
{}
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_xml(string)
|
67
|
+
Nokogiri::XML(string).tap(&:remove_namespaces!)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,11 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'arbetsformedlingen/api/values/ad_result_values'
|
2
4
|
|
3
5
|
module Arbetsformedlingen
|
4
6
|
module API
|
5
7
|
module AdResult
|
6
|
-
|
7
|
-
|
8
|
+
# Build API result object for ad result
|
9
|
+
# @param [API::Response] response
|
10
|
+
# @return [Values::Ad]
|
11
|
+
def self.build(response)
|
12
|
+
return build_empty(response) unless response.success?
|
13
|
+
|
14
|
+
build_page(response)
|
15
|
+
end
|
8
16
|
|
17
|
+
# private
|
18
|
+
|
19
|
+
def self.build_page(response)
|
20
|
+
response_data = response.json
|
21
|
+
data = response_data.fetch('platsannons')
|
9
22
|
ad_data = data.fetch('annons')
|
10
23
|
|
11
24
|
Values::Ad.new(
|
@@ -19,32 +32,37 @@ module Arbetsformedlingen
|
|
19
32
|
total_vacancies: ad_data.fetch('antal_platser'),
|
20
33
|
municipalities: ad_data.fetch('kommunnamn'),
|
21
34
|
municipality_id: ad_data.fetch('kommunkod'),
|
22
|
-
total_vacancies_with_visa: ad_data.fetch('antalplatserVisa'),
|
35
|
+
total_vacancies_with_visa: ad_data.fetch('antalplatserVisa', nil),
|
23
36
|
employment_type: ad_data.fetch('anstallningstyp'),
|
24
37
|
terms: build_terms(data.fetch('villkor')),
|
25
38
|
application: build_application(data.fetch('ansokan')),
|
26
39
|
workplace: build_workplace(data.fetch('arbetsplats')),
|
27
|
-
requirements: build_requirements(data.fetch('krav'))
|
40
|
+
requirements: build_requirements(data.fetch('krav')),
|
41
|
+
response: response
|
28
42
|
)
|
29
43
|
end
|
30
44
|
|
45
|
+
def self.build_empty(response)
|
46
|
+
Values::Ad.new(response: response)
|
47
|
+
end
|
48
|
+
|
31
49
|
def self.build_terms(data)
|
32
50
|
Values::Terms.new(
|
33
|
-
duration: data.fetch('varaktighet'),
|
34
|
-
working_hours: data.fetch('arbetstid'),
|
35
|
-
working_hours_description: data.fetch('arbetstidvaraktighet'),
|
51
|
+
duration: data.fetch('varaktighet', nil),
|
52
|
+
working_hours: data.fetch('arbetstid', nil),
|
53
|
+
working_hours_description: data.fetch('arbetstidvaraktighet', nil),
|
36
54
|
salary_type: data.fetch('lonetyp'),
|
37
|
-
salary_form: data.fetch('loneform')
|
55
|
+
salary_form: data.fetch('loneform', nil)
|
38
56
|
)
|
39
57
|
end
|
40
58
|
|
41
59
|
def self.build_application(data)
|
42
60
|
Values::Application.new(
|
43
61
|
reference: data['referens'],
|
44
|
-
application_url: data.fetch('webbplats'),
|
62
|
+
application_url: data.fetch('webbplats', nil),
|
45
63
|
email: data['epostadress'],
|
46
64
|
last_application_at: data.fetch('sista_ansokningsdag', nil),
|
47
|
-
application_comment: data.fetch('ovrigt_om_ansokan')
|
65
|
+
application_comment: data.fetch('ovrigt_om_ansokan', nil)
|
48
66
|
)
|
49
67
|
end
|
50
68
|
|
@@ -53,11 +71,11 @@ module Arbetsformedlingen
|
|
53
71
|
name: data.fetch('arbetsplatsnamn'),
|
54
72
|
postal: build_postal(data),
|
55
73
|
country: data.fetch('land'),
|
56
|
-
visit_address: data.fetch('besoksadress'),
|
57
|
-
logotype_url: data.fetch('logotypurl'),
|
58
|
-
website: data.fetch('hemsida'),
|
74
|
+
visit_address: data.fetch('besoksadress', nil),
|
75
|
+
logotype_url: data.fetch('logotypurl', nil),
|
76
|
+
website: data.fetch('hemsida', nil),
|
59
77
|
contacts: (
|
60
|
-
data.dig('kontaktpersonlista', '
|
78
|
+
data.dig('kontaktpersonlista', 'kontaktpersondata') || []
|
61
79
|
).map do |contact_data|
|
62
80
|
build_workplace_contacts(contact_data)
|
63
81
|
end
|
@@ -66,10 +84,10 @@ module Arbetsformedlingen
|
|
66
84
|
|
67
85
|
def self.build_postal(data)
|
68
86
|
Values::Postal.new(
|
69
|
-
code: data.fetch('postnummer'),
|
70
|
-
address: data.fetch('postadress'),
|
71
|
-
city: data.fetch('postort'),
|
72
|
-
country: data.fetch('postland')
|
87
|
+
code: data.fetch('postnummer', nil),
|
88
|
+
address: data.fetch('postadress', nil),
|
89
|
+
city: data.fetch('postort', nil),
|
90
|
+
country: data.fetch('postland', nil)
|
73
91
|
)
|
74
92
|
end
|
75
93
|
|
@@ -1,9 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'arbetsformedlingen/api/values/matchning_result_values'
|
2
4
|
|
3
5
|
module Arbetsformedlingen
|
4
6
|
module API
|
5
7
|
module MatchningResult
|
6
|
-
|
8
|
+
# Build API result object for matchning result
|
9
|
+
# @param [API::Response] response
|
10
|
+
# @return [Values::MatchningPage]
|
11
|
+
def self.build(response)
|
12
|
+
return empty_matchning_page(response) unless response.success?
|
13
|
+
|
14
|
+
build_matchning_page(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
# private
|
18
|
+
|
19
|
+
def self.empty_matchning_page(response)
|
20
|
+
response_data = response.json
|
21
|
+
|
22
|
+
Values::MatchningPage.new(
|
23
|
+
list_name: 'annonser',
|
24
|
+
total_ads: 0,
|
25
|
+
total_ads_exact: 0,
|
26
|
+
total_ads_nearby: 0,
|
27
|
+
total_vacancies_on_page: 0,
|
28
|
+
total_pages: 0,
|
29
|
+
raw_data: response_data,
|
30
|
+
data: [],
|
31
|
+
response: response
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.build_matchning_page(response)
|
36
|
+
response_data = response.json
|
7
37
|
data = response_data.fetch('matchningslista')
|
8
38
|
|
9
39
|
Values::MatchningPage.new(
|
@@ -14,10 +44,19 @@ module Arbetsformedlingen
|
|
14
44
|
total_vacancies_on_page: data.fetch('antal_platserTotal'),
|
15
45
|
total_pages: data.fetch('antal_sidor'),
|
16
46
|
raw_data: response_data,
|
17
|
-
data: data
|
47
|
+
data: build_ad_results(data),
|
48
|
+
response: response
|
18
49
|
)
|
19
50
|
end
|
20
51
|
|
52
|
+
# private
|
53
|
+
|
54
|
+
def self.build_ad_results(data)
|
55
|
+
data.fetch('matchningdata', []).map do |ad_data|
|
56
|
+
build_ad_result(ad_data)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
21
60
|
def self.build_ad_result(ad_data)
|
22
61
|
Values::MatchningAd.new(
|
23
62
|
id: ad_data.fetch('annonsid'),
|
@@ -32,7 +71,7 @@ module Arbetsformedlingen
|
|
32
71
|
url: ad_data.fetch('annonsurl'),
|
33
72
|
relevance: ad_data.fetch('relevans'),
|
34
73
|
total_vacancies: ad_data.fetch('antalplatser'),
|
35
|
-
total_vacancies_with_visa: ad_data.fetch('antalPlatserVisa'),
|
74
|
+
total_vacancies_with_visa: ad_data.fetch('antalPlatserVisa', nil),
|
36
75
|
duration_id: ad_data.fetch('varaktighetId', nil),
|
37
76
|
counties: ad_data.fetch('lan'),
|
38
77
|
country_id: ad_data.fetch('lanid'),
|
@@ -1,9 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'arbetsformedlingen/api/values/soklista_values'
|
2
4
|
|
3
5
|
module Arbetsformedlingen
|
4
6
|
module API
|
5
7
|
module SoklistaResult
|
6
|
-
|
8
|
+
# Build API result object for "soklista"
|
9
|
+
# @param [API::Response] response
|
10
|
+
# @param list_name [String] result list name
|
11
|
+
# @return [Values::SoklistaPage]
|
12
|
+
def self.build(response, list_name: nil)
|
13
|
+
build_page(response, list_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# private
|
17
|
+
|
18
|
+
def self.build_page(response, list_name)
|
19
|
+
response_data = response.json
|
7
20
|
data = response_data.fetch('soklista', {})
|
8
21
|
|
9
22
|
Values::SoklistaPage.new(
|
@@ -13,7 +26,8 @@ module Arbetsformedlingen
|
|
13
26
|
raw_data: response_data,
|
14
27
|
data: data.fetch('sokdata', []).map do |result|
|
15
28
|
build_search_result(result)
|
16
|
-
end
|
29
|
+
end,
|
30
|
+
response: response
|
17
31
|
)
|
18
32
|
end
|
19
33
|
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'net/http'
|
5
|
+
require 'arbetsformedlingen/api/response'
|
3
6
|
|
4
7
|
begin
|
5
8
|
require 'nokogiri'
|
@@ -8,11 +11,14 @@ end
|
|
8
11
|
|
9
12
|
module Arbetsformedlingen
|
10
13
|
module API
|
14
|
+
# API SOAP request
|
11
15
|
class SOAPRequest
|
12
|
-
|
16
|
+
# SOAP response
|
17
|
+
# Response = KeyStruct.new(:code, :body, :xml)
|
13
18
|
|
14
19
|
attr_reader :locale, :uri, :url
|
15
20
|
|
21
|
+
# Initialize SOAP request
|
16
22
|
def initialize(url, locale: nil)
|
17
23
|
unless Object.const_defined?(:Nokogiri)
|
18
24
|
raise(ArgumentError, "unable to require 'nokogiri' gem, please install it")
|
@@ -23,6 +29,9 @@ module Arbetsformedlingen
|
|
23
29
|
@locale = locale
|
24
30
|
end
|
25
31
|
|
32
|
+
# Performs a POST request
|
33
|
+
# @param [String] the post body
|
34
|
+
# @return [Response] the response
|
26
35
|
def post(body)
|
27
36
|
http = Net::HTTP.new(uri.host, uri.port)
|
28
37
|
http.use_ssl = true if uri.scheme == 'https'
|
@@ -34,15 +43,7 @@ module Arbetsformedlingen
|
|
34
43
|
|
35
44
|
response = http.request(request)
|
36
45
|
|
37
|
-
Response.new(
|
38
|
-
code: response.code,
|
39
|
-
body: response.read_body,
|
40
|
-
xml: parse_xml(response.read_body)
|
41
|
-
)
|
42
|
-
end
|
43
|
-
|
44
|
-
def parse_xml(string)
|
45
|
-
Nokogiri::XML(string).tap { |doc| doc.remove_namespaces! }
|
46
|
+
Response.new(response)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|