Created
August 21, 2012 16:38
-
-
Save jamesarosen/3417116 to your computer and use it in GitHub Desktop.
An experiment in modeling a process with ActiveModel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Our starting point: | |
| # a Rack endpoint that performs HTTP Basic auth. | |
| class SignInEndpoint | |
| def call(env) | |
| email, password = *credentials(env) | |
| user = User.authenticate(email, password) | |
| if user.nil? || user.deleted? | |
| respond_with_error 'Invalid credentials' | |
| elsif user.suspended? | |
| respond_with_error 'Your account has been suspended' | |
| else | |
| process_successful_sign_in(env, user) | |
| end | |
| end | |
| private | |
| def credentials(env) | |
| Rack::Auth::Basic::Request.new(env).credentials | |
| end | |
| def respond_with_error(text) | |
| [ | |
| 403, | |
| { 'Content-Type' => 'text/plain', 'Content-Length' => text.length }, | |
| [ text ] | |
| ] | |
| end | |
| def process_successful_sign_in(env, user) | |
| session = Rack::Request.new(env).session | |
| session['user'] ||= {} | |
| session['user']['id'] = user.id | |
| [ 200, { 'Content-Type' => 'text/plain', 'Content-Length' => '7' }, ['Success'] ] | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # An attempt at refactoring with ActiveModel: | |
| class SignInEndpoint | |
| def call(env) | |
| email, password = *credentials(env) | |
| session = Rack::Request.new(env).session | |
| SignInProcessor.new(email, password, session).process! | |
| [ 200, { 'Content-Type' => 'text/plain', 'Content-Length' => '7' }, ['Success'] ] | |
| rescue SignInProcessor::Failed => e | |
| respond_with_error(e) | |
| end | |
| private | |
| def credentials(env) | |
| Rack::Auth::Basic::Request.new(env).credentials | |
| end | |
| def respond_with_error(e) | |
| error_messages = e.errors.full_messages | |
| total_length = error_messages.map(&:length).sum | |
| [ | |
| 403, | |
| { 'Content-Type' => 'text/plain', 'Content-Length' => total_length }, | |
| error_messages | |
| ] | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class SignInProcessor | |
| include ActiveModel::Validations | |
| class Failed < Exception | |
| attr_reader :errors | |
| def initialize(validation_errors) | |
| @errors = validation_errors | |
| end | |
| end | |
| attr_reader :email, :password, :session | |
| def initialize(email, password, session) | |
| @email, @password, @session = email, password, session | |
| end | |
| validates_presence_of :email, :password, :session, :user | |
| validates :validate_user_not_deleted, :if => :user? | |
| validates :validate_user_not_suspended, :if => :user? | |
| def process! | |
| validate | |
| raise Failed.new(errors) if errors.any? | |
| session['user'] ||= {} | |
| session['user']['id'] = user.id | |
| end | |
| private | |
| def user | |
| return @user if instance_variable_defined?(:@user) | |
| @user = User.authenticate(email, password) | |
| end | |
| def user? | |
| user.present? | |
| end | |
| def validate_user_not_deleted | |
| errors.add(:user, :deleted) if user.deleted? | |
| end | |
| def validate_user_not_suspended | |
| errors.add(:user, :suspended) if user.suspended? | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I prefer
01to02 + 03.01is pretty straightforward and easy to read. It also hides some of its logic inUser.authenticate(email, password). Although I do have some tiny nitpicky pedantic gripes with01, in their current incantations, I prefer01.