chell
chell

Reputation: 7866

trouble with this case statement Ruby

Can someone help me understand how to write this case statement properly its not working and as a NOOB I have no idea how to fix it:

def hide_link?(link, mailing)
  case link
  when 'edit' &&  ['sent', 'sending', 'archived'].include?(mailing.status)
    return true
  when 'send_schedule' &&  ['sent', 'sending', 'archived'].include?(mailing.status)
    return true
  when 'archive' && ['archived'].include?(mailing.status)
    puts "I should be in here"
    return true
  else 'dashboard' && ['sending', 'draft'].include?(mailing.status)
    return true
  end
end

Basically I want to return true when the link matches certain criteria.

Upvotes: 1

Views: 230

Answers (3)

Wayne Conrad
Wayne Conrad

Reputation: 108019

The conditions in the case statement all follow the same form, which suggest that there is an opportunity to eliminate some repetition, and to separate policy from implementation. The policy is the set of conditions under which the link should be hidden:

WHEN_TO_HIDE_LINK = [
  ['edit', %w(sent sending archived)],
  ['send_schedule', %w(sent sending archived)],
  ['archive', %w(archived)],
  ['dashboard', %w(sending draft)],
]

The implementation is the code that applies the policy:

def hide_link?(link, mailing)
  WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
    link_value == link && mailing_statuses.include?(mailing.status)
  end
end

Explanations below the fold.


%w

%w is a way to specify a list of strings without typing all those quotes and commas. This:

%w(sent sending archived)

is equivalent to this:

['sent', 'sending', 'archived']

any?

Enumerable#any? passes each element of the array to the block (the bit between the do and the end). If the block ever returns truthy, then the result of any? is true; otherwise, the value of any? is false.

array decomposition

Did you notice that although each element of WHEN_TO_HIDE_LINK is an array, the block passed to any? does not take an array? You might expect that you'd have to do this:

WHEN_TO_HIDE_LINK.any? do |when_to_hide|
  link_value = when_to_hide[0]
  mailing_statuses = when_to_hide[1]
  ...

but Ruby will decompose array into parts for you. Here's one way to do it:

WHEN_TO_HIDE_LINK.any? do |when_to_hide|
  link_value, mailing_statuses = when_to_hide
  ...

When there is an array on the right side of the = and comma-separated variables on the left, Ruby decomposes the array into its elements and assigns them to the variables separately.

But Ruby can make things even easier:

WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
  ...

This is equivalent to either of the preceding two fragments.

Upvotes: 1

Rob
Rob

Reputation: 415

Remove return.

link = "fred"
case link
  when "fred"
    true
  else
    false
end

case will return the value itself which will then be passed to the method.

Refactor of megas's version:

def hide_link?(link, mailing)
  statuses_to_hide = case link
    when 'edit', 'send_schedule'
      %w{sent sending archived}
    when 'archive'
      %w{archived}
    when 'dashboard'
      %w{sending draft}
    else
      []
    end
  statuses_to_hide.include?(mailing.status)
end

Upvotes: 1

megas
megas

Reputation: 21791

I believe that if link doesn't match these criterias the method should return false. Thus:

def hide_link?(link, mailing)
  case link
  when 'edit'
    ['sent', 'sending', 'archived'].include?(mailing.status)
  when 'send_schedule'
    ['sent', 'sending', 'archived'].include?(mailing.status)
  when 'archive'
    puts "I should be in here"
    ['archived'].include?(mailing.status)
  when 'dashboard'
    ['sending', 'draft'].include?(mailing.status)
  else
    false
  end
end

The construction [...].include?(mailing.status) has result true or false which will be returned as a result of hide_link? method.

Upvotes: 3

Related Questions