Readme.md in use_case-0.5.0 vs Readme.md in use_case-0.6.0

- old
+ new

@@ -28,11 +28,11 @@ The following example is a simplified use case from [Gitorious](http://gitorious.org) where we want to create a new repository. To do this, we need a user that can admin the project under which we want the new repository to live. -This example illustrates how to solve common design challenges in Rails +_NB!_ This example illustrates how to solve common design challenges in Rails applications; that does not mean that `UseCase` is only useful to Rails applications. First, let's look at what your Rails controller will look like using a `UseCase`: @@ -155,33 +155,34 @@ # any dependencies you need. def initialize(auth, user) input_class(NewRepositoryInput) pre_condition(UserLoggedInPrecondition.new(user)) pre_condition(ProjectAdminPrecondition.new(auth, user)) - # Multiple validators can be added if needed - validator(NewRepositoryValidator) - command(CreateRepositoryCommand.new(user)) + # A command has 0, 1 or many validators (e.g. :validators => [...]) + # The use case can span multiple commands (see below) + command(CreateRepositoryCommand.new(user), :validator => NewRepositoryValidator) end end ``` ## The use case pipeline at a glance This is the high-level overview of how `UseCase` strings up a pipeline for you to plug in various kinds of business logic: ``` -User input (-> input sanitation) (-> pre-conditions) (-> builder) (-> validations) -> command (-> command...) +User input (-> input sanitation) (-> pre-conditions) [(-> builder) (-> validations) -> command]* ``` -* Start with a hash of user input -* Optionally wrap this in an object that performs type-coercion, - enforces types etc. By default, input will be wrapped in an `OpenStruct` -* Optionally run pre-conditions on the santized input -* Optionally refine input by running it through a pre-execution "builder" -* Optionally (refined) input through one or more validators -* Execute command(s) with (refined) input +1. Start with a hash of user input +2. Optionally wrap this in an object that performs type-coercion, + enforces types etc. +3. Optionally run pre-conditions on the santized input +4. Optionally refine input by running it through a pre-execution "builder" +5. Optionally run (refined) input through one or more validators +6. Execute command(s) with (refined) input +7. Repeat steps 4-7 as necessary ## Input sanitation In your `UseCase` instance (typically in the constructor), you can call the `input_class` method to specify which class is used to santize inputs. If you do @@ -203,31 +204,33 @@ pre-condition instance that failed. ## Validations The validator uses `ActiveModel::Validations`, so any Rails validation can go in -here. The main difference is that the validator is created as a stand-alone -object that can be used with any model instance. This design allows you to -define multiple context-sensitive validations for a single object. +here (except for `validates_uniqueness_of`, which apparently comes from +elsewhere - see example below for how to work around this). The main difference +is that the validator is created as a stand-alone object that can be used with +any model instance. This design allows you to define multiple context-sensitive +validations for a single object. You can of course provide your own validation if you want - any object that defines `call(object)` and returns something that responds to `valid?` is good. -I am following the Datamapper project closely in this area. +I am following the Datamapper2 project closely in this area. Because `UseCase::Validation` is not a required part of `UseCase`, and people may want to control their own dependencies, `activemodel` is _not_ a hard dependency. To use this feature, `gem install activemodel`. ## Builders When user input has passed input sanitation and pre-conditions have been satisfied, you can optionally pipe input through a "builder" -before handing it over to validations and the commands. +before handing it over to validations and a command. -The builder should be an object with a `build` method. The method will -be called with santized input. The return value from `build` will be -passed on to validators and the commands. +The builder should be an object with a `build` or a `call` method (if it has +both, `build` will be preferred). The method will be called with santized input. +The return value will be passed on to validators and the commands. Builders can be useful if you want to run validations on a domain object rather than directly on "dumb" input. ### Example @@ -282,14 +285,13 @@ class CreateUser include UseCase def initialize input_class(NewUserInput) - validator(UserValidator) cmd = NewUserCommand.new - builder(cmd) # Use the command as a builder too - command(cmd) + # Use the command as a builder too + command(cmd, :builder => cmd, :validator => UserValidator) end end # Usage: outcome = CreateUser.new.execute(:name => "Chris") @@ -308,23 +310,30 @@ return value will be passed to the outcome's `success` block. Any errors raised by this method is not rescued, so be sure to wrap `use_case.execute(params)` in a rescue block if you're worried that it raises. Better yet, detect known causes of exceptions in a pre-condition so you know that the command does not raise. -A use case can execute multiple commands. When you do, the result of the first -command will be the input to the second command and so on. The result of the -last command will be the final `outcome.result`. - ## Use cases A use case simply glues together all the components. Define a class, include `UseCase`, and configure the instance in the constructor. The constructor can take any arguments you like, making this solution suitable for DI (dependency injection) style designs. The use case can optionally call `input_class` once, `pre_condition` multiple -times, and `validator` multiple times. It *must* call `command` once with the -command object. +times, and `command` multiple times. + +When using multiple commands, input sanitation with the `input_class` is +performed once only. Pre-conditions are also only checked once - before any +commands are executed. The use case will then execute the commands: + +``` +command_1: sanitizied_input -> (builder ->) (validators ->) command +command_n: command_n-1 result -> (builder ->) (validators ->) command +``` + +In other words, all commands except the first one will be executed with the +result of the previous command as input. ## Outcomes `UseCase#execute` returns an `Outcome`. You can use the outcome in primarily two ways. The primary approach is one that takes blocks for the three situations: