continuance 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ecc48f905df3cf9c02090295747570b3bf66e71
4
+ data.tar.gz: 1f21afe9f4c2d9772bc075e8a6bfe37569aee0f9
5
+ SHA512:
6
+ metadata.gz: c0436131cd8662586b5fd6c320edd0618d032b6759946cce8a2439048c86509269461bd52666b805cc80feb7cb20cbbca8c717b7bb73615ea150618ef0433702
7
+ data.tar.gz: 244d4ab1500e50b139c0b55c50565f759c64b8c8807efdf4755a0b6c93c0bfa973846182f5aa04a9fef6e1f630740a6f3975916160a09f4ef2e10799d9a1320d
@@ -0,0 +1,71 @@
1
+ Continuance
2
+ ===========
3
+
4
+ Continuance is a library for handling duration as opposed to absolute time.
5
+ At present the minimum resolution for measuring duration in Continuance is in hours, with
6
+ the maximum resolution being a nanosecond.
7
+
8
+ Current Build Status:
9
+ [ ![Codeship Status for shirren/continuance](https://codeship.com/projects/c20de200-f6d0-0132-1815-2e788669a94e/status?branch=master)](https://codeship.com/projects/86171)
10
+
11
+ ## Installation
12
+
13
+ The continuance gem runs with all Ruby versions >= 1.9.3.
14
+
15
+ ```ruby
16
+ gem 'continuance'
17
+ ```
18
+
19
+ ## Example
20
+
21
+ To create a duration of 1 hour is simply.
22
+
23
+ ```ruby
24
+ Continuance::Duration.new(1, 0, 0, 0)
25
+ ```
26
+
27
+ or with a named argument.
28
+
29
+ ```ruby
30
+ Continuance::Duration.new(hours = 1)
31
+ ```
32
+
33
+ To create a duration of 1 hour and 5 minutes is simply.
34
+
35
+ ```ruby
36
+ Continuance::Duration.new(1, 5, 0, 0)
37
+ ```
38
+
39
+ Once again named arguments can be used if preferred. Now to create a more complex duration of 1 hour, 10 minutes, 23.005 seconds.
40
+
41
+ ```ruby
42
+ Continuance::Duration.new(1, 10, 23, 5_000_000)
43
+ ```
44
+
45
+ As we can see the 4th argument of the constructor takes the point second value as a measure nanoseconds. Continuance only supports unsigned whole numbers for each unit. If there is a need to support signed floating point numbers we will look into it.
46
+
47
+ ## Math Operations
48
+
49
+ Duration objects support the math operation `+` and `-`. So a duration object can be either be added to another or subtracted from another. The result of each of these operations is another duration object.
50
+
51
+ For example to add a duration of 1 hour 5 minutes to 6 minutes 32 seconds we can write the following code.
52
+
53
+ ```ruby
54
+ Continuance::Duration.new(1, 0, 5, 0) + Continuance::Duration.new(0, 0, 6, 32)
55
+ ```
56
+
57
+ ## Collection Operations
58
+
59
+ At present Continuance provides `total` and `average` aggregate operations on a collection of duration objects. The Ruby code below demonstrates how to use these collection operations.
60
+
61
+ ```ruby
62
+ duration1 = Continuance::Duration.new(1, 0, 5, 0)
63
+ duration2 = Continuance::Duration.new(0, 0, 6, 32)
64
+ durations = Continuance::Duration.new([duration1, duration2])
65
+ average = durations.average
66
+ total = durations.total
67
+ ```
68
+
69
+ The average method returns a Duration object, where as total returns the total number of seconds as a floating point value, this could change in the future. The order of complexity of both operations is `O(n)`.
70
+
71
+ Report bugs on GitHub.
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Continuance'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,3 @@
1
+ # Module Continuance
2
+ module Continuance
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'time'
2
+
3
+ # To calculate duration in Ruby we need to calculate time
4
+ # differences, but in Ruby when you are dealing with finer units
5
+ # like days, hours, minutes etc we need to make sure the base YY-MM-DD is
6
+ # set to 1970-01-01 to remove potential errors
7
+ class BaseDate
8
+ attr_reader :val
9
+
10
+ def initialize
11
+ @val = Time.new(1970, 1, 1)
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ module Continuance
5
+ module Core
6
+ # The configuration class abstracts access to the underlying YAML file
7
+ class YamlConfiguration
8
+ include Singleton
9
+
10
+ # If the configuration loader fails for any reason then just
11
+ # return an empty hash
12
+ def load(configuration_key)
13
+ config_file_path = File.join(Rails.root, "config/#{configuration_key}.yml")
14
+ YAML.load_file(config_file_path)[Rails.env]
15
+ rescue
16
+ {}
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,85 @@
1
+ require 'time'
2
+ require 'continuance/base_date'
3
+
4
+ # Continuance module
5
+ module Continuance
6
+ # Duration is a measure of how long something takes to do
7
+ # where our resolution is limited to hours, minutes, seconds
8
+ # and nano seconds which should be enough to measure anything
9
+ class Duration
10
+ include Comparable
11
+
12
+ attr_reader :hours, :minutes
13
+ attr_reader :seconds, :nano_seconds
14
+
15
+ # Default constructor which creates a base time marker in order
16
+ # to calculate proper times when a duration is specified as a string
17
+ def initialize(hours = 0, minutes = 0, seconds = 0, nano_seconds = 0)
18
+ @hours = hours
19
+ @minutes = minutes
20
+ @seconds = seconds
21
+ @nano_seconds = nano_seconds
22
+ end
23
+
24
+ # Should be able to calculate the difference between two durations objects
25
+ # as seconds
26
+ def -(other)
27
+ other_time = Time.strptime(other.to_s, '%H:%M:%S.%N', BaseDate.new.val)
28
+ self_time = Time.strptime(to_s, '%H:%M:%S.%N', BaseDate.new.val)
29
+ convert_to(self_time - other_time)
30
+ end
31
+
32
+ # Should be able to calculate the sum of two durations objects
33
+ # as seconds, this is returned as a float value
34
+ def +(other)
35
+ convert_to(to_f + other.to_f)
36
+ end
37
+
38
+ # A duration can be serialized to a string, this is used not only for visual
39
+ # purposes, but to also use in other computations like the difference operation
40
+ def to_s
41
+ sub_sec = @nano_seconds.to_f / (10**9)
42
+ sub_sec_str = sub_sec.to_s.gsub('0.', '')
43
+ "#{@hours}:#{@minutes}:#{@seconds}.#{sub_sec_str}"
44
+ end
45
+
46
+ # Converts a duration object to it's equivalent number of seconds, this value
47
+ # is returned as a float value
48
+ def to_f
49
+ # TODO: This calculation can result in floating point errors when the duration
50
+ # is converted to and from a float value
51
+ (@hours * 3600) + (@minutes * 60) + @seconds + (@nano_seconds.to_f / 10**9)
52
+ end
53
+
54
+ # Total equivalence implementation including the objects type
55
+ def eql?(other)
56
+ other.class == self.class && self == other
57
+ end
58
+
59
+ # Comparable implementation for a duration object
60
+ def <=>(other)
61
+ if to_f < other.to_f
62
+ -1
63
+ elsif to_f > other.to_f
64
+ 1
65
+ else
66
+ 0
67
+ end
68
+ end
69
+
70
+ # A duration can be created by specifying the time as a string and providing a valid
71
+ # format for the time. The supported formats are listed at
72
+ # http://ruby-doc.org/core-2.2.1/Time.html
73
+ def self.create(duration, format)
74
+ time_val = Time.strptime(duration, format, BaseDate.new.val)
75
+ Duration.new(time_val.hour, time_val.min, time_val.sec, time_val.nsec)
76
+ end
77
+
78
+ private
79
+
80
+ # Converts a time in seconds to a duration object
81
+ def convert_to(time_as_float)
82
+ Duration.create(time_as_float.to_s, '%S.%N')
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,39 @@
1
+ # Module Continuance
2
+ module Continuance
3
+ # Durations takes a collection of duration objects on which some standard
4
+ # collection operations can be performed such as average etc
5
+ class Durations
6
+ attr_reader :items
7
+
8
+ # Constructor can take a sequence of duration objects, objects outside
9
+ # of durations are ignored
10
+ def initialize(items = [])
11
+ @items = items
12
+ end
13
+
14
+ # Given a series of durations, this function calculates the average duration
15
+ # by converting each duration to an absolute float value
16
+ def average
17
+ time = Time.at(avg_as_f).utc.strftime('%H:%M:%S:%N')
18
+ Duration.create(time, '%H:%M:%S:%L')
19
+ end
20
+
21
+ # Provides a sum of all the durations
22
+ def total
23
+ items.inject(0.0) do |sum, duration|
24
+ sum += duration.to_f if duration.respond_to?(:to_f)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Calculates the average of the time as a float
31
+ def avg_as_f
32
+ if items.empty?
33
+ 0.0
34
+ else
35
+ total / items.size
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ # Major.Minor.Patch version numbering
2
+ module Continuance
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'continuance/base_date'
3
+
4
+ # Specs for bullet points
5
+ module Continuance
6
+ describe BaseDate do
7
+ let(:base_date) { BaseDate.new }
8
+
9
+ it 'should always be set to 1970-01-01' do
10
+ expect(base_date.val).to eq(Time.new(1970, 1, 1))
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'continuance/core/yaml_configuration'
3
+
4
+ # Specs for bullet points
5
+ module Continuance
6
+ # Specs for yaml configuration
7
+ module Core
8
+ describe YamlConfiguration do
9
+ it 'should be able to handle missing configuration files' do
10
+ YamlConfiguration.instance.load('dummy')
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'continuance/duration'
3
+
4
+ # Specs for bullet points
5
+ module Continuance
6
+ describe Duration do
7
+ it 'should be able to measure Duration to a nanosecond' do
8
+ duration = Duration.create('00:00.000000001', '%H:%M.%N')
9
+ expect(duration.nano_seconds).to eq(1)
10
+ end
11
+
12
+ it 'should be able to measure Duration to a microsecond' do
13
+ duration = Duration.create('00:00.0000001', '%H:%M.%N')
14
+ expect(duration.nano_seconds).to eq(100)
15
+ end
16
+
17
+ it 'should be able to measure Duration to a millisecond' do
18
+ duration = Duration.create('00:00.001', '%H:%M.%L')
19
+ expect(duration.nano_seconds).to eq(100_000_0)
20
+ end
21
+
22
+ it 'should be able to measure Duration to a nanosecond' do
23
+ duration = Duration.create('00:00.000000001', '%H:%M.%L')
24
+ expect(duration.nano_seconds).to eq(1)
25
+ end
26
+
27
+ it 'should be able to measure Duration to a second' do
28
+ duration = Duration.create('00:00.1', '%H:%M.%S')
29
+ expect(duration.seconds).to eq(1)
30
+ end
31
+
32
+ it 'should be able to measure Duration to a minute' do
33
+ duration = Duration.create('00:01:00', '%H:%M:%S')
34
+ expect(duration.minutes).to eq(1)
35
+ expect(duration.hours).to eq(0)
36
+ expect(duration.seconds).to eq(0)
37
+ end
38
+
39
+ it 'should be able to measure Duration to a hour' do
40
+ duration = Duration.create('01:00:00', '%H:%M:%S')
41
+ expect(duration.minutes).to eq(0)
42
+ expect(duration.hours).to eq(1)
43
+ expect(duration.seconds).to eq(0)
44
+ end
45
+
46
+ it 'should be subtractable from a lesser duration' do
47
+ duration1 = Duration.create('00:00:02', '%H:%M:%S')
48
+ duration2 = Duration.create('00:00:03', '%H:%M:%S')
49
+ expect(duration2 - duration1).to eq(Duration.new(0, 0, 1, 0))
50
+ end
51
+
52
+ it 'should be subtractable from a duration with a different resolution' do
53
+ duration1 = Duration.create('00:00:00.005', '%H:%M:%S.%N')
54
+ duration2 = Duration.create('00:00:01', '%H:%M:%S')
55
+ expect(duration2 - duration1).to eq(Duration.new(0, 0, 0, 995_000_000))
56
+ end
57
+
58
+ it 'should be subtractable from a duration with a vastly different resolution' do
59
+ duration1 = Duration.create('00:00:00.005', '%H:%M:%S.%N')
60
+ duration2 = Duration.create('00:01:00', '%H:%M:%S')
61
+ expect(duration2 - duration1).to eq(Duration.new(0, 0, 59, 995_000_000))
62
+ end
63
+
64
+ it 'should be addable to a lesser duration' do
65
+ duration1 = Duration.create('00:00:02', '%H:%M:%S')
66
+ duration2 = Duration.create('00:00:03', '%H:%M:%S')
67
+ expect(duration2 + duration1).to eq(Duration.new(0, 0, 5, 0))
68
+ end
69
+
70
+ it 'should be addable to a duration with a different resolution' do
71
+ duration1 = Duration.create('00:00:00.005', '%H:%M:%S.%N')
72
+ duration2 = Duration.create('00:00:01', '%H:%M:%S')
73
+ expect(duration2 + duration1).to eq(Duration.new(0, 0, 1, 5_000_000))
74
+ end
75
+
76
+ it 'should be addable to a duration with a vastly different resolution' do
77
+ duration1 = Duration.create('00:00:00.005', '%H:%M:%S.%N')
78
+ duration2 = Duration.create('00:01:00', '%H:%M:%S')
79
+ expect(duration2 + duration1).to eq(Duration.new(0, 1, 0, 5_000_000))
80
+ end
81
+
82
+ context 'with a lesser value' do
83
+ let(:duration1) { Duration.new(0, 0, 0, 100) }
84
+ let(:duration2) { Duration.new(0, 0, 0, 101) }
85
+
86
+ it 'should report as so when compared' do
87
+ expect(duration1).to be < duration2
88
+ end
89
+ end
90
+
91
+ context 'with an equal value' do
92
+ let(:duration1) { Duration.new(0, 0, 0, 100) }
93
+ let(:duration2) { Duration.new(0, 0, 0, 100) }
94
+
95
+ it 'should report as so when compared' do
96
+ expect(duration1).to eql(duration2)
97
+ end
98
+ end
99
+
100
+ context 'with a greater value' do
101
+ let(:duration1) { Duration.new(0, 0, 0, 101) }
102
+ let(:duration2) { Duration.new(0, 0, 0, 100) }
103
+
104
+ it 'should report as so when compared' do
105
+ expect(duration1).to be > duration2
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'continuance/duration'
3
+ require 'continuance/durations'
4
+
5
+ # Specs for Continuance durations
6
+ module Continuance
7
+ describe Durations do
8
+ context 'with an empty collection' do
9
+ let(:durations) { Durations.new }
10
+
11
+ it 'should be able to calculate the total' do
12
+ expect(durations.total).to eq(0.0)
13
+ end
14
+
15
+ it 'should be able to calculate an average' do
16
+ expect(durations.average).to eq(Duration.new)
17
+ end
18
+ end
19
+
20
+ context 'with a single duration' do
21
+ let(:duration) { Duration.new(1, 1, 1, 0) }
22
+ let(:durations) { Durations.new([duration]) }
23
+
24
+ it 'should be able to calculate the total' do
25
+ expect(durations.total).to eq(3661.0)
26
+ end
27
+
28
+ it 'should be able to calculate an average' do
29
+ expect(durations.average).to eq(Duration.new(1, 1, 1, 0))
30
+ end
31
+ end
32
+
33
+ context 'with multiple durations' do
34
+ let(:duration1) { Duration.new(1, 0, 0, 0) }
35
+ let(:duration2) { Duration.new(0, 1, 0, 0) }
36
+ let(:duration3) { Duration.new(0, 0, 1, 0) }
37
+ let(:duration4) { Duration.new(0, 0, 0, 1_000_000) }
38
+ let(:durations) { Durations.new([duration1, duration2, duration3, duration4]) }
39
+
40
+ it 'should be able to calculate the total' do
41
+ expect(durations.total).to eq(3661.001)
42
+ end
43
+
44
+ it 'should be able to calculate an average' do
45
+ expect(durations.average).to eq(Duration.new(0, 15, 15, 250_250_000))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,83 @@
1
+ require 'byebug'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
6
+ # file to always be loaded, without a need to explicitly require it in any files.
7
+ #
8
+ # Given that it is always loaded, you are encouraged to keep this file as
9
+ # light-weight as possible. Requiring heavyweight dependencies from this file
10
+ # will add to the boot time of your test suite on EVERY test run, even for an
11
+ # individual file that may not need all of that loaded. Instead, make a
12
+ # separate helper file that requires this one and then use it only in the specs
13
+ # that actually need it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ RSpec.configure do |config|
20
+ # The settings below are suggested to provide a good initial experience
21
+ # with RSpec, but feel free to customize to your heart's content.
22
+ # These two settings work together to allow you to limit a spec run
23
+ # to individual examples or groups you care about by tagging them with
24
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
25
+ # get run.
26
+ # config.filter_run :focus
27
+ # config.run_all_when_everything_filtered = true
28
+
29
+ # Many RSpec users commonly either run the entire suite or an individual
30
+ # file, and it's useful to allow more verbose output when running an
31
+ # individual spec file.
32
+ if config.files_to_run.one?
33
+ # RSpec filters the backtrace by default so as not to be so noisy.
34
+ # This causes the full backtrace to be printed when running a single
35
+ # spec file (e.g. to troubleshoot a particular spec failure).
36
+ config.full_backtrace = true
37
+
38
+ # Use the documentation formatter for detailed output,
39
+ # unless a formatter has already been configured
40
+ # (e.g. via a command-line flag).
41
+ config.default_formatter = 'doc'
42
+ end
43
+
44
+ # Print the 10 slowest examples and example groups at the
45
+ # end of the spec run, to help surface which specs are running
46
+ # particularly slow.
47
+ config.profile_examples = 10
48
+
49
+ # Run specs in random order to surface order dependencies. If you find an
50
+ # order dependency and want to debug it, you can fix the order by providing
51
+ # the seed, which is printed after each run.
52
+ # --seed 1234
53
+ config.order = :random
54
+
55
+ # Seed global randomization in this process using the `--seed` CLI option.
56
+ # Setting this allows you to use `--seed` to deterministically reproduce
57
+ # test failures related to randomization by passing the same `--seed` value
58
+ # as the one that triggered the failure.
59
+ # Kernel.srand config.seed
60
+
61
+ # rspec-expectations config goes here. You can use an alternate
62
+ # assertion/expectation library such as wrong or the stdlib/minitest
63
+ # assertions if you prefer.
64
+ config.expect_with :rspec do |expectations|
65
+ # Enable only the newer, non-monkey-patching expect syntax.
66
+ # For more details, see:
67
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
68
+ expectations.syntax = :expect
69
+ end
70
+
71
+ # rspec-mocks config goes here. You can use an alternate test double
72
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
73
+ config.mock_with :rspec do |mocks|
74
+ # Enable only the newer, non-monkey-patching expect syntax.
75
+ # For more details, see:
76
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
77
+ mocks.syntax = :expect
78
+
79
+ # Prevents you from mocking or stubbing a method that does not exist on
80
+ # a real object. This is generally recommended.
81
+ mocks.verify_partial_doubles = true
82
+ end
83
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: continuance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Shirren Premaratne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Continuance is a library for handling durations as opposed to time. A
14
+ duration represents a finite period of time meaurable in hours, minutes, seconds
15
+ and nanoseconds.
16
+ email:
17
+ - shirren.premaratne@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - README.md
23
+ - Rakefile
24
+ - lib/continuance.rb
25
+ - lib/continuance/base_date.rb
26
+ - lib/continuance/core/yaml_configuration.rb
27
+ - lib/continuance/duration.rb
28
+ - lib/continuance/durations.rb
29
+ - lib/continuance/version.rb
30
+ - spec/continuance/base_date_spec.rb
31
+ - spec/continuance/core/yaml_configuration_spec.rb
32
+ - spec/continuance/duration_spec.rb
33
+ - spec/continuance/durations_spec.rb
34
+ - spec/spec_helper.rb
35
+ homepage: https://github.com/shirren/continuance
36
+ licenses: []
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.4.8
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Continuance is a library for handling durations as opposed to time.
58
+ test_files:
59
+ - spec/continuance/base_date_spec.rb
60
+ - spec/continuance/core/yaml_configuration_spec.rb
61
+ - spec/continuance/duration_spec.rb
62
+ - spec/continuance/durations_spec.rb
63
+ - spec/spec_helper.rb