by_star 1.0.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +23 -0
- data/Gemfile +1 -7
- data/Gemfile.lock +77 -29
- data/README.markdown +28 -172
- data/by_star.gemspec +6 -0
- data/lib/by_star.rb +48 -28
- data/lib/by_star/by_day.rb +30 -0
- data/lib/by_star/by_direction.rb +52 -0
- data/lib/by_star/by_fortnight.rb +54 -0
- data/lib/by_star/by_month.rb +35 -0
- data/lib/by_star/by_week.rb +30 -0
- data/lib/by_star/by_weekend.rb +17 -0
- data/lib/by_star/by_year.rb +60 -0
- data/lib/by_star/instance_methods.rb +13 -0
- data/lib/by_star/time_ext.rb +2 -1
- data/lib/by_star/version.rb +1 -1
- data/spec/by_star/by_day_spec.rb +52 -0
- data/spec/by_star/by_direction_spec.rb +82 -0
- data/spec/by_star/by_fortnight_spec.rb +46 -0
- data/spec/by_star/by_month_spec.rb +60 -0
- data/spec/by_star/by_week_spec.rb +39 -0
- data/spec/by_star/by_weekend_spec.rb +12 -0
- data/spec/by_star/by_year_spec.rb +57 -0
- data/spec/database.yml +13 -7
- data/spec/fixtures/models.rb +7 -60
- data/spec/spec_helper.rb +13 -7
- data/spec/time_ext_spec.rb +10 -0
- metadata +114 -59
- data/init.rb +0 -2
- data/lib/by_star/calculations.rb +0 -23
- data/lib/by_star/calculations/count.rb +0 -20
- data/lib/by_star/calculations/sum.rb +0 -25
- data/lib/by_star/neighbours.rb +0 -15
- data/lib/by_star/range_calculations.rb +0 -23
- data/lib/by_star/shared.rb +0 -18
- data/lib/by_star/vanilla.rb +0 -276
- data/spec/by_star_spec.rb +0 -722
data/by_star.gemspec
CHANGED
@@ -15,6 +15,12 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.rubyforge_project = "by_star"
|
16
16
|
|
17
17
|
s.add_development_dependency "bundler", ">= 1.0.0"
|
18
|
+
s.add_development_dependency "sqlite3"
|
19
|
+
s.add_development_dependency "pg"
|
20
|
+
s.add_development_dependency "mysql2"
|
21
|
+
s.add_development_dependency "rspec-rails", "~> 2.8"
|
22
|
+
s.add_development_dependency "timecop", "~> 0.3"
|
23
|
+
|
18
24
|
s.add_dependency "activerecord", ">= 2.0.0"
|
19
25
|
s.add_dependency "chronic"
|
20
26
|
|
data/lib/by_star.rb
CHANGED
@@ -1,43 +1,63 @@
|
|
1
1
|
require 'chronic'
|
2
2
|
|
3
|
-
require 'by_star/shared'
|
4
|
-
require 'by_star/range_calculations'
|
5
3
|
require 'by_star/time_ext'
|
6
|
-
require 'by_star/
|
7
|
-
require 'by_star/neighbours'
|
4
|
+
require 'by_star/instance_methods'
|
8
5
|
|
9
|
-
require 'by_star/
|
10
|
-
require 'by_star/
|
11
|
-
|
12
|
-
require 'by_star/
|
6
|
+
require 'by_star/by_direction'
|
7
|
+
require 'by_star/by_year'
|
8
|
+
require 'by_star/by_month'
|
9
|
+
require 'by_star/by_fortnight'
|
10
|
+
require 'by_star/by_week'
|
11
|
+
require 'by_star/by_weekend'
|
12
|
+
require 'by_star/by_day'
|
13
13
|
|
14
14
|
module ByStar
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
def by_star_field(field=nil)
|
17
|
+
@by_star_field ||= field
|
18
|
+
@by_star_field || "created_at"
|
19
|
+
end
|
20
|
+
|
21
|
+
include ByDirection
|
22
|
+
include ByYear
|
23
|
+
include ByMonth
|
24
|
+
include ByFortnight
|
25
|
+
include ByWeek
|
26
|
+
include ByWeekend
|
27
|
+
include ByDay
|
28
|
+
|
29
|
+
class ParseError < StandardError
|
30
|
+
|
25
31
|
end
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
# Returns all records between a given start and finish time.
|
34
|
+
#
|
35
|
+
# Currently only supports Time objects.
|
36
|
+
def between(start, finish, options={})
|
37
|
+
field = options[:field] || by_star_field
|
38
|
+
scope = where("#{field} >= ? AND #{field} <= ?",
|
39
|
+
start, finish)
|
40
|
+
scope = scope.order(options[:order]) if options[:order]
|
41
|
+
scope
|
32
42
|
end
|
33
43
|
|
34
|
-
|
35
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
# Used inside the by_* methods to determine what kind of object "time" is.
|
47
|
+
# These methods take the result of the time_klass method, and call other methods
|
48
|
+
# using it, such as by_year_Time and by_year_String.
|
49
|
+
def time_klass(time)
|
50
|
+
case time
|
51
|
+
when ActiveSupport::TimeWithZone
|
52
|
+
Time
|
53
|
+
else
|
54
|
+
time.class
|
55
|
+
end
|
36
56
|
end
|
37
57
|
|
38
|
-
class ParseError < Exception; end
|
39
|
-
class MonthNotFound < Exception; end
|
40
58
|
end
|
41
59
|
|
42
|
-
ActiveRecord::Base.send :
|
43
|
-
ActiveRecord::Relation.send :
|
60
|
+
ActiveRecord::Base.send :extend, ByStar
|
61
|
+
ActiveRecord::Relation.send :extend, ByStar
|
62
|
+
|
63
|
+
ActiveRecord::Base.send :include, ByStar::InstanceMethods
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByDay
|
3
|
+
def by_day(*args)
|
4
|
+
options = args.extract_options!.symbolize_keys!
|
5
|
+
time = args.first
|
6
|
+
time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
|
7
|
+
time ||= Time.zone.now
|
8
|
+
send("by_day_#{time_klass(time)}", time, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def today(options={})
|
12
|
+
by_day_Time(Time.zone.now, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def yesterday(options={})
|
16
|
+
by_day_Time(Time.zone.now.yesterday, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def tomorrow(options={})
|
20
|
+
by_day_Time(Time.zone.now.tomorrow, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def by_day_Time(time, options)
|
26
|
+
between(time.beginning_of_day, time.end_of_day, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByDirection
|
3
|
+
def before(*args)
|
4
|
+
options = args.extract_options!.symbolize_keys!
|
5
|
+
time = args.first || Time.zone.now
|
6
|
+
send("before_#{time_klass(time)}", time, options)
|
7
|
+
end
|
8
|
+
alias_method :before_now, :before
|
9
|
+
|
10
|
+
def after(*args)
|
11
|
+
options = args.extract_options!.symbolize_keys!
|
12
|
+
time = args.first || Time.zone.now
|
13
|
+
send("after_#{time_klass(time)}", time, options)
|
14
|
+
end
|
15
|
+
alias_method :after_now, :after
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def before_Time_or_Date(time_or_date, options={})
|
20
|
+
field = options[:field] || by_star_field
|
21
|
+
where("#{field} <= ?", time_or_date)
|
22
|
+
end
|
23
|
+
alias_method :before_Time, :before_Time_or_Date
|
24
|
+
alias_method :before_Date, :before_Time_or_Date
|
25
|
+
|
26
|
+
def before_String(string, options={})
|
27
|
+
field = options[:field] || by_star_field
|
28
|
+
if time = Chronic.parse(string)
|
29
|
+
where("#{field} <= ?", time)
|
30
|
+
else
|
31
|
+
raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_Time_or_Date(time_or_date, options={})
|
36
|
+
field = options[:field] || by_star_field
|
37
|
+
where("#{field} >= ?", time_or_date)
|
38
|
+
end
|
39
|
+
alias_method :after_Time, :after_Time_or_Date
|
40
|
+
alias_method :after_Date, :after_Time_or_Date
|
41
|
+
|
42
|
+
def after_String(string, options={})
|
43
|
+
field = options[:field] || by_star_field
|
44
|
+
if time = Chronic.parse(string)
|
45
|
+
where("#{field} >= ?", time)
|
46
|
+
else
|
47
|
+
raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByFortnight
|
3
|
+
# For reasoning why I use *args rather than variables here,
|
4
|
+
# please see the by_year method comments in lib/by_star/by_year.rb
|
5
|
+
|
6
|
+
def by_fortnight(*args)
|
7
|
+
options = args.extract_options!.symbolize_keys!
|
8
|
+
time = args.first
|
9
|
+
time ||= Time.local(options[:year], 1, 1) if options[:year]
|
10
|
+
time ||= Time.zone.now
|
11
|
+
send("by_fortnight_#{time_klass(time)}", time, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def by_fortnight_Time(time, options={})
|
17
|
+
# We want to get the current fortnight and so...
|
18
|
+
# We need to find the current week number and take one from it,
|
19
|
+
# so that we are correctly offset from the start of the year.
|
20
|
+
# The first fortnight of the year should of course start on the 1st January,
|
21
|
+
# and not the beginning of that week.
|
22
|
+
start_time = time.beginning_of_year + (time.strftime("%U").to_i - 1).weeks
|
23
|
+
between(start_time, (start_time + 2.weeks).end_of_day, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def by_fortnight_String_or_Fixnum(weeks, options={})
|
27
|
+
weeks = weeks.to_i
|
28
|
+
current_time = Time.zone.local(options[:year] || Time.zone.now.year)
|
29
|
+
if weeks <= 26
|
30
|
+
start_time = current_time + (weeks * 2).weeks
|
31
|
+
between(start_time, (start_time + 2.weeks).end_of_day, options)
|
32
|
+
else
|
33
|
+
raise ParseError, "by_fortnight takes only a Time, Date or a Fixnum (less than or equal to 26)."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias_method :by_fortnight_String, :by_fortnight_String_or_Fixnum
|
37
|
+
alias_method :by_fortnight_Fixnum, :by_fortnight_String_or_Fixnum
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
# def omg
|
42
|
+
# time.beginning_of_year + (time.strftime("%U").to_i).weeks
|
43
|
+
# if time.is_a?(Numeric) && time <= 26
|
44
|
+
# Time.utc(year, 1, 1) + ((time.to_i) * 2).weeks
|
45
|
+
# else
|
46
|
+
# raise ParseError, "by_fortnight takes only a Time or Date object, a Fixnum (less than or equal to 26) or a Chronicable string."
|
47
|
+
# end
|
48
|
+
|
49
|
+
# between(start_time.beginning_of_week, start_time + 2.weeks)
|
50
|
+
# end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByMonth
|
3
|
+
# For reasoning why I use *args rather than variables here,
|
4
|
+
# please see the by_year method comments in lib/by_star/by_year.rb
|
5
|
+
|
6
|
+
def by_month(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
time = args.first || Time.zone.now
|
9
|
+
send("by_month_#{time_klass(time)}", time, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def by_month_Time(time, options={})
|
15
|
+
between(time.beginning_of_month, time.end_of_month, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def by_month_String_or_Fixnum(month, options={})
|
19
|
+
if valid_month?(month)
|
20
|
+
year = options[:year] || Time.zone.now.year
|
21
|
+
by_month_Time("#{year}-#{month}-01".to_time, options)
|
22
|
+
else
|
23
|
+
raise ParseError, "Month must be a number between 1 and 12 or the full month name (e.g. 'January', 'Feburary', etc.)"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method :by_month_String, :by_month_String_or_Fixnum
|
27
|
+
alias_method :by_month_Fixnum, :by_month_String_or_Fixnum
|
28
|
+
|
29
|
+
|
30
|
+
def valid_month?(month)
|
31
|
+
(1..12).include?(month) || Date::MONTHNAMES.include?(month)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByWeek
|
3
|
+
|
4
|
+
# For reasoning why I use *args rather than variables here,
|
5
|
+
# please see the by_year method comments in lib/by_star/by_year.rb
|
6
|
+
|
7
|
+
def by_week(*args)
|
8
|
+
options = args.extract_options!.symbolize_keys!
|
9
|
+
time = args.first
|
10
|
+
time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
|
11
|
+
time ||= Time.zone.now
|
12
|
+
send("by_week_#{time_klass(time)}", time, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def by_week_Time_or_Date(time, options={})
|
18
|
+
between(time.beginning_of_week, time.end_of_week, options)
|
19
|
+
end
|
20
|
+
alias_method :by_week_Time, :by_week_Time_or_Date
|
21
|
+
alias_method :by_week_Date, :by_week_Time_or_Date
|
22
|
+
|
23
|
+
def by_week_Fixnum(week, options={})
|
24
|
+
time = Time.zone.local(options[:year], 1, 1) if options[:year]
|
25
|
+
time ||= Time.zone.now
|
26
|
+
start_time = time.beginning_of_year + week.to_i.weeks
|
27
|
+
between(start_time, (start_time + 1.week).end_of_day, options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByWeekend
|
3
|
+
def by_weekend(*args)
|
4
|
+
options = args.extract_options!.symbolize_keys!
|
5
|
+
time = args.first
|
6
|
+
time ||= Time.zone.now
|
7
|
+
send("by_weekend_#{time_klass(time)}", time, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def by_weekend_Time(time, options={})
|
13
|
+
between(time.beginning_of_weekend, time.end_of_weekend)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ByStar
|
2
|
+
module ByYear
|
3
|
+
|
4
|
+
# NOTE: We would define this as this:
|
5
|
+
#
|
6
|
+
# def by_year(time=Time.zone.now, options={})
|
7
|
+
#
|
8
|
+
# But, there's a potential situation where people want to just pass options.
|
9
|
+
# Like this:
|
10
|
+
#
|
11
|
+
# Post.by_year(:field => "published_at")
|
12
|
+
#
|
13
|
+
# And so, we support any number of arguments and just parse them as necessary.
|
14
|
+
# By doing it this way, we can support *both* this:
|
15
|
+
#
|
16
|
+
# Post.by_year(2012, :field => "published_at")
|
17
|
+
#
|
18
|
+
# And this:
|
19
|
+
#
|
20
|
+
# Post.by_year(:field => "published_at")
|
21
|
+
#
|
22
|
+
# This is because the time variable is going to be defaulting to the current time.
|
23
|
+
|
24
|
+
def by_year(*args)
|
25
|
+
options = args.extract_options!.symbolize_keys!
|
26
|
+
time = args.first || Time.zone.now
|
27
|
+
|
28
|
+
send("by_year_#{time_klass(time)}", time, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def by_year_Time_or_Date(time, options={})
|
34
|
+
between(time.beginning_of_year, time.end_of_year, options)
|
35
|
+
end
|
36
|
+
alias_method :by_year_Time, :by_year_Time_or_Date
|
37
|
+
alias_method :by_year_Date, :by_year_Time_or_Date
|
38
|
+
|
39
|
+
def by_year_String_or_Fixnum(year, options={})
|
40
|
+
by_year_Time("#{work_out_year(year)}-01-01".to_time, options)
|
41
|
+
end
|
42
|
+
alias_method :by_year_String, :by_year_String_or_Fixnum
|
43
|
+
alias_method :by_year_Fixnum, :by_year_String_or_Fixnum
|
44
|
+
|
45
|
+
def work_out_year(value)
|
46
|
+
case value.to_i
|
47
|
+
when 0..39
|
48
|
+
2000 + value
|
49
|
+
when 40..99
|
50
|
+
1900 + value
|
51
|
+
when nil
|
52
|
+
Time.zone.now.year
|
53
|
+
else
|
54
|
+
# We may be passed something that's not a straight out integer
|
55
|
+
# These things include: BigDecimals, Floats and Strings.
|
56
|
+
value.to_i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ByStar
|
2
|
+
module InstanceMethods
|
3
|
+
def previous(options={})
|
4
|
+
field = options[:field] || self.class.by_star_field
|
5
|
+
self.class.where("#{field} < ?", self.send(field)).reorder("#{field} DESC").first
|
6
|
+
end
|
7
|
+
|
8
|
+
def next(options={})
|
9
|
+
field = options[:field] || self.class.by_star_field
|
10
|
+
self.class.where("#{field} > ?", self.send(field)).reorder("#{field} ASC").first
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/by_star/time_ext.rb
CHANGED
data/lib/by_star/version.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "by day" do
|
4
|
+
def posts_count(*args)
|
5
|
+
Post.by_day(*args).count
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be able to find a post for today" do
|
9
|
+
posts_count.should eql(4)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be able to find a post by a given date in last year" do
|
13
|
+
posts_count(:year => Time.zone.now.year - 1).should eql(1)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to use an alternative field" do
|
17
|
+
Event.by_day(Time.now.yesterday, :field => "start_time").size.should eql(1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "today" do
|
22
|
+
it "should show the post for today" do
|
23
|
+
Post.today.map(&:text).should include("Today's post")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to use an alternative field" do
|
27
|
+
# Test may occur on an event day.
|
28
|
+
Event.today(:field => "start_time").size.should eql(1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "yesterday" do
|
33
|
+
|
34
|
+
it "should show the post for yesterday" do
|
35
|
+
Post.yesterday.map(&:text).should include("Yesterday's post")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to use an alternative field" do
|
39
|
+
Event.yesterday(:field => "start_time").size.should eql(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "tomorrow" do
|
45
|
+
it "should show the post for tomorrow" do
|
46
|
+
Post.tomorrow.map(&:text).should include("Tomorrow's post")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to use an alternative field" do
|
50
|
+
Event.tomorrow(:field => "start_time").size.should eql(1)
|
51
|
+
end
|
52
|
+
end
|