gooddata_marketo 0.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +131 -0
  4. data/README.md +207 -0
  5. data/bin/Gemfile +10 -0
  6. data/bin/auth.json +16 -0
  7. data/bin/main.rb +24 -0
  8. data/bin/process.rbx +541 -0
  9. data/examples/all_lead_changes.rb +119 -0
  10. data/examples/all_leads.rb +249 -0
  11. data/examples/lead_changes_to_ads.rb +63 -0
  12. data/gooddata_marketo.gemspec +25 -0
  13. data/lib/gooddata_marketo/adapters/rest.rb +287 -0
  14. data/lib/gooddata_marketo/client.rb +373 -0
  15. data/lib/gooddata_marketo/data/activity_types.rb +104 -0
  16. data/lib/gooddata_marketo/data/reserved_sql_keywords.rb +205 -0
  17. data/lib/gooddata_marketo/helpers/s3.rb +141 -0
  18. data/lib/gooddata_marketo/helpers/stringwizard.rb +32 -0
  19. data/lib/gooddata_marketo/helpers/table.rb +323 -0
  20. data/lib/gooddata_marketo/helpers/webdav.rb +118 -0
  21. data/lib/gooddata_marketo/loads.rb +235 -0
  22. data/lib/gooddata_marketo/models/campaigns.rb +57 -0
  23. data/lib/gooddata_marketo/models/channels.rb +30 -0
  24. data/lib/gooddata_marketo/models/child/activity.rb +104 -0
  25. data/lib/gooddata_marketo/models/child/criteria.rb +17 -0
  26. data/lib/gooddata_marketo/models/child/lead.rb +118 -0
  27. data/lib/gooddata_marketo/models/child/mobj.rb +68 -0
  28. data/lib/gooddata_marketo/models/etl.rb +75 -0
  29. data/lib/gooddata_marketo/models/leads.rb +493 -0
  30. data/lib/gooddata_marketo/models/load.rb +17 -0
  31. data/lib/gooddata_marketo/models/mobjects.rb +121 -0
  32. data/lib/gooddata_marketo/models/streams.rb +137 -0
  33. data/lib/gooddata_marketo/models/tags.rb +35 -0
  34. data/lib/gooddata_marketo/models/validate.rb +46 -0
  35. data/lib/gooddata_marketo.rb +24 -0
  36. data/process.rb +517 -0
  37. metadata +177 -0
@@ -0,0 +1,373 @@
1
+ # encoding: UTF-8
2
+
3
+ begin
4
+ verbose, $VERBOSE = $VERBOSE, nil
5
+
6
+ dependencies = ['aws-sdk -v 1.61.0',
7
+ 'savon -v 2.8.0',
8
+ 'rubyntlm -v 0.3.2',
9
+ 'gooddata -v 0.6.11',
10
+ 'rest-client -v 1.7.2',
11
+ 'pmap -v 1.0.2']
12
+
13
+ puts "#{Time.now} => CheckDependencies: #{dependencies.join(', ')}" if GoodDataMarketo.logging
14
+ dependencies.each do |dep|
15
+ begin
16
+ gem dep.split(' ').first
17
+ rescue LoadError
18
+ puts "Installing dependency #{dep}..."
19
+ system("gem install #{dep}")
20
+ Gem.clear_paths
21
+ next
22
+ end
23
+ end
24
+
25
+ require 'openssl'
26
+ require 'savon'
27
+ require 'json'
28
+ require 'timeout'
29
+ require 'date'
30
+ require 'aws-sdk'
31
+ require 'pmap'
32
+ require 'gooddata'
33
+ require 'gooddata_datawarehouse'
34
+
35
+ require 'gooddata_marketo/loads'
36
+ require 'gooddata_marketo/helpers/stringwizard'
37
+ require 'gooddata_marketo/helpers/table'
38
+ require 'gooddata_marketo/helpers/webdav'
39
+ require 'gooddata_marketo/helpers/s3'
40
+
41
+ require 'gooddata_marketo/models/leads'
42
+ require 'gooddata_marketo/models/validate'
43
+ require 'gooddata_marketo/models/campaigns'
44
+ require 'gooddata_marketo/models/channels'
45
+ require 'gooddata_marketo/models/mobjects'
46
+ require 'gooddata_marketo/models/streams'
47
+ require 'gooddata_marketo/models/tags'
48
+
49
+ require 'gooddata_marketo/adapters/rest'
50
+
51
+ require 'gooddata_marketo/data/activity_types'
52
+ require 'gooddata_marketo/data/reserved_sql_keywords'
53
+
54
+ ensure
55
+ $VERBOSE = verbose
56
+ end
57
+
58
+ class GoodDataMarketo::Client
59
+
60
+ DEFAULT_CONFIG = {
61
+ wsdl: 'http://app.marketo.com/soap/mktows/2_4?WSDL',
62
+ read_timeout: 500,
63
+ open_timeout: 500,
64
+ namespace_identifier: :ns1,
65
+ env_namespace: 'SOAP-ENV',
66
+ namespaces: { 'xmlns:ns1' => 'http://www.marketo.com/mktows/' },
67
+ pretty_print_xml: true,
68
+ api_subdomain: '363-IXI-287',
69
+ api_version: '2_7',
70
+ log: false,
71
+ log_level: :debug
72
+ }
73
+
74
+ attr_accessor :load
75
+ attr_accessor :webdav
76
+ attr_accessor :api_limit
77
+ attr_accessor :activity_types
78
+
79
+ def initialize(config = {})
80
+
81
+ GoodDataMarketo.logging = true if config[:log]
82
+
83
+ @api_limit = config[:api_limit] || MARKETO_API_LIMIT
84
+
85
+ @loads = nil # Default is no jobs will be active.
86
+ @load = nil # No job is currently set.
87
+ @leads = nil
88
+
89
+ config = DEFAULT_CONFIG.merge(config)
90
+
91
+ @api_version = config.delete(:api_version).freeze
92
+ @subdomain = config.delete(:api_subdomain).freeze
93
+ @webdav = config.delete(:webdav)
94
+ @logger = config.delete(:logger)
95
+
96
+ @activity_types = ActivityTypes.new.values
97
+
98
+ user_id = config.delete(:user_id)
99
+ encryption_key = config.delete(:encryption_key)
100
+
101
+ @auth = AuthHeader.new(user_id, encryption_key)
102
+
103
+ @wsdl = "http://app.marketo.com/soap/mktows/#{@api_version}?WSDL".freeze
104
+ @endpoint = "https://#{@subdomain}.mktoapi.com/soap/mktows/#{@api_version}".freeze
105
+
106
+ #Create SOAP Header
107
+ @savon = Savon.client(config.merge(endpoint: @endpoint))
108
+
109
+ if GoodDataMarketo.logging
110
+ puts use = self.usage
111
+ puts "#{Time.now} => Marketo:SOAP/REST:Used of #{MARKETO_API_LIMIT}"
112
+
113
+ end
114
+
115
+ end
116
+
117
+ def test_rest
118
+ puts "#{Time.now} => SETUP: Connected to Marketo REST API:#{@subdomain}" if GoodDataMarketo.logging
119
+ begin
120
+ self.usage
121
+ true
122
+ rescue
123
+ false
124
+ end
125
+ end
126
+
127
+ def test_soap
128
+ puts "#{Time.now} => SETUP: Connected to Marketo SOAP API:#{@subdomain}" if GoodDataMarketo.logging
129
+ begin
130
+ self.leads.get_by_email('test@test.com')
131
+ true
132
+ rescue
133
+ false
134
+ end
135
+ end
136
+
137
+ def configuration
138
+ DEFAULT_CONFIG
139
+ end
140
+
141
+ def call(web_method, params, config = {}) #:nodoc:
142
+ @api_limit -= 1
143
+ @params = params
144
+ @timeouts = 0
145
+ @error = nil
146
+
147
+ # If the time limit is up, sect the process to wait
148
+ if @api_limit < 1
149
+
150
+ now = DateTime.now.utc
151
+ cst = now.in_time_zone('Central Time (US & Canada)')
152
+ delay = (24 - cst.hours) * 2
153
+ delay = 1 if delay == 0
154
+
155
+ sleep (delay * 60 * 60)
156
+ puts "#{Time.now} => API_LIMIT:Sleeping: #{delay}"
157
+
158
+ end
159
+
160
+ begin
161
+
162
+ def timed_call web_method, params, config = {}
163
+
164
+ puts "#{Time.now} => API Limit: #{@api_limit}" if GoodDataMarketo.logging
165
+ puts "#{Time.now} => Call: #{web_method}: #{@params.to_s}" if GoodDataMarketo.logging
166
+
167
+ Timeout.timeout(config[:timeout] || 4999) do
168
+
169
+ response = @savon.call(
170
+ web_method,
171
+ message: params,
172
+ soap_header: { 'ns1:AuthenticationHeader' => @auth.signature }
173
+ ).to_hash
174
+
175
+ # Add control flow to the root call because Marketo API changes for structure for just getLeadActivities
176
+ if response[:success_get_lead_activity]
177
+ response[:success_get_lead_activity][:lead_activity_list]
178
+ else
179
+ response[response.keys.first][:result]
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+
186
+ timed_call web_method, @params, config
187
+
188
+ rescue Timeout::Error => e
189
+ @timeouts += 1
190
+
191
+ forward_days = config[:forward_days] || 30
192
+
193
+ def move_forward_30_days original_time, days
194
+ smart_time = Date.parse(original_time) + days
195
+ Time.parse(smart_time.to_s).to_s
196
+ end
197
+
198
+ if @params[:start_position]
199
+
200
+ last_created_at = @params[:start_position][:last_created_at]
201
+ latest_created_at = @params[:start_position][:latest_created_at]
202
+ oldest_created_at = @params[:start_position][:oldest_created_at]
203
+ last_updated_at = @params[:start_position][:last_updated_at]
204
+
205
+ if oldest_created_at
206
+ new_time = move_forward_30_days(oldest_created_at, forward_days)
207
+ @params[:start_position][:oldest_created_at] = new_time
208
+ elsif last_created_at
209
+ new_time = move_forward_30_days(last_created_at, forward_days)
210
+ @params[:start_position][:last_created_at] = new_time
211
+ elsif latest_created_at
212
+ new_time = move_forward_30_days(latest_created_at, forward_days)
213
+ @params[:start_position][:latest_created_at] = new_time
214
+ elsif last_updated_at
215
+ new_time = move_forward_30_days(last_updated_at, forward_days)
216
+ @params[:start_position][:last_updated_at] = new_time
217
+ else
218
+ exit
219
+ end
220
+
221
+ puts "#{Time.now} => #{e}:Retrying requested date +#{forward_days} days:attempts:#{@timeouts}/12" if GoodDataMarketo.logging
222
+ retry unless @timeouts > 12
223
+
224
+ else
225
+ exit
226
+ end
227
+
228
+ end
229
+
230
+ rescue Exception => e
231
+ puts @error = e
232
+ @logger.log(e) if @logger
233
+ nil
234
+ end
235
+
236
+ def died(json = nil)
237
+
238
+ if json
239
+ file = File.open('marketo_connector_log.json','wb')
240
+ file.write(json.to_json)
241
+ exit
242
+ else
243
+ json = File.open('marketo_connector_log.json', 'r')
244
+ json.to_h
245
+ end
246
+
247
+ end
248
+
249
+ def stream(web_method, params, config = {})
250
+
251
+ # If the stream is part of a load.
252
+
253
+ if self.load
254
+ "#{Time.now} => Load:#{self.load.json[:type]}:#{self.load.json[:method]}" if GoodDataMarketo.logging
255
+ end
256
+
257
+ puts "#{Time.now} => Stream: #{web_method}: #{params.to_s}" if GoodDataMarketo.logging
258
+
259
+ safe = config[:safe] || true
260
+ @timeouts = 0
261
+
262
+ def safe_stream web_method, params, config
263
+ begin
264
+ #Timeout.timeout(config[:timeout] || 18000) do
265
+ GoodDataMarketo::Stream.new web_method, params, :client => self
266
+ #end
267
+ rescue Timeout::Error => e
268
+ @timeouts += 1
269
+ puts e if GoodDataMarketo.logging
270
+ params[:timeouts] = @timeouts
271
+ self.load.log('TIMEOUT') if self.load
272
+ self.died params
273
+ end
274
+
275
+ end
276
+
277
+ if safe
278
+ safe_stream web_method, params, config
279
+ else
280
+ GoodDataMarketo::Stream.new web_method, params, :client => self
281
+ end
282
+
283
+
284
+ end
285
+
286
+ class AuthHeader #:nodoc:
287
+ DIGEST = OpenSSL::Digest.new('sha1')
288
+ private_constant :DIGEST
289
+
290
+ def initialize(user_id, encryption_key)
291
+ if user_id.nil? || encryption_key.nil?
292
+ raise ArgumentError, ":user_id and :encryption_key required"
293
+ end
294
+
295
+ @user_id = user_id
296
+ @encryption_key = encryption_key
297
+ end
298
+
299
+ attr_reader :user_id
300
+
301
+ # Compute the HMAC signature and return it.
302
+ def signature
303
+ time = Time.now
304
+ {
305
+ mktowsUserId: user_id,
306
+ requestTimestamp: time.to_s,
307
+ requestSignature: hmac(time),
308
+ }
309
+ end
310
+
311
+ private
312
+ def hmac(time)
313
+ OpenSSL::HMAC.hexdigest(
314
+ DIGEST,
315
+ @encryption_key,
316
+ "#{time}#{user_id}"
317
+ )
318
+ end
319
+
320
+ end
321
+
322
+ private_constant :AuthHeader
323
+
324
+ def rest
325
+ GoodDataMarketo::RESTAdapter.new :webdav => @webdav
326
+ end
327
+
328
+ def get_all_leads config = {}
329
+ rest = GoodDataMarketo::RESTAdapter.new :webdav => @webdav
330
+ rest.get_all_leads
331
+ end
332
+
333
+ alias :write_all_lead_ids_to_csv :get_all_leads
334
+
335
+ def set_load load
336
+ @load = load
337
+ end
338
+
339
+ def leads # http://developers.marketo.com/documentation/soap/getleadchanges/
340
+ GoodDataMarketo::Leads.new :client => self
341
+ end
342
+
343
+ def usage(config = {})
344
+ rest = GoodDataMarketo::RESTAdapter.new :webdav => @webdav
345
+ rest.usage
346
+ end
347
+
348
+ def campaigns # http://developers.marketo.com/documentation/soap/campaigns/
349
+ GoodDataMarketo::Campaigns.new :client => self
350
+ end
351
+
352
+ def mobjects # http://developers.marketo.com/documentation/soap/getmobjects/
353
+ GoodDataMarketo::MObjects.new :client => self
354
+ end
355
+
356
+ def operations
357
+ @savon.operations
358
+ end
359
+
360
+ def loads(config = {})
361
+ self.load = true
362
+ @loads = GoodDataMarketo::Loads.new config
363
+ end
364
+
365
+ def load=(load)
366
+ @load = load
367
+ end
368
+
369
+ alias :objects :mobjects
370
+
371
+ alias :lead :leads
372
+
373
+ end
@@ -0,0 +1,104 @@
1
+
2
+ class ActivityTypes
3
+
4
+ attr_accessor :edited
5
+ attr_accessor :values
6
+
7
+ def initialize
8
+ @values = [
9
+ "Activity Pruning",
10
+ "Add to List",
11
+ "Add to Nurture",
12
+ "Add to Opportunity",
13
+ "Add to Segment",
14
+ "Add to SFDC Campaign",
15
+ "Add to Smart Campaign",
16
+ "Assign Nurture Content",
17
+ "Attend Event",
18
+ "Capture Activity History",
19
+ "Capture Campaign Membership History",
20
+ "Capture List Membership History",
21
+ "Change Account Owner",
22
+ "Change Attribute Datatype",
23
+ "Change CRM User",
24
+ "Change Custom Object",
25
+ "Change Data Value",
26
+ "Change Nurture Cadence",
27
+ "Change Nurture Exhausted",
28
+ "Change Nurture Track",
29
+ "Change Owner",
30
+ "Change Program Data",
31
+ "Change Revenue Stage",
32
+ "Change Revenue Stage Manually",
33
+ "Change Score",
34
+ "Change Segment",
35
+ "Change Status in Progression",
36
+ "Change Status in SFDC Campaign",
37
+ "Click Email",
38
+ "Click Link",
39
+ "Click Sales Email",
40
+ "Click Shared Link",
41
+ "Compute Data Value",
42
+ "Convert Lead",
43
+ "Create Task",
44
+ "Decide Test Group Winner",
45
+ "Delete Lead from SFDC",
46
+ "Email Bounced",
47
+ "Email Bounced Soft",
48
+ "Email Delivered",
49
+ "Enrich with Data.com",
50
+ "Field Reparent",
51
+ "Fill Out Form",
52
+ "Interesting Moment",
53
+ "Lead Assigned",
54
+ "Lead Pruning",
55
+ "Merge Attributes",
56
+ "Merge Leads",
57
+ "New Lead",
58
+ "New SFDC Opportunity",
59
+ "Nurture Deploy",
60
+ "Open Email",
61
+ "Open Sales Email",
62
+ "Receive Sales Email",
63
+ "Received Forward to Friend Email",
64
+ "Register for Event",
65
+ "Remove from Flow",
66
+ "Remove from List",
67
+ "Remove from Opportunity",
68
+ "Remove from SFDC Campaign",
69
+ "Request Campaign",
70
+ "Resolve Conflicts",
71
+ "Resolve Ruleset",
72
+ "Revenue Stage Initial Assignment",
73
+ "Reward Test Group Variant",
74
+ "Run Sub-flow",
75
+ "Sales Email Bounced",
76
+ "Schedule Test Variants",
77
+ "Segmentation Approval",
78
+ "Send Alert",
79
+ "Send Email",
80
+ "Send Sales Email",
81
+ "Sent Forward to Friend Email",
82
+ "SFDC Activity",
83
+ "SFDC Activity Updated",
84
+ "SFDC Merge Leads",
85
+ "Share Content",
86
+ "Sync Lead to SFDC",
87
+ "Sync Lead Updates to SFDC",
88
+ "Touch Complete",
89
+ "Unsubscribe Email",
90
+ "Update Opportunity",
91
+ "Visit Webpage",
92
+ "Wait"
93
+ ]
94
+ end
95
+
96
+ def set_types array_types
97
+ self.edited = true
98
+ self.values = array_types
99
+ end
100
+
101
+ end
102
+
103
+
104
+
@@ -0,0 +1,205 @@
1
+
2
+ class ReservedSqlKeywords
3
+
4
+ attr_accessor :edited
5
+ attr_accessor :values
6
+
7
+ def initialize
8
+ @values = [
9
+ 'ADD',
10
+ 'EXTERNAL',
11
+ 'PROCEDURE',
12
+ 'ALL',
13
+ 'FETCH',
14
+ 'PUBLIC',
15
+ 'ALTER',
16
+ 'FILE',
17
+ 'RAISERROR',
18
+ 'AND',
19
+ 'FILLFACTOR',
20
+ 'READ',
21
+ 'ANY',
22
+ 'FOR',
23
+ 'READTEXT',
24
+ 'AS',
25
+ 'FOREIGN',
26
+ 'RECONFIGURE',
27
+ 'ASC',
28
+ 'FREETEXT',
29
+ 'REFERENCES',
30
+ 'AUTHORIZATION',
31
+ 'FREETEXTTABLE',
32
+ 'REPLICATION',
33
+ 'BACKUP',
34
+ 'FROM',
35
+ 'RESTORE',
36
+ 'BEGIN',
37
+ 'FULL',
38
+ 'RESTRICT',
39
+ 'BETWEEN',
40
+ 'FUNCTION',
41
+ 'RETURN',
42
+ 'BREAK',
43
+ 'GOTO',
44
+ 'REVERT',
45
+ 'BROWSE',
46
+ 'GRANT',
47
+ 'REVOKE',
48
+ 'BULK',
49
+ 'GROUP',
50
+ 'RIGHT',
51
+ 'BY',
52
+ 'HAVING',
53
+ 'ROLLBACK',
54
+ 'CASCADE',
55
+ 'HOLDLOCK',
56
+ 'ROWCOUNT',
57
+ 'CASE',
58
+ 'IDENTITY',
59
+ 'ROWGUIDCOL',
60
+ 'CHECK',
61
+ 'IDENTITY_INSERT',
62
+ 'RULE',
63
+ 'CHECKPOINT',
64
+ 'IDENTITYCOL',
65
+ 'SAVE',
66
+ 'CLOSE',
67
+ 'IF',
68
+ 'SCHEMA',
69
+ 'CLUSTERED',
70
+ 'IN',
71
+ 'SECURITYAUDIT',
72
+ 'COALESCE',
73
+ 'INDEX',
74
+ 'SELECT',
75
+ 'COLLATE',
76
+ 'INNER',
77
+ 'SEMANTICKEYPHRASETABLE',
78
+ 'COLUMN',
79
+ 'INSERT',
80
+ 'SEMANTICSIMILARITYDETAILSTABLE',
81
+ 'COMMIT',
82
+ 'INTERSECT',
83
+ 'SEMANTICSIMILARITYTABLE',
84
+ 'COMPUTE',
85
+ 'INTO',
86
+ 'SESSION_USER',
87
+ 'CONSTRAINT',
88
+ 'IS',
89
+ 'SET',
90
+ 'CONTAINS',
91
+ 'JOIN',
92
+ 'SETUSER',
93
+ 'CONTAINSTABLE',
94
+ 'KEY',
95
+ 'SHUTDOWN',
96
+ 'CONTINUE',
97
+ 'KILL',
98
+ 'SOME',
99
+ 'CONVERT',
100
+ 'LEFT',
101
+ 'STATISTICS',
102
+ 'CREATE',
103
+ 'LIKE',
104
+ 'SYSTEM_USER',
105
+ 'CROSS',
106
+ 'LINENO',
107
+ 'TABLE',
108
+ 'CURRENT',
109
+ 'LOAD',
110
+ 'TABLESAMPLE',
111
+ 'CURRENT_DATE',
112
+ 'MERGE',
113
+ 'TEXTSIZE',
114
+ 'CURRENT_TIME',
115
+ 'NATIONAL',
116
+ 'THEN',
117
+ 'CURRENT_TIMESTAMP',
118
+ 'NOCHECK',
119
+ 'TO',
120
+ 'CURRENT_USER',
121
+ 'NONCLUSTERED',
122
+ 'TOP',
123
+ 'CURSOR',
124
+ 'NOT',
125
+ 'TRAN',
126
+ 'DATABASE',
127
+ 'NULL',
128
+ 'TRANSACTION',
129
+ 'DBCC',
130
+ 'NULLIF',
131
+ 'TRIGGER',
132
+ 'DEALLOCATE',
133
+ 'OF',
134
+ 'TRUNCATE',
135
+ 'DECLARE',
136
+ 'OFF',
137
+ 'TRY_CONVERT',
138
+ 'DEFAULT',
139
+ 'OFFSETS',
140
+ 'TSEQUAL',
141
+ 'DELETE',
142
+ 'ON',
143
+ 'UNION',
144
+ 'DENY',
145
+ 'OPEN',
146
+ 'UNIQUE',
147
+ 'DESC',
148
+ 'OPENDATASOURCE',
149
+ 'UNPIVOT',
150
+ 'DISK',
151
+ 'OPENQUERY',
152
+ 'UPDATE',
153
+ 'DISTINCT',
154
+ 'OPENROWSET',
155
+ 'UPDATETEXT',
156
+ 'DISTRIBUTED',
157
+ 'OPENXML',
158
+ 'USE',
159
+ 'DOUBLE',
160
+ 'OPTION',
161
+ 'USER',
162
+ 'DROP',
163
+ 'OR',
164
+ 'VALUES',
165
+ 'DUMP',
166
+ 'ORDER',
167
+ 'VARYING',
168
+ 'ELSE',
169
+ 'OUTER',
170
+ 'VIEW',
171
+ 'END',
172
+ 'OVER',
173
+ 'WAITFOR',
174
+ 'ERRLVL',
175
+ 'PERCENT',
176
+ 'WHEN',
177
+ 'ESCAPE',
178
+ 'PIVOT',
179
+ 'WHERE',
180
+ 'EXCEPT',
181
+ 'PLAN',
182
+ 'WHILE',
183
+ 'EXEC',
184
+ 'PRECISION',
185
+ 'WITH',
186
+ 'EXECUTE',
187
+ 'PRIMARY',
188
+ 'WITHIN GROUP',
189
+ 'EXISTS',
190
+ 'PRINT',
191
+ 'WRITETEXT',
192
+ 'EXIT',
193
+ 'PROC'
194
+ ]
195
+ end
196
+
197
+ def set_types array_types
198
+ self.edited = true
199
+ self.values = array_types
200
+ end
201
+
202
+ end
203
+
204
+
205
+