lib/by_star.rb in by_star-0.2.5 vs lib/by_star.rb in by_star-0.3.0

- old
+ new

@@ -1,316 +1,23 @@ require 'chronic' +require 'shared' +require 'range_calculations' +require 'time_ext' +require 'vanilla' +Dir[File.dirname(__FILE__) + '/calculations/*.rb'].each { |file| require file } +require 'calculations' module ByStar def self.included(base) base.extend ClassMethods end module ClassMethods - # Examples: - # by_year(2010) - # # 2-digit year: - # by_year(10) - # # Time or Date object: - # by_year(time) - # # String: - # by_year("2010") - def by_year(time=Time.zone.now.year, options={}, &block) - year = work_out_year(time) - - start_time = Time.utc(year, 1, 1) - end_time = start_time.end_of_year - by_star(start_time, end_time, options, &block) - rescue ArgumentError - raise ParseError, "Invalid arguments detected, year may possibly be outside of valid range (1902-2039). This is no longer a problem on Ruby versions > 1.8.7, so we recommend you upgrade to at least 1.8.7." - end - - # Examples: - # by_month(1) - # by_month("January") - # by_month("January", :year => 2008) - # by_month(time) - def by_month(time=Time.zone.now.month, options={}, &block) - time = Time.zone.now.month if time.nil? - year = options[:year] ||= Time.zone.now.year - # Work out what actual month is. - month = if time.is_a?(Numeric) && (1..12).include?(time) - time - elsif valid_time_or_date?(time) - year = time.year - time.month - elsif time.is_a?(String) && Date::MONTHNAMES.include?(time) - Date::MONTHNAMES.index(time) - else - raise ParseError, "Value is not an integer (between 1 and 12), time object or string (make sure you typed the name right)." - end - - start_time = Time.utc(year, month, 1) - end_time = start_time.end_of_month - - by_star(start_time, end_time, options, &block) - end - - # Examples: - # # 18th fortnight of 2004 - # Post.by_fortnight(18, :year => 2004) - def by_fortnight(time=Time.zone.now, options = {}, &block) - time = parse(time) - - # If options[:year] is passed in, use that year regardless. - year = work_out_year(options[:year]) if options[:year] - # If the first argument is a date or time, ask it for the year - year ||= time.year unless time.is_a?(Numeric) - # If the first argument is a fixnum, assume this year. - year ||= Time.zone.now.year - - # Dodgy! - # Surely there's a method in Rails to do this. - start_time = if valid_time_or_date?(time) - time.beginning_of_year + (time.strftime("%U").to_i).weeks - elsif 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 - start_time = start_time.beginning_of_week - end_time = start_time + 2.weeks - by_star(start_time, end_time, options, &block) - end - - # Examples: - # # 36th week - # Post.by_week(36) - # Post.by_week(36.54) - # Post.by_week(36, :year => 2004) - # Post.by_week(<Time object>) - # Post.by_week(<Date object>) - # Post.by_week("next tuesday") - def by_week(time=Time.zone.now, options = {}, &block) - time = parse(time) - - # If options[:year] is passed in, use that year regardless. - year = work_out_year(options[:year]) if options[:year] - # If the first argument is a date or time, ask it for the year - year ||= time.year unless time.is_a?(Numeric) - # If the first argument is a fixnum, assume this year. - year ||= Time.now.year - - # Dodgy! - # Surely there's a method in Rails to do this. - start_time = if valid_time_or_date?(time) - weeks = time.strftime("%U").to_i - time.beginning_of_year - elsif time.is_a?(Numeric) && time < 53 - weeks = time.to_i - Time.utc(year, 1, 1) - else - raise ParseError, "by_week takes only a Time or Date object, a Fixnum (less than or equal to 53) or a Chronicable string." - end - start_time += weeks.weeks - end_time = start_time + 1.week - by_star(start_time, end_time, options, &block) - end - - - # Examples: - # Post.by_weekend - # Post.by_weekend(Time.now + 5.days) - # Post.by_weekend(Date.today + 5) - # Post.by_weekend("next tuesday") - def by_weekend(time=Time.zone.now, options = {}, &block) - time = parse(time) - start_time = time.beginning_of_weekend - by_star(start_time, (start_time + 1.day).end_of_day, options, &block) - end - - - # Examples: - # Post.by_current_weekend - def by_current_weekend(options = {}, &block) - time = Time.zone.now - # Friday, 3pm - start_time = time.beginning_of_weekend - # Monday, 3am - end_time = time.end_of_weekend - by_star(start_time, end_time, options, &block) - end - - # Examples: - # Post.by_current_work_week - def by_current_work_week(options = {}, &block) - time = Time.zone.now - # Monday, 3am - time = time + 1.week if time.wday == 6 || time.wday == 0 - start_time = time.beginning_of_week + 3.hours - # Friday, 3pm - end_time = time.beginning_of_weekend - by_star(start_time, end_time, options, &block) - end - - - # Examples: - # Post.by_day - # Post.by_day(Time.yesterday) - # Post.by_day("next tuesday") - def by_day(time = Time.zone.now, options = {}, &block) - time = parse(time) - by_star(time.beginning_of_day, time.end_of_day, options, &block) - end - alias_method :today, :by_day - - # Examples: - # Post.yesterday - # # 2 days ago: - # Post.yesterday(Time.yesterday) - # # day before next tuesday - # Post.yesterday("next tuesday") - def yesterday(time = Time.zone.now, options = {}, &block) - time = parse(time) - by_day(time.advance(:days => -1), options, &block) - end - - # Examples: - # Post.tomorrow - # # 2 days from now: - # Post.tomorrow(Time.tomorrow) - # # day after next tuesday - # Post.tomorrow("next tuesday") - def tomorrow(time = Time.zone.now, options = {}, &block) - time = parse(time) - by_day(time.advance(:days => 1), options, &block) - end - - # Scopes to records older than current or given time - # Post.past - # Post.past() - def past(time = Time.zone.now, options = {}, &block) - time = parse(time) - by_direction("<", time, options, &block) - end - - # Scopes to records newer than current or given time - def future(time = Time.zone.now, options = {}, &block) - time = parse(time) - by_direction(">", time, options, &block) - end - - private - - def by_direction(condition, time, options = {}, &block) - field = connection.quote_table_name(table_name) - field << "." << connection.quote_column_name(options[:field] || "created_at") - with_scope(:find => { :conditions => ["#{field} #{condition} ?", time.utc] }) do - if block_given? - with_scope(:find => block.call) do - find(:all) - end - else - find(:all) - end - end - end - - # scopes results between start_time and end_time - def by_star(start_time, end_time, options = {}, &block) - start_time = parse(start_time) - end_time = parse(end_time) - - raise ParseError, "End time is before start time, searching like this will return no results." if end_time < start_time - - field = options[:field] || "created_at" - with_scope(:find => { :conditions => { field => start_time.utc..end_time.utc } }) do - if block_given? - with_scope(:find => block.call) do - find(:all) - end - else - find(:all) - end - end - end - - alias :between :by_star - public :between - - # This will work for the next 30 years (written in 2009) - def work_out_year(value) - case value - 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 - - # Checks if the object is a Time, Date or TimeWithZone object. - def valid_time_or_date?(value) - value.is_a?(Time) || value.is_a?(Date) || value.is_a?(ActiveSupport::TimeWithZone) - end - - def parse(object) - object = case object.class.to_s - when "NilClass" - o = Time.zone.now - when "String" - o = object - Chronic.parse(object, :now => Time.zone.now) - when "Date" - object.to_time(:utc) - else - object - end - raise ParseError, "Chronic couldn't work out #{o.inspect}; please be more precise." if object.nil? - object - end - - def method_missing(method, *args) - if method.to_s =~ /^(as_of|up_to)_(.+)$/ - method = $1 - expr = $2.humanize - unless time = parse(expr) - raise ParseError, "Chronic couldn't work out #{expr.inspect}; please be more precise." - end - - reference = args.first || Time.now - - if "as_of" == method - between(time, reference) - else - between(reference, time) - end - else - super - end - end + include RangeCalculations + include Shared + include Vanilla + include Calculations end class ParseError < Exception; end class MonthNotFound < Exception; end end - -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 \ No newline at end of file