mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-29 21:38:49 +01:00
Merge branch 'master' of git://github.com/bsag/tracks
This commit is contained in:
commit
1769c4b4f4
39 changed files with 2554 additions and 3688 deletions
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -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
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "doc/manual"]
|
||||
path = doc/manual
|
||||
url = git://github.com/bsag/tracks_manual.git
|
||||
2
Capfile
Normal file
2
Capfile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
|
||||
load 'config/deploy'
|
||||
14
README
14
README
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 -%>
|
||||
|
||||
|
|
|
|||
|
|
@ -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='';" %>
|
||||
|
|
|
|||
|
|
@ -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
101
config/deploy.rb-example
Normal 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
|
||||
|
|
@ -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
|
||||
9
db/migrate/045_remove_user_from_taggings.rb.rb
Normal file
9
db/migrate/045_remove_user_from_taggings.rb.rb
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
1
doc/manual
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d3746adc39333f056f14969d40aa1f0072eb4eed
|
||||
|
|
@ -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;}
|
||||
281
doc/manual.html
281
doc/manual.html
|
|
@ -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’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’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’d like to install Tracks on a local machine, try <a href="http://bitnami.org/stack/tracks">BitNami</a> – it runs on Windows, Mac OS X and Linux.</p>
|
||||
|
||||
<p>If you’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 ‘Subscriptions’ 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’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.</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’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’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’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 “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</li>
|
||||
<li>If you are using Windows, you may need to check the ‘shebang’ 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 – you did back up your database didn’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 – you did back up your database didn’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’s very important that you <strong>back up your database</strong> 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 <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] > [dump file]</code></p>
|
||||
|
||||
<p>Rename your old Tracks installation (e.g. to ‘tracks-old’) 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 ‘shebang’ 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’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’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’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 ‘tracks-old’ 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 ‘tracks-old’ 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 ‘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.</li>
|
||||
<li>In <code>database.yml</code> 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 <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 ‘done’ 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 ‘development’ and ‘production’ 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 ‘shebang’ 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’t have <code>env</code> installed, you’ll need to change this line to point to the location of your Ruby binary.<a href="#fnref:env" class="reversefootnote"> ↩</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’t have <code>env</code> installed, you’ll need to change this line to point to the location of your Ruby binary.<a href="#fnref:env" class="reversefootnote"> ↩</a></p></li>
|
||||
|
||||
</ol>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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.
|
||||
BIN
doc/manual.pdf
BIN
doc/manual.pdf
Binary file not shown.
668
doc/manual.tex
668
doc/manual.tex
|
|
@ -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}
|
||||
|
|
@ -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
27
lib/tasks/database.rake
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
8
spec/views/login/login_mobile.html.erb_spec.rb
Normal file
8
spec/views/login/login_mobile.html.erb_spec.rb
Normal 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
|
||||
6
test/fixtures/taggings.yml
vendored
6
test/fixtures/taggings.yml
vendored
|
|
@ -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
|
||||
|
|
@ -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+ 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+ 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 !(/ /.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+ 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+ 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 !(/ /.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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 !(/ /.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 !(/ /.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 !(/ /.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 !(/ /.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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
4
vendor/plugins/webrat/lib/webrat/core.rb
vendored
4
vendor/plugins/webrat/lib/webrat/core.rb
vendored
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue