gooddata_marketo 0.0.1-java

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.
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
+