Improve Code in Your Ruby Application with RubyCritic
Brena Monteiro
Posted on October 26, 2022
RubyCritic provides visual reports highlighting code smells, code structure, ease of testing, and test coverage in your Ruby application.
It's in active development, with new code analysis tools often being introduced as new features. It's well worth keeping track of RubyCritic's releases.
This article will touch on some of RubyCritic's benefits, its dependencies, and how to read its code reports.
Let's get going!
Why Choose RubyCritic for Your Ruby on Rails Application?
You should consider using RubyCritic if you want a single place to review code improvements for your project. Including RubyCritic in your development process will certainly reduce the time a development team spends working on technical debts. Most technical debts will be mapped out at development time.
Some benefits that RubyCritic can provide to your project and development process include:
- Unified information in one place
- Visual reports
- Easy installation
- Zero configuration
- Customization allowed
- Being extensible — you can make your own open-source integration
- A badge generator 🎉
To understand how RubyCritic works, let's look at the internal dependencies it uses to make reports.
Internal Dependencies in RubyCritic
When you add RubyCritic to your project, some dependencies will also be included.
Let's highlight the dependencies that make magic happen: the Reek, Flay, and Flog gems. These dependencies allow RubyCritic to show you valuable information about your code. Understanding how they work also makes RubyCritic easier to use.
Reek: Detect Code Smells in Ruby
Reek is a gem for detecting code smells in Ruby. A bad smell in code is not about identifying wrong code, it is more about analyzing if the code could be written better.
Reek's analysis identifies if something could be implemented in another way. It does not suggest how, as most code smells are associated with business logic and a developer's experience with a language.
For example, you could easily rewrite an if
statement using metaprogramming techniques. The way to correct it, though, is up to a developer according to a project's context. In this case, no library will be able to indicate the best solution.
Reek detects an extensive list of smells. It examines and identifies possible smells in:
- Classes
- Attributes
- Methods
- Parameters
- Modules
- Iterators
- The implementation of polymorphism
By finding smells, you can take steps to make your code more readable and maintainable.
Reek allows for custom configuration to:
- Disable a detector by its type
- Exclude directories from being scanned
- Use filters to silence warnings
You can even define specific code to suppress in a scan, a very useful feature when code is not yet finalized or refactored, or even if it is legacy code.
Let's see a sample of how Reek works. In this code, the exception is just defined as e
.
# app/controllers/erp/orders_controller.rb
def create
...
rescue JSON::Schema::ValidationError => e
render status: :unprocessable_entity, json: {
type: "invalid-schema",
title: "Your request does not match the expected schema.",
detail: e.message
}
end
end
It is easy to imagine that e
means an exception, but what if we have other exceptions? Identifying them correctly is the best way to maintain good code.
Reek will identify e
as UncommunicativeVariableName and show a warning.
$ reek app/controllers/erp/orders_controller.rb
Inspecting 1 file(s):
S
app/controllers/erp/orders_controller.rb -- 1 warning:
[91]:UncommunicativeVariableName: Erp::OrdersController#create has the variable name 'e' [https://github.com/troessner/reek/blob/v6.1.1/docs/Uncommunicative-Variable-Name.md]
Flay: Check for Ruby Code Duplication
Flay identifies structural Ruby code similarities, including:
- Detecting code duplication within a project
- Checking the difference at any code level
- Generating a score to measure how good your code is (the lower your score, the better the code)
If Flay reports a similarity in your code, it's a high indicator that refactoring is needed. Don't ignore this! Duplicate code is a gateway to bugs. If you fix something in one place but forget about another, more bugs appear.
We can check how Flay works by running it in its own source code:
$ flay lib/flay.rb
Total score (lower is better) = 36
1) Similar code found in :iter (mass = 36)
lib/flay.rb:80
lib/flay.rb:105
Flay identifies similarities between these two:
# lib/flay.rb:80
opts.on("-m", "--mass MASS", Integer, "Sets mass threshold (default = #{options[:mass]})") do |m|
options[:mass] = m.to_i
end
# lib/flay.rb:105
opts.on("-t", "--timeout TIME", Integer, "Set the timeout. (default = #{options[:timeout]})") do |t|
options[:timeout] = t.to_i
end
Note that the code's spelling is not exactly the same, but its functionality is and can be refactored to avoid duplication. That's the magic of Flay!
Flog: Examine Your Code Complexity in Ruby
Flog checks how difficult your code is to test. It sets a complexity score for each line of code and sums up the score for each method and class.
The higher the score, the more your code needs to be refactored because it signifies that you have a highly complex implementation.
Let's see Flog in action! A small change can cause your score to variate.
def validate_expiration
return if exp_month.blank? || exp_year.blank?
...
end
$ flog app/models/credit_card.rb
5.2: CreditCard#validate_expiration app/models/credit_card.rb:12-15
Note that in the first part of the code, we have an or
check that increases the score by 0.4 points.
def validate_expiration
return if exp_month.blank?
return if exp_year.blank?
...
end
$ flog app/models/credit_card.rb
4.8: CreditCard#validate_expiration app/models/credit_card.rb:12-15
Other RubyCritic Dependencies
RubyCritic also uses other runtime dependencies, such as:
-
byebug
- this elevates debugging Ruby applications. It allows you to run a program line by line, add breakpoints, and evaluate and track values at runtime. If you still useputs
for debugging, it's time you get to know Byebug's features and commands. -
rubocop
- a linter for Ruby code that helps you follow a style guide used by the Ruby community, or even apply your own code style. It's very useful to set standards in your team and avoid silly conflicts about spaces and tabs. -
SimpleCov
- a tool to check Ruby application code coverage. You can configure it to run alongside your tests. It provides metrics on code coverage so that you can identify what you need to pay attention to and where to invest your time to create better test cases.
Dive into RubyCritic's list of dependencies.
Using RubyCritic for Your Ruby on Rails App
RubyCritic has good documentation to help you get started without much configuration. Therefore, we will focus on utilizing its resources to help us analyze its reports.
RubyCritic provides 'Code', 'Smells', and 'Coverage' reports. We'll look at each of these features in turn.
The Overview in RubyCritic
The 'Overview' page shows a total score for your project on a donut chart, along with ratings (A being the best rating, F the worst). The 'Summary' section shows the details of each rating, including the number of files, churns (commit changes), and smells found.
In the 'Churn vs Complexity' section here, it is already possible to identify the class with the greatest complexity, which should probably be the first point of attention.
To better understand this graph, it is worth recapping code churn. Code that changes frequently can raise an alert that something is wrong — maybe in the logic or the business domain, for example. Either way, looking at 'Churn vs Complexity' can help you see where the pain points are across your project.
Code Report
The 'Code' report shows a score for each class, including indicators for churn, complexity, duplication, and smells.
You can sort this list by any column to view the highest ranking factors and address the most critical issues first.
In addition, this list has a filter that allows you to search by class name quickly.
Clicking on the class name will open a detailed page with the class code and metrics such as:
- Code line
- Quantity methods
- Calculated churn
- Complexity by method
- Complexity score (total per class)
- Amount of duplicates found
- Number of smells
The line of code where an issue is found will be highlighted (based on information provided by the Reek gem).
If Flog identifies any issues, you'll see a score. You'll also see if a Flay report has detected any duplicate code.
Smells Report
The 'Smells' page displays the smell type, the exact location where a smell appears, and the fix status.
As mentioned earlier, the smells are detected by Reek, and sorting and filtering are also available on this page.
Clicking on a class name will open a page with your code details. You can also see the classes grouped by smell type (this is missing from the 'Code' page, which only displays the number of smells).
Coverage Report
Finally, you can see class classifications and the percentage of coverage for each class in the 'Coverage' report. In contrast to the lists on the 'Code' and 'Smells' reports, the list in 'Coverage' does not allow information to be sorted and filtered.
You can only see the percentage of code coverage — no additional information is available.
Integrating the SimpleCov report could add more value and usefulness to this page. But in any case, the 'Coverage' report can help if you need a simple report to examine your project's test coverage.
Wrap Up
In this post, we briefly looked at the benefits of RubyCritic for your Ruby application before diving into its internal dependencies: Reek, Flay, and Flog. We then ran through how to read and analyze RubyCritic's reports.
As a next step, figure out how to use RubyCritic in your pipeline.
Happy code refactoring!
P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!
Posted on October 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.