Skip to content

Instantly share code, notes, and snippets.

@ryanb
Created November 9, 2012 23:07
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ryanb/4048918 to your computer and use it in GitHub Desktop.
Save ryanb/4048918 to your computer and use it in GitHub Desktop.
Variation of RubyTapas episode "021 Domain Model Events" without using callbacks
class TasksController < ApplicationController
def update
tracker = TaskTracker.new(@task)
if @task.update_attributes(params[:task])
TaskPusher.new(tracker, socket_id).push_changes
TaskMailSender.new(tracker, current_user).deliver_email
# success response
else
# failure respond
end
end
end
# This TaskTracker class isn't necessary since Rails dirty tracking has a previous_changes
# method that I overlooked. Thanks Jonas Nicklas for pointing this out and blowmage for
# making a fork using this: https://gist.github.com/4060224
#
# Update: One issue with previous_changes is if the TaskPusher saves the model the
# TaskMailSender would not have the original changes. I think this is good enough
# reason to stick with TaskTracker.
class TaskTracker
attr_reader :task, :original_project_id, :original_status, :original_assignee
def initialize(task)
@task = task
@original_project_id = task.project_id
@original_status = task.status
@original_assignee = task.assignee
end
def project_changed?
original_project_id != task.project_id
end
def status_changed?
original_status != task.status
end
def assignee_changed?
original_assignee != task.assignee
end
end
class TaskPusher < Struct.new(:task_tracker, :socket_id)
def push_changes
if task_tracker.assignee_changed?
# push assignee changes
end
if task_tracker.project_changed?
# push project changes
end
end
end
class TaskMailSender < Struct.new(:task_tracker, :recipient)
def deliver_email
if task_tracker.status_changed?
# email status change
end
if task_tracker.assignee_changed?
# email assignee change
end
end
end
@jnicklas
Copy link

@elight: there is such a thing, it's called previous_changes :)

@blowmage
Copy link

@elight
Copy link

elight commented Nov 12, 2012

@jnicklas, @blowmage: Gee, Rails had a feature and I didn't know about it? This never happens... facepalm

@ryanb
Copy link
Author

ryanb commented Nov 12, 2012

@jnicklas nice, I didn't realize previous_changes existed!

@ryanb
Copy link
Author

ryanb commented Nov 12, 2012

Just realized a big issue with using previous_changes. What if the TaskPusher ends up saving the task again? Then the TaskMailSender would not detect the earlier changes. The TaskTracker class seems like a nice solution now.

@avdi
Copy link

avdi commented Nov 12, 2012

When an object can tell other objects about events that are happening to it as they happen, it eliminates all this extra machinery for tracking changes. Pulling that logic out:

  • removes that knowledge from the most obvious place for it to be - inside the object which is experiencing the events.
  • makes it much more likely that the logic will be duplicated, as other objects also dig into the object's state (ask) instead of letting it emit events (tell).
  • Commits Task to always publicly support the full AM::Dirty interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment