Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can generated interfaces extends custom interfaces? #1210

Closed
ursusursus opened this issue Feb 6, 2019 · 22 comments
Closed

Can generated interfaces extends custom interfaces? #1210

ursusursus opened this issue Feb 6, 2019 · 22 comments

Comments

@ursusursus
Copy link

No description provided.

@JakeWharton
Copy link
Member

JakeWharton commented Feb 6, 2019 via email

@ursusursus
Copy link
Author

ursusursus commented Feb 6, 2019

Im coming from Room, and I like having my enums nested inside such class.

data class Message(
    override val id: String,
    val type: Type,
    val subtype: Subtype,
    val user: String,
    val text: String,
    val isStared: Boolean,
    val timestampMicros: Long
) : Diffable<String> {
    enum class Type {
        MESSAGE, UNKNOWN
    }

    enum class Subtype {
        MESSAGE_COMMENT, UNKNOWN
    }
}

And sometimes I use Diffable interface

interface Diffable<T> {
    val id: T
}

class DiffCallback<T : Diffable<*>>(
    private val oldList: List<T>,
    private val newList: List<T>
) : DiffUtil.Callback() {

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        oldList[oldItemPosition].id == newList[newItemPosition].id

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
        oldList[oldItemPosition] == newList[newItemPosition]

    override fun getOldListSize() = oldList.size

    override fun getNewListSize() = newList.size
}

Minor: Im a bit uneasy that my model basically doesnt exist in kotlin, only as sql table schema, Id like just a marker interface (but I know you going to argue with join results)

@JakeWharton
Copy link
Member

One way to workaround that would be to supply a property reference in the constructor.

class DiffCallback<T>(
  private val oldList: List<T>,
  private val newList: List<T>,
  private val idSelector: (T) -> Long
)

Then you'd have to pass MyModel::id to the constructor.

@ursusursus
Copy link
Author

ursusursus commented Feb 6, 2019

Yea lambdas could work

In cash app / other production apps using sqldelight, you dont have a central Foo type? Everything is just a unrelated mapped projections of the queries even on same foo table, exposed all the way to the ui (unless some transformation in kotlin is necessary to parse it out - like grouping of lists etc, but not necessarily transforming from "db" to "domain" model)?

@AlecKazakova
Copy link
Collaborator

yup. We select the subset of data from our schema we need for the UI/logic, and operate directly on that.

For times where we want a single type to be used in multiple locations we use sqlite views, which essentially create a type over some projection, then you can select from that projection and receive the same type everywhere.

@JakeWharton
Copy link
Member

And if I hadn't been lazy I'd have a blog post about the view stuff out today. Next week!

@ursusursus
Copy link
Author

When I think about it, we all did this back in cursor days and nobody complained, but then we got corrupted by server side orm styles and select *

Thanks!

@ursusursus
Copy link
Author

ursusursus commented Feb 7, 2019

btw @AlecStrong do you have some rule of a thumb on naming of these subset projections?

also, projection is named after label of the operation, where in selects, id most likely expect the label / kotlin method to be plural, and then projection interface singular

@AlecKazakova
Copy link
Collaborator

nothing obvious no. I usually tie it to the views we're using the projection in. For Cash App we have a payment and customer table, but those are joined in an activity view which is used to power the activity ui.

Yea I see what you mean: getAllPayments has a method getAllPayments but then a type GetAllPayments. Another reason to use views! you have tighter control over the name since the result type will be the view name if you SELECT * from the view

@ursusursus
Copy link
Author

ursusursus commented Feb 7, 2019

Well yes, but for regular selects its kind of weird. I don't think full-blown name overriding is worth it but some convention for "if select label ends with "s" then -s from interface type" would be helpful I think. Its only natural to have getPayments(): List<Payment>

But now thinking about it, getAllPayments: List<AllPayment> is silly also. Maybe some syntax for select labels like getAllPayments, Payment ?

@JakeWharton
Copy link
Member

JakeWharton commented Feb 7, 2019 via email

@ursusursus
Copy link
Author

ursusursus commented Feb 14, 2019

btw @JakeWharton Ive found such use case. Imagine ui with 3 tabs displaying list of items, where each tab is just different WHERE over the same table (and most likely same projection, or superset). Therefore each tab extends BaseFooPresenter which has abstract method query(): List<Foo>. Mapping manually to some MappedFoo would obviously work, but having AFoo BFoo CFoo related (since its the same projection on the same table) via some Foo interface out of the box, would be nice

@JakeWharton
Copy link
Member

JakeWharton commented Feb 14, 2019 via email

@ashughes
Copy link

@JakeWharton Any update on the blog post? ;-)

@JakeWharton
Copy link
Member

JakeWharton commented Feb 28, 2019 via email

@ashughes
Copy link

Any chance I missed the blog post?

@JakeWharton
Copy link
Member

JakeWharton commented Jul 13, 2019 via email

@ursusursus
Copy link
Author

Imho sql views are overkill, they need to be migrated etc. I only want a common type for polymorphism sake. This is advantage of Room. However here I'm afraid only passin in the mapper interface and mapping manually to type which has the interface is only solution?

@JakeWharton
Copy link
Member

JakeWharton commented Jul 13, 2019 via email

@ursusursus
Copy link
Author

Well, views have their limitations, I think somehow intercepting the name of the query and passing in custom type would be cleaner. Or maybe somehow signal to the plugin that dont create wrapper type for selection, mapper will be passed to you

@ursusursus
Copy link
Author

What people usually do is have root type Entity, which has id field, so that can be used in generic crud functions on some kind of abstract store class

@ursusursus
Copy link
Author

Here is a real life example I encountered today

messageIdsByChannel:
SELECT id, remoteId
FROM message
WHERE remoteId IS NOT NULL AND channelId=? AND accountId=?;

messageIdsByThread:
SELECT id, remoteId
FROM message
WHERE remoteId IS NOT NULL AND c_messageRemoteId=? AND accountId=?;

This projection is represented by the same data class, and it makes 0 sense to make it a view

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants