survey-gizmo-ruby 4.1.0 → 5.0.2

Sign up to get free protection for your applications and to get access to all the features.
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