Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #32 from johnnyshields/master

Refactor + New Features + All Specs passing
  • Loading branch information...
commit 88131f4da24955eef3f16f4939c2bfa2d3bc010b 2 parents 453f100 + 486589c
@radar authored
Showing with 1,505 additions and 1,269 deletions.
  1. +10 −4 .travis.yml
  2. +21 −0 CHANGELOG.md
  3. +8 −14 Gemfile
  4. +1 −1  MIT-LICENSE
  5. +48 −8 README.markdown → README.md
  6. +3 −1 Rakefile
  7. +10 −0 UPGRADING
  8. +13 −13 by_star.gemspec
  9. +10 −66 lib/by_star.rb
  10. +53 −0 lib/by_star/base.rb
  11. +80 −0 lib/by_star/between.rb
  12. +0 −34 lib/by_star/by_day.rb
  13. +0 −52 lib/by_star/by_direction.rb
  14. +0 −58 lib/by_star/by_fortnight.rb
  15. +0 −47 lib/by_star/by_month.rb
  16. +0 −32 lib/by_star/by_quarter.rb
  17. +0 −32 lib/by_star/by_week.rb
  18. +0 −20 lib/by_star/by_weekend.rb
  19. +0 −62 lib/by_star/by_year.rb
  20. +21 −0 lib/by_star/directional.rb
  21. +0 −15 lib/by_star/instance_methods.rb
  22. +41 −0 lib/by_star/kernel.rb
  23. +118 −0 lib/by_star/normalization.rb
  24. +52 −0 lib/by_star/orm/active_record/by_star.rb
  25. +59 −0 lib/by_star/orm/mongoid/by_star.rb
  26. +0 −21 lib/by_star/time_ext.rb
  27. +1 −1  lib/by_star/version.rb
  28. +0 −83 lib/mongoid/by_star.rb
  29. +0 −50 spec/by_star/active_record/active_record_spec.rb
  30. +0 −44 spec/by_star/mongoid/mongoid_spec.rb
  31. +0 −62 spec/by_star/shared/by_day.rb
  32. +0 −96 spec/by_star/shared/by_direction.rb
  33. +0 −47 spec/by_star/shared/by_fortnight.rb
  34. +0 −109 spec/by_star/shared/by_month.rb
  35. +0 −33 spec/by_star/shared/by_quarter.rb
  36. +0 −41 spec/by_star/shared/by_week.rb
  37. +0 −13 spec/by_star/shared/by_weekend.rb
  38. +0 −54 spec/by_star/shared/by_year.rb
  39. +1 −8 spec/fixtures/active_record/models.rb
  40. +1 −26 spec/fixtures/active_record/schema.rb
  41. +3 −53 spec/fixtures/mongoid/models.rb
  42. +15 −34 spec/fixtures/shared/seeds.rb
  43. +50 −0 spec/integration/active_record/active_record_spec.rb
  44. +43 −0 spec/integration/mongoid/mongoid_spec.rb
  45. +55 −0 spec/integration/shared/by_calendar_month.rb
  46. +84 −0 spec/integration/shared/by_day.rb
  47. +55 −0 spec/integration/shared/by_direction.rb
  48. +48 −0 spec/integration/shared/by_fortnight.rb
  49. +50 −0 spec/integration/shared/by_month.rb
  50. +49 −0 spec/integration/shared/by_quarter.rb
  51. +54 −0 spec/integration/shared/by_week.rb
  52. +49 −0 spec/integration/shared/by_weekend.rb
  53. +48 −0 spec/integration/shared/by_year.rb
  54. +31 −0 spec/integration/shared/offset_parameter.rb
  55. +8 −25 spec/spec_helper.rb
  56. +0 −10 spec/time_ext_spec.rb
  57. +57 −0 spec/unit/kernel_time_spec.rb
  58. +255 −0 spec/unit/normalization_spec.rb
View
14 .travis.yml
@@ -6,11 +6,11 @@ env:
- DB=sqlite
- DB=mysql
- DB=postgres
+ - DB=mongodb
- ACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master DB=sqlite
- ACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master DB=mysql
- ACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master DB=postgres
-
-script: "DISPLAY=:99.0 bundle exec rspec spec/by_star"
+ - ACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master DB=mongodb
notifications:
email:
@@ -21,9 +21,15 @@ branches:
- master
rvm:
- - 1.8.7
- - 1.9.2
- 1.9.3
- 2.0.0
+ - 2.1.0
+ - rbx
+ - jruby
+
+matrix:
+ allow_failures:
+ - rvm: rbx
+ - rvm: jruby
services: mongodb
View
21 CHANGELOG.md
@@ -0,0 +1,21 @@
+# CHANGELOG
+
+## v2.2.0
+
+* Begin tracking CHANGELOG
+* Drop official support for Ruby <= 1.9.2
+* Add Ruby 2.1.0, Rubinius, and JRuby to Travis
+* Decouple gem from ActiveRecord, and put ActiveRecord and Mongoid specific methods in ORM modules.
+* Consolidate all normalization/parsing functions into new Normalization module
+* Remove meta-programming methods, e.g. `send("by_week_#{time_klass}")`
+* Support matching timespan-type objects with distinct start and end times (previously only point-in-time matching was supported)
+* Make Chronic gem optional; use it only if user has included it externally
+* `by_week` always returns a calendar week (i.e. beginning Monday or as specified by Rails setting), regardless of whether Date or Fixnum is given as a parameter.
+* `by_week` and `by_calendar_month` now supports optional `:start_day` option (:monday, :tuesday, etc)
+* Separate `by_calendar_month` into it's own class
+* Rename `between` method to `between_times` internally, as Mongoid already defines `between`. ActiveRecord has an alias of `between` so interface stays consistent.
+* Add `:offset` option to all query methods, in order to offset the time the day begins/ends (for example supposing business cycle begins at 8:00 each day until 7:59:59 the next day)
+* `by_weekend` can now take a fixnum (parsing logic is same as by_week)
+* Two-digit year now considers 70 to be 1970, and 69 to be 2069 (was previously 40 -> 1940)
+* Add Time kernel extensions for fortnight and calendar_month
+* Add Johnny Shields as a gem co-author
View
22 Gemfile
@@ -1,4 +1,4 @@
-source "http://rubygems.org"
+source 'http://rubygems.org'
# Specify your gem's dependencies in by_star.gemspec
gemspec
@@ -7,25 +7,19 @@ active_record_version = ENV['ACTIVE_RECORD_VERSION'] || 'default'
active_record_opts =
case active_record_version
- when 'master'
- {github: 'rails/rails'}
- when 'default'
- '~> 3.0'
- else
- "~> #{active_record_version}"
+ when 'master' then {github: 'rails/rails'}
+ when 'default' then '~> 3.0'
+ else "~> #{active_record_version}"
end
mongoid_version = ENV['MONGOID_VERSION'] || 'default'
mongoid_opts =
case mongoid_version
- when 'master'
- {github: 'mongoid/mongoid'}
- when 'default'
- '~> 3.0'
- else
- "~> #{mongoid_version}"
+ when 'master' then {github: 'mongoid/mongoid'}
+ when 'default' then '~> 3.0'
+ else "~> #{mongoid_version}"
end
gem 'activerecord', active_record_opts
-gem 'mongoid', mongoid_opts if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
+gem 'mongoid', mongoid_opts
View
2  MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2008 [name of plugin creator]
+Copyright (c) 2008 Ryan Bigg
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
View
56 README.markdown → README.md
@@ -1,6 +1,7 @@
# by_*
[![Build Status](https://travis-ci.org/radar/by_star.png)](https://travis-ci.org/radar/by_star)
+[![Code Climate](https://codeclimate.com/github/radar/by_star.png)](https://codeclimate.com/github/radar/by_star)
by_* (by_star) is a plugin that allows you to find ActiveRecord and/or Mongoid objects given certain date objects.
@@ -69,6 +70,20 @@ Want to count records? Simple:
Post.by_month.count
+## Time-Range Type Objects
+
+If your object has both a start and end time, you may pass both params to `by_star_field`:
+
+ by_star_field :start_time, :end_time
+
+By default, ByStar queries will return all objects whose range has any overlap within the desired period (permissive):
+
+ MultiDayEvent.by_month("January") #=> returns MultiDayEvents that overlap in January,
+ even if they start in December and/or end in February
+
+If you'd like to confine results to starting and ending within the given range, use the `:strict` option:
+
+ MultiDayEvent.by_month("January", strict => true) #=> returns MultiDayEvents that both start AND end in January
## By Year (`by_year`)
@@ -127,7 +142,7 @@ Finds records for a given month as shown on a calendar. Includes all the results
Post.by_calendar_month
-Parameter behavior is otherwise the same as `by_month`
+Parameter behavior is otherwise the same as `by_month`. Also, `:start_day` option is supported to specify the start day of the week (`:monday`, `:tuesday`, etc.)
## By Fortnight (`by_fortnight`)
@@ -173,6 +188,8 @@ This will return all posts in the 37th week of 2012.
This will return all posts from the first week of 2012.
+You may pass in a `:start_day` option (`:monday`, `:tuesday`, etc.) to specify the starting day of the week. This may also be configured in Rails.
+
## By Weekend (`by_weekend`)
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.
@@ -267,6 +284,8 @@ You can also pass a string:
Post.before("next tuesday")
+For Time-Range type objects, only the start time is considered for `before`.
+
## After (`after`)
To find all posts after the current time:
@@ -282,19 +301,21 @@ You can also pass a string:
Post.after("next tuesday")
-## Between (`between` or `between_times`)
+For Time-Range type objects, only the start time is considered for `after`.
+
+## Between (`between_times`)
To find records between two times:
- Post.between(time1, time2)
+ Post.between_times(time1, time2)
Also works with dates:
- Post.between(date1, date2)
+ Post.between_times(date1, date2)
-`between_times` is an alias for `between`:
+ActiveRecord only: `between` is an alias for `between_times`:
- Post.between_times(time1, time2) #=> results identical to above
+ Post.between(time1, time2) #=> results identical to between_times above
## Previous (`previous`)
@@ -306,6 +327,8 @@ You can specify a field also:
Post.last.previous("published_at")
+For Time-Range type objects, only the start time is considered for `previous`.
+
## Next (`next`)
To find the record after this one call `next` on any model instance:
@@ -316,6 +339,15 @@ You can specify a field also:
Post.last.next("published_at")
+For Time-Range type objects, only the start time is considered for `next`.
+
+## Offset option
+
+All ByStar finders support an `:offset` option which offsets the time period for which the query is performed.
+This is useful in cases where the daily cycle occurs at a time other than midnight.
+
+ Post.by_day('2014-03-05', offset: 9.hours)
+
## Not using created_at? No worries!
If your database uses something other than `created_at` for storing a timestamp, you can specify the field option like this:
@@ -330,6 +362,13 @@ Or if you're doing it all the time on your model, then it's best to use `by_star
by_star_field :something_else
end
+## Chronic Support
+
+If [Chronic](https://github.com/mojombo/chronic) gem is present, it will be used to parse natural-language date/time
+strings in all ByStar finder methods. Otherwise, the Ruby `Time.parse` kernel method will be used as a fallback.
+
+As of ByStar 2.2.0, you must explicitly include `gem chronic` into your Gemfile in order to use Chronic.
+
## Mongoid
Mongoid is only tested/supported on version 3.0+
@@ -345,7 +384,7 @@ You can also take an ORM-specific test task for a ride:
`bundle exec rake spec:active_record`
-Have an Active Record or Mongoid version in mind? Set the environemtnal variables
+Have an Active Record or Mongoid version in mind? Set the environment variables
`ACTIVE_RECORD_VERSION` and `MONGOID_VERSION` to a version of your choice. A
version number provided will translate to `~> VERSION`, and the string `master`
will grab the latest from Github.
@@ -366,8 +405,9 @@ Thanks to Thomas Sinclair for the original bump for implementing it. I would lik
* August Lilleas (leethal)
* gte351s
* Sam Elliott (lenary)
-* The people who created Chronic
+* The people who created [Chronic](https://github.com/mojombo/chronic) gem
* Erik Fonselius
+* Johnny Shields (johnnyshields)
## Suggestions?
View
4 Rakefile
@@ -6,7 +6,7 @@ RSpec::Core::RakeTask.new(:spec)
def orm_test(orm)
RSpec::Core::RakeTask.new(orm) do |task|
- task.pattern = "./spec/by_star/#{orm}/{,/*/**}/*_spec.rb"
+ task.pattern = "./spec/{unit,integration/#{orm}}/{,/*/**}/*_spec.rb"
end
end
@@ -14,3 +14,5 @@ namespace :spec do
orm_test 'active_record'
orm_test 'mongoid'
end
+
+task default: :spec
View
10 UPGRADING
@@ -0,0 +1,10 @@
+Upgrading to ByStar 2.2.0
+-------------------------
+
+* For Mongoid only, ByStar's `.between` method has been removed and replaced with `.between_times`
+(previously `.between_times` was an alias to `.between`). Mongoid already provides a native `.between`
+finder method that we do not want to mask. ActiveRecord users may continue to use either method.
+
+* Chronic gem (used for time string parsing) has been removed as a hard dependency for ByStar,
+however it is still supported. If you would like to use Chronic with ByStar, please explicitly
+include `gem chronic` into your Gemfile.
View
26 by_star.gemspec
@@ -4,29 +4,29 @@ require File.expand_path("../lib/by_star/version", __FILE__)
Gem::Specification.new do |s|
s.name = "by_star"
s.version = ByStar::VERSION
- s.platform = Gem::Platform::RUBY
- s.authors = ["Ryan Bigg"]
+ s.authors = ["Ryan Bigg", "Johnny Shields"]
s.email = ["radarlistener@gmail.com"]
s.homepage = "http://github.com/radar/by_star"
- s.summary = "ActiveRecord extension for easier date scopes and time ranges"
- s.description = "ActiveRecord extension for easier date scopes and time ranges"
+ s.summary = "ActiveRecord and Mongoid extension for easier date scopes and time ranges"
+ s.description = "ActiveRecord and Mongoid extension for easier date scopes and time ranges"
- s.required_rubygems_version = ">= 1.3.6"
- s.rubyforge_project = "by_star"
+ s.post_install_message = File.read('UPGRADING') if File.exists?('UPGRADING')
+ s.add_dependency "activesupport"
+
+ s.add_development_dependency "chronic"
s.add_development_dependency "bundler", ">= 1.0.0"
s.add_development_dependency "sqlite3"
+ s.add_development_dependency "activerecord"
+ s.add_development_dependency "mongoid"
s.add_development_dependency "pg"
s.add_development_dependency "mysql2"
s.add_development_dependency "rspec-rails", "~> 2.14"
s.add_development_dependency "timecop", "~> 0.3"
- s.add_development_dependency "mongoid" if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
s.add_development_dependency "pry"
- s.add_dependency "activerecord"
- s.add_dependency "chronic"
-
- s.files = `git ls-files`.split("\n")
- s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
- s.require_path = 'lib'
+ s.files = `git ls-files`.split($/)
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
+ s.require_paths = ['lib']
end
View
76 lib/by_star.rb
@@ -1,71 +1,15 @@
-require 'chronic'
-
-require 'by_star/time_ext'
-require 'by_star/instance_methods'
-
-require 'by_star/by_direction'
-require 'by_star/by_year'
-require 'by_star/by_month'
-require 'by_star/by_fortnight'
-require 'by_star/by_week'
-require 'by_star/by_weekend'
-require 'by_star/by_day'
-require 'by_star/by_quarter'
-
-module ByStar
-
- def by_star_field(field=nil)
- @by_star_field ||= field
- @by_star_field || "#{self.table_name}.created_at"
- end
-
- include ByDirection
- include ByYear
- include ByMonth
- include ByFortnight
- include ByWeek
- include ByWeekend
- include ByDay
- include ByQuarter
-
- class ParseError < StandardError
-
- end
-
- # Returns all records between a given start and finish time.
- #
- # Currently only supports Time objects.
- def between(start, finish, options={})
- field = options[:field] || by_star_field
- scope = where("#{field} >= ? AND #{field} <= ?",
- start, finish)
- scope = scope.order(options[:order]) if options[:order]
- scope
- end
- alias_method :between_times, :between
-
- private
-
- # Used inside the by_* methods to determine what kind of object "time" is.
- # These methods take the result of the time_klass method, and call other methods
- # using it, such as by_year_Time and by_year_String.
- def time_klass(time)
- case time
- when ActiveSupport::TimeWithZone
- Time
- else
- time.class
- end
- end
-
-end
+require 'by_star/kernel'
+require 'by_star/normalization'
+require 'by_star/between'
+require 'by_star/directional'
+require 'by_star/base'
if defined?(ActiveRecord)
- ActiveRecord::Base.send :extend, ByStar
- ActiveRecord::Relation.send :extend, ByStar
- ActiveRecord::Base.send :include, ByStar::InstanceMethods
+ require 'by_star/orm/active_record/by_star'
+ ActiveRecord::Base.send :include, ByStar::ActiveRecord
+ ActiveRecord::Relation.send :extend, ByStar::ActiveRecord::ClassMethods
end
if defined?(Mongoid)
- require 'mongoid/by_star'
-end
+ require 'by_star/orm/mongoid/by_star'
+end
View
53 lib/by_star/base.rb
@@ -0,0 +1,53 @@
+module ByStar
+
+ module Base
+
+ include ByStar::Between
+ include ByStar::Directional
+
+ def by_star_field(start_field = nil, end_field = nil, options = {})
+ @by_star_start_field ||= start_field
+ @by_star_end_field ||= end_field
+ @by_star_offset ||= options[:offset]
+ end
+
+ def by_star_offset(options = {})
+ (options[:offset] || @by_star_offset || 0).seconds
+ end
+
+ def by_star_start_field(options={})
+ field = options[:field] ||
+ options[:start_field] ||
+ @by_star_start_field ||
+ by_star_default_field
+ field.to_s
+ end
+
+ def by_star_end_field(options={})
+ field = options[:field] ||
+ options[:end_field] ||
+ @by_star_end_field ||
+ by_star_start_field
+ field.to_s
+ end
+
+ protected
+
+ # Wrapper function which extracts time and options for each by_star query.
+ # Note the following syntax examples are valid:
+ #
+ # Post.by_month # defaults to current time
+ # Post.by_month(2, :year => 2004) # February, 2004
+ # Post.by_month(Time.now)
+ # Post.by_month(Time.now, :field => "published_at")
+ # Post.by_month(:field => "published_at")
+ #
+ def with_by_star_options(*args, &block)
+ options = args.extract_options!.symbolize_keys!
+ time = args.first
+ time ||= Time.zone.local(options[:year]) if options[:year]
+ time ||= Time.zone.now
+ block.call(time, options)
+ end
+ end
+end
View
80 lib/by_star/between.rb
@@ -0,0 +1,80 @@
+module ByStar
+
+ module Between
+
+ def between_times(start, finish, options={})
+ offset = by_star_offset(options)
+ between_times_query(start + offset, finish + offset, options)
+ end
+
+ def by_day(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.time(time)
+ between_times(time.beginning_of_day, time.end_of_day, options)
+ end
+ end
+
+ def today(options={})
+ by_day(Time.zone.now, options)
+ end
+
+ def yesterday(options={})
+ by_day(Time.zone.now.yesterday, options)
+ end
+
+ def tomorrow(options={})
+ by_day(Time.zone.now.tomorrow, options)
+ end
+
+ def by_week(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.week(time, options)
+ start_day = Array(options[:start_day])
+ between_times(time.beginning_of_week(*start_day), time.end_of_week(*start_day), options)
+ end
+ end
+
+ def by_weekend(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.week(time, options)
+ between_times(time.beginning_of_weekend, time.end_of_weekend, options)
+ end
+ end
+
+ def by_fortnight(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.fortnight(time, options)
+ between_times(time.beginning_of_fortnight, time.end_of_fortnight, options)
+ end
+ end
+
+ def by_month(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.month(time, options)
+ between_times(time.beginning_of_month, time.end_of_month, options)
+ end
+ end
+
+ def by_calendar_month(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.month(time, options)
+ start_day = Array(options[:start_day])
+ between_times(time.beginning_of_calendar_month(*start_day), time.end_of_calendar_month(*start_day), options)
+ end
+ end
+
+ def by_quarter(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.quarter(time, options)
+ between_times(time.beginning_of_quarter, time.end_of_quarter, options)
+ end
+ end
+
+ def by_year(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.year(time, options)
+ between_times(time.beginning_of_year, time.end_of_year, options)
+ end
+ end
+ end
+end
View
34 lib/by_star/by_day.rb
@@ -1,34 +0,0 @@
-module ByStar
- module ByDay
- def by_day(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first
- time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- send("by_day_#{time_klass(time)}", time, options)
- end
-
- def today(options={})
- by_day_Time(Time.zone.now, options)
- end
-
- def yesterday(options={})
- by_day_Time(Time.zone.now.yesterday, options)
- end
-
- def tomorrow(options={})
- by_day_Time(Time.zone.now.tomorrow, options)
- end
-
- private
-
- def by_day_Time(time, options)
- between(time.beginning_of_day, time.end_of_day, options)
- end
- alias_method :by_day_Date, :by_day_Time
-
- def by_day_String(string, options)
- by_day_Time(Time.parse(string), options)
- end
- end
-end
View
52 lib/by_star/by_direction.rb
@@ -1,52 +0,0 @@
-module ByStar
- module ByDirection
- def before(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first || Time.zone.now
- send("before_#{time_klass(time)}", time, options)
- end
- alias_method :before_now, :before
-
- def after(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first || Time.zone.now
- send("after_#{time_klass(time)}", time, options)
- end
- alias_method :after_now, :after
-
- private
-
- def before_Time_or_Date(time_or_date, options={})
- field = options[:field] || by_star_field
- where("#{field} <= ?", time_or_date)
- end
- alias_method :before_Time, :before_Time_or_Date
- alias_method :before_Date, :before_Time_or_Date
-
- def before_String(string, options={})
- field = options[:field] || by_star_field
- if time = Chronic.parse(string)
- where("#{field} <= ?", time)
- else
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
- end
- end
-
- def after_Time_or_Date(time_or_date, options={})
- field = options[:field] || by_star_field
- where("#{field} >= ?", time_or_date)
- end
- alias_method :after_Time, :after_Time_or_Date
- alias_method :after_Date, :after_Time_or_Date
-
- def after_String(string, options={})
- field = options[:field] || by_star_field
- if time = Chronic.parse(string)
- where("#{field} >= ?", time)
- else
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
- end
- end
-
- end
-end
View
58 lib/by_star/by_fortnight.rb
@@ -1,58 +0,0 @@
-module ByStar
- module ByFortnight
- # For reasoning why I use *args rather than variables here,
- # please see the by_year method comments in lib/by_star/by_year.rb
-
- def by_fortnight(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first
- time ||= Time.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- send("by_fortnight_#{time_klass(time)}", time, options)
- end
-
- private
-
- def by_fortnight_Time(time, options={})
- # We want to get the current fortnight and so...
- # We need to find the current week number and take one from it,
- # so that we are correctly offset from the start of the year.
- # The first fortnight of the year should of course start on the 1st January,
- # and not the beginning of that week.
- start_time = time.beginning_of_year + (time.strftime("%U").to_i - 1).weeks
- between(start_time, (start_time + 2.weeks).end_of_day, options)
- end
-
- def by_fortnight_Date(date, options={})
- by_fortnight_Time(date.to_time, options)
- end
-
- def by_fortnight_String_or_Fixnum(weeks, options={})
- weeks = weeks.to_i
- current_time = Time.zone.local(options[:year] || Time.zone.now.year)
- if weeks <= 26
- start_time = current_time + (weeks * 2).weeks
- between(start_time, (start_time + 2.weeks).end_of_day, options)
- else
- raise ParseError, "by_fortnight takes only a Time, Date or a Fixnum (less than or equal to 26)."
- end
- end
- alias_method :by_fortnight_String, :by_fortnight_String_or_Fixnum
- alias_method :by_fortnight_Fixnum, :by_fortnight_String_or_Fixnum
-
-
-
- # def omg
- # time.beginning_of_year + (time.strftime("%U").to_i).weeks
- # if time.is_a?(Numeric) && time <= 26
- # Time.utc(year, 1, 1) + ((time.to_i) * 2).weeks
- # else
- # raise ParseError, "by_fortnight takes only a Time or Date object, a Fixnum (less than or equal to 26) or a Chronicable string."
- # end
-
- # between(start_time.beginning_of_week, start_time + 2.weeks)
- # end
-
-
- end
-end
View
47 lib/by_star/by_month.rb
@@ -1,47 +0,0 @@
-module ByStar
- module ByMonth
- # For reasoning why I use *args rather than variables here,
- # please see the by_year method comments in lib/by_star/by_year.rb
-
- def by_month(*args)
- options = args.extract_options!
- time = args.first || Time.zone.now
- send("by_month_#{time_klass(time)}", time, false, options)
- end
-
- def by_calendar_month(*args)
- options = args.extract_options!
- time = args.first || Time.zone.now
- send("by_month_#{time_klass(time)}", time, true, options)
- end
-
- private
-
- def by_month_Time(time, is_calendar=false, options={})
- if is_calendar
- between(time.beginning_of_month.beginning_of_week, time.end_of_month.end_of_week, options)
- else
- between(time.beginning_of_month, time.end_of_month, options)
- end
- end
-
- def by_month_Date(date, is_calendar=false, options={})
- by_month_Time(date.to_time, is_calendar, options)
- end
-
- def by_month_String_or_Fixnum(month, is_calendar=false, options={})
- begin
- year = options[:year] || Time.zone.now.year
- date = Date.parse("#{year}-#{month}-01").to_time
- by_month_Time(date, is_calendar, options)
-
- rescue
- raise ParseError, "Month must be a number between 1 and 12 or the full month name (e.g. 'January', 'Feburary', etc.)"
- end
- end
-
- alias_method :by_month_String, :by_month_String_or_Fixnum
- alias_method :by_month_Fixnum, :by_month_String_or_Fixnum
-
- end
-end
View
32 lib/by_star/by_quarter.rb
@@ -1,32 +0,0 @@
-module ByStar
- module ByQuarter
- # For reasoning why I use *args rather than variables here,
- # please see the by_year method comments in lib/by_star/by_year.rb
-
- def by_quarter(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first
- time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- send("by_quarter_#{time_klass(time)}", time, options)
- end
-
- private
-
- def by_quarter_Time(time, options={})
- between(time.beginning_of_quarter, time.end_of_quarter, options)
- end
-
- def by_quarter_Date(date, options={})
- by_quarter_Time(date.to_time, options)
- end
-
- def by_quarter_Fixnum(quarter, options={})
- raise 'Quarter must be a number between 1 and 4' unless quarter >= 1 && quarter <= 4
- time = Time.zone.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- start_time = time.beginning_of_year + ((quarter.to_i - 1) * 3).months
- between(start_time, start_time.end_of_quarter, options)
- end
- end
-end
View
32 lib/by_star/by_week.rb
@@ -1,32 +0,0 @@
-module ByStar
- module ByWeek
-
- # For reasoning why I use *args rather than variables here,
- # please see the by_year method comments in lib/by_star/by_year.rb
-
- def by_week(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first
- time ||= Time.zone.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- send("by_week_#{time_klass(time)}", time, options)
- end
-
- private
-
- def by_week_Time(time, options={})
- between(time.beginning_of_week, time.end_of_week, options)
- end
-
- def by_week_Date(date, options={})
- by_week_Time(date.to_time, options)
- end
-
- def by_week_Fixnum(week, options={})
- time = Time.zone.local(options[:year], 1, 1) if options[:year]
- time ||= Time.zone.now
- start_time = time.beginning_of_year + week.to_i.weeks
- between(start_time, (start_time + 1.week).end_of_day, options)
- end
- end
-end
View
20 lib/by_star/by_weekend.rb
@@ -1,20 +0,0 @@
-module ByStar
- module ByWeekend
- def by_weekend(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first
- time ||= Time.zone.now
- send("by_weekend_#{time_klass(time)}", time, options)
- end
-
- private
-
- def by_weekend_Time(time, options={})
- between(time.beginning_of_weekend, time.end_of_weekend)
- end
-
- def by_weekend_Date(date, options={})
- by_weekend_Time(date.to_time, options)
- end
- end
-end
View
62 lib/by_star/by_year.rb
@@ -1,62 +0,0 @@
-module ByStar
- module ByYear
-
- # NOTE: We would define this as this:
- #
- # def by_year(time=Time.zone.now, options={})
- #
- # But, there's a potential situation where people want to just pass options.
- # Like this:
- #
- # Post.by_year(:field => "published_at")
- #
- # And so, we support any number of arguments and just parse them as necessary.
- # By doing it this way, we can support *both* this:
- #
- # Post.by_year(2012, :field => "published_at")
- #
- # And this:
- #
- # Post.by_year(:field => "published_at")
- #
- # This is because the time variable is going to be defaulting to the current time.
-
- def by_year(*args)
- options = args.extract_options!.symbolize_keys!
- time = args.first || Time.zone.now
-
- send("by_year_#{time_klass(time)}", time, options)
- end
-
- private
-
- def by_year_Time(time, options={})
- between(time.beginning_of_year, time.end_of_year, options)
- end
-
- def by_year_Date(date, options={})
- by_year_Time(date.to_time, options)
- end
-
- def by_year_String_or_Fixnum(year, options={})
- by_year_Time("#{work_out_year(year)}-01-01".to_time, options)
- end
- alias_method :by_year_String, :by_year_String_or_Fixnum
- alias_method :by_year_Fixnum, :by_year_String_or_Fixnum
-
- def work_out_year(value)
- case value.to_i
- when 0..39
- 2000 + value
- when 40..99
- 1900 + value
- when nil
- Time.zone.now.year
- else
- # We may be passed something that's not a straight out integer
- # These things include: BigDecimals, Floats and Strings.
- value.to_i
- end
- end
- end
-end
View
21 lib/by_star/directional.rb
@@ -0,0 +1,21 @@
+module ByStar
+
+ module Directional
+
+ def before(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.time(time)
+ before_query(time, options)
+ end
+ end
+ alias_method :before_now, :before
+
+ def after(*args)
+ with_by_star_options(*args) do |time, options|
+ time = ByStar::Normalization.time(time)
+ after_query(time, options)
+ end
+ end
+ alias_method :after_now, :after
+ end
+end
View
15 lib/by_star/instance_methods.rb
@@ -1,15 +0,0 @@
-module ByStar
- module InstanceMethods
- def previous(options={})
- field = options[:field] || self.class.by_star_field
- field = field.split(".").last
- self.class.where("#{field} < ?", self.send(field)).reorder("#{field} DESC").first
- end
-
- def next(options={})
- field = options[:field] || self.class.by_star_field
- field = field.split(".").last
- self.class.where("#{field} > ?", self.send(field)).reorder("#{field} ASC").first
- end
- end
-end
View
41 lib/by_star/kernel.rb
@@ -0,0 +1,41 @@
+module ByStar
+
+ module Kernel
+
+ module Time
+
+ # A "Weekend" is defined as the 60-hour period from 15:00 Friday to 03:00 Monday.
+ # The weekend for a given date will be the the next weekend if the day Mon-Thurs,
+ # otherwise the current weekend if the day is Fri-Sun.
+ def beginning_of_weekend
+ beginning_of_week(:monday).advance(:days => 4) + 15.hours
+ end
+
+ def end_of_weekend
+ (beginning_of_weekend + 59.hours).end_of_hour
+ end
+
+ # A "Fortnight" is defined as a two week period, with the first fortnight of the
+ # year beginning on 1st January.
+ def beginning_of_fortnight
+ beginning_of_year + ((self - beginning_of_year) / 2.weeks).to_i * 2.weeks
+ end
+
+ def end_of_fortnight
+ (beginning_of_fortnight + 13.days).end_of_day
+ end
+
+ # A "Calendar Month" is defined as a month as it appears on a calendar, including days
+ # previous/following months which are part of the first/last weeks of the given month.
+ def beginning_of_calendar_month(*args)
+ beginning_of_month.beginning_of_week(*args)
+ end
+
+ def end_of_calendar_month(*args)
+ end_of_month.end_of_week(*args)
+ end
+ end
+ end
+end
+
+::Time.__send__(:include, ByStar::Kernel::Time)
View
118 lib/by_star/normalization.rb
@@ -0,0 +1,118 @@
+module ByStar
+
+ class ParseError < StandardError; end
+
+ module Normalization
+
+ class << self
+
+ def time(value)
+ case value
+ when String then time_string(value)
+ when DateTime then value.to_time
+ when Date then value.to_time_in_current_zone
+ else value
+ end
+ end
+
+ def time_string(value)
+ defined?(Chronic) ? time_string_chronic(value) : time_string_fallback(value)
+ end
+
+ def time_string_chronic(value)
+ Chronic.time_class = Time.zone
+ Chronic.parse(value) || raise(ByStar::ParseError, "Chronic could not parse String #{value.inspect}")
+ end
+
+ def time_string_fallback(value)
+ Time.zone.parse(value) || raise(ByStar::ParseError, "Cannot parse String #{value.inspect}")
+ end
+
+ def week(value, options={})
+ value = try_string_to_int(value)
+ case value
+ when Fixnum then week_fixnum(value, options)
+ else time(value)
+ end
+ end
+
+ def week_fixnum(value, options={})
+ raise ParseError, 'Week number must be between 0 and 52' unless value.in?(0..52)
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
+ time.beginning_of_year + value.to_i.weeks
+ end
+
+ def fortnight(value, options={})
+ value = try_string_to_int(value)
+ case value
+ when Fixnum then fortnight_fixnum(value, options)
+ else time(value)
+ end
+ end
+
+ def fortnight_fixnum(value, options={})
+ raise ParseError, 'Fortnight number must be between 0 and 26' unless value.in?(0..26)
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
+ time + (value * 2).weeks
+ end
+
+ def quarter(value, options={})
+ value = try_string_to_int(value)
+ case value
+ when Fixnum then quarter_fixnum(value, options)
+ else time(value)
+ end
+ end
+
+ def quarter_fixnum(value, options={})
+ raise ParseError, 'Quarter number must be between 1 and 4' unless value.in?(1..4)
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
+ time.beginning_of_year + ((value - 1) * 3).months
+ end
+
+ def month(value, options={})
+ value = try_string_to_int(value)
+ case value
+ when Fixnum, String then month_fixnum(value, options)
+ else time(value)
+ end
+ end
+
+ def month_fixnum(value, options={})
+ year = options[:year] || Time.zone.now.year
+ Time.zone.parse "#{year}-#{value}-01"
+ rescue
+ raise ParseError, 'Month must be a number between 1 and 12 or a month name'
+ end
+
+ def year(value, options={})
+ value = try_string_to_int(value)
+ case value
+ when Fixnum then year_fixnum(value)
+ else time(value)
+ end
+ end
+
+ def year_fixnum(value)
+ Time.zone.local(extrapolate_year(value))
+ end
+
+ def extrapolate_year(value)
+ case value.to_i
+ when 0..69
+ 2000 + value
+ when 70..99
+ 1900 + value
+ else
+ value.to_i
+ end
+ end
+
+ def try_string_to_int(value)
+ value.is_a?(String) ? Integer(value) : value
+ rescue
+ value
+ end
+ end
+ end
+end
View
52 lib/by_star/orm/active_record/by_star.rb
@@ -0,0 +1,52 @@
+module ByStar
+ module ActiveRecord
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ include ::ByStar::Base
+
+ # Returns all records between a given start and finish time.
+ #
+ # Currently only supports Time objects.
+ def between_times_query(start, finish, options={})
+ scope = if options[:strict] || by_star_start_field == by_star_end_field
+ where("#{by_star_start_field} >= ? AND #{by_star_end_field} <= ?", start, finish)
+ else
+ where("#{by_star_end_field} > ? AND #{by_star_start_field} < ?", start, finish)
+ end
+ scope = scope.order(options[:order]) if options[:order]
+ scope
+ end
+
+ alias_method :between, :between_times
+
+ protected
+
+ def by_star_default_field
+ "#{self.table_name}.created_at"
+ end
+
+ def before_query(time, options={})
+ field = by_star_start_field
+ where("#{field} <= ?", time)
+ end
+
+ def after_query(time, options={})
+ field = by_star_start_field
+ where("#{field} >= ?", time)
+ end
+ end
+
+ def previous(options={})
+ field = self.class.by_star_start_field
+ value = self.send(field.split(".").last)
+ self.class.where("#{field} < ?", value).reorder("#{field} DESC").first
+ end
+
+ def next(options={})
+ field = self.class.by_star_start_field
+ value = self.send(field.split(".").last)
+ self.class.where("#{field} > ?", value).reorder("#{field} ASC").first
+ end
+ end
+end
View
59 lib/by_star/orm/mongoid/by_star.rb
@@ -0,0 +1,59 @@
+# In keeping with Mongoid standards, this module must be included into your model class, i.e.
+#
+# include Mongoid::ByStar
+#
+module Mongoid
+ module ByStar
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ include ::ByStar::Base
+
+ def between_times_query(start, finish, options={})
+ scope = if options[:strict] || by_star_start_field == by_star_end_field
+ gte(by_star_start_field => start).lte(by_star_end_field => finish)
+ else
+ gt(by_star_end_field => start).lt(by_star_start_field => finish)
+ end
+ scope = scope.order_by(field => options[:order]) if options[:order]
+ scope
+ end
+
+ def by_star_end_field_with_mongoid(options = {})
+ database_field_name by_star_end_field_without_mongoid(options)
+ end
+ alias_method_chain :by_star_end_field, :mongoid
+
+ def by_star_start_field_with_mongoid(options = {})
+ database_field_name by_star_start_field_without_mongoid(options)
+ end
+ alias_method_chain :by_star_start_field, :mongoid
+
+ protected
+
+ def by_star_default_field
+ :created_at
+ end
+
+ def before_query(time, options={})
+ field = by_star_start_field
+ lte(field => time)
+ end
+
+ def after_query(time, options={})
+ field = by_star_start_field
+ gte(field => time)
+ end
+ end
+
+ def previous(options={})
+ field = self.class.by_star_start_field
+ self.class.lt(field => self.send(field)).desc(field).first
+ end
+
+ def next(options={})
+ field = self.class.by_star_start_field
+ self.class.gt(field => self.send(field)).asc(field).first
+ end
+ end
+end
View
21 lib/by_star/time_ext.rb
@@ -1,21 +0,0 @@
-class Time
- def beginning_of_weekend
- friday = case self.wday
- when 0
- self.end_of_week.beginning_of_day.advance(:days => -2)
- when 5
- self.beginning_of_day
- else
- self.beginning_of_week.advance(:days => 4)
- end
- # 3pm, Friday.
- (friday + 15.hours)
- end
-
- def end_of_weekend
- # 3am, Monday.
- # LOL I CHEATED.
- beginning_of_weekend + 3.days - 12.hours
- end
-end
-
View
2  lib/by_star/version.rb
@@ -1,3 +1,3 @@
module ByStar
- VERSION = "2.1.0.beta2"
+ VERSION = '2.2.0'
end
View
83 lib/mongoid/by_star.rb
@@ -1,83 +0,0 @@
-module Mongoid::ByStar
- extend ActiveSupport::Concern
-
- module ClassMethods
- include ByStar
-
- def by_star_field(field=nil)
- @by_star_field ||= field
- @by_star_field || :created_at
- end
-
- # override ByStar method
- def between(start, finish, options={})
- field = by_star_field_class(options)
- scope = gte(field => start).lte(field => finish)
- scope = scope.order_by(field => options[:order]) if options[:order]
- scope
- end
- alias_method :between_times, :between
-
- # override private methods in ByStar::ByDirection
- def before_Time_or_Date(time_or_date, options={})
- field = by_star_field_class(options)
- lte(field => time_or_date)
- end
- alias_method :before_Time, :before_Time_or_Date
- alias_method :before_Date, :before_Time_or_Date
-
- def before_String(string, options={})
- field = by_star_field_class(options)
- if time = Chronic.parse(string)
- lte(field => time)
- else
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
- end
- end
-
- def after_Time_or_Date(time_or_date, options={})
- field = by_star_field_class(options)
- gte(field => time_or_date)
- end
- alias_method :after_Time, :after_Time_or_Date
- alias_method :after_Date, :after_Time_or_Date
-
- def after_String(string, options={})
- field = by_star_field_class(options)
- if time = Chronic.parse(string)
- gte(field => time)
- else
- raise ParseError, "Chronic couldn't understand #{string.inspect}. Please try something else."
- end
- end
-
- protected
-
- def by_star_field_class(options={})
- field = options[:field] || by_star_field
- field = aliased_fields[field.to_s] if aliased_fields.has_key?(field.to_s)
- field.to_sym
- end
- end
-
- include ByStar::InstanceMethods
-
- # override ByStar::InstanceMethods methods
- def previous(options={})
- field = by_star_field_instance(options)
- self.class.lt(field => self.send(field)).desc(field).first
- end
-
- def next(options={})
- field = by_star_field_instance(options)
- self.class.gt(field => self.send(field)).asc(field).first
- end
-
- protected
-
- def by_star_field_instance(options={})
- field = options[:field] || self.class.by_star_field
- field = self.class.aliased_fields[field.to_s] if self.class.aliased_fields.has_key?(field.to_s)
- field.to_sym
- end
-end
View
50 spec/by_star/active_record/active_record_spec.rb
@@ -1,50 +0,0 @@
-require 'spec_helper'
-Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
-
-describe ActiveRecord do
- before(:all) do
- ActiveRecord::Base.default_timezone = :utc
-
- db_config = YAML::load_file(File.dirname(__FILE__) + "/../../database.yml")
- if db_config.has_key?('sqlite') && db_config['sqlite'].has_key?('database')
- db_config['sqlite']['database'] = File.dirname(__FILE__) + '/../../tmp/' + db_config['sqlite']['database']
- end
-
- ActiveRecord::Base.configurations = db_config
- ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite")
- load File.dirname(__FILE__) + "/../../fixtures/active_record/schema.rb"
- load File.dirname(__FILE__) + "/../../fixtures/active_record/models.rb"
- load File.dirname(__FILE__) + "/../../fixtures/shared/seeds.rb"
-
- ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/../../tmp/activerecord.log")
- end
-
- it_behaves_like "by day"
- it_behaves_like "by direction"
- it_behaves_like "by fortnight"
- it_behaves_like "by month"
- it_behaves_like "by quarter"
- it_behaves_like "by week"
- it_behaves_like "by weekend"
- it_behaves_like "by year"
-
- it "should be able to order the result set" do
- scope = Post.by_year(Time.zone.now.year, :order => "created_at DESC")
- scope.order_values.should == ["created_at DESC"]
- end
-
- describe "#between" do
- it "should return an ActiveRecord::Relation object" do
- Post.between(Date.today - 2, Date.today).class.should == ActiveRecord::Relation
- end
- it "should return a result set between two times" do
- Post.between(Date.today - 2, Date.today).count.should == 1
- end
- end
-
- describe "#between_times" do
- it "should be an alias of #between" do
- Post.between_times(Date.today - 2, Date.today).should == Post.between(Date.today - 2, Date.today)
- end
- end
-end if testing_active_record?
View
44 spec/by_star/mongoid/mongoid_spec.rb
@@ -1,44 +0,0 @@
-require 'spec_helper'
-Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
-
-describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3') do
-
- before(:all) do
- DATABASE_NAME = "mongoid_#{Process.pid}"
-
- Mongoid.configure do |config|
- config.connect_to DATABASE_NAME
- end
-
- load File.dirname(__FILE__) + "/../../fixtures/mongoid/models.rb"
- load File.dirname(__FILE__) + "/../../fixtures/shared/seeds.rb"
- end
-
- after(:all) do
- Mongoid.purge!
- end
-
- it_behaves_like "by day"
- it_behaves_like "by direction"
- it_behaves_like "by fortnight"
- it_behaves_like "by month"
- it_behaves_like "by quarter"
- it_behaves_like "by week"
- it_behaves_like "by weekend"
- it_behaves_like "by year"
-
- describe "#between" do
- it "should return a Mongoid::Critera object" do
- Post.between(Date.today - 2, Date.today).class.should == Mongoid::Criteria
- end
- it "should return a result set between two times" do
- Post.between(Date.today - 2, Date.today).count.should == 1
- end
- end
-
- describe "#between_times" do
- it "should be an alias of #between" do
- Post.between_times(Date.today - 2, Date.today).should == Post.between(Date.today - 2, Date.today)
- end
- end
-end if testing_mongoid?
View
62 spec/by_star/shared/by_day.rb
@@ -1,62 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by day" do
- describe "by day" do
- def posts_count(*args)
- Post.by_day(*args).count
- end
-
- it "should be able to find a post for today" do
- posts_count.should eql(4)
- end
-
- it "should be able to find a post by a given date in last year" do
- posts_count(:year => Time.zone.now.year - 1).should eql(1)
- end
-
- it "should be able to use an alternative field" do
- Event.by_day(Time.now.yesterday, :field => "start_time").size.should eql(1)
- end
-
- it "should be able to use a date" do
- posts_count(Date.today).should eql(4)
- end
-
- it "should be able to use a String" do
- posts_count(Date.today.to_s).should eql(4)
- end
- end
-
- describe "today" do
- it "should show the post for today" do
- Post.today.map(&:text).should include("Today's post")
- end
-
- it "should be able to use an alternative field" do
- # Test may occur on an event day.
- Event.today(:field => "start_time").size.should eql(1)
- end
- end
-
- describe "yesterday" do
-
- it "should show the post for yesterday" do
- Post.yesterday.map(&:text).should include("Yesterday's post")
- end
-
- it "should be able to use an alternative field" do
- Event.yesterday(:field => "start_time").size.should eql(1)
- end
-
- end
-
- describe "tomorrow" do
- it "should show the post for tomorrow" do
- Post.tomorrow.map(&:text).should include("Tomorrow's post")
- end
-
- it "should be able to use an alternative field" do
- Event.tomorrow(:field => "start_time").size.should eql(1)
- end
- end
-end
View
96 spec/by_star/shared/by_direction.rb
@@ -1,96 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by direction" do
- describe "before" do
- def posts(*args)
- Post.before(*args)
- end
-
- it "should show the correct number of posts in the past" do
- posts.count.should == 5
- end
-
- it "is aliased as before_now" do
- Post.before_now.count.should == 5
- end
-
- it "should find for a given time" do
- posts(Time.zone.now - 2.days).count.should eql(2)
- end
-
- it "should find for a given date" do
- posts(Date.today - 2).count.should eql(2)
- end
-
- it "should find for a given string" do
- posts = posts("next tuesday").map(&:text)
- posts.should include("This time, last year")
- posts.should include("Yesterday's post")
- posts.should include("post 1")
- posts.should include("Today's post")
- posts.should include("The 'Current' Fortnight")
- posts.should include("The 'Current' Week")
- posts.should include("Tomorrow's post")
- # Next tuesday falls on different days each year, causing this post to sometimes be there, sometimes not.
- if Post.find_by_text("Weekend").created_at < Chronic.parse("next tuesday")
- posts.should include("Weekend")
- end
- end
-
- it "raises an exception when Chronic can't parse" do
- lambda { posts(";aosdjbjisdabdgofbi").count }.should raise_error(ByStar::ParseError)
- end
-
- it "should be able to find all events before Ryan's birthday using a non-standard field" do
- Event.before(Time.local(Time.zone.now.year+2), :field => "start_time").count.should eql(8)
- end
- end
-
- describe "future" do
- def posts_count(*args)
- Post.after(*args).count
- end
-
- it "should show the correct number of posts in the future" do
- Post.after.count.should eql(posts_count)
- Post.after_now.count.should eql(posts_count)
- end
-
- it "should find for a given date" do
- posts_count(Date.today - 2).should eql(19)
- end
-
- it "should find for a given string" do
- posts_count("next tuesday").should eql(13)
- end
-
- it "should be able to find all events before Dad's birthday using a non-standard field" do
- Event.after(Time.zone.local(Time.zone.now.year, 7, 5), :field => "start_time").count.should eql(3)
- end
- end
-
- describe "previous and next" do
- let(:current_post) { Post.find_by_text("post 1") }
- let(:current_event) { Event.find_by_name("Mum's birthday!") }
-
- context "previous" do
- it "can find the previous post" do
- current_post.previous.text.should == "Yesterday's post"
- end
-
- it "takes the field option" do
- current_event.previous(:field => "start_time").name.should == "Dad's birthday!"
- end
- end
-
- context "next" do
- it "can find the next post" do
- current_post.next.text.should == "Today's post"
- end
-
- it "takes the field option" do
- current_event.next(:field => "start_time").name.should == "Ryan's birthday!"
- end
- end
- end
-end
View
47 spec/by_star/shared/by_fortnight.rb
@@ -1,47 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by fortnight" do
- describe "by fortnight" do
- def find_posts(*args)
- Post.by_fortnight(*args)
- end
-
- def posts_count(*args)
- find_posts(*args).count
- end
-
- it "should be able to find posts in the current fortnight" do
- posts_count.should eql(6)
- end
-
- it "should be able to find posts in the 1st fortnight of the current year" do
- posts_count(0).should eql(6)
- posts_count("0").should eql(6)
- # There was previously a situation where incorrect time zone math
- # caused the 'post 1' post to NOT appear, so count would be 7, rather than 8.
- # So this line simply regression tests that problem.
- Post.by_fortnight(0).map(&:text).should include("post 1")
- end
-
- it "should be able to find posts for a fortnight ago" do
- posts_count(2.weeks.ago).should eql(2)
- end
-
- it "should be able to find posts for a given fortnight in a year" do
- posts_count(0, :year => Time.zone.now.year - 1).should eql(1)
- end
-
- it "should be able to find posts for the current fortnight in a specific year" do
- posts_count(:year => Time.zone.now.year - 1).should eql(1)
- end
-
- it "should raise an error when given an invalid argument" do
- lambda { find_posts(27) }.should raise_error(ByStar::ParseError, "by_fortnight takes only a Time, Date or a Fixnum (less than or equal to 26).")
- end
-
- it "should be able to use an alternative field" do
- Event.by_fortnight(nil, :field => "start_time").count.should eql(2)
- end
- end
-end
-
View
109 spec/by_star/shared/by_month.rb
@@ -1,109 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by month" do
- describe "by month" do
- def find_posts(time=Time.zone.now, options={})
- Post.by_month(time, options)
- end
-
- def posts_count(time=Time.zone.now, options={})
- find_posts(time, options).count
- end
-
- it "should be able to find posts for the current month" do
- posts_count.should eql(6)
- end
-
- it "should be able to find a single post for January" do
- # If it is January we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not January.
- posts_count("January").should eql(6)
- end
-
- it "should be able to find two posts for the 2nd month" do
- # If it is February we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not February.
- posts_count(2).should eql(1)
- end
-
- it "should be able to find three posts for the 3rd month, using time instance" do
- # If it is March we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not March.
- time = Time.local(Time.zone.now.year, 3, 1)
- posts_count(time).should eql(1)
- end
-
- it "should be able to find a single post from January last year" do
- posts_count("January", :year => Time.zone.now.year - 1).should eql(1)
- end
-
- it "should fail when given incorrect months" do
- lambda { find_posts(0) }.should raise_error(ByStar::ParseError)
- lambda { find_posts(13) }.should raise_error(ByStar::ParseError)
- lambda { find_posts("Ryan") }.should raise_error(ByStar::ParseError)
- # LOL arrays
- lambda { find_posts([1,2,3]) }.should raise_error(NoMethodError)
- end
-
- it "should be able to use an alternative field" do
- Timecop.freeze(Time.zone.local(Time.zone.now.year, 12, 1, 0, 0, 0)) do
- Event.by_month(:field => "start_time").size.should eql(1)
- end
- end
-
- it "should be able to specify the year as a string" do
- posts_count(1, :year => (Time.zone.now.year - 1).to_s).should eql(1)
- end
-
- it "should be able to accept time datatype" do
- posts_count(Time.now - 1.year).should eql(1)
- end
-
- it "should be able to accept date datatype" do
- posts_count(Date.today - 1.year).should eql(1)
- end
- end
-
- describe "by calendar month" do
- def find_posts(time=Time.zone.now, options={})
- Post.by_calendar_month(time, options)
- end
-
- def posts_count(time=Time.zone.now, options={})
- find_posts(time, options).count
- end
-
- it "should be able to find posts for the current calendar month" do
- posts_count.should eql(8)
- end
-
- it "should be able to find a single post for January calendar month" do
- # If it is January we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not January.
- posts_count("January").should eql(8)
- end
-
- it "should be able to find two posts for the 2nd calendar month" do
- # If it is February we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not February.
- posts_count(2).should eql(2)
- end
-
- it "should be able to find three posts for the 3rd calendar month, using time instance" do
- # If it is March we'll have all the "current" posts in there.
- # This makes the count 10.
- # I'm sure you can guess what happens when it's not March.
- time = Time.local(Time.zone.now.year, 3, 1)
- posts_count(time).should eql(1)
- end
-
- it "should be able to find a single post from January last year" do
- posts_count("January", :year => Time.zone.now.year - 1).should eql(1)
- end
- end
-end
View
33 spec/by_star/shared/by_quarter.rb
@@ -1,33 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by quarter" do
- describe "by quarter" do
- def posts_count(*args)
- find_posts(*args).count
- end
-
- def find_posts(*args)
- Post.by_quarter(*args)
- end
-
- it "should be able to find posts in the current quarter" do
- posts_count.should eql(8)
- end
-
- it "should be able to find posts in the 1st quarter" do
- posts_count(1).should eql(8)
- end
-
- it "should be able to find posts in the 1st quarter of last year" do
- posts_count(1, :year => Time.zone.now.year-1).should eql(1)
- end
-
- it "should be able to use an alternative field" do
- Event.by_quarter(:field => "start_time").size.should eql(3)
- end
-
- it "should find posts at the last quarter of the year" do
- posts_count(Time.zone.now.end_of_year).should eql(4)
- end
- end
-end
View
41 spec/by_star/shared/by_week.rb
@@ -1,41 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by week" do
- describe "by week" do
- def posts_count(*args)
- find_posts(*args).count
- end
-
- def find_posts(*args)
- Post.by_week(*args)
- end
-
- it "should be able to find posts in the current week" do
- posts_count.should eql(5)
- end
-
- it "should be able to find posts in the 1st week" do
- posts_count(0).should eql(6)
- end
-
- it "should be able to find posts in the 1st week of last year" do
- posts_count(0, :year => Time.zone.now.year-1).should eql(1)
- end
-
- it "should not find any posts from a week ago" do
- posts_count(1.week.ago).should eql(1)
- end
-
- it "should be able to use an alternative field" do
- Event.by_week(:field => "start_time").size.should eql(2)
- end
-
- it "should find posts at the start of the year" do
- posts_count(0).should eql(6)
- end
-
- it "should find posts at the end of the year" do
- posts_count(Time.zone.now.end_of_year).should eql(1)
- end
- end
-end
View
13 spec/by_star/shared/by_weekend.rb
@@ -1,13 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by weekend" do
- describe "by weekend" do
- it "should be able to find the posts on the weekend of the 1st of January" do
- Post.by_weekend.count.should eql(6)
- end
-
- it "should be able to use an alternative field" do
- Event.by_weekend(:field => "start_time").count.should eql(3)
- end
- end
-end
View
54 spec/by_star/shared/by_year.rb
@@ -1,54 +0,0 @@
-require 'spec_helper'
-
-shared_examples_for "by year" do
- describe "by year" do
- def posts_count(*args)
- find_posts(*args).count
- end
-
- def find_posts(*args)
- options = args.extract_options!
- Post.by_year(args.first, options)
- end
-
- let(:this_years_posts) { 18 }
-
- it "should be able to find all the posts in the current year" do
- posts_count.should eql(this_years_posts)
- end
-
- it "should be able to find if given a string" do
- posts_count(Time.zone.now.year.to_s).should eql(this_years_posts)
- end
-
- it "should be able to find a single post from last year" do
- posts_count(Time.zone.now.year-1).should eql(3)
- end
-
- it "knows what last year's posts were" do
- find_posts(Time.zone.now.year-1).map(&:text).should =~ ["A week ago", "This time, last year", "Yesterday's post"]
- end
-
- it "can find posts given a 2-digit year" do
- # Should be good for at least a couple of years.
- posts_count(Time.zone.now.year-2001).should eql(3)
- end
-
- it "should be able to use an alternative field (string)" do
- Event.by_year(:field => "start_time").count.should eql(6)
- end
-
- it "should be able to use an alternative field (symbol)" do
- Event.by_year(:field => :start_time).count.should eql(6)
- end
-
- it "should not have to specify the field when using by_star_field" do
- Event.by_year.count.should eql(6)
- end
-
- it "should not include yesterday's (Dec 31st <last year>) event in by_year" do
- Event.by_year.map(&:name).should_not include("Yesterday")
- end
- end
-end
-
View
9 spec/fixtures/active_record/models.rb
@@ -1,13 +1,6 @@
class Post < ActiveRecord::Base
- default_scope :order => "#{quoted_table_name}.created_at ASC"
- has_and_belongs_to_many :tags
-
- def self.factory(text, created_at = nil)
- create!(:text => text, :created_at => created_at)
- end
end
class Event < ActiveRecord::Base
- by_star_field :start_time
- scope :secret, :conditions => { :public => false }
+ by_star_field :start_time, :end_time, offset: 3.hours
end
View
27 spec/fixtures/active_record/schema.rb
@@ -1,36 +1,11 @@
ActiveRecord::Schema.define do
self.verbose = false
- create_table :invoices, :force => true do |t|
- t.integer :value
- t.integer :number
- t.timestamps
- end
-
create_table :posts, :force => true do |t|
- t.string :text
- t.timestamps
- end
-
- create_table :posts_tags, :force => true, :id => false do |t|
- t.integer :post_id, :tag_id
- end
-
- create_table :tags, :force => true do |t|
- t.string :name
t.timestamps
end
create_table :events, :force => true do |t|
t.datetime :start_time, :end_time
- t.string :name
- t.boolean :public, :default => true
end
-
- create_table :day_entries, :force => true do |t|
- t.references :invoice
- t.datetime :spent_at
- t.string :name
- end
-
-end
+end
View
56 spec/fixtures/mongoid/models.rb
@@ -2,64 +2,14 @@ class Post
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::ByStar
-
- field :text, type: String
-
- default_scope order_by([[:created_at, :asc]])
- has_and_belongs_to_many :tags
-
- def self.factory(text, created_at = nil)
- create!(:text => text, :created_at => created_at)
- end
-
- def self.find_by_text(text)
- where(:text => text).first
- end
end
class Event
include Mongoid::Document
include Mongoid::ByStar
- field :st, as: :start_time, type: DateTime
- field :end_time, type: DateTime
- field :name, type: String
- field :public, type: Boolean, default: true
-
- by_star_field :start_time
- scope :secret, where(:public => false)
-
- def self.find_by_name(name)
- where(:name => name).first
- end
-end
-
-class Tag
- include Mongoid::Document
- include Mongoid::Timestamps
- include Mongoid::ByStar
-
- field :name, type: String
+ field :st, as: :start_time, type: Time
+ field :end_time, type: Time
- has_and_belongs_to_many :posts
+ by_star_field :start_time, :end_time, offset: 3.hours
end
-
-class Invoice
- include Mongoid::Document
- include Mongoid::ByStar
-
- field :value, type: Integer
- field :number, type: Integer
-
- has_many :day_entries
-end
-
-class DayEntry
- include Mongoid::Document
- include Mongoid::ByStar
-
- field :spent_at, type: DateTime
- field :name, type: String
-
- belongs_to :invoice
-end
View
49 spec/fixtures/shared/seeds.rb
@@ -1,35 +1,16 @@
-year = Time.zone.now.year
-
-1.upto(12) do |month|
- Post.factory "post #{month}", Time.zone.now.beginning_of_year + (month - 1).months
+%w(2013-12-31
+ 2014-01-01
+ 2014-01-01
+ 2014-01-05
+ 2014-01-10
+ 2014-01-12
+ 2014-01-20
+ 2014-02-01
+ 2014-02-15
+ 2014-03-01
+ 2014-03-15
+ 2014-04-01
+ 2014-04-15).map{|d| Time.zone.parse(d) + 17.hours }.each do |d|
+ Post.create!(:created_at => d)
+ Event.create!(:start_time => d - 5.days, :end_time => d + 5.days)
end
-
-Post.factory "Today's post", Time.zone.now
-Post.factory "Yesterday's post", 1.day.ago
-Post.factory "A week ago", 1.week.ago
-Post.factory "Tomorrow's post", 1.day.from_now
-Post.factory "This time, last year", 1.year.ago
-Post.factory "That's it!", Time.zone.now.end_of_year
-
-# For by_weekend scoped test
-# We need to calculate the weekend.
-weekend_time = Time.zone.now
-weekend_time += 1.day while weekend_time.wday != 6
-
-# For by_weekend scoped test
-post = Post.factory "Weekend", weekend_time
-
-# Offset by two seconds to stop it clashing with "Today's post" in next test
-post = Post.factory "The 'Current' Fortnight", Time.zone.now + 2.seconds
-
-post = Post.factory "The 'Current' Week", Time.zone.now + 2.seconds
-
-
-Event.create(:name => "Ryan's birthday!", :start_time => "04-12-#{Time.zone.now.year}".to_time)
-Event.create(:name => "Ryan's birthday, last year!", :start_time => "04-12-#{Time.zone.now.year-1}".to_time)
-Event.create(:name => "Dad's birthday!", :start_time => "05-07-#{Time.zone.now.year}".to_time)
-Event.create(:name => "Mum's birthday!", :start_time => "17-11-#{Time.zone.now.year}".to_time)
-Event.create(:name => "Today", :start_time => Time.zone.now)
-Event.create(:name => "Yesterday", :start_time => Time.zone.now.yesterday)
-Event.create(:name => "Tomorrow", :start_time => Time.zone.now.tomorrow)
-Event.create(:name => "FBI meeting", :start_time => "02-03-#{Time.zone.now.year}".to_time, :public => false)
View
50 spec/integration/active_record/active_record_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
+
+describe ActiveRecord do
+ before(:all) do
+ ActiveRecord::Base.default_timezone = :utc
+
+ db_config = YAML::load_file(File.dirname(__FILE__) + '/../../database.yml')
+ if db_config.has_key?('sqlite') && db_config['sqlite'].has_key?('database')
+ db_config['sqlite']['database'] = File.dirname(__FILE__) + '/../../tmp/' + db_config['sqlite']['database']
+ end
+
+ ActiveRecord::Base.configurations = db_config
+ ActiveRecord::Base.establish_connection(ENV['DB'] || 'sqlite')
+ load File.dirname(__FILE__) + '/../../fixtures/active_record/schema.rb'
+ load File.dirname(__FILE__) + '/../../fixtures/active_record/models.rb'
+ load File.dirname(__FILE__) + '/../../fixtures/shared/seeds.rb'
+
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/../../tmp/activerecord.log')
+ end
+
+ it_behaves_like 'by day'
+ it_behaves_like 'by direction'
+ it_behaves_like 'by fortnight'
+ it_behaves_like 'by month'
+ it_behaves_like 'by calendar month'
+ it_behaves_like 'by quarter'
+ it_behaves_like 'by week'
+ it_behaves_like 'by weekend'
+ it_behaves_like 'by year'
+ it_behaves_like 'offset parameter'
+
+ it 'should be able to order the result set' do
+ scope = Post.by_year(Time.zone.now.year, :order => 'created_at DESC')
+ scope.order_values.should == ['created_at DESC']
+ end
+
+ describe '#between_times' do
+ subject { Post.between_times(Time.parse('2014-01-01'), Time.parse('2014-01-06')) }
+ it { should be_a(ActiveRecord::Relation) }
+ its(:count) { should eq 3 }
+ end
+
+ describe '#between' do
+ subject { Post.between(Time.parse('2014-01-01'), Time.parse('2014-01-06')) }
+ it 'should be an alias of #between_times' do
+ subject.count.should eq 3
+ end
+ end
+end if testing_active_record?
View
43 spec/integration/mongoid/mongoid_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
+
+describe 'Mongoid' do
+
+ before(:all) do
+ DATABASE_NAME = "mongoid_#{Process.pid}"
+
+ Mongoid.configure do |config|
+ config.connect_to DATABASE_NAME
+ end
+
+ load File.dirname(__FILE__) + '/../../fixtures/mongoid/models.rb'
+ load File.dirname(__FILE__) + '/../../fixtures/shared/seeds.rb'
+ end
+
+ after(:all) do
+ Mongoid.purge!
+ end
+
+ it_behaves_like 'by day'
+ it_behaves_like 'by direction'
+ it_behaves_like 'by fortnight'
+ it_behaves_like 'by month'
+ it_behaves_like 'by calendar month'
+ it_behaves_like 'by quarter'
+ it_behaves_like 'by week'
+ it_behaves_like 'by weekend'
+ it_behaves_like 'by year'
+ it_behaves_like 'offset parameter'
+
+ describe '#between_times' do