fat_core 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,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