Димитър обнови решението на 20.12.2015 22:48 (преди около 9 години)
+module LazyMode
+ extend self
+
+ DATE_PARSER = /(\d{4})-(\d\d)-(\d\d)( \+(\d+)([mwd]))?/
+
+ def create_file(file_name)
+ File.new(file_name).instance_eval &Proc.new
+ end
+end
+
+class LazyMode::Note
+ attr_reader :header, :tags, :file_name
+ attr_accessor :date
+
+ def initialize(header, tags, file_name, file)
+ @header = header
+ @tags = tags
+ @file_name = file_name
+ @file = file
+ @body = ''
+ @status = :topostpone
+ end
+
+ def body(content = nil)
+ content ? @body = content : @body
+ end
+
+ def status(type = nil)
+ type ? @status = type : @status
+ end
+
+ def scheduled_on_date?(date)
+ @dates.take_while { |current_date| current_date <= date }.last == date
+ end
+
+ private
+
+ def scheduled(date_string)
+ date = LazyMode::Date.new(date_string)
+ date_parts = LazyMode::DATE_PARSER.match date_string
+ count = date_parts[5].to_i
+ type = date_parts[6]
+ @dates = LazyMode::DateEnumerator.new(date, count, type)
+ end
+
+ def note(header, *tags)
+ new_note = LazyMode::Note.new(header, tags, @file_name, @file)
+ new_note.instance_eval &Proc.new
+ @file.notes << new_note
+
+ @file
+ end
+end
+
+class LazyMode::File
+ attr_reader :name, :notes
+
+ def initialize(name, notes = [])
+ @name = name
+ @notes = notes
+ end
+
+ def daily_agenda(date)
+ notes = @notes.select { |note| note.scheduled_on_date?(date) }
+ notes = notes.each { |note| note.date = date }
+
+ LazyMode::File.new("#{@name}-#{date.to_s}", notes)
+ end
+
+ def weekly_agenda(date)
+ agenda = LazyMode::File.new("#{@name}-#{date.to_s}-weekly")
+
+ dates = LazyMode::DateEnumerator.new(date, 1, 'd')
+ dates.take(7).each { |date| agenda.notes.concat(daily_agenda(date).notes) }
+
+ agenda
+ end
+
+ def where(tag: nil, text: //, status: nil)
+ notes = @notes.dup
+ notes = filter_notes_by_tag(notes, tag) if tag
+ notes = filter_notes_by_status(notes, status) if status
+ notes = filter_notes_by_text(notes, text)
+
+ LazyMode::File.new("#{@name}-where", notes)
+ end
+
+ private
+
+ def note(header, *tags)
+ new_note = LazyMode::Note.new(header, tags, @name, self)
+ new_note.instance_eval &Proc.new
+ @notes << new_note
+
+ self
+ end
+
+ def filter_notes_by_tag(notes, tag)
+ notes.select { |note| note.tags.include? tag }
+ end
+
+ def filter_notes_by_status(notes, status)
+ notes.select { |note| note.status == status }
+ end
+
+ def filter_notes_by_text(notes, text)
+ notes.select { |note| text.match note.header or text.match note.body }
+ end
+end
+
+class LazyMode::Date
+ include Comparable
+
+ attr_reader :year, :month, :day
+
+ def initialize(date_string)
+ date_parts = LazyMode::DATE_PARSER.match date_string
+ @year = date_parts[1].to_i
+ @month = date_parts[2].to_i
+ @day = date_parts[3].to_i
+ @date_string = date_string
+ end
+
+ def self.construct(year, month, day)
+ year = year.to_s.rjust(4, '0')
+ month = month.to_s.rjust(2, '0')
+ day = day.to_s.rjust(2, '0')
+
+ new("#{year}-#{month}-#{day}")
+ end
+
+ def to_s
+ @date_string
+ end
+
+ def <=>(other)
+ if @year == other.year
+ compare_months(other)
+ else
+ @year <=> other.year
+ end
+ end
+
+ private
+
+ def compare_months(other)
+ @month == other.month ? @day <=> other.day : @month <=> other.month
+ end
+end
+
+class LazyMode::DateEnumerator < Enumerator
+ def initialize(date, count, type)
+ @date = date
+ @count = count
+ @type = type
+ end
+
+ def each
+ case @type
+ when 'm' then each_months &Proc.new
+ when 'w' then each_weeks &Proc.new
+ when 'd' then each_days &Proc.new
+ else yield @date
+ end
+ end
+
+ private
+
+ def add_months_to_date(count, date)
+ year = date.year + ((date.month - 1) + count) / 12
+ month = ((date.month - 1) + count) % 12 + 1
+
+ LazyMode::Date.construct(year, month, date.day)
+ end
+
+ def add_days_to_date(count, date)
+ date = add_months_to_date(((date.day - 1) + count) / 30, date)
+ day = ((date.day - 1) + count) % 30 + 1
+
+ LazyMode::Date.construct(date.year, date.month, day)
+ end
+
+ def each_months
+ current_date = @date
+ loop do
+ yield current_date
+ current_date = add_months_to_date(@count, current_date)
+ end
+ end
+
+ def each_weeks
+ days_in_weeks = @count * 7
+ current_date = @date
+ loop do
+ yield current_date
+ current_date = add_days_to_date(days_in_weeks, current_date)
+ end
+ end
+
+ def each_days
+ current_date = @date
+ loop do
+ yield current_date
+ current_date = add_days_to_date(@count, current_date)
+ end
+ end
+end