Search code examples
ruby-on-railsrubyruby-on-rails-3view-helperscontent-tag

How to make this code more Ruby-way?


I am new to Ruby and Rails (switched from Python and Python frameworks). I'm writing a simple dashboard website which displays information about the S.M.A.R.T. state of hard disks. Here I wrote a helper to display a badge in a table cell near the relevant S.M.A.R.T attribute if it's value meets a condition. At first, the helper code was as simple as in Listing 1, but then I decided to draw a summary of all badges for the specific drive, in addition to the badges near individual S.M.A.R.T. attributes in the table. So at first I added a simple method like:

def smart_chk_device(attrs)
    attrs.each { |item| smart_chk_attr(item) }
end

But this approach didn't work and it caused the entire array of attributes to be output to the resulting page. It only started to work when I made it as in Listing 2, but I believe there's something wrong there, and the same thing can be done in a more simple way. Please show me the right, Ruby-way of doing it.

Listing 1:

module HomeHelper
    def smart_chk_attr(attr)
        case attr[:id].to_i
        when 1,197
            content_tag(:span, "Warning", :class => "label label-warning") if attr[:raw].to_i > 0
        when 5,7,10,196,198
            content_tag(:span, "Critical", :class => "label label-important") if attr[:raw].to_i > 0
        end
    end
end

Listing 2 (works, but I don't like it):

module HomeHelper
    def smart_chk_attr(attr)
        case attr[:id].to_i
        when 1,197
            return content_tag(:span, "Warning", :class => "label label-warning") if attr[:raw].to_i > 0
        when 5,7,10,196,198
            return content_tag(:span, "Critical", :class => "label label-important") if attr[:raw].to_i > 0
        else
            return String.new
        end
        return String.new
    end

    def smart_chk_device(attrs)
        output = ""
        attrs.each { |item| output << smart_chk_attr(item) }
        return output.html_safe
    end
end

attrs is an Array of Hashes, where each Hash contains keys :id and :raw with the numeric code of a S.M.A.R.T attribute and its RAW value, both in Strings.

Also, RoR complaints if to remove the last "return String.new" in Listing 2. Why is it so? Doesn't the "case" block all the possible cases, so that control should never reach the end of the function?


Solution

  • I believe this would behave in the same way, and is much shorter:

    module HomeHelper
    
      def smart_chk_attr(attr)
        return '' unless attr[:raw].to_i > 0
        case attr[:id].to_i
          when 1,197
            content_tag(:span, "Warning", :class => "label label-warning")
          when 5,7,10,196,198
            content_tag(:span, "Critical", :class => "label label-important")
          else ''
        end
      end
    
      def smart_chk_device(attrs)
        attrs.map { |item| smart_chk_attr(item) }.join.html_safe
      end
    
    end
    

    Ruby methods return the value of the last expression, so get rid of the explicit returns all over that method. Also, DRY: Don't Repeat Yourself (the attr[:raw] check). In this case, I replaced those with a guard clause at the start of the method. Short-circuiting guard clauses are a matter of taste, but I like them and you'll see them in a lot of Ruby code.