Skip to content

Commit a5d3d6c

Browse files
committed
API change: Replace Response.Formatter and Viewable annotation with Result fix #52
1 parent 16ec770 commit a5d3d6c

23 files changed

Lines changed: 361 additions & 517 deletions

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,7 @@ A [view engine](http://jooby.org/apidocs/org/jooby/View.Engine.html) is a specia
12241224
{
12251225
use(new MyTemplateEngine());
12261226

1227-
get("/", (req, rsp) -> rsp.send(View.of("viewname", model));
1227+
get("/", (req, rsp) -> rsp.send(View.of("viewname", "model", model));
12281228

12291229
}
12301230
```
@@ -1238,7 +1238,7 @@ As you learnt before, content negotiation is done and executed every time a requ
12381238
```java
12391239
get("/", (req, rsp) ->
12401240
rsp.format()
1241-
.when("text/html", () -> View.of("viewname", model))
1241+
.when("text/html", () -> View.of("viewname", "model", model))
12421242
.when("application/json", () -> model)
12431243
.when("*", () -> Status.NOT_ACCEPTABLE)
12441244
.send()
@@ -1332,7 +1332,7 @@ public class MyRoutes {
13321332
13331333
@GET
13341334
public View home() {
1335-
return View.of("home", model);
1335+
return View.of("home", "model", model);
13361336
}
13371337
}
13381338
```
@@ -1354,7 +1354,7 @@ public class MyRoutes {
13541354
13551355
@GET
13561356
public View home() {
1357-
return View.of("home", model);
1357+
return View.of("home", "model", model);
13581358
}
13591359
}
13601360
```
@@ -1511,7 +1511,7 @@ If you need/want to render a view, just return a *org.jooby.View* instance:
15111511
```java
15121512
@GET
15131513
public View home() {
1514-
return View.of("home", model);
1514+
return View.of("home", "model", model);
15151515
}
15161516
```
15171517

coverage-report/src/test/java/org/jooby/ContentNegotiationFeature.java

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@
77
import org.jooby.test.ServerFeature;
88
import org.junit.Test;
99

10-
import com.google.inject.multibindings.Multibinder;
11-
1210
public class ContentNegotiationFeature extends ServerFeature {
1311

1412
@Path("/r")
1513
public static class Resource {
1614

1715
@Path("/any")
1816
@GET
19-
public String any() {
20-
return "body";
17+
public Result any() {
18+
return Results
19+
.when("text/html", () -> View.of("test", "this", "body"))
20+
.when("*/*", () -> "body");
2121
}
2222

2323
@Path("/html")
2424
@GET
2525
@Produces("text/html")
26-
public String html() {
27-
return "body";
26+
public View html() {
27+
return View.of("test", "this", "body");
2828
}
2929

3030
@Path("/json")
@@ -38,30 +38,23 @@ public String json() {
3838

3939
{
4040

41-
use((mode, config, binder) -> {
42-
Multibinder.newSetBinder(binder, Body.Formatter.class)
43-
.addBinding().toInstance(BodyConverters.toHtml);
41+
use(BodyConverters.toHtml);
4442

45-
Multibinder.newSetBinder(binder, Body.Formatter.class)
46-
.addBinding().toInstance(BodyConverters.toJson);
47-
});
43+
use(BodyConverters.toJson);
4844

49-
get("/any", (req, resp) ->
50-
resp.format()
45+
get("/any", req ->
46+
Results
5147
.when("text/html", () -> View.of("test", "this", "body"))
52-
.when("*/*", () -> "body")
53-
.send());
48+
.when("*/*", () -> "body"));
5449

55-
get("/status", (req, resp) ->
56-
resp.format()
57-
.when("*", () -> Status.NOT_ACCEPTABLE)
58-
.send());
50+
get("/status", req ->
51+
Results
52+
.when("*", () -> Status.NOT_ACCEPTABLE));
5953

60-
get("/like", (req, resp) ->
61-
resp.format()
54+
get("/like", req ->
55+
Results
6256
.when("text/html", () -> View.of("test", "this", "body"))
63-
.when("application/json", () -> "body")
64-
.send());
57+
.when("application/json", () -> "body"));
6558

6659
get("/html", (req, resp) -> resp.send(View.of("test", "this", "body")))
6760
.produces(MediaType.html);
@@ -85,7 +78,7 @@ public void chromeAccept() throws Exception {
8578
request()
8679
.get("/r/any")
8780
.header("Accept", CHROME_ACCEPT)
88-
.expect("<html><body>any: {this=body}</body></html>");
81+
.expect("<html><body>test: {this=body}</body></html>");
8982
}
9083

9184
@Test
@@ -98,7 +91,7 @@ public void htmlAccept() throws Exception {
9891
request()
9992
.get("/r/any")
10093
.header("Accept", "text/html")
101-
.expect("<html><body>any: {this=body}</body></html>");
94+
.expect("<html><body>test: {this=body}</body></html>");
10295

10396
request()
10497
.get("/html")
@@ -108,7 +101,7 @@ public void htmlAccept() throws Exception {
108101
request()
109102
.get("/r/html")
110103
.header("Accept", CHROME_ACCEPT)
111-
.expect("<html><body>html: {this=body}</body></html>");
104+
.expect("<html><body>test: {this=body}</body></html>");
112105

113106
request()
114107
.get("/json")
@@ -216,6 +209,11 @@ public void like() throws Exception {
216209
.get("/like")
217210
.header("Accept", "application/*+json")
218211
.expect("{\"body\": \"body\"}");
212+
213+
request()
214+
.get("/like")
215+
.header("Accept", "application/xml")
216+
.expect(406);
219217
}
220218

221219
@Test

coverage-report/src/test/java/org/jooby/TemplateEngineFeature.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import org.jooby.mvc.GET;
66
import org.jooby.mvc.Path;
7-
import org.jooby.mvc.Viewable;
87
import org.jooby.test.ServerFeature;
98
import org.junit.Test;
109

@@ -21,12 +20,6 @@ public View view() throws IOException {
2120
return View.of("test", "this", "model");
2221
}
2322

24-
@Path("/view/template")
25-
@Viewable("template")
26-
@GET
27-
public Object template() throws IOException {
28-
return "model";
29-
}
3023
}
3124

3225
{
@@ -54,12 +47,4 @@ public void view() throws Exception {
5447
.expect("<html><body>test: {this=model}</body></html>");
5548
}
5649

57-
@Test
58-
public void templateAnnotation() throws Exception {
59-
request()
60-
.get("/r/view/template")
61-
.expect("<html><body>template: {this=model}</body></html>");
62-
63-
}
64-
6550
}

jooby-jdbi/src/main/java/org/jooby/jdbi/Jdbi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
*
113113
* <pre>
114114
* {
115-
* use(new Jdbi().doWith((dbi, config) -> {
115+
* use(new Jdbi().doWith((dbi, config) {@literal ->} {
116116
* // set custom option
117117
* }));
118118
* }

jooby/src/main/java/org/jooby/Err.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ public void handle(final Request req, final Response rsp, final Exception ex)
5959

6060
Map<String, Object> err = err(req, rsp, ex);
6161

62-
rsp.format()
63-
.when(MediaType.html, () -> View.of(errPage(req, rsp, ex), "err", err))
64-
.when(MediaType.all, () -> err)
65-
.send();
62+
rsp.send(
63+
Results
64+
.when(MediaType.html, () -> View.of(errPage(req, rsp, ex), "err", err))
65+
.when(MediaType.all, () -> err)
66+
);
6667
}
6768

6869
}

jooby/src/main/java/org/jooby/Jooby.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@
341341
*
342342
* <p>
343343
* To learn more about Mvc Routes, please check {@link org.jooby.mvc.Path},
344-
* {@link org.jooby.mvc.Produces} {@link org.jooby.mvc.Consumes}, and {@link org.jooby.mvc.Viewable}
344+
* {@link org.jooby.mvc.Produces} {@link org.jooby.mvc.Consumes}
345345
* .
346346
* </p>
347347
*
@@ -2475,8 +2475,7 @@ public Route.Definition head(final @Nonnull String path, final @Nonnull Route.Ha
24752475
*
24762476
* <p>
24772477
* To learn more about Mvc Routes, please check {@link org.jooby.mvc.Path},
2478-
* {@link org.jooby.mvc.Produces} {@link org.jooby.mvc.Consumes} and
2479-
* {@link org.jooby.mvc.Viewable}.
2478+
* {@link org.jooby.mvc.Produces} {@link org.jooby.mvc.Consumes}.
24802479
* </p>
24812480
*
24822481
* @param routeClass A route(s) class.

jooby/src/main/java/org/jooby/Response.java

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import javax.annotation.Nonnull;
3535

3636
import org.jooby.Cookie.Definition;
37-
import org.jooby.util.ExSupplier;
3837

3938
/**
4039
* Give you access to the actual HTTP response. You can read/write headers and write HTTP body.
@@ -226,11 +225,6 @@ public void end() {
226225
rsp.end();
227226
}
228227

229-
@Override
230-
public Formatter format() {
231-
return rsp.format();
232-
}
233-
234228
@Override
235229
public void redirect(final String location) throws Exception {
236230
rsp.redirect(location);
@@ -284,58 +278,6 @@ public static Response unwrap(final @Nonnull Response rsp) {
284278
}
285279
}
286280

287-
/**
288-
* Handle content negotiation. For example:
289-
*
290-
* <pre>
291-
* {{
292-
* get("/", (req, resp) {@literal ->} {
293-
* Object model = ...;
294-
* resp.when("text/html", () {@literal ->} Viewable.of("view", model))
295-
* .when("application/json", () {@literal ->} model)
296-
* .send();
297-
* });
298-
* }}
299-
* </pre>
300-
*
301-
* The example above will render a view when accept header is "text/html" or just send a text
302-
* version of model when the accept header is "application/json".
303-
*
304-
* @author edgar
305-
* @since 0.1.0
306-
*/
307-
interface Formatter {
308-
309-
/**
310-
* Add a new when clause for a custom media-type.
311-
*
312-
* @param type A media type to test for.
313-
* @param supplier An object supplier.
314-
* @return The current {@link Response.Formatter}.
315-
*/
316-
default @Nonnull Formatter when(final String type,
317-
final @Nonnull ExSupplier<Object> supplier) {
318-
return when(MediaType.valueOf(type), supplier);
319-
}
320-
321-
/**
322-
* Add a new when clause for a custom media-type.
323-
*
324-
* @param type A media type to test for.
325-
* @param supplier An object supplier.
326-
* @return A {@link Response.Formatter}.
327-
*/
328-
@Nonnull
329-
Formatter when(MediaType type, @Nonnull ExSupplier<Object> supplier);
330-
331-
/**
332-
* Send the response.
333-
*
334-
* @throws Exception If something fails.
335-
*/
336-
void send() throws Exception;
337-
}
338-
339281
/**
340282
* Transfer the file at path as an "attachment". Typically, browsers will prompt the user for
341283
* download. The <code>Content-Disposition</code> "filename=" parameter (i.e. the one that will
@@ -649,27 +591,6 @@ default void send(@Nonnull final Object result) throws Exception {
649591
*/
650592
void send(@Nonnull Result result) throws Exception;
651593

652-
/**
653-
* Performs content-negotiation on the Accept HTTP header on the request object. It select a
654-
* handler for the request, based on the acceptable types ordered by their quality values.
655-
* If the header is not specified, the first callback is invoked. When no match is found,
656-
* the server responds with 406 "Not Acceptable", or invokes the default callback: {@code ** / *}.
657-
*
658-
* <pre>
659-
* get("/jsonOrHtml", (req, rsp) {@literal ->}
660-
* rsp.format()
661-
* .when("text/html", () {@literal ->} Viewable.of("view", model))
662-
* .when("application/json", () {@literal ->} model)
663-
* .when("*", () {@literal ->} Status.NOT_ACCEPTABLE)
664-
* .send()
665-
* );
666-
* </pre>
667-
*
668-
* @return A response formatter.
669-
*/
670-
@Nonnull
671-
Formatter format();
672-
673594
/**
674595
* Redirect to the given url with status code defaulting to {@link Status#FOUND}.
675596
*

jooby/src/main/java/org/jooby/Result.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@
3535
* }
3636
* </pre>
3737
*
38+
* A result is also responsible for content negotiation (if required):
39+
*
40+
* <pre>
41+
* {
42+
* get("/", () {@literal ->} {
43+
* Object model = ...;
44+
* return Results.when("text/html", () {@literal ->} Viewable.of("view", "model", model))
45+
* .when("application/json", () {@literal ->} model);
46+
* });
47+
* }
48+
* </pre>
49+
*
50+
* <p>
51+
* The example above will render a view when accept header is "text/html" or just send a text
52+
* version of model when the accept header is "application/json".
53+
* </p>
54+
*
3855
* @author edgar
3956
* @since 0.5.0
4057
* @see Results
@@ -134,13 +151,6 @@ public class Result {
134151
return this;
135152
}
136153

137-
/**
138-
* @return True if result has content.
139-
*/
140-
public boolean hasContent() {
141-
return data.size() > 0;
142-
}
143-
144154
/**
145155
* @return Raw headers for content.
146156
*/
@@ -167,6 +177,9 @@ public boolean hasContent() {
167177
}
168178

169179
/**
180+
* Get a result value for the given types (accept header).
181+
*
182+
* @param types Accept header.
170183
* @return Result content.
171184
*/
172185
public @Nonnull Optional<Object> get(final List<MediaType> types) {
@@ -181,7 +194,7 @@ public boolean hasContent() {
181194
Supplier<Object> provider = MediaType
182195
.matcher(types)
183196
.first(ImmutableList.copyOf(data.keySet()))
184-
.map(it -> data.get(it))
197+
.map(it -> data.remove(it))
185198
.orElseThrow(
186199
() -> new Err(Status.NOT_ACCEPTABLE, Joiner.on(", ").join(types))
187200
);

0 commit comments

Comments
 (0)