huebot 1.2.0 → 1.4.0

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
  SHA256:
3
- metadata.gz: 7febcd2a42112b4c3636581d9419a58c490a1157637e143fd46e6e7d94aefba5
4
- data.tar.gz: d4a9ab3178d381786595f14a14899e55af7789b4650cfa2da1928b83b5fe2135
3
+ metadata.gz: 2fd718fb625c5f417b31619579459bdfcaffac98276d9981882df7d9b1e192b9
4
+ data.tar.gz: 213fb99651e6826283d4c0be21c26e9442f146ea2df1734a2acb5c53f0e6eafa
5
5
  SHA512:
6
- metadata.gz: 15150d4cd9652f7d5e2518bd714de8155f1b574054360f61c0ac383c30f6fe81e3421f0bf64f0da43f090dabd2677b71609ae0a1e4f3741524adcdd631bd1116
7
- data.tar.gz: 548d5adc4af710d2b1c9719e54fad7aa5fe5ba18749845089d0cb66fb59b4fd5e8279acc4214e961757c9956bad0b79ed8dacf4e531e3f54097322ce44a6b4bb
6
+ metadata.gz: caa7f46fb1d7ac5954f7b1e8f8b48c81b69c3cea59c7ce0ad246eb6ecafda318bd161eec1aff2a41e7e8f6a84af4d2a18a555f56dab808e97b39ff3396b91d9c
7
+ data.tar.gz: 2221b64eb67ab257232358e52657446cb3b8c95d7f102220954ee71e9a85bc1447ec1566712b651bd1a0d5c4db8be2eff82097551302186cd092688ad0df5548
data/README.md CHANGED
@@ -34,13 +34,15 @@ serial:
34
34
  state:
35
35
  bri: 150
36
36
  time: 10 # 10 second transition
37
- pause: 2 # 2 second pause before the next step
37
+ pause:
38
+ after: 2 # 2 second pause before the next step
38
39
 
39
40
  - transition:
40
41
  state:
41
42
  bri: 254
42
43
  time: 10 # 10 second transition
43
- pause: 2 # 2 second pause before the next step
44
+ pause:
45
+ after: 2 # 2 second pause before the next step
44
46
 
45
47
  - transition:
46
48
  state:
@@ -78,12 +80,14 @@ serial:
78
80
  state:
79
81
  bri: 254
80
82
  time: 10 # transition over 10 seconds
81
- pause: 5 # pause an extra 5 sec after the transition
83
+ pause:
84
+ after: 5 # pause an extra 5 sec after the transition
82
85
  - transition:
83
86
  state:
84
87
  bri: 25
85
88
  time: 10
86
- pause: 5
89
+ pause:
90
+ after: 5
87
91
 
88
92
  # Parallel branch 2: Fade inputs #2 and #4 down and up
89
93
  - serial:
@@ -96,12 +100,14 @@ serial:
96
100
  state:
97
101
  bri: 25
98
102
  time: 10
99
- pause: 5
103
+ pause:
104
+ after: 5
100
105
  - transition:
101
106
  state:
102
107
  bri: 254
103
108
  time: 10
104
- pause: 5
109
+ pause:
110
+ after: 5
105
111
  ```
106
112
 
107
113
  [See the Wiki](https://github.com/jhollinger/huebot/wiki) for more documentation and examples.
data/lib/huebot/bot.rb CHANGED
@@ -18,47 +18,49 @@ module Huebot
18
18
  private
19
19
 
20
20
  def exec(node)
21
- i = node.instruction
22
- case i
21
+ case node.instruction
23
22
  when Program::AST::Transition
24
- transition i.state, i.devices, i.sleep
23
+ transition node.instruction
25
24
  when Program::AST::SerialControl
26
- serial node.children, i.loop, i.sleep
25
+ serial node.children, node.instruction
27
26
  when Program::AST::ParallelControl
28
- parallel node.children, i.loop, i.sleep
27
+ parallel node.children, node.instruction
29
28
  else
30
- raise Error, "Unexpected instruction '#{i.class.name}'"
29
+ raise Error, "Unexpected instruction '#{node.instruction.class.name}'"
31
30
  end
32
31
  end
33
32
 
34
- def transition(state, device_refs, sleep_time = nil)
35
- time = (state["transitiontime"] || 4).to_f / 10
36
- devices = map_devices device_refs
33
+ def transition(i)
34
+ time = (i.state["transitiontime"] || 4).to_f / 10
35
+ devices = map_devices i.devices
37
36
  @logger.log :transition, {devices: devices.map(&:name)}
38
37
 
38
+ wait i.pause.pre if i.pause&.pre
39
39
  devices.map { |device|
40
40
  Thread.new {
41
41
  # TODO error handling
42
- _res = device.set_state state
43
- @logger.log :set_state, {device: device.name, state: state, result: nil}
44
- wait time
42
+ _res = device.set_state i.state
43
+ @logger.log :set_state, {device: device.name, state: i.state, result: nil}
44
+ wait Program::AST::Num.new(time) if i.wait
45
45
  }
46
46
  }.map(&:join)
47
- wait sleep_time if sleep_time
47
+ wait i.pause.post if i.pause&.post
48
48
  end
49
49
 
50
- def serial(nodes, lp, sleep_time = nil)
51
- control_loop(lp) { |loop_type|
50
+ def serial(nodes, i)
51
+ wait i.pause.pre if i.pause&.pre
52
+ control_loop(i.loop) { |loop_type|
52
53
  @logger.log :serial, {loop: loop_type}
53
54
  nodes.each { |node|
54
55
  exec node
55
56
  }
56
57
  }
57
- wait sleep_time if sleep_time
58
+ wait i.pause.post if i.pause&.post
58
59
  end
59
60
 
60
- def parallel(nodes, lp, sleep_time = nil)
61
- control_loop(lp) { |loop_type|
61
+ def parallel(nodes, i)
62
+ wait i.pause.pre if i.pause&.pre
63
+ control_loop(i.loop) { |loop_type|
62
64
  @logger.log :parallel, {loop: loop_type}
63
65
  nodes.map { |node|
64
66
  Thread.new {
@@ -67,33 +69,37 @@ module Huebot
67
69
  }
68
70
  }.map(&:join)
69
71
  }
70
- wait sleep_time if sleep_time
72
+ wait i.pause.post if i.pause&.post
71
73
  end
72
74
 
73
75
  def control_loop(lp)
74
76
  case lp
75
77
  when Program::AST::InfiniteLoop
76
78
  loop {
79
+ wait lp.pause.pre if lp.pause&.pre
77
80
  yield :infinite
78
- wait lp.pause if lp.pause
81
+ wait lp.pause.post if lp.pause&.post
79
82
  }
80
83
  when Program::AST::CountedLoop
81
- lp.n.times {
84
+ number(lp.n).round.times {
85
+ wait lp.pause.pre if lp.pause&.pre
82
86
  yield :counted
83
- wait lp.pause if lp.pause
87
+ wait lp.pause.post if lp.pause&.post
84
88
  }
85
89
  when Program::AST::DeadlineLoop
86
90
  until Time.now >= lp.stop_time
91
+ wait lp.pause.pre if lp.pause&.pre
87
92
  yield :deadline
88
- wait lp.pause if lp.pause
93
+ wait lp.pause.post if lp.pause&.post
89
94
  end
90
95
  when Program::AST::TimerLoop
91
96
  sec = ((lp.hours * 60) + lp.minutes) * 60
92
97
  time = 0
93
98
  until time >= sec
94
99
  start = Time.now
100
+ wait lp.pause.pre if lp.pause&.pre
95
101
  yield :timer
96
- wait lp.pause if lp.pause
102
+ wait lp.pause.post if lp.pause&.post
97
103
  time += (Time.now - start).round
98
104
  end
99
105
  else
@@ -118,11 +124,23 @@ module Huebot
118
124
  }
119
125
  end
120
126
 
121
- def wait(seconds)
127
+ def wait(n)
128
+ seconds = number n
122
129
  @logger.log :pause, {time: seconds}
123
130
  @waiter.call seconds
124
131
  end
125
132
 
133
+ def number(n)
134
+ case n
135
+ when Program::AST::Num
136
+ n.n
137
+ when Program::AST::RandomNum
138
+ rand(n.min..n.max)
139
+ else
140
+ raise Error, "Unknown numeric type. Expected Program::AST::Num, Program::AST::NRandomNum, found: #{n.class.name}"
141
+ end
142
+ end
143
+
126
144
  module Waiter
127
145
  def self.call(seconds)
128
146
  sleep seconds
@@ -5,6 +5,8 @@ require 'json'
5
5
  module Huebot
6
6
  module CLI
7
7
  module Helpers
8
+ DEFAULT_API_VERSION = 1.2
9
+
8
10
  #
9
11
  # Returns the command given to huebot.
10
12
  #
@@ -70,7 +72,7 @@ module Huebot
70
72
  opts.stderr.puts "Unknown file extension '#{ext}'. Expected .yaml, .yml, or .json"
71
73
  return []
72
74
  end
73
- version = (src.delete("version") || 1.0).to_f
75
+ version = (src.delete("version") || DEFAULT_API_VERSION).to_f
74
76
  Program::Src.new(src, path, version)
75
77
  }
76
78
 
@@ -80,7 +82,7 @@ module Huebot
80
82
  src = raw[0] == "{" ? JSON.load(raw) : YAML.safe_load(raw)
81
83
 
82
84
  opts.stdout.puts "Executing..." if opts.read_stdin
83
- version = (src.delete("version") || 1.0).to_f
85
+ version = (src.delete("version") || DEFAULT_API_VERSION).to_f
84
86
  sources << Program::Src.new(src, "STDIN", version)
85
87
  end
86
88
  sources
@@ -8,6 +8,8 @@ module Huebot
8
8
  rescue ::Huebot::Error => e
9
9
  opts.stderr.puts "#{e.class.name}: #{e.message}"
10
10
  return 1
11
+ rescue Interrupt
12
+ return 130
11
13
  end
12
14
 
13
15
  def self.run(sources, lights, groups, opts)
@@ -25,6 +27,8 @@ module Huebot
25
27
  rescue ::Huebot::Error => e
26
28
  opts.stderr.puts "#{e.class.name}: #{e.message}"
27
29
  return 1
30
+ rescue Interrupt
31
+ return 130
28
32
  end
29
33
 
30
34
  def self.check(sources, lights, groups, opts)
@@ -47,6 +51,8 @@ module Huebot
47
51
  rescue ::Huebot::Error => e
48
52
  opts.stderr.puts "#{e.class.name}: #{e.message}"
49
53
  return 1
54
+ rescue Interrupt
55
+ return 130
50
56
  end
51
57
 
52
58
  def self.get_state(lights, groups, opts)
@@ -56,6 +62,8 @@ module Huebot
56
62
  opts.stdout.puts " #{device.get_state}"
57
63
  end
58
64
  0
65
+ rescue Interrupt
66
+ return 130
59
67
  end
60
68
 
61
69
  def self.set_ip(config, ip, opts)
@@ -64,6 +72,8 @@ module Huebot
64
72
  rescue ::Huebot::Error => e
65
73
  opts.stderr.puts "#{e.class.name}: #{e.message}"
66
74
  return 1
75
+ rescue Interrupt
76
+ return 130
67
77
  end
68
78
 
69
79
  def self.clear_ip(config, opts)
@@ -72,6 +82,8 @@ module Huebot
72
82
  rescue ::Huebot::Error => e
73
83
  opts.stderr.puts "#{e.class.name}: #{e.message}"
74
84
  return 1
85
+ rescue Interrupt
86
+ return 130
75
87
  end
76
88
 
77
89
  def self.unregister(config, opts)
@@ -80,6 +92,8 @@ module Huebot
80
92
  rescue ::Huebot::Error => e
81
93
  opts.stderr.puts "#{e.class.name}: #{e.message}"
82
94
  return 1
95
+ rescue Interrupt
96
+ return 130
83
97
  end
84
98
  end
85
99
  end
@@ -10,6 +10,8 @@ module Huebot
10
10
  PARALLEL_KEYS = ["parallel"].freeze
11
11
  INFINITE_KEYS = ["infinite"].freeze
12
12
  COUNT_KEYS = ["count"].freeze
13
+ RANDOM_KEYS = ["random"].freeze
14
+ MIN_MAX = ["min", "max"].freeze
13
15
  TIMER_KEYS = ["timer"].freeze
14
16
  DEADLINE_KEYS = ["until"].freeze
15
17
  HHMM = /\A[0-9]{2}:[0-9]{2}\Z/.freeze
@@ -51,40 +53,56 @@ module Huebot
51
53
  end
52
54
 
53
55
  def build_transition(t, errors, warnings, inherited_devices = nil)
56
+ if t.nil?
57
+ errors << "'transition' may not be blank"
58
+ t = {}
59
+ end
60
+
54
61
  state = build_state(t, errors, warnings)
55
62
  devices = build_devices(t, errors, warnings, inherited_devices)
56
- slp = build_sleep(t, errors, warnings)
63
+ pause = build_pause(t, errors, warnings)
64
+ wait = @api_version >= 1.1 ? build_wait(t, errors, warnings) : true
57
65
 
58
66
  errors << "'transition' requires devices" if devices.empty?
59
67
  errors << "Unknown keys in 'transition': #{t.keys.join ", "}" if t.keys.any?
60
68
 
61
- instruction = Program::AST::Transition.new(state, devices, slp)
69
+ instruction = Program::AST::Transition.new(state, devices, wait, pause)
62
70
  return instruction, []
63
71
  end
64
72
 
65
73
  def build_serial(t, errors, warnings, inherited_devices = nil)
74
+ if t.nil?
75
+ errors << "'serial' may not be blank"
76
+ t = {}
77
+ end
78
+
66
79
  lp = build_loop(t, errors, warnings)
67
- slp = build_sleep(t, errors, warnings)
80
+ pause = build_pause(t, errors, warnings)
68
81
  devices = build_devices(t, errors, warnings, inherited_devices)
69
82
  children = build_steps(t, errors, warnings, devices)
70
83
 
71
84
  errors << "'serial' requires steps" if children.empty?
72
85
  errors << "Unknown keys in 'serial': #{t.keys.join ", "}" if t.keys.any?
73
86
 
74
- instruction = Program::AST::SerialControl.new(lp, slp)
87
+ instruction = Program::AST::SerialControl.new(lp, pause)
75
88
  return instruction, children
76
89
  end
77
90
 
78
91
  def build_parallel(t, errors, warnings, inherited_devices = nil)
92
+ if t.nil?
93
+ errors << "'parallel' may not be blank"
94
+ t = {}
95
+ end
96
+
79
97
  lp = build_loop(t, errors, warnings)
80
- slp = build_sleep(t, errors, warnings)
98
+ pause = build_pause(t, errors, warnings)
81
99
  devices = build_devices(t, errors, warnings, inherited_devices)
82
100
  children = build_steps(t, errors, warnings, devices)
83
101
 
84
102
  errors << "'parallel' requires steps" if children.empty?
85
103
  errors << "Unknown keys in 'parallel': #{t.keys.join ", "}" if t.keys.any?
86
104
 
87
- instruction = Program::AST::ParallelControl.new(lp, slp)
105
+ instruction = Program::AST::ParallelControl.new(lp, pause)
88
106
  return instruction, children
89
107
  end
90
108
 
@@ -167,32 +185,58 @@ module Huebot
167
185
  loop_val = t.delete "loop"
168
186
  case loop_val
169
187
  when Hash
170
- pause = loop_val.delete "pause"
171
- errors << "'loop.pause' must be an integer. Found '#{pause.class.name}'" if pause and !pause.is_a? Integer
172
-
188
+ pause = build_pause(loop_val, errors, warnings)
173
189
  lp =
174
190
  case loop_val.keys
175
191
  when INFINITE_KEYS
176
- loop_val["infinite"] == true ? Program::AST::InfiniteLoop.new : Program::AST::CountedLoop.new(1)
192
+ loop_val.fetch("infinite") == true ? Program::AST::InfiniteLoop.new : Program::AST::CountedLoop.new(Program::AST::Num.new(1))
177
193
  when COUNT_KEYS
178
- num = loop_val["count"]
194
+ num = loop_val.fetch("count")
179
195
  errors << "'loop.count' must be an integer. Found '#{num.class.name}'" unless num.is_a? Integer
180
- Program::AST::CountedLoop.new(num)
196
+ Program::AST::CountedLoop.new(Program::AST::Num.new(num))
197
+ when RANDOM_KEYS
198
+ n = build_random loop_val, errors, warnings
199
+ Program::AST::CountedLoop.new(n)
181
200
  when TIMER_KEYS
182
- build_timer_loop loop_val["timer"], errors, warnings
201
+ build_timer_loop loop_val.fetch("timer"), errors, warnings
183
202
  when DEADLINE_KEYS
184
- build_deadline_loop loop_val["until"], errors, warnings
203
+ build_deadline_loop loop_val.fetch("until"), errors, warnings
185
204
  else
186
205
  errors << "'loop' must contain exactly one of: 'infinite', 'count', 'timer', or 'until', and optionally 'pause'. Found: #{loop_val.keys.join ", "}"
187
- Program::AST::CountedLoop.new(1)
206
+ Program::AST::CountedLoop.new(Program::AST::Num.new(1))
188
207
  end
189
208
  lp.pause = pause
190
209
  lp
191
210
  when nil
192
- Program::AST::CountedLoop.new(1)
211
+ Program::AST::CountedLoop.new(Program::AST::Num.new(1))
193
212
  else
194
213
  errors << "'loop' must be an object. Found '#{loop_val.class.name}'"
195
- Program::AST::CountedLoop.new(1)
214
+ Program::AST::CountedLoop.new(Program::AST::Num.new(1))
215
+ end
216
+ end
217
+
218
+ def build_random(t, errors, warnings)
219
+ random = t.delete("random") || {}
220
+ min = build_random_n(random, "min", errors, warnings)
221
+ max = build_random_n(random, "max", errors, warnings)
222
+ errors << "'random.max' must be greater than 'random.min'" unless max > min
223
+ errors << "Unknown keys in 'random': #{random.keys.join ", "}" if random.keys.any?
224
+ Program::AST::RandomNum.new(min, max)
225
+ end
226
+
227
+ def build_random_n(t, name, errors, warnings)
228
+ n = t.delete name
229
+ case n
230
+ when Integer, Float
231
+ if n >= 0
232
+ n
233
+ else
234
+ errors << "'random.#{name}' must be >= 0, found #{n}"
235
+ 0
236
+ end
237
+ else
238
+ errors << "'random.#{name}' must be an integer or float > 0, found #{n.class.name}"
239
+ 0
196
240
  end
197
241
  end
198
242
 
@@ -216,11 +260,20 @@ module Huebot
216
260
  Program::AST::DeadlineLoop.new(stop_time)
217
261
  end
218
262
 
219
- def build_sleep(t, errors, warnings)
220
- sleep_val = t.delete "pause"
221
- case sleep_val
263
+ def build_pause(t, errors, warnings)
264
+ case @api_version
265
+ when 1.0 then build_pause_1_0(t, errors, warnings)
266
+ when 1.1 then build_pause_1_1(t, errors, warnings)
267
+ when 1.2 then build_pause_1_2(t, errors, warnings)
268
+ else raise Error, "Unknown api version '#{@api_version}'"
269
+ end
270
+ end
271
+
272
+ def build_pause_1_0(t, errors, warnings)
273
+ pause_val = t.delete "pause"
274
+ case pause_val
222
275
  when Integer, Float
223
- sleep_val
276
+ Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
224
277
  when nil
225
278
  nil
226
279
  else
@@ -229,6 +282,86 @@ module Huebot
229
282
  end
230
283
  end
231
284
 
285
+ def build_pause_1_1(t, errors, warnings)
286
+ pause_val = t.delete "pause"
287
+ case pause_val
288
+ when Integer, Float
289
+ Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
290
+ when Hash
291
+ pre = pause_val.delete "before"
292
+ post = pause_val.delete "after"
293
+ errors << "'pause.before' must be an integer or float" unless pre.nil? or pre.is_a? Integer or pre.is_a? Float
294
+ errors << "'pause.after' must be an integer or float" unless post.nil? or post.is_a? Integer or post.is_a? Float
295
+ errors << "Unknown keys in 'pause': #{pause_val.keys.join ", "}" if pause_val.keys.any?
296
+ pre = Program::AST::Num.new(pre) if pre
297
+ post = Program::AST::Num.new(post) if post
298
+ Program::AST::Pause.new(pre, post)
299
+ when nil
300
+ nil
301
+ else
302
+ errors << "'pause' must be an integer or float, or an object with 'before' and/or 'after'"
303
+ nil
304
+ end
305
+ end
306
+
307
+ def build_pause_1_2(t, errors, warnings)
308
+ pause_val = t.delete "pause"
309
+ case pause_val
310
+ when Integer, Float
311
+ Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
312
+ when Hash
313
+ pre = build_pause_part(pause_val, "before", errors, warnings)
314
+ post = build_pause_part(pause_val, "after", errors, warnings)
315
+ errors << "'pause' requires one or both of 'before' or 'after'" if pre.nil? and post.nil?
316
+ errors << "Unknown keys in 'pause': #{pause_val.keys.join ", "}" if pause_val.keys.any?
317
+ Program::AST::Pause.new(pre, post)
318
+ when nil
319
+ nil
320
+ else
321
+ errors << "'pause' must be an integer or float, or an object with 'before' and/or 'after'"
322
+ nil
323
+ end
324
+ end
325
+
326
+ def build_pause_part(t, part, errors, warnings)
327
+ val = t.delete part
328
+ case val
329
+ when nil
330
+ nil
331
+ when Integer, Float
332
+ Program::AST::Num.new(val)
333
+ when Hash
334
+ if @api_version < 1.2
335
+ errors << "Unknown 'pause.#{part}' type (#{val.class.name})"
336
+ return Program::AST::Num.new(1)
337
+ end
338
+
339
+ case val.keys
340
+ when RANDOM_KEYS
341
+ build_random val, errors, warnings
342
+ else
343
+ errors << "Expected 'pause.#{part}' to contain 'random', found #{val.keys.join ", "}"
344
+ Program::AST::Num.new(1)
345
+ end
346
+ else
347
+ errors << "Unknown 'pause.#{part}' type (#{val.class.name})"
348
+ Program::AST::Num.new(1)
349
+ end
350
+ end
351
+
352
+ def build_wait(t, errors, warnings)
353
+ wait = t.delete "wait"
354
+ case wait
355
+ when true, false
356
+ wait
357
+ when nil
358
+ true
359
+ else
360
+ errors << "'transition.wait' must be true or false"
361
+ true
362
+ end
363
+ end
364
+
232
365
  def build_devices(t, errors, warnings, inherited_devices = nil)
233
366
  devices_ref = t.delete("devices") || {}
234
367
  return inherited_devices if devices_ref.empty? and inherited_devices
@@ -13,7 +13,7 @@ module Huebot
13
13
  def self.build(src)
14
14
  compiler_class =
15
15
  case src.api_version
16
- when 1.0 then ApiV1
16
+ when 1.0, 1.1, 1.2 then ApiV1
17
17
  else raise Error, "Unknown API version '#{src.api_version}'"
18
18
  end
19
19
  compiler = compiler_class.new(src.api_version)
@@ -16,15 +16,18 @@ module Huebot
16
16
  module AST
17
17
  Node = Struct.new(:instruction, :children, :errors, :warnings)
18
18
 
19
- Transition = Struct.new(:state, :devices, :sleep)
20
- SerialControl = Struct.new(:loop, :sleep)
21
- ParallelControl = Struct.new(:loop, :sleep)
19
+ Transition = Struct.new(:state, :devices, :wait, :pause)
20
+ SerialControl = Struct.new(:loop, :pause)
21
+ ParallelControl = Struct.new(:loop, :pause)
22
22
 
23
23
  InfiniteLoop = Struct.new(:pause)
24
24
  CountedLoop = Struct.new(:n, :pause)
25
25
  TimerLoop = Struct.new(:hours, :minutes, :pause)
26
26
  DeadlineLoop = Struct.new(:stop_time, :pause)
27
27
 
28
+ Pause = Struct.new(:pre, :post)
29
+ Num = Struct.new(:n)
30
+ RandomNum = Struct.new(:min, :max)
28
31
  DeviceRef = Struct.new(:ref)
29
32
  Light = Struct.new(:name)
30
33
  Group = Struct.new(:name)
@@ -55,14 +58,14 @@ module Huebot
55
58
  end
56
59
 
57
60
  def errors(node = data)
58
- node.children.reduce(node.errors) { |errors, child|
59
- errors + child.errors
61
+ node.children.reduce(node.errors) { |acc, child|
62
+ acc + errors(child)
60
63
  }
61
64
  end
62
65
 
63
66
  def warnings(node = data)
64
- node.children.reduce(node.warnings) { |warnings, child|
65
- warnings + child.warnings
67
+ node.children.reduce(node.warnings) { |acc, child|
68
+ acc + warnings(child)
66
69
  }
67
70
  end
68
71
 
@@ -1,4 +1,4 @@
1
1
  module Huebot
2
2
  # Gem version
3
- VERSION = '1.2.0'
3
+ VERSION = '1.4.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: huebot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-21 00:00:00.000000000 Z
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest