📜 ⬆️ ⬇️

Tips & Tricks

In almost all programming languages, the same problem can be solved in several ways. However, some of them are better, some worse. For some, you need to write 10 lines of code, for others you can do without one.

Improving the code and optimizing it sometimes takes more time than it took to write the first version. You often met a new code for you or an interesting implementation, and said to yourself: “It turns out that this can be done by standard means, and I invented the bicycle”? Personally, I - yes. Therefore, in this article I collected my collection of “bikes” and told how to get rid of them.

Array Methods


A simple example - let's say you have an array of objects of the class User. They have an activated property, which is set to 1 if the user has activated his account. You need to check whether all users from the array are activated. I do not take into account ActiveRecord (there you can do it differently), my goal is to show the work with arrays.
')
The first method is the most primitive:
  1. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  2. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  3. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  4. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  5. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  6. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size
  7. @users = User. find ( :all ) activated_users = 0 foreach user in @users activated_users + = 1 if user. activated == 1 end # , activated_users == @users . size

The first thing I did here was to use the each method of the array instead of a loop:
  1. @users = User. find ( : all )
  2. activated_users = 0
  3. I personally like this way of recording more
  4. # than standard for many languages ​​foreach cycle
  5. @users . each do | user |
  6. activated_users + = 1 if user. activated == 1
  7. end
  8. # or in one line:
  9. @users . each { | user | activated_users + = 1 if user. activated == 1 }
But even this record did not suit me afterwards. I found a more rational option:
  1. # The select method allows you to pull out the elements of the array for a given condition
  2. # And then we compare the sizes of both arrays
  3. @users = User. find ( : all )
  4. @users . select { | user | user. activated == 1 } . size == @users . size
However, all good things have already been invented for us, you just need to read the documentation. Arrays have a wonderful all method ?, which checks whether all elements satisfy the condition. By the way, there is still the any? Method that returns true if at least one element meets the condition.

All our code can be written in just one line:
  1. @users . all ? { | user | user. activated == 1 }
  2. # or even like this:
  3. @users . all ? ( & activated ) # provided that activated takes the value true / false, because 0 in ruby ​​is considered true

In addition, there are several little-known, but useful methods:
  1. # Take the first 5 items
  2. @array . take ( 5 )
  3. # Select random item
  4. @array . choice
  5. # Scatter items in random order
  6. @array . shuffle !
  7. # Get array element by its number
  8. # And this method is faster than @array [1]
  9. @array . at ( 1 )

What is the best way to count the number of elements in an array?


The count is the most functional. You can set parameters in brackets:
  1. array = [ 1 , 1 , 2 , 2 , 3 , 4 , 5 ]
  2. array. count # => 7
  3. array. count ( 1 ) # counts the number of elements "1" => 2
  4. array. count { | p | p > 2 } # count elements by condition => 3


Size or length (for arrays they are identical) is the fastest way, because its only purpose is to simply count the number of all elements.

Frequently Used ActiveRecord Requests


Another “bicycle” that I met with is its own implementation of the named scope . For example, you have a table Cars. You need to frequently refer to the base and pull out cars of a certain color. You can write your method in the model:
  1. class Car < ActiveRecord :: Base
  2. def self . only_red
  3. self . find ( : all ,: conditions => "color = 'red'" )
  4. end
  5. end
He just searches for all the cars in red. It is easy to contact him - Car.only_red. But the main drawback is that if you want to sort it all out (or add something like: limit => 5), you will have to either modify the method in the model, or use the standard find.

Very convenient and easy to use named_scope comes to the rescue.
  1. class Car < ActiveRecord :: Base
  2. named_scope : red ,: conditions => 'color = "red"'
  3. end

Use it almost as well - Car.red. But besides this, you can use it with the find method, for example:
  1. Car. red . find ( : all ,: limit => 10 ,: order => "id DESC" )
And they can be combined. First, add a new scope that will allow users who own the machines to be included in the request:
  1. named_scope : with_users , include => : users

Now Car.red.with_users will create you-know-what query. Conveniently? Sure.

Link_to and his friends


Some more interesting analogs are at the most usual link link_to.

The first is link_to_if, which will display the link only if it matches the condition. If it does not match, there will be no link; instead, only text will remain.
For example, we have the link “Raise Karma”. If the user has already voted, it should not be available. Some make a separate style for this purpose, and display the link as plain text. But you can write:
  1. link_to_if ( user. voted_for_karma ? == false ) , "Raise Karma ",: action => "add_karma"
What can I change? Of course, (user.voted_for_karma? == false) looks incomprehensible, because you can simply remove the comparison with false using link_to_unless:
  1. link_to_unless user. voted_for_karma ?, "Raise Karma ",: action => "add_karma"
You can make it even cooler - let us inform you that the user has already voted.
  1. # link_name is here (not) used as the name of the original link
  2. link_to_unless ( user. voted_for_karma ?, "Raise Karma ",: action => "add_karma" ) { | link_name | "You have already voted" }
