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

Handle date/time conversion in runtime via instantiable FormattedResources #390

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

drewhamilton
Copy link
Collaborator

Implements Patrick's idea from #258. Closes #230.

This pulls all android.icu imports out of generated code and into the runtime, allowing substitution of non-Android ICU dependencies in JVM unit tests. A generic DateTimeConverter interface is introduced to format date/time parameters. The default AndroidDateTimeConverter implementation instantiates android.icu.util.Calendars. JVM tests can substitute a JvmDateTimeConverter into their FormattedResources instance to instantiate com.ibm.icu.util.Calendars, avoiding the need for Robolectric to format date/time strings.

This also makes the generated FormattedResources an instantiable class, rather than an object, so the appropriate DateTimeConverter can be injected. A default AndroidFormattedResources property is introduced so simple static access is still supported.

.addProperty(
PropertySpec.builder(
name = "dateTimeConverter",
type = Types.DateTimeConverter.parameterizedBy(ANY),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's not a more concise way to share the ParameterSpec and the PropertySpec that have the same name and type, is there?

@@ -59,8 +60,35 @@ internal fun writeResources(
""".trimIndent(),
)
.addImport(packageName = packageName, "R")
.addProperty(
PropertySpec.builder(
name = "AndroidFormattedResources",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went back and forth on how to name these:

  • BaseFormattedResources and FormattedResources was Patrick's original suggestion, and allows for the simplest transition for current consumers. But it's the base class that most consumers would probably deal with, so giving it the longer name feels clunky. And having many different classes generated with the same prefix Base might be confusing since it is not a common base.
  • FormattedResources and AndroidFormattedResources is what I've gone with. The generic type has the snappier name. But it makes the transition for current consumers more involved. And using the prefix Android might seem redundant for an Android-targeted library.
  • Another idea: Rename AndroidFormattedResources to FR. This makes static invocations somewhat parallel to static invocations of the standard Android resource: R.string.whateverFR.whatever(args). But it's still an involved transition from the previous Paraphrase API. And maybe too cute.

Curious what others think for naming these two.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the Base prefix either, I'm happy to go with either of the other two options. Another option might be a base of ParaphraseResources and implementations of AndroidResources and JvmResources.

I'm not sure there's a perfect naming scheme and I'm also not sure it's a big deal. I'm cool with just picking something and shipping it!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like ParaphraseResources because it steps around the verb-tense problem with "Formatted" and also reminds you what this thing is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would also let me include a deprecated val FormattedResources with a built-in ReplaceWith to the new name, if we want to get fancy and make the transition easy.

@drewhamilton drewhamilton force-pushed the drew/handle-date-time-conversion-in-runtime-via-injection branch 3 times, most recently from f4873dc to 0070c19 Compare July 31, 2024 21:20
Copy link
Collaborator

@theisenp theisenp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, seems like this worked out pretty well! Thanks for putting it together 🙏

Now that we have both this and #258, does one approach feel better than the other?

Comment on lines 90 to 92
private fun appSamples(
formattedResources: AppFormattedResources,
): List<Sample> = listOf(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wanted to keep this as a val with static references to AppAndroidFormattedResources that would still work right? Are we just switching to a fun to demonstrate that you can pass the resources as the interface type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly, just demonstrating interaction with the interface type.

Comment on lines 29 to 32
private val stringResolver = FakeStringResolver(
R.string.library_date_argument to "{release_date, date, short}",
R.string.library_time_argument to "{showtime, time, short}",
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the resolver type here useful, or could we just have the Map<Int, String>?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can switch to a map if you prefer. I think I was just writing what felt like a real app might do; write a fake stand-in for an Android resource-resolving interface.

import java.util.Locale
import org.junit.Test

class FormattedResourcesTest {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea here just to show people how they might override the FormattedResources type in tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests that date/time resolution works in a JVM test. This test fails on main because of the ICU4J/android.icu discrepancy.

@@ -59,8 +60,35 @@ internal fun writeResources(
""".trimIndent(),
)
.addImport(packageName = packageName, "R")
.addProperty(
PropertySpec.builder(
name = "AndroidFormattedResources",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the Base prefix either, I'm happy to go with either of the other two options. Another option might be a base of ParaphraseResources and implementations of AndroidResources and JvmResources.

I'm not sure there's a perfect naming scheme and I'm also not sure it's a big deal. I'm cool with just picking something and shipping it!

@drewhamilton
Copy link
Collaborator Author

Now that we have both this and #258, does one approach feel better than the other?

This one feels better. We've all been through the swapping-static-references-in-tests phase before and agree it's painful, so let's not go there.

@drewhamilton drewhamilton force-pushed the drew/handle-date-time-conversion-in-runtime-via-injection branch from 0070c19 to 12d4335 Compare October 18, 2024 16:17
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

Successfully merging this pull request may close these issues.

Improve JVM testing for date/time format args
2 participants