mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-27 20:38:48 +01:00
I'm not sure the test failures caused by reload not working are caused by the new aasm, perhaps the thread isolation is causing that...
172 lines
4.1 KiB
Ruby
172 lines
4.1 KiB
Ruby
class Project < ActiveRecord::Base
|
|
has_many :todos, :dependent => :delete_all, :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC'
|
|
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
|
has_many :recurring_todos
|
|
|
|
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
|
belongs_to :user
|
|
|
|
scope :active, :conditions => { :state => 'active' }
|
|
scope :hidden, :conditions => { :state => 'hidden' }
|
|
scope :completed, :conditions => { :state => 'completed'}
|
|
scope :uncompleted, :conditions => ["NOT(state = ?)", 'completed']
|
|
|
|
scope :with_name_or_description, lambda { |body| where("name LIKE ? OR description LIKE ?", body, body) }
|
|
|
|
validates_presence_of :name
|
|
validates_length_of :name, :maximum => 255
|
|
validates_uniqueness_of :name, :scope => "user_id"
|
|
|
|
acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\'', :top_of_list => 0
|
|
|
|
include AASM
|
|
|
|
aasm :column => :state do
|
|
|
|
state :active, :initial => true
|
|
state :hidden, :enter => :hide_todos, :exit => :unhide_todos
|
|
state :completed, :enter => :set_completed_at_date, :exit => :clear_completed_at_date
|
|
|
|
event :activate do
|
|
transitions :to => :active, :from => [:active, :hidden, :completed]
|
|
end
|
|
|
|
event :hide do
|
|
transitions :to => :hidden, :from => [:active, :completed]
|
|
end
|
|
|
|
event :complete do
|
|
transitions :to => :completed, :from => [:active, :hidden]
|
|
end
|
|
end
|
|
|
|
attr_protected :user
|
|
attr_accessor :cached_note_count
|
|
|
|
def self.null_object
|
|
NullProject.new
|
|
end
|
|
|
|
def self.create_from_todo(todo)
|
|
project = Project.new(:name => todo.description,
|
|
:description => todo.notes,
|
|
:default_context => todo.context)
|
|
|
|
project.user = todo.user
|
|
|
|
if project.valid?
|
|
todo.destroy
|
|
project.save!
|
|
end
|
|
|
|
project
|
|
end
|
|
|
|
def hide_todos
|
|
todos.each do |t|
|
|
unless t.completed? || t.deferred?
|
|
t.hide!
|
|
t.save
|
|
end
|
|
end
|
|
end
|
|
|
|
def unhide_todos
|
|
todos.each do |t|
|
|
if t.project_hidden?
|
|
t.unhide!
|
|
t.save
|
|
end
|
|
end
|
|
end
|
|
|
|
def set_completed_at_date
|
|
self.completed_at = Time.zone.now
|
|
end
|
|
|
|
def clear_completed_at_date
|
|
self.completed_at = nil
|
|
end
|
|
|
|
def note_count
|
|
# TODO: test this for eager and not eager loading!!!
|
|
return 0 if notes.size == 0
|
|
cached_note_count || notes.count
|
|
end
|
|
|
|
alias_method :original_default_context, :default_context
|
|
|
|
def default_context
|
|
original_default_context.nil? ? Context.null_object : original_default_context
|
|
end
|
|
|
|
# would prefer to call this method state=(), but that causes an endless loop
|
|
# as a result of acts_as_state_machine calling state=() to update the attribute
|
|
def transition_to(candidate_state)
|
|
case candidate_state.to_sym
|
|
when aasm_current_state
|
|
return
|
|
when :hidden
|
|
hide!
|
|
when :active
|
|
activate!
|
|
when :completed
|
|
complete!
|
|
end
|
|
end
|
|
|
|
def needs_review?(current_user)
|
|
return active? && ( last_reviewed.nil? ||
|
|
(last_reviewed < current_user.time - current_user.prefs.review_period.days))
|
|
end
|
|
|
|
def blocked?
|
|
## mutually exclusive for stalled and blocked
|
|
# blocked is uncompleted project with deferred or pending todos, but no next actions
|
|
return false if self.completed?
|
|
return !self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
|
|
end
|
|
|
|
def stalled?
|
|
# Stalled projects are active projects with no active next actions
|
|
return false if self.completed? || self.hidden?
|
|
return self.todos.deferred_or_blocked.empty? && self.todos.active.empty?
|
|
end
|
|
|
|
def shortened_name(length=40)
|
|
name.truncate(length, :omission => "...").html_safe
|
|
end
|
|
|
|
def name=(value)
|
|
self[:name] = value.gsub(/\s{2,}/, " ").strip
|
|
end
|
|
|
|
def new_record_before_save?
|
|
@new_record_before_save
|
|
end
|
|
|
|
def age_in_days
|
|
@age_in_days ||= (Date.today - created_at.to_date + 1).to_i
|
|
end
|
|
|
|
end
|
|
|
|
class NullProject
|
|
|
|
def hidden?
|
|
false
|
|
end
|
|
|
|
def nil?
|
|
true
|
|
end
|
|
|
|
def id
|
|
nil
|
|
end
|
|
|
|
def name
|
|
""
|
|
end
|
|
|
|
end
|