And you can also give another link if the user is not logged in, for example, redirect to registration:
  1. link_to_if ( user. logged_in ?, "Create topic ",: action => "add_post" ) do | link_name |
  2. # if a logged-in user will click on the link, he will go to the login page
  3. link_to ( link_name,: controller => "users" ,: action => "login" )
  4. end

Quite often you can find the replacement of the link text, if the browser is on the current page. This is done using link_to_unless_current:
  1. <! - index.html.erb ->
  2. < ul id = "navbar" >
  3. < li > <% = link_to_unless_current ( "Home" , { : action = > "index"})%> < / li >
  4. < li > <% = link_to_unless_current ( "About Us" , { : action = > "about"})%> < / li >
  5. < / ul >
  6. <! - if we go to the About Us page, then in the html code we will see the following: ->
  7. < ul id = "navbar" >
  8. < li > <a href = "/ controller / index"> Home < / a > < / li >
  9. < li > About Us < / li >
  10. < / ul >


Russification Validation


Another problem that I once encountered was validation and errors. My application was in Russian (of course), and by default, errors were issued in this format: “Title cannot be empty.”

First of all, you need to install the Russian gem , I really hope that you already know about it. This will allow you to localize most standard messages.
Then in the config / locales folder, open (or create, if not) the file ru.yml. Here is an example from my file:
  1. ru:
  2. activerecord:
  3. errors :
  4. full_messages:
  5. rate:
  6. exists: "It is impossible to evaluate the same user twice"
  7. number_limit: "The score may take values ​​from -2 to 2"
  8. attributes:
  9. news:
  10. title: "Title"
  11. body: "text"
  12. article: "Article"
As you can see, to localize the title field in the news table, it’s enough to add the above entry to the localization file.

The full_messages field is used for full error messages. Here is how they are written in the model:
  1. class Rate < ActiveRecord :: Base
  2. validates_uniqueness_of : rate_owner_id,: message => "rate.exists"
  3. validates_inclusion_of : value ,: in => [ - 2 , - 1 , 1 , 2 ] ,: message => "rate.number_limit"
  4. end
The message parameter takes the localization file, goes to the address activerecord.errors.full_messages, then at the specified in the model, and returns the localized string. Everything is quite simple. If you do not specify message, then the output will be a standard error phrase, but in my case, I replaced it with mine.

Methods for the controller


If you need to give data in an action and as an Ajax, and “in the old-fashioned way”, then a check for it can be done using request.xhr?, For example:
  1. def search
  2. @users = User. find ( : all ,: conditions => "login LIKE '# {params [: q]}'" ,: limit => 30 )
  3. render : json => @users if request. xhr ?
  4. end
Another useful method is verify. Judging by the name, it serves to ensure that the correct request has arrived at the controller. This is mainly for protection, and I strongly recommend using it for some methods.
  1. class UsersController < ApplicationController
  2. # We check the create method - it should only accept post requests and the specified parameters.
  3. # If something is wrong, then we redirect the user to the beginning, and add a flash error message.
  4. verify : only => : create ,: method => : post ,: params => [ : login ,: email,: name ] ,: redirect_to => : index ,: add_flash => { : error => 'Invalid request. Are you all right? " }
  5. end
By the way, you should not paint the error in as much detail as possible - the main validation should take place in the model. Immediately we check the parameters, and swear if something is wrong.

Debugging


I use logger and ruby-debug gems for debugging applications. Logger is a simple and fast way to find out “why the hell is it nil instead of users returns”. It is done simply:
  1. # inspect I call to conveniently display objects in the console
  2. logger. debug "users = # {users.inspect}"
By the way, you can use not only logger.debug, but also logger.info, logger.warn or even logger.fatal. In your own classes, you can write RAILS_DEFAULT_LOGGER.debug, because it will not respond to the phrase logger.debug.

If you need to see or change the state of variables, you can use the debugger. To do this, put a debugger entry, for example:
  1. class UsersController < ApplicationController
  2. def index
  3. @user = User. find_by_id ( params [ : id ] )
  4. role = @user . role
  5. debugger
  6. end
  7. end
After that, we need to either start the server with the - debugger parameter, or add
  1. require 'ruby-debug'
in environment.rb. After that we update the index page in the browser - but it will stop and will not be loaded. It's time to look into the console, there is a debugger waiting for us.
  1. (rdb: 1) irb
  2. irb (# <UsersController: 0x1039edd20>): 001: 0> @user
  3. => # <User id: 1, login: "Vizakenjack">
  4. irb (# <UsersController: 0x1039edd20>): 001: 0> @ user.login = 'Admin'
  5. => "Admin">

First go to irb, and then we look at our variables. In addition to viewing, they can be changed and then later let them into the application.

According to the debugger, you can write a whole book, here I gave examples of basic use only. For those who want "everything at once" - read the links in the paragraph below.

Conclusion



Here I will provide some useful links that have helped me, and, I hope, will be useful to you too.

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


All Articles