commiting before i fallasleep

This commit is contained in:
Scott Davis 2010-03-05 01:48:00 -05:00
parent 6d31b3e4b5
commit a950d216ec
42 changed files with 498 additions and 70 deletions

0
Project, Normal file
View File

0
Task, Normal file
View File

View File

@ -2,6 +2,8 @@
# Likewise, all the methods added will be available for all controllers.
class ApplicationController < ActionController::Base
before_filter :authenticate
include Clearance::Authentication
#helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details

View File

@ -1,8 +1,9 @@
class ProjectsController < ApplicationController
before_filter :authenticate
# GET /projects
# GET /projects.xml
def index
@projects = Project.all
@projects = Project.active.paginate :per_page => 30, :page => params[:page]
respond_to do |format|
format.html # index.html.erb
@ -13,8 +14,8 @@ class ProjectsController < ApplicationController
# GET /projects/1
# GET /projects/1.xml
def show
@project = Project.find(params[:id])
@project = Project.find(params[:id], :include => [:users])
@tasks = @project.tasks.parents.paginate(:per_page => 30, :page => params[:page], :include => :tasks)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @project }

View File

@ -0,0 +1,25 @@
class UsersController < Clearance::UsersController
def dashboard
@user = current_user
@projects = @user.projects.active.paginate(:per_page => 30, :page => params[:page])
end
def show
@user = User.find_by_short_name(params[:id])
end
def inline
user = current_user
old = user.send(params[:id])
user.update_attribute(params[:id], params['value'])
if user.save
value = user.send(params[:id])
else
value = old
end
render :text => value , :layout => false
end
end

View File

@ -7,4 +7,5 @@ module ApplicationHelper
out << tag(:meta, :name => 'csrf-param', :content => 'authenticity_token')
end
end

View File

@ -1,4 +1,11 @@
class Project < ActiveRecord::Base
has_many :tasks
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
validates_presence_of :name
has_and_belongs_to_many :users
validates_uniqueness_of :name
named_scope :archived, :conditions => {:archived => true}
named_scope :active, :conditions => {:archived => false}
end

View File

@ -1,9 +1,31 @@
class Task < ActiveRecord::Base
belongs_to :project
has_many :tasks, :foreign_key => :parent
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
belongs_to :assignee, :class_name => "User", :foreign_key => 'assigned_id'
#named scopes
named_scope :parents, :conditions => {:parent => nil}
#validations
validates_presence_of :name, :description, :project_id
validates_associated :project
validate :assignee_validation, :owner_validation
def assignee_validation
unless assigned_id.blank?
unless User.exists?(assigned_id)
errors.add :assignee, "Assignee doesn't exist"
end
end
end
def owner_validation
unless owner_id.blank?
unless User.exists?(owner_id)
errors.add :owner, "Owner doesn't exist"
end
end
end
end

17
app/models/user.rb Normal file
View File

@ -0,0 +1,17 @@
class User < ActiveRecord::Base
include Clearance::User
has_and_belongs_to_many :projects
has_many :owned_projects, :class_name => "Project", :foreign_key => "owner_id"
has_many :owned_tasks, :class_name => "Task", :foreign_key => "owner_id"
has_many :assigned_tasks, :class_name => "Task", :foreign_key => "assigned_id"
validates_uniqueness_of :short_name
validates_presence_of :short_name
validates_format_of :phone, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.phone.blank?}
validates_format_of :mobile, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.mobile.blank?}
def gravatar(size = 48)
hash = Digest::MD5.hexdigest email
"http://www.gravatar.com/avatar/#{hash}.jpg?s=#{size}"
end
end

View File

@ -7,13 +7,18 @@
<%= stylesheet_link_tag 'scaffold', 'style' %>
</head>
<body>
<div id='header'>
Collab
</div>
<% flash.each_key do |flash_key| %>
<div id='flash_<%= flash_key.to_s %>'>
<%= flash[flash_key] %>
<div id='flash_<%=h flash_key.to_s %>' class='flash'>
<%=h flash[flash_key] %>
</div>
<% end %>
<div id='wrapper'>
<div id='content'>
<%= yield %>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<h2>Change your password</h2>
<p>
Your password has been reset. Choose a new password below.
</p>
<% semantic_form_for(:user,
:url => user_password_path(@user, :token => @user.confirmation_token),
:html => { :method => :put }) do |form| %>
<%= form.error_messages %>
<% form.inputs do -%>
<%= form.input :password, :as => :password,
:label => "Choose password" %>
<%= form.input :password_confirmation, :as => :password,
:label => "Confirm password" %>
<% end -%>
<% form.buttons do -%>
<%= form.commit_button "Save this password" %>
<% end -%>
<% end %>

View File

@ -0,0 +1,15 @@
<h2>Reset your password</h2>
<p>
We will email you a link to reset your password.
</p>
<% semantic_form_for :password, :url => passwords_path do |form| -%>
<% form.inputs do -%>
<%= form.input :email, :label => "Email address" %>
<% end -%>
<% form.buttons do -%>
<%= form.commit_button "Reset password" %>
<% end -%>
<% end -%>

View File

@ -8,7 +8,7 @@
<% @projects.each do |project| %>
<tr>
<td><%=h project.name %></td>
<td><%= link_to 'Show', project %></td>
<td><%= link_to 'Show', project_path(project) %></td>
<td><%= link_to 'Edit', edit_project_path(project) %></td>
<td><%= link_to 'Destroy', project, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
@ -17,4 +17,6 @@
<br />
<%= will_paginate @projects %>
<%= link_to 'New project', new_project_path %>

View File

@ -2,7 +2,14 @@
<b>Name:</b>
<%=h @project.name %>
</p>
<div id='project_users'>
<% @project.users.each do |user| %>
<%= image_tag user.gravatar %> <%= link_to "@#{user.short_name}", user_path(user.short_name) %>
<% end %>
</div>
<div id='project_tasks'>
<%= render :partial => 'tasks/task_list' %>
</div>
<%= will_paginate @tasks %>
<%= link_to 'Edit', edit_project_path(@project) %> |
<%= link_to 'Back', projects_path %>

View File

@ -0,0 +1,21 @@
<h2>Sign in</h2>
<% semantic_form_for :session, :url => session_path do |form| %>
<% form.inputs do %>
<%= form.input :email %>
<%= form.input :password, :as => :password %>
<% end %>
<% form.buttons do %>
<%= form.commit_button "Sign in" %>
<% end %>
<% end %>
<ul>
<li>
<%= link_to "Sign up", sign_up_path %>
</li>
<li>
<%= link_to "Forgot password?", new_password_path %>
</li>
</ul>

View File

@ -21,5 +21,4 @@
</table>
<% else %>
<p> No Dependet Tasks </p>
<p><%= link_to 'Create One', new_project_task_path(@project, :parent => @task.id) %>
<% end %>

View File

@ -0,0 +1,7 @@
<% form.inputs do %>
<%= form.input :email %>
<%= form.input :password %>
<%= form.input :password_confirmation, :label => "Confirm password" %>
<%= form.input :short_name %>
<% end %>

View File

@ -0,0 +1,18 @@
<%= javascript_include_tag 'jeditable.min'%>
<script type='text/javascript'>
$(document).ready(function() {
$('#phone').editable('<%= user_inline_edit_path('phone') %>', {col:'phone'});
$('#mobile').editable('<%= user_inline_edit_path('mobile') %>', {col:'phone'});
$('#im').editable('<%= user_inline_edit_path('im') %>', {col:'phone'});
});
</script>
<h1>Dashboard</h1>
<%= image_tag @user.gravatar %>
<div id='dashboard_user_info'>
<% [:phone, :mobile, :im].each do |col| %>
<p><%= col.to_s %>: <span id='<%= col.to_s %>'><%=h @user.send(col) %></span></p>
<% end %>
</div>
<% @projects.each do |project| %>
<p><%= link_to project.name, project %></p>
<% end %>

