Skip to content

ducktape 0.2.8

Latest
Compare
Choose a tag to compare
@arainko arainko released this 19 Mar 06:06
· 43 commits to series/0.2.x since this release
cd0aafe

ducktape 0.2.8

Highlights of this release:

  • Support for field and case name transformations via Field.modifySourceNames, Field.modifyDestNames, Case.modifySourceNames and Case.modifyDestNames and modifiers for those config options that let you fine-tune the transformations further:
    • .regional(_.path.to.field) - this will apply renames to all transformations 'below' the path you've picked (this is the default)
    • .typeSpecific[SomeType] - this will apply the renames to subtypes of a type you've picked
    • .local(_.path.to.field) - this will apply renames to the field corresponding to that field/enum subcase (but not the field and the enum subcase themselves!)

Head on over to the docs to see more.

Examples
 test("dest field regional flag covers the selected case class and everything below it") {
    case class Source(int: Int, str: String, level1: SourceLevel1)
    case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
    case class SourceLevel2(INT: Int, STR: String)

    case class Dest(int: Int, str: String, level1: DestLevel1)
    case class DestLevel1(INT: Int, STR: String, LEVEL2: DestLevel2)
    case class DestLevel2(INT: Int, STR: String)

    val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
    val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))

    assertTransformsConfigured(source, expected)(
      Field.modifyDestNames(_.toUpperCase).regional(_.level1)
    )
  }
  test("source case regional flag covers the selected subtype and everything below (picked as a field in case class)") {
    case class Source(int: Int, level1: SourceEnum)
    case class Dest(int: Int, level1: DestEnum)

    enum DestEnum {
      case one(int: Int, str: String)
      case two(int: Int, str: String, level1: DestLevel1, level2: DestLevel1Enum)
      case three(int: Int, str: String)
    }

    enum SourceEnum {
      case ONE(int: Int, str: String)
      case TWO(int: Int, str: String, level1: SourceLevel1, level2: SourceLevel1Enum)
      case THREE(int: Int, str: String)
    }

    enum SourceLevel1Enum {
      case One
      case Two
    }

    enum DestLevel1Enum {
      case one
      case two
    }

    case class SourceLevel1(int: Int)
    case class DestLevel1(int: Int)

    assertTransformsConfigured(
      Source(1, SourceEnum.TWO(2, "2", SourceLevel1(3), SourceLevel1Enum.Two)),
      Dest(1, DestEnum.two(2, "2", DestLevel1(3), DestLevel1Enum.two))
    )(
      Case.modifySourceNames(_.toLowerCase).regional(_.level1)
    )
  }

Examples of local flags:

test("dest field local flag covers the selected case class and nothing else") {
    case class Source(int: Int, str: String, level1: SourceLevel1)
    case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
    case class SourceLevel2(int: Int, str: String)

    case class Dest(int: Int, str: String, level1: DestLevel1)
    case class DestLevel1(int: Int, str: String, level2: DestLevel2)
    case class DestLevel2(int: Int, str: String)

    val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
    val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))

    assertTransformsConfigured(source, expected)(
      Field.modifyDestNames(_.toUpperCase).local(_.level1)
    )
  }

and an example of a type specific flags:

 test("source field type specific flag covers all subtypes of an enum and nothing else (even when the enum is nested)") {
    case class Source(int: Int, level1: SourceEnum)
    case class Dest(int: Int, level1: DestEnum)

    sealed trait SourceEnum

    object SourceEnum {
      sealed trait NestLevel1 extends SourceEnum
      sealed trait NestLevel2 extends SourceEnum

      case class One(int: Int, str: String) extends NestLevel2
      case class Two(int: Int, str: String, level1: SourceLevel1) extends NestLevel1
      case class Three(int: Int, str: String) extends NestLevel2
    }

    sealed trait DestEnum

    object DestEnum {
      sealed trait NestLevel1 extends DestEnum
      sealed trait NestLevel2 extends DestEnum

      case class One(INT: Int, STR: String) extends NestLevel2
      case class Two(INT: Int, STR: String, LEVEL1: DestLevel1) extends NestLevel1
      case class Three(INT: Int, STR: String) extends NestLevel2
    }

    case class SourceLevel1(int: Int)
    case class DestLevel1(int: Int)

    assertTransformsConfigured(
      Source(1, SourceEnum.Two(2, "2", SourceLevel1(3))),
      Dest(1, DestEnum.Two(2, "2", DestLevel1(3)))
    )(
      Field.modifySourceNames(_.toUpperCase).typeSpecific[SourceEnum]
    )
  }
  • Renamer is the thing that fuels the rename DSL:
sealed trait Renamer {

  /**
   * Equivalent to `String#toUpperCase`
   */
  def toUpperCase: Renamer

  /**
   * Equivalent to `String#toLowerCase`
   */
  def toLowerCase: Renamer

  /**
   * Equivalent to the function `(str: String) => if str == from then to else str`
   */
  def rename(from: String, to: String): Renamer

  /**
   * Equivalent to `String#replace(target, replacement)`
   */
  def replace(target: String, replacement: String): Renamer

  /**
   * Equivalent to the function `(str: String) => Pattern.compile(pattern).matcher(str).replaceAll(replacement)`
   */
  def regexReplace(pattern: String, replacement: String): Renamer

  /**
   * Equivalent to `String#stripPrefix(prefix)`
   */
  def stripPrefix(prefix: String): Renamer

  /**
   * Equivalent to `String#stripSuffix(suffix)`
   */
  def stripSuffix(suffix: String): Renamer

  /**
   * Equivalent to `String#capitalize`
   */
  def capitalize: Renamer
}
  • Most of the API has finally had scaladoc with examples added to it

What's Changed

Full Changelog: v0.2.7...v0.2.8