Skip to content
This repository has been archived by the owner on Jul 4, 2023. It is now read-only.

Add new pour_bottle? DSL #47888

Closed
wants to merge 12 commits into from
Closed

Add new pour_bottle? DSL #47888

wants to merge 12 commits into from

Conversation

MikeMcQuaid
Copy link
Member

I dislike the previous one I created because it’s pretty opaque to users; they don’t know why a bottle is failing to pour. Replace it instead with one that outputs a message.

I’ll obviously add comments/tests once people 👍 the general approach.

# TODO
def pour_bottle?(reason, &block)
@pour_bottle_reason = reason
@pour_bottle_block = block
Copy link
Member

Choose a reason for hiding this comment

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

Some suggestions:

  • We should allow no reason. The current pour_bottle? nil looks weird.
  • If no reason is given, it may be a good idea to offer a default one.
  • Instead of introducing new pour_bottle_block variable, we can store it in pour_bottle? as define_method(: pour_bottle?, &block). As result, Formula#pour_bottle? should be tagged as private instead of deprecated. And you don't need to check pour_bottle_block existence in formula_installer. In general, it should be help to simplify the code and make it more readable.

Copy link
Member Author

Choose a reason for hiding this comment

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

We should allow no reason. The current pour_bottle? nil looks weird.

Agreed it's weird. I'd like to us avoid having no reason as it's user-hostile but I've thought of the right reason for this case.

Instead of introducing new pour_bottle_block variable, we can store it in pour_bottle? as define_method

Cool idea, will try it out.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think there should always be a reason. I cannot think of a situation where we would want to omit it and which default reason might apply then. But if there's a good example, I'd like to hear it.

@DomT4
Copy link
Member

DomT4 commented Jan 9, 2016

Agree with the comment Xu made but this is a lot better (and more obvious) than the existing system.

# shared with the mysql and mariadb formulae.
def datadir
@datadir ||= (var/"percona").directory? ? var/"percona" : var/"mysql"
end

def pour_bottle?
pour_bottle? do "The bottle needs a #{var}/mysql datadir (yours is #{var}/percona)."
Copy link
Contributor

Choose a reason for hiding this comment

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

Is do supposed to be at the end of this line?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope :D

@bfontaine
Copy link
Contributor

I’ve a style issue with a method that ends with a ? but takes arguments.

Edit: @UniqMartin reminded me that we have things like String#start_with? that take arguments. The proposed DSL method here is to change things, not to ask about some state, hence my issue. I won’t actively fight this if other maintainers are happy though.

@@ -61,8 +61,8 @@ def osmajor

# The bottles are built on systems with the CLT installed, and do not work
# out of the box on Xcode-only systems due to an incorrect sysroot.
def pour_bottle?
MacOS::CLT.installed?
pour_bottle? "The bottle needs the Xcode CLT to be installed." do
Copy link
Contributor

Choose a reason for hiding this comment

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

I find “Xcode CLT” to be massively confusing. Apple refers to them as “Command Line Tools for Xcode” and we usually just refer as “CLT” to them. Can we make that “The bottle needs the Command Line Tools to be installed.” instead (here and in all other formulae with the same condition)?

@UniqMartin
Copy link
Contributor

I like the idea of always providing a reason a lot. In the hopes that this PR gets merged, I already took the liberty to comment on minor stuff that should end up in the final implementation, one way or another.

However, the pour_bottle? <reason> { <condition> } DSL looks really weird to me. It also currently limits us to a single condition, if I'm not mistaken. Maybe we can make it look something like this:

bottle_needs do
  satisfy { OS::Mac::CLT.installed? }
  fail_message "The bottle needs the Command Line Tools to be installed."
end

The same formula could easily have a second bottle_needs block then:

bottle_needs do
  satisfy { HOMEBREW_PREFIX.to_s == "/usr/local" }
  fail_message "The bottle needs the prefix to be /usr/local (yours is #{HOMEBREW_PREFIX})."
end

I'm not entirely happy with the DSL keywords bottle_needs, satisfy, and fail_message, so naming suggestions are more than welcome.

@MikeMcQuaid
Copy link
Member Author

I’ve a style issue with a method that ends with a ? but takes arguments.

I think it's fine as ? indicates the return value will be boolean rather than arguments not being used. I've seen ? used with arguments pretty often.

@MikeMcQuaid
Copy link
Member Author

I've updated this based on some of the comments but left the DSL mostly intact as I think it's good for the use-case we need it for.

@UniqMartin
Copy link
Contributor

I still like the general idea and see it as an improvement. At the same time I continue to have a hard time to wrap my head around the proposed DSL. I strongly feel that the order of <reason> and <condition> in pour_bottle? <reason> { <condition> } feels backwards to me. Given that we should be using this DSL rather sparingly, I think we can safely trade terseness for being more expressive and I hope if we can come up with better keywords for the suggestion in #47888 (comment) this will ultimately result in greater clarity.

Sorry if I'm kinda repeating myself, but I'm too convinced this is going to be better long-term. However, if my proposal for an alternative DSL is really not that convincing, i.e. it turns out I'm the only one who thinks like that, feel free to ignore me. (I still favor this PR as-is over doing nothing.)

@UniqMartin
Copy link
Contributor

@MikeMcQuaid Addendum: If you want to move all core formulae to the new DSL, the recently changed fontconfig needs to be updated, too.

# If `pour_bottle?` returns `false` the user-visible reason to display for
# why they cannot use the bottle.
# @private
attr_reader :pour_bottle_reason
Copy link
Member

Choose a reason for hiding this comment

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

Could we have a better name for this variable? Because it's actually not pouring reason.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed.

Copy link
Contributor

Choose a reason for hiding this comment

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

:bottle_sealed_reason
:cap_sealed_reason
:bottle_opener_denied_reason
:pour_bottle_blocked_reason
:pour_bottle_denied_reason
:bottle_denied_reason
:bottle_unavailable_reason
:pour_denied_reason
:pour_bottle_unavailable_reason
:bring_your_own_glass_reason

Copy link
Member

Choose a reason for hiding this comment

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

Some variation of pour_bottle_impossible or bottle_unavailable_reason if you're not wed to the pour_* wording seems okay to me.

@xu-cheng
Copy link
Member

Instead of introducing new pour_bottle_block variable, we can store it in pour_bottle? as define_method(:pour_bottle?, &block). As result, Formula#pour_bottle? should be tagged as private instead of deprecated. And you don't need to check pour_bottle_block existence in formula_installer. In general, it should be help to simplify the code and make it more readable.

Repost it since it's buried in folded comment. What's your opinion of this? @MikeMcQuaid

@MikeMcQuaid
Copy link
Member Author

@UniqMartin My reasoning for the current approach is that is resembles keg_only and resource DSLs. I'd like more @Homebrew/maintainers input on which DSL they prefer, though.

@MikeMcQuaid
Copy link
Member Author

@xu-cheng I'm not sure I like using define_method like that; it feels odd to me to define a new class method based on a block.

@xu-cheng
Copy link
Member

@xu-cheng I'm not sure I like using define_method like that; it feels odd to me to define a new class method based on a block.

🆒

As for DSL. how about something like(as similar to DSL in Requirement):

pour_bottle do
  reason ""
  satisfy do
    ...
  end
end

@MikeMcQuaid
Copy link
Member Author

@xu-cheng Seems you agree with @UniqMartin on the DSL. I'll implement it that way 👍

@@ -12,7 +12,7 @@ class Lua < Formula
sha256 "a84d3ebd9afa4a61b0120471e5a0dfcc670d294701a64edebd25fcc815fe76f8" => :mavericks
end

def pour_bottle?
pour_bottle? "The bottle needs installed into /usr/local." do
Copy link
Contributor

Choose a reason for hiding this comment

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

s/needs/needs to be/

@MikeMcQuaid
Copy link
Member Author

Pushed an update with the new DSL. The main issue I'm having is I cannot get it to execute in the current formula's scope. Run brew install percona-server with this branch checked-out to see what I mean. If this cannot be resolved I think it's an argument for the previous implementation (but I think it probably can be).

@@ -60,7 +61,7 @@ def post_install
--- a/fc-cache/fc-cache.c
+++ b/fc-cache/fc-cache.c
@@ -187,13 +187,8 @@ scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force,

Copy link
Member

Choose a reason for hiding this comment

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

Space cannot be removed when it's from patch.

@xu-cheng
Copy link
Member

The main issue I'm having is I cannot get it to execute in the current formula's scope.

One possible solution I can think of:

class PourBottleCheck
  def initialize(formula)
    @formula = formula
  end

  def reason(reason)
    @formula.pour_bottle_check_unsatisfied_reason = reason
  end

  def satisfy(&block)
    @formula.send(:define_method, :pour_bottle?, &block)
  end
end

And use Formula#pour_bottle? directly to eval the block.

@ilovezfs
Copy link
Contributor

@xu-cheng Isn't this a more general problem with all of the dsl methods in a formula though? Shouldn't they all have transparent access to the formula's environment, variables, etc.?

@MikeMcQuaid
Copy link
Member Author

Updated with all comments addressed. Thanks for the sample code, @xu-cheng, worked great 👍

end

def reason(reason)
@formula.pour_bottle_check_unsatisfied_reason(reason)
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like it should have been an assignment instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is an assignment using our attr_rw setter method.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right; I wasn't even aware that attr_rw is a custom thing. Thanks for the clarification!

Copy link
Member

Choose a reason for hiding this comment

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

I think it's better to use attr_accessor rather than attr_rw here. The reason is attr_rw is majorly used as DSL method in the codebase. It's more straightful to use assignment here.

Also any chance to have a shorter variable name here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's better to use attr_accessor rather than attr_rw here.

🆗

Also any chance to have a shorter variable name here?

Given it's not used widely or externally and there was some justified confusion about the existing name I'd rather it was super explicit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Given it's not used widely or externally and there was some justified confusion about the existing name I'd rather it was super explicit.

I second that.

@UniqMartin
Copy link
Contributor

Thanks for implementing the suggested DSL! I'm very happy with how it looks in the formulae.

Do you have any idea why the build failed on Jenkins with a pour_bottle?-related error, but Travis did its thing without failure until it was stopped by the timeout?

@MikeMcQuaid
Copy link
Member Author

Added tests, addressed comments and pushed. Thanks!

@MikeMcQuaid MikeMcQuaid deleted the pour_bottle_reason branch February 18, 2016 10:43
MikeMcQuaid added a commit that referenced this pull request Feb 18, 2016
Check to see if `HEAD` is the same as what we have locally. If it is:
don't bother to `git fetch`.

Closes #47888.

Closes #49219.

Signed-off-by: Mike McQuaid <mike@mikemcquaid.com>
@Homebrew Homebrew locked and limited conversation to collaborators Jul 10, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants