Skip to content

Instantly share code, notes, and snippets.

@TWiStErRob
Last active June 12, 2023 00:21
Show Gist options
  • Select an option

  • Save TWiStErRob/507a44b2a746b1cd9cab8c2169658f59 to your computer and use it in GitHub Desktop.

Select an option

Save TWiStErRob/507a44b2a746b1cd9cab8c2169658f59 to your computer and use it in GitHub Desktop.

Revisions

  1. TWiStErRob revised this gist Jan 20, 2023. No changes.
  2. TWiStErRob revised this gist Feb 28, 2019. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,8 @@ To edit this in IntelliJ IDEA:
    * import the project from `idea/build.gradle`.
    Note: `@DependsOn` dependencies have to be duplicated in `build.gradle`

    Debugging:
    Debugging: https://youtrack.jetbrains.com/issue/KT-30211

    `java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -noverify -cp "%KOTLIN_HOME%\lib\kotlin-compiler.jar" org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -cp "%KOTLIN_HOME%/lib/kotlin-main-kts.jar" -script github.main.kts twisterrob`

    Note: default encoding is not UTF-8 on Windows, so adding `java -Dfile.encoding=UTF-8` fixes the output issue of `ó`.
  3. TWiStErRob revised this gist Feb 28, 2019. 2 changed files with 7 additions and 3 deletions.
    6 changes: 5 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,11 @@ To edit this in IntelliJ IDEA:
    * import the project from `idea/build.gradle`.
    Note: `@DependsOn` dependencies have to be duplicated in `build.gradle`

    Debugging:
    `java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -noverify -cp "%KOTLIN_HOME%\lib\kotlin-compiler.jar" org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -cp "%KOTLIN_HOME%/lib/kotlin-main-kts.jar" -script github.main.kts twisterrob`

    Note: default encoding is not UTF-8 on Windows, so adding `java -Dfile.encoding=UTF-8` fixes the output issue of `ó`.

    TODO:
    * debugging
    * rx -> coroutines (may need to use Retrofit 2.6-SNAPSHOT)

    4 changes: 2 additions & 2 deletions build.gradle
    Original file line number Diff line number Diff line change
    @@ -10,12 +10,12 @@ dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    implementation "org.jetbrains.kotlin:kotlin-script-runtime"
    implementation "org.jetbrains.kotlin:kotlin-main-kts"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
    //implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"

    implementation "com.squareup.retrofit2:retrofit:2.5.0"
    implementation "com.squareup.retrofit2:converter-gson:2.5.0"
    implementation "com.squareup.retrofit2:adapter-rxjava:2.5.0"
    implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
    //implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"

    implementation "junit:junit:4.13-beta-2"
    implementation "org.hamcrest:hamcrest-all:1.3"
  4. TWiStErRob revised this gist Feb 28, 2019. 1 changed file with 7 additions and 13 deletions.
    20 changes: 7 additions & 13 deletions github.main.kts
    Original file line number Diff line number Diff line change
    @@ -3,9 +3,10 @@
    @file:Suppress("RedundantSuspendModifier")
    @file:Repository("https://jcenter.bintray.com")
    // prod
    // TODO something is wrong with coroutines, when run from cmd it picks up an older version?
    @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1")
    @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.1")
    // These coroutines don't work because kotlin-main-kts has embedded coroutines
    // see https://youtrack.jetbrains.com/issue/KT-30210
    //@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1")
    //@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.1")
    @file:DependsOn("com.google.code.gson:gson:2.8.0")
    @file:DependsOn("io.reactivex:rxjava:1.3.8")
    @file:DependsOn("com.squareup.okhttp3:okhttp:3.12.0")
    @@ -14,7 +15,7 @@
    @file:DependsOn("com.squareup.retrofit2:converter-gson:2.5.0")
    @file:DependsOn("com.squareup.retrofit2:adapter-rxjava:2.5.0")
    // TODO https://github.com/square/retrofit/pull/2886 merged 2019-02, will be in Retrofit 2.6
    @file:DependsOn("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")
    //@file:DependsOn("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")
    // test
    @file:DependsOn("junit:junit:4.13-beta-2")
    @file:DependsOn("org.hamcrest:hamcrest-all:1.3")
    @@ -74,8 +75,7 @@ class Script(
    ) {

    companion object {
    // GlobalScope.launch { // java.lang.NoSuchMethodError: kotlinx.coroutines.BuildersKt.launch$default
    suspend fun di(userName: String) {
    fun di(userName: String) {
    val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    @@ -88,13 +88,12 @@ class Script(
    }
    }

    suspend fun start(userName: String) {
    fun start(userName: String) {
    val user = github.user(userName)
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.trampoline())
    .toBlocking() // because background threads are daemon, need to keep alive
    .value()
    //val user = github.userCo(userName).await()
    handleUser(user)
    }

    @@ -145,11 +144,6 @@ interface GitHub {
    @GET("users/{user}")
    fun user(@Path("user") userName: String): Single<User>

    // @GET("users/{user}")
    // fun userCo(@Path("user") userName: String): Deferred<User>
    // @GET("users/{user}")
    // suspend fun userSus(@Path("user") userName: String): User

    data class User(
    @SerializedName("login")
    val login: String,
  5. TWiStErRob revised this gist Feb 28, 2019. 2 changed files with 5 additions and 2 deletions.
    5 changes: 4 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,14 @@
    The file name has to end in `.main.kts` and `kotlin-main-kts.jar` has to be on classpath. See https://youtrack.jetbrains.com/issue/KT-27853
    Notes:
    * The file name has to end in `.main.kts` and `kotlin-main-kts.jar` has to be on classpath. See https://youtrack.jetbrains.com/issue/KT-27853
    * Transitive dependencies have to be explicitly listed, and version conflicts manually resolved by hard-coding the right versions.

    `kotlinc`: https://kotlinlang.org/docs/tutorials/command-line.html

    To edit this in IntelliJ IDEA:
    * create and `idea` folder next to the `.kts` file
    * save `build.gradle` to that folder
    * import the project from `idea/build.gradle`.
    Note: `@DependsOn` dependencies have to be duplicated in `build.gradle`

    TODO:
    * debugging
    2 changes: 1 addition & 1 deletion github.main.kts
    Original file line number Diff line number Diff line change
    @@ -109,8 +109,8 @@ class Script(

    @Fixture lateinit var fixtUser: GitHub.User

    private lateinit var sut: Script
    private lateinit var fixture: JFixture
    private lateinit var sut: Script

    @Before fun setUp() {
    fixture = JFixture()
  6. TWiStErRob revised this gist Feb 28, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    The file name has to end in `.main.kts` and `kotlin-main-kts.jar` has to be on classpath. See https://youtrack.jetbrains.com/issue/KT-27853

    `kotlinc`: https://kotlinlang.org/docs/tutorials/command-line.html

    To edit this in IntelliJ IDEA:
    * create and `idea` folder next to the `.kts` file
    * save `build.gradle` to that folder
  7. TWiStErRob created this gist Feb 28, 2019.
    11 changes: 11 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    The file name has to end in `.main.kts` and `kotlin-main-kts.jar` has to be on classpath. See https://youtrack.jetbrains.com/issue/KT-27853

    To edit this in IntelliJ IDEA:
    * create and `idea` folder next to the `.kts` file
    * save `build.gradle` to that folder
    * import the project from `idea/build.gradle`.

    TODO:
    * debugging
    * rx -> coroutines (may need to use Retrofit 2.6-SNAPSHOT)

    27 changes: 27 additions & 0 deletions build.gradle
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    plugins {
    id "org.jetbrains.kotlin.jvm" version "1.3.21"
    }

    repositories {
    jcenter()
    }

    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    implementation "org.jetbrains.kotlin:kotlin-script-runtime"
    implementation "org.jetbrains.kotlin:kotlin-main-kts"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"

    implementation "com.squareup.retrofit2:retrofit:2.5.0"
    implementation "com.squareup.retrofit2:converter-gson:2.5.0"
    implementation "com.squareup.retrofit2:adapter-rxjava:2.5.0"
    implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"

    implementation "junit:junit:4.13-beta-2"
    implementation "org.hamcrest:hamcrest-all:1.3"
    implementation "com.flextrade.jfixture:jfixture:2.7.2"
    implementation "org.mockito:mockito-core:2.24.5"
    implementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
    }

    sourceSets.main.java.srcDirs new File(rootDir, "..")
    242 changes: 242 additions & 0 deletions github.main.kts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,242 @@
    // Try "%KOTLIN_HOME%\bin\kotlinc" -cp "%KOTLIN_HOME%/lib/kotlin-main-kts.jar" -script github.main.kts twisterrob

    @file:Suppress("RedundantSuspendModifier")
    @file:Repository("https://jcenter.bintray.com")
    // prod
    // TODO something is wrong with coroutines, when run from cmd it picks up an older version?
    @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1")
    @file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.1")
    @file:DependsOn("com.google.code.gson:gson:2.8.0")
    @file:DependsOn("io.reactivex:rxjava:1.3.8")
    @file:DependsOn("com.squareup.okhttp3:okhttp:3.12.0")
    @file:DependsOn("com.squareup.okio:okio:1.15.0")
    @file:DependsOn("com.squareup.retrofit2:retrofit:2.5.0")
    @file:DependsOn("com.squareup.retrofit2:converter-gson:2.5.0")
    @file:DependsOn("com.squareup.retrofit2:adapter-rxjava:2.5.0")
    // TODO https://github.com/square/retrofit/pull/2886 merged 2019-02, will be in Retrofit 2.6
    @file:DependsOn("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")
    // test
    @file:DependsOn("junit:junit:4.13-beta-2")
    @file:DependsOn("org.hamcrest:hamcrest-all:1.3")
    @file:DependsOn("com.flextrade.jfixture:jfixture:2.7.2")
    @file:DependsOn("org.mockito:mockito-core:2.24.5")
    @file:DependsOn("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
    @file:DependsOn("net.bytebuddy:byte-buddy:1.9.7")
    @file:DependsOn("net.bytebuddy:byte-buddy-agent:1.9.7")
    @file:DependsOn("org.objenesis:objenesis:2.6")

    import com.flextrade.jfixture.FixtureAnnotations
    import com.flextrade.jfixture.JFixture
    import com.flextrade.jfixture.annotations.Fixture
    import com.google.gson.annotations.SerializedName
    import com.nhaarman.mockitokotlin2.whenever
    import kotlinx.coroutines.runBlocking
    import org.hamcrest.MatcherAssert.assertThat
    import org.hamcrest.Matchers.containsString
    import org.hamcrest.Matchers.hasSize
    import org.junit.Before
    import org.junit.Test
    import org.junit.runner.JUnitCore
    import org.mockito.Mock
    import org.mockito.MockitoAnnotations
    import retrofit2.Retrofit
    import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
    import retrofit2.converter.gson.GsonConverterFactory
    import retrofit2.http.GET
    import retrofit2.http.Path
    import rx.Single
    import rx.schedulers.Schedulers
    import kotlin.system.exitProcess

    //Class.forName("kotlinx.coroutines.BuildersKt").declaredMethods.forEach(::println)

    main(*args)

    /**
    * @see https://youtrack.jetbrains.com/issue/KT-27853
    * @see https://github.com/Kotlin/KEEP/blob/scripting/proposals/scripting-support.md
    */
    @Suppress("KDocUnresolvedReference")
    fun main(vararg args: String) = runBlocking {
    test()

    if (args.size != 1) {
    help()
    exitProcess(1)
    }

    Script.di(args[0])
    }

    class Script(
    private val github: GitHub,
    private val output: (String) -> Unit
    ) {

    companion object {
    // GlobalScope.launch { // java.lang.NoSuchMethodError: kotlinx.coroutines.BuildersKt.launch$default
    suspend fun di(userName: String) {
    val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    //.addCallAdapterFactory(CoroutineCallAdapterFactory())
    .build()
    val github = retrofit.create(GitHub::class.java)

    Script(github, ::println).start(userName)
    }
    }

    suspend fun start(userName: String) {
    val user = github.user(userName)
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.trampoline())
    .toBlocking() // because background threads are daemon, need to keep alive
    .value()
    //val user = github.userCo(userName).await()
    handleUser(user)
    }

    private fun handleUser(user: GitHub.User) {
    output("${user.login}: ${user.name} works at ${user.company}")
    }

    @Suppress("FunctionName", "unused")
    class ScriptTest {

    @Mock lateinit var mockGithub: GitHub

    @Fixture lateinit var fixtUser: GitHub.User

    private lateinit var sut: Script
    private lateinit var fixture: JFixture

    @Before fun setUp() {
    fixture = JFixture()
    FixtureAnnotations.initFixtures(this, fixture)
    MockitoAnnotations.initMocks(this)
    sut = Script(mockGithub, ::capture)
    }

    @Test fun `github user gets printed`() = runBlocking {
    val fixtUserName = fixture.create(String::class.java)
    whenever(mockGithub.user(fixtUserName)).thenReturn(Single.just(fixtUser))
    //whenever(mockGithub.userCo(fixtUserName)).thenReturn(async { fixtUser })

    sut.start(fixtUserName)

    assertThat(captured, hasSize(1))
    val output = captured[0]
    assertThat(output, containsString(fixtUser.login))
    assertThat(output, containsString(fixtUser.name))
    assertThat(output, containsString(fixtUser.company))
    }

    private val captured = mutableListOf<String>()
    private fun capture(output: String) {
    captured.add(output)
    }
    }
    }

    interface GitHub {

    @GET("users/{user}")
    fun user(@Path("user") userName: String): Single<User>

    // @GET("users/{user}")
    // fun userCo(@Path("user") userName: String): Deferred<User>
    // @GET("users/{user}")
    // suspend fun userSus(@Path("user") userName: String): User

    data class User(
    @SerializedName("login")
    val login: String,
    @SerializedName("id")
    val id: String,
    @SerializedName("node_id")
    val nodeId: String,
    @SerializedName("avatar_url")
    val avatarUrl: String,
    @SerializedName("gravatar_id")
    val gravatarId: String,
    @SerializedName("url")
    val url: String,
    @SerializedName("html_url")
    val htmlUrl: String,
    @SerializedName("followers_url")
    val followersUrl: String,
    @SerializedName("following_url")
    val followingUrl: String,
    @SerializedName("gists_url")
    val gistsUrl: String,
    @SerializedName("starred_url")
    val starredUrl: String,
    @SerializedName("subscriptions_url")
    val subscriptionsUrl: String,
    @SerializedName("organizations_url")
    val organizationsUrl: String,
    @SerializedName("repos_url")
    val reposUrl: String,
    @SerializedName("events_url")
    val eventsUrl: String,
    @SerializedName("received_events_url")
    val receivedEventsUrl: String,
    @SerializedName("type")
    val type: String,
    @SerializedName("site_admin")
    val siteAdmin: Boolean,
    @SerializedName("name")
    val name: String,
    @SerializedName("company")
    val company: String,
    @SerializedName("blog")
    val blog: String,
    @SerializedName("location")
    val location: String,
    @SerializedName("email")
    val email: String,
    @SerializedName("hireable")
    val hireable: Boolean,
    @SerializedName("bio")
    val bio: String,
    @SerializedName("public_repos")
    val publicRepos: Int,
    @SerializedName("public_gists")
    val publicGists: Int,
    @SerializedName("followers")
    val followers: Int,
    @SerializedName("following")
    val following: Int,
    @SerializedName("created_at")
    val createdAt: String,
    @SerializedName("updated_at")
    val updatedAt: String
    )
    }

    fun help() {
    val dollar = '$'
    println(
    """
    Usage
    * Windows: "%KOTLIN_HOME%\bin\kotlinc" -cp "%KOTLIN_HOME%/lib/kotlin-main-kts.jar" -script github.main.kts <username>
    * Unix: "${dollar}{KOTLIN_HOME}/bin/kotlinc" -cp "${dollar}{KOTLIN_HOME}/lib/kotlin-main-kts.jar" -script github.main.kts <username>
    """.trimIndent()
    )
    }

    fun test() {
    JUnitCore.runClasses(
    Script.ScriptTest::class.java
    ).run {
    val stats = "in ${runTime}ms: ran ${runCount} (ignored ${ignoreCount}), failed ${failureCount}"
    if (wasSuccessful()) {
    println("Self test complete $stats")
    } else {
    println("Self test failed $stats")
    failures.forEach(::println)
    }
    }
    }