Георги обнови решението на 05.11.2013 19:30 (преди около 11 години)
+class TrueClass
+ def !
+ false
+ end
+end
+
+class FalseClass
+ def !
+ true
+ end
+end
+
+class Task
+ attr_accessor :status, :description, :priority, :tags
+
+ def initialize(status, description, priority, tags = [])
+ @status = status.downcase.to_sym
+ @description = description
+ @priority = priority.downcase.to_sym
+ @tags = tags != [] ? tags.split(', ') : tags
+ end
+end
+
+class Criteria
+ class << self
+ def status(st)
+ CriteriaExpression.new nil, [[:status, st]]
+ end
+
+ def priority(pr)
+ CriteriaExpression.new nil, [[:priority, pr]]
+ end
+
+ def tags(tgs)
+ CriteriaExpression.new nil, [[:tags, tgs]]
+ end
+ end
+end
+
+class CriteriaExpression
+ attr_accessor :operator, :operands
+
+ def initialize(operator, operands)
+ @operator = operator
+ @operands = operands
+ end
+
+ def to_a
+ [operator, operands]
+ end
+
+ def &(other)
+ CriteriaExpression.new :&, [to_a, other.to_a]
+ end
+
+ def |(other)
+ CriteriaExpression.new :|, [to_a, other.to_a]
+ end
+
+ def !
+ CriteriaExpression.new :!, [to_a]
+ end
+
+ def eval(task)
+ operator.nil? ? eval_without_operator(task) : eval_with_operator(task)
+ end
+
+ private
+
+ def eval_with_operator(task)
+ if operands.length == 2
+ expr = CriteriaExpression.new(*operands[0]).eval(task)
+ expr.send(operator, CriteriaExpression.new(*operands[1]).eval(task))
+ else
+ CriteriaExpression.new(*operands[0]).eval(task).send(operator)
+ end
+ end
+
+ def eval_without_operator(task)
+ if operands[0][0] != :tags
+ return task.send(operands[0][0]) == operands[0][1]
+ end
+ operands[0][1].all? { |item| task.send(operands[0][0]).include? item }
+
+ end
+end
+
+class TodoList
+ class << self
+ def parse(text)
+ tasks = text.split("\n").each { |task| task.strip! }
+ TaskList.new(tasks.map { |item| Task.new(*(item.split(/\s*\|\s*/))) })
+ end
+ end
+end
+
+class TaskList
+ include Enumerable
+
+ def each
+ data.each { |item| yield item }
+ end
+
+ attr_accessor :data
+
+ def initialize(data)
+ @data = data
+ end
+
+ def filter(criteria)
+ TaskList.new(data.select { |task| criteria.eval(task) })
+ end
+
+ def adjoin(other)
+ TaskList.new(data | (other.data))
+ end
+
+ def tasks_todo
+ filter(Criteria.status(:todo)).data.length
+ end
+
+ def tasks_in_progress
+ filter(Criteria.status(:current)).data.length
+ end
+
+ def tasks_completed
+ filter(Criteria.status(:done)).data.length
+ end
+
+ def completed?
+ data.all? { |task| task.status == :done }
+ end
+end