Мария обнови решението на 02.11.2013 15:42 (преди около 11 години)
+class Todo
+ attr_reader :status, :description, :priority, :tags
+
+ def initialize(status, description, priority, tags)
+ @status, @priority = [status, priority].map(&:downcase).map(&:to_sym)
+ @description, @tags = description, tags
+ end
+end
+
+class Criteria
+ attr_reader :block
+
+ def self.status(status)
+ Criteria.new { |task| task.status == status }
+ end
+
+ def self.priority(priority)
+ Criteria.new { |task| task.priority == priority }
+ end
+
+ def self.tags(tags)
+ Criteria.new { |task| tags & task.tags == tags }
+ end
+
+ def initialize(&block)
+ @block = block
+ end
+
+ def &(other)
+ Criteria.new { |task| @block.call task and other.block.call task }
+ end
+
+ def |(other)
+ Criteria.new { |task| @block.call task or other.block.call task }
+ end
+
+ def !
+ Criteria.new { |task| not @block.call task }
+ end
+end
+
+module TasksInformation
+ def tasks_todo
+ tasks_information :todo
+ end
+
+ def tasks_in_progress
+ tasks_information :current
+ end
+
+ def tasks_completed
+ tasks_information :done
+ end
+
+ def completed?
+ tasks_completed == count
+ end
+
+ private
+ def tasks_information(status)
+ filter(Criteria.status status).count
+ end
+end
+
+class TodoList
+ include Enumerable
+ include TasksInformation
+
+ attr_reader :tasks
+
+ def self.parse(text)
+ text_formatted = text.lines.map { |line| line.split('|').map(&:strip) }
+ tasks = text_formatted.map do |status, description, priority, tags|
+ Todo.new status, description, priority, tags.split(',').map(&:strip)
+ end
+ TodoList.new tasks
+ end
+
+ def initialize(tasks)
+ @tasks = tasks
+ end
+
+ def each
+ @tasks.each { |task| yield task }
+ end
+
+ def count
+ @tasks.count
+ end
+
+ def filter(criteria)
+ TodoList.new select(&criteria.block)
+ end
+
+ def adjoin(other)
+ TodoList.new @tasks.concat(other.tasks).uniq
+ end
+end