Resolve object style references as simple references

Addresses DiUS/java-faker#148

If the yml contains an object style reference like Address.street_title which doesn't
resolve to a method name on Address, then try 'address.street_title' as a last ditch effort
which is probably how it should be encoded in the yml anyhow.
This commit is contained in:
Trever Shick
2016-10-30 12:00:14 -04:00
committed by Pascal Schumacher
parent f7f499681d
commit de89bf740b
2 changed files with 33 additions and 11 deletions
@@ -59,7 +59,7 @@ public class FakeValuesService {
for (int i=0;i< locales.size();i++) { for (int i=0;i< locales.size();i++) {
final Locale l = locales.get(i); final Locale l = locales.get(i);
final StringBuilder filename = new StringBuilder(l.getLanguage()); final StringBuilder filename = new StringBuilder(language(l));
if (!"".equals(l.getCountry())) { if (!"".equals(l.getCountry())) {
filename.append("-").append(l.getCountry()); filename.append("-").append(l.getCountry());
} }
@@ -83,6 +83,17 @@ public class FakeValuesService {
this.fakeValuesMaps = Collections.unmodifiableList(all); this.fakeValuesMaps = Collections.unmodifiableList(all);
} }
/**
* If you new up a locale with "he", it gets converted to "iw" which is old.
* This addresses that unfortunate condition.
*/
private String language(Locale l) {
if (l.getLanguage().equals("iw")) {
return "he";
}
return l.getLanguage();
}
/** /**
* @return the embedded faker: clause from the loaded Yml by the localeName, so .yml > en-us: > faker: * @return the embedded faker: clause from the loaded Yml by the localeName, so .yml > en-us: > faker:
*/ */
@@ -178,6 +189,9 @@ public class FakeValuesService {
if (o == null) return defaultIfNull; if (o == null) return defaultIfNull;
if (o instanceof List) { if (o instanceof List) {
List<String> values = (List<String>) o; List<String> values = (List<String>) o;
if (values.size() == 0) {
return defaultIfNull;
}
return values.get(randomService.nextInt(values.size())); return values.get(randomService.nextInt(values.size()));
} else { } else {
return (String) o; return (String) o;
@@ -407,6 +421,15 @@ public class FakeValuesService {
resolved = resolveFakerObjectAndMethod(root, directive, args); resolved = resolveFakerObjectAndMethod(root, directive, args);
} }
// last ditch effort. Due to Ruby's dynamic nature, something like 'Address.street_title' will resolve
// because 'street_title' is a dynamic method on the Address object. We can't do this in Java so we go
// thru the normal resolution above, but if we will can't resolve it, we once again do a 'safeFetch' as we
// did first but FIRST we change the Object reference Class.method_name with a yml style internal refernce ->
// class.method_name (lowercase)
if (resolved == null && isDotDirective(directive)) {
resolved = safeFetch(simpleDirective.toLowerCase(), null);
}
return resolved; return resolved;
} }
@@ -453,22 +476,21 @@ public class FakeValuesService {
String fakerMethodName = classAndMethod[0].replaceAll("_", ""); String fakerMethodName = classAndMethod[0].replaceAll("_", "");
MethodAndCoercedArgs fakerAccessor = accessor(faker, fakerMethodName, Collections.<String>emptyList()); MethodAndCoercedArgs fakerAccessor = accessor(faker, fakerMethodName, Collections.<String>emptyList());
if (fakerAccessor == null) { if (fakerAccessor == null) {
throw new RuntimeException("Can't find top level faker object named " + fakerMethodName + "."); log.fine("Can't find top level faker object named " + fakerMethodName + ".");
return null;
} }
Object objectWithMethodToInvoke = fakerAccessor.invoke(faker); Object objectWithMethodToInvoke = fakerAccessor.invoke(faker);
String nestedMethodName = classAndMethod[1].replaceAll("_", ""); String nestedMethodName = classAndMethod[1].replaceAll("_", "");
final MethodAndCoercedArgs accessor = accessor(objectWithMethodToInvoke, classAndMethod[1].replaceAll("_", ""), args); final MethodAndCoercedArgs accessor = accessor(objectWithMethodToInvoke, classAndMethod[1].replaceAll("_", ""), args);
if (accessor == null) { if (accessor == null) {
throw new RuntimeException("Can't find method on " log.fine("Can't find method on "
+ objectWithMethodToInvoke.getClass().getSimpleName() + objectWithMethodToInvoke.getClass().getSimpleName()
+ " called " + nestedMethodName + "."); + " called " + nestedMethodName + ".");
} }
return string(accessor.invoke(objectWithMethodToInvoke)); return string(accessor.invoke(objectWithMethodToInvoke));
} catch (Exception e) { } catch (Exception e) {
if (e instanceof RuntimeException) { log.fine(e.getMessage());
throw (RuntimeException) e;
}
return null; return null;
} }
} }
@@ -209,13 +209,13 @@ public class FakeValuesServiceTest extends AbstractFakerTest {
@Test @Test
public void expressionWithInvalidFakerObject() { public void expressionWithInvalidFakerObject() {
expressionShouldFailWith("#{ObjectNotOnFaker.methodName}", expressionShouldFailWith("#{ObjectNotOnFaker.methodName}",
"Can't find top level faker object named ObjectNotOnFaker."); "Unable to resolve #{ObjectNotOnFaker.methodName} directive.");
} }
@Test @Test
public void expressionWithValidFakerObjectButInvalidMethod() { public void expressionWithValidFakerObjectButInvalidMethod() {
expressionShouldFailWith("#{Name.nonExistentMethod}", expressionShouldFailWith("#{Name.nonExistentMethod}",
"Can't find method on Name called nonExistentMethod."); "Unable to resolve #{Name.nonExistentMethod} directive.");
} }
/** /**
@@ -229,7 +229,7 @@ public class FakeValuesServiceTest extends AbstractFakerTest {
@Test @Test
public void expressionWithValidFakerObjectValidMethodInvalidArgs() { public void expressionWithValidFakerObjectValidMethodInvalidArgs() {
expressionShouldFailWith("#{Number.number_between 'x','y'}", expressionShouldFailWith("#{Number.number_between 'x','y'}",
"Can't find method on Number called numberbetween."); "Unable to resolve #{Number.number_between 'x','y'} directive.");
} }
/** /**