Merge branch 'master' of git://github.com/bsag/tracks

This commit is contained in:
Reinier Balt 2009-01-15 22:40:13 +01:00
commit 1769c4b4f4
39 changed files with 2554 additions and 3688 deletions

22
.gitignore vendored
View file

@ -1,13 +1,15 @@
*.tmproj
config/database.yml
config/environment.rb
log
tmp
db/data.yml
db/*.sqlite3
nbproject
vendor/plugins/query_trace/
db/schema.rb
.dotest
/.emacs-project
config/database.yml
config/deploy.rb
config/environment.rb
db/*.sqlite3
db/data.yml
db/schema.rb
log
nbproject
public/javascripts/cache
public/stylesheets/cache
public/stylesheets/cache
tmp
vendor/plugins/query_trace/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "doc/manual"]
path = doc/manual
url = git://github.com/bsag/tracks_manual.git

2
Capfile Normal file
View file

@ -0,0 +1,2 @@
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
load 'config/deploy'

14
README
View file

@ -1,19 +1,21 @@
# Tracks: a GTD(TM) web application, built with Ruby on Rails
* Project homepage: http://www.rousette.org.uk/projects/
* Project homepage: http://getontracks.org/
* GitHub: http://github.com/bsag/tracks/
* Trac (for bug reports and feature requests): http://dev.rousette.org.uk/report/6
* Wiki (community contributed information): http://www.rousette.org.uk/projects/wiki/
* Forum: http://www.rousette.org.uk/projects/forums/
* Assembla space (for bug reports and feature requests): http://www.assembla.com/spaces/tracks-tickets/tickets
* Wiki (community contributed information): http://getontracks.org/wiki/
* Forum: http://getontracks.org/forums/
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
* Original developer: bsag (http://www.rousette.org.uk/)
* Contributors: http://dev.rousette.org.uk/wiki/Tracks/Contributing/Contributors
* Version: 1.7RC
* Contributors: http://getontracks.org/wiki/Tracks/Contributing/Contributors
* Version: 1.7
* Copyright: (cc) 2004-2008 rousette.org.uk.
* License: GNU GPL
All the documentation for Tracks can be found within the /doc directory. It contains a manual in HTML (manual.html) or PDF format (manual.pdf), and this includes full instructions for both new installations and upgrades from older installations of Tracks. The instructions might appear long and intimidatingly complex, but that is mostly because of the number of different platforms supported, and the different configurations which can be used (e.g. running Tracks on your local computer or on a remote server). If you choose the appropriate section for your situation (installation vs. upgrade), and use the easiest (recommended) method, you should find the instructions easy to follow. If you encounter problems, try searching the wiki, forum or mailing list (URLs above), and ask a question if you cannot find a solution to your problem.
If you checked out Tracks from the GitHub repository, the manual is not provided by default and is in its own git submodule. To checkout the manual's source files, type "git submodule init doc/manual & git submodule update doc/manual". From then on, you should be able to issue the command "git pull" in the doc/manual directory to update the manual with the latest changes.
For those upgrading, change notes are available in /doc/CHANGELOG. If you are thinking about contributing towards the development of Tracks, please read /doc/README_DEVELOPERS for general information, or /doc/tracks_api_wrapper.rb for information on Tracks' API.
While fully usable for everyday use, Tracks is still a work in progress. Make sure that you take sensible precautions and back up all your data frequently, taking particular care when you are upgrading.

View file

@ -40,8 +40,10 @@ class ProjectsController < ApplicationController
end
def show
@contexts = current_user.contexts(true)
init_data_for_sidebar unless mobile?
@projects = current_user.projects
@contexts = current_user.contexts
@page_title = "TRACKS::Project: #{@project.name}"
@project.todos.send :with_scope, :find => { :include => [:context] } do
@not_done = @project.not_done_todos(:include_project_hidden_todos => true)

View file

@ -6,8 +6,9 @@ class StatsController < ApplicationController
def index
@page_title = 'TRACKS::Statistics'
@unique_tags = @tags.count(:all, {:group=>"tag_id"})
@tags_count = get_total_number_of_tags_of_user
@unique_tags_count = get_unique_tags_of_user.size
@hidden_contexts = @contexts.hidden
@first_action = @actions.find(:first, :order => "created_at ASC")
@ -394,7 +395,7 @@ class StatsController < ApplicationController
def context_running_actions_data
# get incomplete action count per visible context
#
#
# Went from GROUP BY c.id to c.name for compatibility with postgresql. Since
# the name is forced to be unique, this should work.
@all_actions_per_context = @contexts.find_by_sql(
@ -643,11 +644,32 @@ class StatsController < ApplicationController
private
def get_unique_tags_of_user
tag_ids = @actions.find_by_sql([
"SELECT DISTINCT tags.id as id "+
"FROM tags, taggings, todos "+
"WHERE todos.user_id=? "+
"AND tags.id = taggings.tag_id " +
"AND taggings.taggable_id = todos.id ", current_user.id])
tags_ids_s = tag_ids.map(&:id).sort.join(",")
return {} if tags_ids_s.blank? # return empty array for .size to work
return Tag.find(:all, :conditions => "id in (" + tags_ids_s + ")")
end
def get_total_number_of_tags_of_user
# same query as get_unique_tags_of_user except for the DISTINCT
return @actions.find_by_sql([
"SELECT tags.id as id "+
"FROM tags, taggings, todos "+
"WHERE todos.user_id=? "+
"AND tags.id = taggings.tag_id " +
"AND taggings.taggable_id = todos.id ", current_user.id]).size
end
def init
@actions = @user.todos
@projects = @user.projects
@contexts = @user.contexts
@tags = @user.tags
# default chart dimensions
@chart_width=460
@ -724,7 +746,7 @@ class StatsController < ApplicationController
def get_stats_contexts
# get action count per context for TOP 5
#
#
# Went from GROUP BY c.id to c.id, c.name for compatibility with postgresql.
# Since the name is forced to be unique, this should work.
@actions_per_context = @contexts.find_by_sql(
@ -737,7 +759,7 @@ class StatsController < ApplicationController
)
# get incomplete action count per visible context for TOP 5
#
#
# Went from GROUP BY c.id to c.id, c.name for compatibility with postgresql.
# Since the name is forced to be unique, this should work.
@running_actions_per_context = @contexts.find_by_sql(
@ -752,7 +774,7 @@ class StatsController < ApplicationController
def get_stats_projects
# get the first 10 projects and their action count (all actions)
#
#
# Went from GROUP BY p.id to p.name for compatibility with postgresql. Since
# the name is forced to be unique, this should work.
@projects_and_actions = @projects.find_by_sql(
@ -812,9 +834,10 @@ class StatsController < ApplicationController
# Get the tag cloud for all tags for actions
query = "SELECT tags.id, name, count(*) AS count"
query << " FROM taggings, tags"
query << " FROM taggings, tags, todos"
query << " WHERE tags.id = tag_id"
query << " AND taggings.user_id="+@user.id.to_s+" "
query << " AND taggings.taggable_id = todos.id"
query << " AND todos.user_id="+current_user.id.to_s+" "
query << " AND taggings.taggable_type='Todo' "
query << " GROUP BY tags.id, tags.name"
query << " ORDER BY count DESC, name"
@ -833,7 +856,7 @@ class StatsController < ApplicationController
query = "SELECT tags.id, tags.name AS name, count(*) AS count"
query << " FROM taggings, tags, todos"
query << " WHERE tags.id = tag_id"
query << " AND taggings.user_id=? "
query << " AND todos.user_id=? "
query << " AND taggings.taggable_type='Todo' "
query << " AND taggings.taggable_id=todos.id "
query << " AND (todos.created_at > ? OR "
@ -842,7 +865,7 @@ class StatsController < ApplicationController
query << " ORDER BY count DESC, name"
query << " LIMIT 100"
@tags_for_cloud_90days = Tag.find_by_sql(
[query, @user.id, @cut_off_3months, @cut_off_3months]
[query, current_user.id, @cut_off_3months, @cut_off_3months]
).sort_by { |tag| tag.name.downcase }
max_90days, @tags_min_90days = 0, 0

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,37 @@
class Preference < ActiveRecord::Base
belongs_to :user
belongs_to :sms_context, :class_name => 'Context'
def self.due_styles
{ :due_in_n_days => 0, :due_on => 1}
end
def self.day_number_to_name_map
{ 0 => "Sunday",
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday"}
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday"}
end
def hide_completed_actions?
return show_number_completed == 0
end
def parse_date(s)
return nil if s.blank?
user.at_midnight(Date.strptime(s, date_format))
date = nil
if s.is_a?(Time)
date = s.to_date
elsif s.is_a?(String)
date = Date.strptime(s, date_format)
else
raise ArgumentError.new("Bad argument type:#{s.class}")
end
user.at_midnight(date)
end
end
end

View file

@ -5,7 +5,6 @@ class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :taggable, :polymorphic => true
# belongs_to :user
# If you also need to use <tt>acts_as_list</tt>, you will have to manage the tagging positions manually by creating decorated join records when you associate Tags with taggables.
# acts_as_list :scope => :taggable

View file

@ -104,8 +104,6 @@ class User < ActiveRecord::Base
end
has_many :notes, :order => "created_at DESC", :dependent => :delete_all
has_one :preference, :dependent => :destroy
has_many :taggings
has_many :tags, :through => :taggings, :select => "DISTINCT tags.*"
attr_protected :is_admin
@ -202,7 +200,7 @@ class User < ActiveRecord::Base
# These create and unset the fields required for remembering users between browser closes
def remember_me
self.remember_token_expires_at = 2.weeks.from_now.utc
self.remember_token = self.class.sha1("#{login}--#{remember_token_expires_at}")
self.remember_token ||= self.class.sha1("#{login}--#{remember_token_expires_at}")
save(false)
end

View file

@ -39,7 +39,7 @@
<h4>...or login with an Open ID:</h4>
<div id="openid_auth_form">
<% form_tag formatted_open_id_begin_path(:format => 'm') do %>
<% form_tag formatted_login_path(:format => 'm') do %>
<table>
<tr>
<td width="100px"><label for="openid_url">Identity URL:</label></td>

View file

@ -27,17 +27,17 @@
:condition => "!$('todo_new_action_submit').isWaiting() && askIfNewContextProvided()") do -%>
<script type="text/javascript" charset="utf-8">
function askIfNewContextProvided() {
var contexts = new Array(<%= @contexts.map{|c| '\'' + c.name + '\''}.join(", ") %>);
var givenContextName = $('todo_context_name').value;
if (givenContextName.length == 0) return true; // show rails validation error
for (var i = 0; i < contexts.length; ++i) {
if (contexts[i] == givenContextName) {
return true;
}
}
return confirm('New context "' + givenContextName + '" will be also created. Are you sure?');
}
function askIfNewContextProvided() {
var contexts = new Array(<%= @contexts.map{|c| '\'' + c.name + '\''}.join(", ") %>);
var givenContextName = $('todo_context_name').value;
if (givenContextName.length == 0) return true; // show rails validation error
for (var i = 0; i < contexts.length; ++i) {
if (contexts[i] == givenContextName) {
return true;
}
}
return confirm('New context "' + givenContextName + '" will be also created. Are you sure?');
}
</script>
<div id="status"><%= error_messages_for("item", :object_name => 'action') %></div>

View file

@ -1,3 +1,3 @@
<div id="footer">
<p>Send feedback on <%= TRACKS_VERSION %>: <a href="http://dev.rousette.org.uk/report/6">Trac</a> | <a href="http://www.rousette.org.uk/projects/forums/">Forum</a> | <a href="http://www.rousette.org.uk/projects/wiki/index">Wiki</a> | <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.rousette.org.uk/projects/">Website</a> | <a href="http://www.rousette.org.uk/projects/tracks/contribute">Contribute</a></p>
<p>Send feedback on <%= TRACKS_VERSION %>: <a href="http://www.assembla.com/spaces/tracks-tickets/tickets">Bugs</a> | <a href="http://www.getontracks.org/forums/">Forum</a> | <a href="http://www.getontracks.org/wiki/">Wiki</a> | <a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback">Email</a> | <a href="http://www.getontracks.org/">Website</a> | <a href="http://www.getontracks.org/development/">Contribute</a></p>
</div>

View file

@ -16,7 +16,7 @@ deferred actions. </p>
a total of <%= @actions.count %> actions.
<%= @actions.count(:conditions => "NOT completed_at IS NULL") %> of these are completed.
<p>You have <%= @tags.count-%> tags placed on actions. Of those tags,
<%= @unique_tags.size -%> are unique.
<p>You have <%= @tags_count-%> tags placed on actions. Of those tags,
<%= @unique_tags_count -%> are unique.
<% end -%>

View file

@ -31,4 +31,4 @@
<% end -%>
</div>
</div>
<%= apply_behaviour ".date_clear:click","var selector_x = this.getAttribute('id').replace('_x', ''); $(selector_x).value='';" %>
<%= apply_behaviour ".date_clear:click","var selector_x = this.getAttribute('id').replace('_x', ''); $(selector_x).value='';" %>

View file

@ -1,4 +1,4 @@
page[dom_id(@todo, 'form')].down('.placeholder').replace_html :partial => 'todos/edit_form'
page[dom_id(@todo, 'line')].hide
page[dom_id(@todo, 'edit')].show
page[dom_id(@todo, 'form')].down('table').down('input').focus
page[dom_id(@todo, 'form')].down('input#todo_description').focus

101
config/deploy.rb-example Normal file
View file

@ -0,0 +1,101 @@
#############################################################
# This file is designed as a starting point to use
# capistrano to deploy the trunk of tracks to a webhost
# where it is served using Phusion Passenger. For more
# info on getting started with Passenger, see
# http://www.modrails.com/
#############################################################
#############################################################
# Application
#############################################################
set :application, "tracks"
set :deploy_to, "/var/www/apps/tracks"
#############################################################
# Settings
#############################################################
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
set :use_sudo, true
set :scm_verbose, true
set :rails_env, "production"
#############################################################
# Servers
#############################################################
#set :user, "your_login_name_on_your_webhost_if_different_from_local"
set :domain, "tracks.yoursite.com"
server domain, :app, :web
role :db, domain, :primary => true
#############################################################
# Git
#############################################################
set :scm, :git
set :branch, "master"
set :repository, "git://github.com/bsag/tracks.git"
set :deploy_via, :remote_cache
#############################################################
# Passenger
#############################################################
namespace :deploy do
desc "Symlink config files"
task :before_symlink do
run "rm #{release_path}/public/.htaccess" #not compatible with Passenger
run "ln -s #{shared_path}/config/database.yml #{release_path}/config/database.yml"
run "ln -s #{shared_path}/config/environment.rb #{release_path}/config/environment.rb"
end
# Restart passenger on deploy
desc "Restarting mod_rails with restart.txt"
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{current_path}/tmp/restart.txt"
end
[:start, :stop].each do |t|
desc "#{t} task is a no-op with mod_rails"
task t, :roles => :app do ; end
end
end
namespace :db do
desc 'Dumps the production database to db/production_data.sql on the remote server'
task :remote_db_dump, :roles => :db, :only => { :primary => true } do
run "cd #{deploy_to}/#{current_dir} && " +
"rake RAILS_ENV=#{rails_env} db:dump_sql --trace"
end
desc 'Downloads db/production_data.sql from the remote production environment to your local machine'
task :remote_db_download, :roles => :db, :only => { :primary => true } do
execute_on_servers(options) do |servers|
self.sessions[servers.first].sftp.connect do |tsftp|
tsftp.download!("#{deploy_to}/#{current_dir}/db/production_data.sql", "db/production_data.sql")
end
end
end
desc 'Cleans up data dump file'
task :remote_db_cleanup, :roles => :db, :only => { :primary => true } do
execute_on_servers(options) do |servers|
self.sessions[servers.first].sftp.connect do |tsftp|
tsftp.remove! "#{deploy_to}/#{current_dir}/db/production_data.sql"
end
end
end
desc 'Dumps, downloads and then cleans up the production data dump'
task :remote_db_runner do
remote_db_dump
remote_db_download
remote_db_cleanup
end
end

View file

@ -1,111 +1,111 @@
# Be sure to restart your webserver when you modify this file.
# Uncomment below to force Rails into production mode
# (Use only when you can't set environment variables through your web/app server)
# ENV['RAILS_ENV'] = 'production'
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
# This is the 'salt' to add to the password before it is encrypted
# You need to change this to something unique for yourself
SALT = "change-me"
class Rails::Configuration
attr_accessor :action_web_service
end
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
# If you choose ldap, see the additional configuration options further down.
AUTHENTICATION_SCHEMES = ['database']
Rails::Initializer.run do |config|
# Skip frameworks you're not going to use
# config.frameworks -= [ :action_web_service, :action_mailer ]
config.frameworks += [ :action_web_service]
config.action_web_service = Rails::OrderedOptions.new
config.load_paths += %W( #{RAILS_ROOT}/app/apis )
config.gem "highline"
config.action_controller.use_accept_header = true
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/app/services )
# Force all environments to use the same logger level
# (by default production uses :info, the others :debug)
# config.log_level = :debug
# Use the database for sessions instead of the file system
# (create the session table with 'rake create_sessions_table')
config.action_controller.session_store = :active_record_store
config.action_controller.session = {
:session_key => '_tracks_session_id',
:secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
}
# Enable page/fragment caching by setting a file-based store
# (remember to create the caching directory and make it readable to the application)
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector
# Make Active Record use UTC-base instead of local time
config.active_record.default_timezone = :utc
# You''ll probably want to change this to the time zone of the computer where Tracks is running
# run rake time:zones:local have Rails suggest time zone names on your system
config.time_zone = 'UTC'
# Use Active Record's schema dumper instead of SQL when creating the test database
# (enables use of different database adapters for development and test environments)
config.active_record.schema_format = :ruby
# See Rails::Configuration for more options
end
# Add new inflection rules using the following format
# (all these examples are active by default):
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
# Include your application configuration below
require 'name_part_finder'
require 'tracks/todo_list'
require 'tracks/config'
require 'tagging_extensions' # Needed for tagging-specific extensions
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
require 'prototype_helper_extensions'
if (AUTHENTICATION_SCHEMES.include? 'ldap')
require 'net/ldap' #requires ruby-net-ldap gem be installed
require 'simple_ldap_authenticator'
SimpleLdapAuthenticator.ldap_library = 'net/ldap'
SimpleLdapAuthenticator.servers = %w'localhost'
SimpleLdapAuthenticator.use_ssl = false
SimpleLdapAuthenticator.login_format = 'cn=%s,dc=example,dc=com'
end
if (AUTHENTICATION_SCHEMES.include? 'open_id')
#requires ruby-openid gem to be installed
end
# setting this to true will make the cookies only available over HTTPS
TRACKS_COOKIES_SECURE = false
tracks_version='1.7RC'
# comment out next two lines if you do not want (or can not) the date of the
# last git commit in the footer
# info=`git log --pretty=format:"%ai" -1`
# tracks_version=tracks_version + ' ('+info+')'
TRACKS_VERSION=tracks_version
# Be sure to restart your webserver when you modify this file.
# Uncomment below to force Rails into production mode
# (Use only when you can't set environment variables through your web/app server)
# ENV['RAILS_ENV'] = 'production'
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
# This is the 'salt' to add to the password before it is encrypted
# You need to change this to something unique for yourself
SALT = "change-me"
class Rails::Configuration
attr_accessor :action_web_service
end
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
# If you choose ldap, see the additional configuration options further down.
AUTHENTICATION_SCHEMES = ['database']
Rails::Initializer.run do |config|
# Skip frameworks you're not going to use
# config.frameworks -= [ :action_web_service, :action_mailer ]
config.frameworks += [ :action_web_service]
config.action_web_service = Rails::OrderedOptions.new
config.load_paths += %W( #{RAILS_ROOT}/app/apis )
config.gem "highline"
config.action_controller.use_accept_header = true
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/app/services )
# Force all environments to use the same logger level
# (by default production uses :info, the others :debug)
# config.log_level = :debug
# Use the database for sessions instead of the file system
# (create the session table with 'rake create_sessions_table')
config.action_controller.session_store = :active_record_store
config.action_controller.session = {
:session_key => '_tracks_session_id',
:secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
}
# Enable page/fragment caching by setting a file-based store
# (remember to create the caching directory and make it readable to the application)
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector
# Make Active Record use UTC-base instead of local time
config.active_record.default_timezone = :utc
# You''ll probably want to change this to the time zone of the computer where Tracks is running
# run rake time:zones:local have Rails suggest time zone names on your system
config.time_zone = 'UTC'
# Use Active Record's schema dumper instead of SQL when creating the test database
# (enables use of different database adapters for development and test environments)
config.active_record.schema_format = :ruby
# See Rails::Configuration for more options
end
# Add new inflection rules using the following format
# (all these examples are active by default):
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
# Include your application configuration below
require 'name_part_finder'
require 'tracks/todo_list'
require 'tracks/config'
require 'tagging_extensions' # Needed for tagging-specific extensions
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
require 'prototype_helper_extensions'
if (AUTHENTICATION_SCHEMES.include? 'ldap')
require 'net/ldap' #requires ruby-net-ldap gem be installed
require 'simple_ldap_authenticator'
SimpleLdapAuthenticator.ldap_library = 'net/ldap'
SimpleLdapAuthenticator.servers = %w'localhost'
SimpleLdapAuthenticator.use_ssl = false
SimpleLdapAuthenticator.login_format = 'cn=%s,dc=example,dc=com'
end
if (AUTHENTICATION_SCHEMES.include? 'open_id')
#requires ruby-openid gem to be installed
end
# setting this to true will make the cookies only available over HTTPS
TRACKS_COOKIES_SECURE = false
tracks_version='1.7'
# comment out next two lines if you do not want (or can not) the date of the
# last git commit in the footer
# info=`git log --pretty=format:"%ai" -1`
# tracks_version=tracks_version + ' ('+info+')'
TRACKS_VERSION=tracks_version

View file

@ -0,0 +1,9 @@
class RemoveUserFromTaggings < ActiveRecord::Migration
def self.up
remove_column :taggings, :user_id
end
def self.down
add_column :taggings, :user_id, :integer
end
end

View file

@ -1,13 +1,13 @@
= Tracks: a GTD web application, built with Ruby on Rails
* Project homepage: http://www.rousette.org.uk/projects/
* Project homepage: http://getontracks.org/
* GitHub: http://github.com/bsag/tracks/
* Trac (for bug reports and feature requests): http://dev.rousette.org.uk/report/6
* Wiki (community contributed information): http://www.rousette.org.uk/projects/wiki/
* Forum: http://www.rousette.org.uk/projects/forums/
* Assembla space (for bug reports and feature requests): http://www.assembla.com/spaces/tracks-tickets/tickets
* Wiki (community contributed information): http://getontracks.org/wiki/
* Forum: http://getontracks.org/forums/
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
* Original developer: bsag (http://www.rousette.org.uk/)
* Contributors: http://dev.rousette.org.uk/wiki/Tracks/Contributing/Contributors
* Contributors: http://getontracks.org/wiki/Tracks/Contributing/Contributors
* Version: 1.7
* Copyright: (cc) 2004-2008 rousette.org.uk.
* License: GNU GPL

View file

@ -1,6 +1,6 @@
1. Wiki
There are some pointers for setting up your Tracks copy for testing at http://dev.rousette.org.uk/wiki/Tracks/Testing
There are some pointers for setting up your Tracks copy for testing at http://www.rousette.org.uk/projects/wiki/Testing/
2. SQLITE3 FOR TESTING

1
doc/manual Submodule

@ -0,0 +1 @@
Subproject commit d3746adc39333f056f14969d40aa1f0072eb4eed

View file

@ -1,29 +0,0 @@
body {
width: 60%;
font-family: Helvetica, Arial, sans-serif;
font-size: 1em;
margin: 20px auto;
line-height: 1.5em;
}
a {
color: #9E2029;
text-decoration: none;
}
a:hover {
color: #DE8E30;
text-decoration: underline;
}
a:visited {
color: #DE8E30;
}
code {
color: #000;
background-color: #E3E3E3;
padding: 2px;
}
.footnote {font-size: 0.9em; vertical-align: super;}

View file

@ -1,281 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"
"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- Processed by MultiMarkdown -->
<meta name="Author" content="Tracks Development Team" />
<meta name="BaseHeaderLevel" content="2" />
<link type="text/css" rel="stylesheet" href="manual.css" />
<meta name="Copyright" content="2008 rousette.org.uk
This work is licensed under a Creative Commons License.
http://creativecommons.org/licenses/by-nc-sa/3.0/" />
<meta name="Date" content="2008-12-14" />
<meta name="Format" content="complete" />
<meta name="LaTeXXSLT" content="memoir-twosided-manual.xslt" />
<meta name="Revision" content="$Id: manual.markdown 2008-12-14 11:50:00Z bsag $" />
<title>Tracks 1.7 Manual</title>
<meta name="Version" content="1.7" />
<meta name="XMP" content="CCAttributionShareAlike" />
</head>
<body>
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
<h2 id="installingtracks1.7">Installing Tracks 1.7</h2>
<h3 id="introduction">Introduction</h3>
<p>Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it&#8217;s only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in <code>doc/CHANGELOG</code>. Full API documentation can be found at <code>doc/app/index.html</code>, once you have run <code>rake appdoc</code></p>
<p>There are two methods of downloading Tracks 1.7:</p>
<ol>
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
<li>If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):</li>
</ol>
<pre>
<code>
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
</code>
</pre>
<h3 id="requirements">Requirements</h3>
<p>The Tracks interface is accessed through a web browser, so you need to run a webserver to serve the Tracks pages up to you. This isn&#8217;t as daunting as it sounds, however: Tracks ships with a built-in web server called Mongrel which you can run on your own computer to serve the Tracks application locally. If you want to be able to access Tracks from any computer connected to the Internet, then you need to install Tracks on a publicly accessible server, and you will probably be better off using a more robust server such as <a href="http://www.apache.org/">Apache</a> or <a href="http://www.lighttpd.net/">Lighttpd</a> to serve the pages, particularly if it will be used by many people.</p>
<p>Tracks stores its data in a database, and you can either use SQLite3, MySQL or PostgreSQL. SQLite3 is the best choice for a single user (or a small number of users) on a local installation, while MySQL or PostgreSQL is better for multiple users on a remote installation.</p>
<h4 id="easyinstallationoptions">Easy installation options</h4>
<p>If you&#8217;d like to install Tracks on a local machine, try <a href="http://bitnami.org/stack/tracks">BitNami</a> &#8211; it runs on Windows, Mac OS X and Linux.</p>
<p>If you&#8217;d like an easy way to access Tracks from any internet-connected computer, sign up for a free account at <a href="http://www.morphexchange.com/">Morph eXchange</a>. Sign up for a free account, then choose &#8216;Subscriptions&#8217; to subscribe to the Tracks service.</p>
<h4 id="whatisincludedwiththetrackspackage">What is included with the Tracks package</h4>
<ol>
<li>Tracks itself</li>
<li>Rails 2.2.2 (installed in the <code>/vendor/rails</code> directory, so you do not need to install Rails yourself)</li>
<li>An empty SQLite3 database, set up with the correct database schema</li>
</ol>
<h4 id="whatyouneed">What you need to install</h4>
<p>If you don&#8217;t want to (or can&#8217;t) use one of the all in one installations, you&#8217;ll need to install a few things, depending on your platform and your needs.</p>
<ol>
<li><strong>Ruby</strong>. Version 1.8.6 is recommended, but it is also possible to use 1.8.5, 1.8.4 and 1.8.2. Note that 1.8.3 is not compatible. If you are running Mac OS X Leopard, you already have Ruby 1.8.6 installed by default, so you have nothing to do here. You can get the source to compile yourself <a href="http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.gz">here</a> for all platforms, or Windows users can use an easy <a href="http://rubyforge.org/frs/?group_id=167">installer</a>. If you&#8217;re using a version of Mac OS X earlier than 10.5.0, it is recommended that you use the <a href="http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx">instructions here</a> to install all the Rails dependencies, though you can skip the step to install Rails if you like.</li>
<li><strong>RubyGems</strong>. The gems needed by Rails to interact with the database have to be compiled on the platform on which they will be run, so we cannot include them with the Tracks package, unlike some other gems. So you will need to <a href="http://rubyforge.org/frs/?group_id=126">download</a> and install RubyGems (run <code>ruby setup.rb</code> after extracting the package). Note that once again, Mac OS X Leopard users get an easy life, because RubyGems and the SQLite3 gem is already installed. Once installed you can use RubyGems to install the gems you need for your database. If you are using SQLite3, run <code>sudo gem install sqlite3-ruby</code>, then select the appropriate package for your platform (version 1.2.1 recommended). You can use MySQL without installing a gem, but installing the gem can speed things up a bit: <code>sudo install gem mysql</code>. If you&#8217;re using Leopard, there are a few work-arounds necessary, which are explained on <a href="http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting#IcannotbuildrubymysqlonLeopardwithmysql.combinaries">Mac OS Forge</a>. The ruby-mysql bindings can sometimes be a bit troublesome to install, so to be honest, it&#8217;s probably not worth the bother unless you are trying to wring maximum speed out of your system. If you are using PostgreSQL, then you can install a postgres gem: <code>gem install postgres</code>.</li>
<li><strong>Database</strong>. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the <code>sqlite3-ruby</code> gem, as described in step 2, and the SQLite3 libraries and binary (see <a href="http://sqlite.org/download.html">sqlite.org</a> for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from <a href="http://dev.mysql.com/downloads/mysql/5.0.html">MySQL.com</a>. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.</li>
</ol>
<p>You can find several installation howtos for specific setups <a href="http://dev.rousette.org.uk/wiki/Tracks/Install">here</a>. They were contributed by various Tracks users.</p>
<h3 id="installation">Installation</h3>
<p>This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see <a href="#upgrading" title="Upgrading to Tracks 1.7">Upgrading to Tracks 1.7</a>.</p>
<ol>
<li><a href="#unzip_install" title="Unzip Tracks and install">Unzip tracks</a> and install in a directory</li>
<li>Decide on a <a href="#database_install" title="Decide on a database">database</a> to use
<ol><li>SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database</li>
<li>MySQL - create new MySQL db and grant all privileges </li></ol></li>
<li><a href="#config_install" title="Configure variables">Configure some variables</a></li>
<li>Populate the database with the <a href="#rake_install" title="Populate your database with the Tracks 1.7 schema">Tracks 1.7 schema</a></li>
<li><a href="#startserver_install" title="Start the server">Start the server</a></li>
<li><a href="#signup_install" title="Visit Tracks in a browser">Visit Tracks in a browser</a></li>
<li><a href="#customise_install" title="Customise Tracks">Customise Tracks</a></li>
</ol>
<h4 id="unzip_install">Unzip Tracks and install</h4>
<p>Unzip the package and move Tracks into the directory you want to run it from. For example, for Mac OS X users, <code>~/Sites</code> is a good choice.</p>
<h4 id="database_install">Decide on a database</h4>
<p>Before you go any further, you need to decide which database you will use. See the <a href="#whatyouneed" title="What you need to install">What you need to install</a> section for details on installing the required components for you choice of database.</p>
<ol>
<li><strong>SQLite3</strong>. All you need to do is make sure that you point Tracks to the included SQLite3 database in <code>/db</code> in the next step, <a href="#config_install" title="Configure variables">Configure variables</a>.</li>
<li><strong>MySQL</strong>. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:</li>
</ol>
<pre>
<code>
mysql -uroot -p
mysql> CREATE DATABASE tracks16;
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
</code>
</pre>
<h4 id="config_install">Configure variables</h4>
<ol>
<li>If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files <code>database.yml.tmpl</code> and <code>environment.yml.tmpl</code> and remove the <code>*.tmpl</code> extension from the duplicates. Similarly, duplicate <code>/log.tmpl</code> and remove the <code>*.tmpl</code> extension, then edit the files as described in steps 2 and 3.</li>
<li>Open the file <code>/config/database.yml</code> and edit the <code>production:</code> section with the details of your database. If you are using MySQL the <code>adapter:</code> line should read <code>adapter: mysql</code>, <code>host: localhost</code> (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: <code>adapter: sqlite3</code> and <code>database: db/tracks-15-blank.db</code>. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.</li>
<li>Open the file <code>/config/environment.rb</code>, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the <code>SALT = "change-me"</code> line (change the string &#8220;change-me&#8221; to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time_zone option and leave the timezone to use in your database to :utc</li>
<li>If you are using Windows, you may need to check the &#8216;shebang&#8217; lines (<code>#!/usr/bin/env ruby</code>) of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like <code>#c:/ruby/bin/ruby</code> to point to the Ruby binary on your system.</li>
</ol>
<h4 id="rake_install">Populate your database with the Tracks 1.7 schema</h4>
<p>Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:</p>
<p><code>rake db:migrate RAILS_ENV=production</code></p>
<p>This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).</p>
<h4 id="startserver_install">Start the server</h4>
<p>While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:</p>
<p><code>script/server -e production</code></p>
<p>If all goes well, you should see some text informing you that the Mongrel server is running: <code>** Mongrel available at 0.0.0.0:3000</code>. If you are already running other services on port 3000, you need to select a different port when running the server, using the <code>-p</code> option. You can stop the server again by the key combination Ctrl-C.</p>
<h4 id="signup_install">Visit Tracks in a browser</h4>
<p>Visit <code>http://0.0.0.0:3000/signup</code> in a browser (or whatever URL and port was reported when you started the server in the step above) and chose a user name and password for admin user. Once logged in as admin, you can add other (ordinary level) users. If you need to access Tracks from a mobile/cellular phone browser, visit <code>http://yourdomain.com/mobile/</code>. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.</p>
<h4 id="customise_install">Customise Tracks</h4>
<p>Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!</p>
<h2 id="upgrading">Upgrading to Tracks 1.7</h2>
<h3 id="upgrading_1.6">Upgrading from Tracks 1.6</h3>
<p>You will need to upgrade your <code>config/environment.rb</code> with the new content from <code>config/environment.rb.tmpl</code> included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.</p>
<ol>
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.7</a> in a new directory</li>
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.</li>
<li>Rebuild your environment.rb from the updated environment.rb.tmpl</li>
<li>Run <code>rake db:migrate RAILS_ENV=production</code> to <a href="#rake_upgrade" title="Update your old database to the new format">update your old database</a> to the new schema &#8211; you did back up your database didn&#8217;t you?</li>
<li>Run <code>script/server</code> inside your Tracks 1.7 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.7</a>.</li>
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
</ol>
<h3 id="upgrading_1.5">Upgrading from Tracks 1.5</h3>
<p>There are no changes to the database between 1.5 and 1.6, but you will need to upgrade your <code>config/environment.rb</code> with the new content from <code>config/environment.rb.tmpl</code> included in 1.6, as the format of this file has changed a great deal between 1.5 and 1.6.</p>
<ol>
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.6</a> in a new directory</li>
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
<li>Rebuild your environment.rb from the updated environment.rb.tmpl</li>
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
</ol>
<h3 id="upgrading_1043">Upgrading from Tracks 1.043</h3>
<p>This should be a relatively straightforward, and involves the following main steps:</p>
<ol>
<li><a href="#backup_upgrade" title="Backing up">Back up</a> your existing database and installation of Tracks</li>
<li><a href="#install_upgrade" title="Install Tracks 1.7">Install Tracks 1.6</a> in a new directory</li>
<li><a href="#config_upgrade" title="Copy over old configuration files">Copy over</a> a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory</li>
<li>Run <code>rake db:migrate RAILS_ENV=production</code> to <a href="#rake_upgrade" title="Update your old database to the new format">update your old database</a> to the new schema &#8211; you did back up your database didn&#8217;t you?</li>
<li>Run <code>script/server</code> inside your Tracks 1.6 directory to <a href="#startserver_upgrade" title="Start the server">start up Tracks 1.6</a>.</li>
<li>Once you are happy that everything is working well, <a href="#cleanup_upgrade" title="Clean up your old installation">delete your old Tracks directory</a>.</li>
</ol>
<h4 id="backup_upgrade">Backing up</h4>
<p>It&#8217;s very important that you <strong>back up your database</strong> before you start the upgrade process. It&#8217;s always possible for things to go wrong with the database update, and you don&#8217;t want to lose any data. If you are using SQLite3 and you are leaving your old Tracks directory in place, then you don&#8217;t need to do anything. However, there is no harm in taking extra precautions and copying your database from <code>/db</code> to a safe location as an extra backup, or making a dump of the schema and contents. You will never regret making too many backups! If you are using MySQL, make a SQL dump of your database, replacing the terms in square brackets with the correct information for your setup:</p>
<p><code>mysqldump -user [user name] -password=[password] [database name] &gt; [dump file]</code></p>
<p>Rename your old Tracks installation (e.g. to &#8216;tracks-old&#8217;) so that you can install Tracks 1.7 along side it.</p>
<h4 id="install_upgrade">Install Tracks 1.7</h4>
<p>There are two methods of downloading Tracks 1.7:</p>
<ol>
<li>(Recommended for most people) Download the <a href="http://www.rousette.org.uk/projects/files/tracks-current.zip">zipped package</a>, and unzip in your preferred location (e.g. <code>~/Sites</code> for Mac OS X users).</li>
<li>If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):</li>
3.
</ol>
<pre>
<code>
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
</code>
</pre>
<h4 id="config_upgrade">Copy over old configuration files</h4>
<p>There are a few files you need to copy over from your old installation. If you copy them over rather than moving them, you can still run your old version of Tracks if anything goes awry with the installation process.</p>
<ol>
<li>Copy <code>/config/database.yml</code> from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.</li>
<li>Duplicate <code>/config/environment.rb.tmpl</code> in the Tracks 1.7 directory, and rename the file to <code>environment.rb</code>. Open the file and alter the line <code>SALT = "change-me"</code> so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to <code>environment.rb</code> in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.</li>
<li>Copy your <code>/log</code> directory over from your old installation to the root of the new one, or just rename <code>/log.tmpl</code> to <code>log</code> to start afresh.</li>
<li>If you are using SQLite3, copy your database from <code>/db</code> in your old Tracks directory to the same location in the new one.</li>
<li>If you are using Windows, you may need to check the &#8216;shebang&#8217; lines (<code>#!/usr/bin/env ruby</code>)<a href="#fn:env" id="fnref:env" class="footnote">1</a> of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. Check the format of those lines in your old installation, and change the new ones as necessary.</li>
</ol>
<h4 id="rake_upgrade">Update your old database to the new format</h4>
<p>In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:</p>
<p><code>rake db:migrate RAILS_ENV=production</code></p>
<p>Watch the output carefully for errors, but it should report at the end of the process that everything worked OK. If you do get errors, you&#8217;ll have to fix them before you proceed any further. Running rake with the <code>--trace</code> option can help to track down the problem.</p>
<h4 id="startserver_upgrade">Start the server</h4>
<p>If you&#8217;re still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:</p>
<p><code>script/server -e production</code></p>
<p>Visit the URL indicated by the output (e.g. <code>** Mongrel available at 0.0.0.0:3000</code>
) in a browser, and with any luck, you should be able to log in and find all your actions as you left them! If you need to access Tracks from a mobile/cellular phone browser, visit <code>http://yourdomain.com/mobile/</code>. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.</p>
<h4 id="cleanup_upgrade">Clean up your old installation</h4>
<p>Once you&#8217;re certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.</p>
<h3 id="upgradingfromversionspriorto1.043">Upgrading from versions prior to 1.043</h3>
<p>The best option for versions prior to 1.043 is to follow the instructions below to upgrade to version 1.043, then use the instructions above to <a href="#upgrading_1043" title="Upgrading from Tracks 1.043">upgrade from version 1.043</a>.</p>
<ol>
<li>For safety, rename your current Tracks directory to &#8216;tracks-old&#8217; or something similar.</li>
<li>Before you do anything else, <strong>BACK UP YOUR DATABASE</strong> (tables and content) and keep the SQL dumps somewhere safe so that you can recreate the old database if necesary.</li>
<li>Download a copy of Tracks 1.043 and unzip alongside your &#8216;tracks-old&#8217; directory.</li>
<li>Open the file <code>config/environment.rb</code> and look at the last line which should read: <code>SALT = "change-me"</code>. Change the word change-me to something else of your choosing. This string will be used as a &#8216;salt&#8217; to encrypt your password and make it a bit more secure. Also look at the timezone setting at the bottom. You can leave it commented out if your server is in the same time zone as you, but you may need to adjust it if your server is in a different time zone.</li>
<li>In <code>database.yml</code> insert your old database name, user and password under the &#8216;development&#8217; section. If you are using SQLite3 rather than MySQL or PostgreSQL, you need only the database name, and to change the &#8216;adapter&#8217; line to &#8216;sqlite3&#8217;. You also need to copy (NOT MOVE!), your SQLite3 database from your tracks-old <code>db</code> directory to your new tracks <code>db</code> directory</li>
<li>Run the command <code>rake extract_fixtures</code> inside the Tracks directory. This will populate the <code>db/exported_fixtures</code> directory with <code>*.yml</code> files corresponding to the contexts, projects and todos table from the contents of your old database.</li>
<li>Open <code>db/exported_fixtures/todos.yml</code> and search for the lines starting <code>created:</code> and replace with <code>created_at:</code>. If you are using SQLite3, you also need to change the following: <code>done: "0"</code> with <code>done: "f"</code> and <code>done: "1"</code> with <code>done: "t"</code>. You need to replace the similar &#8216;done&#8217; lines in <code>projects.yml</code>, and in <code>contexts.yml</code> replace <code>hide: "0"</code> with <code>hide: "f"</code> and <code>hide: "1"</code> with <code>hide: "t"</code>.</li>
<li>Create a new MySQL database (named tracks1043, for example). In <code>database.yml</code> insert this new database name, user and password under the &#8216;development&#8217; and &#8216;production&#8217; sections. If you are using SQLite3, insert a new name for a database to hold your Tracks 1.043 data.</li>
<li>Run the command <code>rake db_schema_import</code> inside the Tracks directory. This should import the upgraded schema for 1.043 into your new database.</li>
<li>Run the command <code>rake load_exported_fixtures</code> which will import the contents of your old database from the fixtures files in <code>db/exported_fixtures</code>.</li>
<li>If you are using Windows, you may need to check the &#8216;shebang&#8217; lines (<code>#!/usr/bin/env ruby</code>)<a href="#fn:env" id="fnref:env" class="footnote">2</a> of the <code>/public/dispatch.*</code> files and all the files in the <code>/script</code> directory. They are set to <code>#!/usr/bin/env ruby</code> by default. Check the format of those lines in your old installation, and change the new ones as necessary.</li>
<li>Try starting up the server with <code>script/server</code> to make sure that all your data has migrated successfully. If all is well, follow the instructions above to <a href="#upgrading_1043" title="Upgrading from Tracks 1.043">upgrade from version 1.043</a> to Tracks 1.6. If you need to access Tracks from a mobile/cellular phone browser, visit <code>http://yourdomain.com/mobile/</code>. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.</li>
</ol>
<div class="footnotes">
<hr />
<ol>
<li id="fn:env"><p>The <code>env</code> binary helps to locate other binaries, regardless of their location. If you don&#8217;t have <code>env</code> installed, you&#8217;ll need to change this line to point to the location of your Ruby binary.<a href="#fnref:env" class="reversefootnote">&#160;&#8617;</a></p></li>
<li id="fn:env"><p>The <code>env</code> binary helps to locate other binaries, regardless of their location. If you don&#8217;t have <code>env</code> installed, you&#8217;ll need to change this line to point to the location of your Ruby binary.<a href="#fnref:env" class="reversefootnote">&#160;&#8617;</a></p></li>
</ol>
</div>
</body>
</html>

View file

@ -1,235 +0,0 @@
Title: Tracks 1.7 Manual
Author: Tracks Development Team
Date: 2008-12-14
Revision: $Id: manual.markdown 2008-12-14 11:50:00Z bsag $
Version: 1.7
Copyright: 2008 rousette.org.uk
This work is licensed under a Creative Commons License.
http://creativecommons.org/licenses/by-nc-sa/3.0/
XMP: CCAttributionShareAlike
Format: complete
Base Header Level: 2
LaTeX XSLT: memoir-twosided-manual.xslt
CSS: manual.css
<!-- The HTML file manual.html is generated from manual.markdown, so make edits to the *.markdown file -->
# Installing Tracks 1.7 #
## Introduction ##
Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in `doc/CHANGELOG`. Full API documentation can be found at `doc/app/index.html`, once you have run `rake appdoc`
There are two methods of downloading Tracks 1.7:
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
<pre>
<code>
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
</code>
</pre>
## Requirements ##
The Tracks interface is accessed through a web browser, so you need to run a webserver to serve the Tracks pages up to you. This isn't as daunting as it sounds, however: Tracks ships with a built-in web server called Mongrel which you can run on your own computer to serve the Tracks application locally. If you want to be able to access Tracks from any computer connected to the Internet, then you need to install Tracks on a publicly accessible server, and you will probably be better off using a more robust server such as [Apache](http://www.apache.org/) or [Lighttpd](http://www.lighttpd.net/) to serve the pages, particularly if it will be used by many people.
Tracks stores its data in a database, and you can either use SQLite3, MySQL or PostgreSQL. SQLite3 is the best choice for a single user (or a small number of users) on a local installation, while MySQL or PostgreSQL is better for multiple users on a remote installation.
### Easy installation options ###
If you'd like to install Tracks on a local machine, try [BitNami](http://bitnami.org/stack/tracks) -- it runs on Windows, Mac OS X and Linux.
If you'd like an easy way to access Tracks from any internet-connected computer, sign up for a free account at [Morph eXchange](http://www.morphexchange.com/). Sign up for a free account, then choose 'Subscriptions' to subscribe to the Tracks service.
### What is included with the Tracks package ###
1. Tracks itself
2. Rails 2.2.2 (installed in the `/vendor/rails` directory, so you do not need to install Rails yourself)
3. An empty SQLite3 database, set up with the correct database schema
### What you need to install [whatyouneed] ###
If you don't want to (or can't) use one of the all in one installations, you'll need to install a few things, depending on your platform and your needs.
1. **Ruby**. Version 1.8.6 is recommended, but it is also possible to use 1.8.5, 1.8.4 and 1.8.2. Note that 1.8.3 is not compatible. If you are running Mac OS X Leopard, you already have Ruby 1.8.6 installed by default, so you have nothing to do here. You can get the source to compile yourself [here](http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.gz) for all platforms, or Windows users can use an easy [installer](http://rubyforge.org/frs/?group_id=167). If you're using a version of Mac OS X earlier than 10.5.0, it is recommended that you use the [instructions here](http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx) to install all the Rails dependencies, though you can skip the step to install Rails if you like.
2. **RubyGems**. The gems needed by Rails to interact with the database have to be compiled on the platform on which they will be run, so we cannot include them with the Tracks package, unlike some other gems. So you will need to [download](http://rubyforge.org/frs/?group_id=126) and install RubyGems (run `ruby setup.rb` after extracting the package). Note that once again, Mac OS X Leopard users get an easy life, because RubyGems and the SQLite3 gem is already installed. Once installed you can use RubyGems to install the gems you need for your database. If you are using SQLite3, run `sudo gem install sqlite3-ruby`, then select the appropriate package for your platform (version 1.2.1 recommended). You can use MySQL without installing a gem, but installing the gem can speed things up a bit: `sudo install gem mysql`. If you're using Leopard, there are a few work-arounds necessary, which are explained on [Mac OS Forge](http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting#IcannotbuildrubymysqlonLeopardwithmysql.combinaries). The ruby-mysql bindings can sometimes be a bit troublesome to install, so to be honest, it's probably not worth the bother unless you are trying to wring maximum speed out of your system. If you are using PostgreSQL, then you can install a postgres gem: `gem install postgres`.
3. **Database**. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the `sqlite3-ruby` gem, as described in step 2, and the SQLite3 libraries and binary (see [sqlite.org](http://sqlite.org/download.html) for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from [MySQL.com](http://dev.mysql.com/downloads/mysql/5.0.html). The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
You can find several installation howtos for specific setups [here](http://dev.rousette.org.uk/wiki/Tracks/Install). They were contributed by various Tracks users.
## Installation ##
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see [Upgrading to Tracks 1.7][upgrading].
1. [Unzip tracks][unzip_install] and install in a directory
2. Decide on a [database][database_install] to use
1. SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database
2. MySQL - create new MySQL db and grant all privileges
3. [Configure some variables][config_install]
4. Populate the database with the [Tracks 1.7 schema][rake_install]
5. [Start the server][startserver_install]
6. [Visit Tracks in a browser][signup_install]
7. [Customise Tracks][customise_install]
### Unzip Tracks and install [unzip_install] ###
Unzip the package and move Tracks into the directory you want to run it from. For example, for Mac OS X users, `~/Sites` is a good choice.
### Decide on a database [database_install] ###
Before you go any further, you need to decide which database you will use. See the [What you need to install][whatyouneed] section for details on installing the required components for you choice of database.
1. **SQLite3**. All you need to do is make sure that you point Tracks to the included SQLite3 database in `/db` in the next step, [Configure variables][config_install].
2. **MySQL**. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:
<pre>
<code>
mysql -uroot -p
mysql> CREATE DATABASE tracks16;
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
</code>
</pre>
### Configure variables [config_install] ###
1. If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files `database.yml.tmpl` and `environment.yml.tmpl` and remove the `*.tmpl` extension from the duplicates. Similarly, duplicate `/log.tmpl` and remove the `*.tmpl` extension, then edit the files as described in steps 2 and 3.
2. Open the file `/config/database.yml` and edit the `production:` section with the details of your database. If you are using MySQL the `adapter:` line should read `adapter: mysql`, `host: localhost` (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: `adapter: sqlite3` and `database: db/tracks-15-blank.db`. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
3. Open the file `/config/environment.rb`, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the `SALT = "change-me"` line (change the string "change-me" to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time_zone option and leave the timezone to use in your database to :utc
4. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`) of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like `#c:/ruby/bin/ruby` to point to the Ruby binary on your system.
### Populate your database with the Tracks 1.7 schema [rake_install] ###
Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:
`rake db:migrate RAILS_ENV=production`
This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
### Start the server [startserver_install] ###
While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:
`script/server -e production`
If all goes well, you should see some text informing you that the Mongrel server is running: `** Mongrel available at 0.0.0.0:3000`. If you are already running other services on port 3000, you need to select a different port when running the server, using the `-p` option. You can stop the server again by the key combination Ctrl-C.
### Visit Tracks in a browser [signup_install] ###
Visit `http://0.0.0.0:3000/signup` in a browser (or whatever URL and port was reported when you started the server in the step above) and chose a user name and password for admin user. Once logged in as admin, you can add other (ordinary level) users. If you need to access Tracks from a mobile/cellular phone browser, visit `http://yourdomain.com/mobile/`. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
### Customise Tracks [customise_install] ###
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
# Upgrading to Tracks 1.7 [upgrading] #
## Upgrading from Tracks 1.6 [upgrading_1.6] ##
You will need to upgrade your `config/environment.rb` with the new content from `config/environment.rb.tmpl` included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.
1. [Back up][backup_upgrade] your existing database and installation of Tracks
2. [Install Tracks 1.7][install_upgrade] in a new directory
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.
4. Rebuild your environment.rb from the updated environment.rb.tmpl
5. Run `rake db:migrate RAILS_ENV=production` to [update your old database][rake_upgrade] to the new schema -- you did back up your database didn't you?
6. Run `script/server` inside your Tracks 1.7 directory to [start up Tracks 1.7][startserver_upgrade].
7. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
## Upgrading from Tracks 1.5 [upgrading_1.5] ##
There are no changes to the database between 1.5 and 1.6, but you will need to upgrade your `config/environment.rb` with the new content from `config/environment.rb.tmpl` included in 1.6, as the format of this file has changed a great deal between 1.5 and 1.6.
1. [Back up][backup_upgrade] your existing database and installation of Tracks
2. [Install Tracks 1.6][install_upgrade] in a new directory
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
4. Rebuild your environment.rb from the updated environment.rb.tmpl
5. Run `script/server` inside your Tracks 1.6 directory to [start up Tracks 1.6][startserver_upgrade].
6. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
## Upgrading from Tracks 1.043 [upgrading_1043] ##
This should be a relatively straightforward, and involves the following main steps:
1. [Back up][backup_upgrade] your existing database and installation of Tracks
2. [Install Tracks 1.6][install_upgrade] in a new directory
3. [Copy over][config_upgrade] a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
5. Run `rake db:migrate RAILS_ENV=production` to [update your old database][rake_upgrade] to the new schema -- you did back up your database didn't you?
6. Run `script/server` inside your Tracks 1.6 directory to [start up Tracks 1.6][startserver_upgrade].
7. Once you are happy that everything is working well, [delete your old Tracks directory][cleanup_upgrade].
### Backing up [backup_upgrade] ###
It's very important that you **back up your database** before you start the upgrade process. It's always possible for things to go wrong with the database update, and you don't want to lose any data. If you are using SQLite3 and you are leaving your old Tracks directory in place, then you don't need to do anything. However, there is no harm in taking extra precautions and copying your database from `/db` to a safe location as an extra backup, or making a dump of the schema and contents. You will never regret making too many backups! If you are using MySQL, make a SQL dump of your database, replacing the terms in square brackets with the correct information for your setup:
`mysqldump -user [user name] -password=[password] [database name] > [dump file]`
Rename your old Tracks installation (e.g. to 'tracks-old') so that you can install Tracks 1.7 along side it.
### Install Tracks 1.7 [install_upgrade] ###
There are two methods of downloading Tracks 1.7:
1. (Recommended for most people) Download the [zipped package](http://www.rousette.org.uk/projects/files/tracks-current.zip), and unzip in your preferred location (e.g. `~/Sites` for Mac OS X users).
2. If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
3.
<pre>
<code>
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
</code>
</pre>
### Copy over old configuration files [config_upgrade] ###
There are a few files you need to copy over from your old installation. If you copy them over rather than moving them, you can still run your old version of Tracks if anything goes awry with the installation process.
1. Copy `/config/database.yml` from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
2. Duplicate `/config/environment.rb.tmpl` in the Tracks 1.7 directory, and rename the file to `environment.rb`. Open the file and alter the line `SALT = "change-me"` so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to `environment.rb` in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.
3. Copy your `/log` directory over from your old installation to the root of the new one, or just rename `/log.tmpl` to `log` to start afresh.
4. If you are using SQLite3, copy your database from `/db` in your old Tracks directory to the same location in the new one.
5. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`)[^env] of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. Check the format of those lines in your old installation, and change the new ones as necessary.
### Update your old database to the new format [rake_upgrade] ###
In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:
`rake db:migrate RAILS_ENV=production`
Watch the output carefully for errors, but it should report at the end of the process that everything worked OK. If you do get errors, you'll have to fix them before you proceed any further. Running rake with the `--trace` option can help to track down the problem.
### Start the server [startserver_upgrade] ###
If you're still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:
`script/server -e production`
Visit the URL indicated by the output (e.g. `** Mongrel available at 0.0.0.0:3000`
) in a browser, and with any luck, you should be able to log in and find all your actions as you left them! If you need to access Tracks from a mobile/cellular phone browser, visit `http://yourdomain.com/mobile/`. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
### Clean up your old installation [cleanup_upgrade] ###
Once you're certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.
[^env]: The `env` binary helps to locate other binaries, regardless of their location. If you don't have `env` installed, you'll need to change this line to point to the location of your Ruby binary.
## Upgrading from versions prior to 1.043 ##
The best option for versions prior to 1.043 is to follow the instructions below to upgrade to version 1.043, then use the instructions above to [upgrade from version 1.043][upgrading_1043].
1. For safety, rename your current Tracks directory to 'tracks-old' or something similar.
2. Before you do anything else, **BACK UP YOUR DATABASE** (tables and content) and keep the SQL dumps somewhere safe so that you can recreate the old database if necesary.
3. Download a copy of Tracks 1.043 and unzip alongside your 'tracks-old' directory.
4. Open the file `config/environment.rb` and look at the last line which should read: `SALT = "change-me"`. Change the word change-me to something else of your choosing. This string will be used as a 'salt' to encrypt your password and make it a bit more secure. Also look at the timezone setting at the bottom. You can leave it commented out if your server is in the same time zone as you, but you may need to adjust it if your server is in a different time zone.
5. In `database.yml` insert your old database name, user and password under the 'development' section. If you are using SQLite3 rather than MySQL or PostgreSQL, you need only the database name, and to change the 'adapter' line to 'sqlite3'. You also need to copy (NOT MOVE!), your SQLite3 database from your tracks-old `db` directory to your new tracks `db` directory
6. Run the command `rake extract_fixtures` inside the Tracks directory. This will populate the `db/exported_fixtures` directory with `*.yml` files corresponding to the contexts, projects and todos table from the contents of your old database.
7. Open `db/exported_fixtures/todos.yml` and search for the lines starting `created:` and replace with `created_at:`. If you are using SQLite3, you also need to change the following: `done: "0"` with `done: "f"` and `done: "1"` with `done: "t"`. You need to replace the similar 'done' lines in `projects.yml`, and in `contexts.yml` replace `hide: "0"` with `hide: "f"` and `hide: "1"` with `hide: "t"`.
8. Create a new MySQL database (named tracks1043, for example). In `database.yml` insert this new database name, user and password under the 'development' and 'production' sections. If you are using SQLite3, insert a new name for a database to hold your Tracks 1.043 data.
9. Run the command `rake db_schema_import` inside the Tracks directory. This should import the upgraded schema for 1.043 into your new database.
10. Run the command `rake load_exported_fixtures` which will import the contents of your old database from the fixtures files in `db/exported_fixtures`.
11. If you are using Windows, you may need to check the 'shebang' lines (`#!/usr/bin/env ruby`)[^env] of the `/public/dispatch.*` files and all the files in the `/script` directory. They are set to `#!/usr/bin/env ruby` by default. Check the format of those lines in your old installation, and change the new ones as necessary.
12. Try starting up the server with `script/server` to make sure that all your data has migrated successfully. If all is well, follow the instructions above to [upgrade from version 1.043][upgrading_1043] to Tracks 1.6. If you need to access Tracks from a mobile/cellular phone browser, visit `http://yourdomain.com/mobile/`. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.

Binary file not shown.

View file

@ -1,668 +0,0 @@
\documentclass[10pt,twoside]{memoir}
\usepackage{layouts}[2001/04/29]
\usepackage{palatino}
\usepackage{color,calc}
\newsavebox{\ChpNumBox}
\definecolor{ChapBlue}{rgb}{0.00,0.65,0.65}
\makeatletter
\newcommand*{\thickhrulefill}{%
\leavevmode\leaders\hrule height 1\p@ \hfill \kern \z@}
\newcommand*\BuildChpNum[2]{%
\begin{tabular}[t]{@{}c@{}}
\makebox[0pt][c]{#1\strut} \\[.5ex]
\colorbox{ChapBlue}{%
\rule[-10em]{0pt}{0pt}%
\rule{1ex}{0pt}\color{black}#2\strut
\rule{1ex}{0pt}}%
\end{tabular}}
\makechapterstyle{BlueBox}{%
\renewcommand{\chapnamefont}{\large\scshape}
\renewcommand{\chapnumfont}{\Huge\bfseries}
\renewcommand{\chaptitlefont}{\raggedright\Huge\bfseries}
\setlength{\beforechapskip}{20pt}
\setlength{\midchapskip}{26pt}
\setlength{\afterchapskip}{40pt}
\renewcommand{\printchaptername}{}
\renewcommand{\chapternamenum}{}
\renewcommand{\printchapternum}{%
\sbox{\ChpNumBox}{%
\BuildChpNum{\chapnamefont\@chapapp}%
{\chapnumfont\thechapter}}}
\renewcommand{\printchapternonum}{%
\sbox{\ChpNumBox}{%
\BuildChpNum{\chapnamefont\vphantom{\@chapapp}}%
{\chapnumfont\hphantom{\thechapter}}}}
\renewcommand{\afterchapternum}{}
\renewcommand{\printchaptertitle}[1]{%
\usebox{\ChpNumBox}\hfill
\parbox[t]{\hsize-\wd\ChpNumBox-1em}{%
\vspace{\midchapskip}%
\thickhrulefill\par
\chaptitlefont ##1\par}}%
}
\chapterstyle{BlueBox}
\setsecheadstyle{\sffamily\bfseries\Large}
\setsubsecheadstyle{\sffamily\bfseries\normal}
\makepagestyle{myruledpagestyle}
\makeevenhead{myruledpagestyle}{\thepage}{}{\leftmark}
\makeoddhead{myruledpagestyle}{\rightmark}{}{\thepage}
\makeatletter
\makepsmarks{myruledpagestyle}{
\def\chaptermark##1{\markboth{%
\ifnum \value{secnumdepth} > -1
\if@mainmatter
\chaptername\ \thechapter\ --- %
\fi
\fi
##1}{}}
\def\sectionmark##1{\markright{%
\ifnum \value{secnumdepth} > 0
\thesection. \ %
\fi
##1}}
}
\makeatother
\makerunningwidth{myruledpagestyle}{1.1\textwidth}
\makeheadposition{myruledpagestyle}{flushright}{flushleft}{flushright}{flushleft}
\makeheadrule{myruledpagestyle}{1.1\textwidth}{\normalrulethickness}
\makeglossary
\makeindex
\def\mychapterstyle{BlueBox}
\def\mypagestyle{myruledpagestyle}
\def\revision{}
%%% need more space for ToC page numbers
\setpnumwidth{2.55em}
\setrmarg{3.55em}
%%% need more space for ToC section numbers
\cftsetindents{part}{0em}{3em}
\cftsetindents{chapter}{0em}{3em}
\cftsetindents{section}{3em}{3em}
\cftsetindents{subsection}{4.5em}{3.9em}
\cftsetindents{subsubsection}{8.4em}{4.8em}
\cftsetindents{paragraph}{10.7em}{5.7em}
\cftsetindents{subparagraph}{12.7em}{6.7em}
%%% need more space for LoF numbers
\cftsetindents{figure}{0em}{3.0em}
%%% and do the same for the LoT
\cftsetindents{table}{0em}{3.0em}
%%% set up the page layout
\settrimmedsize{\stockheight}{\stockwidth}{*} % Use entire page
\settrims{0pt}{0pt}
\setlrmarginsandblock{1.5in}{1.5in}{*}
\setulmarginsandblock{1.5in}{1.5in}{*}
\setmarginnotes{17pt}{51pt}{\onelineskip}
\setheadfoot{\onelineskip}{2\onelineskip}
\setheaderspaces{*}{2\onelineskip}{*}
\checkandfixthelayout
\usepackage{fancyvrb} % Allow \verbatim et al. in footnotes
\usepackage{graphicx} % To include graphics in pdf's (jpg, gif, png, etc)
\usepackage{booktabs} % Better tables
\usepackage{tabulary} % Support longer table cells
\usepackage[utf8]{inputenc} % For UTF-8 support
%\usepackage{xcolor} % Allow for color (annotations)
%\geometry{landscape} % Activate for rotated page geometry
%\usepackage[parfill]{parskip} % Activate to begin paragraphs with an empty
% line rather than an indent
\def\myauthor{Author} % In case these were not included in metadata
\def\mytitle{Title}
\def\mykeywords{}
\def\mybibliostyle{plain}
\def\bibliocommand{}
\VerbatimFootnotes
\def\myauthor{Tracks Development Team}
\def\baseheaderlevel{2}
\def\mycopyright{2008 rousette.org.uk \\ This work is licensed under a Creative Commons License. \\ http://creativecommons.org/licenses/by-nc-sa/3.0/}
\date{2008-12-14}
\def\format{complete}
\def\latexxslt{memoir-twosided-manual.xslt}
\def\revision{Revision: \$Id: manual.markdown 2008-12-14 11:50:00Z bsag \$}
\def\mytitle{Tracks 1.7 Manual}
\def\version{1.7}
\usepackage{xmpincl}
\includexmp{CCAttributionShareAlike}
%
% PDF Stuff
%
%\ifpdf % Removed for XeLaTeX compatibility
% \pdfoutput=1 % Removed for XeLaTeX compatibility
\usepackage[
plainpages=false,
pdfpagelabels,
pdftitle={\mytitle},
pagebackref,
pdfauthor={\myauthor},
pdfkeywords={\mykeywords},
colorlinks=true,
urlcolor=blue,
linkcolor=red
]{hyperref}
\usepackage{memhfixc}
%\fi % Removed for XeLaTeX compatibility
%
% Title Information
%
\ifx\subtitle\undefined
\else
\addtodef{\mytitle}{}{ \\ \subtitle}
\fi
\ifx\affiliation\undefined
\else
\addtodef{\myauthor}{}{ \\ \affiliation}
\fi
\ifx\address\undefined
\else
\addtodef{\myauthor}{}{ \\ \address}
\fi
\ifx\phone\undefined
\else
\addtodef{\myauthor}{}{ \\ \phone}
\fi
\ifx\email\undefined
\else
\addtodef{\myauthor}{}{ \\ \email}
\fi
\ifx\web\undefined
\else
\addtodef{\myauthor}{}{ \\ \web}
\fi
\title{\mytitle}
\author{\myauthor}
\maxsecnumdepth{subsection}
\setsecnumdepth{subsection}
\begin{document}
\chapterstyle{\mychapterstyle}
\pagestyle{\mypagestyle}
%
% Front Matter
%
\frontmatter
% Title Page
\maketitle
\clearpage
% Copyright Page
\vspace*{\fill}
\setlength{\parindent}{0pt}
\ifx\mycopyright\undefined
\else
\textcopyright{} \mycopyright
\fi
\revision
\begin{center}
\framebox{ \parbox[t]{1.5in}{\centering Formatted for \LaTeX \\
by MultiMarkdown}}
\end{center}
\setlength{\parindent}{1em}
\clearpage
% Table of Contents
\tableofcontents
%\listoffigures % activate to include a List of Figures
%\listoftables % activate to include a List of Tables
%
% Main Content
%
% Layout settings
\setlength{\parindent}{0pt}
\setlength{\parskip}{\baselineskip/2}
\mainmatter
\chapter{Installing Tracks 1.7}
\label{installingtracks1.7}
\section{Introduction}
\label{introduction}
Tracks 1.7 has been thoroughly beta tested by a large number of people, and should be fully stable for everyday use. However, once set up, Tracks will contain the majority of your plans for your work and personal life, so it's only sensible to make sure that you have frequent, reliable backups of your data. Full changenotes on the release can be found in \texttt{doc/CHANGELOG}. Full API documentation can be found at \texttt{doc/app/index.html}, once you have run \texttt{rake appdoc}
There are two methods of downloading Tracks 1.7:
\begin{enumerate}
\item (Recommended for most people) Download the \href{http://www.rousette.org.uk/projects/files/tracks-current.zip}{zipped package}, and unzip in your preferred location (e.g.\ \texttt{\ensuremath{\sim}/Sites} for Mac OS X users).
\item If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
\end{enumerate}
\begin{adjustwidth}{2.5em}{2.5em}
\begin{verbatim}
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
\end{verbatim}
\end{adjustwidth}
\section{Requirements}
\label{requirements}
The Tracks interface is accessed through a web browser, so you need to run a webserver to serve the Tracks pages up to you. This isn't as daunting as it sounds, however: Tracks ships with a built-in web server called Mongrel which you can run on your own computer to serve the Tracks application locally. If you want to be able to access Tracks from any computer connected to the Internet, then you need to install Tracks on a publicly accessible server, and you will probably be better off using a more robust server such as \href{http://www.apache.org/}{Apache} or \href{http://www.lighttpd.net/}{Lighttpd} to serve the pages, particularly if it will be used by many people.
Tracks stores its data in a database, and you can either use SQLite3, MySQL or PostgreSQL. SQLite3 is the best choice for a single user (or a small number of users) on a local installation, while MySQL or PostgreSQL is better for multiple users on a remote installation.
\subsection{Easy installation options}
\label{easyinstallationoptions}
If you'd like to install Tracks on a local machine, try \href{http://bitnami.org/stack/tracks}{BitNami} -- it runs on Windows, Mac OS X and Linux.
If you'd like an easy way to access Tracks from any internet-connected computer, sign up for a free account at \href{http://www.morphexchange.com/}{Morph eXchange}. Sign up for a free account, then choose `Subscriptions' to subscribe to the Tracks service.
\subsection{What is included with the Tracks package}
\label{whatisincludedwiththetrackspackage}
\begin{enumerate}
\item Tracks itself
\item Rails 2.2.2 (installed in the \texttt{/vendor/rails} directory, so you do not need to install Rails yourself)
\item An empty SQLite3 database, set up with the correct database schema
\end{enumerate}
\subsection{What you need to install}
\label{whatyouneed}
If you don't want to (or can't) use one of the all in one installations, you'll need to install a few things, depending on your platform and your needs.
\begin{enumerate}
\item \textbf{Ruby}. Version 1.8.6 is recommended, but it is also possible to use 1.8.5, 1.8.4 and 1.8.2. Note that 1.8.3 is not compatible. If you are running Mac OS X Leopard, you already have Ruby 1.8.6 installed by default, so you have nothing to do here. You can get the source to compile yourself \href{http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.gz}{here} for all platforms, or Windows users can use an easy \href{http://rubyforge.org/frs/?group_id=167}{installer}. If you're using a version of Mac OS X earlier than 10.5.0, it is recommended that you use the \href{http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx}{instructions here} to install all the Rails dependencies, though you can skip the step to install Rails if you like.
\item \textbf{RubyGems}. The gems needed by Rails to interact with the database have to be compiled on the platform on which they will be run, so we cannot include them with the Tracks package, unlike some other gems. So you will need to \href{http://rubyforge.org/frs/?group_id=126}{download} and install RubyGems (run \texttt{ruby setup.rb} after extracting the package). Note that once again, Mac OS X Leopard users get an easy life, because RubyGems and the SQLite3 gem is already installed. Once installed you can use RubyGems to install the gems you need for your database. If you are using SQLite3, run \texttt{sudo gem install sqlite3-ruby}, then select the appropriate package for your platform (version 1.2.1 recommended). You can use MySQL without installing a gem, but installing the gem can speed things up a bit: \texttt{sudo install gem mysql}. If you're using Leopard, there are a few work-arounds necessary, which are explained on \href{http://trac.macosforge.org/projects/ruby/wiki/Troubleshooting#IcannotbuildrubymysqlonLeopardwithmysql.combinaries}{Mac OS Forge}. The ruby-mysql bindings can sometimes be a bit troublesome to install, so to be honest, it's probably not worth the bother unless you are trying to wring maximum speed out of your system. If you are using PostgreSQL, then you can install a postgres gem: \texttt{gem install postgres}.
\item \textbf{Database}. The easiest option is to use SQLite3, as the database is included in the package. All you need then is the \texttt{sqlite3-ruby} gem, as described in step 2, and the SQLite3 libraries and binary (see \href{http://sqlite.org/download.html}{sqlite.org} for downloads and installation instructions). If you want to use MySQL, download and install a package for your platform from \href{http://dev.mysql.com/downloads/mysql/5.0.html}{MySQL.com}. The basic steps for Postgresql should be similar to those for MySQL, but they will not be discussed further here.
\end{enumerate}
You can find several installation howtos for specific setups \href{http://dev.rousette.org.uk/wiki/Tracks/Install}{here}. They were contributed by various Tracks users.
\section{Installation}
\label{installation}
This description is intended for people installing Tracks from scratch. If you would like to upgrade an existing installation, please see Upgrading to Tracks 1.7 (\autoref{upgrading}).
\begin{enumerate}
\item Unzip tracks (\autoref{unzip_install}) and install in a directory
\item Decide on a database (\autoref{database_install}) to use
\begin{enumerate}
\item SQLite3 - change database.yml to point to SQLite3 database. Make sure you add the complete path to the database
\item MySQL - create new MySQL db and grant all privileges
\end{enumerate}
\item Configure some variables (\autoref{config_install})
\item Populate the database with the Tracks 1.7 schema (\autoref{rake_install})
\item Start the server (\autoref{startserver_install})
\item Visit Tracks in a browser (\autoref{signup_install})
\item Customise Tracks (\autoref{customise_install})
\end{enumerate}
\subsection{Unzip Tracks and install}
\label{unzip_install}
Unzip the package and move Tracks into the directory you want to run it from. For example, for Mac OS X users, \texttt{\ensuremath{\sim}/Sites} is a good choice.
\subsection{Decide on a database}
\label{database_install}
Before you go any further, you need to decide which database you will use. See the What you need to install (\autoref{whatyouneed}) section for details on installing the required components for you choice of database.
\begin{enumerate}
\item \textbf{SQLite3}. All you need to do is make sure that you point Tracks to the included SQLite3 database in \texttt{/db} in the next step, Configure variables (\autoref{config_install}).
\item \textbf{MySQL}. Once you have MySQL installed, you need to create a database and database-user to use with Tracks 1.7. For this, you can use MySQL Administrator or go into a terminal and issue the following commands:
\end{enumerate}
\begin{adjustwidth}{2.5em}{2.5em}
\begin{verbatim}
mysql -uroot -p
mysql> CREATE DATABASE tracks16;
mysql> GRANT ALL PRIVILEGES ON tracks16.* TO yourmysqluser@localhost \
IDENTIFIED BY 'password-goes-here' WITH GRANT OPTION;
\end{verbatim}
\end{adjustwidth}
\subsection{Configure variables}
\label{config_install}
\begin{enumerate}
\item If you downloaded Tracks 1.7 via Subversion, you need to duplicate the files \texttt{database.yml.tmpl} and \texttt{environment.yml.tmpl} and remove the \texttt{*.tmpl} extension from the duplicates. Similarly, duplicate \texttt{/log.tmpl} and remove the \texttt{*.tmpl} extension, then edit the files as described in steps 2 and 3.
\item Open the file \texttt{/config/database.yml} and edit the \texttt{production:} section with the details of your database. If you are using MySQL the \texttt{adapter:} line should read \texttt{adapter: mysql}, \texttt{host: localhost} (in the majority of cases), and your username and password should match those you assigned when you created the database. If you are using SQLite3, you should have only two lines under the production section: \texttt{adapter: sqlite3} and \texttt{database: db/tracks-15-blank.db}. If you downloaded the zipped file, the database.yml file is already configured to use the provided SQLite3 file.
\item Open the file \texttt{/config/environment.rb}, and read through the settings to make sure that they suit your setup. In most cases, all you need to change is the \texttt{SALT = "change-me"} line (change the string ``change-me" to some other string of your choice), and the time zone setting. For the time zone setting, most people will only want to change the config.time\_zone option and leave the timezone to use in your database to :utc
\item If you are using Windows, you may need to check the `shebang' lines (\texttt{\#!/usr/bin/env ruby}) of the \texttt{/public/dispatch.*} files and all the files in the \texttt{/script} directory. They are set to \texttt{\#!/usr/bin/env ruby} by default. This should work for all *nix based setups (Linux or Mac OS X), but Windows users will probably have to change it to something like \texttt{\#c:/ruby/bin/ruby} to point to the Ruby binary on your system.
\end{enumerate}
\subsection{Populate your database with the Tracks 1.7 schema}
\label{rake_install}
Open a terminal and change into the root of your Tracks 1.7 directory. Enter the following command:
\texttt{rake db:migrate RAILS\_ENV=production}
This will update your database with the required schema for Tracks 1.7. If you are using SQLite3, it is not strictly necessary, because the SQLite3 database included with Tracks already has the schema included in it, but it should not do any harm to run the command (nothing will happen if it is up to date).
\subsection{Start the server}
\label{startserver_install}
While still in the Terminal inside the Tracks 1.7 root directory, issue the following command:
\texttt{script/server -e production}
If all goes well, you should see some text informing you that the Mongrel server is running: \texttt{** Mongrel available at 0.0.0.0:3000}. If you are already running other services on port 3000, you need to select a different port when running the server, using the \texttt{-p} option. You can stop the server again by the key combination Ctrl-C.
\subsection{Visit Tracks in a browser}
\label{signup_install}
Visit \texttt{http://0.0.0.0:3000/signup} in a browser (or whatever URL and port was reported when you started the server in the step above) and chose a user name and password for admin user. Once logged in as admin, you can add other (ordinary level) users. If you need to access Tracks from a mobile/cellular phone browser, visit \texttt{http://yourdomain.com/mobile/}. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
\subsection{Customise Tracks}
\label{customise_install}
Once logged in, add some Contexts and Projects, and then go ahead and add your actions. You might also want to visit the Preferences page to edit various settings to your liking. Have fun!
\chapter{Upgrading to Tracks 1.7}
\label{upgrading}
\section{Upgrading from Tracks 1.6}
\label{upgrading_1.6}
You will need to upgrade your \texttt{config/environment.rb} with the new content from \texttt{config/environment.rb.tmpl} included in 1.7, as the format of this file has changed a bit between 1.6 and 1.7. Also, there were some database changes in Tracks 1.7, so you need to migrate to them.
\begin{enumerate}
\item Back up (\autoref{backup_upgrade}) your existing database and installation of Tracks
\item Install Tracks 1.7 (\autoref{install_upgrade}) in a new directory
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.6 directory. If using SQLite3, copy the old database into the new Tracks 1.7 directory.
\item Rebuild your environment.rb from the updated environment.rb.tmpl
\item Run \texttt{rake db:migrate RAILS\_ENV=production} to update your old database (\autoref{rake_upgrade}) to the new schema -- you did back up your database didn't you?
\item Run \texttt{script/server} inside your Tracks 1.7 directory to start up Tracks 1.7 (\autoref{startserver_upgrade}).
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
\end{enumerate}
\section{Upgrading from Tracks 1.5}
\label{upgrading_1.5}
There are no changes to the database between 1.5 and 1.6, but you will need to upgrade your \texttt{config/environment.rb} with the new content from \texttt{config/environment.rb.tmpl} included in 1.6, as the format of this file has changed a great deal between 1.5 and 1.6.
\begin{enumerate}
\item Back up (\autoref{backup_upgrade}) your existing database and installation of Tracks
\item Install Tracks 1.6 (\autoref{install_upgrade}) in a new directory
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
\item Rebuild your environment.rb from the updated environment.rb.tmpl
\item Run \texttt{script/server} inside your Tracks 1.6 directory to start up Tracks 1.6 (\autoref{startserver_upgrade}).
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
\end{enumerate}
\section{Upgrading from Tracks 1.043}
\label{upgrading_1043}
This should be a relatively straightforward, and involves the following main steps:
\begin{enumerate}
\item Back up (\autoref{backup_upgrade}) your existing database and installation of Tracks
\item Install Tracks 1.6 (\autoref{install_upgrade}) in a new directory
\item Copy over (\autoref{config_upgrade}) a few configuration files from your Tracks 1.043 directory. If using SQLite3, copy the old database into the new Tracks 1.6 directory
\item Run \texttt{rake db:migrate RAILS\_ENV=production} to update your old database (\autoref{rake_upgrade}) to the new schema -- you did back up your database didn't you?
\item Run \texttt{script/server} inside your Tracks 1.6 directory to start up Tracks 1.6 (\autoref{startserver_upgrade}).
\item Once you are happy that everything is working well, delete your old Tracks directory (\autoref{cleanup_upgrade}).
\end{enumerate}
\subsection{Backing up}
\label{backup_upgrade}
It's very important that you \textbf{back up your database} before you start the upgrade process. It's always possible for things to go wrong with the database update, and you don't want to lose any data. If you are using SQLite3 and you are leaving your old Tracks directory in place, then you don't need to do anything. However, there is no harm in taking extra precautions and copying your database from \texttt{/db} to a safe location as an extra backup, or making a dump of the schema and contents. You will never regret making too many backups! If you are using MySQL, make a SQL dump of your database, replacing the terms in square brackets with the correct information for your setup:
\texttt{mysqldump ---user [user name] ---password=[password] [database name] $>$ [dump file]}
Rename your old Tracks installation (e.g.\ to `tracks-old') so that you can install Tracks 1.7 along side it.
\subsection{Install Tracks 1.7}
\label{install_upgrade}
There are two methods of downloading Tracks 1.7:
\begin{enumerate}
\item (Recommended for most people) Download the \href{http://www.rousette.org.uk/projects/files/tracks-current.zip}{zipped package}, and unzip in your preferred location (e.g.\ \texttt{\ensuremath{\sim}/Sites} for Mac OS X users).
\item If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
\end{enumerate}
\begin{adjustwidth}{2.5em}{2.5em}
\begin{verbatim}
cd ~/Sites
git clone git://github.com/bsag/tracks.git
cd tracks
\end{verbatim}
\end{adjustwidth}
\subsection{Copy over old configuration files}
\label{config_upgrade}
There are a few files you need to copy over from your old installation. If you copy them over rather than moving them, you can still run your old version of Tracks if anything goes awry with the installation process.
\begin{enumerate}
\item Copy \texttt{/config/database.yml} from your old Tracks directory to the same location in the new one. Double check that the information there is still correct.
\item Duplicate \texttt{/config/environment.rb.tmpl} in the Tracks 1.7 directory, and rename the file to \texttt{environment.rb}. Open the file and alter the line \texttt{SALT = "change-me"} so that it matches what you had in this file in your old installation. You may also want to change the time zone setting as appropriate for your location. If you have made any other customisations to \texttt{environment.rb} in the past, copy those over, but the contents of the file have slightly changed since 1.5, so check it carefully.
\item Copy your \texttt{/log} directory over from your old installation to the root of the new one, or just rename \texttt{/log.tmpl} to \texttt{log} to start afresh.
\item If you are using SQLite3, copy your database from \texttt{/db} in your old Tracks directory to the same location in the new one.
\item If you are using Windows, you may need to check the `shebang' lines (\texttt{\#!/usr/bin/env ruby})\footnote{The \texttt{env} binary helps to locate other binaries, regardless of their location. If you don't have \texttt{env} installed, you'll need to change this line to point to the location of your Ruby binary.The \texttt{env} binary helps to locate other binaries, regardless of their location. If you don't have \texttt{env} installed, you'll need to change this line to point to the location of your Ruby binary.} of the \texttt{/public/dispatch.*} files and all the files in the \texttt{/script} directory. They are set to \texttt{\#!/usr/bin/env ruby} by default. Check the format of those lines in your old installation, and change the new ones as necessary.
\end{enumerate}
\subsection{Update your old database to the new format}
\label{rake_upgrade}
In a terminal, change directories so that you are inside the Tracks 1.7 directory. Then issue the command to update your Tracks 1.6 database to the format required for Tracks 1.7:
\texttt{rake db:migrate RAILS\_ENV=production}
Watch the output carefully for errors, but it should report at the end of the process that everything worked OK. If you do get errors, you'll have to fix them before you proceed any further. Running rake with the \texttt{--trace} option can help to track down the problem.
\subsection{Start the server}
\label{startserver_upgrade}
If you're still in the Tracks 1.7 root directory in a terminal, enter the following command to start up Tracks in production mode:
\texttt{script/server -e production}
Visit the URL indicated by the output (e.g.\ \texttt{** Mongrel available at 0.0.0.0:3000}
) in a browser, and with any luck, you should be able to log in and find all your actions as you left them! If you need to access Tracks from a mobile/cellular phone browser, visit \texttt{http://yourdomain.com/mobile/}. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
\subsection{Clean up your old installation}
\label{cleanup_upgrade}
Once you're certain that your new Tracks 1.7 installation is working perfectly, you can delete your old Tracks directory.
\section{Upgrading from versions prior to 1.043}
\label{upgradingfromversionspriorto1.043}
The best option for versions prior to 1.043 is to follow the instructions below to upgrade to version 1.043, then use the instructions above to upgrade from version 1.043 (\autoref{upgrading_1043}).
\begin{enumerate}
\item For safety, rename your current Tracks directory to `tracks-old' or something similar.
\item Before you do anything else, \textbf{BACK UP YOUR DATABASE} (tables and content) and keep the SQL dumps somewhere safe so that you can recreate the old database if necesary.
\item Download a copy of Tracks 1.043 and unzip alongside your `tracks-old' directory.
\item Open the file \texttt{config/environment.rb} and look at the last line which should read: \texttt{SALT = "change-me"}. Change the word change-me to something else of your choosing. This string will be used as a `salt' to encrypt your password and make it a bit more secure. Also look at the timezone setting at the bottom. You can leave it commented out if your server is in the same time zone as you, but you may need to adjust it if your server is in a different time zone.
\item In \texttt{database.yml} insert your old database name, user and password under the `development' section. If you are using SQLite3 rather than MySQL or PostgreSQL, you need only the database name, and to change the `adapter' line to `sqlite3'. You also need to copy (NOT MOVE!), your SQLite3 database from your tracks-old \texttt{db} directory to your new tracks \texttt{db} directory
\item Run the command \texttt{rake extract\_fixtures} inside the Tracks directory. This will populate the \texttt{db/exported\_fixtures} directory with \texttt{*.yml} files corresponding to the contexts, projects and todos table from the contents of your old database.
\item Open \texttt{db/exported\_fixtures/todos.yml} and search for the lines starting \texttt{created:} and replace with \texttt{created\_at:}. If you are using SQLite3, you also need to change the following: \texttt{done: "0"} with \texttt{done: "f"} and \texttt{done: "1"} with \texttt{done: "t"}. You need to replace the similar `done' lines in \texttt{projects.yml}, and in \texttt{contexts.yml} replace \texttt{hide: "0"} with \texttt{hide: "f"} and \texttt{hide: "1"} with \texttt{hide: "t"}.
\item Create a new MySQL database (named tracks1043, for example). In \texttt{database.yml} insert this new database name, user and password under the `development' and `production' sections. If you are using SQLite3, insert a new name for a database to hold your Tracks 1.043 data.
\item Run the command \texttt{rake db\_schema\_import} inside the Tracks directory. This should import the upgraded schema for 1.043 into your new database.
\item Run the command \texttt{rake load\_exported\_fixtures} which will import the contents of your old database from the fixtures files in \texttt{db/exported\_fixtures}.
\item If you are using Windows, you may need to check the `shebang' lines (\texttt{\#!/usr/bin/env ruby})\footnote{The \texttt{env} binary helps to locate other binaries, regardless of their location. If you don't have \texttt{env} installed, you'll need to change this line to point to the location of your Ruby binary.The \texttt{env} binary helps to locate other binaries, regardless of their location. If you don't have \texttt{env} installed, you'll need to change this line to point to the location of your Ruby binary.} of the \texttt{/public/dispatch.*} files and all the files in the \texttt{/script} directory. They are set to \texttt{\#!/usr/bin/env ruby} by default. Check the format of those lines in your old installation, and change the new ones as necessary.
\item Try starting up the server with \texttt{script/server} to make sure that all your data has migrated successfully. If all is well, follow the instructions above to upgrade from version 1.043 (\autoref{upgrading_1043}) to Tracks 1.6. If you need to access Tracks from a mobile/cellular phone browser, visit \texttt{http://yourdomain.com/mobile/}. This mobile version is a special, lightweight version of Tracks, designed to use on a mobile browser.
\end{enumerate}
%
% Back Matter
%
\backmatter
%\appendixpage
% Bibliography
\bibliographystyle{\mybibliostyle}
\bibliocommand
% Glossary
\printglossary
% Index
\printindex
\end{document}

View file

@ -1,134 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- XHTML-to-Memoir converter by Fletcher Penney
specifically designed for use with MultiMarkdown created XHTML
Uses the LaTeX memoir class for output with the twoside option
MultiMarkdown Version 2.0.b3
$Id: memoir-twosided.xslt 400 2007-05-26 18:42:49Z fletcher $
-->
<!--
# Copyright (C) 2005-2007 Fletcher T. Penney <fletcher@fletcherpenney.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.
# 59 Temple Place, Suite 330
# Boston, MA 02111-1307 USA
-->
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml"
version="1.0">
<xsl:import href="memoir.xslt"/>
<xsl:output method='text' encoding='utf-8'/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:apply-templates select="html:html/html:head"/>
<xsl:apply-templates select="html:html/html:body"/>
<xsl:call-template name="latex-footer"/>
</xsl:template>
<xsl:template name="latex-document-class">
<xsl:text>\documentclass[10pt,twoside]{memoir}
\usepackage{layouts}[2001/04/29]
\usepackage{palatino}
\usepackage{color,calc}
\newsavebox{\ChpNumBox}
\definecolor{ChapBlue}{rgb}{0.00,0.65,0.65}
\makeatletter
\newcommand*{\thickhrulefill}{%
\leavevmode\leaders\hrule height 1\p@ \hfill \kern \z@}
\newcommand*\BuildChpNum[2]{%
\begin{tabular}[t]{@{}c@{}}
\makebox[0pt][c]{#1\strut} \\[.5ex]
\colorbox{ChapBlue}{%
\rule[-10em]{0pt}{0pt}%
\rule{1ex}{0pt}\color{black}#2\strut
\rule{1ex}{0pt}}%
\end{tabular}}
\makechapterstyle{BlueBox}{%
\renewcommand{\chapnamefont}{\large\scshape}
\renewcommand{\chapnumfont}{\Huge\bfseries}
\renewcommand{\chaptitlefont}{\raggedright\Huge\bfseries}
\setlength{\beforechapskip}{20pt}
\setlength{\midchapskip}{26pt}
\setlength{\afterchapskip}{40pt}
\renewcommand{\printchaptername}{}
\renewcommand{\chapternamenum}{}
\renewcommand{\printchapternum}{%
\sbox{\ChpNumBox}{%
\BuildChpNum{\chapnamefont\@chapapp}%
{\chapnumfont\thechapter}}}
\renewcommand{\printchapternonum}{%
\sbox{\ChpNumBox}{%
\BuildChpNum{\chapnamefont\vphantom{\@chapapp}}%
{\chapnumfont\hphantom{\thechapter}}}}
\renewcommand{\afterchapternum}{}
\renewcommand{\printchaptertitle}[1]{%
\usebox{\ChpNumBox}\hfill
\parbox[t]{\hsize-\wd\ChpNumBox-1em}{%
\vspace{\midchapskip}%
\thickhrulefill\par
\chaptitlefont ##1\par}}%
}
\chapterstyle{BlueBox}
\setsecheadstyle{\sffamily\bfseries\Large}
\setsubsecheadstyle{\sffamily\bfseries\normal}
\makepagestyle{myruledpagestyle}
\makeevenhead{myruledpagestyle}{\thepage}{}{\leftmark}
\makeoddhead{myruledpagestyle}{\rightmark}{}{\thepage}
\makeatletter
\makepsmarks{myruledpagestyle}{
\def\chaptermark##1{\markboth{%
\ifnum \value{secnumdepth} > -1
\if@mainmatter
\chaptername\ \thechapter\ --- %
\fi
\fi
##1}{}}
\def\sectionmark##1{\markright{%
\ifnum \value{secnumdepth} > 0
\thesection. \ %
\fi
##1}}
}
\makeatother
\makerunningwidth{myruledpagestyle}{1.1\textwidth}
\makeheadposition{myruledpagestyle}{flushright}{flushleft}{flushright}{flushleft}
\makeheadrule{myruledpagestyle}{1.1\textwidth}{\normalrulethickness}
\makeglossary
\makeindex
\def\mychapterstyle{BlueBox}
\def\mypagestyle{myruledpagestyle}
\def\revision{}
</xsl:text>
</xsl:template>
</xsl:stylesheet>

27
lib/tasks/database.rake Normal file
View file

@ -0,0 +1,27 @@
require 'rake'
namespace :db do
desc "Dump the current SQLite3 or MySQL database to a sql file"
task :dump_sql do
load 'config/environment.rb'
abcs = ActiveRecord::Base.configurations
case abcs[RAILS_ENV]["adapter"]
when 'mysql'
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
if abcs[RAILS_ENV]["password"].blank?
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} #{abcs[RAILS_ENV]["database"]}`
else
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} -p#{abcs[RAILS_ENV]["password"]} #{abcs[RAILS_ENV]["database"]}`
end
end
when 'sqlite3'
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
f << `sqlite3 #{abcs[RAILS_ENV]["database"]} .dump`
end
else
raise "Task not supported by '#{abcs[RAILS_ENV]['adapter']}'"
end
end
end

View file

@ -170,7 +170,7 @@ describe Todo do
it 'is starred if tag is "starred"' do
todo = create_todo
todo.should_not be_starred
todo.add_tag('starred')
todo._add_tags('starred')
todo.reload
todo.should be_starred
end
@ -178,7 +178,7 @@ describe Todo do
describe 'when toggling star flag' do
it 'toggles to not starred when starred' do
todo = create_todo
todo.add_tag('starred')
todo._add_tags('starred')
todo.should be_starred
todo.toggle_star!
todo.reload

View file

@ -68,14 +68,6 @@ describe User do
with_dependent(:delete_all)
end
it 'has many taggings' do
User.should have_many(:taggings)
end
it 'has many tags through taggings' do
User.should have_many(:tags).through(:taggings).with_select('DISTINCT tags.*')
end
it 'has one preference' do
User.should have_one(:preference)
end

View file

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '/../../spec_helper'
describe "/login.m" do
it "should render without an error" do
render :action => 'login/login_mobile.html.erb', :layout => 'mobile.m.erb'
response.should_not have_tag("div#Application-Trace")
end
end

View file

@ -4,14 +4,12 @@ foo_bar1:
tag_id: 1
taggable_id: 1 # Call Bill Gates
taggable_type: Todo
user_id: 1
foo_bar2:
id: 2
tag_id: 2
taggable_id: 1 # Call Bill Gates
taggable_type: Todo
user_id: 1
# Todo 2 should be tagged with foo
foo1:
@ -19,11 +17,9 @@ foo1:
tag_id: 1
taggable_id: 2 # Call dinosaur exterminator
taggable_type: Todo
user_id: 1
foo2:
id: 4
tag_id: 1
taggable_id: 3 # Buy milk - completed
taggable_type: Todo
user_id: 1
taggable_type: Todo

View file

@ -1,259 +1,259 @@
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/todo_container_controller_test_base'
require 'projects_controller'
# Re-raise errors caught by the controller.
class ProjectsController; def rescue_action(e) raise e end; end
class ProjectsControllerTest < TodoContainerControllerTestBase
fixtures :users, :todos, :preferences, :projects, :contexts
def setup
perform_setup(Project, ProjectsController)
end
def test_projects_list
login_as :admin_user
get :index
end
def test_show_exposes_deferred_todos
p = projects(:timemachine)
login_as :admin_user
get :show, :id => p.to_param
assert_not_nil assigns['deferred']
assert_equal 1, assigns['deferred'].size
t = p.not_done_todos[0]
t.show_from = 1.days.from_now.utc
t.save!
get :show, :id => p.to_param
assert_equal 2, assigns['deferred'].size
end
def test_show_exposes_next_project_in_same_state
login_as :admin_user
get :show, :id => projects(:timemachine).to_param
assert_equal(projects(:moremoney), assigns['next_project'])
end
def test_show_exposes_previous_project_in_same_state
login_as :admin_user
get :show, :id => projects(:moremoney).to_param
assert_equal(projects(:timemachine), assigns['previous_project'])
end
def test_create_project_via_ajax_increments_number_of_projects
assert_ajax_create_increments_count 'My New Project'
end
def test_create_project_with_ajax_success_rjs
ajax_create 'My New Project'
assert_rjs :insert_html, :bottom, "list-active-projects"
assert_rjs :sortable, 'list-active-projects', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-active-projects'), :url => order_projects_path }
# not yet sure how to write the following properly...
assert_rjs :call, "Form.reset", "project-form"
assert_rjs :call, "Form.focusFirstElement", "project-form"
end
def test_create_project_and_go_to_project_page
num_projects = Project.count
xhr :post, :create, { :project => {:name => 'Immediate Project Planning Required'}, :go_to_project => 1}
assert_js_redirected_to %r{/?projects/\d+}
assert_equal num_projects + 1, Project.count
end
def test_create_with_comma_in_name_does_not_increment_number_of_projects
assert_ajax_create_does_not_increment_count 'foo,bar'
end
def test_create_with_comma_in_name_fails_with_rjs
ajax_create 'foo,bar'
assert_rjs :show, 'status'
# Not working with Rails 2.0 upgrade
# assert_rjs :update, 'status', "<div class=\"ErrorExplanation\" id=\"ErrorExplanation\"><h2>1 error prohibited this record from being saved</h2><p>There were problems with the following fields:</p><ul>Name cannot contain the comma (',') character</ul></div>"
end
def test_todo_state_is_project_hidden_after_hiding_project
p = projects(:timemachine)
todos = p.todos.find_in_state(:all, :active)
login_as(:admin_user)
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
todos.each do |t|
assert_equal :project_hidden, t.reload().current_state
end
assert p.reload().hidden?
end
def test_not_done_counts_after_hiding_and_unhiding_project
p = projects(:timemachine)
todos = p.todos.find_in_state(:all, :active)
login_as(:admin_user)
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"active"}
todos.each do |t|
assert_equal :active, t.reload().current_state
end
assert p.reload().active?
end
def test_rss_feed_content
login_as(:admin_user)
get :index, { :format => "rss" }
assert_equal 'application/rss+xml', @response.content_type
#puts @response.body
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Tracks Projects'
assert_select '>description', "Lists all the projects for #{users(:admin_user).display_name}"
assert_select 'language', 'en-us'
assert_select 'ttl', '40'
end
assert_select 'item', 3 do
assert_select 'title', /.+/
assert_select 'description' do
assert_select_encoded do
assert_select 'p', /^\d+&nbsp;actions\. Project is (active|hidden|completed)\.$/
end
end
%w(guid link).each do |node|
assert_select node, /http:\/\/test.host\/projects\/.+/
end
assert_select 'pubDate', projects(:timemachine).updated_at.to_s(:rfc822)
end
end
end
def test_rss_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "rss" }
assert_response 401
end
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "rss", :token => 'foo' }
assert_response 401
end
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "rss", :token => users(:admin_user).token }
assert_response :ok
end
def test_atom_feed_content
login_as :admin_user
get :index, { :format => "atom" }
assert_equal 'application/atom+xml', @response.content_type
#puts @response.body
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
assert_select '>title', 'Tracks Projects'
assert_select '>subtitle', "Lists all the projects for #{users(:admin_user).display_name}"
assert_select 'entry', 3 do
assert_select 'title', /.+/
assert_select 'content[type="html"]' do
assert_select_encoded do
assert_select 'p', /\d+&nbsp;actions. Project is (active|hidden|completed)./
end
end
assert_select 'published', /(#{Regexp.escape(projects(:timemachine).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
end
end
end
def test_atom_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "atom" }
assert_response 401
end
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "atom", :token => 'foo' }
assert_response 401
end
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "atom", :token => users(:admin_user).token }
assert_response :ok
end
def test_text_feed_content
login_as :admin_user
get :index, { :format => "txt" }
assert_equal 'text/plain', @response.content_type
assert !(/&nbsp;/.match(@response.body))
#puts @response.body
end
def test_text_feed_content_for_projects_with_no_actions
login_as :admin_user
p = projects(:timemachine)
p.todos.each { |t| t.destroy }
get :index, { :format => "txt", :only_active_with_no_next_actions => true }
assert (/^\s*BUILD A WORKING TIME MACHINE\s+0 actions. Project is active.\s*$/.match(@response.body))
assert !(/[1-9] actions/.match(@response.body))
end
def test_text_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "txt" }
assert_response 401
end
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "txt", :token => 'foo' }
assert_response 401
end
def test_text_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "txt", :token => users(:admin_user).token }
assert_response :ok
end
def test_actionize_sorts_active_projects_by_number_of_tasks
login_as :admin_user
u = users(:admin_user)
post :actionize, :state => "active", :format => 'js'
assert_equal 1, projects(:gardenclean).position
assert_equal 2, projects(:moremoney).position
assert_equal 3, projects(:timemachine).position
end
def test_alphabetize_sorts_active_projects_alphabetically
login_as :admin_user
u = users(:admin_user)
post :alphabetize, :state => "active", :format => 'js'
assert_equal 1, projects(:timemachine).position
assert_equal 2, projects(:gardenclean).position
assert_equal 3, projects(:moremoney).position
end
def test_alphabetize_assigns_state
login_as :admin_user
post :alphabetize, :state => "active", :format => 'js'
assert_equal "active", assigns['state']
end
def test_alphabetize_assigns_projects
login_as :admin_user
post :alphabetize, :state => "active", :format => 'js'
exposed_projects = assigns['projects']
assert_equal 3, exposed_projects.length
assert_equal projects(:timemachine), exposed_projects[0]
assert_equal projects(:gardenclean), exposed_projects[1]
assert_equal projects(:moremoney), exposed_projects[2]
end
def protect_against_forgery?
false
end
end
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/todo_container_controller_test_base'
require 'projects_controller'
# Re-raise errors caught by the controller.
class ProjectsController; def rescue_action(e) raise e end; end
class ProjectsControllerTest < TodoContainerControllerTestBase
fixtures :users, :todos, :preferences, :projects, :contexts
def setup
perform_setup(Project, ProjectsController)
end
def test_projects_list
login_as :admin_user
get :index
end
def test_show_exposes_deferred_todos
p = projects(:timemachine)
login_as :admin_user
get :show, :id => p.to_param
assert_not_nil assigns['deferred']
assert_equal 1, assigns['deferred'].size
t = p.not_done_todos[0]
t.show_from = 1.days.from_now.utc
t.save!
get :show, :id => p.to_param
assert_equal 2, assigns['deferred'].size
end
def test_show_exposes_next_project_in_same_state
login_as :admin_user
get :show, :id => projects(:timemachine).to_param
assert_equal(projects(:moremoney), assigns['next_project'])
end
def test_show_exposes_previous_project_in_same_state
login_as :admin_user
get :show, :id => projects(:moremoney).to_param
assert_equal(projects(:timemachine), assigns['previous_project'])
end
def test_create_project_via_ajax_increments_number_of_projects
assert_ajax_create_increments_count 'My New Project'
end
def test_create_project_with_ajax_success_rjs
ajax_create 'My New Project'
assert_rjs :insert_html, :bottom, "list-active-projects"
assert_rjs :sortable, 'list-active-projects', { :tag => 'div', :handle => 'handle', :complete => visual_effect(:highlight, 'list-active-projects'), :url => order_projects_path }
# not yet sure how to write the following properly...
assert_rjs :call, "Form.reset", "project-form"
assert_rjs :call, "Form.focusFirstElement", "project-form"
end
def test_create_project_and_go_to_project_page
num_projects = Project.count
xhr :post, :create, { :project => {:name => 'Immediate Project Planning Required'}, :go_to_project => 1}
assert_js_redirected_to %r{/?projects/\d+}
assert_equal num_projects + 1, Project.count
end
def test_create_with_comma_in_name_does_not_increment_number_of_projects
assert_ajax_create_does_not_increment_count 'foo,bar'
end
def test_create_with_comma_in_name_fails_with_rjs
ajax_create 'foo,bar'
assert_rjs :show, 'status'
# Not working with Rails 2.0 upgrade
# assert_rjs :update, 'status', "<div class=\"ErrorExplanation\" id=\"ErrorExplanation\"><h2>1 error prohibited this record from being saved</h2><p>There were problems with the following fields:</p><ul>Name cannot contain the comma (',') character</ul></div>"
end
def test_todo_state_is_project_hidden_after_hiding_project
p = projects(:timemachine)
todos = p.todos.find_in_state(:all, :active)
login_as(:admin_user)
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
todos.each do |t|
assert_equal :project_hidden, t.reload().current_state
end
assert p.reload().hidden?
end
def test_not_done_counts_after_hiding_and_unhiding_project
p = projects(:timemachine)
todos = p.todos.find_in_state(:all, :active)
login_as(:admin_user)
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"active"}
todos.each do |t|
assert_equal :active, t.reload().current_state
end
assert p.reload().active?
end
def test_rss_feed_content
login_as(:admin_user)
get :index, { :format => "rss" }
assert_equal 'application/rss+xml', @response.content_type
#puts @response.body
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Tracks Projects'
assert_select '>description', "Lists all the projects for #{users(:admin_user).display_name}"
assert_select 'language', 'en-us'
assert_select 'ttl', '40'
end
assert_select 'item', 3 do
assert_select 'title', /.+/
assert_select 'description' do
assert_select_encoded do
assert_select 'p', /^\d+&nbsp;actions\. Project is (active|hidden|completed)\.$/
end
end
%w(guid link).each do |node|
assert_select node, /http:\/\/test.host\/projects\/.+/
end
assert_select 'pubDate', projects(:timemachine).updated_at.to_s(:rfc822)
end
end
end
def test_rss_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "rss" }
assert_response 401
end
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "rss", :token => 'foo' }
assert_response 401
end
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "rss", :token => users(:admin_user).token }
assert_response :ok
end
def test_atom_feed_content
login_as :admin_user
get :index, { :format => "atom" }
assert_equal 'application/atom+xml', @response.content_type
#puts @response.body
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
assert_select '>title', 'Tracks Projects'
assert_select '>subtitle', "Lists all the projects for #{users(:admin_user).display_name}"
assert_select 'entry', 3 do
assert_select 'title', /.+/
assert_select 'content[type="html"]' do
assert_select_encoded do
assert_select 'p', /\d+&nbsp;actions. Project is (active|hidden|completed)./
end
end
assert_select 'published', /(#{Regexp.escape(projects(:timemachine).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
end
end
end
def test_atom_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "atom" }
assert_response 401
end
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "atom", :token => 'foo' }
assert_response 401
end
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "atom", :token => users(:admin_user).token }
assert_response :ok
end
def test_text_feed_content
login_as :admin_user
get :index, { :format => "txt" }
assert_equal 'text/plain', @response.content_type
assert !(/&nbsp;/.match(@response.body))
#puts @response.body
end
def test_text_feed_content_for_projects_with_no_actions
login_as :admin_user
p = projects(:timemachine)
p.todos.each { |t| t.destroy }
get :index, { :format => "txt", :only_active_with_no_next_actions => true }
assert (/^\s*BUILD A WORKING TIME MACHINE\s+0 actions. Project is active.\s*$/.match(@response.body))
assert !(/[1-9] actions/.match(@response.body))
end
def test_text_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "txt" }
assert_response 401
end
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "txt", :token => 'foo' }
assert_response 401
end
def test_text_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "txt", :token => users(:admin_user).token }
assert_response :ok
end
def test_actionize_sorts_active_projects_by_number_of_tasks
login_as :admin_user
u = users(:admin_user)
post :actionize, :state => "active", :format => 'js'
assert_equal 1, projects(:moremoney).position
assert_equal 2, projects(:gardenclean).position
assert_equal 3, projects(:timemachine).position
end
def test_alphabetize_sorts_active_projects_alphabetically
login_as :admin_user
u = users(:admin_user)
post :alphabetize, :state => "active", :format => 'js'
assert_equal 1, projects(:timemachine).position
assert_equal 2, projects(:gardenclean).position
assert_equal 3, projects(:moremoney).position
end
def test_alphabetize_assigns_state
login_as :admin_user
post :alphabetize, :state => "active", :format => 'js'
assert_equal "active", assigns['state']
end
def test_alphabetize_assigns_projects
login_as :admin_user
post :alphabetize, :state => "active", :format => 'js'
exposed_projects = assigns['projects']
assert_equal 3, exposed_projects.length
assert_equal projects(:timemachine), exposed_projects[0]
assert_equal projects(:gardenclean), exposed_projects[1]
assert_equal projects(:moremoney), exposed_projects[2]
end
def protect_against_forgery?
false
end
end

View file

@ -57,8 +57,8 @@ class StatsControllerTest < Test::Unit::TestCase
assert_equal 3, assigns['projects'].count(:conditions => "state = 'active'")
assert_equal 10, assigns['contexts'].count
assert_equal 16, assigns['actions'].count
assert_equal 4, assigns['tags'].count
assert_equal 2, assigns['unique_tags'].size
assert_equal 4, assigns['tags_count']
assert_equal 2, assigns['unique_tags_count']
assert_equal 2.week.ago.utc.beginning_of_day, assigns['first_action'].created_at
end
@ -90,5 +90,21 @@ class StatsControllerTest < Test::Unit::TestCase
assert_response :success
assert_template "stats/show_selection_from_chart"
end
def test_stats_render_when_tasks_have_no_taggings
login_as(:admin_user)
# using the default fixtures, todos have tags
get :index
assert_response :success
# clear taggings table and render again
taggings = Tagging.find(:all)
taggings.each do |t|
t.delete
end
get :index
assert_response :success
end
end

View file

@ -1,488 +1,508 @@
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 < Test::Rails::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos
def setup
@controller = TodosController.new
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
end
def test_get_index_when_not_logged_in
get :index
assert_redirected_to :controller => 'login', :action => 'login'
end
def test_not_done_counts
login_as(:admin_user)
get :index
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_tag_is_retrieved_properly
login_as(:admin_user)
get :index
t = assigns['not_done_todos'].find{|t| t.id == 2}
assert_equal 1, t.tags.count
assert_equal 'foo', t.tags[0].name
assert !t.starred?
end
def test_not_done_counts_after_hiding_project
p = Project.find(1)
p.hide!
p.save!
login_as(:admin_user)
get :index
assert_equal nil, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 2, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal nil, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_not_done_counts_after_hiding_and_unhiding_project
p = Project.find(1)
p.hide!
p.save!
p.activate!
p.save!
login_as(:admin_user)
get :index
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_deferred_count_for_project_source_view
login_as(:admin_user)
xhr :post, :toggle_check, :id => 5, :_source_view => 'project'
assert_equal 1, assigns['deferred_count']
xhr :post, :toggle_check, :id => 15, :_source_view => 'project'
assert_equal 0, assigns['deferred_count']
end
def test_destroy_todo
login_as(:admin_user)
xhr :post, :destroy, :id => 1, :_source_view => 'todo'
assert_rjs :page, "todo_1", :remove
# #assert_rjs :replace_html, "badge-count", '9'
end
def test_create_todo
assert_difference Todo, :count do
login_as(:admin_user)
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
end
end
def test_create_todo_via_xml
login_as(:admin_user)
assert_difference Todo, :count do
put :create, :format => "xml", "request" => { "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
assert_response 201
end
end
def test_fail_to_create_todo_via_xml
login_as(:admin_user)
# #try to create with no context, which is not valid
put :create, :format => "xml", "request" => { "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
assert_response 422
assert_xml_select "errors" do
assert_xml_select "error", "Context can't be blank"
end
end
def test_create_deferred_todo
original_todo_count = Todo.count
login_as(:admin_user)
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2026", 'show_from' => '30/10/2026'}, "tag_list"=>"foo bar"
assert_equal original_todo_count + 1, Todo.count
end
def test_update_todo_project
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
t = Todo.find(1)
assert_equal 1, t.project_id
end
def test_update_todo_project_to_none
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"None", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
t = Todo.find(1)
assert_nil t.project_id
end
def test_update_todo_to_deferred_is_reflected_in_badge_count
login_as(:admin_user)
get :index
assert_equal 11, assigns['count']
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Make more money than Billy Gates", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006", "show_from"=>"30/11/2030"}, "tag_list"=>"foo bar"
assert_equal 10, assigns['down_count']
end
def test_update_todo
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
t = Todo.find(1)
assert_equal "Call Warren Buffet to find out how much he makes per day", t.description
assert_equal "bar, foo", t.tag_list
expected = Date.new(2006,11,30)
actual = t.due.to_date
assert_equal expected, actual, "Expected #{expected.to_s(:db)}, was #{actual.to_s(:db)}"
end
def test_update_todos_with_blank_project_name
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', :project_name => '', "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
t.reload
assert t.project.nil?
end
def test_update_todo_tags_to_none
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>""
t = Todo.find(1)
assert_equal true, t.tag_list.empty?
end
def test_update_todo_tags_with_whitespace_and_dots
t = Todo.find(1)
login_as(:admin_user)
taglist = " one , two,three ,four, 8.1.2, version1.5"
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>taglist
t = Todo.find(1)
assert_equal "8.1.2, four, one, three, two, version1.5", t.tag_list
end
def test_find_tagged_with
login_as(:admin_user)
@user = User.find(@request.session['user_id'])
tag = Tag.find_by_name('foo').todos
@tagged = tag.find(:all, :conditions => ['taggings.user_id = ?', @user.id]).size
get :tag, :name => 'foo'
assert_response :success
assert_equal 3, @tagged
end
def test_rss_feed
login_as(:admin_user)
get :index, { :format => "rss" }
assert_equal 'application/rss+xml', @response.content_type
# puts @response.body
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'language', 'en-us'
assert_select 'ttl', '40'
assert_select 'item', 11 do
assert_select 'title', /.+/
assert_select 'description', /.*/
assert_select 'link', %r{http://test.host/contexts/.+}
assert_select 'guid', %r{http://test.host/todos/.+}
assert_select 'pubDate', todos(:book).updated_at.to_s(:rfc822)
end
end
end
end
def test_rss_feed_with_limit
login_as(:admin_user)
get :index, { :format => "rss", :limit => '5' }
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'item', 5 do
assert_select 'title', /.+/
assert_select 'description', /.*/
end
end
end
end
def test_rss_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "rss" }
assert_response 401
end
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "rss", :token => 'foo' }
assert_response 401
end
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "rss", :token => users(:admin_user).token }
assert_response :ok
end
def test_atom_feed_content
login_as :admin_user
get :index, { :format => "atom" }
assert_equal 'application/atom+xml', @response.content_type
# #puts @response.body
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
assert_xml_select '>title', 'Actions'
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
assert_xml_select 'entry', 11 do
assert_xml_select 'title', /.+/
assert_xml_select 'content[type="html"]', /.*/
assert_xml_select 'published', /(#{Regexp.escape(todos(:book).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
end
end
end
def test_atom_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "atom" }
assert_response 401
end
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "atom", :token => 'foo' }
assert_response 401
end
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "atom", :token => users(:admin_user).token }
assert_response :ok
end
def test_text_feed_content
login_as(:admin_user)
get :index, { :format => "txt" }
assert_equal 'text/plain', @response.content_type
assert !(/&nbsp;/.match(@response.body))
# #puts @response.body
end
def test_text_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "txt" }
assert_response 401
end
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "txt", :token => 'foo' }
assert_response 401
end
def test_text_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "txt", :token => users(:admin_user).token }
assert_response :ok
end
def test_ical_feed_content
login_as :admin_user
get :index, { :format => "ics" }
assert_equal 'text/calendar', @response.content_type
assert !(/&nbsp;/.match(@response.body))
# #puts @response.body
end
def test_mobile_index_uses_text_html_content_type
login_as(:admin_user)
get :index, { :format => "m" }
assert_equal 'text/html', @response.content_type
end
def test_mobile_index_assigns_down_count
login_as(:admin_user)
get :index, { :format => "m" }
assert_equal 11, assigns['down_count']
end
def test_mobile_create_action_creates_a_new_todo
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
t = Todo.find_by_description("test_mobile_create_action")
assert_not_nil t
assert_equal 2, t.context_id
assert_equal 1, t.project_id
assert t.active?
assert_equal 'test notes', t.notes
assert_nil t.show_from
assert_equal Date.new(2007,1,2), t.due.to_date
end
def test_mobile_create_action_redirects_to_mobile_home_page_when_successful
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
assert_redirected_to '/m'
end
def test_mobile_create_action_renders_new_template_when_save_fails
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "state"=>"0"}, "tag_list"=>"test, test2"}
assert_template 'todos/new'
end
def test_index_html_assigns_default_project_name_map
login_as(:admin_user)
get :index, {"format"=>"html"}
assert_equal '"{\\"Build a working time machine\\": \\"lab\\"}"', assigns(:default_project_context_name_map)
end
def test_toggle_check_on_recurring_todo
login_as(:admin_user)
# link todo_1 and recurring_todo_1
recurring_todo_1 = RecurringTodo.find(1)
todo_1 = Todo.find_by_recurring_todo_id(1)
# mark todo_1 as complete by toggle_check
xhr :post, :toggle_check, :id => todo_1.id, :_source_view => 'todo'
todo_1.reload
assert todo_1.completed?
# check that there is only one active todo belonging to recurring_todo
count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert_equal 1, count
# check there is a new todo linked to the recurring pattern
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert_equal "Call Bill Gates every day", next_todo.description
# check that the new todo is not the same as todo_1
assert_not_equal todo_1.id, next_todo.id
# change recurrence pattern to monthly and set show_from 2 days before due
# date this forces the next todo to be put in the tickler
recurring_todo_1.show_from_delta = 2
recurring_todo_1.recurring_period = 'monthly'
recurring_todo_1.recurrence_selector = 0
recurring_todo_1.every_other1 = 1
recurring_todo_1.every_other2 = 2
recurring_todo_1.every_other3 = 5
recurring_todo_1.save
# mark next_todo as complete by toggle_check
xhr :post, :toggle_check, :id => next_todo.id, :_source_view => 'todo'
next_todo.reload
assert next_todo.completed?
# check that there are three todos belonging to recurring_todo: two
# completed and one deferred
count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id})
assert_equal 3, count
# check there is a new todo linked to the recurring pattern in the tickler
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !next_todo.nil?
assert_equal "Call Bill Gates every day", next_todo.description
# check that the todo is in the tickler
assert !next_todo.show_from.nil?
end
def test_toggle_check_on_rec_todo_show_from_today
login_as(:admin_user)
# link todo_1 and recurring_todo_1
recurring_todo_1 = RecurringTodo.find(1)
todo_1 = Todo.find_by_recurring_todo_id(1)
today = Time.now.utc.at_midnight
# change recurrence pattern to monthly and set show_from to today
recurring_todo_1.target = 'show_from_date'
recurring_todo_1.recurring_period = 'monthly'
recurring_todo_1.recurrence_selector = 0
recurring_todo_1.every_other1 = today.day
recurring_todo_1.every_other2 = 1
recurring_todo_1.save
# mark todo_1 as complete by toggle_check, this gets rid of todo_1 that was
# not correctly created from the adjusted recurring pattern we defined
# above.
xhr :post, :toggle_check, :id => todo_1.id, :_source_view => 'todo'
todo_1.reload
assert todo_1.completed?
# locate the new todo. This todo is created from the adjusted recurring
# pattern defined in this test
new_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert !new_todo.nil?
# mark new_todo as complete by toggle_check
xhr :post, :toggle_check, :id => new_todo.id, :_source_view => 'todo'
new_todo.reload
assert todo_1.completed?
# locate the new todo in tickler
new_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !new_todo.nil?
assert_equal "Call Bill Gates every day", new_todo.description
# check that the new todo is not the same as todo_1
assert_not_equal todo_1.id, new_todo.id
# check that the new_todo is in the tickler to show next month
assert !new_todo.show_from.nil?
assert_equal Time.utc(today.year, today.month, today.day)+1.month, new_todo.show_from
end
def test_check_for_next_todo
login_as :admin_user
recurring_todo_1 = RecurringTodo.find(5)
@todo = Todo.find_by_recurring_todo_id(1)
assert @todo.from_recurring_todo?
# rewire @todo to yearly recurring todo
@todo.recurring_todo_id = 5
# make todo due tomorrow and change recurring date also to tomorrow
@todo.due = Time.zone.now + 1.day
@todo.save
recurring_todo_1.every_other1 = @todo.due.day
recurring_todo_1.every_other2 = @todo.due.month
recurring_todo_1.save
# mark todo complete
xhr :post, :toggle_check, :id => @todo.id, :_source_view => 'todo'
@todo.reload
assert @todo.completed?
# check that there is no active todo
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert next_todo.nil?
# check for new deferred todo
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !next_todo.nil?
# check that the due date of the new todo is later than tomorrow
assert next_todo.due > @todo.due
end
end
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 < Test::Rails::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos
def setup
@controller = TodosController.new
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
end
def test_get_index_when_not_logged_in
get :index
assert_redirected_to :controller => 'login', :action => 'login'
end
def test_not_done_counts
login_as(:admin_user)
get :index
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_tag_is_retrieved_properly
login_as(:admin_user)
get :index
t = assigns['not_done_todos'].find{|t| t.id == 2}
assert_equal 1, t.tags.count
assert_equal 'foo', t.tags[0].name
assert !t.starred?
end
def test_not_done_counts_after_hiding_project
p = Project.find(1)
p.hide!
p.save!
login_as(:admin_user)
get :index
assert_equal nil, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 2, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal nil, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_not_done_counts_after_hiding_and_unhiding_project
p = Project.find(1)
p.hide!
p.save!
p.activate!
p.save!
login_as(:admin_user)
get :index
assert_equal 2, assigns['project_not_done_counts'][projects(:timemachine).id]
assert_equal 3, assigns['context_not_done_counts'][contexts(:call).id]
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
end
def test_deferred_count_for_project_source_view
login_as(:admin_user)
xhr :post, :toggle_check, :id => 5, :_source_view => 'project'
assert_equal 1, assigns['deferred_count']
xhr :post, :toggle_check, :id => 15, :_source_view => 'project'
assert_equal 0, assigns['deferred_count']
end
def test_destroy_todo
login_as(:admin_user)
xhr :post, :destroy, :id => 1, :_source_view => 'todo'
assert_rjs :page, "todo_1", :remove
# #assert_rjs :replace_html, "badge-count", '9'
end
def test_create_todo
assert_difference Todo, :count do
login_as(:admin_user)
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
end
end
def test_create_todo_via_xml
login_as(:admin_user)
assert_difference Todo, :count do
put :create, :format => "xml", "request" => { "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
assert_response 201
end
end
def test_create_todo_via_xml_show_from
login_as(:admin_user)
assert_difference Todo, :count do
xml = "<todo><description>Call Warren Buffet to find out how much he makes per day</description><project_id>#{projects(:timemachine).id}</project_id><context_id>#{contexts(:agenda).id}</context_id><show-from type=\"datetime\">#{1.week.from_now.xmlschema}</show-from></todo>"
#p parse_xml_body(xml)
post :create, parse_xml_body(xml).update(:format => "xml")
assert_response :created
end
end
def parse_xml_body(body)
env = { 'rack.input' => StringIO.new(body),
'HTTP_X_POST_DATA_FORMAT' => 'xml',
'CONTENT_LENGTH' => body.size.to_s }
ActionController::RackRequest.new(env).request_parameters
end
def test_fail_to_create_todo_via_xml
login_as(:admin_user)
# #try to create with no context, which is not valid
put :create, :format => "xml", "request" => { "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar" }
assert_response 422
assert_xml_select "errors" do
assert_xml_select "error", "Context can't be blank"
end
end
def test_create_deferred_todo
original_todo_count = Todo.count
login_as(:admin_user)
put :create, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2026", 'show_from' => '30/10/2026'}, "tag_list"=>"foo bar"
assert_equal original_todo_count + 1, Todo.count
end
def test_update_todo_project
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Build a working time machine", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
t = Todo.find(1)
assert_equal 1, t.project_id
end
def test_update_todo_project_to_none
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"None", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo bar"
t = Todo.find(1)
assert_nil t.project_id
end
def test_update_todo_to_deferred_is_reflected_in_badge_count
login_as(:admin_user)
get :index
assert_equal 11, assigns['count']
xhr :post, :update, :id => 1, :_source_view => 'todo', "context_name"=>"library", "project_name"=>"Make more money than Billy Gates", "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006", "show_from"=>"30/11/2030"}, "tag_list"=>"foo bar"
assert_equal 10, assigns['down_count']
end
def test_update_todo
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
t = Todo.find(1)
assert_equal "Call Warren Buffet to find out how much he makes per day", t.description
assert_equal "bar, foo", t.tag_list
expected = Date.new(2006,11,30)
actual = t.due.to_date
assert_equal expected, actual, "Expected #{expected.to_s(:db)}, was #{actual.to_s(:db)}"
end
def test_update_todos_with_blank_project_name
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', :project_name => '', "todo"=>{"id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>"foo, bar"
t.reload
assert t.project.nil?
end
def test_update_todo_tags_to_none
t = Todo.find(1)
login_as(:admin_user)
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>""
t = Todo.find(1)
assert_equal true, t.tag_list.empty?
end
def test_update_todo_tags_with_whitespace_and_dots
t = Todo.find(1)
login_as(:admin_user)
taglist = " one , two,three ,four, 8.1.2, version1.5"
xhr :post, :update, :id => 1, :_source_view => 'todo', "todo"=>{"context_id"=>"1", "project_id"=>"2", "id"=>"1", "notes"=>"", "description"=>"Call Warren Buffet to find out how much he makes per day", "due"=>"30/11/2006"}, "tag_list"=>taglist
t = Todo.find(1)
assert_equal "8.1.2, four, one, three, two, version1.5", t.tag_list
end
def test_find_tagged_with
login_as(:admin_user)
@user = User.find(@request.session['user_id'])
tag = Tag.find_by_name('foo').todos
@tagged = tag.count
get :tag, :name => 'foo'
assert_response :success
assert_equal 3, @tagged
end
def test_rss_feed
login_as(:admin_user)
get :index, { :format => "rss" }
assert_equal 'application/rss+xml', @response.content_type
# puts @response.body
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'language', 'en-us'
assert_select 'ttl', '40'
assert_select 'item', 11 do
assert_select 'title', /.+/
assert_select 'description', /.*/
assert_select 'link', %r{http://test.host/contexts/.+}
assert_select 'guid', %r{http://test.host/todos/.+}
assert_select 'pubDate', todos(:book).updated_at.to_s(:rfc822)
end
end
end
end
def test_rss_feed_with_limit
login_as(:admin_user)
get :index, { :format => "rss", :limit => '5' }
assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do
assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'item', 5 do
assert_select 'title', /.+/
assert_select 'description', /.*/
end
end
end
end
def test_rss_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "rss" }
assert_response 401
end
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "rss", :token => 'foo' }
assert_response 401
end
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "rss", :token => users(:admin_user).token }
assert_response :ok
end
def test_atom_feed_content
login_as :admin_user
get :index, { :format => "atom" }
assert_equal 'application/atom+xml', @response.content_type
# #puts @response.body
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
assert_xml_select '>title', 'Actions'
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
assert_xml_select 'entry', 11 do
assert_xml_select 'title', /.+/
assert_xml_select 'content[type="html"]', /.*/
assert_xml_select 'published', /(#{Regexp.escape(todos(:book).updated_at.xmlschema)}|#{Regexp.escape(projects(:moremoney).updated_at.xmlschema)})/
end
end
end
def test_atom_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "atom" }
assert_response 401
end
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "atom", :token => 'foo' }
assert_response 401
end
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "atom", :token => users(:admin_user).token }
assert_response :ok
end
def test_text_feed_content
login_as(:admin_user)
get :index, { :format => "txt" }
assert_equal 'text/plain', @response.content_type
assert !(/&nbsp;/.match(@response.body))
# #puts @response.body
end
def test_text_feed_not_accessible_to_anonymous_user_without_token
login_as nil
get :index, { :format => "txt" }
assert_response 401
end
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
login_as nil
get :index, { :format => "txt", :token => 'foo' }
assert_response 401
end
def test_text_feed_accessible_to_anonymous_user_with_valid_token
login_as nil
get :index, { :format => "txt", :token => users(:admin_user).token }
assert_response :ok
end
def test_ical_feed_content
login_as :admin_user
get :index, { :format => "ics" }
assert_equal 'text/calendar', @response.content_type
assert !(/&nbsp;/.match(@response.body))
# #puts @response.body
end
def test_mobile_index_uses_text_html_content_type
login_as(:admin_user)
get :index, { :format => "m" }
assert_equal 'text/html', @response.content_type
end
def test_mobile_index_assigns_down_count
login_as(:admin_user)
get :index, { :format => "m" }
assert_equal 11, assigns['down_count']
end
def test_mobile_create_action_creates_a_new_todo
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
t = Todo.find_by_description("test_mobile_create_action")
assert_not_nil t
assert_equal 2, t.context_id
assert_equal 1, t.project_id
assert t.active?
assert_equal 'test notes', t.notes
assert_nil t.show_from
assert_equal Date.new(2007,1,2), t.due.to_date
end
def test_mobile_create_action_redirects_to_mobile_home_page_when_successful
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "description"=>"test_mobile_create_action", "state"=>"0"}}
assert_redirected_to '/m'
end
def test_mobile_create_action_renders_new_template_when_save_fails
login_as(:admin_user)
post :create, {"format"=>"m", "todo"=>{"context_id"=>"2",
"due(1i)"=>"2007", "due(2i)"=>"1", "due(3i)"=>"2",
"show_from(1i)"=>"", "show_from(2i)"=>"", "show_from(3i)"=>"",
"project_id"=>"1",
"notes"=>"test notes", "state"=>"0"}, "tag_list"=>"test, test2"}
assert_template 'todos/new'
end
def test_index_html_assigns_default_project_name_map
login_as(:admin_user)
get :index, {"format"=>"html"}
assert_equal '"{\\"Build a working time machine\\": \\"lab\\"}"', assigns(:default_project_context_name_map)
end
def test_toggle_check_on_recurring_todo
login_as(:admin_user)
# link todo_1 and recurring_todo_1
recurring_todo_1 = RecurringTodo.find(1)
todo_1 = Todo.find_by_recurring_todo_id(1)
# mark todo_1 as complete by toggle_check
xhr :post, :toggle_check, :id => todo_1.id, :_source_view => 'todo'
todo_1.reload
assert todo_1.completed?
# check that there is only one active todo belonging to recurring_todo
count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert_equal 1, count
# check there is a new todo linked to the recurring pattern
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert_equal "Call Bill Gates every day", next_todo.description
# check that the new todo is not the same as todo_1
assert_not_equal todo_1.id, next_todo.id
# change recurrence pattern to monthly and set show_from 2 days before due
# date this forces the next todo to be put in the tickler
recurring_todo_1.show_from_delta = 2
recurring_todo_1.recurring_period = 'monthly'
recurring_todo_1.recurrence_selector = 0
recurring_todo_1.every_other1 = 1
recurring_todo_1.every_other2 = 2
recurring_todo_1.every_other3 = 5
recurring_todo_1.save
# mark next_todo as complete by toggle_check
xhr :post, :toggle_check, :id => next_todo.id, :_source_view => 'todo'
next_todo.reload
assert next_todo.completed?
# check that there are three todos belonging to recurring_todo: two
# completed and one deferred
count = Todo.count(:all, :conditions => {:recurring_todo_id => recurring_todo_1.id})
assert_equal 3, count
# check there is a new todo linked to the recurring pattern in the tickler
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !next_todo.nil?
assert_equal "Call Bill Gates every day", next_todo.description
# check that the todo is in the tickler
assert !next_todo.show_from.nil?
end
def test_toggle_check_on_rec_todo_show_from_today
login_as(:admin_user)
# link todo_1 and recurring_todo_1
recurring_todo_1 = RecurringTodo.find(1)
todo_1 = Todo.find_by_recurring_todo_id(1)
today = Time.now.utc.at_midnight
# change recurrence pattern to monthly and set show_from to today
recurring_todo_1.target = 'show_from_date'
recurring_todo_1.recurring_period = 'monthly'
recurring_todo_1.recurrence_selector = 0
recurring_todo_1.every_other1 = today.day
recurring_todo_1.every_other2 = 1
recurring_todo_1.save
# mark todo_1 as complete by toggle_check, this gets rid of todo_1 that was
# not correctly created from the adjusted recurring pattern we defined
# above.
xhr :post, :toggle_check, :id => todo_1.id, :_source_view => 'todo'
todo_1.reload
assert todo_1.completed?
# locate the new todo. This todo is created from the adjusted recurring
# pattern defined in this test
new_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert !new_todo.nil?
# mark new_todo as complete by toggle_check
xhr :post, :toggle_check, :id => new_todo.id, :_source_view => 'todo'
new_todo.reload
assert todo_1.completed?
# locate the new todo in tickler
new_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !new_todo.nil?
assert_equal "Call Bill Gates every day", new_todo.description
# check that the new todo is not the same as todo_1
assert_not_equal todo_1.id, new_todo.id
# check that the new_todo is in the tickler to show next month
assert !new_todo.show_from.nil?
assert_equal Time.utc(today.year, today.month, today.day)+1.month, new_todo.show_from
end
def test_check_for_next_todo
login_as :admin_user
recurring_todo_1 = RecurringTodo.find(5)
@todo = Todo.find_by_recurring_todo_id(1)
assert @todo.from_recurring_todo?
# rewire @todo to yearly recurring todo
@todo.recurring_todo_id = 5
# make todo due tomorrow and change recurring date also to tomorrow
@todo.due = Time.zone.now + 1.day
@todo.save
recurring_todo_1.every_other1 = @todo.due.day
recurring_todo_1.every_other2 = @todo.due.month
recurring_todo_1.save
# mark todo complete
xhr :post, :toggle_check, :id => @todo.id, :_source_view => 'todo'
@todo.reload
assert @todo.completed?
# check that there is no active todo
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'active'})
assert next_todo.nil?
# check for new deferred todo
next_todo = Todo.find(:first, :conditions => {:recurring_todo_id => recurring_todo_1.id, :state => 'deferred'})
assert !next_todo.nil?
# check that the due date of the new todo is later than tomorrow
assert next_todo.due > @todo.due
end
end

View file

@ -1,283 +1,283 @@
require File.dirname(__FILE__) + '/../test_helper'
class RecurringTodoTest < Test::Rails::TestCase
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings, :recurring_todos
def setup
@every_day = RecurringTodo.find(1).reload
@every_workday = RecurringTodo.find(2).reload
@weekly_every_day = RecurringTodo.find(3).reload
@monthly_every_last_friday = RecurringTodo.find(4).reload
@yearly = RecurringTodo.find(5).reload
@today = Time.now.utc
@tomorrow = @today + 1.day
@in_three_days = Time.now.utc + 3.days
@in_four_days = @in_three_days + 1.day # need a day after start_from
@friday = Time.zone.local(2008,6,6)
@saturday = Time.zone.local(2008,6,7)
@sunday = Time.zone.local(2008,6,8) # june 8, 2008 was a sunday
@monday = Time.zone.local(2008,6,9)
@tuesday = Time.zone.local(2008,6,10)
@wednesday = Time.zone.local(2008,6,11)
@thursday = Time.zone.local(2008,6,12)
end
def test_pattern_text
assert_equal "every day", @every_day.recurrence_pattern
assert_equal "on work days", @every_workday.recurrence_pattern
assert_equal "every last Friday of every 2 months", @monthly_every_last_friday.recurrence_pattern
assert_equal "every year on June 8", @yearly.recurrence_pattern
end
def test_daily_every_day
# every_day should return todays date if there was no previous date
due_date = @every_day.get_due_date(nil)
# use strftime in compare, because milisec / secs could be different
assert_equal @today.strftime("%d-%m-%y"), due_date.strftime("%d-%m-%y")
# when the last todo was completed today, the next todo is due tomorrow
due_date =@every_day.get_due_date(@today)
assert_equal @tomorrow, due_date
# do something every 14 days
@every_day.every_other1=14
due_date = @every_day.get_due_date(@today)
assert_equal @today+14.days, due_date
end
def test_daily_work_days
assert_equal @monday, @every_workday.get_due_date(@friday)
assert_equal @monday, @every_workday.get_due_date(@saturday)
assert_equal @monday, @every_workday.get_due_date(@sunday)
assert_equal @tuesday, @every_workday.get_due_date(@monday)
end
def test_show_from_date
# assume that target due_date works fine, i.e. don't do the same tests over
@every_day.target='show_from_date'
# when recurrence is targeted on show_from, due date shoult remain nil
assert_equal nil, @every_day.get_due_date(nil)
assert_equal nil, @every_day.get_due_date(@today-3.days)
# check show from get the next day
assert_equal @today, @every_day.get_show_from_date(@today-1.days)
assert_equal @today+1.day, @every_day.get_show_from_date(@today)
@every_day.target='due_date'
# when target on due_date, show_from is relative to due date unless delta=0
assert_equal nil, @every_day.get_show_from_date(@today-1.days)
@every_day.show_from_delta=10
assert_equal @today, @every_day.get_show_from_date(@today+9.days) #today+1+9-10
# TODO: show_from has no use case for daily pattern. Need to test on
# weekly/monthly/yearly
end
def test_end_date_on_recurring_todo
assert_equal true, @every_day.has_next_todo(@in_three_days)
assert_equal true, @every_day.has_next_todo(@in_four_days)
@every_day.end_date = @in_four_days
assert_equal false, @every_day.has_next_todo(@in_four_days)
end
def test_weekly_every_day_setters
@weekly_every_day.every_day = ' '
@weekly_every_day.weekly_return_sunday=('s')
assert_equal 's ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_monday=('m')
assert_equal 'sm ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_tuesday=('t')
assert_equal 'smt ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_wednesday=('w')
assert_equal 'smtw ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_thursday=('t')
assert_equal 'smtwt ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_friday=('f')
assert_equal 'smtwtf ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_saturday=('s')
assert_equal 'smtwtfs', @weekly_every_day.every_day
# test remove
@weekly_every_day.weekly_return_wednesday=(' ')
assert_equal 'smt tfs', @weekly_every_day.every_day
end
def test_weekly_pattern
assert_equal true, @weekly_every_day.has_next_todo(nil)
due_date = @weekly_every_day.get_due_date(@sunday)
assert_equal @monday, due_date
# saturday is last day in week, so the next date should be sunday + n_weeks
due_date = @weekly_every_day.get_due_date(@saturday)
assert_equal @sunday + 2.weeks, due_date
# remove tuesday and wednesday
@weekly_every_day.weekly_return_tuesday=(' ')
@weekly_every_day.weekly_return_wednesday=(' ')
assert_equal 'sm tfs', @weekly_every_day.every_day
due_date = @weekly_every_day.get_due_date(@monday)
assert_equal @thursday, due_date
@weekly_every_day.every_other1 = 1
@weekly_every_day.every_day = ' tw '
due_date = @weekly_every_day.get_due_date(@tuesday)
assert_equal @wednesday, due_date
due_date = @weekly_every_day.get_due_date(@wednesday)
assert_equal @tuesday+1.week, due_date
end
def test_monthly_pattern
due_date = @monthly_every_last_friday.get_due_date(@sunday)
assert_equal Time.zone.local(2008,6,27), due_date
friday_is_last_day_of_month = Time.zone.local(2008,10,31)
due_date = @monthly_every_last_friday.get_due_date(friday_is_last_day_of_month-1.day )
assert_equal friday_is_last_day_of_month , due_date
@monthly_every_third_friday = @monthly_every_last_friday
@monthly_every_third_friday.every_other3=3 #third
due_date = @monthly_every_last_friday.get_due_date(@sunday) # june 8th 2008
assert_equal Time.zone.local(2008, 6, 20), due_date
# set date past third friday of this month
due_date = @monthly_every_last_friday.get_due_date(Time.zone.local(2008,6,21)) # june 21th 2008
assert_equal Time.zone.local(2008, 8, 15), due_date # every 2 months, so aug
@monthly = @monthly_every_last_friday
@monthly.recurrence_selector=0
@monthly.every_other1 = 8 # every 8th day of the month
@monthly.every_other2 = 2 # every 2 months
due_date = @monthly.get_due_date(@saturday) # june 7th
assert_equal @sunday, due_date # june 8th
due_date = @monthly.get_due_date(@sunday) # june 8th
assert_equal Time.zone.local(2008,8,8), due_date # aug 8th
end
def test_yearly_pattern
# beginning of same year
due_date = @yearly.get_due_date(Time.zone.local(2008,2,10)) # feb 10th
assert_equal @sunday, due_date # june 8th
# same month, previous date
due_date = @yearly.get_due_date(@saturday) # june 7th
show_from_date = @yearly.get_show_from_date(@saturday) # june 7th
assert_equal @sunday, due_date # june 8th
assert_equal @sunday-5.days, show_from_date
# same month, day after
due_date = @yearly.get_due_date(@monday) # june 9th
assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year
# very overdue
due_date = @yearly.get_due_date(@monday+5.months-2.days) # november 7
assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year
@yearly.recurrence_selector = 1
@yearly.every_other3 = 2 # second
@yearly.every_count = 3 # wednesday
# beginning of same year
due_date = @yearly.get_due_date(Time.zone.local(2008,2,10)) # feb 10th
assert_equal Time.zone.local(2008,6,11), due_date # june 11th
# same month, before second wednesday
due_date = @yearly.get_due_date(@saturday) # june 7th
assert_equal Time.zone.local(2008,6,11), due_date # june 11th
# same month, after second wednesday
due_date = @yearly.get_due_date(Time.zone.local(2008,6,12)) # june 7th
assert_equal Time.zone.local(2009,6,10), due_date # june 10th
# test handling of nil
due_date1 = @yearly.get_due_date(nil)
due_date2 = @yearly.get_due_date(Time.now.utc + 1.day)
assert_equal due_date1, due_date2
end
def test_last_sunday_of_march
@yearly.recurrence_selector = 1
@yearly.every_other2 = 3 # march
@yearly.every_other3 = 5 # last
@yearly.every_count = 0 # sunday
due_date = @yearly.get_due_date(Time.zone.local(2008,10,1)) # oct 1st
assert_equal Time.zone.local(2009,3,29), due_date # march 29th
end
def test_start_from_in_future
# every_day should return start_day if it is in the future
@every_day.start_from = @in_three_days
due_date = @every_day.get_due_date(nil)
assert_equal @in_three_days, due_date
due_date = @every_day.get_due_date(@tomorrow)
assert_equal @in_three_days, due_date
# if we give a date in the future for the previous todo, the next to do
# should be based on that future date.
due_date = @every_day.get_due_date(@in_four_days)
assert_equal @in_four_days+1.day, due_date
@weekly_every_day.start_from = Time.zone.local(2020,1,1)
assert_equal Time.zone.local(2020,1,1), @weekly_every_day.get_due_date(nil)
assert_equal Time.zone.local(2020,1,1), @weekly_every_day.get_due_date(Time.zone.local(2019,10,1))
assert_equal Time.zone.local(2020,1,10), @weekly_every_day.get_due_date(Time.zone.local(2020,1,9))
@monthly_every_last_friday.start_from = Time.zone.local(2020,1,1)
assert_equal Time.zone.local(2020,1,31), @monthly_every_last_friday.get_due_date(nil) # last friday of jan
assert_equal Time.zone.local(2020,1,31), @monthly_every_last_friday.get_due_date(Time.zone.local(2019,12,1)) # last friday of jan
assert_equal Time.zone.local(2020,2,28), @monthly_every_last_friday.get_due_date(Time.zone.local(2020,2,1)) # last friday of feb
# start from after june 8th 2008
@yearly.start_from = Time.zone.local(2020,6,12)
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(nil) # jun 8th next year
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(Time.zone.local(2019,6,1)) # also next year
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(Time.zone.local(2020,6,15)) # also next year
this_year = Time.now.utc.year
@yearly.start_from = Time.zone.local(this_year+1,6,12)
due_date = @yearly.get_due_date(nil)
assert_equal due_date.year, this_year+2
end
def test_toggle_completion
t = @yearly
assert_equal :active, t.current_state
t.toggle_completion!
assert_equal :completed, t.current_state
t.toggle_completion!
assert_equal :active, t.current_state
end
def test_starred
@yearly.tag_with("1, 2, starred")
@yearly.tags.reload
assert_equal true, @yearly.starred?
assert_equal false, @weekly_every_day.starred?
@yearly.toggle_star!
assert_equal false, @yearly.starred?
@yearly.toggle_star!
assert_equal true, @yearly.starred?
end
def test_occurence_count
@every_day.number_of_occurences = 2
assert_equal true, @every_day.has_next_todo(@in_three_days)
@every_day.inc_occurences
assert_equal true, @every_day.has_next_todo(@in_three_days)
@every_day.inc_occurences
assert_equal false, @every_day.has_next_todo(@in_three_days)
# after completion, when you reactivate the recurring todo, the occurences
# count should be reset
assert_equal 2, @every_day.occurences_count
@every_day.toggle_completion!
@every_day.toggle_completion!
assert_equal true, @every_day.has_next_todo(@in_three_days)
assert_equal 0, @every_day.occurences_count
end
end
require File.dirname(__FILE__) + '/../test_helper'
class RecurringTodoTest < Test::Rails::TestCase
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings, :recurring_todos
def setup
@every_day = RecurringTodo.find(1).reload
@every_workday = RecurringTodo.find(2).reload
@weekly_every_day = RecurringTodo.find(3).reload
@monthly_every_last_friday = RecurringTodo.find(4).reload
@yearly = RecurringTodo.find(5).reload
@today = Time.now.utc
@tomorrow = @today + 1.day
@in_three_days = Time.now.utc + 3.days
@in_four_days = @in_three_days + 1.day # need a day after start_from
@friday = Time.zone.local(2008,6,6)
@saturday = Time.zone.local(2008,6,7)
@sunday = Time.zone.local(2008,6,8) # june 8, 2008 was a sunday
@monday = Time.zone.local(2008,6,9)
@tuesday = Time.zone.local(2008,6,10)
@wednesday = Time.zone.local(2008,6,11)
@thursday = Time.zone.local(2008,6,12)
end
def test_pattern_text
assert_equal "every day", @every_day.recurrence_pattern
assert_equal "on work days", @every_workday.recurrence_pattern
assert_equal "every last Friday of every 2 months", @monthly_every_last_friday.recurrence_pattern
assert_equal "every year on June 8", @yearly.recurrence_pattern
end
def test_daily_every_day
# every_day should return todays date if there was no previous date
due_date = @every_day.get_due_date(nil)
# use strftime in compare, because milisec / secs could be different
assert_equal @today.strftime("%d-%m-%y"), due_date.strftime("%d-%m-%y")
# when the last todo was completed today, the next todo is due tomorrow
due_date =@every_day.get_due_date(@today)
assert_equal @tomorrow, due_date
# do something every 14 days
@every_day.every_other1=14
due_date = @every_day.get_due_date(@today)
assert_equal @today+14.days, due_date
end
def test_daily_work_days
assert_equal @monday, @every_workday.get_due_date(@friday)
assert_equal @monday, @every_workday.get_due_date(@saturday)
assert_equal @monday, @every_workday.get_due_date(@sunday)
assert_equal @tuesday, @every_workday.get_due_date(@monday)
end
def test_show_from_date
# assume that target due_date works fine, i.e. don't do the same tests over
@every_day.target='show_from_date'
# when recurrence is targeted on show_from, due date shoult remain nil
assert_equal nil, @every_day.get_due_date(nil)
assert_equal nil, @every_day.get_due_date(@today-3.days)
# check show from get the next day
assert_equal @today, @every_day.get_show_from_date(@today-1.days)
assert_equal @today+1.day, @every_day.get_show_from_date(@today)
@every_day.target='due_date'
# when target on due_date, show_from is relative to due date unless delta=0
assert_equal nil, @every_day.get_show_from_date(@today-1.days)
@every_day.show_from_delta=10
assert_equal @today, @every_day.get_show_from_date(@today+9.days) #today+1+9-10
# TODO: show_from has no use case for daily pattern. Need to test on
# weekly/monthly/yearly
end
def test_end_date_on_recurring_todo
assert_equal true, @every_day.has_next_todo(@in_three_days)
assert_equal true, @every_day.has_next_todo(@in_four_days)
@every_day.end_date = @in_four_days
assert_equal false, @every_day.has_next_todo(@in_four_days)
end
def test_weekly_every_day_setters
@weekly_every_day.every_day = ' '
@weekly_every_day.weekly_return_sunday=('s')
assert_equal 's ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_monday=('m')
assert_equal 'sm ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_tuesday=('t')
assert_equal 'smt ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_wednesday=('w')
assert_equal 'smtw ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_thursday=('t')
assert_equal 'smtwt ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_friday=('f')
assert_equal 'smtwtf ', @weekly_every_day.every_day
@weekly_every_day.weekly_return_saturday=('s')
assert_equal 'smtwtfs', @weekly_every_day.every_day
# test remove
@weekly_every_day.weekly_return_wednesday=(' ')
assert_equal 'smt tfs', @weekly_every_day.every_day
end
def test_weekly_pattern
assert_equal true, @weekly_every_day.has_next_todo(nil)
due_date = @weekly_every_day.get_due_date(@sunday)
assert_equal @monday, due_date
# saturday is last day in week, so the next date should be sunday + n_weeks
due_date = @weekly_every_day.get_due_date(@saturday)
assert_equal @sunday + 2.weeks, due_date
# remove tuesday and wednesday
@weekly_every_day.weekly_return_tuesday=(' ')
@weekly_every_day.weekly_return_wednesday=(' ')
assert_equal 'sm tfs', @weekly_every_day.every_day
due_date = @weekly_every_day.get_due_date(@monday)
assert_equal @thursday, due_date
@weekly_every_day.every_other1 = 1
@weekly_every_day.every_day = ' tw '
due_date = @weekly_every_day.get_due_date(@tuesday)
assert_equal @wednesday, due_date
due_date = @weekly_every_day.get_due_date(@wednesday)
assert_equal @tuesday+1.week, due_date
end
def test_monthly_pattern
due_date = @monthly_every_last_friday.get_due_date(@sunday)
assert_equal Time.zone.local(2008,6,27), due_date
friday_is_last_day_of_month = Time.zone.local(2008,10,31)
due_date = @monthly_every_last_friday.get_due_date(friday_is_last_day_of_month-1.day )
assert_equal friday_is_last_day_of_month , due_date
@monthly_every_third_friday = @monthly_every_last_friday
@monthly_every_third_friday.every_other3=3 #third
due_date = @monthly_every_last_friday.get_due_date(@sunday) # june 8th 2008
assert_equal Time.zone.local(2008, 6, 20), due_date
# set date past third friday of this month
due_date = @monthly_every_last_friday.get_due_date(Time.zone.local(2008,6,21)) # june 21th 2008
assert_equal Time.zone.local(2008, 8, 15), due_date # every 2 months, so aug
@monthly = @monthly_every_last_friday
@monthly.recurrence_selector=0
@monthly.every_other1 = 8 # every 8th day of the month
@monthly.every_other2 = 2 # every 2 months
due_date = @monthly.get_due_date(@saturday) # june 7th
assert_equal @sunday, due_date # june 8th
due_date = @monthly.get_due_date(@sunday) # june 8th
assert_equal Time.zone.local(2008,8,8), due_date # aug 8th
end
def test_yearly_pattern
# beginning of same year
due_date = @yearly.get_due_date(Time.zone.local(2008,2,10)) # feb 10th
assert_equal @sunday, due_date # june 8th
# same month, previous date
due_date = @yearly.get_due_date(@saturday) # june 7th
show_from_date = @yearly.get_show_from_date(@saturday) # june 7th
assert_equal @sunday, due_date # june 8th
assert_equal @sunday-5.days, show_from_date
# same month, day after
due_date = @yearly.get_due_date(@monday) # june 9th
assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year
# very overdue
due_date = @yearly.get_due_date(@monday+5.months-2.days) # november 7
assert_equal Time.zone.local(2009,6,8), due_date # june 8th next year
@yearly.recurrence_selector = 1
@yearly.every_other3 = 2 # second
@yearly.every_count = 3 # wednesday
# beginning of same year
due_date = @yearly.get_due_date(Time.zone.local(2008,2,10)) # feb 10th
assert_equal Time.zone.local(2008,6,11), due_date # june 11th
# same month, before second wednesday
due_date = @yearly.get_due_date(@saturday) # june 7th
assert_equal Time.zone.local(2008,6,11), due_date # june 11th
# same month, after second wednesday
due_date = @yearly.get_due_date(Time.zone.local(2008,6,12)) # june 7th
assert_equal Time.zone.local(2009,6,10), due_date # june 10th
# test handling of nil
due_date1 = @yearly.get_due_date(nil)
due_date2 = @yearly.get_due_date(Time.now.utc + 1.day)
assert_equal due_date1, due_date2
end
def test_last_sunday_of_march
@yearly.recurrence_selector = 1
@yearly.every_other2 = 3 # march
@yearly.every_other3 = 5 # last
@yearly.every_count = 0 # sunday
due_date = @yearly.get_due_date(Time.zone.local(2008,10,1)) # oct 1st
assert_equal Time.zone.local(2009,3,29), due_date # march 29th
end
def test_start_from_in_future
# every_day should return start_day if it is in the future
@every_day.start_from = @in_three_days
due_date = @every_day.get_due_date(nil)
assert_equal @in_three_days, due_date
due_date = @every_day.get_due_date(@tomorrow)
assert_equal @in_three_days, due_date
# if we give a date in the future for the previous todo, the next to do
# should be based on that future date.
due_date = @every_day.get_due_date(@in_four_days)
assert_equal @in_four_days+1.day, due_date
@weekly_every_day.start_from = Time.zone.local(2020,1,1)
assert_equal Time.zone.local(2020,1,1), @weekly_every_day.get_due_date(nil)
assert_equal Time.zone.local(2020,1,1), @weekly_every_day.get_due_date(Time.zone.local(2019,10,1))
assert_equal Time.zone.local(2020,1,10), @weekly_every_day.get_due_date(Time.zone.local(2020,1,9))
@monthly_every_last_friday.start_from = Time.zone.local(2020,1,1)
assert_equal Time.zone.local(2020,1,31), @monthly_every_last_friday.get_due_date(nil) # last friday of jan
assert_equal Time.zone.local(2020,1,31), @monthly_every_last_friday.get_due_date(Time.zone.local(2019,12,1)) # last friday of jan
assert_equal Time.zone.local(2020,2,28), @monthly_every_last_friday.get_due_date(Time.zone.local(2020,2,1)) # last friday of feb
# start from after june 8th 2008
@yearly.start_from = Time.zone.local(2020,6,12)
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(nil) # jun 8th next year
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(Time.zone.local(2019,6,1)) # also next year
assert_equal Time.zone.local(2021,6,8), @yearly.get_due_date(Time.zone.local(2020,6,15)) # also next year
this_year = Time.now.utc.year
@yearly.start_from = Time.zone.local(this_year+1,6,12)
due_date = @yearly.get_due_date(nil)
assert_equal due_date.year, this_year+2
end
def test_toggle_completion
t = @yearly
assert_equal :active, t.current_state
t.toggle_completion!
assert_equal :completed, t.current_state
t.toggle_completion!
assert_equal :active, t.current_state
end
def test_starred
@yearly.tag_with("1, 2, starred")
@yearly.tags.reload
assert_equal true, @yearly.starred?
assert_equal false, @weekly_every_day.starred?
@yearly.toggle_star!
assert_equal false, @yearly.starred?
@yearly.toggle_star!
assert_equal true, @yearly.starred?
end
def test_occurence_count
@every_day.number_of_occurences = 2
assert_equal true, @every_day.has_next_todo(@in_three_days)
@every_day.inc_occurences
assert_equal true, @every_day.has_next_todo(@in_three_days)
@every_day.inc_occurences
assert_equal false, @every_day.has_next_todo(@in_three_days)
# after completion, when you reactivate the recurring todo, the occurences
# count should be reset
assert_equal 2, @every_day.occurences_count
@every_day.toggle_completion!
@every_day.toggle_completion!
assert_equal true, @every_day.has_next_todo(@in_three_days)
assert_equal 0, @every_day.occurences_count
end
end

