by_star 2.1.0.beta2 → 2.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gitignore +5 -4
  2. data/.travis.yml +35 -25
  3. data/CHANGELOG.md +21 -0
  4. data/Gemfile +25 -4
  5. data/MIT-LICENSE +20 -20
  6. data/{README.markdown → README.md} +414 -348
  7. data/Rakefile +18 -2
  8. data/UPGRADING +10 -0
  9. data/by_star.gemspec +32 -31
  10. data/cleaner.rb +24 -24
  11. data/lib/by_star.rb +15 -71
  12. data/lib/by_star/base.rb +53 -0
  13. data/lib/by_star/between.rb +80 -0
  14. data/lib/by_star/directional.rb +21 -0
  15. data/lib/by_star/kernel.rb +41 -0
  16. data/lib/by_star/normalization.rb +118 -0
  17. data/lib/by_star/orm/active_record/by_star.rb +52 -0
  18. data/lib/by_star/orm/mongoid/by_star.rb +59 -0
  19. data/lib/by_star/version.rb +3 -3
  20. data/spec/database.yml +15 -15
  21. data/spec/fixtures/active_record/models.rb +6 -13
  22. data/spec/fixtures/active_record/schema.rb +11 -36
  23. data/spec/fixtures/mongoid/models.rb +15 -65
  24. data/spec/fixtures/shared/seeds.rb +16 -35
  25. data/spec/integration/active_record/active_record_spec.rb +50 -0
  26. data/spec/integration/mongoid/mongoid_spec.rb +43 -0
  27. data/spec/integration/shared/by_calendar_month.rb +55 -0
  28. data/spec/integration/shared/by_day.rb +84 -0
  29. data/spec/integration/shared/by_direction.rb +55 -0
  30. data/spec/integration/shared/by_fortnight.rb +48 -0
  31. data/spec/integration/shared/by_month.rb +50 -0
  32. data/spec/integration/shared/by_quarter.rb +49 -0
  33. data/spec/integration/shared/by_week.rb +54 -0
  34. data/spec/integration/shared/by_weekend.rb +49 -0
  35. data/spec/integration/shared/by_year.rb +48 -0
  36. data/spec/integration/shared/offset_parameter.rb +31 -0
  37. data/spec/spec_helper.rb +29 -35
  38. data/spec/unit/kernel_time_spec.rb +57 -0
  39. data/spec/unit/normalization_spec.rb +255 -0
  40. metadata +131 -56
  41. data/Gemfile.lock +0 -94
  42. data/lib/by_star/by_day.rb +0 -34
  43. data/lib/by_star/by_direction.rb +0 -52
  44. data/lib/by_star/by_fortnight.rb +0 -58
  45. data/lib/by_star/by_month.rb +0 -47
  46. data/lib/by_star/by_quarter.rb +0 -32
  47. data/lib/by_star/by_week.rb +0 -32
  48. data/lib/by_star/by_weekend.rb +0 -20
  49. data/lib/by_star/by_year.rb +0 -62
  50. data/lib/by_star/instance_methods.rb +0 -13
  51. data/lib/by_star/time_ext.rb +0 -21
  52. data/lib/mongoid/by_star.rb +0 -83
  53. data/spec/by_star/active_record/active_record_spec.rb +0 -50
  54. data/spec/by_star/mongoid/mongoid_spec.rb +0 -44
  55. data/spec/by_star/shared/by_day.rb +0 -62
  56. data/spec/by_star/shared/by_direction.rb +0 -85
  57. data/spec/by_star/shared/by_fortnight.rb +0 -47
  58. data/spec/by_star/shared/by_month.rb +0 -109
  59. data/spec/by_star/shared/by_quarter.rb +0 -33
  60. data/spec/by_star/shared/by_week.rb +0 -41
  61. data/spec/by_star/shared/by_weekend.rb +0 -13
  62. data/spec/by_star/shared/by_year.rb +0 -54
  63. data/spec/time_ext_spec.rb +0 -10
@@ -1,58 +0,0 @@
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_Date(date, options={})
27
- by_fortnight_Time(date.to_time, options)
28
- end
29
-
30
- def by_fortnight_String_or_Fixnum(weeks, options={})
31
- weeks = weeks.to_i
32
- current_time = Time.zone.local(options[:year] || Time.zone.now.year)
33
- if weeks <= 26
34
- start_time = current_time + (weeks * 2).weeks
35
- between(start_time, (start_time + 2.weeks).end_of_day, options)
36
- else
37
- raise ParseError, "by_fortnight takes only a Time, Date or a Fixnum (less than or equal to 26)."
38
- end
39
- end
40
- alias_method :by_fortnight_String, :by_fortnight_String_or_Fixnum
41
- alias_method :by_fortnight_Fixnum, :by_fortnight_String_or_Fixnum
42
-
43
-
44
-
45
- # def omg
46
- # time.beginning_of_year + (time.strftime("%U").to_i).weeks
47
- # if time.is_a?(Numeric) && time <= 26
48
- # Time.utc(year, 1, 1) + ((time.to_i) * 2).weeks
49
- # else
50
- # raise ParseError, "by_fortnight takes only a Time or Date object, a Fixnum (less than or equal to 26) or a Chronicable string."
51
- # end
52
-
53
- # between(start_time.beginning_of_week, start_time + 2.weeks)
54
- # end
55
-
56
-
57
- end
58
- end
@@ -1,47 +0,0 @@
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, false, options)
10
- end
11
-
12
- def by_calendar_month(*args)
13
- options = args.extract_options!
14
- time = args.first || Time.zone.now
15
- send("by_month_#{time_klass(time)}", time, true, options)
16
- end
17
-
18
- private
19
-
20
- def by_month_Time(time, is_calendar=false, options={})
21
- if is_calendar
22
- between(time.beginning_of_month.beginning_of_week, time.end_of_month.end_of_week, options)
23
- else
24
- between(time.beginning_of_month, time.end_of_month, options)
25
- end
26
- end
27
-
28
- def by_month_Date(date, is_calendar=false, options={})
29
- by_month_Time(date.to_time, is_calendar, options)
30
- end
31
-
32
- def by_month_String_or_Fixnum(month, is_calendar=false, options={})
33
- begin
34
- year = options[:year] || Time.zone.now.year
35
- date = Date.parse("#{year}-#{month}-01").to_time
36
- by_month_Time(date, is_calendar, options)
37
-
38
- rescue
39
- raise ParseError, "Month must be a number between 1 and 12 or the full month name (e.g. 'January', 'Feburary', etc.)"
40
- end
41
- end
42
-
43
- alias_method :by_month_String, :by_month_String_or_Fixnum
44
- alias_method :by_month_Fixnum, :by_month_String_or_Fixnum
45
-
46
- end
47
- end
@@ -1,32 +0,0 @@
1
- module ByStar
2
- module ByQuarter
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_quarter(*args)
7
- options = args.extract_options!.symbolize_keys!
8
- time = args.first
9
- time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
10
- time ||= Time.zone.now
11
- send("by_quarter_#{time_klass(time)}", time, options)
12
- end
13
-
14
- private
15
-
16
- def by_quarter_Time(time, options={})
17
- between(time.beginning_of_quarter, time.end_of_quarter, options)
18
- end
19
-
20
- def by_quarter_Date(date, options={})
21
- by_quarter_Time(date.to_time, options)
22
- end
23
-
24
- def by_quarter_Fixnum(quarter, options={})
25
- raise 'Quarter must be a number between 1 and 4' unless quarter >= 1 && quarter <= 4
26
- time = Time.zone.local(options[:year], 1, 1) if options[:year]
27
- time ||= Time.zone.now
28
- start_time = time.beginning_of_year + ((quarter.to_i - 1) * 3).months
29
- between(start_time, start_time.end_of_quarter, options)
30
- end
31
- end
32
- end
@@ -1,32 +0,0 @@
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(time, options={})
18
- between(time.beginning_of_week, time.end_of_week, options)
19
- end
20
-
21
- def by_week_Date(date, options={})
22
- by_week_Time(date.to_time, options)
23
- end
24
-
25
- def by_week_Fixnum(week, options={})
26
- time = Time.zone.local(options[:year], 1, 1) if options[:year]
27
- time ||= Time.zone.now
28
- start_time = time.beginning_of_year + week.to_i.weeks
29
- between(start_time, (start_time + 1.week).end_of_day, options)
30
- end
31
- end
32
- end
@@ -1,20 +0,0 @@
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
- def by_weekend_Date(date, options={})
17
- by_weekend_Time(date.to_time, options)
18
- end
19
- end
20
- end
@@ -1,62 +0,0 @@
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(time, options={})
34
- between(time.beginning_of_year, time.end_of_year, options)
35
- end
36
-
37
- def by_year_Date(date, options={})
38
- by_year_Time(date.to_time, options)
39
- end
40
-
41
- def by_year_String_or_Fixnum(year, options={})
42
- by_year_Time("#{work_out_year(year)}-01-01".to_time, options)
43
- end
44
- alias_method :by_year_String, :by_year_String_or_Fixnum
45
- alias_method :by_year_Fixnum, :by_year_String_or_Fixnum
46
-
47
- def work_out_year(value)
48
- case value.to_i
49
- when 0..39
50
- 2000 + value
51
- when 40..99
52
- 1900 + value
53
- when nil
54
- Time.zone.now.year
55
- else
56
- # We may be passed something that's not a straight out integer
57
- # These things include: BigDecimals, Floats and Strings.
58
- value.to_i
59
- end
60
- end
61
- end
62
- end
@@ -1,13 +0,0 @@
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
@@ -1,21 +0,0 @@
1
- class Time
2
- def beginning_of_weekend
3
- friday = case self.wday
4
- when 0
5
- self.end_of_week.beginning_of_day.advance(:days => -2)
6
- when 5
7
- self.beginning_of_day
8
- else
9
- self.beginning_of_week.advance(:days => 4)
10
- end
11
- # 3pm, Friday.
12
- (friday + 15.hours)
13
- end
14
-
15
- def end_of_weekend
16
- # 3am, Monday.
17
- # LOL I CHEATED.
18
- beginning_of_weekend + 3.days - 12.hours
19
- end
20
- end
21
-
@@ -1,83 +0,0 @@
1
- module Mongoid::ByStar
2
- extend ActiveSupport::Concern
3
-
4
- module ClassMethods
5
- include ByStar
6
-
7
- def by_star_field(field=nil)
8
- @by_star_field ||= field
9
- @by_star_field || :created_at
10
- end
11
-
12
- # override ByStar method
13
- def between(start, finish, options={})
14
- field = by_star_field_class(options)
15
- scope = gte(field => start).lte(field => finish)
16
- scope = scope.order_by(field => options[:order]) if options[:order]
17
- scope
18
- end
19
- alias_method :between_times, :between
20
-
21
- # override private methods in ByStar::ByDirection
22
- def before_Time_or_Date(time_or_date, options={})
23
- field = by_star_field_class(options)
24
- lte(field => time_or_date)
25
- end
26
- alias_method :before_Time, :before_Time_or_Date
27
- alias_method :before_Date, :before_Time_or_Date
28
-
29
- def before_String(string, options={})
30
- field = by_star_field_class(options)
31
- if time = Chronic.parse(string)
32
- lte(field => time)
33
- else
34
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
35
- end
36
- end
37
-
38
- def after_Time_or_Date(time_or_date, options={})
39
- field = by_star_field_class(options)
40
- gte(field => time_or_date)
41
- end
42
- alias_method :after_Time, :after_Time_or_Date
43
- alias_method :after_Date, :after_Time_or_Date
44
-
45
- def after_String(string, options={})
46
- field = by_star_field_class(options)
47
- if time = Chronic.parse(string)
48
- gte(field => time)
49
- else
50
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
51
- end
52
- end
53
-
54
- protected
55
-
56
- def by_star_field_class(options={})
57
- field = options[:field] || by_star_field
58
- field = aliased_fields[field.to_s] if aliased_fields.has_key?(field.to_s)
59
- field.to_sym
60
- end
61
- end
62
-
63
- include ByStar::InstanceMethods
64
-
65
- # override ByStar::InstanceMethods methods
66
- def previous(options={})
67
- field = by_star_field_instance(options)
68
- self.class.lt(field => self.send(field)).desc(field).first
69
- end
70
-
71
- def next(options={})
72
- field = by_star_field_instance(options)
73
- self.class.gt(field => self.send(field)).asc(field).first
74
- end
75
-
76
- protected
77
-
78
- def by_star_field_instance(options={})
79
- field = options[:field] || self.class.by_star_field
80
- field = self.class.aliased_fields[field.to_s] if self.class.aliased_fields.has_key?(field.to_s)
81
- field.to_sym
82
- end
83
- end
@@ -1,50 +0,0 @@
1
- require 'spec_helper'
2
- Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
3
-
4
- describe ActiveRecord do
5
- before(:all) do
6
- ActiveRecord::Base.default_timezone = :utc
7
-
8
- db_config = YAML::load_file(File.dirname(__FILE__) + "/../../database.yml")
9
- if db_config.has_key?('sqlite') && db_config['sqlite'].has_key?('database')
10
- db_config['sqlite']['database'] = File.dirname(__FILE__) + '/../../tmp/' + db_config['sqlite']['database']
11
- end
12
-
13
- ActiveRecord::Base.configurations = db_config
14
- ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite")
15
- load File.dirname(__FILE__) + "/../../fixtures/active_record/schema.rb"
16
- load File.dirname(__FILE__) + "/../../fixtures/active_record/models.rb"
17
- load File.dirname(__FILE__) + "/../../fixtures/shared/seeds.rb"
18
-
19
- ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/../../tmp/activerecord.log")
20
- end
21
-
22
- it_behaves_like "by day"
23
- it_behaves_like "by direction"
24
- it_behaves_like "by fortnight"
25
- it_behaves_like "by month"
26
- it_behaves_like "by quarter"
27
- it_behaves_like "by week"
28
- it_behaves_like "by weekend"
29
- it_behaves_like "by year"
30
-
31
- it "should be able to order the result set" do
32
- scope = Post.by_year(Time.zone.now.year, :order => "created_at DESC")
33
- scope.order_values.should == ["created_at DESC"]
34
- end
35
-
36
- describe "#between" do
37
- it "should return an ActiveRecord::Relation object" do
38
- Post.between(Date.today - 2, Date.today).class.should == ActiveRecord::Relation
39
- end
40
- it "should return a result set between two times" do
41
- Post.between(Date.today - 2, Date.today).count.should == 1
42
- end
43
- end
44
-
45
- describe "#between_times" do
46
- it "should be an alias of #between" do
47
- Post.between_times(Date.today - 2, Date.today).should == Post.between(Date.today - 2, Date.today)
48
- end
49
- end
50
- end
@@ -1,44 +0,0 @@
1
- require 'spec_helper'
2
- Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
3
-
4
- describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3') do
5
-
6
- before(:all) do
7
- DATABASE_NAME = "mongoid_#{Process.pid}"
8
-
9
- Mongoid.configure do |config|
10
- config.connect_to DATABASE_NAME
11
- end
12
-
13
- load File.dirname(__FILE__) + "/../../fixtures/mongoid/models.rb"
14
- load File.dirname(__FILE__) + "/../../fixtures/shared/seeds.rb"
15
- end
16
-
17
- after(:all) do
18
- Mongoid.purge!
19
- end
20
-
21
- it_behaves_like "by day"
22
- it_behaves_like "by direction"
23
- it_behaves_like "by fortnight"
24
- it_behaves_like "by month"
25
- it_behaves_like "by quarter"
26
- it_behaves_like "by week"
27
- it_behaves_like "by weekend"
28
- it_behaves_like "by year"
29
-
30
- describe "#between" do
31
- it "should return a Mongoid::Critera object" do
32
- Post.between(Date.today - 2, Date.today).class.should == Mongoid::Criteria
33
- end
34
- it "should return a result set between two times" do
35
- Post.between(Date.today - 2, Date.today).count.should == 1
36
- end
37
- end
38
-
39
- describe "#between_times" do
40
- it "should be an alias of #between" do
41
- Post.between_times(Date.today - 2, Date.today).should == Post.between(Date.today - 2, Date.today)
42
- end
43
- end
44
- end