Skip to content

Commit

Permalink
Add en_CA instances, StringRegexPart (#18)
Browse files Browse the repository at this point in the history
* Add en_CA instances, StringRegexPart

* typo

* en_CA ResourceLoader

* Update resource loading

* StringRegexPart coverage

* Add test coverage across locales

* Add other case

* add codecov threshold

* at least 100 for tests
  • Loading branch information
etspaceman authored Jul 6, 2020
1 parent 53a4d7c commit 5e59208
Show file tree
Hide file tree
Showing 15 changed files with 523 additions and 403 deletions.
5 changes: 5 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
project:
default:
threshold: 5%
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A short description of the changes that were introduced

## Checklist (check all that apply)

- [ ] This change maintains backwards compatability
- [ ] This change maintains backwards compatibility
- [ ] I have introduced tests for all new features and changes
- [ ] I have added documentation covering all new features and changes
- [ ] This pull-request is ready for review
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ libraryDependencies ++= Seq(
TypesafeConfig,
PureConfig,
ApacheCommons,
Generex,
ScalaTest % Test,
ScalaTestPlusScalaCheck % Test
)
Expand Down
1 change: 1 addition & 0 deletions project/LibraryDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ object LibraryDependencies {
val OrganizeImports =
"com.github.liancheng" %% "organize-imports" % "0.3.1-RC3"
val PureConfig = "com.github.pureconfig" %% "pureconfig" % "0.13.0"
val Generex = "com.github.mifmif" % "generex" % "1.0.2"
}
3 changes: 3 additions & 0 deletions src/main/resources/en_CA.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include "en_CA/address.conf"
include "en_CA/internet.conf"
include "en_CA/phone.conf"
22 changes: 22 additions & 0 deletions src/main/resources/en_CA/address.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
address {
postal-code-builder = {options = [{parts = [{type = "string-regex-part", value = "[A-CEJ-NPR-TVXY][0-9][A-CEJ-NPR-TV-Z] ?[0-9][A-CEJ-NPR-TV-Z][0-9]"}]}]}
provinces = ["AB", "BC", "MB", "NB", "NL", "NS", "NU", "NT", "ON", "PE", "QC", "SK", "YT"]
full-address-builder = {
options = [
{parts = [
{type = "string-builder-part", value = ${address.street-address-builder}, suffix = ", "},
{type = "string-builder-part", value = ${address.city-builder}, suffix = ", "},
{type = "seq-string-part", value = ${address.provinces}, suffix = " "},
{type = "string-builder-part", value = ${address.postal-code-builder}}
]},
{parts = [
{type = "string-builder-part", value = ${address.street-address-builder}, suffix = " "},
{type = "seq-string-part", value = ${address.secondary-addresses}, suffix = ", "},
{type = "string-builder-part", value = ${address.city-builder}, suffix = ", "},
{type = "seq-string-part", value = ${address.provinces}, suffix = " "},
{type = "string-builder-part", value = ${address.postal-code-builder}}
]}
]
}
default-country = {code = "CA", name = "Canada"}
}
4 changes: 4 additions & 0 deletions src/main/resources/en_CA/internet.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
internet {
free-email-domains = [gmail.com, yahoo.ca, hotmail.com]
domain-suffixes = [ca, com, biz, info, name, net, org]
}
26 changes: 26 additions & 0 deletions src/main/resources/en_CA/phone.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
phone {
area-codes = ["204", "226", "236", "249", "250", "289", "306", "343", "365", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "579", "581", "587", "604", "613", "639", "647", "705", "709", "778", "780", "807", "819", "867", "873", "902", "905"]
exchange-codes = ["201", "202", "203", "205", "206", "207", "208", "209", "210", "212", "213", "214", "215", "216", "217", "218", "219", "224", "225", "227", "228", "229", "231", "234", "239", "240", "248", "251", "252", "253", "254", "256", "260", "262", "267", "269", "270", "276", "281", "283", "301", "302", "303", "304", "305", "307", "308", "309", "310", "312", "313", "314", "315", "316", "317", "318", "319", "320", "321", "323", "330", "331", "334", "336", "337", "339", "347", "351", "352", "360", "361", "386", "401", "402", "404", "405", "406", "407", "408", "409", "410", "412", "413", "414", "415", "417", "419", "423", "424", "425", "434", "435", "440", "443", "445", "464", "469", "470", "475", "478", "479", "480", "484", "501", "502", "503", "504", "505", "507", "508", "509", "510", "512", "513", "515", "516", "517", "518", "520", "530", "540", "541", "551", "557", "559", "561", "562", "563", "564", "567", "570", "571", "573", "574", "580", "585", "586", "601", "602", "603", "605", "606", "607", "608", "609", "610", "612", "614", "615", "616", "617", "618", "619", "620", "623", "626", "630", "631", "636", "641", "646", "650", "651", "660", "661", "662", "667", "678", "682", "701", "702", "703", "704", "706", "707", "708", "712", "713", "714", "715", "716", "717", "718", "719", "720", "724", "727", "731", "732", "734", "737", "740", "754", "757", "760", "763", "765", "770", "772", "773", "774", "775", "781", "785", "786", "801", "802", "803", "804", "805", "806", "808", "810", "812", "813", "814", "815", "816", "817", "818", "828", "830", "831", "832", "835", "843", "845", "847", "848", "850", "856", "857", "858", "859", "860", "862", "863", "864", "865", "870", "872", "878", "901", "903", "904", "906", "907", "908", "909", "910", "912", "913", "914", "915", "916", "917", "918", "919", "920", "925", "928", "931", "936", "937", "940", "941", "947", "949", "952", "954", "956", "959", "970", "971", "972", "973", "975", "978", "979", "980", "984", "985", "989"]
subscriber-number = "####"
extension = ${phone.subscriber-number}
phone-number-builder = {
options = [
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}], weight = 2},
{prefix = "(", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = ")-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}], weight = 2},
{prefix = "1-", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}], weight = 2},
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "."}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "."}, {type = "string-part", value = ${phone.subscriber-number}}], weight = 2},
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}, suffix = " "}, {prefix = "x", type = "string-part", value = ${phone.extension}}], weight = 3},
{prefix = "(", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = ")-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}, suffix = " "}, {prefix = "x", type = "string-part", value = ${phone.extension}}], weight = 3},
{prefix = "1-", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}, suffix = " "}, {prefix = "x", type = "string-part", value = ${phone.extension}}], weight = 3},
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "."}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "."}, {type = "string-part", value = ${phone.subscriber-number}, suffix = " "}, {prefix = "x", type = "string-part", value = ${phone.extension}}], weight = 3},
]
}
cell-number-builder = {
options = [
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}]},
{prefix = "(", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = ")-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}]},
{prefix = "1-", parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "-"}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "-"}, {type = "string-part", value = ${phone.subscriber-number}}]},
{parts = [{type = "seq-string-part", value = ${phone.area-codes}, suffix = "."}, {type = "seq-string-part", value = ${phone.exchange-codes}, suffix = "."}, {type = "string-part", value = ${phone.subscriber-number}}]},
]
}
}
5 changes: 3 additions & 2 deletions src/main/scala/faker/Faker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.scalacheck.Arbitrary

