Skip to content

Instantly share code, notes, and snippets.

@soruma
Last active June 22, 2018 06:45
Show Gist options
  • Save soruma/028334c733d2e457aaab5b3bcefcc540 to your computer and use it in GitHub Desktop.
Save soruma/028334c733d2e457aaab5b3bcefcc540 to your computer and use it in GitHub Desktop.

表示

IO.write "foo" # => 改行なし
IO.puts "foo"  # => 改行あり
IO.inspect 123 # => デバッグ用

特徴

型がない

配列内に異なる型の値を入れることが出来る

関数の中で変更は行わない

構文

変数宣言

x = 1

コメント

# コメント

タイプ

iex> 1          # integer
iex> 0x1F       # integer
iex> 1.0        # float
iex> true       # boolean
iex> :atom      # atom / symbol
iex> "elixir"   # string
iex> [1, 2, 3]  # list
iex> {1, 2, 3}  # tuple

型チェック

iex> is_integer 1
iex> is_boolean true
iex> is_list [1, 2, 3]

文字列

シングルクォーテーションやダブルクオーテーションで囲む、#{x}で変数の中身を出力することもできる。

str1 = 'abc'
str2 = "def"
str3 = "a\tbc\n"
str4 = "#{str1} def" # => "abc def"

文字列連結

<>

掛け算/割り算

divrem

切り上げ/切り捨て

roundtranc

メソッド

演算子

+もメソッドであり、Elixer上では+/2と定義されている

メソッドの再定義

elixir のエラーでround/1round/2というのがよく出る。 これは、引数の数を表し、roundの場合は同じ名前で引数の個数が異なるメソッドが存在することを表す

Atom

Rubyのシンボルのようなもの :testで宣言出来て、ただ一つの物体になる。 truefalseはAtomで定義されている。

is_atom(true)    # => true
is_boolean(true) # => true

無名関数

add = fn a, b -> a + b end
add.(1, 2) # => 3
is_function(add) # => true
is_function(add, 2) # => true 引数の個数もチェック
is_function(add, 1) # => false 引数の個数もチェック

無名関数では、ドット(.)とかっこは必須、命名メソッドでは省略することが出来る

無名関数はクロージャ―なので、スコープ内の変数にアクセスすることが出来る アクセスすることはできるが、変更した結果は関数の外では無効になる

配列

  • ++: 和結合
  • --: 差結合(個数分しか削除されない)
[1, 2, 3, 4] ++ [4, 4, 5, 6] # => [1, 2, 3, 4, 4, 4, 5, 6]
[1, 2, true, 3, 4, false, true] -- [true, false] => [1, 2, 3, 4, true]
[1, 2, true, 3, 4, false, true] -- [true, true, false] => [1, 2, 3, 4]
  • hd: 先頭を返す
  • tl: 後部を返す
ary = [1, 2, 3, 4]
hd ary # => 1
tl ary # => [2, 3, 4]

もし、配列がからの場合はArgmentErrorを返す

hd []
#** (ArgumentError) argument error
#    :erlang.hd([])

配列の値が数値の場合、出力するとASCIIに置き換えられる

[65, 66, 67]
# ABC

i メソッドを用いることで、変数の型に関する情報を見ることが出来る

iex(4)> i 'hello'
Term
  'hello'
Data type
  List
Description
  This is a list of integers that is printed as a sequence of characters
  delimited by single quotes because all the integers in it represent valid
  ASCII characters. Conventionally, such lists of integers are referred to as
  "charlists" (more precisely, a charlist is a list of Unicode codepoints,
  and ASCII is a subset of Unicode).
Raw representation
  [104, 101, 108, 108, 111]
Reference modules
  List
Implemented protocols
  IEx.Info, Collectable, Enumerable, Inspect, List.Chars, String.Chars

'hello'と"hello"はそれぞれ別者扱いになる。

シングルクォーテーションはList型、ダブルクオーテーションはBigString型になる

