Skip to content

Commit

Permalink
Closes #150 Shorten usage of Fields in include, where, and orderBy AP…
Browse files Browse the repository at this point in the history
…I functions (#151)
  • Loading branch information
vestrel00 authored Dec 25, 2021
1 parent 562ac1a commit 98edfd3
Show file tree
Hide file tree
Showing 42 changed files with 1,010 additions and 195 deletions.
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,25 +173,25 @@ the results) ordered by display name in descending order, matching ALL of these
```kotlin
val contacts = Contacts(context)
.query()
.where(
(Fields.Name.GivenName startsWith "leo") and
((Fields.Email.Address endsWith "gmail.com") or (Fields.Email.Address endsWith "hotmail.com")) and
(Fields.Address.Country equalToIgnoreCase "us") and
((Fields.Event.Date lessThan Date().toWhereString()) and (Fields.Event.Type equalTo EventEntity.Type.BIRTHDAY)) and
(Fields.Contact.Options.Starred equalTo true) and
(Fields.Nickname.Name equalTo "DarEdEvil") and
(Fields.Organization.Company `in` listOf("facebook", "FB")) and
(Fields.Note.Note.isNotNullOrEmpty())
)
.where {
(Name.GivenName startsWith "leo") and
(Email.Address { endsWith("gmail.com") or endsWith("hotmail.com") }) and
(Address.Country equalToIgnoreCase "us") and
(Event { (Date lessThan Date().toWhereString()) and (Type equalTo EventEntity.Type.BIRTHDAY) }) and
(Contact.Options.Starred equalTo true) and
(Nickname.Name equalTo "DarEdEvil") and
(Organization.Company `in` listOf("facebook", "FB")) and
(Note.Note.isNotNullOrEmpty())
}
.accounts(
Account("john.doe@gmail.com", "com.google"),
Account("john.doe@myspace.com", "com.myspace"),
)
.include(
Fields.Contact.Id,
Fields.Contact.DisplayNamePrimary,
Fields.Phone.Number
)
.include { setOf(
Contact.Id,
Contact.DisplayNamePrimary,
Phone.Number
) }
.orderBy(ContactsFields.DisplayNamePrimary.desc())
.offset(0)
.limit(5)
Expand Down Expand Up @@ -273,7 +273,7 @@ val emails = Contacts(context)
.data()
.query()
.emails()
.where(Fields.Email.Address endsWith "gmail.com")
.where { Email.Address endsWith "gmail.com" }
.orderBy(Fields.Email.Address.desc(ignoreCase = true))
.offset(0)
.limit(20)
Expand Down
5 changes: 3 additions & 2 deletions async/src/main/java/contacts/async/data/DataQueryAsync.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package contacts.async.data

import contacts.async.ASYNC_DISPATCHER
import contacts.core.AbstractDataFieldSet
import contacts.core.DataField
import contacts.core.data.DataQuery
import contacts.core.entities.ExistingDataEntity
Expand All @@ -15,7 +16,7 @@ import kotlin.coroutines.CoroutineContext
*
* See [DataQuery.find].
*/
suspend fun <F : DataField, E : ExistingDataEntity> DataQuery<F, E>.findWithContext(
suspend fun <F : DataField, S : AbstractDataFieldSet<F>, E : ExistingDataEntity> DataQuery<F, S, E>.findWithContext(
context: CoroutineContext = ASYNC_DISPATCHER
): List<E> = withContext(context) { find { !isActive } }

Expand All @@ -28,6 +29,6 @@ suspend fun <F : DataField, E : ExistingDataEntity> DataQuery<F, E>.findWithCont
*
* See [DataQuery.find].
*/
fun <F : DataField, E : ExistingDataEntity> DataQuery<F, E>.findAsync(
fun <F : DataField, S : AbstractDataFieldSet<F>, E : ExistingDataEntity> DataQuery<F, S, E>.findAsync(
context: CoroutineContext = ASYNC_DISPATCHER
): Deferred<List<E>> = CoroutineScope(context).async { find { !isActive } }
52 changes: 43 additions & 9 deletions core/src/main/java/contacts/core/BroadQuery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,40 @@ import contacts.core.util.unsafeLazy
* ## Usage
*
* Here is an example query that returns the first 10 [Contact]s, skipping the first 5, where the
* any Contact data (e.g. name, email, address, phone, etc) matches the search term "john", ordered
* by the Contact display name primary (given name first) in ascending order (ignoring case). Only
* Contacts with at least one RawContact belonging to the given account and groups are included.
* Only the full name and email address attributes of the [Contact] objects are included.
* any Contact data (e.g. name, email, address, phone, note, etc) partially matches the search term
* "john", ordered by the Contact display name primary (given name first) in ascending order
* (ignoring case). Include only Contacts with at least one RawContact belonging to the given
* account and groups. Include only the name and email properties of [Contact]s.
*
* In Kotlin,
*
* ```kotlin
* import contacts.core.Fields.Name
* import contacts.core.Fields.Address
* import contacts.core.ContactsFields.DisplayNamePrimary
* val contacts : List<Contact> = broadQuery.
* .accounts(account)
* .groups(groups)
* .include { Name.all + Address.all }
* .whereAnyContactDataPartiallyMatches("john")
* .orderBy(ContactsFields.DisplayNamePrimary.asc())
* .offset(5)
* .limit(10)
* .find()
* ```
*
* In Java,
*
* ```java
* import static contacts.core.Fields.*;
* import static contacts.core.OrderByKt.*;
*
* val contacts : List<Contact> = broadQuery.
* .accounts(account)
* .groups(groups)
* .include(Name, Address)
* .include(new ArrayList<>() {{
* addAll(Name.getAll());
* addAll(Address.getAll());
* }})
* .whereAnyContactDataPartiallyMatches("john")
* .orderBy(DisplayNamePrimary.asc())
* .orderBy(asc(ContactsFields.DisplayNamePrimary))
* .offset(5)
* .limit(10)
* .find()
Expand Down Expand Up @@ -270,6 +288,11 @@ interface BroadQuery {
*/
fun include(fields: Sequence<AbstractDataField>): BroadQuery

/**
* See [BroadQuery.include].
*/
fun include(fields: Fields.() -> Collection<AbstractDataField>): BroadQuery

/**
* Filters the [Contact]s partially matching the [searchString]. If not specified or null or
* empty, then all [Contact]s are returned.
Expand Down Expand Up @@ -334,6 +357,11 @@ interface BroadQuery {
*/
fun orderBy(orderBy: Sequence<OrderBy<ContactsField>>): BroadQuery

/**
* See [BroadQuery.orderBy].
*/
fun orderBy(orderBy: ContactsFields.() -> Collection<OrderBy<ContactsField>>): BroadQuery

/**
* Limits the maximum number of returned [Contact]s to the given [limit].
*
Expand Down Expand Up @@ -460,6 +488,9 @@ private class BroadQueryImpl(
}
}

override fun include(fields: Fields.() -> Collection<AbstractDataField>) =
include(fields(Fields))

override fun whereAnyContactDataPartiallyMatches(searchString: String?): BroadQuery = apply {
// Yes, I know DEFAULT_SEARCH_STRING is null. This reads better though.
this.searchString = searchString ?: DEFAULT_SEARCH_STRING
Expand All @@ -478,6 +509,9 @@ private class BroadQueryImpl(
}
}

override fun orderBy(orderBy: ContactsFields.() -> Collection<OrderBy<ContactsField>>) =
orderBy(orderBy(ContactsFields))

override fun limit(limit: Int): BroadQuery = apply {
this.limit = if (limit > 0) {
limit
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/contacts/core/Insert.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ interface Insert {
*/
fun include(fields: Sequence<AbstractDataField>): Insert

/**
* See [Insert.include].
*/
fun include(fields: Fields.() -> Collection<AbstractDataField>): Insert

/**
* Adds a new [NewRawContact] to the insert queue, which will be inserted on [commit].
* The new instance is configured by the [configureRawContact] function.
Expand Down Expand Up @@ -299,6 +304,9 @@ private class InsertImpl(
}
}

override fun include(fields: Fields.() -> Collection<AbstractDataField>) =
include(fields(Fields))

override fun rawContact(configureRawContact: NewRawContact.() -> Unit): Insert =
rawContacts(NewRawContact().apply(configureRawContact))

Expand Down
54 changes: 42 additions & 12 deletions core/src/main/java/contacts/core/Query.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ import contacts.core.util.unsafeLazy
* ## Usage
*
* Here is an example query that returns the first 10 [Contact]s, skipping the first 5, where the
* contact's name starts with "john" and has an email ending with "gmail", ordered by the name in
* ascending order (not ignoring case) and email (ignoring case) in descending order respectively.
* Only Contacts with at least one RawContact belonging to the given account are included. Only the
* full name and email address attributes of the [Contact] objects are included.
* contact's name starts with "john" and has an email ending with "gmail", order by favorite/starred
* status such that favorite/starred contacts appear first in the list AND order by display name
* primary in ascending order (from a to z ignoring case). Include only Contacts with at least one
* RawContact belonging to the given account. Include only name and email properties of [Contact]s.
*
* In Kotlin,
*
* ```kotlin
* import contacts.core.Fields.Name
* import contacts.core.Fields.Address
*
* val contacts : List<Contact> = query
* .accounts(account)
* .include(Name, Address)
* .where((Name.DisplayName startsWith "john") and (Email.Address endsWith "gmail"))
* .orderBy(Name.DisplayName.asc(), Email.Address.desc(true))
* .include { Name.all + Address.all }
* .where { (Name.DisplayName startsWith "john") and (Email.Address endsWith "gmail") }
* .orderBy(
* ContactsFields.Options.Starred.desc(),
* ContactsFields.DisplayNamePrimary.asc()
* )
* .offset(5)
* .limit(10)
* .find()
Expand All @@ -59,9 +59,15 @@ import contacts.core.util.unsafeLazy
*
* List<Contact> contacts = query
* .accounts(account)
* .include(Name, Address)
* .include(new ArrayList<>() {{
* addAll(Name.getAll());
* addAll(Address.getAll());
* }})
* .where(startsWith(Name.DisplayName, "john").and(endsWith(Email.Address, "gmail")))
* .orderBy(asc(Name.DisplayName), desc(Email.Address, true))
* .orderBy(
* desc(ContactsFields.Options.Starred),
* asc(ContactsFields.DisplayNamePrimary.asc)
* )
* .offset(5)
* .limit(10)
* .find();
Expand Down Expand Up @@ -190,6 +196,11 @@ interface Query {
*/
fun include(fields: Sequence<AbstractDataField>): Query

/**
* See [Query.include].
*/
fun include(fields: Fields.() -> Collection<AbstractDataField>): Query

/**
* Filters the [Contact]s matching the criteria defined by the [where]. If not specified or
* null, then all [Contact]s are returned.
Expand Down Expand Up @@ -250,6 +261,12 @@ interface Query {
*/
fun where(where: Where<AbstractDataField>?): Query

/**
* Same as [Query.where] except you have direct access to all properties of [Fields] in the
* function parameter. Use this to shorten your code.
*/
fun where(where: Fields.() -> Where<AbstractDataField>?): Query

/**
* Orders the returned [Contact]s using one or more [orderBy]s. If not specified, then contacts
* are ordered by ID in ascending order.
Expand Down Expand Up @@ -301,6 +318,11 @@ interface Query {
*/
fun orderBy(orderBy: Sequence<OrderBy<ContactsField>>): Query

/**
* See [Query.orderBy].
*/
fun orderBy(orderBy: ContactsFields.() -> Collection<OrderBy<ContactsField>>): Query

/**
* Limits the maximum number of returned [Contact]s to the given [limit].
*
Expand Down Expand Up @@ -415,11 +437,16 @@ private class QueryImpl(
}
}

override fun include(fields: Fields.() -> Collection<AbstractDataField>) =
include(fields(Fields))

override fun where(where: Where<AbstractDataField>?): Query = apply {
// Yes, I know DEFAULT_WHERE is null. This reads better though.
this.where = where ?: DEFAULT_WHERE
}

override fun where(where: Fields.() -> Where<AbstractDataField>?) = where(where(Fields))

override fun orderBy(vararg orderBy: OrderBy<ContactsField>) = orderBy(orderBy.asSequence())

override fun orderBy(orderBy: Collection<OrderBy<ContactsField>>) =
Expand All @@ -433,6 +460,9 @@ private class QueryImpl(
}
}

override fun orderBy(orderBy: ContactsFields.() -> Collection<OrderBy<ContactsField>>) =
orderBy(orderBy(ContactsFields))

override fun limit(limit: Int): Query = apply {
this.limit = if (limit > 0) {
limit
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/contacts/core/Update.kt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ interface Update {
*/
fun include(fields: Sequence<AbstractDataField>): Update

/**
* See [Update.include].
*/
fun include(fields: Fields.() -> Collection<AbstractDataField>): Update

/**
* Adds the given [rawContacts] to the update queue, which will be updated on [commit].
*/
Expand Down Expand Up @@ -289,6 +294,9 @@ private class UpdateImpl(
}
}

override fun include(fields: Fields.() -> Collection<AbstractDataField>) =
include(fields(Fields))

override fun rawContacts(vararg rawContacts: ExistingRawContactEntity) =
rawContacts(rawContacts.asSequence())

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/contacts/core/Where.kt
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,9 @@ class Where<out T : Field> private constructor(
/**
* Construct a copy of this where with the new field type determined by [substituteField].
*/
// Note that this function cannot be inlined because it is recursive (it calls itself). The
// compiler is smart enough to detect this. If the compiler allowed inlining a recursive
// function, then the code generation would look forever! Think about it!
internal fun <R : Field> copyWithNewFieldType(substituteField: (Field) -> Field): Where<R> {
/*
* Okay. Time for some "recursion" hehehe =). You know, I can't believe this interview
Expand Down
Loading

0 comments on commit 98edfd3

Please sign in to comment.