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,493 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'gooddata_marketo/models/child/lead'
4
+ require 'gooddata_marketo/models/child/activity'
5
+
6
+ class GoodDataMarketo::Leads
7
+
8
+ attr_reader :client
9
+
10
+ def initialize config = {}
11
+
12
+ @client = config[:client]
13
+
14
+ end
15
+
16
+ def [](a)
17
+ if a.include? '@'
18
+ self.get_by_email(a)
19
+ else
20
+ self.get_by_id(a)
21
+ end
22
+ end
23
+
24
+ # POSSIBLE KEY TYPES FOR LEAD QUERIES
25
+ #
26
+ # IDNUM: The Marketo ID (e.g. 64)
27
+ # COOKIE: The value generated by the Munchkin Javascript. (e.g. id:561-HYG-937&token:_mch-marketo.com-1258067434006-50277)
28
+ # EMAIL: The email address associated with the lead. (e.g. rufus@marketo.com)
29
+ # SFDCLEADID: The lead ID from SalesForce
30
+ # LEADOWNEREMAIL: The Lead Owner Email
31
+ # SFDCACCOUNTID: The Account ID from SalesForce
32
+ # SFDCCONTACTID: The Contact ID from SalesForce
33
+ # SFDCLEADID: TheLead ID from SalesForce
34
+ # SFDCLEADOWNERID: The Lead owner ID from SalesForce
35
+ # SFDCOPPTYID: The Opportunity ID from SalesForce
36
+
37
+ def get_by_email email # http://developers.marketo.com/documentation/soap/getlead/
38
+ type = 'EMAIL'
39
+ request = { :lead_key => { :key_type => type, :key_value => email } }
40
+ response = client.call(:get_lead, request)
41
+ # GoodDataMarketo::Lead.new response[:lead_record_list][:lead_record], :client => client (CLIENT REMOVED DUE TO PERFORMANCE ISSUE)
42
+ GoodDataMarketo::Lead.new response[:lead_record_list][:lead_record]
43
+
44
+ end
45
+
46
+ def get_by_id id
47
+ type = 'IDNUM'
48
+ request = { :lead_key => { :key_type => type, :key_value => id } }
49
+ response = client.call(:get_lead, request)
50
+ # GoodDataMarketo::Lead.new response[:lead_record_list][:lead_record], :client => client (CLIENT REMOVED DUE TO PERFORMANCE ISSUE)
51
+ GoodDataMarketo::Lead.new response[:lead_record_list][:lead_record]
52
+
53
+ end
54
+
55
+ def get_multiple config = {} # http://developers.marketo.com/documentation/soap/getmultipleleads/
56
+
57
+ values_array = config[:ids] || config[:values]
58
+
59
+ @leads_from_call = []
60
+
61
+ # Possible types
62
+ # IDNUM, COOKIE, EMAIL, LEADOWNEREMAIL, SFDCACCOUNTID, SFDCCONTACTID, SFDCLEADID, SFDCLEADOWNERID, SFDCOPPTYID.
63
+
64
+ type = config[:type] || 'IDNUM'
65
+ xsi_type = config[:xsi_type] || 'ns1:LeadKeySelector'
66
+
67
+ if values_array.is_a? String
68
+ values_array = [values_array]
69
+ end
70
+
71
+ segments = values_array.each_slice(100).to_a
72
+
73
+ segments.each { |values|
74
+
75
+ request = {
76
+ :lead_selector => {
77
+ :key_type => type,
78
+ :key_values => {
79
+ :string_item => values
80
+ }
81
+ },
82
+ :batch_size => config[:batch_size] || "100", # Unable to determine timeout rate at the 1000 so moved to 200.
83
+ :attributes! => { :lead_selector => { 'xsi:type' => xsi_type } }
84
+ }
85
+
86
+ inc = config[:include_attributes] || config[:include] || config[:types]
87
+ if inc
88
+ if inc.is_a? String
89
+ inc = [inc]
90
+ end
91
+ request[:activity_filter][:include_attributes] = inc
92
+ end
93
+
94
+ exc = config[:exclude_attributes] || config[:exclude]
95
+ if exc
96
+ if exc.is_a? String
97
+ exc = [exc]
98
+ end
99
+ request[:activity_filter][:exclude_attributes] = exc
100
+ end
101
+
102
+ activities = config[:filters] || config[:activity_name_filter] || config[:activities]
103
+ if activities
104
+ activities = [activities] if activities.is_a? String
105
+ request[:activity_name_filter] = Hash.new
106
+ request[:activity_name_filter][:string_item] = activities
107
+ end
108
+
109
+ # Ensure that the API call is not made with both.
110
+ if inc && exc
111
+ raise "Include and exclude attributes may not be used in the same call."
112
+ end
113
+
114
+ request[:start_position] = Hash.new if config[:oldest_created_at] || config[:activity_created_at] || config[:offset]
115
+
116
+ if config[:oldest_created_at]
117
+ begin
118
+ oca = StringWizard.time(config[:oldest_created_at]).to_s
119
+ request[:start_position][:oldest_created_at] = oca
120
+ rescue Exception => e
121
+ puts e if GoodDataMarketo.logging
122
+ end
123
+
124
+ end
125
+
126
+ if config[:activity_created_at]
127
+ begin
128
+ aca = StringWizard.time(config[:activity_created_at]).to_s
129
+ request[:start_position][:activity_created_at] = aca
130
+ rescue Exception => e
131
+ puts e if GoodDataMarketo.logging
132
+ end
133
+ end
134
+
135
+ if config[:latest_created_at]
136
+ begin
137
+ lca = StringWizard.time(config[:latest_created_at]).to_s
138
+ request[:start_position][:latest_created_at] = lca
139
+ rescue Exception => e
140
+ puts e if GoodDataMarketo.logging
141
+ end
142
+ end
143
+
144
+ offset_date = config[:offset]
145
+
146
+ request[:start_position][:offset] = offset_date if offset_date
147
+
148
+ # Execute a stream unless the number of leads is less than the batch limit of 100
149
+ begin
150
+
151
+ if values.length < 101 && values.length > 1
152
+
153
+ c = client.call(:get_multiple_leads, request)
154
+ c[:lead_record_list][:lead_record].each { |lead|
155
+ # l = GoodDataMarketo::Lead.new lead, :client => client (CLIENT REMOVED DUE TO PERFORMANCE ISSUE)
156
+ if client.load
157
+ l = lead.to_json
158
+ else
159
+ l = GoodDataMarketo::Lead.new lead
160
+ end
161
+
162
+ @leads_from_call << l
163
+ }
164
+
165
+ if client.load
166
+ ids = client.load.arguments[:ids].drop(values.length)
167
+ client.load.arguments[:ids] = ids
168
+ client.load.save
169
+
170
+ end
171
+
172
+
173
+ elsif values.length == 1
174
+ c = client.call(:get_multiple_leads, request)
175
+
176
+ # l = GoodDataMarketo::Lead.new c[:lead_record_list][:lead_record], :client => client (CLIENT REMOVED DUE TO PERFORMANCE ISSUE)
177
+ if client.load
178
+ l = c[:lead_record_list][:lead_record].to_json
179
+ else
180
+ l = GoodDataMarketo::Lead.new c[:lead_record_list][:lead_record]
181
+ end
182
+
183
+ @leads_from_call << l
184
+
185
+ else
186
+
187
+ c = client.stream(:get_multiple_leads, request)
188
+
189
+ if client.load
190
+
191
+ ids = client.load.arguments[:ids].drop(values.length)
192
+
193
+ client.load.arguments[:ids] = ids
194
+
195
+ client.load.save
196
+
197
+ end
198
+
199
+ puts "#{Time.now} => Marketo:Leads:#{c.storage.length}" if GoodDataMarketo.logging
200
+ c.storage.each do |request|
201
+ request[:lead_record_list][:lead_record].each { |lead|
202
+ # l = GoodDataMarketo::Lead.new lead, :client => client (CLIENT REMOVED DUE TO PERFORMANCE ISSUE)
203
+
204
+ # To conserve memory on large batches sent the raw file to load storage.
205
+ if client.load
206
+ l = lead.to_json
207
+ else
208
+ l = GoodDataMarketo::Lead.new lead
209
+ end
210
+
211
+ @leads_from_call << l
212
+ }
213
+ end
214
+
215
+ end
216
+ rescue Exception => exp
217
+ puts exp if GoodDataMarketo.logging
218
+ puts "#{Time.now} => 0 results for Marketo query."
219
+ end
220
+
221
+ puts "#{Time.now} => Marketo:Leads:Queue:#{@leads_from_call.length}"
222
+
223
+ }
224
+ client.load.log('RESPONSE') if client.load
225
+ client.load.storage = @leads_from_call if client.load
226
+ @leads_from_call
227
+
228
+ end
229
+
230
+ def get_changes(config = {}) # http://developers.marketo.com/documentation/soap/getleadchanges/
231
+
232
+ # EXAMPLE REQUEST
233
+ # request = {
234
+ # :start_position => {
235
+ # :oldest_created_at => "2013-07-01 23:58:14 -0700",
236
+ # },
237
+ # :activity_name_filter => {
238
+ # :stringItem => ["Visit Webpage", "Click Link"] },
239
+ # :batch_size => "100"
240
+ # }
241
+
242
+ # OPTIONAL ACTIVITIES
243
+ # NewLead
244
+ # AssocWithOpprtntyInSales
245
+ # DissocFromOpprtntyInSales
246
+ # UpdateOpprtntyInSales
247
+ # ChangeDataValue
248
+ # MergeLeads
249
+ # OpenEmail
250
+ # SendEmail
251
+
252
+ request = {
253
+ :start_position => Hash.new,
254
+ :batch_size => config[:batch_size] || '1000'
255
+ }
256
+
257
+ ###################
258
+ # Activity Config #
259
+ ###################
260
+
261
+ query = config[:lead] || config[:email] || config[:values] || config[:value]
262
+
263
+ if query
264
+ query = [query] if query.is_a? String
265
+ xsi_type = config[:xsi_type] || 'ns1:LeadKeySelector'
266
+
267
+ request[:lead_selector] = {}
268
+ request[:lead_selector][:key_values] = {}
269
+ request[:lead_selector][:key_type] = config[:type] || 'EMAIL'
270
+ request[:lead_selector][:key_values][:string_item] = query
271
+ request[:attributes!] = { :lead_selector => { 'xsi:type' => xsi_type } }
272
+
273
+ end
274
+
275
+ inc = config[:include_attributes] || config[:include] || config[:types]
276
+ if inc
277
+ if inc.is_a? String
278
+ inc = [inc]
279
+ end
280
+ request[:activity_filter][:include_attributes] = inc
281
+ end
282
+
283
+ exc = config[:exclude_attributes] || config[:exclude]
284
+ if exc
285
+ if exc.is_a? String
286
+ exc = [exc]
287
+ end
288
+ request[:activity_filter][:exclude_attributes] = exc
289
+ end
290
+
291
+ activities = config[:filters] || config[:activity_name_filter] || config[:activities]
292
+ if activities
293
+
294
+ activities = [activities] if activities.is_a? String
295
+ request[:activity_name_filter] = Hash.new
296
+ request[:activity_name_filter][:string_item] = activities
297
+ end
298
+
299
+ # Ensure that the API call is not made with both.
300
+ if inc && exc
301
+ raise "Include and exclude attributes may not be used in the same call."
302
+ end
303
+
304
+ #########################
305
+ # Start Position Config #
306
+ #########################
307
+
308
+ if config[:oldest_created_at]
309
+ begin
310
+ oca = StringWizard.time(config[:oldest_created_at])
311
+ request[:start_position][:oldest_created_at] = oca
312
+ rescue Exception => e
313
+ puts e if GoodDataMarketo.logging
314
+ end
315
+
316
+ end
317
+
318
+ if config[:latest_created_at]
319
+ begin
320
+ lca = StringWizard.time(config[:latest_created_at])
321
+ request[:start_position][:latest_created_at] = lca
322
+ rescue Exception => e
323
+ puts e if GoodDataMarketo.logging
324
+ end
325
+
326
+ end
327
+
328
+ if config[:activity_created_at]
329
+ begin
330
+ aca = StringWizard.time(config[:activity_created_at])
331
+ request[:start_position][:activity_created_at] = aca
332
+ rescue Exception => e
333
+ puts e if GoodDataMarketo.logging
334
+ end
335
+ end
336
+
337
+ o = config[:offset]
338
+ request[:start_position][:offset] = o if o
339
+
340
+ raise 'A start position type is required (:oldest_created_at, :offset, :activity_created_at)' unless oca || aca || o
341
+
342
+ c = client.stream(:get_lead_changes, request)
343
+
344
+ # Load all of the leads from the stream into an array for processes from a load or direct call.
345
+ # If it is a direct call, build an object, if not move the item as raw json.
346
+
347
+ leads_from_changes_call = []
348
+ begin
349
+
350
+ c.storage.pmap do |request|
351
+
352
+ stored_item = request[:lead_change_record_list][:lead_change_record]
353
+
354
+ if stored_item.is_a? Array
355
+ stored_item.each { |activity|
356
+ if client.load
357
+ l = activity.to_json
358
+ else
359
+ l = GoodDataMarketo::Activity.new activity
360
+ end
361
+
362
+ leads_from_changes_call << l
363
+ }
364
+ else
365
+ if client.load
366
+ l = stored_item.to_json
367
+ else
368
+ l = GoodDataMarketo::Activity.new activity
369
+ end
370
+ leads_from_changes_call << l
371
+ end
372
+ end
373
+
374
+ rescue
375
+
376
+ puts "#{Time.now} => 0 results for Marketo query."
377
+ end
378
+
379
+ client.load.log('RESPONSE') if client.load
380
+ client.load.storage = leads_from_changes_call if client.load
381
+
382
+ leads_from_changes_call
383
+
384
+ end
385
+
386
+ def get_activities config = {} # http://developers.marketo.com/documentation/soap/getleadactivity/
387
+
388
+ # EXAMPLE HISTORY REQUEST
389
+ # request = {
390
+ # :lead_selector => {
391
+ # :key_type => "IDNUM",
392
+ # :key_value => "example@host.com"},
393
+ # :activity_filter => {
394
+ # :include_types => {
395
+ # :activity_type => ["VisitWebpage", "FillOutForm"]
396
+ # }
397
+ # },
398
+ # :start_position => {
399
+ # :"last_created_at/" => "",
400
+ # :"offset/" => "" },
401
+ # :batch_size => "10"
402
+ # }
403
+
404
+ request = {
405
+ :lead_key => {
406
+ :key_type => config[:type] || "EMAIL",
407
+ :key_value => config[:value]},
408
+ :batch_size => config[:batch_size] || "1000"
409
+ }
410
+
411
+ activity_types = config[:activity_types] || config[:activity_type]
412
+
413
+ if activity_types
414
+
415
+ activity_types = [activity_types] if activity_types.is_a? String
416
+
417
+ request[:activity_filter] = {}
418
+ request[:activity_filter][:include_types] = {}
419
+ request[:activity_filter][:include_types] = activity_types
420
+
421
+ end
422
+
423
+ # Check if any date options were added and if so add the date hash.
424
+ if config[:last_created_at] || config[:activity_created_at] || config[:latest_created_at] || config[:oldest_created_at]
425
+ request[:start_position] = {}
426
+ end
427
+
428
+ request[:start_position][:last_created_at] = StringWizard.time(config[:last_created_at]) if config[:last_created_at]
429
+ request[:start_position][:activity_created_at] = StringWizard.time(config[:activity_created_at]) if config[:activity_created_at]
430
+ request[:start_position][:latest_created_at] = StringWizard.time(config[:latest_created_at]) if config[:latest_created_at]
431
+ request[:start_position][:oldest_created_at] = StringWizard.time(config[:oldest_created_at]) if config[:oldest_created_at]
432
+
433
+ c = client.stream(:get_lead_activity, request)
434
+
435
+ leads_from_activities_call = []
436
+ begin
437
+ c.storage.each do |request|
438
+ request[:activity_record_list][:activity_record].each { |activity|
439
+ l = GoodDataMarketo::Activity.new activity
440
+ leads_from_activities_call << l
441
+ }
442
+ end
443
+ rescue
444
+ puts "#{Time.now} => Marketo (0) results for query. Adjust the configuration of the request or confirm there has not been changes to the structure of response from Marketo."
445
+ end
446
+
447
+ client.load.log('RESPONSE') if client.load
448
+
449
+ leads_from_activities_call
450
+
451
+ end
452
+
453
+ alias :get_activity :get_activities
454
+
455
+ def get_activity_by_email email
456
+ config = {}
457
+ config[:type] = 'EMAIL'
458
+ config[:value] = email
459
+
460
+ self.get_activities config
461
+
462
+ end
463
+
464
+ def get_activity_by_id id
465
+ config = {}
466
+ config[:type] = 'IDNUM'
467
+ config[:value] = id
468
+
469
+ self.get_activity config
470
+
471
+ end
472
+
473
+ def get_multiple_by_email emails, config = {}
474
+ config[:type] = 'EMAIL'
475
+ config[:ids] = emails
476
+ self.get_multiple config
477
+ end
478
+
479
+ def get_multiple_by_id ids, config = {}
480
+ config[:type] = 'IDNUM'
481
+ config[:ids] = ids
482
+ self.get_multiple config
483
+ end
484
+
485
+ def types
486
+ m = self.methods - Object.methods
487
+ m.delete(:types)
488
+ m
489
+ end
490
+
491
+ end
492
+
493
+
@@ -0,0 +1,17 @@
1
+ class GoodDataMarketo::LoadFile
2
+
3
+ attr_accessor :name
4
+ attr_accessor :method
5
+ attr_accessor :type
6
+ attr_accessor :arguments
7
+
8
+ def initialize config = {}
9
+ @name = config[:name]
10
+ @type = config[:type]
11
+ @method = config[:method]
12
+ @arguments = {
13
+ :ids => config[:ids] || config[:values],
14
+ :type => config[:type]
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: UTF-8
2
+ # http://developers.marketo.com/documentation/soap/getmobjects/
3
+ require 'gooddata_marketo/models/child/criteria'
4
+ require 'gooddata_marketo/models/child/mobj'
5
+
6
+ class GoodDataMarketo::MObjects
7
+
8
+ attr_reader :client
9
+
10
+ def initialize config = {}
11
+
12
+ @obj_criteria_list = []
13
+ @client = config[:client]
14
+
15
+ end
16
+
17
+ def get config = {} # http://developers.marketo.com/documentation/soap/getcampaignsforsource/
18
+
19
+ # EXAMPLE CRITERIA
20
+ # criteria = {
21
+ # :attr_name => "Id", # See the types of content it can search above.
22
+ # :comparison => "LE",
23
+ # :attr_value => "1010"
24
+ # }
25
+
26
+ # EXAMPLE ASSOCIATED OBJECT
27
+ # # m_associated_object = {
28
+ # :m_obj_type => '',
29
+ # :id=> '',
30
+ # :external_key => '' <-- Optional
31
+ # }
32
+
33
+ # EXAMPLE CRITERIA COMPARISONS
34
+ # EQ - Equals
35
+ # NE - Not Equals
36
+ # LT - Less Than
37
+ # LE - Less Than or Equals
38
+ # GT - Greater Than
39
+ # GE - Greater Than or Equals
40
+
41
+ # ACCEPTED ATTRIBUTE VALUE ITEMS
42
+ # Name: Name of the MObject
43
+ # Role: The role associated with an OpportunityPersonRole object
44
+ # Type: The type of an Opportunity object
45
+ # Stage:- The stage of an Opportunity object
46
+ # CRM Id: the CRM Id could refer to the Id of the Salesforce campaign connected to a Marketo program. Note: The SFDC Campaign ID needs to be the 18-digit ID.
47
+ # Created At: Equals, not equals, less than, less than or equal to, greater than, greater than or equal to
48
+ # Two “created dates” can be specified to create a date range
49
+ # Updated At or Tag Type (only one can be specified): Equals, not equals, less than, less than or equal to, greater than, greater than or equal to
50
+ # Two “created dates” can be specified to create a date range
51
+ # Tag Value: (Only one can be specified)
52
+ # Workspace Name: (only one can be specified)
53
+ # Workspace Id: (only one can be specified)
54
+ # Include Archive: Applicable only with Program MObject. Set it to true if you wish to include archived programs.
55
+
56
+ # AVAILABLE TYPES
57
+ # Program
58
+ # OpportunityPersonRole
59
+ # Opportunity
60
+
61
+ type = config[:type] || "Program"
62
+ request = { :type => type }
63
+
64
+ criteria = config[:criteria] || @obj_criteria_list
65
+ request[:m_obj_criteria_list] = criteria if criteria
66
+ associate = config[:association] || config[:m_obj_association] || config[:associate]
67
+
68
+ if associate
69
+ request[:m_obj_association_list] = Hash.new
70
+ request[:m_obj_association_list][:m_obj_association] = associate
71
+ end
72
+
73
+ # This field is optional and only present during `type => 'Program'` calls.
74
+ if type == "Program"
75
+ request[:include_details] = config[:include_details].to_s || "false"
76
+ end
77
+
78
+ c = client.stream(:get_m_objects, request)
79
+
80
+ mobjects_from_call = []
81
+ c.storage.each do |request|
82
+ request[:m_object_list][:m_object].each do |mobj|
83
+ m = GoodDataMarketo::MObject.new mobj
84
+ mobjects_from_call << m
85
+ end
86
+ end
87
+
88
+ binding.pry
89
+ mobjects_from_call
90
+
91
+ end
92
+
93
+ def add_criteria name, value, comparison
94
+
95
+ obj = {}
96
+ obj[:m_obj_criteria] = {
97
+ :attr_name => name.to_s,
98
+ :comparison => comparison.to_s,
99
+ :attr_value => value.to_s
100
+ }
101
+
102
+ @obj_criteria_list << obj
103
+
104
+ end
105
+
106
+ def criteria
107
+ @obj_criteria_list
108
+ end
109
+
110
+ def remove_criteria query
111
+ match = @obj_criteria_list.find {|item| item[:m_obj_criteria][:attr_name] == query }
112
+ @obj_criteria_list.delete(match)
113
+ end
114
+
115
+ alias :remove :remove_criteria
116
+
117
+ alias :add :add_criteria
118
+
119
+ end
120
+
121
+