fat_core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,192 @@
1
+ class Range
2
+ # Return a range that concatenates this range with other; return nil
3
+ # if the ranges are not contiguous.
4
+ def join(other)
5
+ if left_contiguous?(other)
6
+ Range.new(min, other.max)
7
+ elsif right_contiguous?(other)
8
+ Range.new(other.min, max)
9
+ else
10
+ nil
11
+ end
12
+ end
13
+
14
+ # Is self on the left of and contiguous to other?
15
+ def left_contiguous?(other)
16
+ if max.respond_to?(:succ)
17
+ max.succ == other.min
18
+ else
19
+ max == other.min
20
+ end
21
+ end
22
+
23
+ # Is self on the right of and contiguous to other?
24
+ def right_contiguous?(other)
25
+ if other.max.respond_to?(:succ)
26
+ other.max.succ == min
27
+ else
28
+ other.max == min
29
+ end
30
+ end
31
+
32
+ def contiguous?(other)
33
+ left_contiguous?(other) || right_contiguous?(other)
34
+ end
35
+
36
+ def subset_of?(other)
37
+ min >= other.min && max <= other.max
38
+ end
39
+
40
+ def proper_subset_of?(other)
41
+ min > other.min && max < other.max
42
+ end
43
+
44
+ def superset_of?(other)
45
+ min <= other.min && max >= other.max
46
+ end
47
+
48
+ def proper_superset_of?(other)
49
+ min < other.min && max > other.max
50
+ end
51
+
52
+ def overlaps?(other)
53
+ cover?(other.min) || cover?(other.max)
54
+ end
55
+
56
+ def intersection(other)
57
+ return nil unless self.overlaps?(other)
58
+ ([self.min, other.min].max..[self.max, other.max].min)
59
+ end
60
+ alias_method :&, :intersection
61
+
62
+ def union(other)
63
+ return nil unless self.overlaps?(other)
64
+ ([self.min, other.min].min..[self.max, other.max].max)
65
+ end
66
+ alias_method :+, :union
67
+
68
+ # The difference method, -, removes the overlapping part of the other
69
+ # argument from self. Because in the case where self is a superset of the
70
+ # other range, this will result in the difference being two non-contiguous
71
+ # ranges, this returns an array of ranges. If there is no overlap or if
72
+ # self is a subset of the other range, return an empty array
73
+ def difference(other)
74
+ unless max.respond_to?(:succ) && min.respond_to?(:pred) &&
75
+ other.max.respond_to?(:succ) && other.min.respond_to?(:pred)
76
+ raise "Range difference operation requires objects have pred and succ methods"
77
+ end
78
+ # return [] unless self.overlaps?(other)
79
+ if proper_superset_of?(other)
80
+ [(min..other.min.pred),
81
+ (other.max.succ..max)]
82
+ elsif subset_of?(other)
83
+ []
84
+ elsif overlaps?(other) && other.min <= min
85
+ [(other.max.succ .. max)]
86
+ elsif overlaps?(other) && other.max >= max
87
+ [(min .. other.min.pred)]
88
+ else
89
+ []
90
+ end
91
+ end
92
+ alias_method :-, :difference
93
+
94
+ # Return whether any of the ranges that are within self overlap one
95
+ # another
96
+ def has_overlaps_within?(ranges)
97
+ result = false
98
+ unless ranges.empty?
99
+ ranges.each do |r1|
100
+ next unless overlaps?(r1)
101
+ result =
102
+ ranges.any? do |r2|
103
+ r1.object_id != r2.object_id && overlaps?(r2) &&
104
+ r1.overlaps?(r2)
105
+ end
106
+ return true if result
107
+ end
108
+ end
109
+ result
110
+ end
111
+
112
+ # Return true if the given ranges collectively cover this range
113
+ # without overlaps.
114
+ def spanned_by?(ranges)
115
+ joined_range = nil
116
+ ranges.sort_by {|r| r.min}.each do |r|
117
+ unless joined_range
118
+ joined_range = r
119
+ next
120
+ end
121
+ joined_range = joined_range.join(r)
122
+ break if joined_range.nil?
123
+ end
124
+ if !joined_range.nil?
125
+ joined_range.min <= min && joined_range.max >= max
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ # If this range is not spanned by the ranges collectively, return an array
132
+ # of ranges representing the gaps in coverage. Otherwise return an empty
133
+ # array.
134
+ def gaps(ranges)
135
+ if ranges.empty?
136
+ [self.clone]
137
+ elsif spanned_by?(ranges)
138
+ []
139
+ else
140
+ ranges = ranges.sort_by {|r| r.min}
141
+ gaps = []
142
+ cur_point = min
143
+ ranges.each do |rr|
144
+ if rr.min > cur_point
145
+ start_point = cur_point
146
+ end_point = rr.min.pred
147
+ gaps << (start_point..end_point)
148
+ end
149
+ cur_point = rr.max.succ
150
+ end
151
+ if cur_point < max
152
+ gaps << (cur_point..max)
153
+ end
154
+ gaps
155
+ end
156
+ end
157
+
158
+ # Similar to gaps, but within this range return the /overlaps/ among the
159
+ # given ranges. If there are no overlaps, return an empty array. Don't
160
+ # consider overlaps in the ranges that occur outside of self.
161
+ def overlaps(ranges)
162
+ if ranges.empty? || spanned_by?(ranges)
163
+ []
164
+ else
165
+ ranges = ranges.sort_by {|r| r.min}
166
+ overlaps = []
167
+ cur_point = nil
168
+ ranges.each do |rr|
169
+ # Skip ranges outside of self
170
+ next if rr.max < min || rr.min > max
171
+ # Initialize cur_point to max of first range
172
+ if cur_point.nil?
173
+ cur_point = rr.max
174
+ next
175
+ end
176
+ # We are on the second or later range
177
+ if rr.min < cur_point
178
+ start_point = rr.min
179
+ end_point = cur_point
180
+ overlaps << (start_point..end_point)
181
+ end
182
+ cur_point = rr.max
183
+ end
184
+ overlaps
185
+ end
186
+ end
187
+
188
+ # Allow erb documents can directly interpolate ranges
189
+ def tex_quote
190
+ to_s
191
+ end
192
+ end
@@ -0,0 +1,184 @@
1
+ class String
2
+ # See if self contains colon- or space-separated words that include
3
+ # the colon- or space-separated words of other. Return the matched
4
+ # portion of self. Other cannot be a regex embedded in a string.
5
+ def fuzzy_match(other)
6
+ # Remove periods, commas, and apostrophes
7
+ other = other.gsub(/[.,']/, '')
8
+ target = self.gsub(/[.,']/, '')
9
+ matched_text = nil
10
+ matchers = other.split(/[: ]+/)
11
+ regexp_string = matchers.map {|m| ".*?#{Regexp.escape(m)}.*?"}.join('[: ]')
12
+ regexp_string.sub!(/^\.\*\?/, '')
13
+ regexp_string.sub!(/\.\*\?$/, '')
14
+ regexp = /#{regexp_string}/i
15
+ if match = regexp.match(target)
16
+ matched_text = match[0]
17
+ else
18
+ matched_text = nil
19
+ end
20
+ matched_text
21
+ end
22
+
23
+ # Here are instance methods for the class that includes Matchable
24
+ # This tries to convert the receiver object into a string, then
25
+ # matches against the given matcher, either via regex or a fuzzy
26
+ # string matcher.
27
+ def matches_with(str)
28
+ if str.nil?
29
+ nil
30
+ elsif str =~ /^\s*\//
31
+ re = str.to_regexp
32
+ if self.to_s =~ re
33
+ $&
34
+ else
35
+ nil
36
+ end
37
+ else
38
+ self.to_s.fuzzy_match(str)
39
+ end
40
+ end
41
+
42
+ # Convert a string of the form '/.../Iixm' to a regular expression. However,
43
+ # make the regular expression case-insensitive by default and extend the
44
+ # modifier syntax to allow '/I' to indicate case-sensitive.
45
+ def to_regexp
46
+ if self =~ /^\s*\/([^\/]*)\/([Iixm]*)\s*$/
47
+ body = $1
48
+ opts = $2
49
+ flags = Regexp::IGNORECASE
50
+ unless opts.blank?
51
+ flags = 0 if opts.include?('I')
52
+ flags |= Regexp::IGNORECASE if opts.include?('i')
53
+ flags |= Regexp::EXTENDED if opts.include?('x')
54
+ flags |= Regexp::MULTILINE if opts.include?('m')
55
+ end
56
+ flags = nil if flags == 0
57
+ Regexp.new(body, flags)
58
+ else
59
+ Regexp.new(self)
60
+ end
61
+ end
62
+
63
+ # Convert to symbol "Hello World" -> :hello_world
64
+ def as_sym
65
+ strip.squeeze(' ').gsub(/\s+/, '_').downcase.to_sym
66
+ end
67
+
68
+ def as_string
69
+ self
70
+ end
71
+
72
+ def wrap(width=70, hang=0)
73
+ offset = 0
74
+ trip = 1
75
+ result = ''
76
+ while (s = slice(offset, width))
77
+ offset += width
78
+ if trip == 1
79
+ width -= hang
80
+ else
81
+ s = (' ' * hang) + s
82
+ end
83
+ result << s + "\n"
84
+ trip += 1
85
+ end
86
+ # Remove the final newline before exiting
87
+ result.strip
88
+ end
89
+
90
+ def tex_quote
91
+ r = self.dup
92
+ r = r.gsub(/[{]/, 'XzXzXobXzXzX')
93
+ r = r.gsub(/[}]/, 'XzXzXcbXzXzX')
94
+ r = r.gsub(/\\/, '\textbackslash{}')
95
+ r = r.gsub(/\^/, '\textasciicircum{}')
96
+ r = r.gsub(/~/, '\textasciitilde{}')
97
+ r = r.gsub(/\|/, '\textbar{}')
98
+ r = r.gsub(/\</, '\textless{}')
99
+ r = r.gsub(/\>/, '\textgreater{}')
100
+ r = r.gsub(/([_$&%#])/) { |m| '\\' + m }
101
+ r = r.gsub('XzXzXobXzXzX', '\\{')
102
+ r = r.gsub('XzXzXcbXzXzX', '\\}')
103
+ end
104
+
105
+ def self.random(size = 8)
106
+ "abcdefghijklmnopqrstuvwxyz".split('').shuffle[0..size].join('')
107
+ end
108
+
109
+ # Convert a string with an all-digit date to an iso string
110
+ # E.g., "20090923" -> "2009-09-23"
111
+ def digdate2iso
112
+ self.sub(/(\d\d\d\d)(\d\d)(\d\d)/, '\1-\2-\3')
113
+ end
114
+
115
+ def entitle!
116
+ little_words = %w[ a an the and or in on under of from as by to ]
117
+ newwords = []
118
+ words = split(/\s+/)
119
+ first_word = true
120
+ num_words = words.length
121
+ words.each_with_index do |w, k|
122
+ last_word = (k + 1 == num_words)
123
+ if w =~ %r[c/o]i
124
+ # Care of
125
+ newwords.push("c/o")
126
+ elsif w =~ %r[^p\.?o\.?$]i
127
+ # Post office
128
+ newwords.push("P.O.")
129
+ elsif w =~ %r[^[0-9]+(st|nd|rd|th)$]i
130
+ # Ordinals
131
+ newwords.push(w.downcase)
132
+ elsif w =~ %r[^(cr|dr|st|rd|ave|pk|cir)$]i
133
+ # Common abbrs to capitalize
134
+ newwords.push(w.capitalize)
135
+ elsif w =~ %r[^(us|ne|se|rr)$]i
136
+ # Common 2-letter abbrs to upcase
137
+ newwords.push(w.upcase)
138
+ elsif w =~ %r[^[0-9].*$]i
139
+ # Other runs starting with numbers,
140
+ # like 3-A
141
+ newwords.push(w.upcase)
142
+ elsif w =~ %r[^[^aeiouy]*$]i
143
+ # All consonants, probably abbr
144
+ newwords.push(w.upcase)
145
+ elsif w =~ %r[^(\w+)-(\w+)$]i
146
+ # Hypenated double word
147
+ newwords.push($1.capitalize + '-' + $2.capitalize)
148
+ elsif little_words.include?(w.downcase)
149
+ # Only capitalize at beginning or end
150
+ newwords.push((first_word or last_word) ? w.capitalize : w.downcase)
151
+ else
152
+ # All else
153
+ newwords.push(w.capitalize)
154
+ end
155
+ first_word = false
156
+ end
157
+ self[0..-1] = newwords.join(' ')
158
+ end
159
+
160
+ def entitle
161
+ self.dup.entitle!
162
+ end
163
+
164
+ # Thanks to Eugene at stackoverflow for the following.
165
+ # http://stackoverflow.com/questions/8806643/
166
+ # colorized-output-breaks-linewrapping-with-readline
167
+ # These color strings without confusing readline about the length of
168
+ # the prompt string in the shell. (Unlike the rainbow routines)
169
+ def console_red; colorize(self, "\001\e[1m\e[31m\002"); end
170
+ def console_dark_red; colorize(self, "\001\e[31m\002"); end
171
+ def console_green; colorize(self, "\001\e[1m\e[32m\002"); end
172
+ def console_dark_green; colorize(self, "\001\e[32m\002"); end
173
+ def console_yellow; colorize(self, "\001\e[1m\e[33m\002"); end
174
+ def console_dark_yellow; colorize(self, "\001\e[33m\002"); end
175
+ def console_blue; colorize(self, "\001\e[1m\e[34m\002"); end
176
+ def console_dark_blue; colorize(self, "\001\e[34m\002"); end
177
+ def console_purple; colorize(self, "\001\e[1m\e[35m\002"); end
178
+
179
+ def console_def; colorize(self, "\001\e[1m\002"); end
180
+ def console_bold; colorize(self, "\001\e[1m\002"); end
181
+ def console_blink; colorize(self, "\001\e[5m\002"); end
182
+
183
+ def colorize(text, color_code) "#{color_code}#{text}\001\e[0m\002" end
184
+ end
@@ -0,0 +1,17 @@
1
+ class Symbol
2
+ # Convert to capitalized string: :hello_world -> "Hello World"
3
+ def entitle
4
+ to_s.gsub('_', ' ').split(' ')
5
+ .join(' ')
6
+ .entitle
7
+ end
8
+ alias :to_string :entitle
9
+
10
+ def as_sym
11
+ self
12
+ end
13
+
14
+ def tex_quote
15
+ to_s.tex_quote
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module FatCore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,320 @@
1
+ require 'spec_helper'
2
+
3
+ describe Date do
4
+ before :each do
5
+ # Pretend it is this date. Not at beg or end of year, quarter,
6
+ # month, or week. It is a Wednesday
7
+ Date.stub(:current).and_return(Date.parse('2012-07-18'))
8
+ Date.stub(:today).and_return(Date.parse('2012-07-18'))
9
+ @test_today = Date.parse('2012-07-18')
10
+ end
11
+
12
+ describe "class methods" do
13
+
14
+ describe "parse_spec" do
15
+
16
+ it "should choke if spec type is neither :from or :to" do
17
+ expect {
18
+ Date.parse_spec('2011-07-15', :form)
19
+ }.to raise_error
20
+ end
21
+
22
+ it "should parse plain iso dates correctly" do
23
+ Date.parse_spec('2011-07-15').should eq Date.parse('2011-07-15')
24
+ Date.parse_spec('2011-08-05').should eq Date.parse('2011-08-05')
25
+ end
26
+
27
+ it "should parse week numbers such as 'W23' or '23W' correctly" do
28
+ Date.parse_spec('W1').should eq Date.parse('2012-01-02')
29
+ Date.parse_spec('W23').should eq Date.parse('2012-06-04')
30
+ Date.parse_spec('W23', :to).should eq Date.parse('2012-06-10')
31
+ Date.parse_spec('23W').should eq Date.parse('2012-06-04')
32
+ Date.parse_spec('23W', :to).should eq Date.parse('2012-06-10')
33
+ expect {
34
+ Date.parse_spec('W83', :to)
35
+ }.to raise_error
36
+ end
37
+
38
+ it "should parse year-week numbers such as 'YYYY-W23' or 'YYYY-23W' correctly" do
39
+ Date.parse_spec('2003-W1').should eq Date.parse('2002-12-30')
40
+ Date.parse_spec('2003-W1', :to).should eq Date.parse('2003-01-05')
41
+ Date.parse_spec('2003-W23').should eq Date.parse('2003-06-02')
42
+ Date.parse_spec('2003-W23', :to).should eq Date.parse('2003-06-08')
43
+ Date.parse_spec('2003-23W').should eq Date.parse('2003-06-02')
44
+ Date.parse_spec('2003-23W', :to).should eq Date.parse('2003-06-08')
45
+ expect {
46
+ Date.parse_spec('2003-W83', :to)
47
+ }.to raise_error
48
+ end
49
+
50
+ it "should parse year-quarter specs such as YYYY-NQ or YYYY-QN" do
51
+ Date.parse_spec('2011-4Q', :from).should eq Date.parse('2011-10-01')
52
+ Date.parse_spec('2011-4Q', :to).should eq Date.parse('2011-12-31')
53
+ Date.parse_spec('2011-Q4', :from).should eq Date.parse('2011-10-01')
54
+ Date.parse_spec('2011-Q4', :to).should eq Date.parse('2011-12-31')
55
+ expect { Date.parse_spec('2011-5Q') }.to raise_error
56
+ end
57
+
58
+ it "should parse quarter-only specs such as NQ or QN" do
59
+ Date.parse_spec('4Q', :from).should eq Date.parse('2012-10-01')
60
+ Date.parse_spec('4Q', :to).should eq Date.parse('2012-12-31')
61
+ Date.parse_spec('Q4', :from).should eq Date.parse('2012-10-01')
62
+ Date.parse_spec('Q4', :to).should eq Date.parse('2012-12-31')
63
+ expect { Date.parse_spec('5Q') }.to raise_error
64
+ end
65
+
66
+ it "should parse year-month specs such as YYYY-MM" do
67
+ Date.parse_spec('2010-5', :from).should eq Date.parse('2010-05-01')
68
+ Date.parse_spec('2010-5', :to).should eq Date.parse('2010-05-31')
69
+ expect { Date.parse_spec('2010-13') }.to raise_error
70
+ end
71
+
72
+ it "should parse month-only specs such as MM" do
73
+ Date.parse_spec('10', :from).should eq Date.parse('2012-10-01')
74
+ Date.parse_spec('10', :to).should eq Date.parse('2012-10-31')
75
+ expect { Date.parse_spec('99') }.to raise_error
76
+ expect { Date.parse_spec('011') }.to raise_error
77
+ end
78
+
79
+ it "should parse year-only specs such as YYYY" do
80
+ Date.parse_spec('2010', :from).should eq Date.parse('2010-01-01')
81
+ Date.parse_spec('2010', :to).should eq Date.parse('2010-12-31')
82
+ expect { Date.parse_spec('99999') }.to raise_error
83
+ end
84
+
85
+ it "should parse relative day names: today, yesterday" do
86
+ Date.parse_spec('today').should eq Date.current
87
+ Date.parse_spec('this_day').should eq Date.current
88
+ Date.parse_spec('yesterday').should eq Date.current - 1.day
89
+ Date.parse_spec('last_day').should eq Date.current - 1.day
90
+ end
91
+
92
+ it "should parse relative weeks: this_week, last_week" do
93
+ Date.parse_spec('this_week').should eq Date.parse('2012-07-16')
94
+ Date.parse_spec('this_week', :to).should eq Date.parse('2012-07-22')
95
+ Date.parse_spec('last_week').should eq Date.parse('2012-07-09')
96
+ Date.parse_spec('last_week', :to).should eq Date.parse('2012-07-15')
97
+ end
98
+
99
+ it "should parse relative biweeks: this_biweek, last_biweek" do
100
+ Date.parse_spec('this_biweek').should eq Date.parse('2012-07-16')
101
+ Date.parse_spec('this_biweek', :to).should eq Date.parse('2012-07-29')
102
+ Date.parse_spec('last_biweek').should eq Date.parse('2012-07-02')
103
+ Date.parse_spec('last_biweek', :to).should eq Date.parse('2012-07-15')
104
+ end
105
+
106
+ it "should parse relative months: this_semimonth, last_semimonth" do
107
+ Date.parse_spec('this_semimonth').should eq Date.parse('2012-07-16')
108
+ Date.parse_spec('this_semimonth', :to).should eq Date.parse('2012-07-31')
109
+ Date.parse_spec('last_semimonth').should eq Date.parse('2012-07-01')
110
+ Date.parse_spec('last_semimonth', :to).should eq Date.parse('2012-07-15')
111
+ end
112
+
113
+ it "should parse relative months: this_month, last_month" do
114
+ Date.parse_spec('this_month').should eq Date.parse('2012-07-01')
115
+ Date.parse_spec('this_month', :to).should eq Date.parse('2012-07-31')
116
+ Date.parse_spec('last_month').should eq Date.parse('2012-06-01')
117
+ Date.parse_spec('last_month', :to).should eq Date.parse('2012-06-30')
118
+ end
119
+
120
+ it "should parse relative bimonths: this_bimonth, last_bimonth" do
121
+ Date.parse_spec('this_bimonth').should eq Date.parse('2012-07-01')
122
+ Date.parse_spec('this_bimonth', :to).should eq Date.parse('2012-08-31')
123
+ Date.parse_spec('last_bimonth').should eq Date.parse('2012-05-01')
124
+ Date.parse_spec('last_bimonth', :to).should eq Date.parse('2012-06-30')
125
+ end
126
+
127
+ it "should parse relative quarters: this_quarter, last_quarter" do
128
+ Date.parse_spec('this_quarter').should eq Date.parse('2012-07-01')
129
+ Date.parse_spec('this_quarter', :to).should eq Date.parse('2012-09-30')
130
+ Date.parse_spec('last_quarter').should eq Date.parse('2012-04-01')
131
+ Date.parse_spec('last_quarter', :to).should eq Date.parse('2012-06-30')
132
+ end
133
+
134
+ it "should parse relative years: this_year, last_year" do
135
+ Date.parse_spec('this_year').should eq Date.parse('2012-01-01')
136
+ Date.parse_spec('this_year', :to).should eq Date.parse('2012-12-31')
137
+ Date.parse_spec('last_year').should eq Date.parse('2011-01-01')
138
+ Date.parse_spec('last_year', :to).should eq Date.parse('2011-12-31')
139
+ end
140
+
141
+ it "should parse forever and never" do
142
+ Date.parse_spec('forever').should eq Date::BOT
143
+ Date.parse_spec('forever', :to).should eq Date::EOT
144
+ Date.parse_spec('never').should be_nil
145
+ end
146
+ end
147
+
148
+ it "should be able to parse an American-style date" do
149
+ Date.parse_american('2/12/2011').iso.should eq('2011-02-12')
150
+ Date.parse_american('2 / 12/ 2011').iso.should eq('2011-02-12')
151
+ Date.parse_american('2 / 1 / 2011').iso.should eq('2011-02-01')
152
+ Date.parse_american(' 2 / 1 / 2011 ').iso.should eq('2011-02-01')
153
+ Date.parse_american(' 2 / 1 / 15 ').iso.should eq('2015-02-01')
154
+ end
155
+ end
156
+
157
+ describe "instance methods" do
158
+
159
+ it "should know if its a weekend of a weekday" do
160
+ expect(Date.parse('2014-05-17')).to be_weekend
161
+ expect(Date.parse('2014-05-17')).to_not be_weekday
162
+ expect(Date.parse('2014-05-18')).to be_weekend
163
+ expect(Date.parse('2014-05-18')).to_not be_weekday
164
+
165
+ expect(Date.parse('2014-05-22')).to be_weekday
166
+ expect(Date.parse('2014-05-22')).to_not be_weekend
167
+ end
168
+
169
+ it "should know its pred and succ (for Range)" do
170
+ Date.today.pred.should eq (Date.today - 1)
171
+ Date.today.succ.should eq (Date.today + 1)
172
+ end
173
+
174
+ it "should be able to print itself as an American-style date" do
175
+ Date.parse('2011-02-12').american.should eq('2/12/2011')
176
+ end
177
+
178
+ it "should be able to print itself in iso form" do
179
+ Date.today.iso.should == '2012-07-18'
180
+ end
181
+
182
+ it "should be able to print itself in org form" do
183
+ Date.today.org.should eq('[2012-07-18 Wed]')
184
+ (Date.today + 1.day).org.should eq('[2012-07-19 Thu]')
185
+ end
186
+
187
+ it "should be able to print itself in eng form" do
188
+ Date.today.eng.should eq('July 18, 2012')
189
+ (Date.today + 1.day).eng.should eq('July 19, 2012')
190
+ end
191
+
192
+ it "should be able to state its quarter" do
193
+ Date.today.quarter.should eq(3)
194
+ Date.parse('2012-02-29').quarter.should eq(1)
195
+ Date.parse('2012-01-01').quarter.should eq(1)
196
+ Date.parse('2012-03-31').quarter.should eq(1)
197
+ Date.parse('2012-04-01').quarter.should eq(2)
198
+ Date.parse('2012-05-15').quarter.should eq(2)
199
+ Date.parse('2012-06-30').quarter.should eq(2)
200
+ Date.parse('2012-07-01').quarter.should eq(3)
201
+ Date.parse('2012-08-15').quarter.should eq(3)
202
+ Date.parse('2012-09-30').quarter.should eq(3)
203
+ Date.parse('2012-10-01').quarter.should eq(4)
204
+ Date.parse('2012-11-15').quarter.should eq(4)
205
+ Date.parse('2012-12-31').quarter.should eq(4)
206
+ end
207
+
208
+ it "should know about years" do
209
+ Date.parse('2013-01-01').should be_beginning_of_year
210
+ Date.parse('2013-12-31').should be_end_of_year
211
+ Date.parse('2013-04-01').should_not be_beginning_of_year
212
+ Date.parse('2013-12-30').should_not be_end_of_year
213
+ end
214
+
215
+ it "should know about quarters" do
216
+ Date.parse('2013-01-01').should be_beginning_of_quarter
217
+ Date.parse('2013-12-31').should be_end_of_quarter
218
+ Date.parse('2013-04-01').should be_beginning_of_quarter
219
+ Date.parse('2013-06-30').should be_end_of_quarter
220
+ Date.parse('2013-05-01').should_not be_beginning_of_quarter
221
+ Date.parse('2013-07-31').should_not be_end_of_quarter
222
+ end
223
+
224
+ it "should know about bimonths" do
225
+ Date.parse('2013-11-04').beginning_of_bimonth.should eq Date.parse('2013-11-01')
226
+ Date.parse('2013-11-04').end_of_bimonth.should eq Date.parse('2013-12-31')
227
+ Date.parse('2013-03-01').should be_beginning_of_bimonth
228
+ Date.parse('2013-04-30').should be_end_of_bimonth
229
+ Date.parse('2013-01-01').should be_beginning_of_bimonth
230
+ Date.parse('2013-12-31').should be_end_of_bimonth
231
+ Date.parse('2013-05-01').should be_beginning_of_bimonth
232
+ Date.parse('2013-06-30').should be_end_of_bimonth
233
+ Date.parse('2013-06-01').should_not be_beginning_of_bimonth
234
+ Date.parse('2013-07-31').should_not be_end_of_bimonth
235
+ end
236
+
237
+ it "should know about months" do
238
+ Date.parse('2013-01-01').should be_beginning_of_month
239
+ Date.parse('2013-12-31').should be_end_of_month
240
+ Date.parse('2013-05-01').should be_beginning_of_month
241
+ Date.parse('2013-07-31').should be_end_of_month
242
+ Date.parse('2013-05-02').should_not be_beginning_of_month
243
+ Date.parse('2013-07-30').should_not be_end_of_month
244
+ end
245
+
246
+ it "should know about semimonths" do
247
+ Date.parse('2013-11-24').beginning_of_semimonth.should eq Date.parse('2013-11-16')
248
+ Date.parse('2013-11-04').beginning_of_semimonth.should eq Date.parse('2013-11-01')
249
+ Date.parse('2013-11-04').end_of_semimonth.should eq Date.parse('2013-11-15')
250
+ Date.parse('2013-11-24').end_of_semimonth.should eq Date.parse('2013-11-30')
251
+ Date.parse('2013-03-01').should be_beginning_of_semimonth
252
+ Date.parse('2013-03-16').should be_beginning_of_semimonth
253
+ Date.parse('2013-04-15').should be_end_of_semimonth
254
+ Date.parse('2013-04-30').should be_end_of_semimonth
255
+ end
256
+
257
+ it "should know about biweeks" do
258
+ Date.parse('2013-11-07').beginning_of_biweek.should eq Date.parse('2013-11-04')
259
+ Date.parse('2013-11-07').end_of_biweek.should eq Date.parse('2013-11-17')
260
+ Date.parse('2013-03-11').should be_beginning_of_biweek
261
+ Date.parse('2013-03-24').should be_end_of_biweek
262
+ end
263
+
264
+ it "should know about weeks" do
265
+ Date.parse('2013-11-04').should be_beginning_of_week
266
+ Date.parse('2013-11-10').should be_end_of_week
267
+ Date.parse('2013-12-02').should be_beginning_of_week
268
+ Date.parse('2013-12-08').should be_end_of_week
269
+ Date.parse('2013-10-13').should_not be_beginning_of_week
270
+ Date.parse('2013-10-19').should_not be_end_of_week
271
+ end
272
+
273
+ it "should know the beginning of chunks" do
274
+ Date.parse('2013-11-04').beginning_of_chunk(:year).should eq Date.parse('2013-01-01')
275
+ Date.parse('2013-11-04').beginning_of_chunk(:quarter).should eq Date.parse('2013-10-01')
276
+ Date.parse('2013-12-04').beginning_of_chunk(:bimonth).should eq Date.parse('2013-11-01')
277
+ Date.parse('2013-11-04').beginning_of_chunk(:month).should eq Date.parse('2013-11-01')
278
+ Date.parse('2013-11-04').beginning_of_chunk(:semimonth).should eq Date.parse('2013-11-01')
279
+ Date.parse('2013-11-24').beginning_of_chunk(:semimonth).should eq Date.parse('2013-11-16')
280
+ Date.parse('2013-11-08').beginning_of_chunk(:biweek).should eq Date.parse('2013-11-04')
281
+ Date.parse('2013-11-08').beginning_of_chunk(:week).should eq Date.parse('2013-11-04')
282
+ expect {
283
+ Date.parse('2013-11-04').beginning_of_chunk(:wek)
284
+ }.to raise_error
285
+ end
286
+
287
+ it "should know the end of chunks" do
288
+ Date.parse('2013-07-04').end_of_chunk(:year).should eq Date.parse('2013-12-31')
289
+ Date.parse('2013-07-04').end_of_chunk(:quarter).should eq Date.parse('2013-09-30')
290
+ Date.parse('2013-12-04').end_of_chunk(:bimonth).should eq Date.parse('2013-12-31')
291
+ Date.parse('2013-07-04').end_of_chunk(:month).should eq Date.parse('2013-07-31')
292
+ Date.parse('2013-11-04').end_of_chunk(:semimonth).should eq Date.parse('2013-11-15')
293
+ Date.parse('2013-11-24').end_of_chunk(:semimonth).should eq Date.parse('2013-11-30')
294
+ Date.parse('2013-11-08').end_of_chunk(:biweek).should eq Date.parse('2013-11-17')
295
+ Date.parse('2013-07-04').end_of_chunk(:week).should eq Date.parse('2013-07-07')
296
+ expect {
297
+ Date.parse('2013-11-04').end_of_chunk(:wek)
298
+ }.to raise_error
299
+ end
300
+
301
+ it "should know how to expand to chunk periods" do
302
+ Date.parse('2013-07-04').expand_to_period(:year).
303
+ should eq Period.new(Date.parse('2013-01-01'), Date.parse('2013-12-31'))
304
+ Date.parse('2013-07-04').expand_to_period(:quarter).
305
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-09-30'))
306
+ Date.parse('2013-07-04').expand_to_period(:bimonth).
307
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-08-31'))
308
+ Date.parse('2013-07-04').expand_to_period(:month).
309
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-07-31'))
310
+ Date.parse('2013-07-04').expand_to_period(:semimonth).
311
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-07-15'))
312
+ Date.parse('2013-07-04').expand_to_period(:biweek).
313
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-07-14'))
314
+ Date.parse('2013-07-04').expand_to_period(:week).
315
+ should eq Period.new(Date.parse('2013-07-01'), Date.parse('2013-07-07'))
316
+ Date.parse('2013-07-04').expand_to_period(:day).
317
+ should eq Period.new(Date.parse('2013-07-04'), Date.parse('2013-07-04'))
318
+ end
319
+ end
320
+ end