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

Fixes #3313 Allow passing a ClassLoader to ResourceService and Webjar… #3370

Closed
wants to merge 1 commit into from
Closed

Fixes #3313 Allow passing a ClassLoader to ResourceService and Webjar… #3370

wants to merge 1 commit into from

Conversation

ashwinbhaskar
Copy link
Collaborator

…Service

Copy link
Member

@rossabaker rossabaker left a comment

Choose a reason for hiding this comment

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

Thanks. I like the unit tests. Not very functional, but neither is the underlying Java API, and they prove the functionality nicely.

Some compat and API concerns below.

@@ -55,7 +55,9 @@ object WebjarService {
* @param config The configuration for this service
* @return The HttpRoutes
*/
def apply[F[_]](config: Config[F])(implicit F: Sync[F], cs: ContextShift[F]): HttpRoutes[F] = {
def apply[F[_]](config: Config[F], classLoader: Option[ClassLoader] = None)(
Copy link
Member

Choose a reason for hiding this comment

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

This is not binary compatible, so we'll have to relegate this to master. (And we need to figure out why our MiMa is not running. This burned us on 0.20.22.)

Copy link
Collaborator Author

@ashwinbhaskar ashwinbhaskar Apr 30, 2020

Choose a reason for hiding this comment

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

@rossabaker oh okay, do you want me to raise this PR against master?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, unless we can figure out a way to make it binary compatible. We could if we left the original signature, and called the new one something other than apply. Like withClassLoader. But we'd probably want to deprecate that in 1.0 in favor of the builder.

@@ -55,7 +55,9 @@ object WebjarService {
* @param config The configuration for this service
* @return The HttpRoutes
*/
def apply[F[_]](config: Config[F])(implicit F: Sync[F], cs: ContextShift[F]): HttpRoutes[F] = {
def apply[F[_]](config: Config[F], classLoader: Option[ClassLoader] = None)(
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason to introduce another parameter rather than put this in Config?

I have a more radical idea: move this to the same kind of builder pattern we use for server builders, and unlike the case class config or default optional parameters, we'll be able to introduce more parameters without breaking compatibility in the future.

Copy link
Member

Choose a reason for hiding this comment

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

If this doesn't make any sense, I'd be happy to sketch one, and could follow the pattern on the other.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rossabaker I did think about moving it to Config. But then I felt that config is meant to tune something. Config didn't feel like the right place to put classloader in. For example something like shouldUseDefaultClassLoader = true would make sense as part of Config. But having the classloader itself as part of config kind of felt out of place.
Wdyt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rossabaker A builder like this??

  class WebjarServiceBuilder[F[_]] {
    private var webjarAssetFilter: WebjarAssetFilter = _ => true
    private var cacheStrategy: CacheStrategy[F] = NoopCacheStrategy[F]
    private var classLoader: Option[ClassLoader] = None

    def wid(webjarAssetFilter: WebjarAssetFilter): WebjarServiceBuilder[F] = {
      this.webjarAssetFilter = webjarAssetFilter
      this
    }

    def wid(cacheStrategy: CacheStrategy[F]): WebjarServiceBuilder[F] = {
      this.cacheStrategy = cacheStrategy
      this
    }

    def wid(classLoader: ClassLoader): WebjarServiceBuilder[F] = {
      this.classLoader = Some(classLoader)
      this
    }

    def build(implicit F: Sync[F], cs: ContextShift[F]): HttpRoutes[F] = ???
  }

Copy link
Member

Choose a reason for hiding this comment

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

That's pretty close. This is pretty similar to a builder in an object-oriented language, but our backend "builders" are immutable. Something like:

// private constructor: we want people to call it like `WebjarServiceBuilder[F].blah.build`
class WebjarServiceBuilder[F[_]] private (
  webjarAssetFilter: WebjarAssetFilter,
  cacheStrategy: CacheStrategy[F],
  classLoader: Option[ClassLoader]) {

  // You don't strictly need this, but it means you don't need to repeat arguments in all your `with*` methods below. 
  private def copy(
    webjarAssetFilter: WebjarAssetFilter = webjarAssetFilter,
    cacheStrategy: CacheStrategy[F] = cacheStrategy,
    classLoader: Option[ClassLoader] = classLoader) =
    new WebjarServiceBuilder(webjarAssetFilter, cacheStrategy, classLoader)

  // Each field gets its own "with" setter.
  def withWebjarAssetFilter(webjarAssetFilter: WebjarAssetFilter) = 
    copy(webjarAssetFilter = webjarAssetFilter)

  // and so on for cacheStrategy and classLoader

  // Or maybe call this `.toRoutes`?
  def build = ???
}

object WebjarServiceBuilder {
  // If you have arguments with no sensible default, pass them to apply.
  // This is also a good spot for constraints, if you have any.
  def apply[F[_]] = new WebjarServiceBuilder(
     // Supply your default arguments here
     webjarAssetFilter = _ => true,
     cacheStrategy = NoopCacheStrategy[F],
     classLoader = None)
}

I haven't tried to compile any of that, but look at the other files named *Builder for working examples.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rossabaker cool, got 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.

@rossabaker do you want to get rid of the class WebjarService.Config[F[_}] all together? That will impact the way it is interfaced with the outside world. Here is the line. The users of the library will now have to change their way of calling wejarService[F](..) method.

@rossabaker rossabaker added the breaking Change breaks compatibility (binary or source) label Apr 30, 2020
@ashwinbhaskar
Copy link
Collaborator Author

closing this pull request in favor of creating this pull request against master - #3373

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Change breaks compatibility (binary or source)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants