diff --git a/README.md b/README.md index b8be514..88d690d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,17 @@ To get the estimated reading time in seconds use the `calcReadingTimeInSec()` fu - View [Kotlin](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/kotlin/com/example/ReadingTimeExample.kt) or [Java](https://github.com/ethauvin/readingtime/blob/master/examples/src/main/java/com/example/ReadingTimeSample.java) Examples. +### Gradle, Maven, etc. +To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/readingtime/blob/master/examples/build.gradle.kts) file: + +```gradle +dependencies { + implementation("net.thauvin.erik:readingtime:0.9.1") +} +``` + +Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/artifact/net.thauvin.erik/readingtime/0.9.0/jar). ### Properties @@ -30,24 +40,26 @@ The following properties are available: ```kotlin ReadingTime( - text = "some_text", + text, wpm = 275, postfix = "min read", plural = "min read", excludeImages = false, - extra = 0 + extra = 0, + round = RoundingMode.HALF_DOWN ) ``` -Property | Description -:-------------------------- |:------------------------------------------------------------------- -`text` | The text to be evaluated. +Property | Description +:-------------------------- |:----------------------------------------------------------------------------------------------------------------------- +`text` | The text to be evaluated. (Required) `wpm` | The words per minute reading average. `postfix` | The value to be appended to the reading time. `plural` | The value to be appended if the reading time is more than 1 minute. `excludeImages` | Images are excluded from the reading time when set. `extra` | Additional seconds to be added to the total reading time. +`round` | The [rounding mode](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/RoundingMode.html) to apply. ### Functions @@ -73,15 +85,3 @@ A JSP tag is also available for easy incorporation into web applications: ``` None of the attributes are required. - -### Gradle, Maven, etc. - -To use with [Gradle](https://gradle.org/), include the following dependency in your [build](https://github.com/ethauvin/readingtime/blob/master/examples/build.gradle.kts) file: - -```gradle -dependencies { - implementation("net.thauvin.erik:readingtime:0.9.0") -} -``` - -Instructions for using with Maven, Ivy, etc. can be found on [Maven Central](https://search.maven.org/artifact/net.thauvin.erik/readingtime/0.9.0/jar). diff --git a/build.gradle.kts b/build.gradle.kts index 50ea9d2..f85863a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,16 +6,16 @@ import java.io.FileInputStream import java.util.* plugins { - id("com.github.ben-manes.versions") version "0.38.0" + id("com.github.ben-manes.versions") version "0.39.0" id("io.gitlab.arturbosch.detekt") version "1.17.1" + id("jacoco") + id("java") + id("java-library") + id("maven-publish") id("org.jetbrains.dokka") version "1.4.32" - id("org.jetbrains.kotlin.jvm") version "1.5.0" id("org.sonarqube") version "3.2.0" - java - `java-library` - `maven-publish` - jacoco - signing + id("signing") + kotlin("jvm") version "1.5.10" } description = "Estimated Reading Time for Blog Posts, Articles, etc." @@ -34,12 +34,12 @@ repositories { } dependencies { - implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + implementation(platform(kotlin("bom"))) implementation("org.jsoup:jsoup:1.13.1") - testImplementation("org.jetbrains.kotlin:kotlin-test") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit") + testImplementation(kotlin("test")) + testImplementation(kotlin("test-junit")) } java { diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 4e17654..2dd4ebc 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -1,11 +1,11 @@ - + + LongParameterList:ReadingTime.kt$ReadingTime$( text: String, wpm: Int = 275, var postfix: String = "min read", var plural: String = "min read", excludeImages: Boolean = false, extra: Int = 0, var round: RoundingMode = RoundingMode.HALF_DOWN ) MagicNumber:ReadingTime.kt$ReadingTime$10 MagicNumber:ReadingTime.kt$ReadingTime$12 MagicNumber:ReadingTime.kt$ReadingTime$3 - MagicNumber:ReadingTime.kt$ReadingTime$60 MagicNumber:ReadingTime.kt$ReadingTime$60.0 diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 94f1e9f..5ea0c36 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("org.jetbrains.kotlin.jvm") version "1.5.0" - id("com.github.ben-manes.versions") version "0.38.0" - application + id("application") + id("com.github.ben-manes.versions") version "0.39.0" + kotlin("jvm") version "1.5.10" } // ./gradlew run diff --git a/pom.xml b/pom.xml index 080401e..c3ec90c 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ org.jetbrains.kotlin kotlin-bom - 1.5.0 + 1.5.10 pom import @@ -50,7 +50,7 @@ org.jetbrains.kotlin kotlin-stdlib-jdk8 - 1.5.0 + 1.5.10 compile diff --git a/src/main/kotlin/net/thauvin/erik/readingtime/ReadingTime.kt b/src/main/kotlin/net/thauvin/erik/readingtime/ReadingTime.kt index f662f11..6934023 100644 --- a/src/main/kotlin/net/thauvin/erik/readingtime/ReadingTime.kt +++ b/src/main/kotlin/net/thauvin/erik/readingtime/ReadingTime.kt @@ -49,6 +49,7 @@ import java.math.RoundingMode * @param plural The value to be appended if the reading time is more than 1 minute. * @param excludeImages Images are excluded from the reading time when set. * @param extra Additional seconds to be added to the total reading time. + * @param round The [RoundingMode] to apply. Default is [RoundingMode.HALF_DOWN]. */ class ReadingTime @JvmOverloads constructor( text: String, @@ -56,7 +57,8 @@ class ReadingTime @JvmOverloads constructor( var postfix: String = "min read", var plural: String = "min read", excludeImages: Boolean = false, - extra: Int = 0 + extra: Int = 0, + var round: RoundingMode = RoundingMode.HALF_DOWN ) { companion object { private const val INVALID: Double = -1.0 @@ -108,7 +110,9 @@ class ReadingTime @JvmOverloads constructor( } /** - * Calculates and returns the reading time in seconds. + * Calculates and returns the reading time in seconds. + * + * `((word count / wpm) * 60) + images + extra` */ fun calcReadingTimeInSec(): Double { if (readTime == INVALID) { @@ -121,9 +125,11 @@ class ReadingTime @JvmOverloads constructor( /** * Calculates and returns the reading time. (eg. 1 min read) + * + * `(reading time in sec / 60) + postfix` */ fun calcReadingTime(): String { - val time = BigDecimal((calcReadingTimeInSec() / 60.0)).setScale(0, RoundingMode.HALF_DOWN) + val time = BigDecimal((calcReadingTimeInSec() / 60.0)).setScale(0, round) return if (time.compareTo(BigDecimal.ONE) == 1) { "$time $plural".trim() } else { @@ -131,6 +137,10 @@ class ReadingTime @JvmOverloads constructor( } } + /** + * 12 seconds for the first image, 11 for the second, and minus an additional second for each subsequent image. + * Any images after the tenth image are counted at 3 seconds. + */ private fun calcImgReadingTime(): Int { var time = 0 val imgCount = imgCount(text) diff --git a/src/test/kotlin/net/thauvin/erik/readingtime/ReadingTimeTest.kt b/src/test/kotlin/net/thauvin/erik/readingtime/ReadingTimeTest.kt index dd26929..3a2b334 100644 --- a/src/test/kotlin/net/thauvin/erik/readingtime/ReadingTimeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/readingtime/ReadingTimeTest.kt @@ -33,28 +33,16 @@ package net.thauvin.erik.readingtime import java.io.File +import java.math.RoundingMode import kotlin.test.Test import kotlin.test.assertEquals class ReadingTimeTest { - private val rt = ReadingTime("This is a test.\nWith an image: ") + private val img = """""" + private val rt = ReadingTime("This is a test.\nWith an image: $img") private val blogPost = File("src/test/resources/post.html").readText() private val mediumPost = File("src/test/resources/medium.html").readText() - private val twoSeventyFive = """one two three four five six seven eight nine ten one two three four five six seven -eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine -ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two -three four five six seven eight nine ten one two three four five six seven eight nine ten one two three four -five six seven eight nine ten one two three four five six seven eight nine ten one two three four five six -seven eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight -nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten -one two three four five six seven eight nine ten one two three four five six seven eight nine ten one two -three four five six seven eight nine ten one two three four five six seven eight nine ten one two three four -five six seven eight nine ten one two three four five six seven eight nine ten one two three four five six -seven eight nine ten one two three four five six seven eight nine ten one two three four five six seven eight -nine ten one two three four five six seven eight nine ten one two three four five six seven eight nine ten -one two three four five six seven eight nine ten one two three four five""" - private val tenImages = " " + - " " + private val twoSeventyFive = "word ".repeat(275) private fun calcImgTime(imgCount: Int): Double { var time = 0.0 @@ -75,100 +63,111 @@ one two three four five six seven eight nine ten one two three four five""" @Test fun testWordCount() { - assertEquals(0, ReadingTime.wordCount(" ")) - assertEquals(3, ReadingTime.wordCount("one two three")) - assertEquals(2, ReadingTime.wordCount(" one two ")) - assertEquals(7, ReadingTime.wordCount(rt.text)) - assertEquals(505, ReadingTime.wordCount(blogPost)) - assertEquals(391, ReadingTime.wordCount(mediumPost)) - assertEquals(275, ReadingTime.wordCount(twoSeventyFive)) - assertEquals(275, ReadingTime.wordCount("$twoSeventyFive ")) + assertEquals(1, ReadingTime.imgCount(rt.text), "text") + assertEquals(11, ReadingTime.imgCount(blogPost), "blogPost") + assertEquals(3, ReadingTime.imgCount(mediumPost), "mediumPost") + assertEquals(1, ReadingTime.imgCount("$twoSeventyFive $img"), "275 + image") + assertEquals(2, ReadingTime.imgCount("$twoSeventyFive $img $img"), "275 + 2 images") } @Test fun testReadingTimeInSec() { - assertEquals(calcReadingTime(rt.text, rt.wpm) + calcImgTime(1), rt.calcReadingTimeInSec()) + assertEquals(calcReadingTime(rt.text, rt.wpm) + calcImgTime(1), rt.calcReadingTimeInSec(), "text + image") - rt.text = " " - assertEquals(calcImgTime(2), rt.calcReadingTimeInSec()) + rt.text = "$img ${img.uppercase()}" + assertEquals(calcImgTime(2), rt.calcReadingTimeInSec(), "2 images") rt.excludeImages = true - assertEquals(0.0, rt.calcReadingTimeInSec()) + assertEquals(0.0, rt.calcReadingTimeInSec(), "image uppercase") rt.excludeImages = false rt.text = blogPost assertEquals( - calcReadingTime(rt.text, rt.wpm) + calcImgTime(11), rt.calcReadingTimeInSec() + calcReadingTime(rt.text, rt.wpm) + calcImgTime(11), rt.calcReadingTimeInSec(), "blogPost" ) rt.excludeImages = true - assertEquals(calcReadingTime(rt.text, rt.wpm), rt.calcReadingTimeInSec()) + assertEquals(calcReadingTime(rt.text, rt.wpm), rt.calcReadingTimeInSec(), "exclude images") rt.extra = 60 - assertEquals(calcReadingTime(rt.text, rt.wpm) + 60L, rt.calcReadingTimeInSec()) + assertEquals(calcReadingTime(rt.text, rt.wpm) + 60L, rt.calcReadingTimeInSec(), "extra 60") rt.extra = 0 rt.excludeImages = false rt.text = mediumPost rt.wpm = 300 - assertEquals(calcReadingTime(rt.text, 300) + calcImgTime(3), rt.calcReadingTimeInSec()) + assertEquals(calcReadingTime(rt.text, 300) + calcImgTime(3), rt.calcReadingTimeInSec(), "mediumPost 300 wpm") rt.wpm = 275 rt.text = "This is a test" - assertEquals(0.0, rt.calcReadingTimeInSec()) + assertEquals(0.0, rt.calcReadingTimeInSec(), "test") rt.text = twoSeventyFive - assertEquals(60.0, rt.calcReadingTimeInSec()) + assertEquals(60.0, rt.calcReadingTimeInSec(), "275") - rt.text = "$twoSeventyFive " - assertEquals(72.0, rt.calcReadingTimeInSec()) + rt.text = "$twoSeventyFive $img" + assertEquals(72.0, rt.calcReadingTimeInSec(), "275 + image") - rt.text = "$twoSeventyFive " - assertEquals(83.0, rt.calcReadingTimeInSec()) + rt.text = "$twoSeventyFive $img $img" + assertEquals(83.0, rt.calcReadingTimeInSec(), "275 + 2 images") - rt.text = "$twoSeventyFive $tenImages" - assertEquals(60.0 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3, rt.calcReadingTimeInSec()) + rt.text = "$twoSeventyFive ${img.repeat(10)}" + assertEquals(60.0 + 12 + 11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3, rt.calcReadingTimeInSec(), "10 images") - rt.text = "$twoSeventyFive $tenImages " - assertEquals(135.0 + 3, rt.calcReadingTimeInSec()) + rt.text = "$twoSeventyFive ${img.repeat(10)} $img" + assertEquals(135.0 + 3, rt.calcReadingTimeInSec(), "11 images") rt.text = "$twoSeventyFive $twoSeventyFive" - assertEquals(120.0, rt.calcReadingTimeInSec()) + assertEquals(120.0, rt.calcReadingTimeInSec(), "275*2") rt.text = "" - assertEquals(0.0, rt.calcReadingTimeInSec()) + assertEquals(0.0, rt.calcReadingTimeInSec(), "empty") } @Test fun testReadingTime() { rt.text = blogPost - assertEquals("2 min read", rt.calcReadingTime()) + assertEquals("2 min read", rt.calcReadingTime(), "blogPost") rt.plural = "mins read" - assertEquals("2 mins read", rt.calcReadingTime()) + assertEquals("2 mins read", rt.calcReadingTime(), "plural") rt.text = mediumPost rt.plural = "" - assertEquals("2", rt.calcReadingTime()) + assertEquals("2", rt.calcReadingTime(), "mediumPost") rt.text = "This is a test." rt.postfix = "" - assertEquals("0", rt.calcReadingTime()) + assertEquals("0", rt.calcReadingTime(), "test") rt.text = "" - assertEquals("0", rt.calcReadingTime()) + assertEquals("0", rt.calcReadingTime(), "empty") rt.text = twoSeventyFive - assertEquals("1", rt.calcReadingTime()) + assertEquals("1", rt.calcReadingTime(), "275") rt.text = "$twoSeventyFive $twoSeventyFive" - assertEquals("2", rt.calcReadingTime()) + assertEquals("2", rt.calcReadingTime(), "275 * 2") + } + + @Test + fun testRoundingMode() { + rt.text = blogPost + rt.round = RoundingMode.UP + assertEquals("3 min read", rt.calcReadingTime(), "UP") + + rt.text = mediumPost + rt.round = RoundingMode.DOWN + assertEquals("1 min read", rt.calcReadingTime(), "DOWN") } }