As a developer working with Ruby on Rails and the Phlex framework, I encountered a common pain point: writing Phlex views that are clean, readable, and efficient, while also adhering to RuboCop’s default style guidelines. While RuboCop is a fantastic tool for enforcing coding standards and preventing messy codebases, it can feel a bit restrictive when applied to view code, especially when using frameworks like Phlex along with something like TailwindCSS.
The Problem: Lengthy and Complex Phlex Views and Components
Phlex takes a more Ruby-centric approach to generating HTML by using Ruby classes and methods instead of traditional template files like ERB or HAML. While this is great for reusability and code organization (And writing more Ruby 🤩), it can lead to long methods and classes, especially when building complex components or pages. Adding TailwindCSS into the mix further increases line lengths because of its utility-first CSS approach, where multiple classes are often stacked together.
The result? RuboCop flags violations left and right for:
- Line length: Phlex views with TailwindCSS often lead to long lines of code due to the many utility classes.
- Method length: Complex view logic can lead to long methods, especially if you’re rendering deeply nested HTML elements.
- Class length: Phlex encourages structuring views and components as classes, which may grow large if they represent complex components or full pages.
- Cyclomatic complexity: The complexity of HTML structures, conditionals, and loops can trigger RuboCop’s complexity metrics.
While these checks make sense for general Ruby code, enforcing them strictly on views and components doesn’t always add value. Instead, they add friction to writing clear, concise views, especially in the context of Phlex.
The Solution: Tailoring RuboCop for Phlex Views
To reduce this friction, I decided to customize my RuboCop configuration specifically for Phlex views. My goal was to exclude certain style checks from my app/views/**/*
directory (I’m still using Phlex v1.x, if you already upgraded to v2.x, you will need to exclude app/components/**/*
in addition to app/views/**/*
), allowing me to write Phlex views without unnecessary interruptions. Below is the configuration I settled on:
Layout/LineLength:
Exclude:
- 'app/views/**/*'
Metrics/AbcSize:
Exclude:
- 'app/views/**/*'
Metrics/BlockLength:
Exclude:
- 'app/views/**/*'
Metrics/ClassLength:
Exclude:
- 'app/views/**/*'
Metrics/CyclomaticComplexity:
Exclude:
- 'app/views/**/*'
Metrics/MethodLength:
Exclude:
- 'app/views/**/*'
Metrics/ParameterLists:
Exclude:
- 'app/views/**/*'
Metrics/PerceivedComplexity:
Exclude:
- 'app/views/**/*'
Style/IfUnlessModifier:
Exclude:
- 'app/views/**/*'
Why Disable These Specific RuboCop Cops?
Let’s break down why each of these exclusions makes sense for Phlex views and components:
-
Layout/LineLength
: When using TailwindCSS, it’s common to end up with long lines due to multiple class names being stacked together. Breaking these up into smaller lines often makes the code harder to follow and ugly. By excluding this check, I avoid cluttering the view with unnecessary line breaks. -
Metrics/AbcSize
: Phlex views often have higher abstraction levels, meaning they will naturally include a fair amount of assignments, branches, and conditionals. Disabling this check avoids constant warnings about the complexity of view logic. -
Metrics/BlockLength
: HTML structures can get quite large, and Phlex components represent these as Ruby blocks. Disabling this check prevents RuboCop from flagging long blocks in view components. -
Metrics/ClassLength
: Phlex encourages building views as Ruby classes, which can become lengthy when rendering full pages or complex components. Excluding this check allows me to write robust views without breaking them into unnecessarily small pieces. -
Metrics/CyclomaticComplexity
&Metrics/PerceivedComplexity
: HTML is inherently nested and conditional in nature. Phlex views often use loops and conditionals to build the right structure dynamically. Excluding these checks keeps RuboCop from flagging views for the normal complexity of HTML generation. -
Metrics/MethodLength
: Phlex components can sometimes require long methods to structure complex views. Breaking them down into smaller methods doesn’t always improve readability, so I excluded this check to avoid being overly granular. -
Metrics/ParameterLists
: When passing data into Phlex views, sometimes a larger list of parameters is necessary. Excluding this check avoids warnings when building view components that require more complex input.
Style/IfUnlessModifier (It needs a separate section 😁)
In Phlex views and components, I often need to conditionally apply classes based on state or variables. The traditional if/else
block syntax, while clear in Ruby logic, can become cumbersome when embedding conditional logic into class attributes for HTML elements. Disabling the Style/IfUnlessModifier
cop allows me to write more concise conditionals directly in-line, which enhances readability and keeps the code compact.
For example, with this cop disabled, I can write:
('bottom-0 top-auto translate-y-0 end-1/2 start-1/2 -translate-x-1/2 rtl:translate-x-1/2 gap-2 p-4 fixed' if @floating)
Instead of the more verbose:
(if @floating
'bottom-0 top-auto translate-y-0 end-1/2 start-1/2 -translate-x-1/2 rtl:translate-x-1/2 gap-2 p-4 fixed'
end)
This flexibility is especially important in Phlex views and components, where the recommended syntax for passing classes to HTML tags looks something like this:
a(
class: [
("button"),
("active" if is_active),
("disabled" if is_disabled)
]
) { "Click me" }
Here, the inline conditional syntax keeps the class attribute clean and readable, even if the line becomes long. This is particularly useful when working with TailwindCSS, where class names can quickly stack up. Disabling Style/IfUnlessModifier
gives me the flexibility to manage these complex view structures without sacrificing code clarity.
Custom RuboCop Settings Instead of Disabling Cops
While disabling specific RuboCop cops was a practical solution for easing development with Phlex views, another approach worth exploring is customizing the default values for these cops. Adjusting the configuration to align with the particular needs of Phlex views and components (Maybe different defaults!) could provide a balanced solution that maintains code quality while accommodating the unique aspects of Phlex.
For instance, instead of completely excluding checks like Layout/LineLength
, Metrics/ClassLength
, or Metrics/MethodLength
, you could modify their thresholds to better suit the complexity of view code. This way, you ensure that the RuboCop rules still offer guidance without being overly restrictive.
Unfortunately, I didn’t have the time to thoroughly investigate the appropriate values for each cop to strike this balance.
Conclusion
By tailoring RuboCop’s configuration to exclude certain checks in my app/views/
directory, I’ve created a smoother development experience that allows me to write Phlex views more naturally. This setup enables me to take full advantage of Phlex’s Ruby-based approach to view generation without getting bogged down by irrelevant code style warnings.
The key takeaway is that enforcing consistent code style across your entire application is important, but flexibility in your linting tools can also be valuable — especially when dealing with areas like view generation, where different rules may apply.
Top comments (0)