by_star 0.2.5

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.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ tmp
2
+ spec/fixtures/database.yml
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,271 @@
1
+ # by_*
2
+
3
+
4
+ by_* (byStar) is a plugin that allows you to find ActiveRecord objects given certain date objects. This was originally crafted for only finding objects within a given month, but now has extended out to much more. It now supports finding objects for:
5
+
6
+ * A given year
7
+ * A given month
8
+ * A given fortnight
9
+ * A given week
10
+ * A given weekend
11
+ * A given day
12
+ * The current weekend
13
+ * The current work week
14
+ * Between certain times
15
+ * As of a certain time
16
+ * Up to a certain time
17
+
18
+
19
+ It also allows you to do nested finds on the records returned which I personally think is the coolest feature of the whole plugin:
20
+
21
+ Post.by_month(1) do
22
+ { :include => "tags", :conditions => ["tags.name = ?", 'ruby'] }
23
+ end
24
+
25
+ If you're not using the standard `created_at` field: don't worry! I've covered that scenario too.
26
+
27
+
28
+ ## By Year (`by_year`)
29
+
30
+ To find records based on a year you can pass it a two or four digit number:
31
+
32
+ Post.by_year(09)
33
+
34
+ This will return all posts in 2009, whereas:
35
+
36
+ Post.by_year(99)
37
+
38
+ will return all the posts in the year 1999.
39
+
40
+ ## By Month (`by_month`)
41
+
42
+ If you know the number of the month you want:
43
+
44
+ Post.by_month(1)
45
+
46
+ This will return all posts in the first month (January) of the current year.
47
+
48
+ If you like being verbose:
49
+
50
+ Post.by_month("January")
51
+
52
+ This will return all posts created in January of the current year.
53
+
54
+ If you want to find all posts in January of last year just do
55
+
56
+ Post.by_month(1, :year => 2007)
57
+
58
+ or
59
+
60
+ Post.by_month("January", :year => 2007)
61
+
62
+ This will perform a find using the column you've specified.
63
+
64
+ If you have a Time object you can use it to find the posts:
65
+
66
+ Post.by_month(Time.local(2008, 11, 24))
67
+
68
+ This will find all the posts in November 2008.
69
+
70
+ ## By Fortnight (`by_fortnight`)
71
+
72
+ Fortnight numbering starts at 0. The beginning of a fortnight is Monday, 12am.
73
+
74
+ To find records from the current fortnight:
75
+
76
+ Post.by_fortnight
77
+
78
+ To find records based on a fortnight, you can pass in a number (representing the fortnight number) or a time object:
79
+
80
+ Post.by_fortnight(18)
81
+
82
+ This will return all posts in the 18th fortnight of the current year.
83
+
84
+ Post.by_fortnight(18, :year => 2008)
85
+
86
+ This will return all posts in the 18th fortnight week of 2008.
87
+
88
+ Post.by_fortnight(Time.local(2008,1,1))
89
+
90
+ This will return all posts from the first fortnight of 2008.
91
+
92
+ ## By Week (`by_week`)
93
+
94
+ Week numbering starts at 0. The beginning of a week is Monday, 12am.
95
+
96
+ To find records from the current week:
97
+
98
+ Post.by_week
99
+
100
+ To find records based on a week, you can pass in a number (representing the week number) or a time object:
101
+
102
+ Post.by_week(36)
103
+
104
+ This will return all posts in the 36th week of the current year.
105
+
106
+ Post.by_week(36, :year => 2008)
107
+
108
+ This will return all posts in the 36th week of 2008.
109
+
110
+ Post.by_week(Time.local(2008,1,1))
111
+
112
+ This will return all posts from the first week of 2008.
113
+
114
+ ## By Weekend (`by_weekend`)
115
+
116
+ If the time passed in (or the time now is a weekend) it will return posts from 12am Saturday to 11:59:59PM Sunday. If the time is a week day, it will show all posts for the coming weekend.
117
+
118
+ Post.by_weekend(Time.now)
119
+
120
+ ## By Day (`by_day` or `today`)
121
+
122
+ To find records for today:
123
+
124
+ Post.by_day
125
+ Post.today
126
+
127
+ To find records for a certain day:
128
+
129
+ Post.by_day(Time.local(2008, 1, 1))
130
+
131
+ You can also pass a string:
132
+
133
+ Post.by_day("next tuesday")
134
+
135
+ This will return all posts for the given day.
136
+
137
+ ## Current Weekend (`by_current_weekend`)
138
+
139
+ If you are currently in a weekend (between 3pm Friday and 3am Monday) this will find all records starting at 3pm the previous Friday up until 3am, Monday.
140
+
141
+ If you are not in a weekend (between 3am Monday and 3pm Friday) this will find all records from the next Friday 3pm to the following Monday 3am.
142
+
143
+ ## Current Work Week (`by_current_work_week`)
144
+
145
+ If you are currently in a work week (between 3am Monday and 3pm Friday) this will find all records in that range. If you are currently in a weekend (between 3pm Friday and 3am Monday) this will return all records in the upcoming work week.
146
+
147
+
148
+ ## Tomorrow (`tomorrow`)
149
+
150
+ *This method has been shown to be shifty when passed a `Date` object, it is recommended that you pass it a `Time` object instead.*
151
+
152
+ To find all posts from the day after the current date:
153
+
154
+ Post.tomorrow
155
+
156
+ To find all posts after a given Date or Time object:
157
+
158
+ Post.tomorrow(Date.today + 2)
159
+ Post.tomorrow(Time.now + 5.days)
160
+
161
+ You can also pass a string:
162
+
163
+ Post.tomorrow("next tuesday")
164
+
165
+ ## Yesterday (`yesterday`)
166
+
167
+ *This method has been shown to be shifty when passed a `Date` object, it is recommended that you pass it a `Time` object instead.*
168
+
169
+ To find all posts from the day before the current date:
170
+
171
+ Post.yesterday
172
+
173
+ To find all posts before a given Date or Time object:
174
+
175
+ Post.yesterday(Date.today + 2)
176
+ Post.yesterday(Time.now + 5.days)
177
+
178
+ You can also pass a string:
179
+
180
+ Post.yesterday("next tuesday")
181
+
182
+ ## Past (`past`)
183
+
184
+ To find all posts before the current time:
185
+
186
+ Post.past
187
+
188
+ To find all posts before certain time or date:
189
+
190
+ Post.past(Date.today + 2)
191
+ Post.past(Time.now + 5.days)
192
+
193
+ You can also pass a string:
194
+
195
+ Post.past("next tuesday")
196
+
197
+ ## Future (`future`)
198
+
199
+ To find all posts after the current time:
200
+
201
+ Post.future
202
+
203
+ To find all posts after certain time or date:
204
+
205
+ Post.future(Date.today + 2)
206
+ Post.future(Time.now + 5.days)
207
+
208
+ You can also pass a string:
209
+
210
+ Post.future("next tuesday")
211
+
212
+ ## Between (`between`)
213
+
214
+ To find records between two times:
215
+
216
+ Post.between(time1, time2)
217
+
218
+ Also works with dates:
219
+
220
+ Post.between(date1, date2)
221
+
222
+ And with strings:
223
+
224
+ Post.between("last tuesday", "next wednesday")
225
+
226
+ ## As of (`as_of_<dynamic>`)
227
+
228
+ To find records as of a certain date up until the current time:
229
+
230
+ Post.as_of_2_weeks_ago
231
+
232
+ This uses the Chronic "human mind reading" (read: it's really good at determining what time you mean using written English) library to work it out.
233
+
234
+ ## Up to (`up_to_<dynamic>`)
235
+
236
+ To find records up to a certain time from the current time:
237
+
238
+ Post.up_to_6_weeks_from_now
239
+
240
+ ## Not using created_at? No worries!
241
+
242
+ If your database uses something other than `created_at` for storing a timestamp, you can specify the field option like this:
243
+
244
+ Post.by_month("January", :field => :something_else)
245
+
246
+ All methods support this extra option.
247
+
248
+ ## Scoping the find
249
+
250
+ All the `by_*` methods takes a block which will then scope the find based on the options passed into it. The supported options are the same options that are supported by `ActiveRecord::Base.find`:
251
+
252
+ Post.by_month(1) do
253
+ { :include => "tags", :conditions => ["tags.name = ?", 'ruby'] }
254
+ end
255
+ ## "Chronicable string"
256
+
257
+ This means a string that can be parsed with the Chronic gem.
258
+
259
+ ## Collaborators
260
+
261
+ Unfortunately I forget who exactly prompted me to write the plugin, but I would like to thank #rubyonrails for their support and the following people:
262
+
263
+ * Mislav Marohnic
264
+ * August Lilleas (leethal)
265
+ * gte351s
266
+ * Thomase Sinclair (anathematic)
267
+ * The dude(s) & gal(s) who created Chronic
268
+
269
+ ## Suggestions?
270
+
271
+ If you have suggestions, please contact me at radarlistener@gmail.com
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "by_star"
8
+ gem.summary = %Q{ActiveRecord extension for easier date scopes and time ranges}
9
+ gem.description = %Q{ActiveRecord extension for easier date scopes and time ranges}
10
+ gem.email = "radarlistener@gmail.com"
11
+ gem.homepage = "http://github.com/radar/by_star"
12
+ gem.authors = ["Ryan Bigg", "Mislav Marohnić"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "by_star #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
50
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.5
data/by_star.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{by_star}
8
+ s.version = "0.2.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ryan Bigg", "Mislav Marohni\304\207"]
12
+ s.date = %q{2009-10-15}
13
+ s.description = %q{ActiveRecord extension for easier date scopes and time ranges}
14
+ s.email = %q{radarlistener@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.markdown",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "by_star.gemspec",
25
+ "lib/by_star.rb",
26
+ "rails/init.rb",
27
+ "spec/by_star_spec.rb",
28
+ "spec/fixtures/models.rb",
29
+ "spec/fixtures/schema.rb",
30
+ "spec/spec_helper.rb",
31
+ "tmp/.gitignore"
32
+ ]
33
+ s.homepage = %q{http://github.com/radar/by_star}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.5}
37
+ s.summary = %q{ActiveRecord extension for easier date scopes and time ranges}
38
+ s.test_files = [
39
+ "spec/by_star_spec.rb",
40
+ "spec/fixtures/models.rb",
41
+ "spec/fixtures/schema.rb",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<rspec>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<rspec>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<rspec>, [">= 0"])
56
+ end
57
+ end
data/lib/by_star.rb ADDED
@@ -0,0 +1,316 @@
1
+ require 'chronic'
2
+ module ByStar
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # Examples:
10
+ # by_year(2010)
11
+ # # 2-digit year:
12
+ # by_year(10)
13
+ # # Time or Date object:
14
+ # by_year(time)
15
+ # # String:
16
+ # by_year("2010")
17
+ def by_year(time=Time.zone.now.year, options={}, &block)
18
+ year = work_out_year(time)
19
+
20
+ start_time = Time.utc(year, 1, 1)
21
+ end_time = start_time.end_of_year
22
+ by_star(start_time, end_time, options, &block)
23
+ rescue ArgumentError
24
+ raise ParseError, "Invalid arguments detected, year may possibly be outside of valid range (1902-2039). This is no longer a problem on Ruby versions > 1.8.7, so we recommend you upgrade to at least 1.8.7."
25
+ end
26
+
27
+ # Examples:
28
+ # by_month(1)
29
+ # by_month("January")
30
+ # by_month("January", :year => 2008)
31
+ # by_month(time)
32
+ def by_month(time=Time.zone.now.month, options={}, &block)
33
+ time = Time.zone.now.month if time.nil?
34
+ year = options[:year] ||= Time.zone.now.year
35
+ # Work out what actual month is.
36
+ month = if time.is_a?(Numeric) && (1..12).include?(time)
37
+ time
38
+ elsif valid_time_or_date?(time)
39
+ year = time.year
40
+ time.month
41
+ elsif time.is_a?(String) && Date::MONTHNAMES.include?(time)
42
+ Date::MONTHNAMES.index(time)
43
+ else
44
+ raise ParseError, "Value is not an integer (between 1 and 12), time object or string (make sure you typed the name right)."
45
+ end
46
+
47
+ start_time = Time.utc(year, month, 1)
48
+ end_time = start_time.end_of_month
49
+
50
+ by_star(start_time, end_time, options, &block)
51
+ end
52
+
53
+ # Examples:
54
+ # # 18th fortnight of 2004
55
+ # Post.by_fortnight(18, :year => 2004)
56
+ def by_fortnight(time=Time.zone.now, options = {}, &block)
57
+ time = parse(time)
58
+
59
+ # If options[:year] is passed in, use that year regardless.
60
+ year = work_out_year(options[:year]) if options[:year]
61
+ # If the first argument is a date or time, ask it for the year
62
+ year ||= time.year unless time.is_a?(Numeric)
63
+ # If the first argument is a fixnum, assume this year.
64
+ year ||= Time.zone.now.year
65
+
66
+ # Dodgy!
67
+ # Surely there's a method in Rails to do this.
68
+ start_time = if valid_time_or_date?(time)
69
+ time.beginning_of_year + (time.strftime("%U").to_i).weeks
70
+ elsif time.is_a?(Numeric) && time <= 26
71
+ Time.utc(year, 1, 1) + ((time.to_i) * 2).weeks
72
+ else
73
+ raise ParseError, "by_fortnight takes only a Time or Date object, a Fixnum (less than or equal to 26) or a Chronicable string."
74
+ end
75
+ start_time = start_time.beginning_of_week
76
+ end_time = start_time + 2.weeks
77
+ by_star(start_time, end_time, options, &block)
78
+ end
79
+
80
+ # Examples:
81
+ # # 36th week
82
+ # Post.by_week(36)
83
+ # Post.by_week(36.54)
84
+ # Post.by_week(36, :year => 2004)
85
+ # Post.by_week(<Time object>)
86
+ # Post.by_week(<Date object>)
87
+ # Post.by_week("next tuesday")
88
+ def by_week(time=Time.zone.now, options = {}, &block)
89
+ time = parse(time)
90
+
91
+ # If options[:year] is passed in, use that year regardless.
92
+ year = work_out_year(options[:year]) if options[:year]
93
+ # If the first argument is a date or time, ask it for the year
94
+ year ||= time.year unless time.is_a?(Numeric)
95
+ # If the first argument is a fixnum, assume this year.
96
+ year ||= Time.now.year
97
+
98
+ # Dodgy!
99
+ # Surely there's a method in Rails to do this.
100
+ start_time = if valid_time_or_date?(time)
101
+ weeks = time.strftime("%U").to_i
102
+ time.beginning_of_year
103
+ elsif time.is_a?(Numeric) && time < 53
104
+ weeks = time.to_i
105
+ Time.utc(year, 1, 1)
106
+ else
107
+ raise ParseError, "by_week takes only a Time or Date object, a Fixnum (less than or equal to 53) or a Chronicable string."
108
+ end
109
+ start_time += weeks.weeks
110
+ end_time = start_time + 1.week
111
+ by_star(start_time, end_time, options, &block)
112
+ end
113
+
114
+
115
+ # Examples:
116
+ # Post.by_weekend
117
+ # Post.by_weekend(Time.now + 5.days)
118
+ # Post.by_weekend(Date.today + 5)
119
+ # Post.by_weekend("next tuesday")
120
+ def by_weekend(time=Time.zone.now, options = {}, &block)
121
+ time = parse(time)
122
+ start_time = time.beginning_of_weekend
123
+ by_star(start_time, (start_time + 1.day).end_of_day, options, &block)
124
+ end
125
+
126
+
127
+ # Examples:
128
+ # Post.by_current_weekend
129
+ def by_current_weekend(options = {}, &block)
130
+ time = Time.zone.now
131
+ # Friday, 3pm
132
+ start_time = time.beginning_of_weekend
133
+ # Monday, 3am
134
+ end_time = time.end_of_weekend
135
+ by_star(start_time, end_time, options, &block)
136
+ end
137
+
138
+ # Examples:
139
+ # Post.by_current_work_week
140
+ def by_current_work_week(options = {}, &block)
141
+ time = Time.zone.now
142
+ # Monday, 3am
143
+ time = time + 1.week if time.wday == 6 || time.wday == 0
144
+ start_time = time.beginning_of_week + 3.hours
145
+ # Friday, 3pm
146
+ end_time = time.beginning_of_weekend
147
+ by_star(start_time, end_time, options, &block)
148
+ end
149
+
150
+
151
+ # Examples:
152
+ # Post.by_day
153
+ # Post.by_day(Time.yesterday)
154
+ # Post.by_day("next tuesday")
155
+ def by_day(time = Time.zone.now, options = {}, &block)
156
+ time = parse(time)
157
+ by_star(time.beginning_of_day, time.end_of_day, options, &block)
158
+ end
159
+ alias_method :today, :by_day
160
+
161
+ # Examples:
162
+ # Post.yesterday
163
+ # # 2 days ago:
164
+ # Post.yesterday(Time.yesterday)
165
+ # # day before next tuesday
166
+ # Post.yesterday("next tuesday")
167
+ def yesterday(time = Time.zone.now, options = {}, &block)
168
+ time = parse(time)
169
+ by_day(time.advance(:days => -1), options, &block)
170
+ end
171
+
172
+ # Examples:
173
+ # Post.tomorrow
174
+ # # 2 days from now:
175
+ # Post.tomorrow(Time.tomorrow)
176
+ # # day after next tuesday
177
+ # Post.tomorrow("next tuesday")
178
+ def tomorrow(time = Time.zone.now, options = {}, &block)
179
+ time = parse(time)
180
+ by_day(time.advance(:days => 1), options, &block)
181
+ end
182
+
183
+ # Scopes to records older than current or given time
184
+ # Post.past
185
+ # Post.past()
186
+ def past(time = Time.zone.now, options = {}, &block)
187
+ time = parse(time)
188
+ by_direction("<", time, options, &block)
189
+ end
190
+
191
+ # Scopes to records newer than current or given time
192
+ def future(time = Time.zone.now, options = {}, &block)
193
+ time = parse(time)
194
+ by_direction(">", time, options, &block)
195
+ end
196
+
197
+ private
198
+
199
+ def by_direction(condition, time, options = {}, &block)
200
+ field = connection.quote_table_name(table_name)
201
+ field << "." << connection.quote_column_name(options[:field] || "created_at")
202
+ with_scope(:find => { :conditions => ["#{field} #{condition} ?", time.utc] }) do
203
+ if block_given?
204
+ with_scope(:find => block.call) do
205
+ find(:all)
206
+ end
207
+ else
208
+ find(:all)
209
+ end
210
+ end
211
+ end
212
+
213
+ # scopes results between start_time and end_time
214
+ def by_star(start_time, end_time, options = {}, &block)
215
+ start_time = parse(start_time)
216
+ end_time = parse(end_time)
217
+
218
+ raise ParseError, "End time is before start time, searching like this will return no results." if end_time < start_time
219
+
220
+ field = options[:field] || "created_at"
221
+ with_scope(:find => { :conditions => { field => start_time.utc..end_time.utc } }) do
222
+ if block_given?
223
+ with_scope(:find => block.call) do
224
+ find(:all)
225
+ end
226
+ else
227
+ find(:all)
228
+ end
229
+ end
230
+ end
231
+
232
+ alias :between :by_star
233
+ public :between
234
+
235
+ # This will work for the next 30 years (written in 2009)
236
+ def work_out_year(value)
237
+ case value
238
+ when 0..39
239
+ 2000 + value
240
+ when 40..99
241
+ 1900 + value
242
+ when nil
243
+ Time.zone.now.year
244
+ else
245
+ # We may be passed something that's not a straight out integer
246
+ # These things include: BigDecimals, Floats and Strings.
247
+ value.to_i
248
+ end
249
+ end
250
+
251
+ # Checks if the object is a Time, Date or TimeWithZone object.
252
+ def valid_time_or_date?(value)
253
+ value.is_a?(Time) || value.is_a?(Date) || value.is_a?(ActiveSupport::TimeWithZone)
254
+ end
255
+
256
+ def parse(object)
257
+ object = case object.class.to_s
258
+ when "NilClass"
259
+ o = Time.zone.now
260
+ when "String"
261
+ o = object
262
+ Chronic.parse(object, :now => Time.zone.now)
263
+ when "Date"
264
+ object.to_time(:utc)
265
+ else
266
+ object
267
+ end
268
+ raise ParseError, "Chronic couldn't work out #{o.inspect}; please be more precise." if object.nil?
269
+ object
270
+ end
271
+
272
+ def method_missing(method, *args)
273
+ if method.to_s =~ /^(as_of|up_to)_(.+)$/
274
+ method = $1
275
+ expr = $2.humanize
276
+ unless time = parse(expr)
277
+ raise ParseError, "Chronic couldn't work out #{expr.inspect}; please be more precise."
278
+ end
279
+
280
+ reference = args.first || Time.now
281
+
282
+ if "as_of" == method
283
+ between(time, reference)
284
+ else
285
+ between(reference, time)
286
+ end
287
+ else
288
+ super
289
+ end
290
+ end
291
+ end
292
+
293
+ class ParseError < Exception; end
294
+ class MonthNotFound < Exception; end
295
+ end
296
+
297
+ class Time
298
+ def beginning_of_weekend
299
+ friday = case self.wday
300
+ when 0
301
+ self.end_of_week.beginning_of_day.advance(:days => -2)
302
+ when 5
303
+ self.beginning_of_day
304
+ else
305
+ self.beginning_of_week.advance(:days => 4)
306
+ end
307
+ # 3pm, Friday.
308
+ (friday + 15.hours)
309
+ end
310
+
311
+ def end_of_weekend
312
+ # 3am, Monday.
313
+ # LOL I CHEATED.
314
+ beginning_of_weekend + 3.days - 12.hours
315
+ end
316
+ end