Skip to content

Commit

Permalink
Make Faker a class and add FakerConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
serpro69 committed Oct 2, 2019
1 parent d7e0935 commit a0ebb97
Show file tree
Hide file tree
Showing 155 changed files with 958 additions and 880 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3] - UNRELEASED
### Added
- `FakerConfig` for configuration of `Faker` instance

### Changed
- Make `Faker` a class instead of object

## [0.2] - 2019-09-30
### Added
- [#1](https://github.com/serpro69/kotlin-faker/issues/1) Random class instance generator
Expand Down
137 changes: 77 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@
## ToC
- [About](#about)
- [Usage](#usage)
- [Add a dependency](#add-a-dependency)
- [Initialize Faker](#initialize-faker-singleton)
- [Generate data](#generate-some-data)
- [Using custom locales](#using-custom-locales)
- [Downloading](#downloading)
- [Generating data](#generating-data)
- [Configuring Faker](#configuring-faker)
- [Default configuration](#default-configuration)
- [Deterministic Random](#deterministic-random)
- [Localized dictionary](#localized-dictionary)
- [Java interop](#java-interop)
- [Data Providers](#data-providers)
- [Generation a random instance of any class](#generating-a-random-instance-of-any-class)
- [Contributing](#contributing)
- [Thanks](#thanks)
- [Licence](#licence)


Expand Down Expand Up @@ -77,57 +81,53 @@ Add dependency:
The jar and pom files can also be found at this [link](https://dl.bintray.com/serpro69/maven/io/github/serpro69/kotlin-faker/)

### Generating data
First initialize `Faker` singleton:
```kotlin
Faker.init()
```

Then call properties of `Faker` which represent different data categories (i.e. address, name, etc.):
```kotlin
Faker.name.firstName() // => Ana
val faker = Faker()

Faker.address.city() // => New York
faker.name.firstName() // => Ana
faker.address.city() // => New York
```

### Deterministic Random
### Configuring Faker
#### Default configuration
If no `FakerConfig` instance is passed to `Faker` constructor then default configuration will be used:
- `locale` is set to `en`
- `random` is seeded with a pseudo-randomly generated number.

#### Deterministic Random
Faker supports seeding of it's PRNG (pseudo-random number generator) through configuration to
provide deterministic output of repeated function invocations.

```kotlin
Faker.Config.random = Random(42)
Faker.init()
val city1 = Faker.address.city()
val name1 = Faker.name.name()
val fakerConfig = FakerConfig.builder().create {
random = Random(42)
}

val faker = Faker(fakerConfig)
val city1 = faker.address.city()
val name1 = faker.name.name()

Faker.Config.random = Random(42)
Faker.init()
val city2 = Faker.address.city()
val name2 = Faker.name.name()
val otherFaker = Faker(fakerConfig)
val city1 = otherFaker.address.city()
val name1 = otherFaker.name.name()

city1 == city2 // => true
name1 == name2 // => true
```

**Re-initializing `Faker` will discard any previous configuration settings.**
```kotlin
Faker.Config.random = Random(42)
Faker.init()
val city1 = Faker.address.city()
val name1 = Faker.name.name()

Faker.init()
val city2 = Faker.address.city()
val name2 = Faker.name.name()

city1 == city2 // => false
name1 == name2 // => false
```
#### Localized dictionary
`Faker` can be configured to use a localized dictionary file instead of the default `en` locale.

### Using custom locales
`Faker` instance can be initialized with a custom locale:
```kotlin
Faker.init(locale)
val fakerConfig = FakerConfig.builder().create {
locale = "nb-NO"
}

val faker = Faker(fakerConfig)
val city1 = faker.address.city() // => Oslo
```

##### Available Locales
<details><summary><b>List of available locales (clickable):</b></summary>
<p>

Expand Down Expand Up @@ -191,39 +191,57 @@ Faker.init(locale)
Using a non-default locale will replace the values in some of the providers with the values from localized dictionary.

```kotlin
Faker.init("es")
Faker.address.city() // => Barcelona
val fakerConfig = FakerConfig.builder().create {
locale = "es"
}
val faker = Faker(fakerConfig)
faker.address.city() // => Barcelona
```

*Note that if the localized dictionary file does not contain a category (or a parameter in a category)*
*that is present in the default locale, then non-localized value will be used instead.*
<i>Note that if the localized dictionary file does not contain a category (or a parameter in a category)
that is present in the default locale, then non-localized value will be used instead.</i>

```kotlin
Faker.init()
Faker.gameOfThrones.cities() // => Braavos
val faker = Faker()
faker.gameOfThrones.cities() // => Braavos

Faker.init("nb-NO")
val fakerConfig = FakerConfig.builder().create {
locale = "nb-NO"
}
val localizedFaker = Faker(fakerConfig)
// `game_of_thrones` category is not localized for `nb-NO` locale
Faker.gameOfThrones.cities() // => Braavos
localizedFaker.gameOfThrones.cities() // => Braavos
```

### Java interop
Although this lib was created with Kotlin in mind it is still possible to use from a Java-based project
thanks to great Kotlin-to-Java interop.

First initialize `Faker`:
Configuring `Faker`:
```java
Faker.init()
```
FakerConfig fakerConfig = FakerConfigBuilder.create(FakerConfig.builder(), fromConsumer(builder -> {
builder.setRandom(new Random(42));
builder.setLocale("en-AU");
}));
```
If `builder` parameter is not called with help of `fromConsumer` method,
then explicit return should be specified:
```java
FakerConfig fakerConfig = FakerConfigBuilder.create(FakerConfig.builder(), builder -> {
builder.setRandom(new Random(42));
builder.setLocale("en-AU");
return Unit.INSTANCE;
});
```

Then call the getter methods on any of the available providers:
Calling `Faker` methods:
```java
String bldNum = Faker.address.getBuildingNumber().invoke() // => 123
new Faker(fakerConfig).getName().getFirstName().invoke();
```
*Note the `invoke()` at the end. This is basically the only difference when it comes to using this library from Java.*
*Calling `invoke()` is needed because all the methods in providers' classes are function literals, not properties,*
*therefore to get the `String` value of the method `getBuildingNumber()` an `invoke()` operator should be called.*
*This is not necessary in Kotlin because you can call function literals with just braces like so: `buildingNumber()`*
<i>Note the `invoke()` at the end. This is basically the only difference when it comes to using this library from Java.
Calling `invoke()` is needed because all the methods in providers' classes are function literals, not properties,
therefore to get the `String` value of the method `getBuildingNumber()` an `invoke()` operator should be called.
This is not necessary in Kotlin because you can call function literals with just braces like so: `buildingNumber()`</i>


## Data Providers
Expand Down Expand Up @@ -384,7 +402,7 @@ For more details see the particular `.md` file for each provider below.</i>
</details>

### Generating a random instance of any class
It is possible to generate a random instance of any class with `Faker.randomProvider`. For example:
It is possible to generate a random instance of any class with `Faker().randomProvider`. For example:

```kotlin
class Foo(val a: String)
Expand All @@ -393,13 +411,12 @@ class Bar(val foo: Foo)
class Test {
@Test
fun test() {
Faker.init()
val faker = Faker()

val foo: Foo = Faker.randomProvider.randomClassInstance()
val bar: Bar = Faker.randomProvider.randomClassInstance()
val foo: Foo = faker.randomProvider.randomClassInstance()
val bar: Bar = faker.randomProvider.randomClassInstance()
}
}

```

There are the following rules when creating a random instance of a class:
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies {
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("io.github.classgraph:classgraph:4.8.24")
testImplementation("io.kotlintest:kotlintest-runner-junit5:3.2.1")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")
runtime(kotlin("script-runtime"))
}

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/io/github/serpro69/kfaker/FunctionalUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.serpro69.kfaker;

import kotlin.Unit;
import kotlin.jvm.functions.Function1;

import java.util.function.Consumer;

public class FunctionalUtil {

public static <T> Function1<T, Unit> fromConsumer(Consumer<T> callable) {
return t -> {
callable.accept(t);
return Unit.INSTANCE;
};
}
}

0 comments on commit a0ebb97

Please sign in to comment.