Слав обнови решението на 05.11.2013 14:56 (преди около 11 години)
+class Task
+ attr_accessor :status, :description, :priority, :tags
+
+ def initialize(status, description, priority, *tags)
+ @status = status.strip.downcase.to_sym
+ @description = description.strip
+ @priority = priority.strip.downcase.to_sym
+ @tags = tags.first.nil? ? [] : tags.first.split(',').map(&:strip)
+ end
+
+ def status
+ @status
+ end
+
+ def description
+ @description
+ end
+
+ def priority
+ @priority
+ end
+
+ def tags
+ @tags
+ end
+
+ def ==(other)
+ status == other.status and
+ description == other.description and
+ priority == other.priority and
+ tags == other.tags
+ end
+ alias eql? ==
+end
+
+class Criterion
+ def &(other)
+ Conjunction.new self, other
+ end
+
+ def |(other)
+ Disjunction.new self, other
+ end
+
+ def !
+ Negation.new self
+ end
+end
+
+class Conjunction < Criterion
+ def initialize(left, right)
+ @left = left
+ @right = right
+ end
+
+ def met_by?(task)
+ @left.met_by?(task) and @right.met_by?(task)
+ end
+end
+
+class Disjunction < Criterion
+ def initialize(left, right)
+ @left = left
+ @right = right
+ end
+
+ def met_by?(task)
+ @left.met_by?(task) or @right.met_by?(task)
+ end
+end
+
+class Negation < Criterion
+ def initialize(criterion)
+ @criterion = criterion
+ end
+
+ def met_by?(task)
+ not @criterion.met_by?(task)
+ end
+end
+
+module Criteria
+ def self.status(description)
+ StatusMatches.new description
+ end
+
+ class StatusMatches < Criterion
+ def initialize(status)
+ @status = status
+ end
+
+ def met_by?(task)
+ task.status == @status
+ end
+ end
+
+ def self.priority(description)
+ PriorityMatches.new description
+ end
+
+ class PriorityMatches < Criterion
+ def initialize(priority)
+ @priority = priority
+ end
+
+ def met_by?(task)
+ task.priority == @priority
+ end
+ end
+
+ def self.tags(description)
+ TagsMatches.new description
+ end
+
+ class TagsMatches < Criterion
+ def initialize(tags)
+ @tags = tags
+ end
+
+ def met_by?(task)
+ @tags.all? { |tag| task.tags.include?(tag) }
+ end
+ end
+end
+
+class TodoList
+ include Enumerable
+ include Criteria
+
+ attr_accessor :tasks
+ def initialize(tasks)
+ @tasks = tasks
+ end
+
+ def each(&block)
+ @tasks.each(&block)
+ end
+
+ def self.parse(text)
+ text_modified = text.lines.map { |line| line.split('|') }
+ tasks = text_modified.map do |status, description, priority, tags|
+ Task.new status, description, priority, tags
+ end
+ new tasks
+ end
+
+ def adjoin(other)
+ adjoined_tasks = (tasks + other.tasks).uniq
+ TodoList.new adjoined_tasks
+ end
+
+ def filter(criteria)
+ TodoList.new @tasks.select { |task| criteria.met_by? task }
+ end
+
+ def tasks_todo
+ (filter Criteria.status(:todo)).to_a.size
+ end
+
+ def tasks_in_progress
+ (filter Criteria.status(:current)).to_a.size
+ end
+
+ def tasks_completed
+ (filter Criteria.status(:done)).to_a.size
+ end
+
+ def completed?
+ all? { |element| element.status == :done }
+ end
+end