Edited at

Ruby、Railsにおけるコーディング規約メモ


はじめに

こんにちは。

株式会社Nexceedにて、学生フルスタックエンジニアとして働いてる若造です。

Ruby、Railsのコーディング規約を参考サイトで気になったものメモしました。

完全にコピペなんで、詳細はリンクを追って確認してみてください。


参考URL


indent


  • インデントには スペース2つ


ruby文法

### Array

array = [
:foo,
:bar,
:baz,
]

### Hash
hash = {
first: 42,
second: 'foo',
}

### String Array
words = %w(foo bar baz) # -> ['foo', 'bar', 'baz']

### Create Array
[*1..10] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

### print Array
puts [1, 2, 3].map do |i|
i * i
end

### case
case
when song.name == 'Misty'
puts 'Not again!'
when song.duration > 120
puts 'Too long!'
when Time.now.hour > 21
puts "It's too late"
else
song.play
end

### insert case result
kind = case year
when 1850..1889 then 'Blues'
when 1890..1909 then 'Ragtime'
when 1910..1929 then 'New Orleans Jazz'
when 1930..1939 then 'Swing'
when 1940..1950 then 'Bebop'
else 'Jazz'
end

### insert if else result
result = if some_cond
calc_something
else
calc_something_else
end

### def method
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# do something...
end

### long string
long_string = 'First part of the long string' \
' and second part of the long string'

### print big value
num = 1_000_000 # -> 1000000

### メソッドにbooleanを渡す時にはキーワード引数
def some_method(bar: false)
puts bar
end

### class self method
class Foo
class << self
# self.foo
def foo
end
private :foo
end
end

### private method
class Foo
def foo
end

private

def bar
end
end


routing


route.rb


# /subscriptions/1/unsubscribe
resources :subscriptions do
member do
get 'unsubscribe'
end
end

# /photos/search
resources :photos do
collection do
get 'search'
end
end

# posts/1/comments
resources :posts do
resources :comments
end

# admin/products
namespace :admin do
# Directs /admin/products/* to Admin::ProductsController
# (app/controllers/admin/products_controller.rb)
resources :products
end



ActiveRecord


user.rb

class User < ActiveRecord::Base

# 一番上にデフォルト・スコープを記述
default_scope { where(active: true) }

# 定数を記述
COLORS = %w(red green blue)

# attr関連のマクロを記述
attr_accessor :formatted_date_of_birth

# アソシエーションに関するマクロを記述
belongs_to :country
has_many :authentications, dependent: :destroy

# validationに関するマクロを記述
validates :email, presence: true
validates :username, presence: true
validates :username, uniqueness: { case_sensitive: false }
validates :username, format: { with: /\A[A-Za-z][A-Za-z0-9._-]{2,19}\z/ }
validates :password, format: { with: /\A\S{8,128}\z/, allow_nil: true}

# コールバックを記述
before_save :cook
before_save :update_username_lower

end



original validation

app/validators を作成し、その配下に配置

class EmailValidator < ActiveModel::EachValidator

def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || 'is not a valid email') unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
end

class Person
validates :email, email: true
end


all -> find_each


  • バッチ処理などにおいてActiveRecordで効率的に大量データを処理したいときに使うメソッド

  • 大量データを全部取ってメモリ展開して処理すると重くなる

  • データを徐々に展開して処理したいときに使う

# all -> find_each

Person.find_each do |person|
person.do_awesome_stuff
end

Person.where('age > 21').find_each do |person|
person.party_all_night!
end


ActiveRecord Queries


  • SQLインジェクションを防御

Client.where('orders_count = ?', params[:orders])

Client.where(
'created_at >= :start_date AND created_at <= :end_date',
start_date: params[:start_date], end_date: params[:end_date]
)


Controller


  • とにかく薄いコントローラーを書く

def create

@user = ...
@items = ...

# 任意の処理を実行する.
if HandleHardThingWorkflow.new(@user, @items).run
# 正常系の処理(render / redirect)
else
# 異常系(render / redirect)
end
end


  • コントローラーにincludeするのはビューヘルパーではなくConcernにする


app/controllers/concerns/user_session_module.rb

module UserSessionModule

extend ActiveSupport::Concern

included do
helper_method :signed_in?, :current_user

def signed_in?
...
end

def current_user
...
end

...
end
end



Model


  • ビューに関わるものを抽出する

  • 第一の手段としてはデコレーター層を導入


app/decorators/article_decorator.rb

class ArticleDecorator < Draper::Decorator

delegate_all

def publication_status
if published?
"Published at #{published_at}"
else
"Unpublished"
end
end

def published_at
object.published_at.strftime("%A, %B %e")
end
end



  • 第二の手段としてはViewModel層を作成する

# app/view_models/user_collection.rb

class UserCollection
attr_reader :users

def initialize(users)
@users = users.to_a
end

def names
@users.map(&:name).join(', ')
end
end


  • トランザクションスクリプトの抽出


app/workflows/create_article_workflow.rb

class CreateArticleWorkflow

attr_reader :article

def initialize(title, content, author) # 引数はキーワード引数の場合や、options = {}でHashとして受け取る場合もあります。
@title = title
@content = content
@author = author
end

def run
@article = Article.new(title: title, content: content, author: author)
if @article.save
@article.notify(author.followers)
true
else
false
end
end
end



最後に

まだまだ、Ruby、Railsのコーディング経験浅いので、

コーディング規約などまとめてあるサイトであったり、参考書があったら教えていただきたいです:bow_tone1::bow_tone1::bow_tone1: