⬆️ ⬇️

10 bugs in Ruby / Ruby on Rails that are easy to fix

Programmers make mistakes in the code. Some are just annoying (errors, not programmers - approx. Translator), others can be really dangerous for the application.

I present to you my selection of 10 common Ruby / RoR developer errors and tips on how to avoid them. I hope they will help you reduce the time to debug the code.



1. Double negation and difficult conditions.



if !user.nil? # ... end unless user.blank? # ... end unless user.active? || address.confirmed? # ... end 


Double negatives are hard to read. Every time I spend a couple of seconds to parse the condition. Use the Rails API and write user.present? instead of! user.blank ?.



I also rarely find use unless justified, especially with multiple conditions. How quickly will you understand in which case this condition will be met?

')

 unless user.active? || address.confirmed? ... end 




2. Use save instead of save! without checking the return value.



 user = User.new user.name = "John" user.save 


What is the problem with this code? You will not learn about the save error if it occurs. Not a single trace will remain in your logs, and you will spend a lot of time trying to figure out: “Why are there no users in the database?”.

If you are sure that the data is always correct and the record should be saved - use bang methods (save! Create! Etc.). Use save only if you are checking the return value:



 if user.save ... else ... end 




3. Use self unnecessarily.



 class User attr_accessor :first_name attr_accessor :last_name def display_name "#{self.first_name} #{self.last_name}" end end 


In the case of self.first_name, it is enough to write first_name, and the result will not change. But it is rather a matter of style, and does not bear any negative consequences. However, remember that self must be used when assigning values.



 self.first_name = "John" 




4. Problem N + 1 request.



 posts = Post.limit(10) posts.each do |post| do_smth post.user end 


If you look at the logs when executing this code, you will see that 11 queries are being executed to the database: one for a selection of posts and one for each user. The rails are not able to predict your desires and make a request for unloading 10 users at once (perhaps even with posts). In order to direct Rails in the right direction, you can use the includes method (more info here ).



5. A boolean field with three values.



It is assumed that a boolean field can have only two values ​​- true or false, is it not? What about nil ? If you forgot to specify a default value and add the null: false option in your migration, you will have to deal with three Boolean values ​​— true, false and nil. As a result, we have a similar code:



 #  ,      if post.published.nil? # ... end #   if post.published # ... end #   unless post.published # ... end 


If you need to handle three different values, use a text field.



6. Lost entries after deletion.



Consider a simple example:



 class User < ActiveRecord::Base has_many :posts end class Post < ActiveRecord::Base belongs_to :user validates_presence_of :user end 


If we delete a user, the posts associated with it will cease to be correct (which, most likely, will lead to errors in the application). In this case, it is necessary to handle the deletion of related objects:



 class User < ActiveRecord::Base has_many :posts, dependent: :delete end 




7. Using application code in migrations.



Suppose you have the following model:



 class User < ActiveRecord::Base ACTIVE = "after_registration" end 


and you want to add a points field to it. To do this, you create a migration. You also want to set the points field value for existing users to 10. To do this, you write in the migration:



 User.where(status: User::ACTIVE).update_all(points: 10) 


You start migration, and - Hurray! - everything worked: existing users have 10 points each. As time goes by, the code changes, and now you decide to remove the User :: ACTIVE constant. From now on, your migrations do not work, you cannot start migrations from scratch (you will get an uninitialized constant User :: ACTIVE error).



Tip: do not use application code inside migrations. Use, for example, temporary rake tasks to modify existing data.



Translator's Note:

In my opinion, it is much more convenient to use temporary migrations to change values. Especially when rolling out changes to production: the probability of forgetting to run rake is not zero, and migration will be launched for you by Capistrano.

A temporary migration can be cleaned after the release.



8. Ubiquitous puts .



Remaining after debugging puts clogs the logs and output during the test run. Use Rails.logger.debug, which allows you to filter logs depending on the selected level.



Translator's Note:



And better learn how to use debugger. Do not forget to remove binding.pry before commit.



9. Forget about the map .



I often see similar code:



 users = [] posts.each do |post| users << post.user end 


This is the case when you need to use map : concisely and idiomatically.



 users = posts.map do |post| post.user end #      (. ) users = posts.map(&:user) 




10. Do not use Hash # fetch .



 name = params[:user][:name] 


What is wrong here? This code will raise an exception ( NoMethodError: undefined method `[] 'for nil: NilClass ) if the user key is not in the hash. If you expect the key to be a hash, use Hash # fetch:



 name = params.fetch(:user)[:name] 


Then you will get a more meaningful exception:



 KeyError: key not found: :user 




Instead of a conclusion.



What are your favorite mistakes?

Source: https://habr.com/ru/post/250007/



All Articles