Калоян обнови решението на 03.11.2013 12:33 (преди около 11 години)
+class TodoEntry
+ attr_accessor :status
+ attr_accessor :description
+ attr_accessor :priority
+ attr_accessor :tags
+
+ def self.parse(line)
+ parts = line.chomp.split('|').map { |substring| substring.strip }
+ new parts[0], parts[1], parts[2], (parts[3] ? parts[3] : "")
+ end
+
+ def initialize(status, description, priority, tags)
+ @status = status.downcase.to_sym
+ @description = description
+ @priority = priority.downcase.to_sym
+ @tags = tags.split(',').map { |tag| tag.strip }
+ end
+end
+
+module CriteriaOperations
+ def &(other)
+ ComplexCriteria.new self, other, :and_operation
+ end
+
+ def |(other)
+ ComplexCriteria.new self, other, :or_operation
+ end
+
+ def !
+ ComplexCriteria.new self, nil, :not_operation
+ end
+end
+
+class Criteria
+ include CriteriaOperations
+
+ def self.status(status_type)
+ new status_type, :status
+ end
+
+ def self.priority(priority_type)
+ new priority_type, :priority
+ end
+
+ def self.tags(tags_array)
+ new tags_array, :tags
+ end
+
+ def initialize(filter_criteria, type)
+ @filter_criteria = filter_criteria
+ @type = type
+ end
+
+ def check(entry)
+ value_of_entry = entry.send(@type)
+ return (@filter_criteria - value_of_entry).empty? if @type == :tags
+ value_of_entry == @filter_criteria
+ end
+end
+
+class ComplexCriteria
+ include CriteriaOperations
+
+ def initialize(criteria_A, criteria_B, operation)
+ @criteria_A = criteria_A
+ @criteria_B = criteria_B
+ @operation = operation
+ end
+
+ def check(entry)
+ send @operation, *entry
+ end
+
+ def not_operation(entry)
+ not @criteria_A.check(entry)
+ end
+
+ def and_operation(entry)
+ @criteria_A.check(entry) and @criteria_B.check(entry)
+ end
+
+ def or_operation(entry)
+ @criteria_A.check(entry) or @criteria_B.check(entry)
+ end
+end
+
+class TodoList
+ include Enumerable
+
+ attr_accessor :entries
+
+ def self.parse(text)
+ entries = text.lines.map { |line| TodoEntry.parse(line) }
+ new entries
+ end
+
+ def initialize(entries)
+ @entries = entries
+ end
+
+ def tasks_todo
+ @entries.count { |entry| entry.status == :todo }
+ end
+
+ def tasks_in_progress
+ @entries.count { |entry| entry.status == :current }
+ end
+
+ def tasks_completed
+ @entries.count { |entry| entry.status == :done }
+ end
+
+ def completed?
+ tasks_completed == @entries.size
+ end
+
+ def filter(criteria)
+ filtered_entries = @entries.find_all { |entry| criteria.check entry }
+ TodoList.new filtered_entries
+ end
+
+ def adjoin(other)
+ TodoList.new(@entries += other.entries)
+ end
+
+ def each
+ @entries.each { |entry| yield entry }
+ end
+end