trick_bag 0.59 → 0.62.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
  SHA1:
3
- metadata.gz: 80de6b95f6465d84976cfff65217064adab423c6
4
- data.tar.gz: 6a91c5d2a35c666f85de817f64d78587978ace2a
3
+ metadata.gz: e7febbc99a5bd373e0964dd23b7cc4b4dd8482b7
4
+ data.tar.gz: 8c0ea6ac86a0dff90774250edc1143b0af5a6d19
5
5
  SHA512:
6
- metadata.gz: df097a1226f5ba6a5bdcaaf2200050fd8450e7a3cafdcd0827b6766ac15a8bfc5ef4f724b525c6d191891e0a272818348cabbee4a3ae27ed05e018e1f44eb493
7
- data.tar.gz: 6f25b512ee5485ee8a7e78c089764420f29519d4abcb6a1fd2fa6faa1c3cde512f7018b4f3cd26589a3e75da057a65be5a04bb2d5457e75e5bf50a065ed8bd10
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 key_string the string representing the keys to use
24
- # @separator the string to use to separate the
25
- def access(collection, key_string, separator = '.')
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
- is_number_string = ->(s) do
27
+ to_integer = ->(object) do
28
28
  begin
29
- Integer(s)
30
- true
29
+ Integer(object)
31
30
  rescue
32
- false
31
+ raise "Key is not a number string: #{object}"
33
32
  end
34
33
  end
35
34
 
36
- keys = key_string.split(separator)
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 [#{key_string}]: #{e}"
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
- ->(key_string) { access(collection, key_string, separator) }
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
@@ -1,3 +1,3 @@
1
1
  module TrickBag
2
- VERSION = "0.59"
2
+ VERSION = '0.62.0'
3
3
  end
@@ -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' => ['a', 'b'] }
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: '0.59'
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-03-23 00:00:00.000000000 Z
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.2
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: