@Override public void describeTo(StringBuilder stringBuilder) { ClosureUtil.SourceInfo sourceInfo = ClosureUtil.getSourceInfo(invoker.getClosure()); if (sourceInfo == null) { ClassPool pool = ClassPool.getDefault(); try { CtClass ctClass = pool.get(invoker.getClosure().getClass().getName()); CtMethod ctMethod = ctClass.getDeclaredMethod("doCall"); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); ClassFile classFile = ctClass.getClassFile(); String sourceFile = classFile.getSourceFile(); if (lineNumber != -1 && sourceFile != null) { stringBuilder .append("closure at line ") .append(lineNumber) .append(" of ") .append(sourceFile); } else { stringBuilder.append("closure ").append(invoker.getClosure().getClass().getName()); } } catch (NotFoundException e) { stringBuilder.append(invoker.getClosure().getClass().getName()); } } else { stringBuilder .append("closure at line ") .append(sourceInfo.getLineNumber()) .append(" of ") .append(sourceInfo.getUri()); } }
static GroovyEmbeddedApp of( @DelegatesTo(value = GroovyRatpackServerSpec.class, strategy = Closure.DELEGATE_FIRST) Closure<?> definition) throws Exception { return from( EmbeddedApp.of( s -> ClosureUtil.configureDelegateFirst(GroovyRatpackServerSpec.from(s), definition))); }
static GroovyEmbeddedApp fromServer( ServerConfig serverConfig, @DelegatesTo(value = GroovyRatpackServerSpec.class, strategy = Closure.DELEGATE_FIRST) Closure<?> definition) { return from( EmbeddedApp.fromServer( serverConfig, s -> ClosureUtil.configureDelegateFirst(GroovyRatpackServerSpec.from(s), definition))); }
/** * A highly configurable {@link ratpack.test.embed.EmbeddedApplication} implementation that allows * the application to be defined in code at runtime. * * <p>This implementation is usually sufficient for testing Ratpack modules or extensions. * * <pre class="tested"> * import ratpack.test.embed.PathBaseDirBuilder * import ratpack.groovy.test.TestHttpClients * import ratpack.session.SessionModule * * import java.nio.file.Files * * import static ratpack.groovy.test.embed.EmbeddedApplications.embeddedApp * * def tmp = Files.createTempDirectory("ratpack-test") * def baseDir = new PathBaseDirBuilder(tmp) * * // Create a file within the application base dir * baseDir.file "public/foo.txt", "bar" * * def app = embeddedApp(baseDir) { * // Configure the launch config * launchConfig { * other "some.other.property": "value" * development true * } * * // Configure the module registry * bindings { * add new SessionModule() * } * * // Use the GroovyChain DSL for defining the application handlers * handlers { * get { * render "root" * } * assets "public" * } * } * * // Test the application with the test http client * def client = TestHttpClients.testHttpClient(app) * * assert client.getText() == "root" * assert client.getText("foo.txt") == "bar" * * // Cleanup * app.close() * </pre> * * @see ratpack.test.embed.PathBaseDirBuilder * @see ratpack.groovy.test.embed.EmbeddedApplications */ public class ClosureBackedEmbeddedApplication extends LaunchConfigEmbeddedApplication { private Closure<?> handlersClosure = ClosureUtil.noop(); private Closure<?> bindingsClosure = ClosureUtil.noop(); private Closure<?> launchConfigClosure = ClosureUtil.noop(); private final List<Module> modules = new LinkedList<>(); private Factory<Path> baseDirFactory; /** Constructor. */ public ClosureBackedEmbeddedApplication() {} /** * Constructor. * * <p>Useful for deferring the base dir determination * * @param baseDirFactory The factory for the base dir */ public ClosureBackedEmbeddedApplication(Factory<Path> baseDirFactory) { this.baseDirFactory = baseDirFactory; } /** * Constructor. * * @param baseDirBuilder the builder whose {@link BaseDirBuilder#build()} method will be called to * provide the base dir for this app */ public ClosureBackedEmbeddedApplication(final BaseDirBuilder baseDirBuilder) { this( new Factory<Path>() { @Override public Path create() { return baseDirBuilder.build(); } }); } /** * A mutable list of modules to use in this application. * * @return A mutable list of modules to use in this application */ public List<Module> getModules() { return modules; } /** * Constructs the launch config using the other {@code create*} methods. * * <p>A launch config will be created using {@link LaunchConfigBuilder#baseDir(java.io.File)}, * with the path returned by the factory given at construction. The launch config will also * default to using a port value of {@code 0} (i.e. an ephemeral port). * * <p>Register modules and objects using the {@link #bindings(groovy.lang.Closure)} method. * * <p>Define the handlers using the {@link #handlers(groovy.lang.Closure)} method. * * <p>Prefer overriding specific {@code create*} methods instead of this one. * * @return The created launch config. */ @Override protected LaunchConfig createLaunchConfig() { LaunchConfigBuilder launchConfigBuilder; if (this.baseDirFactory != null) { Path baseDirPath = baseDirFactory.create(); launchConfigBuilder = LaunchConfigBuilder.baseDir(baseDirPath).port(0); } else { launchConfigBuilder = LaunchConfigBuilder.noBaseDir().port(0); } configureDelegateFirst(launchConfigBuilder, launchConfigClosure); final Action<? super BindingsSpec> bindingsAction = createBindingsAction(); return launchConfigBuilder.build( new HandlerFactory() { public Handler create(LaunchConfig launchConfig) throws Exception { GuiceBackedHandlerFactory handlerFactory = createHandlerFactory(launchConfig); Function<? super Module, ? extends Injector> injectorFactory = createInjectorFactory(launchConfig); Function<? super Injector, ? extends Handler> handlerTransformer = createHandlerTransformer(launchConfig); return handlerFactory.create(bindingsAction, injectorFactory, handlerTransformer); } }); } /** * Specifies the handlers of the application. * * <p>The given closure will not be executed until this application is started. * * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this * method after the application has started has no effect. * * @param closure The definition of the application handlers */ public void handlers( @DelegatesTo(value = GroovyChain.class, strategy = Closure.DELEGATE_FIRST) Closure<?> closure) { this.handlersClosure = closure; } /** * Specifies the bindings of the application. * * <p>The given closure will not be executed until this application is started. * * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this * method after the application has started has no effect. * * @param closure The definition of the application handlers */ public void bindings( @DelegatesTo(value = GroovyBindingsSpec.class, strategy = Closure.DELEGATE_FIRST) Closure<?> closure) { this.bindingsClosure = closure; } /** * Modifies the launch config of the application. * * <p>The given closure will not be executed until this application is started. * * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this * method after the application has started has no effect. * * @param closure The definition of the application handlers */ public void launchConfig( @DelegatesTo(value = LaunchConfigBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure<?> closure) { this.launchConfigClosure = closure; } /** * Creates a module to injector transformer based on the given launch config. * * <p>Delegates to {@link Guice#newInjectorFactory(ratpack.launch.LaunchConfig)}. * * @param launchConfig The launch config * @return the result of {@link Guice#newInjectorFactory(ratpack.launch.LaunchConfig)} with the * given launch config */ protected Function<? super Module, ? extends Injector> createInjectorFactory( LaunchConfig launchConfig) { return Guice.newInjectorFactory(launchConfig); } /** * Returns the factory to use to create the actual handler. * * <p>This implementation does not add any implicit behaviour. Subclasses may wish to provide * different implementations that do perform implicit behaviour (e.g. implied modules) * * @param launchConfig The launch config * @return The guice handler factory */ protected GuiceBackedHandlerFactory createHandlerFactory(LaunchConfig launchConfig) { return new DefaultGuiceBackedHandlerFactory(launchConfig); } /** * Provides the module registry configuration action. * * <p>This implementation creates an action that registers {@link #getModules()} in order, then * executes the closure given with {@link #bindings(groovy.lang.Closure)}. * * @return The module registry configuration action */ protected Action<? super BindingsSpec> createBindingsAction() { return new Action<BindingsSpec>() { @Override public void execute(BindingsSpec bindingsSpec) throws Exception { bindingsSpec.add(modules); configureDelegateFirst(new DefaultGroovyBindingsSpec(bindingsSpec), bindingsClosure); } }; } /** * Provides the object that, given the {@link com.google.inject.Injector} created by the module * definition, creates the application handler. * * <p>This implementation uses the closure given to {@link #handlers(groovy.lang.Closure)} to * build a handler using the {@link Groovy#chain(ratpack.launch.LaunchConfig, * ratpack.registry.Registry, groovy.lang.Closure)} method. An registry based on the injector * backs the chain. * * @param launchConfig the launch config * @return A transformer that creates the application handler, given an injector */ protected Function<? super Injector, ? extends Handler> createHandlerTransformer( final LaunchConfig launchConfig) { return new Function<Injector, Handler>() { @Override public Handler apply(Injector injector) throws Exception { return Groovy.chain(launchConfig, Guice.registry(injector), handlersClosure); } }; } }
default void test( @DelegatesTo(value = TestHttpClient.class, strategy = Closure.DELEGATE_FIRST) Closure<?> test) throws Exception { test(ClosureUtil.delegatingAction(TestHttpClient.class, test)); }