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
Provide friendlier access to request variants #18939
Conversation
(variants & candidates).any? | ||
end | ||
|
||
def to_ary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is it this gets us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows request.variant
to be compared to other arrays, since Array comparison methods like #==, #&, and #| call to_ary
on the other array:
request.variant = :phone
# => #<VariantInquirer @variants=[:phone]>
[:phone] == request.variant
# => true
Existing framework code relies on this behavior in a few places.
@dhh would an ArrayInquirer also imply an ArrayWithIndifferentAccess? or do a runtime cast e.g. in this example: class SoftString
def to_s
'soft'
end
end
class HardString
def to_str
'hard'
end
end
ai = ActiveSupport::ArrayInquirer.new(['foo', :bar, SoftString.new, HardString.new]) What should each of these return? true, false, or cast exception? ai.foo?
ai.bar?
ai.soft?
ai.hard? And BTW this is not current behavior of StringInquirer: ActiveSupport::StringInquirer.new(:foo)
# => TypeError: can't convert Symbol into String It only works for Strings and objects which implement (perhaps for consistency, |
I'm thinking it's a runtime comparison. So it works with both Indifferent and not. I also think that we should have a SymbolInquery. I've wanted that in the past. |
@dhh do you prefer two separate inquirer classes, one just for strings and one just for symbols, rather than soften StringInquirer to accept symbols (and just cast them to_s on initialize)? |
I was thinking about StringInquirer taking a symbol, but then it’s not a symbol any more, it’s a string. I’d rather just be explicit and have a SymbolInquirer that inherits from Symbol.
|
Anyway, let's split those off into their own tickets. Don't want to swamp this ticket with those concerns. I'm very eager to get this in sooner rather than later. |
Update: I've extracted an ArrayInquirer, but need to write tests. My schedule is a little crazy for the time being, but I will get this done. |
Awesome, George. Not a mega rush. Thanks for helping with this!
|
@@ -288,7 +288,7 @@ def method_missing(name, *args, &block) | |||
end | |||
|
|||
def variant | |||
if @variant.nil? | |||
if @variant.empty? | |||
@variants[:none] || @variants[:any] | |||
elsif (@variants.keys & @variant).any? | |||
@variant.each do |v| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're already touching this code, maybe we can improve it. How about this? 😄
def variant
if @variant.empty?
@variants[:none] || @variants[:any]
else
@variants[ @variant.find { |v| @variants.key?(v) } || :any ]
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took it a step further:
def variant
if @variant.empty?
@variants[:none] || @variants[:any]
else
@variants[variant_key]
end
end
private
def variant_key
@variant.find { |variant| @variants.key?(variant) } || :any
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, 👍
Yes please merge this 👍 My initial inclination when working with Failing that, I was surprised/dismayed by the fact Moreover, since |
@sdhull: Agree about pluralizing |
If left unset, will Edit: nm, I see it will be default to |
|
||
def method_missing(name, *args) | ||
if name[-1] == '?' | ||
any?(name[0..-2]) || any?(name[0..-2].to_sym) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for consistency between string/symbol, this check should be at the any?
level:
def any?(*candidates, &block)
if candidates.none?
super
else
candidates.detect do |candidate|
include?(candidate) || include?(candidate.to_sym)
end || false
end
end
private
def method_missing(name, *args)
if name[-1] == '?'
any?(name[0..2])
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggested code changes are a little off. We don't need to check for none?
or explicitly return false. But I agree with moving the check 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Looks great to me, 👍 |
assert_not @array_inquirer.any? { |v| v == :desktop } | ||
end | ||
|
||
def test_respond_to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert the shortcut method since you added it:
def test_inquiry_method
assert_equal @array_inquirer, [:mobile, :tablet].inquiry
end
Wrapping an array in an `ArrayInquirer` gives a friendlier way to check its string-like contents. For example, `request.variant` returns an `ArrayInquirer` object. To check a request's variants, you can call: request.variant.phone? request.variant.any?(:phone, :tablet) ...instead of: request.variant.include?(:phone) request.variant.any? { |v| v.in?([:phone, :tablet]) } `Array#inquiry` is a shortcut for wrapping the receiving array in an `ArrayInquirer`: pets = [:cat, :dog] pets.cat? # => true pets.ferret? # => false pets.any?(:cat, :ferret} # => true
Provide friendlier access to request variants
Thank you so much. The code is great, but I'd not include the |
Great work, George! 🤘 |
I think the ArrayInquirer makes as much sense as the StringInquirer. I'm certainly going to use it. I think it's fair to highlight that it's there. |
I mean, it will still be highlighted in the CHANGELOG and documentation but I don't think it needs a |
I like the #inquiry form, as it makes it possible to use this inline on a passed parameter without extra setup. And it then maps directly to String#inquiry as well.
|
👍, so I'll add back |
Add back at b5c3502 |
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com> Co-authored-by: Hartley McGuire <skipkayhil@gmail.com>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com> Co-authored-by: Hartley McGuire <skipkayhil@gmail.com> Co-authored-by: Petrik de Heus <petrik@deheus.net>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com> Co-authored-by: Hartley McGuire <skipkayhil@gmail.com> Co-authored-by: Petrik de Heus <petrik@deheus.net>
Add prose and code samples for: * `request.variant=` * `request.variant` Add sections to the Action View and Action Controller guides, along with some code samples. The majority of these changes were excised from past pull requests, such as [rails#12977][] and [rails#18939][]. [rails#12977]: rails#12977 [rails#18939]: rails#18939 Co-authored-by: zzak <zzakscott@gmail.com> Co-authored-by: Hartley McGuire <skipkayhil@gmail.com> Co-authored-by: Petrik de Heus <petrik@deheus.net>
Currently,
request.variant
is a dumb reader for the underlying array used to store the request's variants. If you set the variant like so:...then
request.variant
will return[:phone]
. That can be cumbersome to deal with, since you have to checkrequest.variant.first == :phone
,request.variant == [:phone]
,request.variant.to_a.include?(:phone)
, etc.This PR implements a friendlier interface:
Fixes #18933.