Tuple

タプルのインデックスは0始まり

値の取得

tuple = [:ok, "hello"]
elem(tuple, 1) #=> "hello"
tuple_size(tuple) #=> 2

値の変更

タプルも配列同様不変で、put_elemをしても新しいタプルを返す

tuple = {:ok, "hello"}
put_elem(tuple, 1, "world") #=> {:ok, "world"}
tuple #=> {:ok, "hello"}

List と Tuple どちらを使うべきか

Listは前の値と後ろの値を保持した状態でメモリに格納されている。 一方、Tupleは連続した状態でメモリに格納される。

つまり、Listは値の追加はリスト構造を変更する添え字を変更するだけで良いので、コストは低い しかし、Tupleは値を追加した場合は、新たなTupleを返すのでコストが高くなる。 ただし、Tupleの中の情報自体は共通なので、新たに作成されたTupleの中身のデータを変更すると、前のTupleの中身のデータも変更される。

演算子

  • div 割り算
  • rem 余り
  • ++ listの和結合
  • <> 文字列結合
  • and, or boolean用
false and raise("This error will never be raised") #=> false
true or raise("This error will never be raised") #=> true
  • ll 左辺がnil, false の場合に右辺を返す。左辺がnil以外, trueの場合、左辺を返す

  • && 左辺がnil, false の場合に左辺を返す。左辺がnil以外, trueの場合、右辺を返す

  • ! boolenの場合、値を反転させる。nilの場合、true。nil以外の場合、falseを返す

  • == 値として等しいか 1 == 1.0 #=>true

  • === 型まで確認 1 === 1.0 #=>false

  • 異なる方でもソートをすることが出来る。 ソート順は以下の通り

number < atom < reference < function < port < pid < tuple < map < list < bitstring

プロキシ環境下でダウンロードが失敗する(Windows)

'winhttp set proxy proxy-server="<プロキシサーバー:ポート>"'

cat

  • 行番号を出力したい
    • cat -n <file_name>

Git

Githubで複数ユーザーを使い分ける

cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "<email address>" -f <file_name>

cat <file_name>.pub | pbcopy

Ruby

クラスの祖先を確認する

class A
end
class B < A
end
B.ancestors #=> [B, A, Object, Kernel, BasicObject]

メソッドがどのクラスにあるか確認する

Kernel#methodメソッドからMethodクラスのインスタンスを取得することができる Methodクラスのインスタンス変数ownerで親を確認できる

class A
  def aa
  end
end
class B < A
  def bb
  end
end
B.new.method(:aa).owner #=> A

メソッドの定義場所を調べる

bundlerでgem管理している場合は、vendor/bundle配下のソースパスを返す

class A
  def aa
  end
end
class B < A
  def bb
  end
end
B.new.method(:aa).source_location #=> ["(pry)", 18]

RubyGem作成

https://morizyun.github.io/blog/ruby-gem-easy-publish-library-rails/index.html

Workflow gem

class Article
  include Workflow
  workflow do
    state :new do
      event :submit, :transitions_to => :awaiting_review
    end
    state :awaiting_review do
      event :review, :transitions_to => :being_reviewed
    end
    state :being_reviewed do
      event :accept, :transitions_to => :accepted
      event :reject, :transitions_to => :rejected
    end
    state :accepted
    state :rejected
  end
end

a = Article.new
a.methods
#=> [:reject!, :new?,:submit!,:can_submit?,:awaiting_review?,:review!,
#    :can_review?,:being_reviewed?,:accept!,:can_accept?,:can_reject?,:accepted?,
#    :rejected?,:spec,:current_state,:process_event!,:halted?,:halted_because:halt:halt!
p a.new? #=> `true`
p a.awaiting_review? #=> `false`

#a.review! #=> ステータスがnewなので実行できない
##=> Workflow::NoTransitionAllowed: There is no event review defined for the new state
a.submit! #=> `new`状態から`awaiting_review`へ移行
p a.awaiting_review? #=> `true`

使えそうなメソッド

  • current_state: オブジェクトの状態を見やすく表示

Bundle から Rails new する

bundle initしてGemfileを作成する。 作成されたGemfilegem railsをアンコメントする。

最終的に作成されるGemfileは以下のようになります。

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"

それでは、Railsプロジェクトを作成します。

bundle exec rails new . -d postgresql
# Gemfile がコンフィリクトを起こすので、エンターキーで上書きを許可する

rails generate

  • rails g scaffold <モデル名> <項目名>:<型> ..
  • rails g controller <コントロール名> <アクション名> ..
  • rails g model <モデル名> <項目名>:<型> ..
  • rails g integration_test <インテグレーションテスト名>
  • rails g migration <マイグレーション名> ..
    • rails g migration <マイグレーション名> <属性名>:<型>
    • マイグレーション名をmigration_name_to_model_nameとするとadd_column行が追加され、モデル(model_name)とカラムが追加される

自動生成を削除する

  • rails d scaffold <モデル名>
  • rails d controller <コントロール名> <アクション名> ..
  • rails d model <モデル名>
  • rails d integration_test <インテグレーションテスト名>
  • rails d migration <マイグレーション名>

routes.rb

root '<コントローラ名>#<アクション名>'

ルーティングのバリエーション

  • どノーマル
    • get static_page/help -> static_page_help_url -> static_page#help
  • 名前付きルート
    • get /help, to: 'static_page#help' -> help_path -> static_page#help
  • 名前付きルートの別名
    • get /help, to: 'static_page#help', as: 'helf' -> helf_path -> static_page#help

resources

HTTPリクエスト URL アクション 名前付きルート 用途
GET /users index users_path すべてのユーザーを一覧するページ
GET /users/1 show user_path(user) 特定のユーザーを表示するページ
GET /users/new new new_user_path ユーザーを新規作成するページ (ユーザー登録)
POST /users create users_path ユーザーを作成するアクション
GET /users/1/edit edit edit_user_path(user) id=1のユーザーを編集するページ
PATCH /users/1 update user_path(user) ユーザーを更新するアクション
DELETE /users/1 destroy user_path(user) ユーザーを削除するアクション

テスト

assert

  • assert: 結果がtrueになることを主張する
  • assert_not: 結果がfalseになることを主張する
  • assert_response: 特定のステータスコードを持っていることを主張する
    • assert_response :success -> 200-299
    • assert_response :redirect -> 300-399
    • assert_response :missing -> 404
    • assert_response :error -> 500-599
    • assert_response 402 -> 402
  • assert_select: タグの検証
  • assert_template: 特定のテンプレートファイルが返されることを主張する
    • assert_template 'static_page/home'
  • assert_equal: 期待される値と実際の値を比較する
    • assert_equal <期待される値> <実際の値>
  • assert_no_difference <更新対象> <更新処理ブロック>: 更新対象が変更されないことを主張する
    • assert_no_difference User.count { post users_path, params: { user: { name: "", email: "", password: "bar", password_confirmation: "foo" } } }
  • assert_difference <更新対象> <更新件数> <更新対象ブロック>: 更新対象が変更されることを主張する、更新件数はオプションだが、更新後に影響を受ける件数を表す。createやdestroyのテストに用いる
  • assert_redirected_to: リダイレクト先が正しいかをチェックする。follow_redirect!の前に呼び出す
  • assert_empty: 空かどうかを検証
  • assert_not_empty: 空以外かどうかを検証
  • assert_nil: nilを検証

コマンド

  • rails test: 全てテスト
  • rails test:models: モデルのテストのみ
  • rails test:controllers: コントローラテストのみ
  • rails test:integration: インテグレーションテストのみ

その他

テスト内部でassignsメソッドを使用することでインスタンス変数にアクセスすることができる。

assigns(:user).remember_token

マイグレーション

  • マイグレーション
    • rails db:migrate
  • 元に戻したい
    • rails db:rollback
  • 最初の状態に戻したい
    • rails db:migrate VERSION=0

Migrationファイル

  • add_index :<models>, :<attr>, <属性>: インデックス追加
    • 属性: unique: true

モデル

  • before_saveコールバック: オブジェクトが保存される直前、オブジェクトの作成時や更新時に呼び出される
  • before_createコールバック: オブジェクトが作成される時に呼び出される

validates

  • presence: 必須入力を検証する(presence: true)
  • format: フォーマットを検証する
    • format { with: <正規表現> }: フォーマットを正規表現で検証
  • uniqueness <bool>: 一意性を検証する
    • uniqueness: { case_sensitive: <bool> }: 大文字を区別するかどうか(false:区別しない)
  • allow_nil: 引数にtrueを設定するとnilの検証はスキップする(新規作成時は別途検証を行い、更新時は検証を行わないという用途に用いる)

パーシャル(partial)

View に render を追加すると引数のファイルの内容をそ部分に追加します。 ファイル名の前にはアンダースコア(_)を入れる必要があります。 以下の例では、app/view/layouts/_shim.html.erbが読み込まれます。

<head>
  <%= render 'layouts/shim' %>
</head>

アセット

  • app/assets: 現在のアプリケーション固有のアセット
  • lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
  • vendor/assets: サードパーティのアセット

次の行はapp/assets/stylesheets ディレクトリ中の全てのCSSファイルがアプリケーションCSSのように扱われる。

/*
 *= require_tree .
 */

また、次の行はCSSの読み込みシーケンスの中で、application.css自身もその対象に含める。

/*
 *= require_self
 */

プリプロセッサエンジン

プリプロセッサエンジンは繋げて実行することができる。 例えば、foobar.js.erb.coffeeであれば、CoffeeScriptが実行されたのちERbが実行されます(コードは右から左へ実行される)。

ActiveRecodeのメソッド

  • obj.valid?: オブジェクトを検証後正常かを返す
  • obj.save: オブジェクトを永続化する
  • Recode.create([Hash]): Hashからオブジェクト作成と永続化する
  • obj.destroy: オブジェクトをDBから削除する
  • nRecode.find(Number): idで検索
  • Recode.find_by([Hash]): 属性で検索する
  • Recode.find_by_プロパティ名(String): 属性で検索する
  • Recode.all: モデルの全ての永続化オブジェクトを取得する。オブジェクトはRelationクラスで返ります
  • Recode.first: 最初のオブジェクトを取得する。
  • obj.reload: オブジェクトを変更し、保存を行わずにreloadすると、変更が取り消される
  • obj.update_attributes([Hash]): 属性をHashでオブジェクトを更新する、validationを行う
  • obj.update_attribute([Hash]): 属性をHashでオブジェクトを更新する、validationを行わない
  • obj.errors: ActiveModel::Errorsのインスタンスを返す。
    • obj.errors.full_messages: エラーが発生した属性とメッセージを連結した文字列を返す

Railsで追加されるメソッド?

  • Array.empty?: 配列が空であるかを返す
  • String.empty?: 文字列が空であるかを返す
  • Array.any?: 配列に要素があるかを返す

Rails関連メソッド

  • redirect_to: 特定のURLにリダイレクトする
    • redirect_to @userredirect_to user_url(@user) と等価
  • render: 引数のビューを描画する
  • follow_redirect!: 直前のpost内の処理で行われた、リダイレクトで、指定されたリダイレクト先に移動するメソッド(テスト用)
  • content_tag <タグ名>, <コンテンツ>, <属性>: タグを追加する。文字列エスケープが鬱陶しくなったら使う
  • cookies: セクション情報をブラウザが閉じても保存され続ける
  • sesstion: セッション情報を保持する ブラウザが閉じると消える
  • model.toggle!(:<属性>): boolean型を反転する
  • Time.zone.now: イムゾーンに元ずいた現在の日時を取得する

豆知識

個別のViewからレイアウトViewに値を渡す

provideを用いることで実現できる

個別のView

<% provide(:title, "Home") %>
<h1>Home</h1>

レイアウトView

<html>
<head>
<title><%= yield(:title) %></title>
</head>
</html>

シンボルの式展開

"#{:success}" #> "success"

範囲オブジェクトの作成

(1..10).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1...10).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

既存の認証方式

  • Gem
    • Clearance
    • Authlogic
    • Devise
    • CanCan
  • API
    • OpenID
    • OAuth

一時的なコンソール(サンドボックスモード)

データベースへの変更をコンソール終了時に全てロールバックする。

$ rails console --sandbox

Railsのダックタイプ

  • 1.year.ago: 1年前の日付を取得する

パスワードのハッシュ化

Modelに以下の行を追加する

class User < ApplicationRecord
  has_secure_password
end

上記のメソッドを実装すると以下のことができるようになる

  • セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
  • 2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される。
  • authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。

ただし、DBに自動で属性が追加される訳ではないので、自分でpassword_digest属性を追加する要がある。

$ rails generate migration add_password_digest_to_users password_digest:string

また、バリデーションが追加されているので、オブジェクト作成時に値をセットする必要がある。 以下のように、passwordpassword_confirmationに値をセットする。

@user = User.new(name: "example name", email: "exname@example.com",
                 password: "foobar", password_confirmation: "foobar")

Railsデバッグ

def show
  @user = User.find(params[:id])
  debugger # <-
end

Dockerでデバッグするにはattachする(composeのコンソール(docker-compose up)からはサーバーが止まってしまう)

$ docker-compose attach <コンテナ名>

flashの挙動

flash[:danger] = "error"とするとフラッシュメッセージを表示できるが、別ページに遷移してもメッセージが表示されたままになる。 flash.now[:danger] = "error"とする必要がある。

ランダムな文字列の取得

SecureRandom.urlsafe_base64はA-Z, a-z, 0-9, "-", "_"のいずれかの文字(64種類)の長さ22のランダムな文字列を返す

渡された文字列のハッシュ値を返す

ActiveModel::SecurePassword.min_costの部分はテスト時はハッシュ化のコストを高くする必要がないので、最小の値を設定しています。 本番環境では最適なコストが用いられます。

def self.digest(string)
  cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                BCrypt::Engine.cost
  BCrypt::Password.create(string, cost: cost)
end

cookiesを暗号化と永続化

以下のコードは生でcookiesに保存される

cookies[:user_id] = user.id

以下のコードは署名付きでcookiesに保存される

cookies.signed[:user_id] = user.id

以下のコードは署名した後、永続化するしてcookiesに保存される 実際は、20年後に削除される

cookies.permanent.signed[:user_id] = user.id

トークンとダイジェストが一致していることを確認する

BCrypt::Password.new(password_digest).is_password?(password_token)

fixtures をコンソールで使用する

$ rails db:fixtures:load
$ rails c
> michael = User.find ActiveRecord::FixtureSet.identify('michael')

URLエスケープ

CGI.escape('foo@example.com') # foo%40example.com

パーシャルとRelation

_model.html.erbというパーシャルから<%= render @models %>とすれば@modelsの中身を順に開いて、_model.html.erbに渡されます。

リファクタリング前

以下を次のようにリファクタリングすることができる

<% @users.each do |user| %>
  <%= link_to user.name, user %>
<% end %>

リファクタリング1回目

<li>
  <%= link_to user.name, user %>
</li>
<% @users.each do |user| %>
  <%= render user %>
<% end %>

リファクタリング2回目

<%= render @users %>

RubyGems

  • bcrypt: ハッシュ関数を使用するために使う。パスワードのハッシュ化に用いる
  • guard: ファイルの変更を検出してタスクを自動実行する
  • guard-minitest: guardとminitestの親和性をとる
  • faker: 実際に存在しそうなテストデータを作ることができる。ユーザー名やメールアドレスなどを得ることができる
  • will_paginate: ページネーションを提供する。Bootstrapを使用している場合はbootstrap-will_paginateも合わせて用いる

will_paginate

ページリンクを配置したい場所に<%= will_paginate %>を入れる。 will_paginateでページネーションするためにはpaginateの戻り値をviewに渡す必要があります。 コントローラーでModel.paginate(page: params[:page])とすれば、実現できます。 :pagewill_paginateが自動で渡します。

Heroku へデプロイ

Heroku のインストール(初回のみ)

brew install heroku
heroku login
heroku key:add

プロジェクト作成からHerokuへアップ

Heroku は sqlite に対応していない。今回はプロジェクトを作成する際に、PostgreSQLを使用するように指定して作成する。

rails new <appname> -d postgresql
git init
rails test
git add -A
git commit -m <commit message>
heroku create # この時点で git remote -v に追加される
git push -u heroku master
heroku rake assets:precompile # アセットをコンパイル

アプリケーションを更新する DB更新を伴う場合は、メンテナンスモードにしてから更新したほうがいい

rails test
git add -A
git commit -m <commit message>
git push
heroku maintenance:on
git push heroku
heroku run rails db:migrate
heroku maintenance:off

!注意!

本番データベースをクリアするにはpg:resetを行う

rails test
git push heroku
heroku pg:reset DATABASE
heroku run rails db:migrate
heroku run rails db:seed
heroku restart

Pumaを使用してHTTPSアクセスする

config/puma.rbの中身

workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count

preload_app!

rackup      DefaultRackup
port        ENV['PORT']     || 3000
environment ENV['RACK_ENV'] || 'development'

on_worker_boot do
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/
  # deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
end

./Procfile の中身、Herokuが内容使用してPumaを起動する

web: bundle exec puma -C config/puma.rb

豆知識

本番と開発でGemfileの中身を切り替えている

# 開発環境時にproductionのgemをインストールしない
bundle install --path vendor/bundle --without production

上記のコマンドを実行すると.bundle/configにいかが追加されます。

BUNDLE_WITHOUT: "production"

test環境でApplicationヘルパーのテストを行う

test/test_helper.rbへ以下の行を追加する。

ENV['RAILS_ENV'] ||= 'test'
..
class ActiveSupport::TestCase
  fixtures :all
  include ApplicationHelper
  ..
end

複数形に置き換える

使用例

pluralize(@user.errors.count, "error")
pluralize(1, "error") #> "1 error"
pluralize(2, "error") #> "2 errors"

Heroku のコンソールを見たい

heroku run rails console

開発環境と本番環境を画面上で判断したい

以下のコードをapplication.html.erbに追加する。

<%= debug(params) if Rails.env.development? %>

2つのYaml出力方法

puts user.attributes.to_yaml
y user.attributes

トラブルシューティング

Heroku アップロード直後に The page you were looking for doesn't exist. が表示される

以下のコマンドを実行する

rake assets:precompile

SASS

SCSS でできること

  • ネスト
  • a:hoverのように内方するエンティティの継承 -> &:hover
  • 変数($val)
  • ミックスイン(mixin), @includeを用いる
footer {
  ..
  a {
    ..
    &:hover {
      ..
    }
  }
}

上記のSASSは次のようにプリコンパイルされます。

footer {
  ..
}

footer a {
  ..
}

footer a:hover {
  ..
}

ミックスイン機能

@mixin box_size {
 box-sizing: border-box;
}

.debug_dump {
  width: 100%;
  @include box_size;
}

変数の呼び出し

.field_with_errors {
  @extend .has-error;
  .form-control {
    color: $state-danger-text; // extendした.has-error内の $state-danger-text 使用できる
  }
}

flash ようには、4つのスタイルがある(success, info, warning, danger)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment