Reading this document will help you understand:
- Basic structure of the faker_x package
- How to add a single new value generator to existing resources. for example adding
cat_image
value generator to resourceimage
whether you want to add itglobally
for all existinglocales
or just to a specific locale likeen_us
. - How to localize global value generators.
- How to add support for a new locale that doesn't exist for example
en_uk
.
Let's say we want to add a new fake value generator to our FakerX
class called pet_name
that generates random pet names . this is going to be done in 3 steps
- Define a static String key in
DataKeys
class calledpet_name
- Define a new datasource in the
animal.dart
resource file
const pet_name = StringDataSource(
locale: Locales.en_us,
dataKey: DataKeys.pet_name,
values: [
'Bucky',
'Sparky',
'Coco',
'Wolfy',
.
.
]
);
- run
./generate_and_test.sh
script. this script will create required code, tests & docs and will format, analyze code and run tests at the end.
Now there will be a new getter method that generates fake pet names in our FakerX
class.
final myPetName = FakerX.defaultInstance.animal.petName;
faker_x contains a lib/src/locales
directory; this is where changes need to be added.
in lib/src/locales
you will find different locales like en_us
, fa_ir
etc including a global
.
in each locale directory there is structure like below. a datasources directory that contains a dart file for each resource
like job.dart
in each of these resource files there are one or several DataSource
variables. these datasources are fake value generators. they will generate fake values for that resource.
├── en_us
├── datasources
│ ├── address.dart
│ ├── animal.dart
│ ├── automotive.dart
│ ├── color.dart
│ ├── job.dart
│ ├── lorem.dart
│ ├── person.dart
│ ├── phone.dart
│ └── vehicle.dart
└── en_us.dart
For example below you can see the code in en/datasources/phone.dart
resource file with all the datasources in it. faker_x library uses phone_number
and international_phone_number
DataSources to generate those fake values for resource phone
and locale en_us
.
import 'package:faker_x/src/base/base.dart';
final phone_number = StringDataSource(
dataKey: DataKeys.phone_number,
locale: Locales.en_us,
values: [],
formats: [
Format('##########'),
],
);
final international_phone_number = StringDataSource(
dataKey: DataKeys.international_phone_number,
locale: Locales.en_us,
values: [],
formats: [
Format('+1##########'),
Format('001##########'),
],
);
when we run code generation scripts result in our generated FakerX
class will be seen like below example:
final phoneNumber = FakerX.localized.en_us.phone.phoneNumber;
final international = FakerX.localized.en_us.phone.internationalPhoneNumber;
a DataSource
is basically a fake value provider. it either provides a fake value through a list of hardcoded values or a builder method (or a list of Formats
in case of StringDataSource
).
There are 2 kinds of DataSources
that we have StringDataSource
, TypeDataSource
each DataSource
must contain a dataKey defined in DataKeys
class and a locale defined in Locales
class.
Each DataSource
can use either a list of values
or a builder
method to provide fake values.
StringDataSource
can be used to generate string values like names, urls and so forth. Below there are multiple examples of how StringDataSource can be used to generate fake string values.
- Using StringDataSource to generate string values from a list of string
const color_name = StringDataSource(
locale: Locales.en_us,
dataKey: DataKeys.color_name,
values: [
'AliceBlue',
'AntiqueWhite',
'Aqua',
'Aquamarine',
.
.
]
);
- Using StringDataSource to generate fake string values using builder method
final avatar_uri = StringDataSource.withBuilder(
dataKey: DataKeys.avatar_uri,
locale: Locales.en_us,
builder: (_, __) =>
'https://cloudflare-ipfs.com/avatar/${randomInt(1249)}.jpg',
);
NOTE: multiple types can be returned from StringDataSource
which are String
, Format
, StringOrFormat
If the builder function needs to accept arguments by the user, the Below example suits that purpose.
class ImageArgs {
final int width;
final int height;
final List<String> keywords;
ImageArgs({
@nonNullable required this.width,
@nonNullable required this.height,
@nonNullable required this.keywords,
});
}
final image = StringDataSource<ImageArgs>.withBuilder(
dataKey: DataKeys.image,
locale: Locales.en_us,
builder: (ImageArgs args, FakerXLocale locale) {
return _imageUrl(args.width, args.height, args.keywords);
},
);
first an ImageArgs
class is defined then we define our datasource class as StringDataSource<ImageArgs>
and then in our builder method parameter we define arguments as (ImageArgs args, FakerXLocale locale) {...}
.
NOTE: when defining an Args
class like ImageArgs
above
-
Make sure to add the arg type as the DataSource arg type eg: define data source like this
StringDataSource<ImageArg>
instead of justStringDataSource
. if you forget to do this, you will have runtime errors, possibly shown when generated tests for this added data source will run. -
make sure to define constructor arguments as named arguments.
-
If the argument is required make sure to add
@nonNullable
annotation on that argument. (this is because dart:mirror api does not support null safety) -
Using StringDataSource to generate fake values using formats. alternatively you can use
formats
to generate fake values
// In formats all # characters are converted into random int and all ? characters are converted into random letters and returned for
// example below format can be used to generate fake values like : 1AL3456
const license_plate = StringDataSource(
dataKey: DataKeys.license_plate,
locale: Locales.en_us,
values: [],
formats: [
// Alabama
Format("#??####"), // 1AL3456
Format("##??###"), // 32OL456
// Alaska
Format("### ???"), // 120 ALO
// American Samoa
Format("####"), // 9536
// Arizona
.
.
],
);
Also with Formats we can use string interpolation to get a random value from another DataSource that generates string values. Here is an example of full_name
DataSource. we just need to put the dataKey
of that DataSource
inside {{}}
like {{first_name}}
.
make sure its not hardcoded meaning instead of using {{first_name}}
you use this {{${DataKeys.first_name}}}
const full_name = StringDataSource(
dataKey: DataKeys.full_name,
locale: Locales.en_us,
formats: [
Format('{{${DataKeys.first_name_female}}} {{${DataKeys.last_name}}}', chance: 10),
Format('{{${DataKeys.first_name_male}}} {{${DataKeys.last_name}}}', chance: 1),
],
values: [],
);
In formats we can define chance occurrences for that format. to specify what are the chances we get a fake value from that format object as opposed to others in the same list. For example, in the above example, getting a fake value of full_name
generated using the first format is 10 times more than the second format.
With TypeDataSources we can define a datasource that returns a value of the Type that we have defined for it
final geo_location = TypeDataSource<GeoLocation, dynamic>.withBuilder(
dataKey: DataKeys.geo_location,
locale: Locales.en_us,
builder: (_, __) => GeoLocation(
-90 + randomDouble() * 90 * 2,
-180 + randomDouble() * 180 * 2,
),
);
define the return type in lib/src/base/types.dart
class GeoLocation {
final double latitude;
final double longitude;
GeoLocation(this.latitude, this.longitude);
@override
String toString() => '$latitude - $longitude';
}
- In defining our datasource above
TypeDataSource<GeoLocation, dynamic>
we have set the value type toGeoLocation
and builder method's argument type todynamic
since we don't require an argument from the user in our builder method. - type
GeoLocation
must be defined inlib/src/base/value_types.dart
Alternatively we can use a list of values
final product = TypeDataSource<Product, dynamic>(
dataKey: DataKeys.geo_location,
locale: Locales.en_us,
values: [
Product(...),
Product(...),
Product(...),
Product(...),
Product(...),
.
.
.
],
);
Let's say we want to add a new DataSource
to generate cat images. we need to add it to the relevant resource. in this case we are going to add a new cat_image
datasource to the image
resource, so in our generated FakerX
class we can have a getter method that generates cat images like below
final newCatImage = FakerX.defaultInstance.image.catImage;
Now that we want to add the Datasource we should decide whether we want to add it as a global datasource or only for a specific locale. if we add it as a global DataSource it will be available for all generated locales. all global DataSources are marked green in Resources and Localizations in docs.
For example DataSource ipv4
for resource internet
is defined globally. That means resource internet
has a getter method ipv4
, for all locales.
final i1 = FakerX.localized.fa_ir.internet.ipv4;
final i2 = FakerX.localized.en_us.internet.ipv4;
final i3 = FakerX.localized.es.internet.ipv4;
but if we define it only for a specific locale the generator getter method will only appear for that locale. For example, getter method neighborhood
for resource address
is only available for en_us
locale.
Let's add cat_image
datasource globally
first we define a new datakey called cat_image
in DataKeys
class in lib/src/base/keys.dart
NOTE: Make sure the data key you use for the datasources are not plural. for example use cat_image
instead of cat_images
for datakey value
class DataKeys{
// rest of the keys ...
static const cat_image = 'cat_image';
}
Then we add the cat_image
datasource the resource file in global directory in lib/src/locales/global/datasources/image.dart
final cat_image = StringDataSource.withBuilder(
dataKey: DataKeys.cat_image,
locale: Locales.en_us,
builder: (_, __) => 'https://cci.com/cats/${randomInt(550)}.jpg',
);
If we want to add it only to a specific locale like en_us
we add the datasource code above in lib/src/locales/en_us/datasources/image.dart
Now all we need to do is run the command bash script generate_and_test.sh
. This script will generate code
, tests
, example
& runs
dart format
, analyze
& test
commands. So run command.
$ bash generate_and_test.sh
NOTE: read the console logs it might tell you to run it again.
Let's say we want to add a new locale that does not exist in our library and so that we can generate fake values localized for that locale. let's try adding en_uk
locale
- First we need to run a script to create empty resource files and generate basic classes required. run below script
$ dart scripts/create_locale.dart en_uk
NOTE: read the console logs it might tell you to run it again.
- New files will be generated in the project with empty datasources that are required for each locale. you need to fill out these datasources with values (and more if you wish so).
├── en_uk
├── datasources
│ ├── address.dart
│ ├── animal.dart
│ ├── automotive.dart
│ ├── color.dart
│ ├── job.dart
│ ├── lorem.dart
│ ├── person.dart
│ ├── phone.dart
│ └── vehicle.dart
└── en_uk.dart
- then when you're done adding values you only need to run the generate command again.
$ bash generate_and_test.sh
NOTE: read the console logs it might tell you to run it again.
NOTE: With DataSources that use builder methods to generate the value we can use builder methods of other DataSources in them. for example:
final uri = StringDataSource<UriArgs>.withBuilder(
dataKey: DataKeys.uri,
locale: Locales.en_us,
builder: (UriArgs args, _) {
return Format('${args.protocol}://{{${DataKeys.domain_name}}}');
},
);
final https_url = StringDataSource.withBuilder(
dataKey: DataKeys.https_url,
locale: Locales.en_us,
builder: (_, __) {
// here we are calling build method on uri DataSource defined above
// to return an https url
// ! NOTE: call build method not builder callback
return uri.build(UriArgs(protocol: 'https')),
}
);
NOTE: There are a lot of warning and error messages to notify you if you have done something wrong and how you should fix them. so if you ever ran the generation command and saw an error or warning in the terminal just fix the code and rerun the command.
NOTE: even though a lot of checks have been set in scripts to predict and prevent if code is going to be generated incorrectly or with syntax error messages. this might happen. if you ran a generation command and there was syntax error in generated code in lib/
directory just silence the syntax error by commenting them out or removing them so you can run generate command again without dart telling you there are syntax errors need to be fixed. doesn't matter if you change generated files manually to silence syntax errors, because they will be regenerated. then change your code if necessary and rerun the generation command.