Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
services: mongodb
services:
- mongodb
- mysql
- postgresql

before_script:
- "mysql -e 'create database by_star_test;'"
Expand Down
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ See sections below for detailed argument usage of each:
| `by_month` | Query by month. Allows integer arg, e.g. `11` for November. |
| `by_calendar_month` | Month as it appears on a calendar; days form previous/following months which are part of the first/last weeks of the given month. |
| `by_quarter` | 3-month intervals of the year. |
| `by_semester` | 6-month intervals of the year. |
| `by_year` | Query by year. Allows integer arg, e.g. `2017`. |

### Relative Scopes
Expand Down Expand Up @@ -544,11 +545,41 @@ Post.by_quarter(2, year: 2012)
This will return all posts in the 2nd quarter of 2012.

```ruby
Post.by_week(Time.local(2012,1,1))
Post.by_quarter(Time.local(2012,1,1))
```

This will return all posts from the first quarter of 2012.

### by_semester

Finds records by 6-month biannual period of year. Semester numbering starts at 1. The two semesters of the year begin on Jan 1 and Jul 1 respectively.

To find records from the current semester:

```ruby
Post.by_semester
```

To find records based on a semester, you can pass in a number (representing the semester number) or a time object:

```ruby
Post.by_semester(1)
```

This will return all posts in the 1st semester of the current year.

```ruby
Post.by_semester(2, year: 2012)
```

This will return all posts in the 2nd semester of 2012.

```ruby
Post.by_semester(Time.local(2012,1,1))
```

This will return all posts from the first semester of 2012.

## Version Support

ByStar is tested against the following versions:
Expand Down
14 changes: 14 additions & 0 deletions lib/by_star/between.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ def by_quarter(*args)
end
end

def by_semester(*args)
with_by_star_options(*args) do |time, options|
date = ByStar::Normalization.semester(time, options)

first_semester_month = date.month - (5 + date.month) % 6
beginning_of_semester = date.beginning_of_month.change(month: first_semester_month)

last_semester_month = date.month + (12 - date.month) % 6
end_of_semester = date.beginning_of_month.change(month: last_semester_month).end_of_month

between_dates(beginning_of_semester, end_of_semester, options)
end
end

def by_year(*args)
with_by_star_options(*args) do |time, options|
date = ByStar::Normalization.year(time, options)
Expand Down
14 changes: 14 additions & 0 deletions lib/by_star/normalization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ def quarter_integer(value, options={})
time.beginning_of_year + ((value - 1) * 3).months
end

def semester(value, options={})
value = try_string_to_int(value)
case value
when Integer then semester_integer(value, options)
else date(value)
end
end

def semester_integer(value, options={})
raise ParseError, 'Semester number must be between 1 and 2' unless value.in?(1..2)
time = Time.zone.local(options[:year] || Time.zone.now.year)
time.beginning_of_year + ((value - 1) * 6).months
end

def month(value, options={})
value = try_string_to_int(value)
case value
Expand Down
1 change: 1 addition & 0 deletions spec/integration/active_record/active_record_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
it_behaves_like 'by month'
it_behaves_like 'by calendar month'
it_behaves_like 'by quarter'
it_behaves_like 'by semester'
it_behaves_like 'by week'
it_behaves_like 'by cweek'
it_behaves_like 'by weekend'
Expand Down
1 change: 1 addition & 0 deletions spec/integration/mongoid/mongoid_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
it_behaves_like 'by month'
it_behaves_like 'by calendar month'
it_behaves_like 'by quarter'
it_behaves_like 'by semester'
it_behaves_like 'by week'
it_behaves_like 'by cweek'
it_behaves_like 'by weekend'
Expand Down
49 changes: 49 additions & 0 deletions spec/integration/shared/by_semester.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'spec_helper'

shared_examples_for 'by semester' do

describe '#by_semester' do

context 'point-in-time' do
subject { Post.by_semester(1) }
it { expect(subject.count).to eql(12) }
end

context 'timespan' do
subject { Event.by_semester(1) }
it { expect(subject.count).to eql(14) }
end

context 'timespan strict' do
subject { Event.by_semester(Date.parse('2014-02-01'), strict: true) }
it { expect(subject.count).to eql(9) }
end

context 'with :year option' do

context 'point-in-time' do
subject { Post.by_semester(2, year: 2013) }
it { expect(subject.count).to eql(10) }
end

context 'timespan' do
subject { Event.by_semester(2, year: 2013) }
it { expect(subject.count).to eql(13) }
end

context 'timespan strict' do
subject { Event.by_semester(2, year: 2013, strict: true) }
it { expect(subject.count).to eql(8) }
end
end

it 'should raise an error when given an invalid argument' do
expect{ Post.by_semester(0) }.to raise_error(ByStar::ParseError, 'Semester number must be between 1 and 2')
expect{ Post.by_semester(5) }.to raise_error(ByStar::ParseError, 'Semester number must be between 1 and 2')
end

it 'should be able to use an alternative field' do
expect(Event.by_semester(1, field: 'end_time').count).to eq(14)
end
end
end
30 changes: 30 additions & 0 deletions spec/unit/normalization_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,36 @@
specify { expect{ ByStar::Normalization.quarter(5) }.to raise_error(ByStar::ParseError, 'Quarter number must be between 1 and 4') }
end
end

describe '#semester' do
subject { ByStar::Normalization.semester(input, options) }
it_behaves_like 'date normalization from string'
it_behaves_like 'date normalization from time value'

context 'Integer 1' do
let(:input){ 1 }
it { expect eq Date.parse('2014-01-01') }
end

context 'Integer 2' do
let(:input){ 2 }
it { expect eq Date.parse('2014-07-01') }
end

context 'with year option' do
let(:options){ { year: 2011 } }

context 'Integer 2' do
let(:input){ 2 }
it { expect eq Date.parse('2011-07-01') }
end
end

context 'out of range' do
specify { expect{ ByStar::Normalization.semester(0) }.to raise_error(ByStar::ParseError, 'Semester number must be between 1 and 2') }
specify { expect{ ByStar::Normalization.semester(5) }.to raise_error(ByStar::ParseError, 'Semester number must be between 1 and 2') }
end
end

describe '#year' do
subject { ByStar::Normalization.year(input, options) }
Expand Down