continuance 0.0.1

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