ruby-macrodroid 0.8.9 → 0.9.1

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.
@@ -0,0 +1,630 @@
1
+ # file: ruby-macrodroid/macro.rb
2
+
3
+
4
+ # This file contains the following classes:
5
+ #
6
+ # ## Macro class
7
+ #
8
+ # Macro
9
+
10
+
11
+
12
+ VAR_TYPES = {
13
+ String: [2, :string_value],
14
+ TrueClass: [0, :boolean_value],
15
+ TrueClass: [0, :boolean_value],
16
+ Integer: [1, :int_value],
17
+ Float: [3, :decimal_value]
18
+ }
19
+
20
+
21
+ class Macro
22
+ using ColouredText
23
+ using Params
24
+
25
+ attr_reader :local_variables, :triggers, :actions, :constraints,
26
+ :guid, :deviceid
27
+ attr_accessor :title, :description, :remote_url
28
+
29
+ def initialize(name=nil, geofences: nil, deviceid: nil, remote_url: nil,
30
+ debug: false)
31
+
32
+ @title, @geofences, @deviceid, @debug = name, geofences, deviceid, debug
33
+ @remote_url = remote_url
34
+
35
+ puts 'inside Macro#initialize' if @debug
36
+
37
+ @local_variables, @triggers, @actions, @constraints = [], [], [], []
38
+ @h = {}
39
+
40
+ end
41
+
42
+ def add(obj)
43
+
44
+ if obj.kind_of? Trigger then
45
+
46
+ puts 'trigger found' if @debug
47
+ @triggers << obj
48
+
49
+ elsif obj.kind_of? Action
50
+
51
+ puts 'action found' if @debug
52
+ @actions << obj
53
+
54
+ elsif obj.kind_of? Constraint
55
+
56
+ puts 'constraint found' if @debug
57
+ @constraints << obj
58
+
59
+ end
60
+
61
+ end
62
+
63
+ def to_h()
64
+
65
+ h = {
66
+ local_variables: varify(@local_variables),
67
+ m_trigger_list: @triggers.map(&:to_h),
68
+ m_action_list: @actions.map(&:to_h),
69
+ m_category: @category,
70
+ m_constraint_list: @constraints.map(&:to_h),
71
+ m_description: '',
72
+ m_name: title(),
73
+ m_excludeLog: false,
74
+ m_GUID: guid(),
75
+ m_isOrCondition: false,
76
+ m_enabled: false,
77
+ m_descriptionOpen: false,
78
+ m_headingColor: 0
79
+ }
80
+
81
+ puts 'h: ' + h.inspect if @debug
82
+
83
+ @h.merge(h)
84
+ end
85
+
86
+ def import_h(h)
87
+
88
+ if @debug then
89
+ puts 'inside import_h'
90
+ puts 'h:' + h.inspect
91
+ end
92
+
93
+ @category = h[:category]
94
+ @title = h[:name]
95
+ @description = h[:description]
96
+
97
+ # fetch the local variables
98
+ if h[:local_variables].any? and h[:local_variables].first.any? then
99
+
100
+ @local_variables = h[:local_variables].map do |var|
101
+
102
+ val = case var[:type]
103
+ when 0 # boolean
104
+ var[:boolean_value]
105
+ when 1 # integer
106
+ var[:int_value]
107
+ when 2 # string
108
+ var[:string_value]
109
+ when 3 # decimal
110
+ var[:decimal_Value]
111
+ end
112
+
113
+ [var[:name], val]
114
+
115
+ end.to_h
116
+ end
117
+
118
+ # fetch the triggers
119
+ @triggers = h[:trigger_list].map do |trigger|
120
+ puts 'trigger: ' + trigger.inspect
121
+ #exit
122
+ object(trigger.to_snake_case)
123
+
124
+ end
125
+
126
+ @actions = h[:action_list].map do |action|
127
+ object(action.to_snake_case)
128
+ end
129
+ puts 'before fetch constraints' if @debug
130
+ # fetch the constraints
131
+ @constraints = h[:constraint_list].map do |constraint|
132
+ object(constraint.to_snake_case)
133
+ end
134
+ puts 'after fetch constraints' if @debug
135
+ @h = h
136
+
137
+ %i(local_variables m_trigger_list m_action_list m_constraint_list)\
138
+ .each {|x| @h[x] = [] }
139
+ puts 'after @h set' if @debug
140
+ @h
141
+
142
+ end
143
+
144
+ def import_xml(node)
145
+
146
+ if @debug then
147
+ puts 'inside Macro#import_xml'
148
+ puts 'node: ' + node.xml.inspect
149
+ end
150
+
151
+ if node.element('triggers') then
152
+
153
+ # level 2
154
+
155
+ @title = node.attributes[:name]
156
+ @category = node.attributes[:category]
157
+ @description = node.attributes[:description]
158
+
159
+
160
+ # get all the triggers
161
+ @triggers = node.xpath('triggers/*').map do |e|
162
+
163
+ puts 'e.name: ' + e.name.inspect if @debug
164
+ {timer: TimerTrigger}[e.name.to_sym].new(e.attributes.to_h)
165
+
166
+ end
167
+
168
+ # get all the actions
169
+ @actions = node.xpath('actions/*').map do |e|
170
+
171
+ if e.name == 'notification' then
172
+
173
+ case e.attributes[:type].to_sym
174
+ when :popup
175
+ e.attributes.delete :type
176
+ ToastAction.new e.attributes.to_h
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+
183
+ # get all the constraints
184
+ @constraints = node.xpath('constraints/*').map do |e|
185
+
186
+ puts 'e.name: ' + e.name.inspect if @debug
187
+ {airplanemode: AirplaneModeConstraint}[e.name.to_sym].new(e.attributes.to_h)
188
+
189
+ end
190
+
191
+ else
192
+
193
+ # Level 1
194
+
195
+ puts 'import_xml: inside level 1' if @debug
196
+
197
+ @title = node.text('macro') || node.attributes[:name]
198
+
199
+ @local_variables = node.xpath('variable').map do |e|
200
+
201
+ label, v = e.text.to_s.split(/: */,2)
202
+
203
+ value = if v.to_f.to_s == v
204
+ v.to_f
205
+ elsif v.downcase == 'true'
206
+ true
207
+ elsif v.downcase == 'false'
208
+ false
209
+ elsif v.to_i.to_s == v
210
+ v.to_i
211
+ else
212
+ v
213
+ end
214
+
215
+ [label, value]
216
+ end
217
+
218
+ #@description = node.attributes[:description]
219
+
220
+ tp = TriggersNlp.new(self)
221
+
222
+ @triggers = node.xpath('trigger').flat_map do |e|
223
+
224
+ r = tp.find_trigger e.text
225
+
226
+ puts 'found trigger ' + r.inspect if @debug
227
+
228
+ item = e.element('item')
229
+ if item then
230
+
231
+ if item.element('description') then
232
+
233
+ item.xpath('description').map do |description|
234
+
235
+ inner_lines = description.text.to_s.strip.lines
236
+ puts 'inner_lines: ' + inner_lines.inspect if @debug
237
+
238
+ trigger = if e.text.to_s.strip.empty? then
239
+ inner_lines.shift.strip
240
+ else
241
+ e.text.strip
242
+ end
243
+
244
+ puts 'trigger: ' + trigger.inspect if @debug
245
+
246
+ r = tp.find_trigger trigger
247
+ puts 'r: ' + r.inspect if @debug
248
+ o = r[0].new([description, self]) if r
249
+ puts 'after o' if @debug
250
+ o
251
+
252
+ end
253
+
254
+ else
255
+
256
+ trigger = e.text.strip
257
+ r = tp.find_trigger trigger
258
+
259
+ a = e.xpath('item/*')
260
+
261
+ h = if a.any? then
262
+ a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
263
+ else
264
+ {}
265
+ end
266
+
267
+ r = tp.find_trigger trigger
268
+ r[0].new(h) if r
269
+
270
+ end
271
+
272
+ else
273
+
274
+ trigger = e.text.strip
275
+ r = tp.find_trigger trigger
276
+ r[0].new(r[1]) if r
277
+
278
+ end
279
+
280
+ end
281
+
282
+ ap = ActionsNlp.new self
283
+
284
+ @actions = node.xpath('action').flat_map do |e|
285
+
286
+ puts 'action e: ' + e.xml.inspect if @debug
287
+ puts 'e.text ' + e.text if @debug
288
+
289
+ item = e.element('item')
290
+ if item then
291
+
292
+ if item.element('description') then
293
+
294
+ item.xpath('description').map do |description|
295
+
296
+ inner_lines = description.text.to_s.strip.lines
297
+ puts 'inner_lines: ' + inner_lines.inspect if @debug
298
+
299
+ action = if e.text.to_s.strip.empty? then
300
+ inner_lines.shift.strip
301
+ else
302
+ e.text.strip
303
+ end
304
+
305
+ puts 'action: ' + action.inspect if @debug
306
+
307
+ r = ap.find_action action
308
+ puts 'r: ' + r.inspect if @debug
309
+ o = r[0].new([description, self]) if r
310
+ puts 'after o' if @debug
311
+ o
312
+
313
+ end
314
+
315
+ else
316
+
317
+ action = e.text.strip
318
+ r = ap.find_action action
319
+
320
+ a = e.xpath('item/*')
321
+
322
+ h = if a.any? then
323
+ a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
324
+ else
325
+ {}
326
+ end
327
+
328
+ r = ap.find_action action
329
+ r[0].new(h) if r
330
+
331
+ end
332
+
333
+ else
334
+
335
+ action = e.text.strip
336
+ r = ap.find_action action
337
+ r[0].new(r[1]) if r
338
+
339
+ end
340
+
341
+ end
342
+
343
+ cp = ConstraintsNlp.new
344
+
345
+ @constraints = node.xpath('constraint').map do |e|
346
+
347
+ r = cp.find_constraint e.text
348
+ puts 'found constraint ' + r.inspect if @debug
349
+
350
+ if r then
351
+ r[0].new(r[1])
352
+ end
353
+
354
+ end
355
+
356
+ end
357
+
358
+ self
359
+
360
+ end
361
+
362
+ def match?(triggerx, detail={time: $env[:time]}, model=nil )
363
+
364
+ if @triggers.any? {|x| x.type == triggerx and x.match?(detail, model) } then
365
+
366
+ if @debug then
367
+ puts 'checking constraints ...'
368
+ puts '@constraints: ' + @constraints.inspect
369
+ end
370
+
371
+ if @constraints.all? {|x| x.match?($env.merge(detail), model) } then
372
+
373
+ true
374
+
375
+ else
376
+
377
+ return false
378
+
379
+ end
380
+
381
+ end
382
+
383
+ end
384
+
385
+ # invokes the actions
386
+ #
387
+ def run()
388
+ @actions.map(&:invoke)
389
+ end
390
+
391
+ # prepares the environment in order for triggers to test fire successfully
392
+ # Used for testing
393
+ #
394
+ def set_env()
395
+ @triggers.each(&:set_env)
396
+ end
397
+
398
+ def to_pc()
399
+
400
+ heading = '# ' + @title
401
+ heading += '\n# ' + @description if @description
402
+ condition = @triggers.first.to_pc
403
+ actions = @actions.map(&:to_pc).join("\n")
404
+
405
+ <<EOF
406
+ #{heading}
407
+
408
+ if #{condition} then
409
+ #{actions}
410
+ end
411
+ EOF
412
+ end
413
+
414
+ def to_s(colour: false)
415
+
416
+ indent = 0 #@actions.map(&:to_s).join.lines.length > 0 ? 1 : 0
417
+
418
+ a = []
419
+ a << '# ' + @category + "\n" if @category
420
+ a << (colour ? "m".bg_cyan.gray.bold : 'm') + ': ' + @title
421
+
422
+
423
+ if @description and @description.length >= 1 then
424
+ a << (colour ? "d".bg_gray.gray.bold : 'd') + ': ' \
425
+ + @description.gsub(/\n/,"\n ")
426
+ end
427
+
428
+ if @local_variables.length >= 1 then
429
+
430
+ vars = @local_variables.map do |k,v|
431
+ label = colour ? 'v'.bg_magenta : 'v'
432
+ label += ': '
433
+ label + "%s: %s" % [k,v]
434
+ end
435
+
436
+ a << vars.join("\n")
437
+ end
438
+
439
+ puts 'before triggers' if @debug
440
+
441
+ a << @triggers.map do |x|
442
+
443
+ puts 'x: ' + x.inspect if @debug
444
+
445
+ s =-x.to_s(colour: colour)
446
+ puts 's: ' + s.inspect if @debug
447
+
448
+ s2 = if s.lines.length > 1 then
449
+ "\n" + s.lines.map {|x| x.prepend (' ' * (indent+1)) }.join
450
+ else
451
+ ' ' + s
452
+ end
453
+
454
+ puts 's2: ' + s2.inspect if @debug
455
+
456
+ #s.lines > 1 ? "\n" + x : x
457
+ (colour ? "t".bg_red.gray.bold : 't') + ":" + s2
458
+ end.join("\n")
459
+
460
+ puts 'before actions' if @debug
461
+ actions = @actions.map do |x|
462
+
463
+ puts 'x: ' + x.inspect if @debug
464
+ raise 'Macro#to_s action cannot be nil' if x.nil?
465
+ s = x.to_s(colour: colour)
466
+ #puts 's: ' + s.inspect
467
+
468
+
469
+
470
+ r = if indent <= 0 then
471
+
472
+ lines = s.lines
473
+
474
+ if lines.length > 1 then
475
+ s = lines.map {|x| x.prepend (' ' * (indent+1)) }.join
476
+ end
477
+
478
+ s2 = s.lines.length > 1 ? "\n" + s : ' ' + s
479
+
480
+ if colour then
481
+ "a".bg_blue.gray.bold + ":" + s2
482
+ else
483
+ "a:" + s2
484
+ end
485
+
486
+ elsif indent > 0
487
+
488
+ if s =~ /^Else/ then
489
+ (' ' * (indent-1)) + "%s" % s
490
+ elsif s =~ /^End/
491
+ indent -= 1
492
+ (' ' * indent) + "%s" % s
493
+ else
494
+ s2 = s.lines[0] + s.lines[1..-1].map {|x| (' ' * indent) + x }.join
495
+ (' ' * indent) + "%s" % s2
496
+ end
497
+
498
+ end
499
+
500
+ if s =~ /^(?:If|DO \/ WHILE)/i then
501
+
502
+ if indent < 1 then
503
+
504
+ r = if colour then
505
+ "a".bg_blue.gray.bold + ":\n %s" % s
506
+ else
507
+ "a:\n %s" % s
508
+ end
509
+
510
+ indent += 1
511
+ else
512
+ r = (' ' * indent) + "%s" % s
513
+ end
514
+
515
+ indent += 1
516
+ end
517
+
518
+ r
519
+
520
+ end.join("\n")
521
+
522
+
523
+
524
+
525
+ a << actions
526
+
527
+
528
+ puts 'before constraints' if @debug
529
+ if @constraints.any? then
530
+ a << @constraints.map do |x|
531
+ (colour ? "c".bg_green.gray.bold : 'c') + ": %s" % x
532
+ end.join("\n")
533
+ end
534
+
535
+
536
+
537
+
538
+
539
+ a.join("\n") + "\n"
540
+
541
+ end
542
+
543
+ def to_summary(colour: false)
544
+
545
+ if colour then
546
+
547
+ a = [
548
+ 'm'.bg_cyan.gray.bold + ': ' + @title,
549
+ 't'.bg_red.gray.bold + ': ' + @triggers.map \
550
+ {|x| x.to_summary(colour: false)}.join(", "),
551
+ 'a'.bg_blue.gray.bold + ': ' + @actions.map \
552
+ {|x| x.to_summary(colour: false)}.join(", ")
553
+ ]
554
+
555
+ if @constraints.any? then
556
+ a << 'c'.bg_green.gray.bold + ': ' + @constraints.map \
557
+ {|x| x.to_summary(colour: false)}.join(", ")
558
+ end
559
+
560
+ else
561
+
562
+ a = [
563
+ 'm: ' + @title,
564
+ 't: ' + @triggers.map {|x| x.to_summary(colour: false)}.join(", "),
565
+ 'a: ' + @actions.map {|x| x.to_summary(colour: false)}.join(", ")
566
+ ]
567
+
568
+ if @constraints.any? then
569
+ a << 'c: ' + @constraints.map \
570
+ {|x| x.to_summary(colour: false)}.join(", ")
571
+ end
572
+ end
573
+
574
+
575
+
576
+ a.join("\n") + "\n"
577
+
578
+ end
579
+
580
+ private
581
+
582
+ def guid()
583
+ '-' + rand(1..9).to_s + 18.times.map { rand 9 }.join
584
+ end
585
+
586
+ def object(h={})
587
+
588
+ puts ('inside object h:' + h.inspect).debug if @debug
589
+ klass = Object.const_get h[:class_type]
590
+ puts klass.inspect.highlight if $debug
591
+
592
+ if klass == GeofenceTrigger then
593
+ puts 'GeofenceTrigger found'.highlight if $debug
594
+ GeofenceTrigger.new(h, geofences: @geofences)
595
+ else
596
+ puts 'before klass'
597
+ h2 = h.merge( macro: self)
598
+ puts 'h2: ' + h2.inspect
599
+ r = klass.new h2
600
+ puts 'r:' + r.inspect
601
+ r
602
+
603
+ end
604
+
605
+ end
606
+
607
+ def varify(local_variables)
608
+
609
+
610
+ local_variables.map do |key, value|
611
+
612
+ puts 'value ' + value.class.to_s.to_sym.inspect
613
+ puts 'VAR_TYPES: ' + VAR_TYPES.inspect
614
+ type = VAR_TYPES[value.class.to_s.to_sym]
615
+ puts 'type: ' + type.inspect
616
+ h = {
617
+ boolean_value: false,
618
+ decimal_value: 0.0,
619
+ int_value: 0,
620
+ name: key,
621
+ string_value: '',
622
+ type: type[0]
623
+ }
624
+ h[type[1]] = value
625
+ h
626
+ end
627
+
628
+ end
629
+
630
+ end