ruippeixotog / scala-scraper

A Scala library for scraping content from HTML pages

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problems with injecting HtmlUnitBrowser via Guice

piercelamb opened this issue · comments

Long time user of your lib here. I'm using Play Framework 2.5 and trying to follow the DI patterns they use to avoid global state.

I'm passing HtmlUnitBrowser like this

class YelpService @Inject() (actorSystem: ActorSystem)(browser: HtmlUnitBrowser)

and getting

ProvisionException: Unable to provision, see the following errors:

  1. Could not find a suitable constructor in net.ruippeixotog.scalascraper.browser.HtmlUnitBrowser. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
    at net.ruippeixotog.scalascraper.browser.HtmlUnitBrowser.class(HtmlUnitBrowser.scala:33)

There doesn't appear to be a zero-argument constructor afaik but it DOES have a default value. Perhaps I should be asking the Guice guys about this, but do you see a way of fixing this?

I'm not sure if this is a good design pattern but

class HtmlUnitBrowserFactory extends HtmlUnitBrowser {
new HtmlUnitBrowser
}

and

class YelpService @Inject() (actorSystem: ActorSystem)(browser: HtmlUnitBrowserFactory)

worked

Hi! Yeah, it seems that even if a Scala constructor has default values in all of its parameters, it isn't compiled down to a no-arg constructor. According to this, the arguments are computed on call site instead, so that the original method will always be called with the same number of arguments (all of them).

Unfortunately, there is no perfect solution for this... dependency injection frameworks (in particular Java ones) that impose restrictions or require modifications to the dependency classes usually don't play well with Scala. Your solution seems to be the best, if not the only, workaround I can think of that doesn't require modifications to this library. On the scala-scraper side, the only solution I can think of is to add a no-arg constructor to HtmlUnitBrowser, like:

class HtmlUnitBrowser(browserType: BrowserVersion = BrowserVersion.CHROME) extends Browser {
  def this() = this(BrowserVersion.CHROME)

But if in the future I wanted to add an argument to HtmlUnitBrowser that had no default value, I would have to remove the no-arg constructor. If you really want to keep using Guice, creating your own wrapper/factory class or subclass seems to me the best approach.

I'm closing this, as the subclass approach seems to have worked.