Class: Todo
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Todo
- Includes:
- AASM, IsTaggable
- Defined in:
- app/models/todo.rb
Constant Summary collapse
- MAX_DESCRIPTION_LENGTH =
300- MAX_NOTES_LENGTH =
60_000- STARRED_TAG_NAME =
"starred".freeze
- DEFAULT_INCLUDES =
[:project, :context, :tags, :taggings, :pending_successors, :uncompleted_predecessors, :recurring_todo].freeze
Class Method Summary collapse
- .due_after(date) ⇒ Object
- .due_between(start_date, end_date) ⇒ Object
- .import(filename, params, user) ⇒ Object
Instance Method Summary collapse
-
#activate_pending_todos ⇒ Object
activate todos that should be activated if the current todo is completed.
- #add_predecessor(t) ⇒ Object
- #add_predecessor_list(predecessor_list) ⇒ Object
-
#add_tags=(params) ⇒ Object
used by the REST API.
-
#block_successors ⇒ Object
Return todos that should be blocked if the current todo is undone.
- #check_show_from_in_future ⇒ Object
- #context=(value) ⇒ Object
- #destroy ⇒ Object
- #from_recurring_todo? ⇒ Boolean
- #guard_for_transition_from_deferred_to_pending ⇒ Object
- #has_pending_successors ⇒ Object
- #has_project? ⇒ Boolean
- #hidden? ⇒ Boolean
-
#initialize(*args) ⇒ Todo
constructor
A new instance of Todo.
-
#is_successor?(todo) ⇒ Boolean
Returns true if t is equal to self or a successor of self.
- #no_uncompleted_predecessors? ⇒ Boolean
- #no_uncompleted_predecessors_or_deferral? ⇒ Boolean
- #not_part_of_hidden_container? ⇒ Boolean
- #original_context= ⇒ Object
- #original_project ⇒ Object
- #original_project= ⇒ Object
-
#predecessor_dependencies=(params) ⇒ Object
XML API fixups.
- #project ⇒ Object
- #project=(value) ⇒ Object
- #raw_notes=(value) ⇒ Object
-
#remove_predecessor(predecessor) ⇒ Object
remove predecessor and activate myself if it was the last predecessor.
- #removed_predecessors ⇒ Object
- #save_predecessors ⇒ Object
- #show_from ⇒ Object
- #show_from=(date) ⇒ Object
-
#specification ⇒ Object
Returns a string with description <context, project>.
- #starred=(starred) ⇒ Object
- #starred? ⇒ Boolean
- #toggle_completion! ⇒ Object
- #toggle_star! ⇒ Object
- #touch_predecessors ⇒ Object
- #uncompleted_predecessors? ⇒ Boolean
Methods included from IsTaggable
Constructor Details
#initialize(*args) ⇒ Todo
Returns a new instance of Todo.
118 119 120 121 122 |
# File 'app/models/todo.rb', line 118 def initialize(*args) super(*args) @predecessor_array = nil # Used for deferred save of predecessors @removed_predecessors = nil end |
Class Method Details
.due_after(date) ⇒ Object
59 60 61 |
# File 'app/models/todo.rb', line 59 def self.due_after(date) where('todos.due > ?', date) end |
.due_between(start_date, end_date) ⇒ Object
63 64 65 |
# File 'app/models/todo.rb', line 63 def self.due_between(start_date, end_date) where('todos.due > ? AND todos.due <= ?', start_date, end_date) end |
.import(filename, params, user) ⇒ Object
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'app/models/todo.rb', line 354 def self.import(filename, params, user) default_context = user.contexts.order('id').first return false if default_context.nil? count = 0 CSV.foreach(filename, headers: true) do |row| unless find_by_description_and_user_id row[params[:description].to_i], user.id todo = new todo.user = user todo.description = row[params[:description].to_i].truncate MAX_DESCRIPTION_LENGTH todo.context = Context.find_by_name_and_user_id(row[params[:context].to_i], user.id) || default_context todo.project = Project.find_by_name_and_user_id(row[params[:project].to_i], user.id) if row[params[:project].to_i].present? todo.state = row[params[:completed_at].to_i].present? ? 'completed' : 'active' todo.notes = row[params[:notes].to_i].truncate MAX_NOTES_LENGTH if row[params[:notes].to_i].present? todo.created_at = row[params[:created_at].to_i] if row[params[:created_at].to_i].present? todo.due = row[params[:due].to_i] todo.completed_at = row[params[:completed_at].to_i] if row[params[:completed_at].to_i].present? todo.save! count += 1 end end count end |
Instance Method Details
#activate_pending_todos ⇒ Object
activate todos that should be activated if the current todo is completed
284 285 286 287 288 |
# File 'app/models/todo.rb', line 284 def activate_pending_todos pending_todos = successors.select { |t| t.uncompleted_predecessors.empty? && !t.completed? } pending_todos.each { |t| t.activate! } return pending_todos end |
#add_predecessor(t) ⇒ Object
276 277 278 279 280 281 |
# File 'app/models/todo.rb', line 276 def add_predecessor(t) return if t.nil? @predecessor_array = predecessors @predecessor_array << t end |
#add_predecessor_list(predecessor_list) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 |
# File 'app/models/todo.rb', line 264 def add_predecessor_list(predecessor_list) return unless predecessor_list.is_a? String @predecessor_array = predecessor_list.split(",").inject([]) do |list, todo_id| predecessor = user.todos.find(todo_id.to_i) if todo_id.present? list << predecessor unless predecessor.nil? list end return @predecessor_array end |
#add_tags=(params) ⇒ Object
used by the REST API.
347 348 349 350 351 352 |
# File 'app/models/todo.rb', line 347 def (params) unless params[:tag].nil? tag_list = params[:tag].inject([]) { |list, value| list << value[:name] } tag_with tag_list.join(", ") end end |
#block_successors ⇒ Object
Return todos that should be blocked if the current todo is undone
291 292 293 294 295 |
# File 'app/models/todo.rb', line 291 def block_successors active_successors = successors.select { |t| t.active? || t.deferred? } active_successors.each { |t| t.block! } return active_successors end |
#check_show_from_in_future ⇒ Object
110 111 112 113 114 115 116 |
# File 'app/models/todo.rb', line 110 def check_show_from_in_future if show_from_changed? # only check on change of show_from if show_from.present? && (show_from < user.date) errors.add("show_from", I18n.t('models.todo.error_date_must_be_future')) end end end |
#context=(value) ⇒ Object
314 315 316 317 318 319 320 321 322 |
# File 'app/models/todo.rb', line 314 def context=(value) if value.is_a? Context self.original_context = (value) else c = Context.where(:name => value[:name]).first c = Context.create(value) if c.nil? self.original_context = (c) end end |
#destroy ⇒ Object
378 379 380 381 382 383 384 385 386 387 388 |
# File 'app/models/todo.rb', line 378 def destroy # activate successors if they only depend on this action pending_successors.each do |successor| successor.uncompleted_predecessors.delete(self) if successor.uncompleted_predecessors.empty? successor.activate! end end super end |
#from_recurring_todo? ⇒ Boolean
260 261 262 |
# File 'app/models/todo.rb', line 260 def from_recurring_todo? return recurring_todo_id != nil end |
#guard_for_transition_from_deferred_to_pending ⇒ Object
137 138 139 |
# File 'app/models/todo.rb', line 137 def guard_for_transition_from_deferred_to_pending no_uncompleted_predecessors? && not_part_of_hidden_container? end |
#has_pending_successors ⇒ Object
211 212 213 |
# File 'app/models/todo.rb', line 211 def has_pending_successors return !pending_successors.empty? end |
#has_project? ⇒ Boolean
342 343 344 |
# File 'app/models/todo.rb', line 342 def has_project? return ! (project_id.nil? || project.is_a?(NullProject)) end |
#hidden? ⇒ Boolean
215 216 217 |
# File 'app/models/todo.rb', line 215 def hidden? project.hidden? || context.hidden? end |
#is_successor?(todo) ⇒ Boolean
Returns true if t is equal to self or a successor of self
196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'app/models/todo.rb', line 196 def is_successor?(todo) if self == todo return true elsif successors.empty? return false else successors.each do |item| if item.is_successor?(todo) return true end end end return false end |
#no_uncompleted_predecessors? ⇒ Boolean
129 130 131 |
# File 'app/models/todo.rb', line 129 def no_uncompleted_predecessors? return !uncompleted_predecessors? end |
#no_uncompleted_predecessors_or_deferral? ⇒ Boolean
124 125 126 127 |
# File 'app/models/todo.rb', line 124 def no_uncompleted_predecessors_or_deferral? no_deferral = show_from.blank? || Time.zone.now > show_from return (no_deferral && no_uncompleted_predecessors?) end |
#not_part_of_hidden_container? ⇒ Boolean
141 142 143 |
# File 'app/models/todo.rb', line 141 def not_part_of_hidden_container? !((project && project.hidden?) || context.hidden?) end |
#original_context= ⇒ Object
313 |
# File 'app/models/todo.rb', line 313 alias_method :original_context=, :context= |
#original_project ⇒ Object
324 |
# File 'app/models/todo.rb', line 324 alias_method :original_project, :project |
#original_project= ⇒ Object
329 |
# File 'app/models/todo.rb', line 329 alias_method :original_project=, :project= |
#predecessor_dependencies=(params) ⇒ Object
XML API fixups
302 303 304 305 306 307 308 309 310 311 |
# File 'app/models/todo.rb', line 302 def predecessor_dependencies=(params) deps = params[:predecessor] return if deps.nil? # for multiple dependencies, value will be an array of id's, but for a single dependency, # value will be a string. In that case convert to array deps = [deps] unless deps.class == Array deps.each { |dep| add_predecessor(user.todos.find(dep.to_i)) if dep.present? } end |
#project ⇒ Object
325 326 327 |
# File 'app/models/todo.rb', line 325 def project original_project.nil? ? Project.null_object : original_project end |
#project=(value) ⇒ Object
330 331 332 333 334 335 336 337 338 339 340 |
# File 'app/models/todo.rb', line 330 def project=(value) if value.is_a? Project self.original_project = (value) elsif !(value.nil? || value.is_a?(NullProject)) p = Project.where(:name => value[:name]).first p = Project.create(value) if p.nil? self.original_project = (p) else self.original_project = value end end |
#raw_notes=(value) ⇒ Object
297 298 299 |
# File 'app/models/todo.rb', line 297 def raw_notes=(value) self[:notes] = value end |
#remove_predecessor(predecessor) ⇒ Object
remove predecessor and activate myself if it was the last predecessor
185 186 187 188 189 190 191 192 193 |
# File 'app/models/todo.rb', line 185 def remove_predecessor(predecessor) predecessors.delete(predecessor) if predecessors.empty? reload # reload predecessors activate! else save! end end |
#removed_predecessors ⇒ Object
180 181 182 |
# File 'app/models/todo.rb', line 180 def removed_predecessors return @removed_predecessors end |
#save_predecessors ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'app/models/todo.rb', line 151 def save_predecessors unless @predecessor_array.nil? # Only save predecessors if they changed current_array = predecessors remove_array = current_array - @predecessor_array add_array = @predecessor_array - current_array @removed_predecessors = [] remove_array.each do |todo| unless todo.nil? @removed_predecessors << todo predecessors.delete(todo) end end add_array.each do |todo| unless todo.nil? predecessors << todo unless predecessors.include?(todo) else logger.error "Could not find #{todo.description}" # Unexpected since validation passed end end end end |
#show_from ⇒ Object
223 224 225 |
# File 'app/models/todo.rb', line 223 def show_from self[:show_from] end |
#show_from=(date) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'app/models/todo.rb', line 227 def show_from=(date) if deferred? && date.blank? activate else # parse Date objects into the proper timezone date = date.in_time_zone.beginning_of_day if date.is_a? Date # show_from needs to be set before state_change because of "bug" in aasm. # If show_from is not set, the todo will not validate and thus aasm will not save # (see http://stackoverflow.com/questions/682920/persisting-the-state-column-on-transition-using-rubyist-aasm-acts-as-state-machi) self[:show_from] = date defer if active? && date.present? && show_from > user.date end end |
#specification ⇒ Object
Returns a string with description <context, project>
146 147 148 149 |
# File 'app/models/todo.rb', line 146 def specification project_name = project.is_a?(NullProject) ? "(none)" : project.name return "\'#{description}\' <\'#{context.title}\'; \'#{project_name}\'>" end |
#starred=(starred) ⇒ Object
251 252 253 254 255 256 257 258 |
# File 'app/models/todo.rb', line 251 def starred=(starred) if starred STARRED_TAG_NAME unless starred? else STARRED_TAG_NAME end starred end |
#starred? ⇒ Boolean
243 244 245 |
# File 'app/models/todo.rb', line 243 def starred? return has_tag?(STARRED_TAG_NAME) end |
#toggle_completion! ⇒ Object
219 220 221 |
# File 'app/models/todo.rb', line 219 def toggle_completion! return completed? ? activate! : complete! end |
#toggle_star! ⇒ Object
247 248 249 |
# File 'app/models/todo.rb', line 247 def toggle_star! self.starred = !starred? end |
#touch_predecessors ⇒ Object
175 176 177 178 |
# File 'app/models/todo.rb', line 175 def touch_predecessors touch predecessors.each(&:touch_predecessors) end |
#uncompleted_predecessors? ⇒ Boolean
133 134 135 |
# File 'app/models/todo.rb', line 133 def uncompleted_predecessors? return !uncompleted_predecessors.empty? end |