View File

@ -0,0 +1,10 @@
<h2>Sign up</h2>
<% semantic_form_for @user do |form| %>
<%= form.error_messages %>
<%= render :partial => "/users/inputs", :locals => { :form => form } %>
<% form.buttons do %>
<%= form.commit_button "Sign up" %>
<% end %>
<% end %>

View File

@ -0,0 +1 @@
<h1>User Activity</h1>

0
assigned_id Normal file
View File

View File

@ -25,8 +25,8 @@ Rails::Initializer.run do |config|
config.gem 'paperclip'
config.gem 'formtastic'
config.gem 'faker'
config.gem 'postgres'
config.gem 'mysql'
config.gem "matthuhiggins-foreigner", :lib => "foreigner", :source => "http://gemcutter.org"
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named

View File

@ -15,3 +15,5 @@ config.action_controller.perform_caching = false
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
config.action_mailer.default_url_options = { :host => 'localhost:3000' }

View File

@ -26,3 +26,5 @@ config.action_mailer.delivery_method = :test
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
config.action_mailer.default_url_options = { :host => 'localhost:3000' }

View File

@ -0,0 +1,3 @@
Clearance.configure do |config|
config.mailer_sender = 'donotreply@example.com'
end

View File

@ -1,39 +1,9 @@
ActionController::Routing::Routes.draw do |map|
# The priority is based upon order of creation: first created -> highest priority.
Clearance::Routes.draw(map)
map.user '/users/:id', :controller => :users, :action => :show
map.user_inline_edit '/users/:id/inline', :controller => :users, :action => :inline, :conditions => { :method => :post }
#map.dashboard '/dashboard', :controller => :users, :action => :dashboard
# Sample of regular route:
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
# map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
# This route can be invoked with purchase_url(:id => product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# map.resources :products
# Sample resource route with options:
# map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
# Sample resource route with sub-resources:
# map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
# Sample resource route with more complex sub-resources
# map.resources :products do |products|
# products.resources :comments
# products.resources :sales, :collection => { :recent => :get }
# end
# Sample resource route within a namespace:
# map.namespace :admin do |admin|
# # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
# admin.resources :products
# end
# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
# map.root :controller => "welcome"
# See how all your routes lay out with "rake routes"
map.resources :projects do |projects|
projects.resources :tasks
@ -45,5 +15,5 @@ ActionController::Routing::Routes.draw do |map|
# consider removing or commenting them out if you're using named routes and resources.
#map.connect ':controller/:action/:id'
#map.connect ':controller/:action/:id.:format'
map.root :controller => :projects, :action =>:index
map.root :controller => :users, :action => :dashboard
end

View File

@ -0,0 +1,21 @@
class ClearanceCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.string :email
t.string :encrypted_password, :limit => 128
t.string :salt, :limit => 128
t.string :confirmation_token, :limit => 128
t.string :remember_token, :limit => 128
t.boolean :email_confirmed, :default => false, :null => false
t.timestamps
end
add_index :users, [:id, :confirmation_token]
add_index :users, :email
add_index :users, :remember_token
end
def self.down
drop_table :users
end
end

View File

@ -0,0 +1,15 @@
class ProjectsUsers < ActiveRecord::Migration
def self.up
create_table :projects_users, :id => false do |t|
t.integer :project_id
t.integer :user_id
t.foreign_key :projects
t.foreign_key :users
t.index :project_id, :user_id
end
end
def self.down
drop_table :projects_users
end
end

View File

@ -0,0 +1,9 @@
class AddShornameToUsers < ActiveRecord::Migration
def self.up
add_column :users, :short_name, :string
end
def self.down
remove_column :users, :short_name
end
end

View File

@ -0,0 +1,21 @@
class AddOwnerToTasksAndProjects < ActiveRecord::Migration
@tables = [:tasks, :projects]
def self.up
@tables.each do |table|
add_column table, :owner_id, :integer
add_index table, :owner_id
add_foreign_key table, :users, :column => :owner_id
end
add_column :tasks, :assigned_id, :integer
add_index :tasks, :assigned_id
end
def self.down
@tables.each do |t|
remove_column t, :owner_id
remove_foreign_key t, :column => :owner_id
end
remove_column :tasks, :assigned_id
end
end

View File

@ -0,0 +1,9 @@
class AddArchivedToProjects < ActiveRecord::Migration
def self.up
add_column :projects, :archived, :boolean, :default => false
end
def self.down
remove_column :projects, :archived
end
end

View File

@ -0,0 +1,13 @@
class AddContactInfoToUsers < ActiveRecord::Migration
def self.up
add_column :users, :phone, :string
add_column :users, :mobile, :string
add_column :users, :im, :string
end
def self.down
remove_column :users, :im
remove_column :users, :mobile
remove_column :users, :phone
end
end

View File

@ -9,12 +9,21 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20100304040201) do
ActiveRecord::Schema.define(:version => 20100305060621) do
create_table "projects", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "owner_id"
t.boolean "archived", :default => false
end
add_index "projects", ["owner_id"], :name => "index_projects_on_owner_id"
create_table "projects_users", :id => false, :force => true do |t|
t.integer "project_id"
t.integer "user_id"
end
create_table "tasks", :force => true do |t|
@ -24,6 +33,34 @@ ActiveRecord::Schema.define(:version => 20100304040201) do
t.integer "parent"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "owner_id"
t.integer "assigned_id"
end
add_index "tasks", ["assigned_id"], :name => "index_tasks_on_assigned_id"
add_index "tasks", ["owner_id"], :name => "index_tasks_on_owner_id"
create_table "users", :force => true do |t|
t.string "email"
t.string "encrypted_password", :limit => 128
t.string "salt", :limit => 128
t.string "confirmation_token", :limit => 128
t.string "remember_token", :limit => 128
t.boolean "email_confirmed", :default => false, :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.string "short_name"
t.string "phone"
t.string "mobile"
t.string "im"
end
add_index "users", ["email"], :name => "index_users_on_email"
add_index "users", ["id", "confirmation_token"], :name => "index_users_on_id_and_confirmation_token"
add_index "users", ["remember_token"], :name => "index_users_on_remember_token"
add_foreign_key "projects", "users", :name => "projects_owner_id_fk", :column => "owner_id"
add_foreign_key "tasks", "users", :name => "tasks_owner_id_fk", :column => "owner_id"
end

View File

@ -1,32 +1,46 @@
path = File.join(RAILS_ROOT, 'factories')
require 'factory_girl'
require File.join(path, 'task_factory')
require File.join(path, 'project_factory')
["user", "task", "project"].each {|factory| require File.join(path, "#{factory}_factory")}
class FactoryLoader
def self.up
self.create_users
self.create_projects
puts "Factories Loaded!"
end
def self.down
[Task, Project].each {|klass| klass.delete_all}
[Task, Project, User].each(&:delete_all)
puts "Factories Unloaded!"
end
def self.create_users
5.times do |n|
u = Factory.create(:user)
u.confirm_email!
end
end
def self.create_projects
101.times do |i|
p = Factory.create(:project)
self.create_tasks(p)
users = User.all
users.each do |user|
10.times do |i|
p = Factory.create(:project, :owner => user, :users => [user])
self.create_tasks(p, user)
end
end
end
def self.create_tasks(project)
def self.create_tasks(project, user)
5.times do |i|
task = Factory.create(:task, :project => project)
task = Factory.create(:task, :project => project, :owner => user, :assignee => user)
5.times do |n|
t = Factory.create(:task, :project => project, :parent => task.id)
t = Factory.create(:task, :project => project, :parent => task.id, :owner => user, :assignee => user)
2.times do |nn|
Factory.create(:task, :project => project, :parent => t.id)
Factory.create(:task, :project => project, :parent => t.id, :owner => user, :assignee => user)
end
end
end

View File

@ -0,0 +1,8 @@
require 'factory_girl'
Factory.define :user do |f|
f.sequence(:email) { |n| "email_#{n}@localhost.com" }
f.sequence(:password) { |n| "password#{n}" }
f.sequence(:short_name) { |n| "short_#{n}" }
end

18
lib/tasks/factory.rake Normal file
View File

@ -0,0 +1,18 @@
namespace :db do
namespace :factory do
desc "Load the factory data"
task :load => [:environment, :load_class] do
FactoryLoader.up
end
desc "Unload all the stories"
task :unload => [:environment, :load_class] do
FactoryLoader.down
end
desc "Reload Factories"
task :reload => [:environment, :load_class, :unload, :load]
task :load_class do
require File.join(RAILS_ROOT, 'factories', 'factory_loader')
end
end
end

0
owner_id Normal file
View File

37
public/javascripts/jeditable.min.js vendored Normal file
View File

@ -0,0 +1,37 @@
(function($){$.fn.editable=function(target,options){if('disable'==target){$(this).data('disabled.editable',true);return;}
if('enable'==target){$(this).data('disabled.editable',false);return;}
if('destroy'==target){$(this).unbind($(this).data('event.editable')).removeData('disabled.editable').removeData('event.editable');return;}
var settings=$.extend({},$.fn.editable.defaults,{target:target},options);var plugin=$.editable.types[settings.type].plugin||function(){};var submit=$.editable.types[settings.type].submit||function(){};var buttons=$.editable.types[settings.type].buttons||$.editable.types['defaults'].buttons;var content=$.editable.types[settings.type].content||$.editable.types['defaults'].content;var element=$.editable.types[settings.type].element||$.editable.types['defaults'].element;var reset=$.editable.types[settings.type].reset||$.editable.types['defaults'].reset;var callback=settings.callback||function(){};var onedit=settings.onedit||function(){};var onsubmit=settings.onsubmit||function(){};var onreset=settings.onreset||function(){};var onerror=settings.onerror||reset;if(settings.tooltip){$(this).attr('title',settings.tooltip);}
settings.autowidth='auto'==settings.width;settings.autoheight='auto'==settings.height;return this.each(function(){var self=this;var savedwidth=$(self).width();var savedheight=$(self).height();$(this).data('event.editable',settings.event);if(!$.trim($(this).html())){$(this).html(settings.placeholder);}
$(this).bind(settings.event,function(e){if(true===$(this).data('disabled.editable')){return;}
if(self.editing){return;}
if(false===onedit.apply(this,[settings,self])){return;}
e.preventDefault();e.stopPropagation();if(settings.tooltip){$(self).removeAttr('title');}
if(0==$(self).width()){settings.width=savedwidth;settings.height=savedheight;}else{if(settings.width!='none'){settings.width=settings.autowidth?$(self).width():settings.width;}
if(settings.height!='none'){settings.height=settings.autoheight?$(self).height():settings.height;}}
if($(this).html().toLowerCase().replace(/(;|")/g,'')==settings.placeholder.toLowerCase().replace(/(;|")/g,'')){$(this).html('');}
self.editing=true;self.revert=$(self).html();$(self).html('');var form=$('<form />');if(settings.cssclass){if('inherit'==settings.cssclass){form.attr('class',$(self).attr('class'));}else{form.attr('class',settings.cssclass);}}
if(settings.style){if('inherit'==settings.style){form.attr('style',$(self).attr('style'));form.css('display',$(self).css('display'));}else{form.attr('style',settings.style);}}
var input=element.apply(form,[settings,self]);var input_content;if(settings.loadurl){var t=setTimeout(function(){input.disabled=true;content.apply(form,[settings.loadtext,settings,self]);},100);var loaddata={};loaddata[settings.id]=self.id;if($.isFunction(settings.loaddata)){$.extend(loaddata,settings.loaddata.apply(self,[self.revert,settings]));}else{$.extend(loaddata,settings.loaddata);}
$.ajax({type:settings.loadtype,url:settings.loadurl,data:loaddata,async:false,success:function(result){window.clearTimeout(t);input_content=result;input.disabled=false;}});}else if(settings.data){input_content=settings.data;if($.isFunction(settings.data)){input_content=settings.data.apply(self,[self.revert,settings]);}}else{input_content=self.revert;}
content.apply(form,[input_content,settings,self]);input.attr('name',settings.name);buttons.apply(form,[settings,self]);$(self).append(form);plugin.apply(form,[settings,self]);$(':input:visible:enabled:first',form).focus();if(settings.select){input.select();}
input.keydown(function(e){if(e.keyCode==27){e.preventDefault();reset.apply(form,[settings,self]);}});var t;if('cancel'==settings.onblur){input.blur(function(e){t=setTimeout(function(){reset.apply(form,[settings,self]);},500);});}else if('submit'==settings.onblur){input.blur(function(e){t=setTimeout(function(){form.submit();},200);});}else if($.isFunction(settings.onblur)){input.blur(function(e){settings.onblur.apply(self,[input.val(),settings]);});}else{input.blur(function(e){});}
form.submit(function(e){if(t){clearTimeout(t);}
e.preventDefault();if(false!==onsubmit.apply(form,[settings,self])){if(false!==submit.apply(form,[settings,self])){if($.isFunction(settings.target)){var str=settings.target.apply(self,[input.val(),settings]);$(self).html(str);self.editing=false;callback.apply(self,[self.innerHTML,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}}else{var submitdata={};submitdata[settings.name]=input.val();submitdata[settings.id]=self.id;if($.isFunction(settings.submitdata)){$.extend(submitdata,settings.submitdata.apply(self,[self.revert,settings]));}else{$.extend(submitdata,settings.submitdata);}
if('PUT'==settings.method){submitdata['_method']='put';}
$(self).html(settings.indicator);var ajaxoptions={type:'POST',data:submitdata,dataType:'html',url:settings.target,success:function(result,status){if(ajaxoptions.dataType=='html'){$(self).html(result);}
self.editing=false;callback.apply(self,[result,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}},error:function(xhr,status,error){onerror.apply(form,[settings,self,xhr]);}};$.extend(ajaxoptions,settings.ajaxoptions);$.ajax(ajaxoptions);}}}
$(self).attr('title',settings.tooltip);return false;});});this.reset=function(form){if(this.editing){if(false!==onreset.apply(form,[settings,self])){$(self).html(self.revert);self.editing=false;if(!$.trim($(self).html())){$(self).html(settings.placeholder);}
if(settings.tooltip){$(self).attr('title',settings.tooltip);}}}};});};$.editable={types:{defaults:{element:function(settings,original){var input=$('<input type="hidden"></input>');$(this).append(input);return(input);},content:function(string,settings,original){$(':input:first',this).val(string);},reset:function(settings,original){original.reset(this);},buttons:function(settings,original){var form=this;if(settings.submit){if(settings.submit.match(/>$/)){var submit=$(settings.submit).click(function(){if(submit.attr("type")!="submit"){form.submit();}});}else{var submit=$('<button type="submit" />');submit.html(settings.submit);}
$(this).append(submit);}
if(settings.cancel){if(settings.cancel.match(/>$/)){var cancel=$(settings.cancel);}else{var cancel=$('<button type="cancel" />');cancel.html(settings.cancel);}
$(this).append(cancel);$(cancel).click(function(event){if($.isFunction($.editable.types[settings.type].reset)){var reset=$.editable.types[settings.type].reset;}else{var reset=$.editable.types['defaults'].reset;}
reset.apply(form,[settings,original]);return false;});}}},text:{element:function(settings,original){var input=$('<input />');if(settings.width!='none'){input.width(settings.width);}
if(settings.height!='none'){input.height(settings.height);}
input.attr('autocomplete','off');$(this).append(input);return(input);}},textarea:{element:function(settings,original){var textarea=$('<textarea />');if(settings.rows){textarea.attr('rows',settings.rows);}else if(settings.height!="none"){textarea.height(settings.height);}
if(settings.cols){textarea.attr('cols',settings.cols);}else if(settings.width!="none"){textarea.width(settings.width);}
$(this).append(textarea);return(textarea);}},select:{element:function(settings,original){var select=$('<select />');$(this).append(select);return(select);},content:function(data,settings,original){if(String==data.constructor){eval('var json = '+data);}else{var json=data;}
for(var key in json){if(!json.hasOwnProperty(key)){continue;}
if('selected'==key){continue;}
var option=$('<option />').val(key).append(json[key]);$('select',this).append(option);}
$('select',this).children().each(function(){if($(this).val()==json['selected']||$(this).text()==$.trim(original.revert)){$(this).attr('selected','selected');}});}}},addInputType:function(name,input){$.editable.types[name]=input;}};$.fn.editable.defaults={name:'value',id:'id',type:'text',width:'auto',height:'auto',event:'click.editable',onblur:'cancel',loadtype:'GET',loadtext:'Loading...',placeholder:'Click to edit',loaddata:{},submitdata:{},ajaxoptions:{}};})(jQuery);

View File

@ -2,16 +2,29 @@
margin:0;
padding:0;
}
#flash_error {
div.flash {
width:1000px;
margin-left:auto;
margin-right:auto;
padding:5px;
background-color:white;
margin-top:20px;
}
div#flash_error {
color:red;
border:red 5px solid;
}
#flash_notice {
div#flash_notice {
color:blue;
border:blue 5px solid;
}
#flash_success {
div#flash_success {
color:green;
border:green 5px solid;
}
@ -22,3 +35,23 @@ table.task_list tr.odd{
table.task_list tr.even{
}
body {
background-color:gray;
}
div#header {
width:1000px;
margin-left:auto;
margin-right:auto;
font-size:24;
font-weight:bold;
}
div#wrapper {
margin-left:auto;
margin-right:auto;
width:1000px;
margin-top:30px;
background-color:white;
}

