by_star 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
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