trick_bag 0.59 → 0.62.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 +4 -4
- data/RELEASE_NOTES.md +15 -0
- data/lib/trick_bag/collections/collection_access.rb +25 -18
- data/lib/trick_bag/timing/elapser.rb +67 -0
- data/lib/trick_bag/timing/timing.rb +26 -0
- data/lib/trick_bag/version.rb +1 -1
- data/spec/trick_bag/collections/collection_access_spec.rb +15 -6
- data/spec/trick_bag/timing/elapser_spec.rb +95 -0
- data/spec/trick_bag/timing/timing_spec.rb +10 -0
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7febbc99a5bd373e0964dd23b7cc4b4dd8482b7
|
|
4
|
+
data.tar.gz: 8c0ea6ac86a0dff90774250edc1143b0af5a6d19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d86943b5df0ab4289411f2c641cd9670d44c907502ce68432e2e79987a81989930678ad59423127897777352d243b4cebef19fe979c16897b59f6f608144583e
|
|
7
|
+
data.tar.gz: 7aa3678b4694537560c327bab79f8f8d4279c97bae890cbeb42b23231ffda852e5ce3e339c516223fcae63ea7e5280edc73ff7b112f0b9edbe5c49e7da908a03
|
data/RELEASE_NOTES.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## v0.62.0
|
|
2
|
+
|
|
3
|
+
* Added Timing.try_with_timeout.
|
|
4
|
+
|
|
5
|
+
## v0.61.0
|
|
6
|
+
|
|
7
|
+
* Added Timing::Elapser.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## v0.60
|
|
11
|
+
|
|
12
|
+
* CollectionAccess methods now support an array of keys/subscripts instead of
|
|
13
|
+
a string.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
## v0.59
|
|
2
17
|
|
|
3
18
|
* Timing.retry_until_true_or_timeout now optionally takes a code block instead of a lambda.
|
|
@@ -19,37 +19,41 @@ module CollectionAccess
|
|
|
19
19
|
#
|
|
20
20
|
# Error occurred processing key [x.1] in [x.1.2]: undefined method `[]' for nil:NilClass
|
|
21
21
|
#
|
|
22
|
-
# @param the collection to access
|
|
23
|
-
# @param
|
|
24
|
-
# @separator the string to use to separate the
|
|
25
|
-
def access(collection,
|
|
22
|
+
# @param collection the collection to access
|
|
23
|
+
# @param key_string_or_array the string or array representing the keys to use
|
|
24
|
+
# @param separator the string to use to separate the keys, defaults to '.'
|
|
25
|
+
def access(collection, key_string_or_array, separator = '.')
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
to_integer = ->(object) do
|
|
28
28
|
begin
|
|
29
|
-
Integer(
|
|
30
|
-
true
|
|
29
|
+
Integer(object)
|
|
31
30
|
rescue
|
|
32
|
-
|
|
31
|
+
raise "Key is not a number string: #{object}"
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
get_key_array = -> do
|
|
36
|
+
case key_string_or_array
|
|
37
|
+
when Array
|
|
38
|
+
key_string_or_array
|
|
39
|
+
when String
|
|
40
|
+
key_string_or_array.split(separator)
|
|
41
|
+
else
|
|
42
|
+
raise "Invalid data type: #{key_string_or_array.class}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
keys = get_key_array.()
|
|
37
47
|
return_object = collection
|
|
38
48
|
|
|
39
49
|
keys.each_with_index do |key, index|
|
|
40
|
-
|
|
41
|
-
if return_object.kind_of?(Array)
|
|
42
|
-
unless is_number_string.(key)
|
|
43
|
-
raise "Key is not a number string: #{key}"
|
|
44
|
-
end
|
|
45
|
-
key = key.to_i
|
|
46
|
-
end
|
|
50
|
+
key = to_integer.(key) if return_object.kind_of?(Array)
|
|
47
51
|
|
|
48
52
|
begin
|
|
49
53
|
return_object = return_object[key]
|
|
50
54
|
rescue => e
|
|
51
55
|
this_key = keys[0..index].join(separator)
|
|
52
|
-
raise "Error occurred processing key [#{this_key}] in [#{
|
|
56
|
+
raise "Error occurred processing key [#{this_key}] in [#{key_string_or_array}]: #{e}"
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
return_object
|
|
@@ -69,7 +73,10 @@ module CollectionAccess
|
|
|
69
73
|
# or
|
|
70
74
|
# accessor.('h.1') # => 'b'
|
|
71
75
|
def accessor(collection, separator = '.')
|
|
72
|
-
->(
|
|
76
|
+
->(*args) do
|
|
77
|
+
key_string = (args.size == 1) ? args.first : args.map(&:to_s).join(separator)
|
|
78
|
+
access(collection, key_string, separator)
|
|
79
|
+
end
|
|
73
80
|
end
|
|
74
81
|
|
|
75
82
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module TrickBag
|
|
2
|
+
module Timing
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Very simple class that enables you to specify an elapsed time in
|
|
6
|
+
# either seconds or by the time itself.
|
|
7
|
+
class Elapser
|
|
8
|
+
|
|
9
|
+
attr_reader :seconds, :end_time
|
|
10
|
+
attr_accessor :never_elapse
|
|
11
|
+
|
|
12
|
+
def self.never_elapser
|
|
13
|
+
@never_elapser ||= begin
|
|
14
|
+
instance = new(0)
|
|
15
|
+
instance.never_elapse = true
|
|
16
|
+
instance
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Can be used to create an instance or return the passed instance (see test for example).
|
|
22
|
+
def self.from(object)
|
|
23
|
+
case object
|
|
24
|
+
when :never
|
|
25
|
+
never_elapser
|
|
26
|
+
when self
|
|
27
|
+
object
|
|
28
|
+
else
|
|
29
|
+
new(object)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Create the instance with the passed parameter. If it's a Time instance,
|
|
35
|
+
# it is assumed to be the end time at which elapsed? should return true.
|
|
36
|
+
# If it's a number, it's assumed to be a number of seconds after which
|
|
37
|
+
# elapsed? should return true.
|
|
38
|
+
def initialize(seconds_or_end_time)
|
|
39
|
+
case seconds_or_end_time
|
|
40
|
+
when Time
|
|
41
|
+
@end_time = seconds_or_end_time
|
|
42
|
+
@seconds = @end_time - Time.now
|
|
43
|
+
when ::Numeric
|
|
44
|
+
@seconds = seconds_or_end_time
|
|
45
|
+
@end_time = Time.now + @seconds
|
|
46
|
+
else
|
|
47
|
+
raise ArgumentError.new("Invalid parameter class: #{seconds_or_end_time.class}, object: #{seconds_or_end_time}")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def elapsed?
|
|
52
|
+
never_elapse ? false : Time.now >= @end_time
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def hash
|
|
57
|
+
Integer(@seconds - @end_time.to_i)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def ==(other)
|
|
62
|
+
other.class == self.class && other.seconds == self.seconds && other.end_time == self.end_time
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -90,5 +90,31 @@ module Timing
|
|
|
90
90
|
return_value
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
|
|
94
|
+
# Runs the passed block in a new thread, ensuring that its execution time
|
|
95
|
+
# does not exceed the specified duration.
|
|
96
|
+
#
|
|
97
|
+
# @param max_seconds maximum number of seconds to wait for completion
|
|
98
|
+
# @param check_interval_in_secs interval in seconds at which to check for completion
|
|
99
|
+
# @block block of code to execute in the secondary thread
|
|
100
|
+
#
|
|
101
|
+
# @return [true, block return value] if the block completes before timeout
|
|
102
|
+
# or [false, nil] if the block is still active (i.e. the thread is still alive)
|
|
103
|
+
# when max_seconds is reached
|
|
104
|
+
def try_with_timeout(max_seconds, check_interval_in_secs, &block)
|
|
105
|
+
raise "Must pass block to this method" unless block_given?
|
|
106
|
+
end_time = Time.now + max_seconds
|
|
107
|
+
block_return_value = nil
|
|
108
|
+
thread = Thread.new { block_return_value = block.call }
|
|
109
|
+
while Time.now < end_time
|
|
110
|
+
unless thread.alive?
|
|
111
|
+
return [true, block_return_value]
|
|
112
|
+
end
|
|
113
|
+
sleep(check_interval_in_secs)
|
|
114
|
+
end
|
|
115
|
+
# thread.kill
|
|
116
|
+
[false, nil]
|
|
117
|
+
end
|
|
118
|
+
|
|
93
119
|
end
|
|
94
120
|
end
|
data/lib/trick_bag/version.rb
CHANGED
|
@@ -19,12 +19,14 @@ describe CollectionAccess do
|
|
|
19
19
|
h = { 'a' => { 'b' => 234 }}
|
|
20
20
|
# instead of h['a']['b']:
|
|
21
21
|
expect(CollectionAccess.access(h, 'a.b')).to eq(234)
|
|
22
|
+
expect(CollectionAccess.access(h, %w(a b))).to eq(234)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
it 'works with 3 keys' do
|
|
25
26
|
h = { 'a' => { 'bb' => { 'ccc' => 345 }}}
|
|
26
27
|
# instead of h['a']['bb']['ccc']:
|
|
27
28
|
expect(CollectionAccess.access(h, 'a.bb.ccc')).to eq(345)
|
|
29
|
+
expect(CollectionAccess.access(h, %w(a bb ccc))).to eq(345)
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
it 'works with spaces as separators' do
|
|
@@ -41,21 +43,26 @@ describe CollectionAccess do
|
|
|
41
43
|
it 'works with numeric array subscripts 1 deep' do
|
|
42
44
|
a = ['a', 'b']
|
|
43
45
|
expect(CollectionAccess.access(a, '1')).to eq('b')
|
|
46
|
+
expect(CollectionAccess.access(a, [1])).to eq('b')
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
it 'works with a hash containing an array' do
|
|
47
50
|
h = { 'h' => ['a', 'b'] }
|
|
48
51
|
expect(CollectionAccess.access(h, 'h.1')).to eq('b')
|
|
52
|
+
expect(CollectionAccess.access(h, ['h', 1])).to eq('b')
|
|
53
|
+
expect(CollectionAccess.access(h, ['h', '1'])).to eq('b')
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
it 'raises an error when accessing an invalid key' do
|
|
52
57
|
h = { 'h' => ['a', 'b'] }
|
|
53
58
|
expect(-> { CollectionAccess.access(h, 'x.1.2') }).to raise_error
|
|
59
|
+
expect(-> { CollectionAccess.access(h, [x, 1, 2]) }).to raise_error
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
it 'raises an error when accessing a string that should be a number' do
|
|
57
63
|
h = { 'x' => ['a', 'b'] }
|
|
58
64
|
expect(-> { CollectionAccess.access(h, 'x.x') }).to raise_error
|
|
65
|
+
expect(-> { CollectionAccess.access(h, [x, x]) }).to raise_error
|
|
59
66
|
end
|
|
60
67
|
end
|
|
61
68
|
|
|
@@ -63,16 +70,18 @@ describe CollectionAccess do
|
|
|
63
70
|
context '#accessor' do
|
|
64
71
|
|
|
65
72
|
it 'works with a hash containing an array' do
|
|
66
|
-
h = { 'h' =>
|
|
73
|
+
h = { 'h' => %w(a b) }
|
|
67
74
|
accessor = CollectionAccess.accessor(h)
|
|
68
75
|
expect(accessor['h.1']).to eq('b')
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
end
|
|
72
76
|
|
|
77
|
+
# Test both [] and .() notations, with 1 as number and string:
|
|
78
|
+
expect(accessor[['h', 1]]).to eq('b')
|
|
79
|
+
expect(accessor.('h', 1)).to eq('b')
|
|
73
80
|
|
|
81
|
+
expect(accessor[['h', '1']]).to eq('b')
|
|
82
|
+
expect(accessor.(['h', '1'])).to eq('b')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
74
85
|
end
|
|
75
|
-
|
|
76
|
-
|
|
77
86
|
end
|
|
78
87
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'trick_bag/timing/elapser'
|
|
4
|
+
|
|
5
|
+
module TrickBag
|
|
6
|
+
module Timing
|
|
7
|
+
|
|
8
|
+
describe Elapser do
|
|
9
|
+
|
|
10
|
+
def test_number(number)
|
|
11
|
+
e = Elapser.new(number)
|
|
12
|
+
expect(e.class).to eq(Elapser)
|
|
13
|
+
expect(e.seconds).to eq(number)
|
|
14
|
+
expect(e.end_time - Time.now).to be_within(2.0).of(number)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
context 'instantiation' do
|
|
19
|
+
|
|
20
|
+
it 'can take an integer' do
|
|
21
|
+
test_number(30)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'can take a float' do
|
|
25
|
+
test_number(30.0)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'can take an end time' do
|
|
29
|
+
now = Time.now
|
|
30
|
+
e = Elapser.new(now + 30)
|
|
31
|
+
expect(e.class).to eq(Elapser)
|
|
32
|
+
expect(e.seconds).to be_within(0.1).of(30)
|
|
33
|
+
expect(e.end_time - now).to be_within(0.1).of(30)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify 'an invalid parameter type raises an error' do
|
|
37
|
+
expect { Elapser.new(nil) }.to raise_error(ArgumentError)
|
|
38
|
+
expect { Elapser.new('30') }.to raise_error(ArgumentError)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
context '#elapsed?' do
|
|
44
|
+
|
|
45
|
+
specify 'elapsed? returns true if seconds is negative' do
|
|
46
|
+
expect(Elapser.new(-1).elapsed?).to eq(true)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
specify 'elapsed? returns false if seconds is positive' do
|
|
50
|
+
expect(Elapser.new(100).elapsed?).to eq(false)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
context '.from' do
|
|
56
|
+
specify 'calling with an Elapsed instance returns that instance' do
|
|
57
|
+
e = Elapser.new(30)
|
|
58
|
+
expect(Elapser.from(e)).to equal(e)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
specify 'calling with a number or time works correctly' do
|
|
62
|
+
now = Time.now
|
|
63
|
+
secs = 45
|
|
64
|
+
|
|
65
|
+
expect(Elapser.from(secs).seconds - (Elapser.new(secs).seconds)).to be_within(1).of(0)
|
|
66
|
+
expect(Elapser.from(secs).end_time - (Elapser.new(secs).end_time)).to be_within(1).of(0)
|
|
67
|
+
|
|
68
|
+
expect(Elapser.from(now).seconds - (Elapser.new(now).seconds)).to be_within(1).of(0)
|
|
69
|
+
expect(Elapser.from(now).end_time - (Elapser.new(now).end_time)).to be_within(1).of(0)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
context '.never' do
|
|
77
|
+
|
|
78
|
+
specify 'never_elapser is intialized correctly' do
|
|
79
|
+
expect(Elapser.never_elapser.elapsed?).to eq(false)
|
|
80
|
+
expect(Elapser.never_elapser.never_elapse).to eq(true)
|
|
81
|
+
|
|
82
|
+
expect(Elapser.from(:never).elapsed?).to eq(false)
|
|
83
|
+
expect(Elapser.from(:never).never_elapse).to eq(true)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context '#hash' do
|
|
88
|
+
specify '#hash returns an Integer' do
|
|
89
|
+
expect(Elapser.new(3).hash).to be_a(Integer)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
@@ -68,4 +68,14 @@ module TrickBag
|
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
|
|
72
|
+
context '.try_with_timeout' do
|
|
73
|
+
it 'should return true and the block return value when timeout does not occur' do
|
|
74
|
+
expect(Timing.try_with_timeout(0.2, 0.01) { 7 }).to eq([true, 7])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should return false and nil when timeout occurs' do
|
|
78
|
+
expect(Timing.try_with_timeout(0.2, 0.01) { sleep 100; 7 }).to eq([false, nil])
|
|
79
|
+
end
|
|
80
|
+
end
|
|
71
81
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trick_bag
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 0.62.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keith Bennett
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-06-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: os
|
|
@@ -146,6 +146,7 @@ files:
|
|
|
146
146
|
- lib/trick_bag/numeric/totals.rb
|
|
147
147
|
- lib/trick_bag/operators/operators.rb
|
|
148
148
|
- lib/trick_bag/system.rb
|
|
149
|
+
- lib/trick_bag/timing/elapser.rb
|
|
149
150
|
- lib/trick_bag/timing/timing.rb
|
|
150
151
|
- lib/trick_bag/validations/gem_dependency_script.rb
|
|
151
152
|
- lib/trick_bag/validations/hash_validations.rb
|
|
@@ -176,6 +177,7 @@ files:
|
|
|
176
177
|
- spec/trick_bag/numeric/totals_spec.rb
|
|
177
178
|
- spec/trick_bag/operators/operators_spec.rb
|
|
178
179
|
- spec/trick_bag/system_spec.rb
|
|
180
|
+
- spec/trick_bag/timing/elapser_spec.rb
|
|
179
181
|
- spec/trick_bag/timing/timing_spec.rb
|
|
180
182
|
- spec/trick_bag/validations/hash_validations_spec.rb
|
|
181
183
|
- spec/trick_bag/validations/object_validations_spec.rb
|
|
@@ -202,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
202
204
|
version: '0'
|
|
203
205
|
requirements: []
|
|
204
206
|
rubyforge_project:
|
|
205
|
-
rubygems_version: 2.4.
|
|
207
|
+
rubygems_version: 2.4.6
|
|
206
208
|
signing_key:
|
|
207
209
|
specification_version: 4
|
|
208
210
|
summary: Miscellaneous general useful tools.
|
|
@@ -230,9 +232,9 @@ test_files:
|
|
|
230
232
|
- spec/trick_bag/numeric/totals_spec.rb
|
|
231
233
|
- spec/trick_bag/operators/operators_spec.rb
|
|
232
234
|
- spec/trick_bag/system_spec.rb
|
|
235
|
+
- spec/trick_bag/timing/elapser_spec.rb
|
|
233
236
|
- spec/trick_bag/timing/timing_spec.rb
|
|
234
237
|
- spec/trick_bag/validations/hash_validations_spec.rb
|
|
235
238
|
- spec/trick_bag/validations/object_validations_spec.rb
|
|
236
239
|
- spec/trick_bag/validations/other_validations_spec.rb
|
|
237
240
|
- spec/trick_bag/validations/regex_validations_spec.rb
|
|
238
|
-
has_rdoc:
|