インスタンスメソッドの理解
ずっともやっとしてた部分を整理したらアハ体験があったのでまとめる。
こんなモデルがある場合。
models/user.rb
class User < ApplicationRecord has_many :items end
models/item.rb
class Item < ApplicationRecord belongs_to :user validates :user_id, presence: true end
中間テーブルはLikeとする。
models/like.rb
class Like < ApplicationRecord belongs_to :user belongs_to :item validates :user_id, presence: true validates :item_id, presence: true end
上の関係は英文にするとこんな風に書ける。
User like item.
冠詞がないのと複数形じゃないのはご愛嬌。
モデル、メソッドのレシーバ・引数の関係を上記の英文を例に考えてみる。
- モデルは主語を担う。(User)
- レシーバは動詞を担う。(like)
- 引数は目的語を担う。(item)
上の関係をメソッドで表す。
models/user.rb
class User < ApplicationRecord has_many :items has_many :likes has_many :like_items, through: :likes, source: :item def like(item) self.likes.find_or_create_by(item_id: item.id) end end
主語はUserなので、Userモデルにメソッドを書く。
メソッドのレシーバが動詞なので、likeというインスタンスメソッドをつくる。
メソッドの引数は目的語なので、itemを引数にする。
Rails 5.1以降 でjqueryを使う
Rails5.1から、デフォルトではjqueryに依存しない仕様になった。
Ruby on Rails 5.1リリースノート | Rails ガイド
jqueryを使うためには下記の手順を踏むこと。
Gemの追加とbundle update
Gemfileに gem "jquery-rails"
を追加し、bundle updateを実行する。
Gemfile.lockはこんな感じに変更される。
+ jquery-rails (4.3.3) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) - sass (3.5.7) + sass (3.6.0) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) + jquery-rails
application.jsに追加
application.jsに下記2行を追記する。
application.js
//= require activestorage //= require turbolinks //= require_tree . # 以降追記 //= require jquery //= require jquery_ujs
Railsアプリを作るときの共通パーツの設定
初期設定はこちら。 mom0tomo.hateblo.jp
共通レイアウトはapp/views/layouts/ に配置する。
headタグのパーツ
app/views/layouts/application.html.erb
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>アプリケーション名</title> <%= csrf_meta_tags %> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> </head> <body> <%= render 'layouts/navbar' %> <div class="container"> <%= render 'layouts/flash_messages' %> <%= yield %> </div> </body> </html>
flashメッセージのパーツ
app/views/layouts/_flash_messages.html.erb
<% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %>
flash[:success] / flash[:danger]などよく使う。
エラーメッセージのパーツ
app/views/layouts/_error_messages.html.erb
<% if model.errors.any? %> <div class="alert alert-warning"> <ul> <% model.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>
以下は必須とは限らないがだいたい必要になるパーツ。
ナビバーのパーツ
app/views/layouts/_navbar.html.erb
<header> <nav class="navbar navbar-inverse navbar-static-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">アプリケーション名</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Signup</a></li> <li><a href="#">Login</a></li> </ul> </div> </div> </nav> </header>
Railsの便利なコマンドと設定
新規アプリケーション作成するとき
DBの種類を設定する
$ rails new {app_name} --database=mysql
テストを作成しない
$ rails new {app_name} --skip-test
bundle installするとき
gemのインストールパスを指定する
$ bundle install --path vendor/bundle
Production環境のgemを除いてインストールする
$ bundle install --without production
CSS/JS/Helper/Routingを自動生成しない
config/initializers/generators.rbを作成し設定を書く。
Rails.application.config.generators do |g| g.stylesheets false g.javascripts false g.helper false g.skip_routes true end
おまけ
gitignoreはこの辺りを設定しておく。
https://www.gitignore.io/api/vim,ruby,macos,rails,rubymine,intellij+all
モデル名と異なるアソシエーション名を使う場合の決まり
Rails tutorialでも出てくるuserがfollowings/followers(どちらもuserテーブルを使う)を持つ関係を例にまとめる。
models/user.rb
class User < ApplicationRecord # followingsのアソシエーション has_many :relationships has_many :followings, through: :relationships, source: :follow # followersのアソシエーション has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id' has_many :followers, through: :reverses_of_relationship, source: :user end
中間テーブルはrelationshipsとする。
models/relationship.rb
class Relationship < ApplicationRecord belongs_to :user belongs_to :follow, class_name: 'User' end
class_nameについて
モデル名_id という名前になっている場合、Railsでは自動的にモデルを参照する。
しかし follow_id のように命名規則に従っていないものを使う場合は、 class_name: 'User'
と補足設定する必要がある。
through, sourceについて
既存のリレーションから中間テーブルを経由して向う側にあるモデルを参照するようにするためには、 through: ... , source: ...
を使用する。
has_many :followings, through: :relationships, source: :follow
これにより、 user.followingsと書けば、該当のuser がフォローしている User群を取得できるようになる。
仕組み
has_many :followings
- usersとfollowingsの関係を新しく命名し、フォローしているUser群を表現している。
- Followingというモデルは無いため、この後ろに取得する情報の補足を付け足している。
through: :relationships
- has_many: relationshipsの結果を中間テーブルとして指定している。
source: :follow
- 中間テーブルのカラムの中でどれを参照先の id とすべきかを選択している。
- user_id というカラム名にすると重複してしまう為、フォローされる側は follow_id とする
上の仕組みによって、 user.followingsでuser が中間テーブル relationships を取得し、
その1つ1つの relationship の follow_id から 自分がフォローしている User 達 を取得する流れができる。
models/user.rbに見やすく色をつけるとこんな感じ。
has_many :relationships
has_many :followings, through: :relationships, source: :follow
has_many :reverses_of_relationships, class_name: 'Relationship', foreign_key: 'follow_id'
has_many :followers, through: :reverses_of_relationship, source: :user
Railsのアソシエーションの仕組み
理解するのにものすごく時間がかかったので、まとめておく。
アソシエーションとは
簡単にいうとモデルを参照するためのメソッド。
例は下記に示す。
メソッドはふだん使うときに書くが(当たり前)、アソシエーションは使い道も書き方も決まっていてるので、初めにまとめて書いておく決まり。
アソシエーションを利用するケース
usersとitemsという多対多のテーブルがあり、relationshipsという中間テーブルを使う場合を考える。
models/user.rb
class User < ApplicationRecord has_many :relationships has_many :items, through: :relationships end
models/item.rb
class Item < ApplicationRecord has_many :relationships has_many :users, through: :relationships end
models/like.rb
class Relationship < ApplicationRecord belongs_to :user belongs_to :item end
下記のように使うことができるが、
@user = item.user
この理由は、 models/item.rb
has_many :users, through: :relationships
上記のアソシエーションはもともと以下のようなメソッドを省略したものだから。
def user User.find_by(id: Relationship.find(self.id).item_id) end
HerokuでgitpushしようとするとPermission denied (publickey).になる
読んだもの
学んだこと
会社でつかっていたリポジトリをクローンして herokuに変更をpushしようとしたところ、下記のエラーになった。
$ git push heroku master Warning: Permanently added the RSA host key for IP address '50.19.85.156' to the list of known hosts. Permission denied (publickey). fatal: Could not read from remote repository.
会社のPCではpublic keyを登録していたが、自宅PCでは登録していなかったのが原因。
$ heroku keys:add
上記コマンドでpublic keyを登録すればOK.
find_or_create_byとfind_or_initialize_byの違い
読んだもの
学んだこと
両者の違いは下記の通り。
- find_or_create_byだと新規作成して保存
- find_or_initialize_byして保存はしない
保存しないことのメリットは、新規作成するときのみ併せて何かする、という処理ができること。 find_or_create_byは新規作成と同時に保存してしまうので、新しいレコードなのか既にあるレコードなのか判断できず、上記のような処理はできない。
例えば、下記のように使う。
@user = User.find_or_initialize_by(name: 'test') unless @user.persisted? # @user が保存されていない場合の処理を書く @user.save end end
Herokuアプリを削除する
git remoteから削除するのを忘れがち。
Heroku上のアプリを削除
$ heroku apps:destroy --app { app_name }
git remoteから削除
$ git remote rm heroku // 確認する $ git remote -v
RailsのアプリケーションをHerokuにデプロイする流れ
たまにしか一連の流れをやらないので、いつも何かを忘れてしまう。 まとめておく。
コマンドラインツールのインストール
$ wget https://cli-assets.heroku.com/heroku-cli/channels/stable/heroku-cli-linux-x64.tar.gz -O heroku.tar.gz $ sudo mkdir -p /usr/local/lib/heroku $ sudo tar --strip-components 1 -zxvf heroku.tar.gz -C /usr/local/lib/heroku $ sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku
ターミナル上でHerokuにログイン
$ heroku login Enter your Heroku credentials. Email: { mail_address } Password: { pass }
Heroku アプリを作成
$ heroku create { app_name } // 作成されたことを確認 $ heroku apps { app_name } // リモートにあることを確認 $ git remote -v heroku https://git.heroku.com/app_name.git (fetch) heroku https://git.heroku.com/app_name.git (push) origin https://github.com/mom0tomo/app_namegit (fetch) origin https://github.com/mom0tomo/app_name.git (push)
DBの設定
Heroku の標準データベースはPostgreSQL。 Rails 側でPostgreSQLを使用するために設定をする。
Gemfile
group :production do gem 'pg', '0.21.0' end
--without productionを指定してbundle installする。
$ bundle install --without production
config/database.yml
production: adapter: postgresql encoding: unicode pool: 5 database: { app_name }_production username: { app_name } password: <%= ENV['APP-NAME_DATABASE_PASSWORD'] %>
Herokuにpush
$ git push heroku master
マイグレーション
$ heroku run rails db:migrate
【オプション】PostgreSQL アドオンの追加
下記のようなエラーが出る場合がある。
PG::ConnectionBad: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
Heroku 上でPostgreSQL がインストールされていないせいで起きるエラー。
デプロイ時に config/database.yml がちゃんと設定されていないと、自動的に PostgreSQL がインストールされないので、手動でインストールする。
$ heroku addons:create heroku-postgresql:hobby-dev
Rspecで"You must pass an argument rather than a block...." エラー
読んだもの
学んだこと
matcherにはブロックを受け取れるものとそうでないものがある。
例えば以下のように書くと表題のエラーになる。
context 'とある場合' do it '名前がほげほげであること' do expect { described_class.new(user).name }.to eq "ほげほげ" end end
You must pass an argument rather than a block to use the provided matcher....
これはeqというmatcherはブロック {}
を受け取れないというエラー。
下記のように引数にすることで実行できる。
context 'とある場合' do it '名前がほげほげであること' do expect(described_class.new(user).name).to eq "ほげほげ" end end
何気なく他の部分のテストを真似るとときどきこのようなエラーを招くので気をつける。
多対多のリレーションをhas_manyでモデルに実装する
Railsでは、ActiveRecordのhas_manyかhas_and_belongs_to_manyを使うことで、多対多のテーブルどうしの関連をモデルに実装できる。
基本的には拡張性のあるhas_manyを使った方がよい。
マイグレーションファイルをつくる
Twitterクローンの例をもとに、userが micropost を likeする例で考える。 usersテーブルとmicropostsテーブルを作成し、中間テーブル(likesテーブル)に両方のテーブルの外部キーを定義する。
$ rails g model User name:string $ rails g model Micropost content:string $ rails g model Like user_id:integer micropost_id:integer
モデルにthroughオプションをつける
throughオプションにより、usersからlikes経由でmicropostsにアクセスできるようになる。 つまり、user.micropostsという形が使える。
app/models/user.rb
class User < ActiveRecord::Base has_many :likes has_many :microposts, through: :likes end
micropostからuserも同じく、micropost.usersと言う形が使える。
app/models/micropost.rb
class Micropost < ActiveRecord::Base has_many :likes has_many :users, through: :likes
中間テーブルとの関係はbelongs_toで表す。
app/models/like.rb
class Like < ActiveRecord::Base belongs_to :user belongs_to :micropost end
【解消法】has_secure_passwordを使おうと思ったらbcryptでエラー
こんなエラーが出た。
cannot load such file -- bcrypt
bcrypt gemでよくあるエラーらしい。
読んだもの
https://github.com/codahale/bcrypt-ruby/issues/142#issuecomment-291345799
学んだこと
解消法
$ gem uninstall bcrypt $ gem uninstall bcrypt-ruby $ gem install bcrypt --platform=ruby
Gemfileのbcryptのバージョンを下記に変更する。
gem 'bcrypt', '~> 3.1.11'
$ bundle install
して、サーバーを再起動する。
原因
$ rails _5.0.6_ new
したときにGemfileに自動で書き込まれるバージョン gem 'bcrypt', '~> 3.1.7'
に不具合がある模様。
3.1.11以降に解決されているため、3.1.7関係を一度アンインストールして、新しい方をインストールし直す。
Homebrewで管理しているElasticsearchにプラグインをインストールする
読んだもの
学んだこと
Elasticsearch::Transport::Transport::Errors::BadRequest:[400] No handler found for [400] No handler found for analysis-icu
などというエラーが出たので、analysis-icuをインストールしようとした。
公式だと
$ bin/elasticsearch-plugin install [plugin_name]
でインストールするように書いてあるが、brewでElasticsearchを管理している場合はパスが異なる。
brew管理の場合は
$ /usr/local/bin/elasticsearch-plugin install [plugin_name]
でインストールする。