Теодор обнови решението на 21.12.2015 16:39 (преди около 9 години)
+class LazyMode
+ class Date
+ DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/
+ MONTHS = 30
+ YEARS = MONTHS * 12
+
+ def initialize(date_string)
+ raise RuntimeError unless date_string =~ DATE_PATTERN
+ year, month, day = date_string.match(DATE_PATTERN).captures
+
+ @total_days = day.to_i - 1 +
+ month.to_i.pred * MONTHS +
+ year.to_i.pred * YEARS
+ end
+
+ def year
+ 1 + @total_days / YEARS
+ end
+
+ def month
+ 1 + @total_days % YEARS / MONTHS
+ end
+
+ def day
+ @total_days % MONTHS % YEARS + 1
+ end
+
+ def +(days)
+ date = Date.new(self.to_s)
+ date.instance_eval { @total_days += days }
+ date
+ end
+
+ def -(other)
+ @total_days - other.total_days
+ end
+
+ def to_s
+ '%04d-%02d-%02d' % [year, month, day]
+ end
+
+ protected
+
+ def total_days
+ @total_days
+ end
+ end
+end
+
+class LazyMode
+ class Occurrence
+ OCCURRENCE_PATTERN = /^(.{10})(?: \+(\d+)([dwm]))?$/
+ PERIODS = {'d' => 1, 'w' => 7, 'm' => 30}
+
+ def self.empty_occurrence
+ Occurrence.new '0000-00-00 +0d' do
+ def match(date)
+ false
+ end
+ end
+ end
+
+ def initialize(string)
+ date, times, period = string.match(OCCURRENCE_PATTERN).captures
+ @date = Date.new(date)
+ @period = times ? times.to_i * PERIODS[period] : 0
+ end
+
+ def match(date)
+ return false if date - @date < 0
+ (@period == 0 && (date - @date) == 0) ||
+ (@period != 0 && (date - @date) % @period == 0)
+ end
+ end
+end
+
+class LazyMode
+ module NotesCollection
+ def note(header, *tags, &block)
+ builder = create_note_builder(header, tags)
+
+ builder.instance_eval(&block) if block_given?
+
+ push_note(builder.get_note)
+ end
+ end
+end
+
+class LazyMode
+ class NoteBuilder
+ include NotesCollection
+
+ def initialize(file, header, tags)
+ @note = Note.new(file, header, tags)
+
+ @note.body = ''
+ @note.status = :topostpone
+ @note.occurrence = Occurrence.empty_occurrence
+ @note.notes = []
+ end
+
+ def scheduled(occurrence)
+ @note.occurrence = Occurrence.new occurrence
+ end
+
+ def status(status)
+ @note.status = status
+ end
+
+ def body(body)
+ @note.body = body
+ end
+
+ def get_note
+ @note
+ end
+
+ def push_note(sub_note)
+ @note.notes.push sub_note
+ end
+
+ def create_note_builder(header, tags)
+ NoteBuilder.new(@note.file_name, header, tags)
+ end
+ end
+end
+
+class LazyMode
+ class Note
+ attr_accessor :file_name, :header, :tags
+ attr_accessor :body, :status, :notes, :occurrence
+
+ def initialize(file, header, tags)
+ @file_name = file
+ @header = header
+ @tags = tags
+ end
+ end
+end
+
+class LazyMode
+ class Agenda
+ attr_reader :notes
+
+ def initialize(date, days, notes)
+ @date = date
+ @days = days
+ @notes = notes
+ end
+
+ def where(status: nil, tag: nil, text: nil)
+ notes = filter_by_status(@notes, status)
+ notes = filter_by_text(notes, text)
+ notes = filter_by_tag(notes, tag)
+ Agenda.new(@date, @days, notes)
+ end
+
+ private
+
+ def filter_by_status(notes, status)
+ return notes unless status
+ notes.select { |note| note.status == status }
+ end
+
+ def filter_by_text(notes, text)
+ return notes unless text
+ notes.select { |note| note.header =~ text || note.body =~ text }
+ end
+
+ def filter_by_tag(notes, tag)
+ return notes unless tag
+ notes.select { |note| note.tags.include? tag }
+ end
+ end
+end
+
+class LazyMode
+ class PeriodAgenda < Agenda
+ def initialize(date, days, notes)
+ super(date, days, select_notes(date, days, notes))
+ end
+
+ private
+
+ def select_notes(date, days, notes)
+ (0...days).reduce [] do |result, day|
+ result.concat(select_notes_for_day(notes, date + day))
+ end
+ end
+
+ def select_notes_for_day(notes, day)
+ notes.reduce [] do |result, note|
+ push_note_for_day(result, note, day)
+ result.concat(select_notes_for_day(note.notes, day))
+ end
+ end
+
+ def push_note_for_day(notes, note, day)
+ if note.occurrence.match day
+ notes.push(note_for_day(note, day))
+ end
+ end
+
+ def note_for_day(note, day)
+ new_note = note.clone
+ new_note.define_singleton_method(:date) do
+ day
+ end
+ new_note
+ end
+ end
+end
+
+class LazyMode
+ class File
+ include NotesCollection
+
+ attr_reader :notes, :name
+
+ def initialize(name, &block)
+ @name = name
+ @notes = []
+ end
+
+ def daily_agenda(date)
+ PeriodAgenda.new(date, 1, @notes)
+ end
+
+ def weekly_agenda(date)
+ PeriodAgenda.new(date, 7, @notes)
+ end
+
+ protected
+
+ def push_note(note)
+ @notes.push note
+ end
+
+ def create_note_builder(header, tags)
+ NoteBuilder.new(@name, header, tags)
+ end
+ end
+end
+
+class LazyMode
+ def self.create_file(name, &block)
+ file = File.new name
+
+ file.instance_eval(&block) if block_given?
+
+ file
+ end
+end