Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ name: Build and Test

on:
push:
branches: [ main, develop* ]
branches: [ release/*, develop*, hotfix/* ]
paths-ignore:
- '**.md'
- 'LICENSE'
- '.gitignore'
pull_request:
branches: [ main, develop* ]
branches: [ release/*, develop*, hotfix/* ]
paths-ignore:
- '**.md'
- 'LICENSE'
Expand Down Expand Up @@ -91,17 +91,3 @@ jobs:
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PRIVATE_KEY_PASS }}

- name: Publish to Maven Central Packages
if: >-
(github.event_name == 'push' && github.event.ref=='refs/heads/main') ||
(github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.base.ref == 'main')
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache -Pversion=${{ steps.version_step.outputs.SemVer }}
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PRIVATE_KEY_PASS }}
96 changes: 96 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Release

on:
release:
types: [created]

jobs:
build:

runs-on: ubuntu-latest

if: github.event.release.target_commitish == 'main'

permissions: # <--- ADD THIS SECTION
contents: read
packages: write # <--- Grants permission to write packages

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 0

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.2.0
with:
versionSpec: '6.2.x'

- name: Determine Version
id: version_step # step id used as reference for output values
uses: gittools/actions/gitversion/execute@v3.2.0
with:
useConfigFile: true
configFilePath: gitversion.yml

- name: Set version in Gradle build file
run: ./gradlew -Pversion=${{ steps.version_step.outputs.SemVer }}

- name: Build with Gradle
run: ./gradlew :lib:assemble

- name: Run JVM tests
run: ./gradlew :lib:jvmTest --info
continue-on-error: false

- name: Run Android Release tests
run: ./gradlew :lib:testReleaseUnitTest --info
continue-on-error: false

- name: Upload Test Report
uses: actions/upload-artifact@v4
with:
name: test-results
path: lib/build/test-results

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts-${{ steps.version_step.outputs.SemVer }}
path: |
lib/build/libs/*.jar
lib/build/outputs/aar/*.aar

- name: Set up GitHub Packages repository
run: |
mkdir -p ~/.m2
cp .github/settings.xml ~/.m2/settings.xml

- name: Publish to GitHub Packages
if: github.event_name != 'pull_request'
run: ./gradlew publishAllPublicationsToGitHubPackagesRepository --no-configuration-cache -Pversion=${{ steps.version_step.outputs.SemVer }}
env:
GITHUB_USERNAME: ${{ secrets.GITHUB_ACTOR }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PRIVATE_KEY_PASS }}

- name: Publish to Maven Central Packages
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache -Pversion=${{ steps.version_step.outputs.SemVer }}
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PRIVATE_KEY_PASS }}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,15 @@ data class ComputeSquareResult(val value: Int)

class MathClass {
val bus = KtBus.getDefault()

fun setup() {
bus.subscribe(this)
}

fun tearDown() {
bus.unsubscribe(this)
}

@RequestHandler
fun computeSquare(event: ComputeSquareRequest) : ComputeSquareResult {
return ComputeSquareResult(event.value * event.value)
Expand Down
43 changes: 27 additions & 16 deletions lib/src/commonMain/kotlin/org/holance/ktbus/KtBus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@

package org.holance.ktbus

import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
Expand All @@ -22,10 +12,21 @@ import kotlin.reflect.full.callSuspend
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.superclasses
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.jvmErasure
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull

const val DefaultChannel = ""

Expand Down Expand Up @@ -601,14 +602,25 @@ class KtBus(val config: KtBusConfig = KtBusConfig()) {
*/
fun subscribe(target: Any) {
if (subscriptions.containsKey(target)) {
logger?.w("Warning: Target ${target::class.simpleName} is already subscribed. Unsubscribe first to avoid duplicate handlers/subscriptions.")
logger?.w(
"Warning: Target ${target::class.simpleName} is already subscribed. Unsubscribe first to avoid duplicate handlers/subscriptions."
)
return
}

val targetClass = target::class
val jobs = subscriptions.computeIfAbsent(target) { mutableListOf() }

targetClass.memberFunctions.forEach { function ->
subscribeRecursive(target, targetClass, jobs)

// If no jobs were added for a new target, remove the empty list entry
if (jobs.isEmpty() && subscriptions[target]?.isEmpty() == true) {
subscriptions.remove(target)
}
}

private fun subscribeRecursive(target: Any, type: KClass<*>, jobs: MutableList<Job>) {
type.memberFunctions.forEach { function ->
// Handle @Subscribe annotations (Broadcast Events)
function.findAnnotation<Subscribe>()?.let {
processSubscriber(target, function, it, jobs)
Expand All @@ -619,9 +631,8 @@ class KtBus(val config: KtBusConfig = KtBusConfig()) {
processRequestHandler(target, function, it, jobs)
}
}
// If no jobs were added for a new target, remove the empty list entry
if (jobs.isEmpty() && subscriptions[target]?.isEmpty() == true) {
subscriptions.remove(target)
type.superclasses.forEach {
subscribeRecursive(target, it, jobs)
}
}

Expand Down Expand Up @@ -656,4 +667,4 @@ class KtBus(val config: KtBusConfig = KtBusConfig()) {
fun <T : Any> getRequestHandlerCount(requestType: KClass<T>): Int {
return requestHandlers[requestType]?.size ?: 0
}
}
}
22 changes: 22 additions & 0 deletions lib/src/commonMain/kotlin/org/holance/ktbus/samples/PostSamples.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.holance.ktbus.samples

import org.holance.ktbus.KtBus

class PostSamples {
val bus = KtBus.getDefault()
fun postEvent() {
bus.post(Event1("Hello, world!"))
}

fun postEventWithChannel() {
bus.post(Event1("Hello, world!"), "myChannel")
}

suspend fun postEventAsync() {
bus.postAsync(Event1("Hello, world!"))
}

suspend fun postEventWithChannelAsync() {
bus.postAsync(Event1("Hello, world!"), "myChannel")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.holance.ktbus.samples

import org.holance.ktbus.KtBus
import org.holance.ktbus.RequestHandler

class RequestSample {
data class MyRequest(val message: String)
data class MyResponse(val result: String)

class HandleRequest {
val bus = KtBus.getDefault()
fun setup() {
bus.subscribe(this)
}

fun tearDown() {
bus.unsubscribe(this)
}

@RequestHandler
fun handleRequest(request: MyRequest): MyResponse {
// Process the request and return a response
return MyResponse("Processed: ${request.message}")
}
}

fun sendRequest() {
val bus = KtBus.getDefault()
val request = MyRequest("Hello, KtBus!")
val response = bus.request<MyRequest, MyResponse>(request)
println("Response: ${response.result}")
}

suspend fun sendRequestAsync() {
val bus = KtBus.getDefault()
val request = MyRequest("Hello, KtBus!")
val response = bus.requestAsync<MyRequest, MyResponse>(request)
println("Response: ${response.result}")
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.holance.ktbus.samples

data class Event1(val message: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.holance.ktbus.samples

import org.holance.ktbus.*

class SubscribeSample {
val bus = KtBus.getDefault()

fun setup() {
bus.subscribe(this)
}

fun tearDown() {
bus.unsubscribe(this)
}

@Subscribe
fun onEvent(event: Event1) {
println("Received event: ${event.message}")
}

@Subscribe(channel = "myChannel")
fun onEventFromMyChannel(event: Event1) {
println("Received event from myChannel: ${event.message}")
}

@Subscribe(scope = DispatcherTypes.IO)
fun onEventOnIO(event: Event1) {
println("Received event and process on IO thread: ${event.message}")
}
}