DEV Community

Cover image for Some helpful syntax/conventions in Rails to you šŸµšŸµ
minhtienvu
minhtienvu

Posted on

Some helpful syntax/conventions in Rails to you šŸµšŸµ

Some helpful syntax/conventions in Rails to you

Hi guys, Tien again, after I read some articles and my hands-on experience in some Rails projects. I write this post to summarize some helpful conventions that can improve your Rails knowledge.

Sip a cup of tea and enjoy it ā¤ļø Have fun guys!!

1. Use _ in the console to get the value of the previous command in your console

YOUR CONSOLE

a = [1,2,3,4,5]

User.where(id: _)
=> SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2, 3, 4, 5)
Enter fullscreen mode Exit fullscreen mode

2. Existence Check Shorthand

UseĀ &&=Ā to set variables that may or may not exist. UsingĀ &&=Ā will change the value only if it exists, removing the need to check its existence withĀ if. Use &&= to set variables that may or may not exist. Using &&= will change the value only if it exists, removing the need to check its existence with if.

# bad
if something
  something = something.downcase
end

# bad
something = something ? something.downcase : nil

# ok
something = something.downcase if something

# good
something = something && something.downcase

# better
something &&= something.downcase
Enter fullscreen mode Exit fullscreen mode

3. map/find/select/reduce/include?/size

PreferĀ mapĀ overĀ collect,Ā findĀ overĀ detect,Ā selectĀ overĀ find_all,Ā reduceĀ overĀ inject,Ā include?Ā overĀ member?Ā andĀ sizeĀ overĀ length. This is not a hard requirement; if the use of the alias enhances readability, itā€™s ok to use it. The rhyming methods are inherited from Smalltalk and are not common in other programming languages. The reason the use ofĀ selectĀ is encouraged overĀ find_allĀ is that it goes together nicely withĀ rejectĀ and its name is pretty self-explanatory.

3.1. countĀ vsĀ size

Donā€™t useĀ countĀ as a substitute forĀ size. ForĀ EnumerableĀ objects other thanĀ ArrayĀ it will iterate the entire collection in order to determine its size.

# bad
some_hash.count

# good
some_hash.size
Enter fullscreen mode Exit fullscreen mode

3.2. flat_map

UseĀ flat_mapĀ instead ofĀ mapĀ +Ā flatten. This does not apply for arrays with a depth greater than 2, i.e. ifĀ users.first.songs == ['a', ['b','c']], then useĀ map + flattenĀ rather thanĀ flat_map.Ā flat_mapĀ flattens the array by 1, whereasĀ flattenĀ flattens it all the way.

# bad
all_songs = users.map(&:songs).flatten.uniq

# good
all_songs = users.flat_map(&:songs).uniq
Enter fullscreen mode Exit fullscreen mode

4. Conditional Variable Initialization Shorthand

UseĀ ||=Ā to initialize variables only if theyā€™re not already initialized.

# bad
name = name ? name : 'Bozhidar'

# bad
name = 'Bozhidar' unless name

# good - set name to 'Bozhidar', only if it's nil or false
name ||= 'Bozhidar'
Enter fullscreen mode Exit fullscreen mode
**NOTE: Donā€™t use ||= to initialize boolean variables. 
Consider what would happen if the current value happened to be false.**

# bad - would set enabled to true even if it was false
enabled ||= true

# good
enabled = true if enabled.nil?
Enter fullscreen mode Exit fullscreen mode

5. Underscores in Numerics

Add underscores to large numeric literals to improve their readability.

# bad - how many 0s are there?
num = 1000000

# good - much easier to parse for the human brain
num = 1_000_000
Enter fullscreen mode Exit fullscreen mode

6. Convert a range to an array

I use some below syntax to convert a range to an array. I hope that you can apply it

(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Enter fullscreen mode Exit fullscreen mode

7. Get array characters from a string

# bad
string.split(//)
string.split('')

# good
string.chars
Enter fullscreen mode Exit fullscreen mode

8. Prefer to use Time.current, Date.current or DateTime.current rather than Time.now, Date.now, DateTime.now

.current will use the Rails environment is set to, .now will use server's timezone (Rails.application.config.time_zone). If it's not set, thenĀ .current
Ā will be the same asĀ .now .Be careful when use them to avoid conflict and unmatched.

Time.current
=> Returns Time.zone.now when Time.zone or config.time_zone are set,
otherwise just returns Time.now.

DateTime.current
=> Returns Time.zone.now.to_datetime when Time.zone or config.time_zone are set,
otherwise returns Time.now.to_datetime.
Enter fullscreen mode Exit fullscreen mode

9. Predicate Methods

Prefer the use of predicate methods to explicit comparisons withĀ ==. Numeric comparisons are OK.

# bad
if x % 2 == 0
end

if x % 2 == 1
end

if x == nil
end

# good
if x.even?
end

if x.odd?
end

if x.nil?
end

if x.zero?
end

if x == 0
end
Enter fullscreen mode Exit fullscreen mode

10. Hash#values_at

UseĀ Hash#values_atĀ when you need to retrieve several values consecutively from a hash.

# bad
email = data['email']
username = data['nickname']

# good
email, username = data.values_at('email', 'nickname')
Enter fullscreen mode Exit fullscreen mode

11. Proc Application Shorthand

Use the Proc call shorthand when the called method is the only operation of a block.

# bad
names.map { |name| name.upcase }

# good
names.map(&:upcase)
Enter fullscreen mode Exit fullscreen mode

12. Single-line Blocks Delimiters

PreferĀ {ā€¦}Ā overĀ doā€¦endĀ for single-line blocks. Avoid usingĀ {ā€¦}Ā for multi-line blocks (multi-line chaining is always ugly). Always useĀ doā€¦endĀ for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). AvoidĀ doā€¦endĀ when chaining.

names = %w[Bozhidar Filipp Sarah]

# bad
names.each do |name|
  puts name
end

# good
names.each { |name| puts name }

# bad
names.select do |name|
  name.start_with?('S')
end.map { |name| name.upcase }

# good
names.select { |name| name.start_with?('S') }.map(&:upcase)
Enter fullscreen mode Exit fullscreen mode

Some will argue that multi-line chaining would look OK with the use of {ā€¦}, but they should ask themselves - is this code really readable and can the blocks' contents be extracted into nifty methods?

13. Defining Class Methods

UseĀ def self.methodĀ to define class methods. This makes the code easier to refactor since the class name is not repeated.

class TestClass
  # bad
  def TestClass.some_method
    # body omitted
  end

  # good
  def self.some_other_method
    # body omitted
  end

  # Also possible and convenient when you
  # have to define many class methods.
  class << self
    def first_method
      # body omitted
    end

    def second_method_etc
      # body omitted
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

14. Hash#each

UseĀ Hash#each_keyinstead ofĀ Hash#keys.eachĀ andĀ Hash#each_valueĀ instead ofĀ Hash#values.each

# bad
hash.keys.each { |k| p k }
hash.values.each { |v| p v }
hash.each { |k, _v| p k }
hash.each { |_k, v| p v }

# good
hash.each_key { |k| p k }
hash.each_value { |v| p v }
Enter fullscreen mode Exit fullscreen mode

15. Hash#fetch

UseĀ Hash#fetchĀ when dealing with hash keys that should be present.

heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' }
# bad - if we make a mistake we might not spot it right away
heroes[:batman] # => 'Bruce Wayne'
heroes[:supermann] # => nil

# good - fetch raises a KeyError making the problem obvious
heroes.fetch(:supermann)
Enter fullscreen mode Exit fullscreen mode

16. Hash#fetchĀ defaults

Introduce default values for hash keys viaĀ Hash#fetchĀ as opposed to using custom logic.

batman = { name: 'Bruce Wayne', is_evil: false }

# bad - if we just use || operator with falsy value we won't get the expected result
batman[:is_evil] || true # => true

# good - fetch works correctly with falsy values
batman.fetch(:is_evil, true) # => false
Enter fullscreen mode Exit fullscreen mode

17. Use Hash Blocks

Prefer the use of the block instead of the default value inĀ Hash#fetchĀ if the code that has to be evaluated may have side effects or be expensive.

batman = { name: 'Bruce Wayne' }

# bad - if we use the default value, we eager evaluate it
# so it can slow the program down if done multiple times
batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers is an expensive call

# good - blocks are lazy evaluated, so only triggered in case of KeyError exception
batman.fetch(:powers) { obtain_batman_powers }
Enter fullscreen mode Exit fullscreen mode

18. Hash#transform_keysĀ andĀ Hash#transform_values

PreferĀ transform_keysĀ orĀ transform_valuesĀ overĀ each_with_objectĀ orĀ mapĀ when transforming just the keys or just the values of a hash.

# bad
{a: 1, b: 2}.each_with_object({}) { |(k, v), h| h[k] = v * v }
{a: 1, b: 2}.map { |k, v| [k.to_s, v] }.to_h

# good
{a: 1, b: 2}.transform_values { |v| v * v }
{a: 1, b: 2}.transform_keys { |k| k.to_s }
Enter fullscreen mode Exit fullscreen mode

19. reverse_each

PreferĀ reverse_eachĀ toĀ reverse.eachĀ because some classes thatĀ include EnumerableĀ will provide an efficient implementation. Even in the worst case where a class does not provide a specialized implementation, the general implementation inherited fromĀ EnumerableĀ will be at least as efficient as usingĀ reverse.each.

# bad
array.reverse.each { ... }

# good
array.reverse_each { ... }
Enter fullscreen mode Exit fullscreen mode

20. Unused Variables Prefix

Prefix withĀ _Ā unused block parameters and local variables. Itā€™s also acceptable to use justĀ _
Ā (although itā€™s a bit less descriptive). This convention is recognized by the Ruby interpreter and tools like RuboCop will suppress their unused variable warnings.

# bad
result = hash.map { |k, v| v + 1 }

def something(x)
  unused_var, used_var = something_else(x)
  # some code
end

# good
result = hash.map { |_k, v| v + 1 }

def something(x)
  _unused_var, used_var = something_else(x)
  # some code
end

# good
result = hash.map { |_, v| v + 1 }

def something(x)
  _, used_var = something_else(x)
  # some code
end
Enter fullscreen mode Exit fullscreen mode

ā¤ļøĀ Thank you for reading and see you ā¤ļø
Follow me: https://tienvm.com/
Donate me:Ā tienvm.com/donate

Top comments (0)