survey-gizmo-ruby 4.1.0 → 5.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00dc389fa6c3415227ed3ea344c8ec1b7152eaa8
4
- data.tar.gz: 342bda557eb8df193f19958de0337a23504c5a49
3
+ metadata.gz: 49463d6fa875d93712a1da0951dac1e0053747ee
4
+ data.tar.gz: 339404fe4dae766bd04abc675cd7fbd34f244488
5
5
  SHA512:
6
- metadata.gz: 35695df30398092c26e8fdb48eead2e669753df6cddd4a615eb70688ede385a80e6731adfd29d33bc83ae93b25a15d875c5cf3d2efa7519cb3124e02b018f6a5
7
- data.tar.gz: 434990416b53a6cdd22c00f102f888125ec5a501c800c2c8df41fe1a8a126d510228595f8d9b4fd7a6482fb6644235d029564ca476c9166ccb14a7ccea3940c5
6
+ metadata.gz: 38c186f0d5e12964b5b559aeb0c674c2291d1ade6f0518c7ed681e806cb3a56cb05d791b5f9374655cc81fe491853406c74d5dab72ba126aa668195963d4ca08
7
+ data.tar.gz: ea37139f7d8fdad647d2b38db73cef15a065b6b6098a05efbbd911e8dda70404b8979bb42e6fad9f1b9eeaa41f1df49e8ddb8e0f7f34708569255f1823ebd388
data/README.md CHANGED
@@ -6,14 +6,22 @@ Currently supports SurveyGizmo API **v4** (default) and **v3**.
6
6
 
7
7
  ## Versions
8
8
 
9
+ ### Major Changes in 5.x
10
+
11
+ * **BREAKING CHANGE**: `.all` returns an `Enumerator`, not an `Array`. This will break your code if you are using the return value of `.all` without iterating over it.
12
+ * **BREAKING CHANGE**: `SurveyCampaign` has been renamed `Campaign` to be in line with the other model names.
13
+ * FEATURE: `.all` will automatically paginate responses for you with the `:all_pages` option. There are also some built in methods like `Survey#responses` that will auto paginate.
14
+ * FEATURE: Built in retries. 1 retry with a 60 second backoff is the default but can be configured.
15
+ * FEATURE: `Response#parsed_answers` returns an array of `Answers` (new class) that are sane, stable Ruby objects instead of the sort of wild and wooly way SurveyGizmo has chosen to represent survey responses.
16
+
9
17
  ### Major Changes in 4.x
10
18
 
11
- * BREAKING CHANGE: There is no more error tracking. If the API gives an error or bad response, an exception will be raised.
12
- * BREAKING CHANGE: There is no more ```copy``` method
19
+ * **BREAKING CHANGE**: There is no more error tracking. If the API gives an error or bad response, an exception will be raised.
20
+ * **BREAKING CHANGE**: There is no more ```copy``` method
13
21
 
14
22
  ### Major Changes in 3.x
15
23
 
16
- * BREAKING CHANGE: Configuration is completely different
24
+ * **BREAKING CHANGE**: Configuration is completely different
17
25
  * Important Change: Defaults to using the v4 SurveyGizmo API endpoint to take advantage of various API bug fixes (notably team ownership is broken in v3)
18
26
 
19
27
  ### Old versions
@@ -30,7 +38,7 @@ Currently supports SurveyGizmo API **v4** (default) and **v3**.
30
38
  gem 'survey-gizmo-ruby'
31
39
  ```
32
40
 
33
- ## Basic Usage
41
+ ## Configuration
34
42
 
35
43
  ```ruby
36
44
  require 'survey-gizmo-ruby'
@@ -43,14 +51,58 @@ SurveyGizmo.configure do |config|
43
51
  # Optional - Defaults to v4, but you can probably set to v3 safely if you suspect a bug in v4
44
52
  config.api_version = 'v4'
45
53
 
54
+ # Optional - Set if you need to hit a different base URL (e.g. the .eu domain)
55
+ config.api_url = 'https://restapi.surveygizmo.eu'
56
+
46
57
  # Optional - Defaults to 50, maximum 500. Setting too high may cause SurveyGizmo to start throwing timeouts.
47
58
  config.results_per_page = 100
48
59
  end
60
+ ```
61
+
62
+ ### Retries
63
+
64
+ The [Pester](https://github.com/lumoslabs/pester) gem is used to manage retry strategies. By default it will be configured to handle 1 retry with a 60 second timeout upon encountering basic net timeouts and rate limit errors, which is enough for most people's needs.
65
+
66
+ If, however, you want to specify more retries, a longer backoff, new classes to retry on, or otherwise get fancy with the retry strategy, you can configured Pester directly. SurveyGizmo API calls are executed in Pester's `survey_gizmo_ruby` environment, so anything you configure there will apply to all your requests.
67
+
68
+ ```ruby
69
+ # For example, to change the retry interval, max attempts, or exception classes to be retried:
70
+ Pester.configure do |config|
71
+ # Retry 10 times
72
+ config.environments[:survey_gizmo_ruby][:max_attempts] = 10
73
+ # Backoff for 2 minutes
74
+ config.environments[:survey_gizmo_ruby][:delay_interval] = 120
75
+ # Retry different exception classes
76
+ config.environments[:survey_gizmo_ruby][:retry_error_classes] = [MyExceptionClass, MyOtherExceptionClass]
77
+ end
78
+
79
+ # To set Pester to retry on ALL exception classes, do this (use with caution! Can include exceptions Rails likes to throw on SIGHUP)
80
+ Pester.configure do |config|
81
+ config.environments[:survey_gizmo_ruby][:retry_error_classes] = nil
82
+ end
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ ### Retrieving Data
49
88
 
50
- # Retrieve the first page of your surveys
51
- surveys = SurveyGizmo::API::Survey.all
52
- # Retrieve ALL your surveys (handle pagination for you)
53
- surveys = SurveyGizmo::API::Survey.all(all_pages: true)
89
+ `SurveyGizmo::API::Klass.first` returns a single instance of the resource.
90
+
91
+ `SurveyGizmo::API::Klass.all` returns an `Enumerator` you can use to loop through your results/questions/surveys etc. It will actually iterate through ALL your results (pagination will be handled for you) if you pass `all_pages: true`.
92
+
93
+ Because `.all` returns an `Enumerator`, you have to call `.to_a` or some other enumerable method to trigger actual API data retrieval.
94
+ ```ruby
95
+ SurveyGizmo::API::Survey.all(all_pages: true) # => #<Enumerator: #<Enumerator::Generator>:each>
96
+ SurveyGizmo::API::Survey.all(all_pages: true).to_a # => [Survey, Survey, Survey, ...]
97
+ ```
98
+
99
+ ### Examples
100
+
101
+ ```ruby
102
+ # Iterate over your all surveys directly with the iterator
103
+ SurveyGizmo::API::Survey.all(all_pages: true).each { |survey| do_something_with(survey) }
104
+ # Iterate over the 1st page of your surveys
105
+ SurveyGizmo::API::Survey.all(page: 1).each { |survey| do_something_with(survey) }
54
106
 
55
107
  # Retrieve the survey with id: 12345
56
108
  survey = SurveyGizmo::API::Survey.first(id: 12345)
@@ -61,10 +113,10 @@ survey.server_has_new_results_since?(Time.now.utc - 2.days) # => true
61
113
  survey.team_names # => ['Development', 'Test']
62
114
  survey.belongs_to?('Development') # => true
63
115
 
64
- # Retrieving Questions for a given survey. Note that page_id is a required parameter.
65
- questions = SurveyGizmo::API::Question.all(survey_id: survey.id, page_id: 1)
66
- # Or just retrieve all questions for all pages of this survey
116
+ # Retrieve all questions for all pages of this survey
67
117
  questions = survey.questions
118
+ # Strip out instruction, urlredirect, logic, media, and other non question "questions"
119
+ questions = survey.actual_questions
68
120
 
69
121
  # Create a question for your survey. The returned object will be given an :id parameter by SG.
70
122
  question = SurveyGizmo::API::Question.create(survey_id: survey.id, title: 'Do you like ruby?', type: 'checkbox')
@@ -74,34 +126,41 @@ question.save
74
126
  # Destroy a question
75
127
  question.destroy
76
128
 
77
- # Retrieve 2nd page of SurveyResponses for a given survey.
78
- responses = SurveyGizmo::API::Response.all(survey_id: 12345, page: 2)
79
- # Retrieve all responses for a given survey.
80
- responses = SurveyGizmo::API::Response.all(all_pages: true, survey_id: 12345)
81
- # Retrieving page 3 of completed, non test data SurveyResponses submitted within the past 3 days
82
- # for contact id 999. This example shows you how to use some of the gem's built in filters and
83
- # filter generators as well as how to construct your own raw filter.
129
+ # Iterate over all your Responses
130
+ survey.responses.each { |response| do_something_with(response) }
131
+ # Use filters to limit results - this example will iterate over page 3 of completed, non test data
132
+ # SurveyResponses submitted within the past 3 days for contact 999. It demonstrates how to use some of the gem's
133
+ # built in filters/generators as well as how to construct a filter.
84
134
  # See: http://apihelp.surveygizmo.com/help/article/link/filters for more info on filters
85
- responses = SurveyGizmo::API::Response.all(
86
- survey_id: 12345,
87
- page: 3,
88
- filters: [
89
- SurveyGizmo::API::Response::NO_TEST_DATA,
90
- SurveyGizmo::API::Response::ONLY_COMPLETED,
91
- SurveyGizmo::API::Response.submitted_since_filter(Time.now - 72.hours),
92
- {
93
- field: 'contact_id',
94
- operator: '=',
95
- value: 999
96
- }
97
- ]
98
- )
135
+ filters = [
136
+ SurveyGizmo::API::Response::NO_TEST_DATA,
137
+ SurveyGizmo::API::Response::ONLY_COMPLETED,
138
+ SurveyGizmo::API::Response.submitted_since_filter(Time.now - 72.hours),
139
+ {
140
+ field: 'contact_id',
141
+ operator: '=',
142
+ value: 999
143
+ }
144
+ ]
145
+ survey.responses(page: 3, filters: filters).each { |response| do_stuff_with(response) }
146
+
147
+ # Parse the answer hash into a more usable format.
148
+ # Answers with keys but empty values will not be returned.
149
+ # "Other" text for some questions is parsed to Answer#other_text; all other answers to Answer#answer_text
150
+ # Custom table question answers have the question_pipe string parsed out to Answer#question_pipe.
151
+ # See http://apihelp.surveygizmo.com/help/article/link/surveyresponse-per-question for more info on answers
152
+ response.parsed_answers => # [#<SurveyGizmo::API::Answer @survey_id=12345, @question_id=1, @option_id=2, @answer_text='text'>]
153
+
154
+ # Retrieve all answers from all responses to all surveys, write rows to your database
155
+ SurveyGizmo::API::Survey.all(all_pages: true).each do |survey|
156
+ survey.responses.each do |response|
157
+ response.parsed_answers.each do |answer|
158
+ MyLocalSurveyGizmoResponseModel.create(answer.to_hash)
159
+ end
160
+ end
161
+ end
99
162
  ```
100
163
 
101
- ## On API Timeouts
102
-
103
- API timeouts are a regular occurrence with the SurveyGizmo API. At Lumos Labs we use our own [Pester gem](https://github.com/lumoslabs/pester) to manage retry strategies. It might work for you.
104
-
105
164
  ## Debugging
106
165
 
107
166
  The GIZMO_DEBUG environment variable will trigger full printouts of SurveyGizmo's HTTP responses and variable introspection for almost everything.
@@ -114,7 +173,7 @@ bundle exec rails whatever
114
173
 
115
174
  ## Adding API Objects
116
175
 
117
- Currently, the following API objects are included in the gem: `Survey`, `Question`, `Option`, `Page`, `Response`, `EmailMessage`, `SurveyCampaign`, `Contact`, `AccountTeams`. If you want to use something that isn't included you can easily write a class that handles it. Here's an example of the how to do so:
176
+ Currently, the following API objects are included in the gem: `Survey`, `Question`, `Option`, `Page`, `Response`, `EmailMessage`, `Campaign`, `Contact`, `AccountTeams`. If you want to use something that isn't included you can easily write a class that handles it. Here's an example of the how to do so:
118
177
 
119
178
  ```ruby
120
179
  class SomeObject
@@ -128,15 +187,16 @@ class SomeObject
128
187
  attribute :type, String
129
188
  attribute :created_on, DateTime
130
189
 
131
- # defing the paths used to retrieve/set info
132
- route '/something/:id', via: [:get, :update, :delete]
133
- route '/something', via: :create
134
-
135
- # this must be defined with the params that would be included in any route related
136
- # to an instance of SomeObject
137
- def to_param_options
138
- { id: self.id }
139
- end
190
+ # define the paths used to retrieve/set info
191
+ # if the routing is such that :get, :update, and :delete only append /:id to the main route, do this
192
+ @route = '/something'
193
+ # but if the class needs special routing, specify each method route with a hash:
194
+ @route = {
195
+ get: '/something/:id',
196
+ update: '/something/weird/:id',
197
+ create: '/something',
198
+ delete: /something/delete/:id'
199
+ }
140
200
  end
141
201
  ```
142
202
 
@@ -154,14 +214,12 @@ The [Virtus](https://github.com/solnic/virtus) gem is included to handle the att
154
214
 
155
215
  ## Desirable/Missing Features
156
216
 
217
+ * Better specs with VCR/Webmock
218
+ * OAuth authentication
157
219
  * Better foreign language support
158
- * Use Faraday instead of Httparty (partied too hard)
159
- * Better specs with VCR/Webmock would be nice.
160
220
  * There are several API objects that are available and not included in this gem. AccountTeams, for instance, has some skeleton code but is untested.
161
- * OAuth authentication ability.
162
221
 
163
222
  # Copyright
164
223
 
165
224
  Copyright (c) 2012 RipTheJacker. See LICENSE.txt for
166
225
  further details.
167
-
@@ -1,27 +1,19 @@
1
- require 'active_support/core_ext/string'
2
- require 'active_support/core_ext/module'
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/array'
3
3
  require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/module'
4
5
  require 'active_support/core_ext/object/blank'
5
- require 'active_support/concern'
6
+ require 'active_support/core_ext/string'
6
7
  require 'active_support/time_with_zone'
8
+
7
9
  require 'awesome_print'
8
10
  require 'digest/md5'
9
- require 'httparty'
11
+ require 'faraday'
12
+ require 'faraday_middleware'
13
+ require 'logger'
14
+ require 'pester'
10
15
  require 'virtus'
11
16
 
12
- require 'survey_gizmo/version'
13
- require 'survey_gizmo/survey_gizmo'
14
- require 'survey_gizmo/configuration'
15
- require 'survey_gizmo/multilingual_title'
16
- require 'survey_gizmo/resource'
17
- require 'survey_gizmo/rest_response'
18
-
19
- require 'survey_gizmo/api/account_teams'
20
- require 'survey_gizmo/api/contact'
21
- require 'survey_gizmo/api/email_message'
22
- require 'survey_gizmo/api/option'
23
- require 'survey_gizmo/api/page'
24
- require 'survey_gizmo/api/question'
25
- require 'survey_gizmo/api/response'
26
- require 'survey_gizmo/api/survey'
27
- require 'survey_gizmo/api/survey_campaign'
17
+ path = File.join(File.expand_path(File.dirname(__FILE__)), 'survey_gizmo')
18
+ Dir["#{path}/*.rb"].each { |f| require f }
19
+ Dir["#{path}/**/*.rb"].each { |f| require f }
@@ -13,12 +13,7 @@ module SurveyGizmo
13
13
  attribute :default_role, String
14
14
  attribute :status, String
15
15
 
16
- route '/accountteams/:id', via: [:get, :update, :delete]
17
- route '/accountteams', via: :create
18
-
19
- def to_param_options
20
- { id: self.id }
21
- end
16
+ @route = '/accountteams'
22
17
  end
23
18
  end
24
19
  end
@@ -0,0 +1,59 @@
1
+ module SurveyGizmo
2
+ module API
3
+ class Answer
4
+ include Virtus.model
5
+
6
+ attribute :key, String
7
+ attribute :value, String
8
+ attribute :survey_id, Integer
9
+ attribute :response_id, Integer
10
+ attribute :question_id, Integer
11
+ attribute :option_id, Integer
12
+ attribute :submitted_at, DateTime
13
+ attribute :answer_text, String
14
+ attribute :other_text, String
15
+ attribute :question_pipe, String
16
+
17
+ def initialize(attrs = {})
18
+ self.attributes = attrs
19
+
20
+ case key
21
+ when /\[question\((\d+)\),\s*option\((\d+|"\d+-other")\)\]/
22
+ @question_id, @option_id = $1, $2
23
+
24
+ if @option_id =~ /other/
25
+ @option_id.delete!('-other"')
26
+ self.other_text = value
27
+ end
28
+ when /\[question\((\d+)\),\s*question_pipe\("(.*)"\)\]/
29
+ @question_id = $1
30
+ @question_pipe = $2
31
+ when /\[question\((\d+)\)\]/
32
+ @question_id = $1
33
+ else
34
+ fail "Can't recognize pattern for #{attrs[:key]} => #{attrs[:value]} - you may have to parse your answers manually."
35
+ end
36
+
37
+ self.question_id = @question_id.to_i
38
+ if @option_id
39
+ fail "Bad option_id #{option_id}!" if option_id.to_i == 0 && option_id != '0'
40
+ self.option_id = @option_id.to_i
41
+ end
42
+ end
43
+
44
+ # Strips out the answer_text when there is a valid option_id
45
+ def to_hash
46
+ {
47
+ response_id: response_id,
48
+ question_id: question_id,
49
+ option_id: option_id,
50
+ question_pipe: question_pipe,
51
+ submitted_at: submitted_at,
52
+ survey_id: survey_id,
53
+ other_text: other_text,
54
+ answer_text: option_id || other_text ? nil : answer_text
55
+ }.reject { |k,v| v.nil? }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,6 +1,6 @@
1
1
  module SurveyGizmo; module API
2
2
  # @see SurveyGizmo::Resource::ClassMethods
3
- class SurveyCampaign
3
+ class Campaign
4
4
  include SurveyGizmo::Resource
5
5
 
6
6
  attribute :id, Integer
@@ -23,11 +23,10 @@ module SurveyGizmo; module API
23
23
  attribute :surveycampaign, Integer
24
24
  attribute :copy, Boolean
25
25
 
26
- route '/survey/:survey_id/surveycampaign/:id', via: [:get, :update, :delete]
27
- route '/survey/:survey_id/surveycampaign', via: :create
26
+ @route = '/survey/:survey_id/surveycampaign'
28
27
 
29
- def to_param_options
30
- { id: self.id, survey_id: self.survey_id }
28
+ def contacts(conditions = {})
29
+ Contact.all(conditions.merge(children_params).merge(all_pages: !conditions[:page]))
31
30
  end
32
31
  end
33
32
  end; end
@@ -36,11 +36,6 @@ module SurveyGizmo; module API
36
36
  attribute :scustomfield9, String
37
37
  attribute :scustomfield10, String
38
38
 
39
- route '/survey/:survey_id/surveycampaign/:campaign_id/contact/:id', via: [:get, :update, :delete]
40
- route '/survey/:survey_id/surveycampaign/:campaign_id/contact', via: :create
41
-
42
- def to_param_options
43
- { id: self.id, survey_id: self.survey_id, campaign_id: self.campaign_id }
44
- end
39
+ @route = '/survey/:survey_id/surveycampaign/:campaign_id/contact'
45
40
  end
46
- end; end
41
+ end; end
@@ -20,11 +20,6 @@ module SurveyGizmo; module API
20
20
  attribute :datecreated, DateTime
21
21
  attribute :datemodified, DateTime
22
22
 
23
- route '/survey/:survey_id/surveycampaign/:campaign_id/emailmessage/:id', :via => [:get, :update, :delete]
24
- route '/survey/:survey_id/surveycampaign/:campaign_id/emailmessage', :via => :create
25
-
26
- def to_param_options
27
- { id: self.id, survey_id: self.survey_id, campaign_id: self.campaign_id }
28
- end
23
+ @route = '/survey/:survey_id/surveycampaign/:campaign_id/emailmessage'
29
24
  end
30
25
  end; end
@@ -11,11 +11,6 @@ module SurveyGizmo; module API
11
11
  attribute :value, String
12
12
  attribute :properties, Hash
13
13
 
14
- route '/survey/:survey_id/surveypage/:page_id/surveyquestion/:question_id/surveyoption', via: :create
15
- route '/survey/:survey_id/surveypage/:page_id/surveyquestion/:question_id/surveyoption/:id', via: [:get, :update, :delete]
16
-
17
- def to_param_options
18
- { id: self.id, survey_id: self.survey_id, page_id: self.page_id, question_id: self.question_id }
19
- end
14
+ @route = '/survey/:survey_id/surveypage/:page_id/surveyquestion/:question_id/surveyoption'
20
15
  end
21
- end; end
16
+ end; end
@@ -1,3 +1,5 @@
1
+ require 'survey_gizmo/api/question'
2
+
1
3
  module SurveyGizmo; module API
2
4
  # @see SurveyGizmo::Resource::ClassMethods
3
5
  class Page
@@ -9,21 +11,23 @@ module SurveyGizmo; module API
9
11
  attribute :properties, Hash
10
12
  attribute :after, Integer
11
13
  attribute :survey_id, Integer
14
+ attribute :questions, Array[Question]
12
15
 
13
- # routing
14
- route '/survey/:survey_id/surveypage', via: :create
15
- route '/survey/:survey_id/surveypage/:id', via: [:get, :update, :delete]
16
+ @route = '/survey/:survey_id/surveypage'
16
17
 
17
18
  def survey
18
- @survey ||= SurveyGizmo::API::Survey.first(id: survey_id)
19
+ @survey ||= Survey.first(id: survey_id)
19
20
  end
20
21
 
21
22
  def questions
22
- @questions ||= SurveyGizmo::API::Question.all(survey_id: survey_id, page_id: id, all_pages: true)
23
- end
23
+ # See note about broken subquestions in resource.rb
24
+ @questions.flat_map { |q| q.sub_question_skus }.each do |sku|
25
+ sku = sku[1] if sku.is_a?(Array)
26
+ next if @questions.find { |q| q.id == sku }
27
+ @questions << Question.first(children_params.merge(id: sku))
28
+ end
24
29
 
25
- def to_param_options
26
- { id: self.id, survey_id: self.survey_id }
30
+ @questions.each { |q| q.attributes = children_params }
27
31
  end
28
32
  end
29
33
  end; end