Георги обнови решението на 02.11.2013 01:09 (преди около 11 години)
+class Task < Struct.new :status, :description, :priority, :tags
+ def self.parse(string)
+ status, description, priority, tags = string.split('|').map(&:strip)
+ new status.downcase.to_sym, description, priority.downcase.to_sym,
+ tags ? tags.split(',').map(&:strip) : []
+ end
+end
+
+class Criteria
+ def initialize(&predicate)
+ @predicate = predicate
+ end
+
+ def self.status(acceptable)
+ new { |task| task.status == acceptable }
+ end
+
+ def self.priority(acceptable)
+ new { |task| task.priority == acceptable }
+ end
+
+ def self.tags(required)
+ new { |task| (required - task.tags).empty? }
+ end
+
+ def to_proc
+ @predicate
+ end
+
+ def !
+ Criteria.new { |task| not to_proc.call task }
+ end
+
+ def &(other)
+ Criteria.new { |task| to_proc.call task and other.to_proc.call task }
+ end
+
+ def |(other)
+ Criteria.new { |task| to_proc.call task or other.to_proc.call task }
+ end
+end
+
+class TodoList
+ include Enumerable
+
+ def initialize(tasks)
+ @tasks = tasks
+ end
+
+ def self.parse(string)
+ new string.split("\n").map { |line| Task.parse line }
+ end
+
+ def each(&block)
+ @tasks.each(&block)
+ end
+
+ def filter(criteria)
+ TodoList.new select(&criteria)
+ end
+
+ def adjoin(other)
+ TodoList.new to_a | other.to_a
+ end
+
+ def tasks_todo
+ count(&Criteria.status(:todo))
+ end
+
+ def tasks_in_progress
+ count(&Criteria.status(:current))
+ end
+
+ def tasks_completed
+ count(&Criteria.status(:done))
+ end
+end