From a7da13fb82711dbb968af512780928e340844df6 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Fri, 6 May 2011 00:42:22 +0100 Subject: [PATCH] some initial work to using rocco for documentation --- LICENSE | 2 +- README.md | 2 +- Rakefile | 53 ++++++-- lib/time/ext.rb | 8 +- lib/time_ext.rb | 22 +++- lib/time_ext/calculations.rb | 141 ++++++++++++++-------- lib/time_ext/core_ext/numeric.rb | 6 +- lib/time_ext/core_ext/time.rb | 11 +- lib/time_ext/iterations.rb | 199 ++++++++++++++++++++----------- lib/time_ext/support.rb | 27 ----- lib/time_ext/version.rb | 2 + time_ext.gemspec | 2 +- 12 files changed, 306 insertions(+), 169 deletions(-) delete mode 100644 lib/time_ext/support.rb diff --git a/LICENSE b/LICENSE index 1147c56..a350b59 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010 Jim Myhrberg. +Copyright (c) 2011 Jim Myhrberg. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 0782324..e845f07 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ I recommend you read the complete class [documentation][docs] for the Time objec ## License and Copyright -Copyright (c) 2010 Jim Myhrberg. +Copyright (c) 2011 Jim Myhrberg. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Rakefile b/Rakefile index 7beca78..fab1698 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,9 @@ require 'bundler' Bundler::GemHelper.install_tasks +require 'rake/testtask' +require 'rake/clean' + # # Rspec @@ -20,18 +23,54 @@ task :default => :spec # -# Yard +# Rocco # -begin - require 'yard' - YARD::Rake::YardocTask.new -rescue LoadError - task :yardoc do - abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard" +require 'rocco/tasks' +Rocco::make 'docs/' + +desc 'Build rocco docs' +task :docs => :rocco +directory 'docs/' + +desc 'Build docs and open in browser for the reading' +task :read => :docs do + sh 'open docs/lib/time_ext.html' +end + +# Make index.html a copy of rocco.html +file 'docs/index.html' => 'docs/lib/time_ext.html' do |f| + cp 'docs/lib/time_ext.html', 'docs/index.html', :preserve => true +end +task :docs => 'docs/index.html' +CLEAN.include 'docs/index.html' + +# Alias for docs task +task :doc => :docs + +# GITHUB PAGES =============================================================== + +desc 'Update gh-pages branch' +task :pages => ['docs/.git', :docs] do + rev = `git rev-parse --short HEAD`.strip + Dir.chdir 'docs' do + sh "git add *.html" + sh "git commit -m 'rebuild pages from #{rev}'" do |ok,res| + if ok + verbose { puts "gh-pages updated" } + sh "git push -q o HEAD:gh-pages" + end + end end end +# Update the pages/ directory clone +file 'docs/.git' => ['docs/'] do |f| + sh "cd docs && git init -q && git remote add o ../.git" if !File.exist?(f.name) + sh "cd docs && git fetch -q o && git reset -q --hard o/gh-pages && touch ." +end +CLOBBER.include 'docs/.git' + # # Misc. diff --git a/lib/time/ext.rb b/lib/time/ext.rb index b2e5d9d..5a85a5f 100644 --- a/lib/time/ext.rb +++ b/lib/time/ext.rb @@ -1 +1,7 @@ -require 'time_ext' \ No newline at end of file +# encoding: utf-8 + +# Enable use by: +# require 'time/ext' +# In addition to the default: +# require 'time_ext' +require 'time_ext' diff --git a/lib/time_ext.rb b/lib/time_ext.rb index 73b975e..2509965 100644 --- a/lib/time_ext.rb +++ b/lib/time_ext.rb @@ -1,7 +1,25 @@ -require 'rubygems' +# encoding: utf-8 +require 'rubygems' +require 'active_support' +require 'time_ext/version' + +# [Calculation][ca] methods such as `#floor`, `#ceil`, `#round`, +# `#prev_week` and many more, adding on top of the defaults present in +# ActiveSupport. +# [ca]: time_ext/calculations.html require 'time_ext/calculations' + +# [Iteration][it] methods allowing the use of `#each`, `#map_each` and more on +# Time objects similar to how you loop over an Array of items. +# [it]: time_ext/iterations.html require 'time_ext/iterations' -require 'time_ext/support' + +# Core extention of [Time][ti] class to load in [Calculation][ca] and +# [Iteration][it] modules. +# [ti]: time_ext/core_ext/time.html require 'time_ext/core_ext/time' + +# Core extention of [Numeric][nu] class to set a couple of required aliases. +# [nu]: time_ext/core_ext/numeric.html require 'time_ext/core_ext/numeric' diff --git a/lib/time_ext/calculations.rb b/lib/time_ext/calculations.rb index 7eb9701..e7a7dbe 100644 --- a/lib/time_ext/calculations.rb +++ b/lib/time_ext/calculations.rb @@ -1,169 +1,191 @@ +# encoding: utf-8 + module TimeExt - # Adds an even greater extent of calculation methods on top of those already provided by ActiveSupport. + # Adds an even greater extent of calculation methods on top of those already + # provided by ActiveSupport. module Calculations - - # Returns a new Time representing the start of the unit specified (second by default). + + #### Public Methods + + # Returns a new Time representing the start of the unit specified (second + # by default). def floor(unit = :sec) self.send("beginning_of_#{unit}") end alias :beginning_of :floor alias :at_beginning_of :floor - + # Returns a new Time representing the start of the next unit specified (second by default). def ceil(unit = :sec) self.send("next_#{unit}").send("beginning_of_#{unit}") end alias :beginning_of_next :ceil alias :at_beginning_of_next :ceil - - # Returns a new Time representing the start of the current or next unit specified (second by default) depending which is closest + + # Returns a new Time representing the start of the current or next unit + # specified (second by default) depending which is closest def round(unit = :sec) next_unit = self.ceil(unit) this_unit = self.floor(unit) (self - this_unit) < (next_unit - self) ? this_unit : next_unit end alias :beginning_of_closest :round - - # Returns a new Time representing the previoius unit specified (defaults to second). + + # Returns a new Time representing the previoius unit specified (defaults + # to second). def prev(unit = :sec) send("prev_#{unit}") end - - # Returns a new Time representing the next unit specified (defaults to second). + + # Returns a new Time representing the next unit specified (defaults to + # second). def next(unit = :sec) send("next_#{unit}") end - + # Short-hand for seconds_ago(1). def prev_second seconds_ago(1) end alias :prev_sec :prev_second - + # Short-hand for seconds_since(1). def next_second seconds_since(1) end alias :next_sec :next_second - + # Short-hand for minutes_ago(1). def prev_minute minutes_ago(1) end alias :prev_min :prev_minute - + # Short-hand for minutes_since(1). def next_minute minutes_since(1) end alias :next_min :next_minute - + # Short-hand for hours_ago(1). def prev_hour hours_ago(1) end - + # Short-hand for hours_since(1). def next_hour hours_since(1) end - + # Short-hand for days_ago(1). def prev_day days_ago(1) end - + # Short-hand for days_since(1). def next_day days_since(1) end - - # Returns a new Time representing the start of the given day in the previous week (default is Monday). + + # Returns a new Time representing the start of the given day in the + # previous week (default is Monday). def prev_week(day = :monday) weeks_ago(1).beginning_of_week.since(days_into_week[day].day).change(:hour => 0) end - + # Short-hand for quarters_since(1).beginning_of_quarter. def next_quarter quarters_since(1).beginning_of_quarter end - + # Short-hand for quarters_ago(1).beginning_of_quarter. def prev_quarter quarters_ago(1).beginning_of_quarter end - - # Returns a new Time representing the time a number of specified minutes ago. + + # Returns a new Time representing the time a number of specified minutes + # ago. def minutes_ago(minutes) ago(minutes.minutes) end alias :mins_ago :minutes_ago - - # Returns a new Time representing the time a number of specified minutes in the future. + + # Returns a new Time representing the time a number of specified minutes + # in the future. def minutes_since(minutes) since(minutes.minutes) end alias :mins_since :minutes_since - - # Returns a new Time representing the time a number of specified hours ago. + + # Returns a new Time representing the time a number of specified hours + # ago. def hours_ago(hours) ago(hours.hours) end - - # Returns a new Time representing the time a number of specified hours in the future. + + # Returns a new Time representing the time a number of specified hours in + # the future. def hours_since(hours) since(hours.hours) end - + # Returns a new Time representing the time a number of specified days ago. def days_ago(days) ago(days.days) end - - # Returns a new Time representing the time a number of specified days in the future. + + # Returns a new Time representing the time a number of specified days in + # the future. def days_since(days) since(days.days) end - - # Returns a new Time representing the time a number of specified weeks ago. + + # Returns a new Time representing the time a number of specified weeks + # ago. def weeks_ago(weeks) ago(weeks.weeks) end - - # Returns a new Time representing the time a number of specified weeks in the future. + + # Returns a new Time representing the time a number of specified weeks in + # the future. def weeks_since(weeks) since(weeks.weeks) end - - # Returns a new Time representing the time a number of specified quarters (3 months) ago. + + # Returns a new Time representing the time a number of specified quarters + # (3 months) ago. def quarters_ago(quarters) ago((quarters * 3).months) end - - # Returns a new Time representing the time a number of specified quarters (3 months) in the future. + + # Returns a new Time representing the time a number of specified quarters + # (3 months) in the future. def quarters_since(quarters) since((quarters * 3).months) end - - # Returns a new Time representing the end of the unit specified (defaults to second). + + # Returns a new Time representing the end of the unit specified (defaults + # to second). def end_of(unit = :sec) send("end_of_#{unit}") end - - # Returns a new Time representing the start of the second, XX:XX:XX.000000 (.000000000 in ruby1.9). + + # Returns a new Time representing the start of the second, XX:XX:XX.000000 + # (.000000000 in ruby1.9). def beginning_of_second change(:usec => 0) end alias :beginning_of_sec :beginning_of_second alias :at_beginning_of_sec :beginning_of_second alias :at_beginning_of_second :beginning_of_second - - # Returns a new Time representing the end of the hour, XX:XX:XX.999999 (.999999999 in ruby1.9). + + # Returns a new Time representing the end of the second, XX:XX:XX.999999 + # (.999999999 in ruby1.9). def end_of_second change(:usec => 999999.999) end alias :end_of_sec :end_of_second - + # Returns a new Time representing the start of the minute (XX:XX:00). def beginning_of_minute change(:sec => 0, :usec => 0) @@ -171,23 +193,38 @@ module TimeExt alias :beginning_of_min :beginning_of_minute alias :at_beginning_of_min :beginning_of_minute alias :at_beginning_of_minute :beginning_of_minute - + # Returns a new Time representing the end of the hour, XX:XX:59.999999 (.999999999 in ruby1.9). def end_of_minute change(:sec => 59, :usec => 999999.999) end alias :end_of_min :end_of_minute - + # Returns a new Time representing the start of the hour (XX:00:00). def beginning_of_hour change(:min => 0, :sec => 0, :usec => 0) end alias :at_beginning_of_hour :beginning_of_hour - - # Returns a new Time representing the end of the hour, XX:59:59.999999 (.999999999 in ruby1.9). + + # Returns a new Time representing the end of the hour, XX:59:59.999999 + # (.999999999 in ruby1.9). def end_of_hour change(:min => 59, :sec => 59, :usec => 999999.999) end + + private + + #### Private Helper Methods + + # Definitive list of days in the week. + def days_into_week + defined?(DAYS_INTO_WEEK) ? DAYS_INTO_WEEK : { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } + end + + # Definitive list of days for each month of the year. + def common_year_days_in_month + defined?(COMMON_YEAR_DAYS_IN_MONTH) ? COMMON_YEAR_DAYS_IN_MONTH : [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + end end end \ No newline at end of file diff --git a/lib/time_ext/core_ext/numeric.rb b/lib/time_ext/core_ext/numeric.rb index 5f31973..f30b218 100644 --- a/lib/time_ext/core_ext/numeric.rb +++ b/lib/time_ext/core_ext/numeric.rb @@ -1,4 +1,8 @@ -require 'active_support/core_ext/numeric/time' unless Numeric.new.respond_to?(:seconds) # fixes rare loading issue +# encoding: utf-8 + +# Support both Active Support 2.x and 3.x, and also addresses rare loading +# issue. +require 'active_support/core_ext/numeric/time' unless Numeric.new.respond_to?(:seconds) class Numeric alias :sec :seconds diff --git a/lib/time_ext/core_ext/time.rb b/lib/time_ext/core_ext/time.rb index c3ca9ed..77b3303 100644 --- a/lib/time_ext/core_ext/time.rb +++ b/lib/time_ext/core_ext/time.rb @@ -1,9 +1,12 @@ -require 'active_support' -require 'active_support/time' unless Time.respond_to?(:days_in_month) # support both Active Support 2.x and 3.x -require 'active_support/core_ext/time/calculations' unless Time.new.respond_to?(:ago) # fixes rare loading issue +# encoding: utf-8 + +# Support both Active Support 2.x and 3.x, and also address rare loading +# issue. +require 'active_support/time' unless Time.respond_to?(:days_in_month) +require 'active_support/core_ext/time/calculations' unless Time.new.respond_to?(:ago) class Time - include TimeExt::Support + # Include TimeExt modules into Time object. include TimeExt::Calculations include TimeExt::Iterations diff --git a/lib/time_ext/iterations.rb b/lib/time_ext/iterations.rb index 01e13e7..4ca6a07 100644 --- a/lib/time_ext/iterations.rb +++ b/lib/time_ext/iterations.rb @@ -1,10 +1,114 @@ -module TimeExt - # Allows you to iterate over Time objects with #each and other methods almost as if it was an Array or Hash. - module Iterations +# encoding: utf-8 - # Used by #each, #map_each and similar methods to iterate over ranges of time. - def iterate(unit, options = {}, &block) - options.reverse_merge!(:map_result => false, :beginning_of => false, :include_start => false, :include_end => true) +module TimeExt + # Allows you to iterate over Time objects with `#each` and other + # methods almost as if it was an Array or Hash. + module Iterations + #### Iterator Methods + + # Executes passed block for each `unit` of time specified, with + # a new time object for each interval passed to the block. + def each(unit, options = {}, &block) + iterate(unit, options.merge(:map_result => false), &block) + end + + # Executes passed block for each `unit` of time specified, with + # a new time object set to the beginning of `unit` for each + # interval passed to the block. + def beginning_of_each(unit, options = {}, &block) + iterate(unit, options.merge(:map_result => false, :beginning_of => true), &block) + end + + # Executes passed block for each `unit` of time specified, + # returning an array with the return values from the passed block. + def map_each(unit, options = {}, &block) + iterate(unit, options.merge(:map_result => true), &block) + end + + # Executes passed block for each `unit` of time specified, + # returning an array with the return values from passed block. + # Additionally the time object passed into the block is set to + # the beginning of specified `unit`. + def map_beginning_of_each(unit, options = {}, &block) + iterate(unit, options.merge(:map_result => true, :beginning_of => true), &block) + end + + #### Limiter Methods + # + # Usually chained with an iterator method to specify at which point + # in time the iterator should stop. + + # Used togeter with `#each` and other iteration methods to specify + # end of interation. + def until(time, &block) + time = time.to_time if time.is_a?(::Date) + @until = time + return call_chain(block) if block_given? + self + end + alias :till :until + + # Used together with `#each` and other interation methods to specify + # start of iteration, and end will be current object. + def from(time, &block) + time = time.to_time if time.is_a?(::Date) + method, args = @method_chain.pop if block_given? + if !method.nil? + time.until(self).send(method, *args, &block) + else + time.until(self) + end + end + + # Let's you iterate over every unit specified in the `#each` or + # `#map_each` call for the specified unit. + def of_the(unit, &block) + @of_the = unit + return call_chain(block) if block_given? + self + end + alias :of :of_the + + #### Shorthand Methods + + # Dynamically define convenience methods, like `#each_hour` as a + # shorthand for `#each(:hour)`. + [:year, :month, :day, :hour, :min, :sec].each do |unit| + [:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method| + define_method "#{method}_#{unit}" do |*args, &block| + send(method, unit, *args, &block) + end + class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min + class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec + end + [:of_the, :of].each do |method| + define_method "#{method}_#{unit}" do |*args, &block| + send(method, unit, *args, &block) + end + class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min + class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec + end + end + + private + + #### Private Helper Methods + + # Default options for `#iterate`. + def default_options + { :map_result => false, + :beginning_of => false, + :end_of => false, + :include_start => false, + :include_end => true } + end + + # Used by `#each`, `#map_each` and similar methods to iterate over + # ranges of time. + def iterate(unit, opts = {}, &block) + options = default_options.merge(opts) + + # Perform the grunt work of iteration. if block_given? units = [:year, :month, :day, :hour, :min, :sec, :usec] parent_unit = units[units.index(unit)-1] @@ -27,77 +131,28 @@ module TimeExt time = time.send(succ_method) end options[:map_result] ? results : self + + # If a block is not given, add itself to the chain list to be + # executed when the chain ends. else add_to_chain(:iterate, unit, options) self end end - - # Used togeter with #each and other iteration methods to specify end of interation. - def until(time, &block) - time = time.to_time if time.is_a?(::Date) - @until = time - return call_chain(block) if block_given? - self + + # Enables chaining of iterator methods with + def add_to_chain(method, *args, &block) + @method_chain ||= [] + @method_chain << [method.to_sym, args, block] end - alias :till :until - - # Used together with #each and other interation methods to specify start of iteration, and end will be current object. - def from(time, &block) - time = time.to_time if time.is_a?(::Date) - method, args = @method_chain.pop if block_given? - if !method.nil? - time.until(self).send(method, *args, &block) - else - time.until(self) - end + + def call_chain(custom_block = nil, &block) + method, args, iblock = @method_chain.pop + return nil if method.nil? + iblock = custom_block if !custom_block.nil? + method, args, iblock = yield(method, args, iblock) if block_given? + self.send(method, *args, &iblock) end - - # Let's you iterate over every unit specified in the #each or #map call for the specified unit. - def of_the(unit, &block) - @of_the = unit - return call_chain(block) if block_given? - self - end - alias :of :of_the - - # Executes passed block for each "unit" of time specified, with a new time object for each interval passed to the block. - def each(unit, options = {}, &block) - iterate(unit, options.merge(:map_result => false), &block) - end - - # Executes passed block for each "unit" of time specified, with a new time object set to the beginning of "unit" for each interval passed to the block. - def beginning_of_each(unit, options = {}, &block) - iterate(unit, options.merge(:map_result => false, :beginning_of => true), &block) - end - - # Executes passed block for each "unit" of time specified, returning an array with the return values from the passed block. - def map_each(unit, options = {}, &block) - iterate(unit, options.merge(:map_result => true), &block) - end - - # Executes passed block for each "unit" of time specified, returning an array with the return values from passed block. Additionally the time object passed into the block is set to the beginning of specified "unit". - def map_beginning_of_each(unit, options = {}, &block) - iterate(unit, options.merge(:map_result => true, :beginning_of => true), &block) - end - - # Dynamically define convenience methods, like #each_hour instead of #each(:hour). - [:year, :month, :day, :hour, :min, :sec].each do |unit| - [:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method| - define_method "#{method}_#{unit}" do |*args, &block| - send(method, unit, *args, &block) - end - class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min - class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec - end - [:of_the, :of].each do |method| - define_method "#{method}_#{unit}" do |*args, &block| - send(method, unit, *args, &block) - end - class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min - class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec - end - end - + end -end \ No newline at end of file +end diff --git a/lib/time_ext/support.rb b/lib/time_ext/support.rb deleted file mode 100644 index ff3e0e2..0000000 --- a/lib/time_ext/support.rb +++ /dev/null @@ -1,27 +0,0 @@ -module TimeExt - # Provides helper methods used by TimeExt::Calculations for backwards compatibility with ActiveSupport, and method chaining helpers for TimeExt::Iterations. - module Support - - def days_into_week - defined?(DAYS_INTO_WEEK) ? DAYS_INTO_WEEK : { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 } - end - - def common_year_days_in_month - defined?(COMMON_YEAR_DAYS_IN_MONTH) ? COMMON_YEAR_DAYS_IN_MONTH : [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - end - - def add_to_chain(method, *args, &block) - @method_chain ||= [] - @method_chain << [method.to_sym, args, block] - end - - def call_chain(custom_block = nil, &block) - method, args, iblock = @method_chain.pop - return nil if method.nil? - iblock = custom_block if !custom_block.nil? - method, args, iblock = yield(method, args, iblock) if block_given? - self.send(method, *args, &iblock) - end - - end -end \ No newline at end of file diff --git a/lib/time_ext/version.rb b/lib/time_ext/version.rb index b9f91fd..ad13cd7 100644 --- a/lib/time_ext/version.rb +++ b/lib/time_ext/version.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + module TimeExt VERSION = "0.2.9" end diff --git a/time_ext.gemspec b/time_ext.gemspec index fc32bd8..e1bc3d3 100644 --- a/time_ext.gemspec +++ b/time_ext.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake', '>= 0.8.7' s.add_development_dependency 'rspec', '>= 2.1.0' - s.add_development_dependency 'yard', '>= 0.6.3' + s.add_development_dependency 'rocco', '>= 0.6.0' s.add_development_dependency 'ruby-debug' end