View File

@ -0,0 +1,13 @@
Factory.sequence :email do |n|
"user#{n}@example.com"
end
Factory.define :user do |user|
user.email { Factory.next :email }
user.password { "password" }
user.password_confirmation { "password" }
end
Factory.define :email_confirmed_user, :parent => :user do |user|
user.email_confirmed { true }
end

View File

@ -1,6 +1,8 @@
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'test_help'
require 'factory_girl'
require 'factories/factory_loader'
class ActiveSupport::TestCase
# Transactional fixtures accelerate your tests by wrapping each test method
@ -32,7 +34,7 @@ class ActiveSupport::TestCase
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
#fixtures :all
# Add more helper methods to be used by all tests here...
end

View File

@ -1,8 +1,28 @@
require 'test_helper'
class TaskTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
test "Assignee Fails User Doesn't exist" do
project = Factory.create(:project)
task = Factory.create(:task, :project => project)
task.assigned_id = 12000
task.save
assert !task.valid?
assert_equal "Assignee doesn't exist", task.errors.first.last
end
test "Owner Fails User Doesn't exist" do
project = Factory.create(:project)
task = Factory.create(:task, :project => project)
task.owner_id = 12000
task.save
assert !task.valid?
assert_equal "Owner doesn't exist", task.errors.first.last
end
end