Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit e727b7e

Browse files
committed
add name to Route.Mapper (helps debugging) fix jooby-project#384
1 parent 704c3e5 commit e727b7e

9 files changed

Lines changed: 174 additions & 23 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.jooby.issues;
2+
3+
import org.jooby.Route;
4+
import org.jooby.test.ServerFeature;
5+
import org.junit.Test;
6+
7+
public class Issue384a extends ServerFeature {
8+
9+
{
10+
11+
mapper(Route.Mapper.create("counter", (final Integer v) -> v + 1));
12+
mapper(Route.Mapper.create("counter", (final Integer v) -> v + 1));
13+
14+
get("/counter", () -> 1);
15+
}
16+
17+
@Test
18+
public void shouldIgnoreMapperWithSameName() throws Exception {
19+
request()
20+
.get("/counter")
21+
.expect("2");
22+
}
23+
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.jooby.issues;
2+
3+
import org.jooby.test.ServerFeature;
4+
import org.junit.Test;
5+
6+
public class Issue384b extends ServerFeature {
7+
8+
{
9+
10+
mapper(((final Integer value) -> value * 2));
11+
12+
mapper((v -> Integer.parseInt(v.toString())));
13+
14+
get("/4", () -> {
15+
return "2";
16+
});
17+
}
18+
19+
@Test
20+
public void shouldStackOrChainMapper() throws Exception {
21+
request()
22+
.get("/4")
23+
.expect("4");
24+
}
25+
26+
}

jooby-mongodb-rx/src/main/java/org/jooby/mongodb/MongoRx.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,15 +403,15 @@ public void configure(final Env env, final Config conf, final Binder binder) {
403403

404404
@SuppressWarnings("rawtypes")
405405
static Route.Mapper mapper() {
406-
return v -> Match(v).of(
406+
return Route.Mapper.create("mongo-rx", v -> Match(v).of(
407407
Case(instanceOf(FindObservable.class), m -> m.toObservable().toList()),
408408
Case(instanceOf(ListCollectionsObservable.class), m -> m.toObservable().toList()),
409409
Case(instanceOf(ListDatabasesObservable.class), m -> m.toObservable().toList()),
410410
Case(instanceOf(AggregateObservable.class), m -> m.toObservable().toList()),
411411
Case(instanceOf(DistinctObservable.class), m -> m.toObservable().toList()),
412412
Case(instanceOf(MapReduceObservable.class), m -> m.toObservable().toList()),
413413
Case(instanceOf(MongoObservable.class), m -> m.toObservable()),
414-
Case($(), v));
414+
Case($(), v)));
415415
}
416416

417417
static MongoClientSettings.Builder settings(final ConnectionString cstr, final Config conf) {

jooby-reactor/src/main/java/org/jooby/reactor/Reactor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public static Route.Mapper<Object> reactor() {
263263

264264
@SuppressWarnings("unchecked")
265265
private static Route.Mapper<Object> reactor(final Optional<Supplier<Scheduler>> subscribeOn) {
266-
return value -> Match(value).of(
266+
return Route.Mapper.create("reactor", value -> Match(value).of(
267267
/** Flux: */
268268
Case(instanceOf(Flux.class),
269269
it -> new Deferred(deferred -> subscribeOn
@@ -277,7 +277,7 @@ private static Route.Mapper<Object> reactor(final Optional<Supplier<Scheduler>>
277277
.orElse(it)
278278
.consume(deferred::set, deferred::set))),
279279
/** Ignore */
280-
Case($(), value));
280+
Case($(), value)));
281281
}
282282

283283
@Override

jooby-rxjava/src/main/java/org/jooby/rx/Rx.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ public static Route.Mapper<Object> rx(final Supplier<Scheduler> subscribeOn) {
350350

351351
@SuppressWarnings("unchecked")
352352
private static Route.Mapper<Object> rx(final Optional<Supplier<Scheduler>> subscribeOn) {
353-
return value -> Match(value).of(
353+
return Route.Mapper.create("rx", value -> Match(value).of(
354354
/** Observable : */
355355
Case(instanceOf(Observable.class),
356356
it -> new Deferred(deferred -> subscribeOn
@@ -370,7 +370,7 @@ private static Route.Mapper<Object> rx(final Optional<Supplier<Scheduler>> subsc
370370
.orElse(it)
371371
.subscribe(new DeferredSubscriber(deferred)))),
372372
/** Ignore */
373-
Case($(), value));
373+
Case($(), value)));
374374
}
375375

376376
@Override

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ public EnvDep(final Predicate<String> predicate, final Consumer<Config> callback
637637
@SuppressWarnings("rawtypes")
638638
private Mapper mapper;
639639

640+
/** Don't add same mapper twice . */
641+
private Set<String> mappers = new HashSet<>();
642+
640643
public Jooby() {
641644
this(null);
642645
}
@@ -3547,11 +3550,14 @@ public void start(final String[] args, final Consumer<List<Route.Definition>> ro
35473550
}
35483551

35493552
@Override
3550-
@SuppressWarnings({"rawtypes", "unchecked" })
3551-
public Jooby mapper(final Mapper mapper) {
3552-
this.mapper = Optional.ofNullable(this.mapper)
3553-
.map(it -> Route.Mapper.compose(it, mapper))
3554-
.orElse(mapper);
3553+
@SuppressWarnings("unchecked")
3554+
public Jooby mapper(final Mapper<?> mapper) {
3555+
requireNonNull(mapper, "Mapper is required.");
3556+
if (mappers.add(mapper.name())) {
3557+
this.mapper = Optional.ofNullable(this.mapper)
3558+
.map(next -> Route.Mapper.chain(mapper, next))
3559+
.orElse((Mapper<Object>) mapper);
3560+
}
35553561
return this;
35563562
}
35573563

jooby/src/main/java/org/jooby/Route.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@
4141
import org.jooby.internal.RouteMatcher;
4242
import org.jooby.internal.RoutePattern;
4343

44+
import com.google.common.base.CaseFormat;
4445
import com.google.common.base.Strings;
4546
import com.google.common.collect.ImmutableList;
4647
import com.google.common.collect.Lists;
4748
import com.google.common.primitives.Primitives;
4849
import com.google.inject.Key;
4950
import com.google.inject.TypeLiteral;
5051

52+
import javaslang.CheckedFunction1;
53+
5154
/**
5255
* Routes are a key concept in Jooby. Routes are executed in the same order they are defined
5356
* (even for Mvc Routes).
@@ -273,14 +276,56 @@ public interface Route {
273276
*/
274277
interface Mapper<T> {
275278

276-
@SuppressWarnings({"rawtypes", "unchecked" })
277-
static Mapper<Object> compose(final Mapper it, final Mapper next) {
278-
return v -> {
279-
Object m = it.map(v);
280-
return m == v ? next.map(v) : m;
279+
/**
280+
* Produces a new mapper by combining the two mapper into one.
281+
*
282+
* @param it The first mapper to apply.
283+
* @param next The second mapper to apply.
284+
* @return A new mapper.
285+
*/
286+
@SuppressWarnings({"rawtypes", "unchecked" })
287+
static Mapper<Object> chain(final Mapper it, final Mapper next) {
288+
return create(it.name() + ">" + next.name(), v -> next.map(it.map(v)));
289+
}
290+
291+
/**
292+
* Creates a new named mapper (just syntax suggar for creating a new mapper).
293+
*
294+
* @param name Mapper's name.
295+
* @param fn Map function.
296+
* @return A new mapper.
297+
*/
298+
static <T> Mapper<T> create(final String name, final CheckedFunction1<T, Object> fn) {
299+
return new Route.Mapper<T>() {
300+
@Override
301+
public String name() {
302+
return name;
303+
}
304+
305+
@Override
306+
public Object map(final T value) throws Throwable {
307+
return fn.apply(value);
308+
}
309+
310+
@Override
311+
public String toString() {
312+
return name();
313+
}
281314
};
282315
}
283316

317+
/**
318+
* @return Mapper's name.
319+
*/
320+
default String name() {
321+
String name = Optional.ofNullable(Strings.emptyToNull(getClass().getSimpleName()))
322+
.orElseGet(() -> {
323+
String classname = getClass().getName();
324+
return classname.substring(classname.lastIndexOf('.') + 1);
325+
});
326+
return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name);
327+
}
328+
284329
/**
285330
* Map the type to something else.
286331
*
@@ -1341,7 +1386,7 @@ public String toString() {
13411386
private Route asRoute(final String method, final RouteMatcher matcher,
13421387
final List<MediaType> produces) {
13431388
return new RouteImpl(filter, this, method, matcher.path(), produces,
1344-
matcher.vars(), mapper);
1389+
matcher.vars(), mapper);
13451390
}
13461391

13471392
}

jooby/src/main/java/org/jooby/Routes.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,17 +2469,16 @@ Route.Collection complete(String method, String pattern, Route.Complete handler,
24692469
*
24702470
* <pre>{@code
24712471
* {
2472-
* mapper(Rx.rx());
2472+
* mapper((Integer v) -> v * 2);
24732473
*
2474-
* mapper(Reactor.reactor());
2475-
*
2476-
* get("/rx", () -> Observable.just("reactive"));
2477-
*
2478-
* get("/flux", () -> Flux.just("reactive"));
2474+
* mapper(v -> Integer.parseInt(v.toString()));
24792475
*
2476+
* get("/four", () -> "2");
24802477
* }
24812478
* }</pre>
24822479
*
2480+
* A call to <code>/four</code> outputs <code>4</code>
2481+
*
24832482
* @param mapper
24842483
* @return This instance.
24852484
*/
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.jooby.issues;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import org.jooby.Route;
7+
import org.jooby.Route.Mapper;
8+
import org.junit.Test;
9+
10+
public class Issue384 {
11+
12+
static class M implements Route.Mapper<Integer> {
13+
14+
@Override
15+
public Object map(final Integer value) throws Throwable {
16+
return value;
17+
}
18+
19+
}
20+
21+
@Test
22+
public void defaultRouteMapperName() {
23+
Route.Mapper<Integer> intMapper = (final Integer v) -> v * 2;
24+
assertTrue(intMapper.name().startsWith("issue384"));
25+
26+
assertEquals("m", new M().name());
27+
28+
assertTrue(new Route.Mapper<String>() {
29+
@Override
30+
public Object map(final String value) throws Throwable {
31+
return value;
32+
};
33+
}.name().startsWith("issue384"));
34+
}
35+
36+
@Test
37+
public void routeFactory() {
38+
Mapper<Integer> intMapper = Route.Mapper.create("x", (final Integer v) -> v * 2);
39+
assertEquals("x", intMapper.name());
40+
assertEquals("x", intMapper.toString());
41+
}
42+
43+
@Test
44+
public void chain() throws Throwable {
45+
Mapper<Integer> intMapper = Route.Mapper.create("int", (final Integer v) -> v * 2);
46+
Mapper<String> strMapper = Route.Mapper.create("str", v -> "{" + v + "}");
47+
assertEquals("int>str", Route.Mapper.chain(intMapper, strMapper).name());
48+
assertEquals("str>int", Route.Mapper.chain(strMapper, intMapper).name());
49+
assertEquals(8, Route.Mapper.chain(intMapper, intMapper).map(2));
50+
}
51+
}

0 commit comments

Comments
 (0)