Search code examples
ratpack

Removing ExecControl to upgrade to Ratpack v1.1.1?


I am learning Ratpack and working from multiple resources. I have the following interface and class that works in Ratpack v0.9.18 but fails in v1.1.1 because of removal of the ExecControl interface.

//file: src/main/groovy/app/UserService.groovy
package app
import ratpack.exec.Promise
interface UserService {
  Promise<Void> save (User user)
  Promise<List<User>> getUsers ()
}

//file: src/main/groovy/app/DefaultUserService.groovy
package app
import ratpack.exec.ExecControl
import ratpack.exec.Promise
class DefaultUserService implements UserService {
  private final List storage = []
  @Override
  Promise<Void> save (User user) {
    storage << user
    ExecControl.current ().promiseOf (null)
  }
  @Override
  Promise<List<User>> getUsers () {
    ExecControl.current ().promiseOf (storage)
  }
}

I thought that changing the line with ExecControl with:

Promise.of (storage)

would work but results in

MissingMethodException: No signature of method: static ratpack.exec.Promise.of() is applicable for argument types: (java.util.ArrayList).

The Promise.of () method is expecting a type of ratpack.exec.Upstream.

How should the above DefaultUserService class be modified to work with Ratpack v1.1.1?


Solution

  • Promise.of(Upstream) http://ratpack.io/manual/current/api/ratpack/exec/Promise.html#of-ratpack.exec.Upstream- is a way to signal the result of some process to downstream consumers. You can signal whether or not something completed successfully via Downstream#error(Throwable) or Downstream#success(value) http://ratpack.io/manual/current/api/ratpack/exec/Downstream.html

    You can also create a Promise from a known value via Promise.value(value)

    In addition to Promise Ratpack also provides ratpack.exec.Operation which is like a Promise<Void> in that it represents async work with no return type.

    I've put together a sample to demonstrate the various ways to create a representation of async work.

    @Grab('io.ratpack:ratpack-groovy:1.1.1')
    
    import ratpack.exec.Operation
    import ratpack.exec.Promise
    import ratpack.handling.Context
    
    import ratpack.jackson.Jackson
    
    import static ratpack.groovy.Groovy.ratpack
    
    class User { String name }
    
    class UserService {
        private final List<User> storage = []
    
        Operation save(User user) {
            storage << user
            Operation.noop()
        }
    
        Promise<List<User>> getUsers() {
            Promise.of { downstream -> downstream.success(storage) }
            // or
    //      Promise.value(storage)
        }
    }
    ratpack {
        bindings {
            bindInstance new UserService()
        }
        handlers {
            get { Context ctx, UserService userService ->
                userService.getUsers()
                        .map(Jackson.&json)
                        .then(ctx.&render)
            }
            get('add/:name') { UserService userService ->
                userService.save(new User(name: pathTokens.get('name')))
                        .then { render 'User saved' }
            }
        }
    }
    

    Here are some sample curl commands I've executed against this:

    $ curl localhost:5050
    []
    
    $ curl localhost:5050/add/dan
    User saved
    
    $ curl localhost:5050/
    [{"name":"dan"}]
    
    $ curl localhost:5050/add/luke
    User saved
    
    $ curl localhost:5050/
    [{"name":"dan"},{"name":"luke"}]