Георги обнови решението на 21.12.2015 04:09 (преди около 9 години)
+module LazyMode
+
+ def self.create_file(file_name, &block)
+ file = File.new(file_name)
+ file.instance_eval(&block)
+ file
+ end
+
+ class Date
+ include Comparable
+ attr_reader :year, :month, :day
+ DATE_FORMAT = "%04d-%02d-%02d"
+ PERIODS_HASH = {"d" => 1, "w" => 7, "m" => 30}
+ def initialize(date_string)
+ trimmed_string = extract_repeater(date_string).first
+ @repeater = extract_repeater(date_string).last
+
+ date_array = trimmed_string.split("-")
+ @year = date_array.first.to_i
+ @month = date_array[1].to_i
+ @day = date_array.last.to_i
+ end
+
+ def advance(days)
+ day = (@day + days) % 30
+ month = day < @day ? (@month + 1) % 12 : @month
+ year = month < @month ? (@year + 1) : @year
+
+ date_string = DATE_FORMAT % [year, month, day]
+ Date.new(date_string)
+ end
+
+ def advance!(days)
+ @day = (@day + days) % 30
+ @month = day < @day ? (@month + 1) % 12 : @month
+ @year = month < @month ? (@year + 1) : @year
+ end
+
+ def week_later
+ advance(7)
+ end
+
+ def to_s
+ date_string = DATE_FORMAT % [@year, @month, @day]
+ date_string
+ end
+
+ def <=>(other)
+ year_comparison = @year <=> other.year
+ return year_comparison unless year_comparison == 0
+ month_comparison = @month <=> other.month
+ return month_comparison unless month_comparison == 0
+ return @day <=> other.day
+ end
+
+ def apply_repeater!
+ return self unless @repeater
+ repeat_period = @repeater[-1]
+ quantifier = @repeater.sub("+", "").sub(repeat_period, "").to_i
+ step_forward = PERIODS_HASH[repeat_period] * quantifier
+ @repeater = nil
+ advance!(step_forward)
+ end
+
+ private
+ def extract_repeater(date_string)
+ /(\+\d+(d|w|y))/.match date_string
+ repeater = $1
+ trimmed_string = date_string.dup
+ trimmed_string.sub!(repeater, "") if repeater
+ [trimmed_string, repeater]
+ end
+ end #of class
+
+ class NoteParent
+ attr_accessor :file_name
+ attr_reader :notes
+
+ def initialize(name)
+ @file_name = name
+ @notes = []
+ end
+
+ def note(header, *tags, &block)
+ note = Note.new(header, tags)
+ note.file_name = file_name
+ note.instance_eval(&block)
+ @notes.push(note)
+ end
+
+ end #of class
+
+ class Note < NoteParent
+ attr_reader :header, :tags
+
+ def initialize(header, *tags)
+ super("")
+ @header = header
+ @tags = tags.flatten
+ @status = :topostpone
+ @body = ""
+ end
+
+ def status(*args)
+ return @status if args.count == 0
+ note_status = args.first
+ @status = note_status
+ end
+
+ def body(*args)
+ return @body if args.count == 0
+ note_body = args.first
+ @body = note_body
+ end
+
+ def scheduled(date_string)
+ note_scheduled = Date.new(date_string)
+ @date = note_scheduled
+ end
+
+ end #of class
+
+ class File < NoteParent
+ def initialize(name)
+ super
+ end
+
+ def daily_agenda(date)
+ notes = all_notes
+ notes_for_date = notes.select { |note| note.date == date }
+ repeated = notes.each { |note| note.date.apply_repeater!}
+ repeated.select! { |note| note.date == date }
+ Agenda.new(notes_for_date | repeated)
+ end
+
+ def weekly_agenda(date)
+ week_notes = all_notes.select { |note| within_a_week?(note, date) }
+ Agenda.new(week_notes)
+ end
+
+ private
+ def all_notes
+ all_notes_array = @notes.map { |note| [note] + all_notes_for_note(note) }
+ all_notes_array.flatten!
+ all_notes_array.each { |note| conform_to_date(note) }
+ all_notes_array
+ end
+
+ def all_notes_for_note(parent_note)
+ return [] if parent_note == nil
+ parent_note.notes.each { |note| [note] + all_notes_for_note(note) }
+ end
+
+ def within_a_week?(note, starting_date)
+ date = note.date.dup
+ within = date >= starting_date and date <= starting_date.week_later
+ date.apply_repeater!
+ within_repeat = date >= starting_date and date <= starting_date.week_later
+
+ within or within_repeat
+
+ end
+
+ def conform_to_date(note)
+ note.instance_eval do
+ def date
+ @date
+ end
+ end
+ end
+
+ end #of class
+
+ class Agenda
+ attr_reader :notes
+
+ def initialize(notes = [])
+ @notes = notes
+ end
+
+ def where(tag: nil, text: nil, status: nil)
+
+ notes_by_tag = by_tag(tag)
+ notes_by_text = by_text(text)
+ notes_by_status = by_status(status)
+
+ filtered_notes = notes_by_tag & notes_by_text & notes_by_status
+
+ Agenda.new(filtered_notes)
+ end
+
+ def by_tag(tag)
+ return @notes if tag == nil
+ @notes.select { |note| note.tags.include? tag }
+ end
+
+ def by_text(text)
+ return @notes if text == nil
+ @notes.select { |note| text.match "#{note.header}\n#{note.body}" }
+ end
+
+ def by_status(status)
+ return @notes if status == nil
+ @notes.select { |note| note.status == status }
+ end
+
+ end #of class
+end #of module