as-duration 0.0.2

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: efd76209348cb343f2ef4b2d5039cb1dd033640c
4
+ data.tar.gz: 3110aaa5b289824b2d18291a2acf675c1b8cffa0
5
+ SHA512:
6
+ metadata.gz: bc6cf946e1a327453b91a30b3282a0478085039b909a538bb0bd9aecfcda44e9aa2a5e8cf06121668fc3867f3a9bd418c40a021d22d65938acfd8899ef764f82
7
+ data.tar.gz: 6f38f2966c070df139baca861f030c62d1456331a5cd5538190fbf63e1fe43c91c0bc421f261a490be168ec81f40e41ec1803ede3c9dcd6e66ec527746ad68ea
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Janko Marohnić
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,145 @@
1
+ # AS::Duration
2
+
3
+ This gem is an extraction of `ActiveSupport::Duration` from Rails, along with the
4
+ related core extensions.
5
+
6
+ Ruby 2.0 or greater is required.
7
+
8
+ ## Why not simply use ActiveSupport?
9
+
10
+ If you're in a Rails project, then you should use `ActiveSupport::Duration`.
11
+ Otherwise there are several reason why you might prefer `as-duration`:
12
+
13
+ * You simply don't want to have ActiveSupport as a dependency
14
+ * You want to control what you require. You may think that requiring
15
+ `active_support/core_ext/integer/time` will only require what you want, but
16
+ in fact it will require a total of **5000 LOC** (a lot of those are additional
17
+ core extensions which you may not have wanted). `as-duration` has only
18
+ under **200 LOC**, and only gives you what you've asked for.
19
+
20
+ ## Is it well tested?
21
+
22
+ It sure is! I copied all the related tests from Rails, and modified them
23
+ so that they work standalone. So, `as-duration` passes all of Rails' tests.
24
+
25
+ ## Installation
26
+
27
+ ```ruby
28
+ gem 'as-duration'
29
+ ```
30
+
31
+ ## Features
32
+
33
+ *DISCLAIMER: In most cases `as-duration` should work exactly like
34
+ `ActiveSupport::Duration`. However, there are a few modifications made, mostly
35
+ removing some of the magic, see [Modifications](#modifications-to-activesupportduration).*
36
+
37
+ ### Numeric methods
38
+
39
+ The following methods are added on `Numeric` (`Float` and `Integer`):
40
+
41
+ ```rb
42
+ # plural versions
43
+ 2.seconds
44
+ 3.minutes
45
+ 4.hours
46
+ 5.days
47
+ 6.weeks
48
+ 7.fortnights
49
+ 8.months
50
+ 9.years
51
+
52
+ # singular versions
53
+ 1.second
54
+ 1.minute
55
+ 1.hour
56
+ 1.day
57
+ 1.week
58
+ 1.fortnight
59
+ 1.month
60
+ 1.year
61
+ ```
62
+
63
+ The only exception is `#months` and `#years` which are only added to `Integer`
64
+ (to maintain precision in calculations).
65
+
66
+ ### Duration/Time arithmetics
67
+
68
+ You can add and subtract durations from `Time` or `Date` objects.
69
+
70
+ ```rb
71
+ Time.now + 2.hours
72
+ Date.today + 1.year
73
+ ```
74
+
75
+ When you add seconds/minutes/hours to a Date, the Date is automatically
76
+ converted to a `Time` object.
77
+
78
+ ```rb
79
+ (Date.today + 1.minute).class #=> Time
80
+ ```
81
+
82
+ As syntax sugar, you can also call time methods on the duration object:
83
+
84
+ ```rb
85
+ # forward in time
86
+ 1.year.from_now
87
+ 2.months.since(Date.new(2015,4,27))
88
+ 2.months.after(Date.new(2015,4,27))
89
+ 2.months.from(Date.new(2015,4,27))
90
+
91
+ # back in time
92
+ 2.hours.ago
93
+ 20.minutes.until(Time.now)
94
+ 20.minutes.before(Time.now)
95
+ 20.minutes.to(Time.now)
96
+ ```
97
+
98
+ ### Duration/Duration arithmetics
99
+
100
+ You can add and subtract durations:
101
+
102
+ ```rb
103
+ 1.week + 1.day
104
+ 2.minutes - 1.second
105
+ ```
106
+
107
+ Unlike `ActiveSupport::Duration`, you can't add durations to integers and vice
108
+ versa. You either have to convert the integer to a duration, or
109
+ the duration to an integer with `AS::Duration#to_i`. This is to help you
110
+ not to mix different time units.
111
+
112
+ ```rb
113
+ # Bad
114
+ 10 + 1.minute # TypeError
115
+ 1.minute + 10 # TypeError
116
+
117
+ # Good
118
+ 10.seconds + 1.minute # AS::Duration
119
+ 1.minute.to_i + 10 # Integer
120
+ ```
121
+
122
+ ## Modifications to `ActiveSupport::Duration`
123
+
124
+ The behaviour of `ActiveSupport::Duration` has been slightly modified, mostly
125
+ to remove some magic:
126
+
127
+ * Added `#from`, `#after`, `#before` and `#to` to `AS::Duration`
128
+ * `#from_now` and `#ago` cannot take any arguments, they always use the current
129
+ time (passing an argument doesn't read well, better to use `#from` and
130
+ `#until`)
131
+ * Removed support for `DateTime`
132
+ - `DateTime` was first introduced in Ruby so that you can represent time
133
+ that the `Time` class at the moment wasn't able to. However, the `Time`
134
+ class improved over time and removed those limitations, so there is no more
135
+ need to use `DateTime`
136
+ * Year lasts 365 days instead of 365.25
137
+ * `AS::Duration` doesn't act like an Integer
138
+ - to compare it with an integer you have to either convert the integer to
139
+ a duration or convert the duration to an integer (with `#to_i`)
140
+ - you can only add and subtract two duration objects
141
+ * Removed hash equality
142
+
143
+ ## License
144
+
145
+ [MIT](LICENSE.txt)
@@ -0,0 +1 @@
1
+ require "as/duration"
@@ -0,0 +1,133 @@
1
+ require "as/duration/core_ext"
2
+ require "time"
3
+
4
+ module AS
5
+ class Duration
6
+ include Comparable
7
+
8
+ attr_reader :parts
9
+
10
+ def initialize(parts)
11
+ parts = parts.dup
12
+
13
+ # Remove partial weeks and days for accurate date behaviour
14
+ if Float === parts[:weeks]
15
+ parts[:weeks], partial_weeks = parts[:weeks].divmod(1)
16
+ parts[:days] = parts.fetch(:days, 0) + 7 * partial_weeks
17
+ end
18
+ if Float === parts[:days]
19
+ parts[:days], partial_days = parts[:days].divmod(1)
20
+ parts[:hours] = parts.fetch(:hours, 0) + 24 * partial_days
21
+ end
22
+
23
+ @parts = parts.freeze
24
+ end
25
+
26
+ def to_i
27
+ @parts.inject(0) do |sum, (type, value)|
28
+ case type
29
+ when :seconds then sum + value
30
+ when :minutes then sum + value * 60
31
+ when :hours then sum + value * 60 * 60
32
+ when :days then sum + value * 60 * 60 * 24
33
+ when :weeks then sum + value * 60 * 60 * 24 * 7
34
+ when :fortnights then sum + value * 60 * 60 * 24 * 14
35
+ when :months then sum + value * 60 * 60 * 24 * 30
36
+ when :years then sum + value * 60 * 60 * 24 * 365
37
+ end
38
+ end
39
+ end
40
+
41
+ def <=>(other)
42
+ return nil if not Duration === other
43
+ self.to_i <=> other.to_i
44
+ end
45
+
46
+ def +(other)
47
+ raise TypeError, "can only add Duration objects" if not Duration === other
48
+ added_parts = parts.merge(other.parts) { |key, old, new| old + new }
49
+ Duration.new(added_parts)
50
+ end
51
+
52
+ def -(other)
53
+ raise TypeError, "can only subtract Duration objects" if not Duration === other
54
+ self + (-other)
55
+ end
56
+
57
+ def -@
58
+ negated_parts = parts.inject({}) { |h, (k, v)| h.update(k => -v) }
59
+ Duration.new(negated_parts)
60
+ end
61
+
62
+ def from(time)
63
+ advance(time)
64
+ end
65
+ alias since from
66
+ alias after from
67
+
68
+ def from_now
69
+ from(Time.now)
70
+ end
71
+
72
+ def until(time)
73
+ (-self).advance(time)
74
+ end
75
+ alias to until
76
+ alias before until
77
+
78
+ def ago
79
+ self.until(Time.now)
80
+ end
81
+
82
+ protected
83
+
84
+ def advance(time)
85
+ case time
86
+ when Time then advance_time(time)
87
+ when Date then advance_date(time)
88
+ else
89
+ raise ArgumentError, "expected Time or Date, got #{time.inspect}"
90
+ end
91
+ end
92
+
93
+ def advance_time(time)
94
+ date = advance_date_part(time.to_date)
95
+
96
+ time_advanced_by_date_args =
97
+ if time.utc?
98
+ Time.utc(date.year, date.month, date.day, time.hour, time.min, time.sec)
99
+ elsif time.zone
100
+ Time.local(date.year, date.month, date.day, time.hour, time.min, time.sec)
101
+ else
102
+ Time.new(date.year, date.month, date.day, time.hour, time.min, time.sec, time.utc_offset)
103
+ end
104
+
105
+ time_advanced_by_date_args + seconds_to_advance
106
+ end
107
+
108
+ def advance_date(date)
109
+ date = advance_date_part(date)
110
+
111
+ if seconds_to_advance == 0
112
+ date
113
+ else
114
+ date.to_time + seconds_to_advance
115
+ end
116
+ end
117
+
118
+ def advance_date_part(date)
119
+ date = date >> parts.fetch(:years, 0) * 12
120
+ date = date >> parts.fetch(:months, 0)
121
+ date = date + parts.fetch(:weeks, 0) * 7
122
+ date = date + parts.fetch(:days, 0)
123
+ date = date.gregorian if date.julian?
124
+ date
125
+ end
126
+
127
+ def seconds_to_advance
128
+ parts.fetch(:seconds, 0) +
129
+ parts.fetch(:minutes, 0) * 60 +
130
+ parts.fetch(:hours, 0) * 3600
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,5 @@
1
+ require "as/duration/core_ext/numeric"
2
+ require "as/duration/core_ext/integer"
3
+
4
+ require "as/duration/core_ext/time"
5
+ require "as/duration/core_ext/date"
@@ -0,0 +1,5 @@
1
+ require "as/duration/operations"
2
+
3
+ class Date
4
+ prepend AS::Duration::Operations::DateAndTime
5
+ end
@@ -0,0 +1,11 @@
1
+ class Integer
2
+ def months
3
+ AS::Duration.new(months: self)
4
+ end
5
+ alias month months
6
+
7
+ def years
8
+ AS::Duration.new(years: self)
9
+ end
10
+ alias year years
11
+ end
@@ -0,0 +1,31 @@
1
+ class Numeric
2
+ def seconds
3
+ AS::Duration.new(seconds: self)
4
+ end
5
+ alias second seconds
6
+
7
+ def minutes
8
+ AS::Duration.new(minutes: self)
9
+ end
10
+ alias minute minutes
11
+
12
+ def hours
13
+ AS::Duration.new(hours: self)
14
+ end
15
+ alias hour hours
16
+
17
+ def days
18
+ AS::Duration.new(days: self)
19
+ end
20
+ alias day days
21
+
22
+ def weeks
23
+ AS::Duration.new(weeks: self)
24
+ end
25
+ alias week weeks
26
+
27
+ def fortnights
28
+ AS::Duration.new(weeks: self * 2)
29
+ end
30
+ alias fortnight fortnights
31
+ end
@@ -0,0 +1,5 @@
1
+ require "as/duration/operations"
2
+
3
+ class Time
4
+ prepend AS::Duration::Operations::DateAndTime
5
+ end
@@ -0,0 +1,23 @@
1
+ module AS
2
+ class Duration
3
+ module Operations
4
+ module DateAndTime
5
+ def +(other)
6
+ if Duration === other
7
+ other.since(self)
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def -(other)
14
+ if Duration === other
15
+ other.until(self)
16
+ else
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: as-duration
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Janko Marohnić
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 5.6.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 5.6.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Extraction of ActiveSupport::Duration and the related core extensions.
42
+ email:
43
+ - janko.marohnic@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE.txt
49
+ - README.md
50
+ - lib/as-duration.rb
51
+ - lib/as/duration.rb
52
+ - lib/as/duration/core_ext.rb
53
+ - lib/as/duration/core_ext/date.rb
54
+ - lib/as/duration/core_ext/integer.rb
55
+ - lib/as/duration/core_ext/numeric.rb
56
+ - lib/as/duration/core_ext/time.rb
57
+ - lib/as/duration/operations.rb
58
+ homepage: https://github.com/janko-m/as-duration
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.0.0
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.4.5
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Extraction of ActiveSupport::Duration and the related core extensions.
82
+ test_files: []
83
+ has_rdoc: