-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from jdickey/create-the-thing
Create the Thing entered in the new-thing form. Widgets and objects and forms; oh my!
- Loading branch information
Showing
19 changed files
with
814 additions
and
65 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
|
||
require 'create_thing' | ||
|
||
# ThingsController is our one and only demo controller. | ||
class ThingsController < ApplicationController | ||
# Encapsulates all logic involved in create action, successful or otherwise. | ||
class CreateResponder | ||
# include SemanticLogger::Loggable | ||
|
||
def initialize(controller) | ||
@controller = controller | ||
self | ||
end | ||
|
||
def call(params, &_block) | ||
create_and_save_thing params | ||
yield @thing | ||
handle_result | ||
end | ||
|
||
private | ||
|
||
attr_reader :controller, :thing | ||
|
||
def create_thing(params) | ||
@thing = FormObjects::CreateThing.new params['thing'] | ||
self | ||
end | ||
|
||
def create_and_save_thing(params) | ||
create_thing params | ||
@thing.save if @thing.valid? | ||
self | ||
end | ||
|
||
def handle_result | ||
return handle_success if @thing.valid? | ||
handle_failure | ||
end | ||
|
||
def handle_failure | ||
# Controller's @thing should already be set by this point per yield | ||
controller.render 'new' | ||
self | ||
end | ||
|
||
def handle_success | ||
controller.redirect_to success_redirect_target, success_flash | ||
end | ||
|
||
def success_flash | ||
message = ['Added your new "', '" Thing!'].join thing.name | ||
{ flash: { success: message } } | ||
end | ||
|
||
def success_redirect_target | ||
controller.root_path | ||
end | ||
end # class ThingsController::CreateResponder | ||
end # class ThingsController |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
|
||
require 'virtus' | ||
|
||
require_relative 'create_thing/uniqueness' | ||
require_relative 'create_thing/validation_error' | ||
|
||
module FormObjects | ||
# All the logic needed to create a new Thing record based on input parameters. | ||
class CreateThing | ||
include SemanticLogger::Loggable | ||
include Virtus.model | ||
include ActiveModel::Validations | ||
|
||
attribute :name, String | ||
attribute :initial_quantity, Integer | ||
attribute :description, String | ||
|
||
validates :name, presence: true | ||
validates :initial_quantity, numericality: { | ||
greater_than_or_equal_to: 1, | ||
only_integer: true | ||
} | ||
validates :description, presence: true, allow_blank: false | ||
validate :unique? | ||
|
||
def new? | ||
!Thing.find(name: name) | ||
end | ||
|
||
def save | ||
validate_before_save | ||
new_record.save | ||
self | ||
end | ||
|
||
private | ||
|
||
def add_uniquness_failure_error | ||
errors.add :name, 'has already been added. Please enter a new one.' | ||
end | ||
|
||
def can_save? | ||
valid? && unique? | ||
end | ||
|
||
def new_record | ||
Thing.new attributes | ||
end | ||
|
||
# Since we don't keep track of record IDs in this class, we have to wing it | ||
# using attributes. | ||
def unique? | ||
return true if Uniqueness.new(attributes).unique? | ||
add_uniquness_failure_error | ||
false | ||
end | ||
|
||
def validate_before_save | ||
fail validation_error unless can_save? | ||
end | ||
|
||
def validation_error | ||
ValidationError.new invalid_attributes: attributes, errors_found: errors | ||
end | ||
end # class FormObjects::CreateThing | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
|
||
module FormObjects | ||
# All the logic needed to create a new Thing record based on input parameters. | ||
class CreateThing | ||
# Encapsulates testing whether a set of ORM attributes are "unique". This is | ||
# true if either of the following conditions is true: | ||
# 1. No ORM instance exists whose name field has the same name, OR | ||
# 2. An ORM instance exists with ALL fields matching the attributes, whereby | ||
# it is presumed to be the "same" record. We really should ensure that a | ||
# database constraint is set to force that. | ||
class Uniqueness | ||
def initialize(attributes) | ||
@attributes = attributes | ||
self | ||
end | ||
|
||
def unique? | ||
!existing || same_instance? | ||
end | ||
|
||
private | ||
|
||
attr_reader :attributes | ||
|
||
def existing | ||
@existing ||= Thing.find(name: attributes[:name]) | ||
end | ||
|
||
def existing_attributes | ||
existing.values.except(:id) | ||
end | ||
|
||
def same_instance? | ||
attributes == existing_attributes | ||
end | ||
end # class FormObjects::CreateThing::Uniqueness | ||
end # class FormObjects::CreateThing | ||
end |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
module FormObjects | ||
# All the logic needed to create a new Thing record based on input parameters. | ||
class CreateThing | ||
# Error to be raised when validation of a CreateThing instance fails. | ||
class ValidationError < RuntimeError | ||
include Virtus.model(strict: true) | ||
|
||
def initialize(invalid_attributes:, errors_found:) | ||
@invalid_attributes = invalid_attributes | ||
@errors_found = errors_found | ||
super() | ||
end | ||
|
||
def message | ||
['Validation of Thing attributes failed. Invalid attributes:', | ||
"#{invalid_attributes}, errors found: #{errors_found.full_messages}" | ||
].join ' ' | ||
end | ||
|
||
values do | ||
attribute :invalid_attributes, Array[String] | ||
attribute :errors_found, ActiveModel::Errors | ||
end | ||
end # class FormObjects::CreateThing::ValidationError | ||
end # class FormObjects::CreateThing | ||
end |
This file contains 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
Oops, something went wrong.