Иван обнови решението на 04.11.2013 10:02 (преди около 11 години)
+class TodoList
+ attr_reader :task
+
+ def initialize(task)
+ @task = task
+ end
+
+ def self.parse(text)
+ list = []
+
+ text.lines do |line|
+ list << (Task.new *(allocate line.strip.gsub(/\s+/, " ").split(" | ")))
+ end
+
+ new list
+ end
+
+ def self.allocate(task)
+ tags = task[3] ? task[3].split(", ") : []
+
+ return task[0].downcase.to_sym, task[1],
+ task[2].downcase.chomp(" |").to_sym, tags
+ end
+
+ def filter(criteria)
+ TodoList.new @task.select { |task| criteria.compare? task }
+ end
+
+ def adjoin(todo_list)
+ adjoined = (@task + todo_list.task)
+ TodoList.new adjoined
+ end
+
+ def tasks_todo
+ filter(Criteria.status :todo ).task.length
+ end
+
+ def tasks_in_progress
+ filter(Criteria.status :current ).task.length
+ end
+
+ def tasks_completed
+ filter(Criteria.status :done ).task.length
+ end
+
+ def completed?
+ filter(Criteria.status :done ).task.length == task.length
+ end
+
+ include Enumerable
+
+ def each(&block)
+ @task.each(&block)
+ end
+
+end
+
+class Task
+ attr_reader :status, :description, :priority, :tags
+
+ def initialize(status, description, priority, tags)
+ @tags = tags
+ @status = status.to_sym
+ @priority = priority.to_sym
+ @description = description
+ end
+end
+
+module Criteria
+ def self.status(status)
+ Pattern.new { |task| task.status == status }
+ end
+
+ def self.description(description)
+ Pattern.new { |task| task.description == description }
+ end
+
+ def self.priority(priority)
+ Pattern.new { |task| task.priority == priority }
+ end
+
+ def self.tags(tags)
+ Pattern.new { |task| Set[*tags].subset? Set[*task.tags] }
+ end
+
+ class Pattern
+ def initialize(&block)
+ @filter = block
+ end
+
+ def compare?(task)
+ @filter.call task
+ end
+
+ def &(other)
+ Pattern.new { |task| self.compare?(task) and other.compare?(task) }
+ end
+
+ def |(other)
+ Pattern.new { |task| self.compare?(task) or other.compare?(task) }
+ end
+
+ def !
+ Pattern.new { |task| not compare?(task) }
+ end
+ end
+end