huebot 1.2.0 → 1.4.0

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