Coder Social home page Coder Social logo

event-binder's Introduction

How do I use It?

Declare your interfaces

interface DataListView {
    val requestList: Event<DataListRequest>
    val onError: Receiver<Error>
    val onLoading: ReceiverU
    val displayList: Receiver<List<Data>>
}

interface DataListService {
    val loadData: Receiver<DataListRequest>
    val errorReceived: Event<Error>
    val startedFetching: EventU
    val dataListReceived: Event<List<Data>>
}

The Event<T> tipe defines an action this interface can perform on another. In this example, DataListView can request a list. The Receiver<T> tipe defines an action this interfaces can receive. In this example DataListView can receive and Error. The EventU and ReceiverU are aliases for events and receivers that don't need to send or receive a parameter

Bind them

fun ViewBindable.bindDataList(view: DataListView, service: dataListService) = bind {
    view.displayList via service.dataListReceived
    view.onError via service.errorReceived
    view.onLoading via service.startedFetching
    view.requestList via service.loadData
}

We can define a function on how this interfaces comunicate binding the emission of events with the corresponding receivers. The function is defined as an extension of:

  • ViewBindable: if we want the binding to live in the lifeCycle of an android component (for example: Fragments).
  • CoBindable: if we want the binding to live in the scope of a coroutine.
  • Bindable: if we want the binds to live for the whole application live.

Implement the interfaces

class NetworkDataListService(repository: NetworkDataRepository) : DataListService, Bindable {
    override val errorReceived: Event<Error> = event(retainValue = false)
    override val startedFetching: EventU = event(retainValue = false)
    override val dataListReceived: Event<List<Data>> = event()
    override val loadData: Receiver<DataListRequest> = suspendReceiver {
        startedFetching()
        runCatching { repository.getList(it) }.fold({
            dataListReceived(it)
        }, {
            errorReceived(Error(it))
        })
    }
}

The service must implement Bindable in order to be able to create event and receiver implementations. Android LifeCycleOwners (for example: Activities) don't need to do this as extensions for them are provided out of the box.

We implement our events, as they are outbound actions no implementation is required, and the Bindable.event method will handle it for us. The optional parameter retainValue is used to specify if we want the lastest data sent to be received by new subscriptors. In this case, we want new binded views to display the last available data, but we don't want the last error to be displayed every time a view is binded with this service.

Then we implement the receiver. receiver and suspendReceiver are provided, suspendReceiver provides a CoroutineScope with the same lifeCycle as the binds, so coroutines are fully suported. Inside the receiver we can call the events as if the were methods of the class which will call the receivers binded to them.

How do I test my code?

A fluent test API is provided to ease the testing process.

We can test that when an Event is received another one is dispatched

    @Test
    fun `load data dispatches fetch and error events if request fails`() {
        val error = Error()
        coEvery { repository.getList(any()) } throws error

        sut.loadData withParameter request shouldDispatch {
            sut.startedFetching withParameter Unit
            sut.errorReceived withType Error::class
            sut.errorReceived assertOverParameter {
                assertEquals(error, it.cause)
            }
        }
    }

several methods such as withAny Paramenter, withType or never Dispatched are provided.

We can manualy dispatch events to verify the behaviour of other components

        val request = DataListRequest()
        (sut.loadData withParameter request).dispatch()
        coVerify { repository.getList(request) }

We can create new binds just for testing pourposes

the method testBind is provided so that test binds can be created and executed in a test environment. For example

    @Test
    fun `test bind example`() = testBind {
        sut.errorReceived via receiver {
            //this will be called when errorReceived is called
        }
        (sut.loadData withParameter request).dispatch()
    }

event-binder's People

Contributors

martori avatar

Watchers

christian ciceri avatar James Cloos avatar Evgeny avatar Sergi Fernández avatar Joaquín Caro avatar  avatar David Gómez avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.