qrapi-wrapper 0.6.0
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 +7 -0
- data/lib/qrapi.rb +79 -0
- data/lib/qrapi/client.rb +58 -0
- data/lib/qrapi/error_handling_resourceable.rb +22 -0
- data/lib/qrapi/mappings/contact_mapping.rb +22 -0
- data/lib/qrapi/mappings/distribution_mapping.rb +68 -0
- data/lib/qrapi/mappings/division_mapping.rb +27 -0
- data/lib/qrapi/mappings/error_mapping.rb +19 -0
- data/lib/qrapi/mappings/group_mapping.rb +27 -0
- data/lib/qrapi/mappings/library_message_mapping.rb +20 -0
- data/lib/qrapi/mappings/mailing_list_mapping.rb +20 -0
- data/lib/qrapi/mappings/organization_mapping.rb +19 -0
- data/lib/qrapi/mappings/response_export_mapping.rb +14 -0
- data/lib/qrapi/mappings/responses_mapping.rb +45 -0
- data/lib/qrapi/mappings/survey_mapping.rb +33 -0
- data/lib/qrapi/mappings/user_mapping.rb +40 -0
- data/lib/qrapi/models/base_model.rb +22 -0
- data/lib/qrapi/models/distribution.rb +45 -0
- data/lib/qrapi/models/distribution/headers.rb +27 -0
- data/lib/qrapi/models/distribution/message.rb +26 -0
- data/lib/qrapi/models/distribution/recipients.rb +26 -0
- data/lib/qrapi/models/distribution/survey_link.rb +26 -0
- data/lib/qrapi/models/division.rb +32 -0
- data/lib/qrapi/models/group.rb +31 -0
- data/lib/qrapi/models/library_message.rb +27 -0
- data/lib/qrapi/models/mailing_list.rb +27 -0
- data/lib/qrapi/models/mailing_list/contact.rb +33 -0
- data/lib/qrapi/models/organization.rb +29 -0
- data/lib/qrapi/models/response.rb +40 -0
- data/lib/qrapi/models/response_export.rb +71 -0
- data/lib/qrapi/models/survey.rb +45 -0
- data/lib/qrapi/models/survey/expiration.rb +25 -0
- data/lib/qrapi/models/user.rb +44 -0
- data/lib/qrapi/resources/distribution_resource.rb +47 -0
- data/lib/qrapi/resources/division_resource.rb +24 -0
- data/lib/qrapi/resources/group_resource.rb +44 -0
- data/lib/qrapi/resources/library_message_resource.rb +41 -0
- data/lib/qrapi/resources/mailing_list_resource.rb +51 -0
- data/lib/qrapi/resources/organization_resource.rb +12 -0
- data/lib/qrapi/resources/response_export_resource.rb +49 -0
- data/lib/qrapi/resources/response_import_resource.rb +26 -0
- data/lib/qrapi/resources/survey_resource.rb +27 -0
- data/lib/qrapi/resources/user_resource.rb +35 -0
- data/lib/qrapi/utils/kartograph_optional_properties.rb +21 -0
- data/lib/qrapi/version.rb +3 -0
- metadata +271 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6f55c877f28d86f3eef311c12ca12259e4cc657f9e62ace2f9276dae6f81a460
|
4
|
+
data.tar.gz: 4ae23eaeb89fb89ba8e08cb4247828c9b30326865abfdcb99bc32de6631fd5fe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 91bfc6a270ee30f6cf7b27707271c8806994090fde5bcb5ea12b0ae169c8c3a84aec0c0554214da6277d3741eb1a5139253e860bd5aa759e13e69af7fcb7a537
|
7
|
+
data.tar.gz: eda8bb8744cd27b4e5a0ca1c740beaffb6e4d62fdea5e6d3372b08f3318c5427660e118f150f73e5245f3952c62678eea98e735ce4246823f9c775223fcd66fc
|
data/lib/qrapi.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "zip"
|
2
|
+
require "json"
|
3
|
+
require "kartograph"
|
4
|
+
require "resource_kit"
|
5
|
+
require "qrapi/version"
|
6
|
+
require "active_model"
|
7
|
+
require "active_support/all"
|
8
|
+
|
9
|
+
module QRAPI
|
10
|
+
autoload :Client, "qrapi/client"
|
11
|
+
|
12
|
+
# Models
|
13
|
+
autoload :BaseModel, "qrapi/models/base_model"
|
14
|
+
autoload :Organization, "qrapi/models/organization"
|
15
|
+
autoload :Division, "qrapi/models/division"
|
16
|
+
autoload :Group, "qrapi/models/group"
|
17
|
+
autoload :Survey, "qrapi/models/survey"
|
18
|
+
Survey.autoload :Expiration, "qrapi/models/survey/expiration"
|
19
|
+
autoload :User, "qrapi/models/user"
|
20
|
+
autoload :LibraryMessage, "qrapi/models/library_message"
|
21
|
+
autoload :Distribution, "qrapi/models/distribution"
|
22
|
+
Distribution.autoload :Headers, "qrapi/models/distribution/headers"
|
23
|
+
Distribution.autoload :Message, "qrapi/models/distribution/message"
|
24
|
+
Distribution.autoload :Recipients, "qrapi/models/distribution/recipients"
|
25
|
+
Distribution.autoload :SurveyLink, "qrapi/models/distribution/survey_link"
|
26
|
+
autoload :ResponseExport, "qrapi/models/response_export"
|
27
|
+
autoload :Response, "qrapi/models/response"
|
28
|
+
autoload :MailingList, "qrapi/models/mailing_list"
|
29
|
+
MailingList.autoload :Contact, "qrapi/models/mailing_list/contact"
|
30
|
+
|
31
|
+
# Resources
|
32
|
+
autoload :OrganizationResource, "qrapi/resources/organization_resource"
|
33
|
+
autoload :DivisionResource, "qrapi/resources/division_resource"
|
34
|
+
autoload :GroupResource, "qrapi/resources/group_resource"
|
35
|
+
autoload :SurveyResource, "qrapi/resources/survey_resource"
|
36
|
+
autoload :UserResource, "qrapi/resources/user_resource"
|
37
|
+
autoload :LibraryMessageResource, "qrapi/resources/library_message_resource"
|
38
|
+
autoload :DistributionResource, "qrapi/resources/distribution_resource"
|
39
|
+
autoload :ResponseExportResource, "qrapi/resources/response_export_resource"
|
40
|
+
autoload :ResponseImportResource, "qrapi/resources/response_import_resource"
|
41
|
+
autoload :MailingListResource, "qrapi/resources/mailing_list_resource"
|
42
|
+
|
43
|
+
# JSON Maps
|
44
|
+
autoload :OrganizationMapping, "qrapi/mappings/organization_mapping"
|
45
|
+
autoload :DivisionMapping, "qrapi/mappings/division_mapping"
|
46
|
+
autoload :GroupMapping, "qrapi/mappings/group_mapping"
|
47
|
+
autoload :SurveyMapping, "qrapi/mappings/survey_mapping"
|
48
|
+
autoload :ExpirationMapping, "qrapi/mappings/expiration_mapping"
|
49
|
+
autoload :UserMapping, "qrapi/mappings/user_mapping"
|
50
|
+
autoload :LibraryMessageMapping, "qrapi/mappings/library_message_mapping"
|
51
|
+
autoload :DistributionMapping, "qrapi/mappings/distribution_mapping"
|
52
|
+
autoload :ResponseExportMapping, "qrapi/mappings/response_export_mapping"
|
53
|
+
autoload :ResponsesMapping, "qrapi/mappings/responses_mapping"
|
54
|
+
autoload :MailingListMapping, "qrapi/mappings/mailing_list_mapping"
|
55
|
+
autoload :ContactMapping, "qrapi/mappings/contact_mapping"
|
56
|
+
|
57
|
+
# Utils
|
58
|
+
autoload :ErrorHandlingResourceable, "qrapi/error_handling_resourceable"
|
59
|
+
autoload :Kartograph, "qrapi/utils/kartograph_optional_properties"
|
60
|
+
|
61
|
+
# Errors
|
62
|
+
autoload :ErrorMapping, "qrapi/mappings/error_mapping"
|
63
|
+
Error = Class.new(StandardError)
|
64
|
+
FailedCreate = Class.new(QRAPI::Error)
|
65
|
+
FailedUpdate = Class.new(QRAPI::Error)
|
66
|
+
|
67
|
+
class RateLimitReached < QRAPI::Error
|
68
|
+
attr_accessor :reset_at
|
69
|
+
attr_writer :limit, :remaining
|
70
|
+
|
71
|
+
def limit
|
72
|
+
@limit.to_i if @limit
|
73
|
+
end
|
74
|
+
|
75
|
+
def remaining
|
76
|
+
@remaning.to_i if @remaning
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/qrapi/client.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require "faraday"
|
2
|
+
|
3
|
+
module QRAPI
|
4
|
+
class Client
|
5
|
+
attr_reader :api_token, :data_center_id
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@api_token = options[:api_token]
|
9
|
+
@data_center_id = options[:data_center_id]
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection
|
13
|
+
Faraday.new(connection_options) do |req|
|
14
|
+
req.adapter :net_http
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.resources
|
19
|
+
{
|
20
|
+
organization: OrganizationResource,
|
21
|
+
divisions: DivisionResource,
|
22
|
+
groups: GroupResource,
|
23
|
+
users: UserResource,
|
24
|
+
surveys: SurveyResource,
|
25
|
+
response_exports: ResponseExportResource,
|
26
|
+
response_imports: ResponseImportResource,
|
27
|
+
library_messages: LibraryMessageResource,
|
28
|
+
distributions: DistributionResource,
|
29
|
+
mailing_lists: MailingListResource
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(name, *args, &block)
|
34
|
+
if self.class.resources.keys.include?(name)
|
35
|
+
resources[name] ||= self.class.resources[name].new(connection: connection)
|
36
|
+
resources[name]
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def resources
|
43
|
+
@resources ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def connection_options
|
49
|
+
{
|
50
|
+
url: "https://#{@data_center_id}.qualtrics.com",
|
51
|
+
headers: {
|
52
|
+
content_type: "application/json",
|
53
|
+
X_API_TOKEN: api_token
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ErrorHandlingResourceable
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:resources) do
|
4
|
+
default_handler do |response|
|
5
|
+
if (200...299).include?(response.status)
|
6
|
+
next
|
7
|
+
elsif response.status == 429
|
8
|
+
error = QRAPI::RateLimitReached.new("#{response.status}: #{response.body}")
|
9
|
+
error.limit = response.headers["RateLimit-Limit"]
|
10
|
+
error.remaining = response.headers["RateLimit-Remaining"]
|
11
|
+
error.reset_at = response.headers["RateLimit-Reset"]
|
12
|
+
raise error
|
13
|
+
else
|
14
|
+
body = JSON.parse(response.body)
|
15
|
+
error_message = body["meta"]["error"]["errorMessage"]
|
16
|
+
error_code = body["meta"]["error"]["errorCode"]
|
17
|
+
raise QRAPI::Error.new("Status: #{response.status} Error: #{error_code} - #{error_message}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class ContactMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping MailingList::Contact
|
7
|
+
root_key plural: "elements", scopes: [:read]
|
8
|
+
|
9
|
+
property :id, scopes: [:read]
|
10
|
+
|
11
|
+
scoped :read, :update do
|
12
|
+
property :first_name, key: "firstName", optional: true
|
13
|
+
property :last_name, key: "lastName", optional: true
|
14
|
+
property :email, optional: true
|
15
|
+
property :external_data_reference, key: "externalDataReference", optional: true
|
16
|
+
property :language, optional: true
|
17
|
+
property :embedded_data, key: "embeddedData", optional: true
|
18
|
+
property :unsubscribed, optional: true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class DistributionMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping Distribution
|
7
|
+
root_key singular: "result", plural: "elements", scopes: [:read]
|
8
|
+
|
9
|
+
property :send_date, scopes: [:read, :create, :create_child], key: "sendDate"
|
10
|
+
|
11
|
+
property :recipients, scopes: [:read, :create] do
|
12
|
+
mapping Distribution::Recipients
|
13
|
+
|
14
|
+
property :mailing_list_id, scopes: [:read, :create], key: "mailingListId"
|
15
|
+
property :contact_id, scopes: [:read, :create], key: "contactId", optional: true
|
16
|
+
end
|
17
|
+
|
18
|
+
property :headers, scopes: [:read, :create, :create_child] do
|
19
|
+
mapping Distribution::Headers
|
20
|
+
|
21
|
+
property :from_email, scopes: [:read, :create, :create_child], key: "fromEmail"
|
22
|
+
property :from_name, scopes: [:read, :create, :create_child], key: "fromName"
|
23
|
+
property :reply_to_email, scopes: [:read, :create, :create_child], key: "replyToEmail"
|
24
|
+
property :subject, scopes: [:read, :create, :create_child]
|
25
|
+
end
|
26
|
+
|
27
|
+
property :message, scopes: [:read, :create], optional: true do
|
28
|
+
mapping Distribution::Message
|
29
|
+
|
30
|
+
property :message_id, scopes: [:read, :create], key: "messageId"
|
31
|
+
property :library_id, scopes: [:read, :create], key: "libraryId"
|
32
|
+
property :message_text, scopes: [:read, :create], key: "messageText"
|
33
|
+
end
|
34
|
+
|
35
|
+
property :survey_link, scopes: [:read, :create], optional: true, key: "surveyLink" do
|
36
|
+
mapping Distribution::SurveyLink
|
37
|
+
|
38
|
+
property :survey_id, scopes: [:read, :create], key: "surveyId"
|
39
|
+
property :expiration_date, scopes: [:read, :create], key: "expirationDate"
|
40
|
+
property :type, scopes: [:read, :create]
|
41
|
+
end
|
42
|
+
|
43
|
+
scoped :read do
|
44
|
+
property :id
|
45
|
+
property :parent_distribution_id, key: "parentDistributionId"
|
46
|
+
property :owner_id, key: "ownerId"
|
47
|
+
property :organization_id, key: "organizationId"
|
48
|
+
property :request_status, key: "requestStatus"
|
49
|
+
property :request_type, key: "requestType"
|
50
|
+
property :created_date, key: "createdDate"
|
51
|
+
property :modified_date, key: "modifiedDate"
|
52
|
+
property :stats
|
53
|
+
end
|
54
|
+
|
55
|
+
scoped :create_child do
|
56
|
+
property :message do
|
57
|
+
mapping Distribution::Message
|
58
|
+
|
59
|
+
scoped :create_child do
|
60
|
+
property :message_id, key: "messageId"
|
61
|
+
property :library_id, key: "libraryId"
|
62
|
+
property :message_text, key: "messageText"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class DivisionMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping Division
|
7
|
+
root_key singular: "result", scopes: [:read]
|
8
|
+
|
9
|
+
property :name, scopes: [:read, :update], optional: true
|
10
|
+
property :status, scopes: [:read, :update], optional: true
|
11
|
+
# property :permissions, scopes: [:read, :create, :update], optional: true
|
12
|
+
|
13
|
+
scoped :read do
|
14
|
+
property :id
|
15
|
+
property :organization_id, key: "organizationId"
|
16
|
+
property :creation_date, key: "creationDate"
|
17
|
+
property :creator_id, key: "creatorId"
|
18
|
+
property :response_counts, key: "responseCounts"
|
19
|
+
end
|
20
|
+
|
21
|
+
scoped :create do
|
22
|
+
property :name
|
23
|
+
property :division_admins, key: "divisionAdmins", optional: true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class ErrorMapping
|
3
|
+
Error = Struct.new(:message, :id)
|
4
|
+
|
5
|
+
include Kartograph::DSL
|
6
|
+
|
7
|
+
kartograph do
|
8
|
+
mapping Error
|
9
|
+
|
10
|
+
property :id, scopes: [:read]
|
11
|
+
property :message, scopes: [:read]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.fail_with(klass, content)
|
15
|
+
error = extract_single(content, :read)
|
16
|
+
raise klass, error.message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class GroupMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping Group
|
7
|
+
root_key singular: "result", plural: "elements", scopes: [:read]
|
8
|
+
|
9
|
+
property :type, scopes: [:read, :create]
|
10
|
+
property :name, scopes: [:read, :create]
|
11
|
+
property :division_id, scopes: [:read, :update], key: "divisionId", optional: true
|
12
|
+
|
13
|
+
scoped :read do
|
14
|
+
property :id
|
15
|
+
property :organization_id, key: "organizationId"
|
16
|
+
property :all_users, key: "allUsers"
|
17
|
+
property :creation_date, key: "creationDate"
|
18
|
+
property :creator_id, key: "creatorId"
|
19
|
+
end
|
20
|
+
|
21
|
+
scoped :update do
|
22
|
+
property :type, optional: true
|
23
|
+
property :name, optional: true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class LibraryMessageMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping LibraryMessage
|
7
|
+
root_key singular: "result", plural: "elements", scopes: [:read]
|
8
|
+
|
9
|
+
property :id, scopes: [:read]
|
10
|
+
property :category, scopes: [:read, :create]
|
11
|
+
property :messages, scopes: [:read, :update], optional: true
|
12
|
+
property :description, scopes: [:read, :update], optional: true
|
13
|
+
|
14
|
+
scoped :create do
|
15
|
+
property :description
|
16
|
+
property :messages
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class MailingListMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping MailingList
|
7
|
+
root_key singular: "result", plural: "elements", scopes: [:read]
|
8
|
+
|
9
|
+
property :id, scopes: [:read]
|
10
|
+
property :name, scopes: [:read, :create]
|
11
|
+
property :library_id, scopes: [:read, :create], key: "libraryId"
|
12
|
+
property :category, scopes: [:read, :create, :update], optional: true
|
13
|
+
|
14
|
+
scoped :update do
|
15
|
+
property :name, optional: true
|
16
|
+
property :library_id, key: "libraryId", optional: true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class OrganizationMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping Organization
|
7
|
+
root_key singular: "result", scopes: [:read]
|
8
|
+
|
9
|
+
scoped :read do
|
10
|
+
property :id
|
11
|
+
property :name
|
12
|
+
property :base_url
|
13
|
+
property :type
|
14
|
+
property :status
|
15
|
+
property :stats
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class ResponseExportMapping
|
3
|
+
include Kartograph::DSL
|
4
|
+
|
5
|
+
kartograph do
|
6
|
+
mapping ResponseExport
|
7
|
+
root_key singular: "result", scopes: [:read]
|
8
|
+
|
9
|
+
scoped :read do
|
10
|
+
property :percent_complete, key: "percentComplete"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module QRAPI
|
2
|
+
class ResponsesMapping
|
3
|
+
# Maps each of the responses to a QRAPI::Response model
|
4
|
+
#
|
5
|
+
# @return [QRAPI::Response]
|
6
|
+
def self.from_json_or_xml(serialized_response, f_type)
|
7
|
+
case f_type
|
8
|
+
when :json
|
9
|
+
responses_hash = JSON.parse(serialized_response)
|
10
|
+
unmapped_responses = responses_hash["responses"]
|
11
|
+
when :xml
|
12
|
+
responses_hash = Hash.from_xml(serialized_response)
|
13
|
+
unmapped_responses = responses_hash["xml"]["Response"]
|
14
|
+
end
|
15
|
+
|
16
|
+
responses = []
|
17
|
+
unmapped_responses.each do |response|
|
18
|
+
r = Response.new
|
19
|
+
r.id = response["ResponseID"]
|
20
|
+
r.response_set = response["ResponseSet"]
|
21
|
+
r.ip_address = response["IPAddress"]
|
22
|
+
r.start_date = response["StartDate"]
|
23
|
+
r.end_date = response["EndDate"]
|
24
|
+
r.last_name = response["RecipientLastName"]
|
25
|
+
r.first_name = response["RecipientFirstName"]
|
26
|
+
r.email = response["RecipientEmail"]
|
27
|
+
r.external_data_reference = response["ExternalDataReference"]
|
28
|
+
r.finished = response["Finished"]
|
29
|
+
r.status = response["Status"]
|
30
|
+
r.location_latitude = response["LocationLatitude"]
|
31
|
+
r.location_longitude = response["LocationLongitude"]
|
32
|
+
r.location_accuracy = response["LocationAccuracy"]
|
33
|
+
|
34
|
+
r.questions = {}
|
35
|
+
response.keys.grep(/Q([0-9xy_]+)/).each do |key|
|
36
|
+
r.questions[key] = response[key]
|
37
|
+
end
|
38
|
+
|
39
|
+
responses << r
|
40
|
+
end
|
41
|
+
|
42
|
+
responses
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|