by_star 1.0.1 → 2.0.0.beta1
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/.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
|