diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb
index 6294a82b..fdf12454 100644
--- a/app/controllers/todos_controller.rb
+++ b/app/controllers/todos_controller.rb
@@ -2,8 +2,8 @@ class TodosController < ApplicationController
helper :todos
- skip_before_filter :login_required, :only => [:index, :calendar]
- prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar]
+ skip_before_filter :login_required, :only => [:index, :calendar, :tag]
+ prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar, :tag]
append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred]
# TODO: replace :except with :only
@@ -584,50 +584,11 @@ class TodosController < ApplicationController
redirect_to project_todos_path(project, :format => 'm')
end
- def get_ids_from_tag_expr(tag_expr)
- ids = []
- tag_expr.each do |tag_list|
- id_list = []
- tag_list.each do |tag|
- tag = Tag.find_by_name(tag)
- id_list << tag.id if tag
- end
- ids << id_list
- end
- return ids
- end
-
- def get_params_for_tag_view
- # use sanitize to prevent XSS attacks
-
- @tag_expr = []
- @tag_expr << sanitize(params[:name]).split(',')
- @tag_expr << sanitize(params[:and]).split(',') if params[:and]
-
- i = 1
- while params['and'+i.to_s]
- @tag_expr << sanitize(params['and'+i.to_s]).split(',')
- i=i+1
- end
-
- @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1
- @tag_name = @tag_expr[0][0] # if @single_tag
- end
-
- def find_todos_with_tag_ids(tag_ids)
- todos = current_user.todos
- tag_ids.each do |ids|
- todos = todos.with_tags(ids) unless ids.nil? || ids.empty?
- end
- return todos
- end
-
# /todos/tag/[tag_name] shows all the actions tagged with tag_name
def tag
- @page_title = t('todos.tagged_page_title', :tag_name => @tag_name)
- @source_view = params['_source_view'] || 'tag'
-
get_params_for_tag_view
+ @page_title = t('todos.tagged_page_title', :tag_name => @tag_title)
+ @source_view = params['_source_view'] || 'tag'
if mobile?
# mobile tags are routed with :name ending on .m. So we need to chomp it
@@ -636,11 +597,7 @@ class TodosController < ApplicationController
init_data_for_sidebar
end
- @tag = Tag.find_by_name(@tag_name)
- @tag = Tag.new(:name => @tag_name) if @tag.nil?
-
- @tag_ids = get_ids_from_tag_expr(@tag_expr)
- todos_with_tag_ids = find_todos_with_tag_ids(@tag_ids)
+ todos_with_tag_ids = find_todos_with_tag_expr(@tag_expr)
@not_done_todos = todos_with_tag_ids.active.not_hidden.find(:all,
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC', :include => Todo::DEFAULT_INCLUDES)
@@ -648,17 +605,15 @@ class TodosController < ApplicationController
:include => Todo::DEFAULT_INCLUDES,
:order => 'todos.completed_at DESC, todos.created_at DESC')
@deferred = todos_with_tag_ids.deferred.find(:all,
- :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES)
+ :order => 'todos.show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES)
@pending = todos_with_tag_ids.blocked.find(:all,
- :order => 'show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES)
+ :order => 'todos.show_from ASC, todos.created_at DESC', :include => Todo::DEFAULT_INCLUDES)
# If you've set no_completed to zero, the completed items box isn't shown on
# the tag page
- max_completed = current_user.prefs.show_number_completed
- @done = current_user.todos.with_tag(@tag).completed.find(:all,
- :limit => max_completed,
- :order => 'todos.completed_at DESC',
- :include => Todo::DEFAULT_INCLUDES)
+ @done = todos_with_tag_ids.completed.find(:all,
+ :limit => current_user.prefs.show_number_completed,
+ :order => 'todos.completed_at DESC', :include => Todo::DEFAULT_INCLUDES)
@projects = current_user.projects
@contexts = current_user.contexts
@@ -680,6 +635,9 @@ class TodosController < ApplicationController
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => "mobile_tag"
}
+ format.text {
+ render :action => 'index', :layout => false, :content_type => Mime::TEXT
+ }
end
end
@@ -1033,6 +991,51 @@ class TodosController < ApplicationController
:include => [ :project, :context, :tags ])
end
+ def tag_title(tag_expr)
+ and_list = tag_expr.inject([]) { |s,tag_list| s << tag_list.join(',') }
+ return and_list.join(' AND ')
+ end
+
+ def get_params_for_tag_view
+ # use sanitize to prevent XSS attacks
+
+ @tag_expr = []
+ @tag_expr << sanitize(params[:name]).split(',')
+ @tag_expr << sanitize(params[:and]).split(',') if params[:and]
+
+ i = 1
+ while params['and'+i.to_s]
+ @tag_expr << sanitize(params['and'+i.to_s]).split(',')
+ i=i+1
+ end
+
+ @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1
+ @tag_name = @tag_expr[0][0]
+ @tag_title = @single_tag ? @tag_name : tag_title(@tag_expr)
+ end
+
+ def get_ids_from_tag_expr(tag_expr)
+ ids = []
+ tag_expr.each do |tag_list|
+ id_list = []
+ tag_list.each do |tag|
+ tag = Tag.find_by_name(tag)
+ id_list << tag.id if tag
+ end
+ ids << id_list
+ end
+ return ids
+ end
+
+ def find_todos_with_tag_expr(tag_expr)
+ tag_ids = get_ids_from_tag_expr(tag_expr)
+ todos = current_user.todos
+ tag_ids.each do |ids|
+ todos = todos.with_tags(ids) unless ids.nil? || ids.empty?
+ end
+ return todos
+ end
+
def determine_down_count
source_view do |from|
from.todo do
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 80481546..45253ab3 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -28,18 +28,18 @@ class Todo < ActiveRecord::Base
named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT(todos.show_from IS NULL)) OR (todos.state = ?)", "pending"]
named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT(todos.state = ?)", "pending"]
named_scope :hidden,
- :joins => :context,
- :conditions => ["todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))",
+ :joins => "INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id",
+ :conditions => ["todos.state = ? OR (c_hidden.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))",
'project_hidden', true, 'active', 'deferred', 'pending']
named_scope :not_hidden,
- :joins => [:context],
- :conditions => ['NOT(todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))',
+ :joins => "INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id",
+ :conditions => ['NOT(todos.state = ? OR (c_hidden.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))',
'project_hidden', true, 'active', 'deferred', 'pending']
# other scopes
named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)']
named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } }
- named_scope :with_tags, lambda { |tag_ids| {:joins => :taggings, :conditions => ["taggings.tag_id IN (?) ", tag_ids] } }
+ named_scope :with_tags, lambda { |tag_ids| {:conditions => ["EXISTS(SELECT * from taggings t WHERE t.tag_id IN (?) AND t.taggable_id=todos.id AND t.taggable_type='Todo')", tag_ids] } }
named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } }
named_scope :completed_after, lambda { |date| {:conditions => ["todos.completed_at > ? ", date] } }
named_scope :completed_before, lambda { |date| {:conditions => ["todos.completed_at < ? ", date] } }
diff --git a/app/views/todos/mobile_tag.rhtml b/app/views/todos/mobile_tag.rhtml
index 5dd8efd3..4dde5250 100644
--- a/app/views/todos/mobile_tag.rhtml
+++ b/app/views/todos/mobile_tag.rhtml
@@ -2,24 +2,24 @@
<% if @not_done_todos.empty? -%>
<%= t('todos.no_actions_found_title') %>
-
<%= t('todos.no_actions_with', :tag_name => @tag_name) %>
+
<%= t('todos.no_actions_with', :tag_name => @tag_title) %>
<% end -%>
<%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%>
-<%= t('todos.deferred_actions_with', :tag_name=> @tag_name) %>
+<%= t('todos.deferred_actions_with', :tag_name=> @tag_title) %>
<% unless (@deferred.nil? or @deferred.size == 0) -%>
<%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "tag" } -%>
-
+
<% else -%>
-<%= t('todos.no_deferred_actions_with', :tag_name => @tag_name) %>
+<%= t('todos.no_deferred_actions_with', :tag_name => @tag_title) %>
<% end -%>
-<%= t('todos.completed_actions_with', :tag_name => @tag_name) %>
+<%= t('todos.completed_actions_with', :tag_name => @tag_title) %>
<% unless (@done.nil? or @done.size == 0) -%>
<%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "tag" } %>
<% else -%>
-<%= t('todos.no_completed_actions_with', :tag_name => @tag_name) %>
+<%= t('todos.no_completed_actions_with', :tag_name => @tag_title) %>
<% end -%>
\ No newline at end of file
diff --git a/app/views/todos/tag.html.erb b/app/views/todos/tag.html.erb
index 646f153c..64506aa1 100644
--- a/app/views/todos/tag.html.erb
+++ b/app/views/todos/tag.html.erb
@@ -8,22 +8,22 @@
:locals => { :collapsible => true } %>
<% unless @deferred.nil? -%>
- <%= render :partial => "todos/deferred", :locals => {
- :deferred => @deferred,
+ <%= render :partial => "todos/deferred", :locals => {
+ :deferred => @deferred,
:pending => @pending,
- :collapsible => true,
- :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name),
- :parent_container_type => 'tag'
+ :collapsible => true,
+ :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title),
+ :parent_container_type => 'tag'
} %>
<% end -%>
<% unless @hidden_todos.nil? -%>
- <%= render :partial => "todos/hidden", :object => @hidden_todos, :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %>
+ <%= render :partial => "todos/hidden", :object => @hidden_todos, :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title) } %>
<% end -%>
<% unless @done.nil? -%>
<%= render :partial => "todos/completed", :object => @done,
- :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %>
+ :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title) } %>
<% end -%>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 338d9531..9feff005 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -391,10 +391,10 @@ en:
all_completed: All completed actions
feed_title_in_context: in context '%{context}'
older_than_days: ""
- completed_tagged_page_title: TRACKS::Completed tasks with tag %{tag_name}
+ completed_tagged_page_title: TRACKS::Completed tasks with tag '%{tag_name}'
edit: Edit
pending: Pending
- completed_actions_with: Completed actions with the tag %{tag_name}
+ completed_actions_with: Completed actions with the tag '%{tag_name}'
deleted_success: The action was deleted succesfully.
completed_tasks_title: TRACKS::Completed tasks
feed_title_in_project: in project '%{project}'
diff --git a/config/routes.rb b/config/routes.rb
index 60f777cd..533ca270 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -40,6 +40,7 @@ ActionController::Routing::Routes.draw do |map|
# UPDATE: added support for mobile view. All tags ending on .m will be
# routed to mobile view of tags.
todos.mobile_tag 'todos/tag/:name.m', :action => "tag", :format => 'm'
+ todos.mobile_tag 'todos/tag/:name.txt', :action => "tag", :format => 'txt'
todos.tag 'todos/tag/:name', :action => "tag", :name => /.*/
todos.done_tag 'todos/done/tag/:name', :action => "done_tag"
todos.all_done_tag 'todos/all_done/tag/:name', :action => "all_done_tag"
diff --git a/test/functional/todos_controller_test.rb b/test/functional/todos_controller_test.rb
index 1abdfadc..20b3c7a5 100644
--- a/test/functional/todos_controller_test.rb
+++ b/test/functional/todos_controller_test.rb
@@ -644,4 +644,68 @@ class TodosControllerTest < ActionController::TestCase
assert_select("div#notes_todo_#{todo.id} a", 'link me to onenote')
assert_select("div#notes_todo_#{todo.id} a[href=onenote:///E:\\OneNote\\dir\\notes.one#PAGE§ion-id={FD597D3A-3793-495F-8345-23D34A00DD3B}&page-id={1C95A1C7-6408-4804-B3B5-96C28426022B}&end]", 'link me to onenote')
end
+
+ def test_get_boolean_expression_from_parameters_of_tag_view_single_tag
+ login_as(:admin_user)
+ get :tag, :name => "single"
+ assert_equal true, assigns['single_tag'], "should recognize it is a single tag name"
+ assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag"
+ end
+
+ def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags
+ login_as(:admin_user)
+ get :tag, :name => "multiple", :and => "tags", :and1 => "present", :and2 => "here"
+ assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
+ assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions"
+ end
+
+ def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags_without_digitless_and
+ login_as(:admin_user)
+ get :tag, :name => "multiple", :and1 => "tags", :and2 => "present", :and3 => "here"
+ assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
+ assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions"
+ end
+
+ def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs
+ login_as(:admin_user)
+ get :tag, :name => "multiple,tags,present"
+ assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
+ assert_equal 1, assigns['tag_expr'].size, "should have 1 expressions"
+ assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression"
+ end
+
+ def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs_and_ANDS
+ login_as(:admin_user)
+ get :tag, :name => "multiple,tags,present", :and => "here,is,two", :and1=>"and,three"
+ assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
+ assert_equal 3, assigns['tag_expr'].size, "should have 3 expressions"
+ assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression"
+ assert_equal 3, assigns['tag_expr'][1].size, "should have 3 ORs in 2nd expression"
+ assert_equal 2, assigns['tag_expr'][2].size, "should have 2 ORs in 3rd expression"
+ end
+
+ def test_set_right_title
+ login_as(:admin_user)
+
+ get :tag, :name => "foo"
+ assert_equal "foo", assigns['tag_title']
+ get :tag, :name => "foo,bar", :and => "baz"
+ assert_equal "foo,bar AND baz", assigns['tag_title']
+ end
+
+ def test_set_default_tag
+ login_as(:admin_user)
+
+ get :tag, :name => "foo"
+ assert_equal "foo", assigns['initial_tags']
+ get :tag, :name => "foo,bar", :and => "baz"
+ assert_equal "foo", assigns['initial_tags']
+ end
+
+ def test_tag_text_feed_not_accessible_to_anonymous_user_without_token
+ login_as nil
+ get :tag, {:name => "foo", :format => "txt" }
+ assert_response 401
+ end
+
end
diff --git a/test/functional/todos_tagging_test.rb b/test/functional/todos_tagging_test.rb
deleted file mode 100644
index 46dd1627..00000000
--- a/test/functional/todos_tagging_test.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-require 'todos_controller'
-
-# Re-raise errors caught by the controller.
-class TodosController; def rescue_action(e) raise e end; end
-
-class TodosControllerTest < ActionController::TestCase
- fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos
-
- def test_get_boolean_expression_from_parameters_of_tag_view_single_tag
- login_as(:admin_user)
- get :tag, :name => "single"
- assert_equal true, assigns['single_tag'], "should recognize it is a single tag name"
- assert_equal "single", assigns['tag_expr'][0][0], "should store the single tag"
- end
-
- def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags
- login_as(:admin_user)
- get :tag, :name => "multiple", :and => "tags", :and1 => "present", :and2 => "here"
- assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
- assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions"
- end
-
- def test_get_boolean_expression_from_parameters_of_tag_view_multiple_tags_without_digitless_and
- login_as(:admin_user)
- get :tag, :name => "multiple", :and1 => "tags", :and2 => "present", :and3 => "here"
- assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
- assert_equal 4, assigns['tag_expr'].size, "should have 4 AND expressions"
- end
-
- def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs
- login_as(:admin_user)
- get :tag, :name => "multiple,tags,present"
- assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
- assert_equal 1, assigns['tag_expr'].size, "should have 1 expressions"
- assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression"
- end
-
- def test_get_boolean_expression_from_parameters_of_tag_view_multiple_ORs_and_ANDS
- login_as(:admin_user)
- get :tag, :name => "multiple,tags,present", :and => "here,is,two", :and1=>"and,three"
- assert_equal false, assigns['single_tag'], "should recognize it has multiple tags"
- assert_equal 3, assigns['tag_expr'].size, "should have 3 expressions"
- assert_equal 3, assigns['tag_expr'][0].size, "should have 3 ORs in 1st expression"
- assert_equal 3, assigns['tag_expr'][1].size, "should have 3 ORs in 2nd expression"
- assert_equal 2, assigns['tag_expr'][2].size, "should have 2 ORs in 3rd expression"
- end
-
- def test_get_ids_from_tag_expr
- login_as(:admin_user)
-
- # make sure the tags exits
- # "multiple,tags,present,here,is,two,and,three".split(',').each { |tag| Tag.find_or_create_by_name(:name=>tag)}
-
- get :tag, :name => "foo,bar", :and => "baz"
-
- assert_equal 1, assigns['tag_ids'][0][0], "first id should be 1 for foo"
- assert_equal 2, assigns['tag_ids'][0][1], "second id should be 2 for bar"
- assert_equal 3, assigns['tag_ids'][1][0], "third id should be 3 for baz"
- end
-
-end
\ No newline at end of file
diff --git a/test/unit/todo_test.rb b/test/unit/todo_test.rb
index 00460139..eb677699 100644
--- a/test/unit/todo_test.rb
+++ b/test/unit/todo_test.rb
@@ -9,7 +9,7 @@ class TodoTest < ActiveSupport::TestCase
@not_completed2 = Todo.find(2).reload
@completed = Todo.find(8).reload
end
-
+
# Test loading a todo item
def test_load
assert_kind_of Todo, @not_completed1
@@ -24,13 +24,13 @@ class TodoTest < ActiveSupport::TestCase
assert_nil @not_completed1.completed_at
assert_equal 1, @not_completed1.user_id
end
-
+
def test_completed
assert_kind_of Todo, @completed
assert @completed.completed?
assert_not_nil @completed.completed_at
end
-
+
def test_completed_at_cleared_after_toggle_to_active
assert_kind_of Todo, @completed
assert @completed.completed?
@@ -38,8 +38,8 @@ class TodoTest < ActiveSupport::TestCase
assert @completed.active?
assert_nil @completed.completed_at
end
-
-
+
+
# Validation tests
#
def test_validate_presence_of_description
@@ -49,7 +49,7 @@ class TodoTest < ActiveSupport::TestCase
assert_equal 1, @not_completed2.errors.count
assert_equal "can't be blank", @not_completed2.errors.on(:description)
end
-
+
def test_validate_length_of_description
assert_equal "Call dinosaur exterminator", @not_completed2.description
@not_completed2.description = generate_random_string(101)
@@ -57,7 +57,7 @@ class TodoTest < ActiveSupport::TestCase
assert_equal 1, @not_completed2.errors.count
assert_equal "is too long (maximum is 100 characters)", @not_completed2.errors.on(:description)
end
-
+
def test_validate_length_of_notes
assert_equal "Ask him if I need to hire a skip for the corpses.", @not_completed2.notes
@not_completed2.notes = generate_random_string(60001)
@@ -74,7 +74,7 @@ class TodoTest < ActiveSupport::TestCase
assert_equal 1, t.errors.count
assert_equal "must be a date in the future", t.errors.on(:show_from)
end
-
+
def test_defer_an_existing_todo
@not_completed2
assert_equal :active, @not_completed2.aasm_current_state
@@ -82,20 +82,20 @@ class TodoTest < ActiveSupport::TestCase
assert @not_completed2.save, "should have saved successfully" + @not_completed2.errors.to_xml
assert_equal :deferred, @not_completed2.aasm_current_state
end
-
+
def test_create_a_new_deferred_todo
user = users(:other_user)
todo = user.todos.build
todo.show_from = next_week
todo.context_id = 1
- todo.description = 'foo'
+ todo.description = 'foo'
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.aasm_current_state
end
def test_create_a_new_deferred_todo_by_passing_attributes
user = users(:other_user)
- todo = user.todos.build(:show_from => next_week, :context_id => 1, :description => 'foo')
+ todo = user.todos.build(:show_from => next_week, :context_id => 1, :description => 'foo')
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.aasm_current_state
end
@@ -167,15 +167,15 @@ class TodoTest < ActiveSupport::TestCase
t.reload
assert_equal :deferred, t.aasm_current_state
end
-
+
def test_todo_is_not_starred
assert !@not_completed1.starred?
end
-
+
def test_todo_2_is_not_starred
assert !Todo.find(2).starred?
end
-
+
def test_todo_is_starred_after_starred_tag_is_added
@not_completed1._add_tags('starred')
assert @not_completed1.starred?
@@ -185,7 +185,7 @@ class TodoTest < ActiveSupport::TestCase
@not_completed1.toggle_star!
assert @not_completed1.starred?
end
-
+
def test_todo_is_not_starred_after_toggle_starred_twice
@not_completed1.toggle_star!
@not_completed1.toggle_star!
@@ -239,4 +239,101 @@ class TodoTest < ActiveSupport::TestCase
assert_equal 2, @predecessor_array.size
end
+ def test_finding_todos_with_a_tag
+ todo = @not_completed1
+ todo.tag_list = "a, b, c"
+ todo.save!
+
+ tag_a = Tag.find_by_name("a")
+ tag_b = Tag.find_by_name("b")
+ tag_c = Tag.find_by_name("c")
+
+ todos_with_a = Todo.with_tag(tag_a)
+ assert 1, todos_with_a.count
+ assert_equal todo.description, todos_with_a.first.description
+
+ todos_with_b = Todo.with_tag(tag_b)
+ assert 1, todos_with_b.count
+ assert_equal todo.id, todos_with_b.first.id
+
+ todo2 = @not_completed2
+ todo2.tag_list = "a, c, d"
+ todo2.save!
+
+ tag_d = Tag.find_by_name("d")
+
+ todos_with_a = Todo.with_tag(tag_a)
+ assert 2, todos_with_a.count
+
+ todos_with_d = Todo.with_tag(tag_d)
+ assert 1, todos_with_a.count
+ end
+
+ def test_finding_todos_with_more_tags_using_OR
+ todo1 = @not_completed1
+ todo1.tag_list = "a, b, c"
+ todo1.save!
+
+ todo2 = @not_completed2
+ todo2.tag_list = "a, c, d"
+ todo2.save!
+
+ tag_a = Tag.find_by_name("a")
+ tag_b = Tag.find_by_name("b")
+ tag_c = Tag.find_by_name("c")
+ tag_d = Tag.find_by_name("d")
+
+ # overlapping tags
+ tag_ids = [tag_a.id, tag_c.id]
+ todos_with_a_or_c = Todo.with_tags(tag_ids)
+ assert 2, todos_with_a_or_c.count
+
+ # non-overlapping tags
+ tag_ids = [tag_b.id, tag_d.id]
+ todos_with_b_or_d = Todo.with_tags(tag_ids)
+ assert 2, todos_with_b_or_d.count
+ end
+
+ def test_finding_todos_with_more_tags_using_AND
+ todo1 = @not_completed1
+ todo1.tag_list = "a, b, c"
+ todo1.save!
+
+ todo2 = @not_completed2
+ todo2.tag_list = "a, c, d"
+ todo2.save!
+
+ tag_a_id = Tag.find_by_name("a").id
+ tag_b_id = Tag.find_by_name("b").id
+
+ todos_with_a_and_b = Todo.with_tags([tag_a_id]).with_tags([tag_b_id])
+ assert 1, todos_with_a_and_b.count
+ assert todo1.id, todos_with_a_and_b.first.id
+ end
+
+ def test_finding_todos_with_more_tags_using_AND_and_OR
+ todo1 = @not_completed1
+ todo1.tag_list = "a, b, c"
+ todo1.save!
+
+ todo2 = @not_completed2
+ todo2.tag_list = "a, c, d"
+ todo2.save!
+
+ tag_a_id = Tag.find_by_name("a").id
+ tag_b_id = Tag.find_by_name("b").id
+ tag_c_id = Tag.find_by_name("c").id
+
+ todos_with_aORc_and_b = Todo.with_tags([tag_a_id, tag_c_id]).with_tags([tag_b_id])
+ assert 1, todos_with_aORc_and_b.count
+ assert todo1.id, todos_with_aORc_and_b.first.id
+
+ # let todo2 fit the expression
+ todo2.tag_list = "a, b, r"
+ todo2.save!
+ todos_with_aORc_and_b = Todo.with_tags([tag_a_id, tag_c_id]).with_tags([tag_b_id])
+ assert 2, todos_with_aORc_and_b.count
+ end
+
+
end