duranged 0.0.1 → 0.0.2

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: a2951d10f37ccb4455790939b928f13d81f99f66
4
- data.tar.gz: 86b329f53282b75ce0fcbd6b1cb0c391e6808544
3
+ metadata.gz: 46db5544d738bea7c2e616f797e6667e817cd9a1
4
+ data.tar.gz: e7fe1eae2d4205fc344175e7ea9f9298549e77fd
5
5
  SHA512:
6
- metadata.gz: 1e1561da9807d9564a2403c990f60ec5c297351bd1187f093c6fc83001440d1cd707eeb9a4155761bb79d629e4f5591156ce8852250fbb4fd5785a023873a56e
7
- data.tar.gz: 776ca9e9111541289f471c0482433e86b19ed24eb1f2d47faa4a962f05b709422c20bef8a7f020c994bcc2bdd9812a024107146ff1537f443f36e4092907d723
6
+ metadata.gz: c38eba26ed2b9208cd0c95bfa61224287940af2cf5f92bc0b4604bf1dc8ef762d63a9a1103d5ec38d79fe46cb59287b0a3e151a948b375b33eec24f18911e979
7
+ data.tar.gz: 2a7319101bfb44860c33ead635bac7d27634c6a0f62ab8c56057cf83dfc6734e51d0d5cdf25269c806c9a089bc9b1c0e24e54ea20c326aadff7c320ac107b9ff
@@ -5,6 +5,8 @@ require 'active_support/core_ext/numeric'
5
5
  require 'active_support/core_ext/time'
6
6
  require 'canfig'
7
7
  require 'chronic_duration'
8
+ require 'duranged/periods'
9
+ require 'duranged/formatters'
8
10
  require 'duranged/base'
9
11
  require 'duranged/duration'
10
12
  require 'duranged/interval'
@@ -1,17 +1,10 @@
1
1
  module Duranged
2
2
  class Base
3
3
  include Comparable
4
- attr_reader :value
4
+ include Periods
5
+ include Formatters
5
6
 
6
- PARTS = [:years, :months, :weeks, :days_after_weeks, :days, :hours, :minutes, :seconds]
7
- FORMATTERS = { 's' => -> (pad,with) { pad_value seconds, pad, with },
8
- 'm' => -> (pad,with) { pad_value minutes, pad, with },
9
- 'h' => -> (pad,with) { pad_value hours, pad, with },
10
- 'd' => -> (pad,with) { pad_value days, pad, with },
11
- 'D' => -> (pad,with) { pad_value days_after_weeks, pad, with },
12
- 'w' => -> (pad,with) { pad_value weeks, pad, with },
13
- 'M' => -> (pad,with) { pad_value months, pad, with },
14
- 'y' => -> (pad,with) { pad_value years, pad, with } }
7
+ attr_reader :value
15
8
 
16
9
  class << self
17
10
  def dump(obj)
@@ -35,118 +28,6 @@ module Duranged
35
28
  end
36
29
  end
37
30
 
38
- def years
39
- (value.to_f / 60 / 60 / 24 / 365.25).to_i
40
- end
41
-
42
- def months
43
- ((value - years.years) / 60 / 60 / 24 / 30).floor
44
- end
45
-
46
- def weeks
47
- (((value - months.months - years.years) / 60 / 60 / 24).floor / 7).floor
48
- end
49
-
50
- def days_after_weeks
51
- ((value - weeks.weeks - months.months - years.years) / 60 / 60 / 24).floor
52
- end
53
-
54
- def days
55
- ((value - months.months - years.years) / 60 / 60 / 24).floor
56
- end
57
-
58
- def hours
59
- ((value - days.days - months.months - years.years) / 60 / 60).floor
60
- end
61
-
62
- def minutes
63
- ((value - hours.hours - days.days - months.months - years.years) / 60).floor
64
- end
65
-
66
- def seconds
67
- (value - minutes.minutes - hours.hours - days.days - months.months - years.years).floor
68
- end
69
-
70
- def +(other)
71
- if other.is_a?(Duration) || other.is_a?(Interval)
72
- Duranged.logger.warn "Warning: You are adding a #{other.class.name} to a #{self.class.name}, which will result in a #{self.class.name}. If you would like a #{other.class.name} object to be returned you must add your #{self.class.name} to your #{other.class.name} instead." if other.is_a?(Range)
73
- self.class.new(value + other.value)
74
- elsif other.is_a?(Integer)
75
- self.class.new(value + other)
76
- else
77
- raise ArgumentError, "value must be an Integer, Duranged::Duration or Duranged::Interval"
78
- end
79
- end
80
-
81
- def -(other)
82
- if other.is_a?(Duration) || other.is_a?(Interval)
83
- Duranged.logger.warn "Warning: You are subtracting a #{other.class.name} from a #{self.class.name}, which will result in a #{self.class.name}. If you would like a #{other.class.name} object to be returned you must subtract your #{self.class.name} from your #{other.class.name} instead." if other.is_a?(Range)
84
- self.class.new(value - other.value)
85
- elsif other.is_a?(Integer)
86
- self.class.new(value - other)
87
- else
88
- raise ArgumentError, "value must be an Integer, Duranged::Duration or Duranged::Interval"
89
- end
90
- end
91
-
92
- def as_json(options=nil)
93
- value
94
- end
95
-
96
- def to_h
97
- PARTS.map do |part|
98
- [part, send(part)]
99
- end.to_h
100
- end
101
-
102
- def to_s
103
- ChronicDuration.output(value, format: :long, joiner: ', ').to_s
104
- end
105
-
106
- def strfdur(format)
107
- str = format.to_s
108
-
109
- # :years(%Y years, ):months(%N months, )%D :days
110
- PARTS.each do |part|
111
- while matches = str.match(/:(#{part})(\((.+)\))?/i) do
112
- if matches[3]
113
- # only replaces if the value is > 0, otherwise blank
114
- matched = ''
115
- depth = 0
116
- matches[3].chars.to_a.each do |char|
117
- depth += 1 if char == '('
118
- depth -= 1 if char == ')'
119
- break if depth == -1
120
- matched += char
121
- end
122
- value = send(part) > 0 ? strfdur(matched.dup) : ''
123
- str.gsub!(":#{part}(#{matched})", value)
124
- else
125
- # if no nested format was passed, replace with a singular
126
- # or plural part name as appropriate
127
- value = send(part) == 1 ? matches[1].to_s.singularize : matches[1].to_s
128
- str.gsub!(matches[0], value)
129
- end
130
- end
131
- end
132
-
133
- FORMATTERS.each do |conversion, block|
134
- while matches = str.match(/%([-_])?([0-9]+)?(#{conversion})/) do
135
- pad_with = matches[1] == '_' ? :space : :zero
136
- value = instance_exec(matches[2] || 2, pad_with, &block)
137
- value = value.to_i.to_s.lstrip if matches[1] == '-'
138
-
139
- str.gsub!(matches[0], value)
140
- end
141
- end
142
-
143
- str
144
- end
145
-
146
- def to_i
147
- value
148
- end
149
-
150
31
  def <=>(other)
151
32
  if value < other.to_i
152
33
  -1
@@ -157,8 +38,14 @@ module Duranged
157
38
  end
158
39
  end
159
40
 
160
- def call
161
- value
41
+ def +(other)
42
+ Duranged.logger.warn "Warning: You are adding a #{other.class.name} to a #{self.class.name}, which will result in a #{self.class.name}. If you would like a #{other.class.name} object to be returned you must add your #{self.class.name} to your #{other.class.name} instead." if other.is_a?(Range)
43
+ self.class.new(to_i + other.to_i)
44
+ end
45
+
46
+ def -(other)
47
+ Duranged.logger.warn "Warning: You are subtracting a #{other.class.name} from a #{self.class.name}, which will result in a #{self.class.name}. If you would like a #{other.class.name} object to be returned you must subtract your #{self.class.name} from your #{other.class.name} instead." if other.is_a?(Range)
48
+ self.class.new(to_i - other.to_i)
162
49
  end
163
50
 
164
51
  protected
@@ -166,17 +53,5 @@ module Duranged
166
53
  def parse_hash(hash)
167
54
  hash.sum { |k,v| v.to_i.send(k.to_sym) }
168
55
  end
169
-
170
- def pad_value(value, pad=2, with=:zero)
171
- send("#{with}_pad".to_sym, value, pad)
172
- end
173
-
174
- def zero_pad(value, pad=2)
175
- "%0#{pad}d" % value
176
- end
177
-
178
- def space_pad(value, pad=2)
179
- "%#{pad}d" % value
180
- end
181
56
  end
182
57
  end
@@ -0,0 +1,108 @@
1
+ module Duranged
2
+ module Formatters
3
+ PERIODS = [:years, :months, :weeks, :days_after_weeks, :days, :hours, :minutes, :seconds]
4
+ FORMATTERS = { 's' => -> (pad,with) { pad_value seconds, pad, with },
5
+ 'm' => -> (pad,with) { pad_value minutes, pad, with },
6
+ 'h' => -> (pad,with) { pad_value hours, pad, with },
7
+ 'd' => -> (pad,with) { pad_value days, pad, with },
8
+ 'D' => -> (pad,with) { pad_value days_after_weeks, pad, with },
9
+ 'w' => -> (pad,with) { pad_value weeks, pad, with },
10
+ 'M' => -> (pad,with) { pad_value months, pad, with },
11
+ 'y' => -> (pad,with) { pad_value years, pad, with } }
12
+
13
+ def as_json(options=nil)
14
+ value
15
+ end
16
+
17
+ def to_h
18
+ Hash[PERIODS.map do |part|
19
+ [part, send(part)]
20
+ end]
21
+ end
22
+
23
+ def to_s
24
+ ChronicDuration.output(value, format: :long, joiner: ', ').to_s
25
+ end
26
+
27
+ def strfdur(format)
28
+ str = format.to_s
29
+
30
+ # :years(%Y years, ):months(%N months, )%D :days
31
+ str = strfparts(str)
32
+
33
+ # %y %M %w %d %m %s
34
+ str = strfformatters(str)
35
+
36
+ str
37
+ end
38
+
39
+ def to_i
40
+ value
41
+ end
42
+
43
+ protected
44
+
45
+ def strfparts(str)
46
+ PERIODS.each do |part|
47
+ while matches = str.match(/:(#{part})(\((.+)\))?/i) do
48
+ if matches[3]
49
+ str = strfpart(str, part)
50
+ else
51
+ # if no nested format was passed, replace with a singular
52
+ # or plural part name as appropriate
53
+ value = send(part) == 1 ? matches[1].to_s.singularize : matches[1].to_s
54
+
55
+ str.gsub!(matches[0], value)
56
+ end
57
+ end
58
+ end
59
+
60
+ str
61
+ end
62
+
63
+ def strfpart(str, part)
64
+ matched = parse_match(str)
65
+ value = send(part) > 0 ? strfdur(matched.dup) : ''
66
+
67
+ str.gsub!(":#{part}(#{matched})", value)
68
+ end
69
+
70
+ def strfformatters(str)
71
+ FORMATTERS.each do |conversion, block|
72
+ while matches = str.match(/%([-_])?([0-9]+)?(#{conversion})/) do
73
+ pad_with = matches[1] == '_' ? :space : :zero
74
+ value = instance_exec(matches[2] || 2, pad_with, &block)
75
+ value = value.to_i.to_s.lstrip if matches[1] == '-'
76
+
77
+ str.gsub!(matches[0], value)
78
+ end
79
+ end
80
+
81
+ str
82
+ end
83
+
84
+ def pad_value(value, pad=2, with=:zero)
85
+ send("#{with}_pad".to_sym, value, pad)
86
+ end
87
+
88
+ def zero_pad(value, pad=2)
89
+ "%0#{pad}d" % value
90
+ end
91
+
92
+ def space_pad(value, pad=2)
93
+ "%#{pad}d" % value
94
+ end
95
+
96
+ def parse_match(match_str)
97
+ match_substr = ''
98
+ depth = 0
99
+ match_str.chars.to_a.each do |char|
100
+ depth += 1 if char == '('
101
+ depth -= 1 if char == ')'
102
+ break if depth == -1
103
+ match_substr += char
104
+ end
105
+ match_substr
106
+ end
107
+ end
108
+ end
@@ -35,6 +35,15 @@ module Duranged
35
35
  end
36
36
  end
37
37
 
38
+ def as_json(options=nil)
39
+ hash = { occurrences: occurrences }
40
+ hash[:interval] = interval.as_json(options) if interval
41
+ hash[:duration] = duration.as_json(options) if duration
42
+ hash[:range] = range.as_json(options) if range
43
+ hash
44
+ end
45
+ alias_method :to_h, :as_json
46
+
38
47
  def occurrences_string
39
48
  case occurrences
40
49
  when 0
@@ -48,15 +57,6 @@ module Duranged
48
57
  end
49
58
  end
50
59
 
51
- def as_json(options=nil)
52
- hash = { occurrences: occurrences }
53
- hash[:interval] = interval.as_json(options) if interval
54
- hash[:duration] = duration.as_json(options) if duration
55
- hash[:range] = range.as_json(options) if range
56
- hash
57
- end
58
- alias_method :to_h, :as_json
59
-
60
60
  def to_s
61
61
  return occurrences_string if occurrences == 0
62
62
 
@@ -86,21 +86,28 @@ module Duranged
86
86
  def strfocc(format)
87
87
  str = format.to_s
88
88
 
89
+ str = strfoccurrences(str)
90
+ str = strfrange(str)
91
+ str = strfdurint(str)
92
+
93
+ str
94
+ end
95
+
96
+ protected
97
+
98
+ def strfoccurrences(str)
89
99
  while matches = str.match(/:occurrence[s]?/) do
90
100
  str.gsub!(matches[0], occurrences.to_s)
91
101
  end
92
-
102
+
103
+ str
104
+ end
105
+
106
+ def strfrange(str)
93
107
  if range
94
108
  while matches = str.match(/:range(\((.+)\))?/) do
95
109
  if matches[2]
96
- matched = ''
97
- depth = 0
98
- matches[2].chars.to_a.each do |char|
99
- depth += 1 if char == '('
100
- depth -= 1 if char == ')'
101
- break if depth == -1
102
- matched += char
103
- end
110
+ matched = parse_match(matches[2])
104
111
  value = range.strfrange(matched.dup)
105
112
  str.gsub!(":range(#{matched})", value)
106
113
  else
@@ -110,6 +117,10 @@ module Duranged
110
117
  end
111
118
  end
112
119
 
120
+ str
121
+ end
122
+
123
+ def strfdurint(str)
113
124
  if duration || interval
114
125
  while matches = str.match(/:(duration|interval)(\(([^\)]+)\))/) do
115
126
  matched = send(matches[1])
@@ -120,5 +131,17 @@ module Duranged
120
131
 
121
132
  str
122
133
  end
134
+
135
+ def parse_match(match_str)
136
+ match_substr = ''
137
+ depth = 0
138
+ match_str.chars.to_a.each do |char|
139
+ depth += 1 if char == '('
140
+ depth -= 1 if char == ')'
141
+ break if depth == -1
142
+ match_substr += char
143
+ end
144
+ match_substr
145
+ end
123
146
  end
124
147
  end
@@ -0,0 +1,66 @@
1
+ module Duranged
2
+ module Periods
3
+ def years
4
+ (value.to_f / 60 / 60 / 24 / 365.25).to_i
5
+ end
6
+
7
+ def months
8
+ (seconds_after_years / 60 / 60 / 24 / 30).floor
9
+ end
10
+
11
+ def weeks
12
+ ((seconds_after_months / 60 / 60 / 24).floor / 7).floor
13
+ end
14
+
15
+ def days_after_weeks
16
+ (seconds_after_weeks / 60 / 60 / 24).floor
17
+ end
18
+
19
+ def days_after_months
20
+ (seconds_after_months / 60 / 60 / 24).floor
21
+ end
22
+
23
+ def days
24
+ # TODO make this a configuration option with the ability to default to :days_after_weeks
25
+ days_after_months
26
+ end
27
+
28
+ def hours
29
+ (seconds_after_days / 60 / 60).floor
30
+ end
31
+
32
+ def minutes
33
+ (seconds_after_hours / 60).floor
34
+ end
35
+
36
+ def seconds
37
+ seconds_after_minutes.floor
38
+ end
39
+
40
+ protected
41
+
42
+ def seconds_after_years
43
+ value - years.years
44
+ end
45
+
46
+ def seconds_after_months
47
+ seconds_after_years - months.months
48
+ end
49
+
50
+ def seconds_after_weeks
51
+ seconds_after_months - weeks.weeks
52
+ end
53
+
54
+ def seconds_after_days
55
+ seconds_after_months - days.days
56
+ end
57
+
58
+ def seconds_after_hours
59
+ seconds_after_days - hours.hours
60
+ end
61
+
62
+ def seconds_after_minutes
63
+ seconds_after_hours - minutes.minutes
64
+ end
65
+ end
66
+ end
@@ -49,23 +49,11 @@ module Duranged
49
49
  alias_method :end_time, :end_at
50
50
 
51
51
  def +(other)
52
- if other.is_a?(Duration) || other.is_a?(Interval)
53
- self.class.new(start_at, value + other.value)
54
- elsif other.is_a?(Integer)
55
- self.class.new(start_at, value + other)
56
- else
57
- raise ArgumentError, "value must be an Integer, Duranged::Duration or Duranged::Interval"
58
- end
52
+ self.class.new(start_at, to_i + other.to_i)
59
53
  end
60
54
 
61
55
  def -(other)
62
- if other.is_a?(Duration) || other.is_a?(Interval)
63
- self.class.new(start_at, value - other.value)
64
- elsif other.is_a?(Integer)
65
- self.class.new(start_at, value - other)
66
- else
67
- raise ArgumentError, "value must be an Integer, Duranged::Duration or Duranged::Interval"
68
- end
56
+ self.class.new(start_at, to_i - other.to_i)
69
57
  end
70
58
 
71
59
  def to_duration
@@ -89,23 +77,32 @@ module Duranged
89
77
  def strfrange(format)
90
78
  str = format.to_s
91
79
 
80
+ str = strfstartend(str)
81
+ str = strfduration(str)
82
+
83
+ str
84
+ end
85
+
86
+ def same_day?
87
+ start_at('%j') == end_at('%j')
88
+ end
89
+
90
+ protected
91
+
92
+ def strfstartend(str)
92
93
  while matches = str.match(/:(start_at|end_at|)\((([^\)]+))\)/) do
93
94
  str.gsub!(matches[0], send(matches[1], matches[2]))
94
95
  end
95
96
 
97
+ str
98
+ end
99
+
100
+ def strfduration(str)
96
101
  while matches = str.match(/:duration\(([^\)]+)\)/) do
97
102
  str.gsub!(matches[0], strfdur(matches[1]))
98
103
  end
99
104
 
100
105
  str
101
106
  end
102
-
103
- def call
104
- to_s
105
- end
106
-
107
- def same_day?
108
- start_at('%j') == end_at('%j')
109
- end
110
107
  end
111
108
  end
@@ -1,3 +1,3 @@
1
1
  module Duranged
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -1,7 +1,7 @@
1
- require 'duranged'
2
- require 'rspec'
3
1
  require 'coveralls'
4
2
  Coveralls.wear!
3
+ require 'duranged'
4
+ require 'rspec'
5
5
 
6
6
  Dir[File.join(File.dirname(__FILE__), '..', "spec/support/**/*.rb")].each { |f| require f }
7
7
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duranged
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rebec
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-11 00:00:00.000000000 Z
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,7 +80,8 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: API for working with durations and time ranges.
83
+ description: A set of classes to facilitate working with and formatting durations,
84
+ intervals, time ranges and occurrences.
84
85
  email:
85
86
  - mark@markrebec.com
86
87
  executables: []
@@ -90,8 +91,10 @@ files:
90
91
  - lib/duranged.rb
91
92
  - lib/duranged/base.rb
92
93
  - lib/duranged/duration.rb
94
+ - lib/duranged/formatters.rb
93
95
  - lib/duranged/interval.rb
94
96
  - lib/duranged/occurrence.rb
97
+ - lib/duranged/periods.rb
95
98
  - lib/duranged/range.rb
96
99
  - lib/duranged/version.rb
97
100
  - spec/duranged/base_spec.rb
@@ -124,7 +127,8 @@ rubyforge_project:
124
127
  rubygems_version: 2.2.2
125
128
  signing_key:
126
129
  specification_version: 4
127
- summary: API for working with durations and time ranges.
130
+ summary: A set of classes to facilitate working with and formatting durations, intervals,
131
+ time ranges and occurrences.
128
132
  test_files:
129
133
  - spec/duranged/base_spec.rb
130
134
  - spec/duranged/duration_spec.rb