import faker.syntax.scalacheck._

final class Faker(private val locale: Locale) {
final class Faker(private[faker] val locale: Locale) {

implicit val loader: ResourceLoader = new ResourceLoader(locale)

Expand Down Expand Up @@ -179,6 +179,7 @@ final class Faker(private val locale: Locale) {

object Faker {
val default = new Faker(Locale.getDefault)
val en_US = new Faker(Locale.US)
val en = new Faker(Locale.ENGLISH)
val en_US = new Faker(Locale.US)
val en_CA = new Faker(Locale.CANADA)
}
35 changes: 26 additions & 9 deletions src/main/scala/faker/ResourceLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,36 @@ import java.util.Locale

import pureconfig._

final class ResourceLoader(private val locale: Locale) {
final class ResourceLoader(private[faker] val locale: Locale) {
private val country: Option[String] = {
val cty = Option(locale.getCountry)
if (cty.exists(_.isEmpty)) None else cty
}
private val language: Option[String] = {
val lang = Option(locale.getLanguage)
if (lang.exists(_.isEmpty)) None else lang
}

private val defaultConfig: ConfigObjectSource =
ConfigSource.resources("default.conf")
private val languageConfig: ConfigObjectSource =
ConfigSource.resources(s"${locale.getLanguage}.conf")
private val localeConfig: ConfigObjectSource =
ConfigSource.resources(s"${locale.toString}.conf")
private val languageConfig: Option[ConfigObjectSource] =
language.map(x => ConfigSource.resources(s"$x.conf"))
private val localeConfig: Option[ConfigObjectSource] = for {
lang <- language
cty <- country
} yield ConfigSource.resources(s"${lang}_$cty.conf")

// Fallback Pattern: en-US.conf -> en.conf -> default.conf
private val conf: ConfigSource =
localeConfig.optional
.withFallback(languageConfig.optional)
.withFallback(defaultConfig)
private val conf: ConfigSource = (languageConfig, localeConfig) match {
case (Some(langConf), Some(localeConf)) =>
localeConf.optional
.withFallback(langConf.optional)
.withFallback(defaultConfig)
case (Some(langConf), _) => langConf.optional.withFallback(defaultConfig)
case (_, Some(localeConf)) =>
localeConf.optional.withFallback(defaultConfig)
case _ => defaultConfig
}

def loadKey[A: ConfigReader: ClassTag](key: String): A =
conf.at(key).loadOrThrow[A]
Expand All @@ -28,6 +44,7 @@ final class ResourceLoader(private val locale: Locale) {
object ResourceLoader {
val default: ResourceLoader = new ResourceLoader(Locale.getDefault)
val US: ResourceLoader = new ResourceLoader(Locale.US)
val en_CA: ResourceLoader = new ResourceLoader(Locale.CANADA)

object Implicits {
implicit val defaultResourceLoader: ResourceLoader = default
Expand Down
13 changes: 13 additions & 0 deletions src/main/scala/faker/StringGenBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ object StringPart {
implicit val stringPartConfigReader: ConfigReader[StringPart] = deriveReader
}

private[faker] final case class StringRegexPart(
prefix: Option[String],
suffix: Option[String],
value: String
) extends StringGenBuilderPart {
val valueGen: Gen[String] = value.regexGen
}

object StringRegexPart {
implicit val stringRegexPartConfigReader: ConfigReader[StringRegexPart] =
deriveReader
}

private[faker] final case class StringBuilderPart(
prefix: Option[String],
suffix: Option[String],
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/faker/syntax/string.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package faker.syntax

import com.mifmif.common.regex.Generex
import org.scalacheck.Gen

object string extends FakerStringSyntax
Expand All @@ -16,5 +17,7 @@ object FakerStringSyntax {
if (char == '#') gen.flatMap(x => Gen.choose(0, 9).map(y => s"$x$y"))
else gen.map(y => y + char.toString)
}

def regexGen: Gen[String] = Gen.delay(new Generex(str).random())
}
}
14 changes: 10 additions & 4 deletions src/test/scala/faker/FakerArbitrarySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import org.scalatest.freespec.AnyFreeSpecLike
import org.scalatestplus.scalacheck.Checkers

trait FakerArbitrarySpec extends AnyFreeSpecLike with Checkers {
def testCanGen[A: Arbitrary](implicit CT: ClassTag[A]): Unit =
s"${CT.runtimeClass.getName} should generate faker data successfully" in {
check((_: A) => true)
}
val resourceLoaders: Seq[ResourceLoader] =
Seq(ResourceLoader.US, ResourceLoader.en_CA)
def testCanGen[A: Arbitrary](implicit
CT: ClassTag[A]
): Unit =
resourceLoaders.foreach(implicit loader =>
s"${CT.runtimeClass.getName} should generate faker data successfully for ${loader.locale}" in {
check((_: A) => true, minSuccessful(100))
}
)
}
Loading

0 comments on commit 5e59208

Please sign in to comment.