View file

@ -1,181 +1,181 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'date'
class TodoTest < Test::Rails::TestCase
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings
def setup
@not_completed1 = Todo.find(1).reload
@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
assert_equal 1, @not_completed1.id
assert_equal 1, @not_completed1.context_id
assert_equal 2, @not_completed1.project_id
assert_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
assert_nil @not_completed1.notes
assert @not_completed1.completed? == false
assert_equal 1.week.ago.beginning_of_day.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
assert_equal 2.week.from_now.beginning_of_day.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
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?
@completed.toggle_completion!
assert @completed.active?
assert_nil @completed.completed_at
end
# Validation tests
#
def test_validate_presence_of_description
assert_equal "Call dinosaur exterminator", @not_completed2.description
@not_completed2.description = ""
assert !@not_completed2.save
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)
assert !@not_completed2.save
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)
assert !@not_completed2.save
assert_equal 1, @not_completed2.errors.count
assert_equal "is too long (maximum is 60000 characters)", @not_completed2.errors.on(:notes)
end
def test_validate_show_from_must_be_a_date_in_the_future
t = @not_completed2
t[:show_from] = 1.week.ago # we have to set this via the indexer because show_from=() updates the state
# and actual show_from value appropriately based on the date
assert !t.save
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.current_state
@not_completed2.show_from = next_week
assert @not_completed2.save, "should have saved successfully" + @not_completed2.errors.to_xml
assert_equal :deferred, @not_completed2.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'
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.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')
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.current_state
end
def test_feed_options
opts = Todo.feed_options(users(:admin_user))
assert_equal 'Tracks Actions', opts[:title], 'Unexpected value for :title key of feed_options'
assert_equal 'Actions for Admin Schmadmin', opts[:description], 'Unexpected value for :description key of feed_options'
end
def test_toggle_completion
t = @not_completed1
assert_equal :active, t.current_state
t.toggle_completion!
assert_equal :completed, t.current_state
t.toggle_completion!
assert_equal :active, t.current_state
end
def test_activate_also_saves
t = @not_completed1
t.show_from = 1.week.from_now
t.save!
assert t.deferred?
t.reload
t.activate!
assert t.active?
t.reload
assert t.active?
end
def test_project_returns_null_object_when_nil
t = @not_completed1
assert !t.project.is_a?(NullProject)
t.project = nil
assert t.project.is_a?(NullProject)
end
def test_initial_state_defaults_to_active
t = Todo.new
t.description = 'foo'
t.context_id = 1
t.save!
t.reload
assert_equal :active, t.current_state
end
def test_initial_state_is_deferred_when_show_from_in_future
t = Todo.new
t.user = users(:admin_user)
t.description = 'foo'
t.context_id = 1
t.show_from = 1.week.from_now.to_date
t.save!
t.reload
assert_equal :deferred, t.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?
end
def test_todo_is_starred_after_toggle_starred
@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!
assert !@not_completed1.starred?
end
end
require File.dirname(__FILE__) + '/../test_helper'
require 'date'
class TodoTest < Test::Rails::TestCase
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings
def setup
@not_completed1 = Todo.find(1).reload
@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
assert_equal 1, @not_completed1.id
assert_equal 1, @not_completed1.context_id
assert_equal 2, @not_completed1.project_id
assert_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
assert_nil @not_completed1.notes
assert @not_completed1.completed? == false
assert_equal 1.week.ago.beginning_of_day.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
assert_equal 2.week.from_now.beginning_of_day.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
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?
@completed.toggle_completion!
assert @completed.active?
assert_nil @completed.completed_at
end
# Validation tests
#
def test_validate_presence_of_description
assert_equal "Call dinosaur exterminator", @not_completed2.description
@not_completed2.description = ""
assert !@not_completed2.save
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)
assert !@not_completed2.save
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)
assert !@not_completed2.save
assert_equal 1, @not_completed2.errors.count
assert_equal "is too long (maximum is 60000 characters)", @not_completed2.errors.on(:notes)
end
def test_validate_show_from_must_be_a_date_in_the_future
t = @not_completed2
t[:show_from] = 1.week.ago # we have to set this via the indexer because show_from=() updates the state
# and actual show_from value appropriately based on the date
assert !t.save
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.current_state
@not_completed2.show_from = next_week
assert @not_completed2.save, "should have saved successfully" + @not_completed2.errors.to_xml
assert_equal :deferred, @not_completed2.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'
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.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')
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.current_state
end
def test_feed_options
opts = Todo.feed_options(users(:admin_user))
assert_equal 'Tracks Actions', opts[:title], 'Unexpected value for :title key of feed_options'
assert_equal 'Actions for Admin Schmadmin', opts[:description], 'Unexpected value for :description key of feed_options'
end
def test_toggle_completion
t = @not_completed1
assert_equal :active, t.current_state
t.toggle_completion!
assert_equal :completed, t.current_state
t.toggle_completion!
assert_equal :active, t.current_state
end
def test_activate_also_saves
t = @not_completed1
t.show_from = 1.week.from_now
t.save!
assert t.deferred?
t.reload
t.activate!
assert t.active?
t.reload
assert t.active?
end
def test_project_returns_null_object_when_nil
t = @not_completed1
assert !t.project.is_a?(NullProject)
t.project = nil
assert t.project.is_a?(NullProject)
end
def test_initial_state_defaults_to_active
t = Todo.new
t.description = 'foo'
t.context_id = 1
t.save!
t.reload
assert_equal :active, t.current_state
end
def test_initial_state_is_deferred_when_show_from_in_future
t = Todo.new
t.user = users(:admin_user)
t.description = 'foo'
t.context_id = 1
t.show_from = 1.week.from_now.to_date
t.save!
t.reload
assert_equal :deferred, t.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?
end
def test_todo_is_starred_after_toggle_starred
@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!
assert !@not_completed1.starred?
end
end

View file

@ -1,3 +1,3 @@
Dir[File.join(File.dirname(__FILE__), "core", "*.rb")].each do |file|
require File.expand_path(file)
%w{field form label link logging page select_option session}.each do |file|
require File.dirname(__FILE__) + "/core/#{file}"
end