Skip to content

Instantly share code, notes, and snippets.

@knewter
Created August 31, 2012 23:52
Show Gist options
  • Save knewter/3561348 to your computer and use it in GitHub Desktop.
Save knewter/3561348 to your computer and use it in GitHub Desktop.
command controller with a callback style
class FoodFightPlayCommandsController < LoggedInController
def create
command = FoodFightPlayCommand.new(params[:food_fight_play_command])
command.person_id = current_person.id
# Set up success / failure callbacks
command.on_success = method(:on_success)
command.on_failure = method(:on_failure)
command.execute!
end
def on_success(command)
redirect_to choose_food_games_food_fight_path, flash: { success: "Answered successfully." }
end
def on_failure(command)
flash.now[:error] = "Incorrect answer."
question_statistics = Games::QuestionStatisticsPresenter.new(command.question)
render '/games/food_fights/incorrect', locals: { food_fight_play_command: command, question_statistics: question_statistics }
end
end
require_relative './active_model_command'
require 'delegate'
class FoodFightPlayCommand < ActiveModelCommand
attr_accessor :question_id, :answer_id, :person_id, :on_success, :on_failure
validates :question_id, numericality: true, presence: true
validates :answer_id, numericality: true,
presence: true,
inclusion: { in: lambda{|o| o.answer_ids } }
validates :person_id, numericality: true, presence: true
delegate :body, to: :question, prefix: :question
def initialize params={}
@question_id = params[:question_id]
@answer_id = params[:answer_id].to_i
end
def question_repository
Games::Question.where(game_type: "FoodFight")
end
def question
question_repository.find(question_id)
end
def question_answer_repository
Games::QuestionAnswer.where(question_id: question_id)
end
def person_answer_repository
Games::PersonAnswer
end
def question_answers
question_answer_repository.all
end
def answer_options
question_answers.map do |qa|
answer = qa.answer
AnswerOption.new(answer).tap do |ao|
ao.chosen = (answer == chosen_answer)
ao.correct = (answer == correct_answer)
end
end
end
def correct_answer
correct_question_answer = question_answers.detect{|a| a.correct? }
return nil unless correct_question_answer
correct_question_answer.answer
end
def chosen_answer
return nil unless chosen_question_answer
chosen_question_answer.answer
end
def chosen_question_answer
question_answers.detect{|a| a.answer_id == answer_id}
end
def answer_ids
answer_options.map(&:id)
end
def correct?
chosen_answer == correct_answer
end
def person_answer_args
{
person_id: person_id,
question_answer_id: chosen_question_answer.id,
question_id: question_id
}
end
def execute!
return on_failure.call(self) unless valid?
answer = person_answer_repository.create(person_answer_args)
return on_success.call(self) if answer.valid? && correct?
return on_failure.call(self)
end
class AnswerOption < SimpleDelegator
attr_accessor :chosen, :correct
def chosen?
chosen == true
end
def correct?
correct == true
end
def incorrectly_chosen?
chosen && !correct?
end
def html_class
return 'incorrectly-chosen' if incorrectly_chosen?
return 'chosen' if chosen?
return 'correct' if correct?
return ''
end
end
end
@knewter
Copy link
Author

knewter commented Sep 21, 2012

So yeah, pushed the latest example - since it actually does the non-callbacky bits, it's substantially larger. However, I can use the commands to write up nice integration tests that just involve running the commands over and over, and I know that the controller's just going to run the command, so my 'driving a browser' style tests don't need to be verifying the entire system like most people's cukes end up doing...

And as I said, I can also compose these which is pleasant.

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