require File.dirname(__FILE__) + '/date_extensions' require File.dirname(__FILE__) + '/date_finder_conditions' class DateFinderBase include DateFinderConditions MAX_DATES, MAX_INTERVALS = 200, 500 @@patterns = {} attr_reader :options def initialize(options = {}) @condition_blocks = {} @options = options.reverse_merge :interval => 1, :start_date => Date.today valid_options! end def valid_options!(options = @options) options.assert_valid_keys :interval, :start_date, :end_date, :max, :max_intervals end class << self def method_missing(method_id, *args) subclass_name = "#{name}::" + method_id.to_s.camelize if subclasses.include?(subclass_name) options = args.last.is_a?(Hash) ? args.last : {} options[:interval] ||= 1 options[:interval] = options[:interval].to_s if options[:interval].is_a?(Symbol) case options[:interval] when 'every_other': options[:interval] = 2 when /^every_(\d)+/: options[:interval] = $1.to_i end subclass_name.constantize.new(options) else super end end end def interval(interval) @options[:interval] = interval end def find(options = {}) options = @options.merge(options) valid_options! options start, intervals = options[:start_date], 0 returning [] do |dates| loop do break if intervals == options[:max_intervals] or intervals == MAX_INTERVALS break if dates.size >= MAX_DATES break if options[:max] and dates.size >= options[:max] if options[:end_date] and options[:end_date] < start dates.reject! { |d| d > options[:end_date] } break end dates.concat dates_matching_conditions(dates_to_test(start)) start = next_start_date(start, options[:interval]) intervals += 1 end if options[:max] dates.pop until dates.size <= options[:max] end end end def dates_matching_conditions(dates) dates.select do |date| @condition_blocks.values.all? do |blocks| blocks.any? { |c| c.call(date) } end end end def condition(name, &block) (@condition_blocks[name] ||= []) << block self end end require File.dirname(__FILE__) + '/patterns'