- 投稿日:2020-06-27T22:53:58+09:00
Docker で Ruby on Jets + React のアプリを作成する開発環境構築
概要
Ruby on Jets(API)と React のアプリを作成するための開発環境構築手順です。
Ruby on Jets は API 専用として作成します。DB は MySQL を利用。
React は Typescript を使います。
Docker は Docker for Mac を利用。ローカルでページを開くと API から DB に保存されているユーザー情報を取得して表示するところまでやっています。
AWS へのデプロイまではしていません。ファイル構成
reactjets/ ├ api/ │ ├ ... ├ front/ │ ├ ... ├ docker/ │ ├ api/ │ │ ├ Dockerfile │ ├ front/ │ │ ├ Dockerfile │ ├ mysql/ │ │ ├ conf.d │ │ │ ├ my.cnf │ ├ docker-compose.ymlDocker 環境構築
Dockerfile(2種),Gemfile, Gemfile.lock, my.cnf, docker-compose.yml を作成
docker/front/DockerfileFROM node:12.18docker/api/DockerfileFROM ruby:2.7.1 WORKDIR /api COPY api /api RUN bundle installapi/Gemfilesource 'https://rubygems.org' gem 'jets'Gemfile.lock は空のファイルを作成
api/Gemfile.locktouch Gemfile.lock
docker/mysql/conf.d/my.cnf[mysqld] character-set-server=utf8mb4 explicit-defaults-for-timestamp=1 [client] default-character-set=utf8mb4docker/docker-compose.ymlversion: '3' services: front: build: context: ../ dockerfile: docker/front/Dockerfile volumes: # :より左の部分は自分の環境に合わせる - ~/Dev/reactjets/front:/front ports: - "8000:3000" stdin_open: true api: build: context: ../ dockerfile: docker/api/Dockerfile command: bash -c "bundle exec jets server --port 3000 --host 0.0.0.0" volumes: # :より左の部分は自分の環境に合わせる - ~/Dev/reactjets/api:/api ports: - "3000:3000" depends_on: - db db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: app MYSQL_USER: docker MYSQL_PASSWORD: docker TZ: 'Asia/Tokyo' ports: - 3306:3306 volumes: - ./mysql/conf.d:/etc/mysql/conf.ddocker-compose ビルド
$ docker-compose build
Jets アプリ作成
$ docker-compose run api jets new . --mode api --database=mysql※ Overwrite /api/Gemfile? (enter "h" for help) [Ynaqdhm] がでてきたら Y
api 配下に jets アプリケーションが作成されていることを確認
DB 接続のために .env.development に環境変数を追加.env.developmentDB_HOST=db DB_NAME=app DB_USER=docker DB_PASS=dockerReact アプリ作成
$ docker-compose run --rm front sh -c 'yarn create react-app front --template typescript'front 配下に react アプリケーションが作成されていることを確認
起動
$ docker-compose up --buildreact 起動
$ docker exec -it docker_front_1 bash # cd front # yarn start画面が表示されることを確認
Ruby on Jets : http://localhost:3000
React : http://localhost:8000
api 作成
user を scaffold で作成
$ docker exec -it docker_api_1 bash # jets generate scaffold user name:string age:integer # jets db:create db:migrate※ jets db:create db:migrate で access denied など出ていたら mysql 側のユーザーの権限設定を確認する
GRANT ALL ON *.* to docker@'%';users テーブルが作成されているので、直接2件適当に INSERT し
http://localhost:3000/users にアクセスすると、以下のようなデータが返ってきていることが確認できる[{"id":1,"name":"mikami","age":26,"created_at":"2020-06-27T18:57:44.000Z","updated_at":"2020-06-27T18:57:44.000Z"},{"id":2,"name":"tomoyuki","age":32,"created_at":"2020-06-27T18:57:44.000Z","updated_at":"2020-06-27T18:57:44.000Z"}]front と api の疎通
front 側
axios インストール
$ docker exec -it docker_front_1 bash # cd front # yarn add axiosApp.tsx を編集
App.tsximport React from 'react'; import axios from 'axios'; import './App.css'; class App extends React.Component<any, any> { constructor(props: any) { super(props); this.state = { users: [] } } componentDidMount() { axios.get('http://localhost:3000/users') .then((result) => { this.setState({ users: result.data }); }) } render() { return ( <div className="App"> <h1>Users</h1> <div> { this.state.users.map((v: any) => { return ( <div key={v.id}> <p>id: {v.id}</p> <p>name: {v.name}</p> <p>age: {v.age}</p> </div> ) })} </div> </div> ) } } export default App;このままでは CORS で通信できないので、api 側で CORS の設定をする
api 側
以下のコメントアウトを外す
application.rbconfig.cors = true # for '*'' # defaults to false確認
再起動
$ docker-compose stop $ docker-compose start $ docker exec -it docker_front_1 bash # cd front # yarn starthttp://localhost:8000/ にアクセスすると以下のように表示されていれば疎通ok
WIP
AWS へのデプロイ手順
Ruby on Jets デプロイ
https://qiita.com/kskinaba/items/9c570093ed912f8f1681 この通りにやる
Ruby 2.5系じゃないとだめらしい…
Deploying to Lambda api-dev environment... /usr/local/bundle/gems/memoist-0.16.2/lib/memoist.rb:213: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call /usr/local/bundle/gems/jets-2.3.16/lib/jets/lambda/dsl.rb:319: warning: The called method `_unmemoized_find_all_tasks' is defined here Building CloudFormation templates. Generated CloudFormation templates at /tmp/jets/api/templates Deploying CloudFormation stack with jets app! Waiting for stack to complete 02:25:31AM CREATE_IN_PROGRESS AWS::CloudFormation::Stack api-dev User Initiated 02:25:34AM CREATE_IN_PROGRESS AWS::S3::Bucket S3Bucket 02:25:35AM CREATE_IN_PROGRESS AWS::S3::Bucket S3Bucket Resource creation Initiated 02:25:56AM CREATE_COMPLETE AWS::S3::Bucket S3Bucket 02:25:58AM CREATE_COMPLETE AWS::CloudFormation::Stack api-dev Stack success status: CREATE_COMPLETE Time took for stack deployment: 28s. You are using Ruby version 2.7.1 which is not supported by Jets. Jets uses Ruby 2.5.3. You should use a variant of Ruby 2.5.x
- 投稿日:2020-06-27T22:36:44+09:00
バブルソート、選択ソートをRubyで作る
目的
ソートアルゴリズムについて、実際にコードを書いてまとめてみる。
言語はRubyでやってみます。ソートについて
ソートは、入力として与えられた数字を小さい順に並び替えること。
バブルソート
バブルソートは、「数列の右から左に向かって、隣り合う2つの数字を比較して入れ替える」操作を繰り返し行う。数列に対して、処理を1周行えば、一番小さい数字が配列の1番目に入る。要素の数だけ周回することで並び替えることができる。
bubble.rbdef bubble_sort(array) length = array.length (length - 1).times do |round| (length - 1 - round).times do |n| if array[length - n - 1] < array[length - n - 2] smaller = array[length - n - 1] array[length - n - 1] = array[length - n - 2] array[length - n - 2] = smaller end end end array end array = [2, 3, 13, 7, 8, 9, 11, 1, 5] bubble_sort(array) #=> [1, 2, 3, 5, 7, 8, 9, 11, 13]バブルソート関数の中身
・配列arrayの長さをlengthで取得(1行目)
・(length - 1)ラウンドで入れ替えが終了する(2行目)
・n回目のラウンドでは、(length - n - 1)回比較して、入れ替える処理を行う(3行目)
・左の数値が右の数値よりも大きい場合は入れ替える(4~7行目)
大小比較の回数は、(length - 1) + (length - 2) + ・・・+ 1 = (length)^2 / 2 になる。選択ソート
選択ソートは、「数列の中から最小値を検索し、左端の数字と入れ替える」という操作を繰り返す。
selection.rbdef selection_sort(array) length = array.length (length - 1).times do |round| min = round + 1 round.upto(length - 1) do |n| if array[n] < array[min] min = n end end min_number = array[min] array[min] = array[round] array[round] = min_number end array end最小値のインデックス番号を取得して、検索した後に入れ替える。
バブルソートと同じで、大小比較する回数は (length)^2 / 2 になる。後書き
次は、挿入ソート、ヒープソート、マージソート、クイックソート、バケットソートについてもまとめようと思います。
- 投稿日:2020-06-27T22:24:37+09:00
Rubyの基本用語
プログラミング初心者が
アウトプット用にRubyの基本用語をまとめていきます。Rubyの基本用語
1.Ruby
大きなWebアプリケーションから小さなプログラムまで作ることができるプログラミング言語。
拡張子は「.rb」2.rubyコマンド
Rubyに関するさまざまな操作が実行できるコマンド。
ターミナルで打ち込む。3.irb(Interactive Ruby)
ターミナルから直接Rubyのプログラムを動かすことができる機能。4.式展開
文字列の中に式を入れることができる機能。
「文字列の中で#{式}」とするだけでOK。5.配列
複数の値をもつことができる値。
[ ]を用いて順番で値を管理する。
配列の中のデータを「要素」という。6.添字
配列の各要素に割り振られた番号のこと。
添字は「0」から始まる。7.ハッシュ
複数の値を持つことができる値。
{ }を用いてキー(名前)とバリュー(値)で管理する。8.クラス
値の共通の属性と処理を定義するもの。設計図のようなもの。9.インスタンス
クラスを元にして作られるデータのこと。10.クラスメソッド
クラスで共通の情報を使った処理に使用するメソッド。
クラス自身が使用する。11.インスタンスメソッド
インスタンスごとの個別の情報を使った処理に使用するメソッド。
「インスタンスメソッドを定義したクラス」から生成されるインスタンスが使用する。12.属性と属性値
属性はデータの性質となる情報のこと。
属性値はそれぞれにあてはまる具体的な情報のこと。13.インスタンス変数
データが持つ属性を定義する変数。
すべてのインスタンスが同じ属性をもち、個々のインスタンスが別の値をもつことができる。
先頭に「@」をつける。14.ローカル変数
データが持つ属性を定義する変数。
記述してあるインスタンスメソッド内でしか使用できない。15.initializeメソッド
インスタンスが生成された瞬間に、生成されたインスタンスが実行する処理を定義するインスタンスメソッド。16.Sinatra
Rubyのフレームワークのひとつ。
基本的に処理速度が速い。
既存の機能が少ないので、初心者でもゼロからWebアプリケーションを構築しやすくなっている。
大規模なアプリを作成しようとすると相応のカスタマイズが必要になる。まとめ
厳密にはRuby"限定"の用語ではないものも含まれていますが、Rubyの学習において学んだ用語なので記述しておきます。
- 投稿日:2020-06-27T22:23:29+09:00
Rubyにおけるクラスとインスタンスの違いについて
はじめに
Rubyを学習していて出てくる、
クラスとインスタンスの概念。初学時にどういう意味かさっぱりわからなかったので、
例を挙げながら書いていきます。クラスとは
値(データ)に持たせたい、共通の属性と処理に関するルールを決めるための雛形。
実体を持たないので、クラスだけではデータを動かせません。例) 車でいうと「設計図」にあたる。
設計図は、色や形(共通の属性)をどうするかとか、
走る、止まる、ウィンカーを出すなど(共通の処理)をどうするか、
などが書かれた紙。
あくまで設計図なので、車(インスタンス)がないと動かない。# クラスの定義(大文字で始める) class Car endインスタンスとは
クラスを元にして作られるデータのこと。
実体を持っており、クラスで定義した共通の属性と処理を持っています。例) 車でいうと「車」にあたる。
車は、色や形(共通の属性)を持っていて、
走る、止まる、ウィンカーを出すなど(共通の処理)ができる。
設計図から作られた車そのものなので、実体もあるし、もちろん動く。# インスタンスの生成 class Car end taxi = Car.new # newメソッドでCarクラスのインスタンスを生成して、変数taxiに代入 puts taxi # インスタンスを出力
- 投稿日:2020-06-27T22:19:04+09:00
DragonRubyで簡易タイピングゲーム
以前、DragonRuby GTK (Game Toolkit) で遊んでみた という記事を書きました。
QiitaにHello, Worldを投稿するとだいたい飽きる(!)のですが、今回は引き続き触っています。
ふと「タイピングゲームを作りたいな」と思ったので、その練習に単純なものを作ってみました。
作ったもの
テキストが表示されて、それを入力するだけです。
タイムオーバーとかもなく、ただそれだけです。こちらのリンクからWebブラウザでプレイできます。
https://tnantoka.github.io/typing-on-rails/
※ Firefoxだとたまにエラーになります(ページ更新で解消するはず)ソースコードはGitHubにあります。
https://github.com/tnantoka/typing-on-railsタイプするテキストをRails Guidesのサンプルコードから借りたので、Typing on Railsという名前になっています。
Tips
基本的にはDragonRuby GTK (Game Toolkit) で遊んでみた の内容で作れるのですが、
一部書いてないものがあったのでメモしておきます。背景色を変える
outputs.background_color
に[R, G, B]
を設定すれば変更できます。# 赤!!! outputs.background_color = [255, 0, 0]押されたキーの一覧を取る
前回使った
inputs.keyboard.key_up.space
でも入力状態は取れるのですが、
今回はキーの一覧を見る実装にしてみました。入力されたキーは
inputs.keyboard.key_down.truthy_keys
で取得できます。# aを入力 [:char, :raw_key, :a] # A(Shift + a)を入力 [:char, :raw_key, :a, :shift] # Command + Shift + aを入力 [:char, :raw_key, :a, :shift, :meta]フォントによってLabelのサイズわりと違う
Label
のsize_enum
には-10
〜10
を指定できるのですが、同じ10
を指定しても、フォントによって結構サイズが違います。例えば、以下の4つのフォントを並べてみると…
こうなります。
使うフォントによって
size_enum
を調整する必要がありそうです。公開したゲームが動かない
packageしたbuildをGitHub pagesで公開したら、動きませんでした。
原因は、
Server reported failure downloading 'app/.main.rb.swp'!
というエラーでした。
.gitignore
でGitは無視してくれますが、当然DragonRubyは無視してくれないので…。
builds/typing-on-rails-html5-0.1/manifest.json
を見ると不要なファイルが含まれていることに気づけるかもしれません。
(今回の.swp
ファイルもバッチリ入っていました)まとめ
200行足らずで作れました。
(前回作ったPrimitives.rbの分は数えていませんが)
お手軽です。
- 投稿日:2020-06-27T21:47:28+09:00
開発メモ ~複数枚投稿した画像の一枚目のみを表示させたい~
はじめに
画像を同時に複数枚投稿した場合に、画像を表示させるための記述に工夫を加えなければなりませんでした。
その内容を忘れないために、メモとして記事を投稿します。やりたいこと
複数枚投稿した写真のindex番号が一番若いものをindex画面に表示させたい
記述
ファイル名.html.haml
section.fifth-headline %h2.title ピックアップカテゴリー .contentsbox .contents-title %h3.heading 新規投稿商品 .contents-lists .contents-list - @newProducts.each do |product| =link_to "#" do .contents-list__itmes .contents-list--item = link_to product_path(product), class: "listBtn" do %figure.product__images = image_tag product.images[0].name.url, alt: "image_url",size: "200x150", class:"new-img" .contents-list--inner %h3.name = product.name .data %ul %li = product.price 円 %li %i.fa.fa-star.likeIcon 0 %p (税込)home_controller
def index @newProducts = Product.includes(:images).where(status: 0).order("RAND()") ~省略~ endポイント
画像を複数枚投稿した場合で、1枚目だけを表示させたい時以下の様に記述する事でindex番号の[0]を取得することができる
= image_tag product.images[0].name.url, alt: "image_url",size: "200x150", class:"new-img"また、この場合はindexアクションで呼び出すファイルを定義してあげなければならい。
よって、.where(status: 0)定義しインスタンス変数に代入してあげる。@newProducts = Product.includes(:images).where(status: 0).order("RAND()")これで上手く実装できると思います!
終わりに
今回は、『こんな書き方もできるんだ!』と思い、記事にしました!
まだまだ、勉強中ですので間違っている箇所がありましたらコメントください!ありがとうございました!
- 投稿日:2020-06-27T21:12:46+09:00
Rails CRUD機能実装②(今回は、編集、詳細)
前回の続きで、編集と詳細を記載します。
機能追加するときの手順復習:routes.rb→controller追記→view追加
編集機能
新規投稿と似たような手順です。
編集画面表示(edit)→入力内容で更新(update)routes.rbresources :birds, only: [:index, :new, :create, :destroy, :edit]一覧画面に編集画面へのリンクを貼ります。
Prefixが「edit_bird」でidを渡して、GETメソッドなので以下のようになりますね。index.html.erb<%=link_to "編集", edit_bird_path(bird.id), method: :get %>渡されたidと一致するレコードを取得し、編集画面へと渡します。
birds_controller.rbdef edit @bird = Bird.find(params[:id]) endそして、編集画面です。
edit.html.erb<%=form_with(model:@bird, local:true) do |form|%> <%=form.text_field :name, placeholder: "鳥の名前"%> <%=form.text_field :pic, placeholder: "鳥の写真のURL"%> <%= form.submit "更新"%> <%end%>ここからは更新処理です!
まずはroutes.rb!
routes.rbresources :birds, only: [:index, :new, :create, :destroy, :edit, :update]controllerで入力内容を受け取って、DBを更新します!新規投稿で作成した、bird_paramを使用します!
birds_controller.rb... def update bird = Bird.find(params[:id]) bird.update(bird_params) end private def bird_param # params.require(:モデル名).permit(:カラム名,:カラム名,......) params.require(:bird).permit(:name, :pic) end ...ちゃんとカワセミ2に変更されていますね!
詳細表示機能
いよいよ最後の機能です!頑張りましょう!!
routes.rbresources :birds, only: [:index, :new, :create, :destroy, :edit, :update, :show]としてもいいですが、全機能追加しているので、onlyの記述はいらないですね!消しましょう!
routes.rbresources :birdsかなりスッキリしました!!
一覧画面に詳細画面へのリンクを貼りましょう!
rails routesコマンドを実行すると
prefixがbird,methodがGETで、idもしてしてあげましょう!index.html.erb<%= link_to '詳細', bird_path(bird.id), method: :get %>そしてcontrollerでidを受け取り、レコードを取得します!
birds_controller.rbdef show @bird = Bird.find(params[:id]) endいよいよ最後に、show.html.erbです!
show.html.erb<%= @bird.name %> <div style= "background-image: url(<%= @bird.pic %>); background-position: center center; background-size: cover; width: 300px; height: 300px; margin-bottom: 10px; "> </div>きちんと選択したものが表示がされています。
まとめ
railsは、modelを渡せばリンク先を自動識別してくれたりと、フレームワークの決まりを理解すれば、爆速で基本的なサイトが作れますね。
今度は、ログイン機能を実装させますので、ご期待ください!
最後まで、ありがとうございました!!
- 投稿日:2020-06-27T18:19:17+09:00
初投稿。グループ機能作成中に起きた事
Routing Error
グループ機能作成中に起きたRouting Errorについて、
この画像では、controllerがgroupsになっている。
下の画像では、groupsとなっているが、ここでgroupにした事でRouting Errorが起きてしまった。
マイグレーションについて
例えばこの様マイグレーションファイルにdb:migrateで間違った記述をしてしまった時
db:rollbackで戻して訂正する事ができる。
また、db:migrateで訂正完了です。db:rollback
db:rollbackでは一つ前しか戻せない。
db:rollback STEP=数字
db:rollback STEP=数字を使う事で任意に所まで戻す事ができる。
- 投稿日:2020-06-27T17:33:35+09:00
Rails CRUD機能実装①(今回は、新規追加、削除)
この記事の目的
railsを用いた、CRUD機能をもったwebアプリケーションの作成手順です。鳥の写真と名前を投稿できるサイト(birdtweet)です。
railsアプリケーションの作成
railsのバージョン6.0.0を指定。-dの後ろに使用するDB(今回はmysql)を指定。
rails _6.0.0_ new birdtweet -d mysql設定変更。
config/database.yml... # encoding: utf8mb4 encoding: utf8 ...DB作成
cd birdtweet rails db:createGemfileの編集
プロジェクト直下にあるGemfileを開くGemfile# gem 'mysql2', '>= 0.4.4' gem 'mysql2', '>= 0.5.3' ........ (ファイル最下行) gem 'pry-rails'ターミナルで設定内容の更新
bundle updatemodelの作成
ターミナルにて
rails g model birdカラムを追記
db/migrate/2020********create_birds.rb... create_table :birds do |t| t.string :name t.text :pic t.timestamps end ...コマンドにて、変更内容の更新!
rails db:migrateコンソールでDBにデータをいくつか入れます。
rails c Bird.create(name: "ツノメドリ", pic: "https://cdn.pixabay.com/photo/2020/05/26/13/22/puffins-5223057_1280.jpg") Bird.create(name: "カワセミ", pic: "https://cdn.pixabay.com/photo/2017/02/07/16/47/kingfisher-2046453_1280.jpg") exitコントローラの作成
以下のコマンドで、コントローラを作成します。
rails g controller birds複数形にしたり、しなかったり混乱しますが、
モデルだけが単数形です!ここからの流れ
各機能作成の流れはほぼほぼ同じです!
routes.rbに追記
↓
index.html.erbに各機能へのリンクを追記(index意外)
↓
birds_controller.rbに追記
↓
機能名.html.erb作成一覧表示機能
railsには7つのアクションがある。
- index:一覧
- show:詳細
- new:新規作成画面への遷移
- create:新規データの保存
- edit:編集画面への遷移
- update:編集内容の更新
- destroy:削除
全機能を使わない場合は、onlyオプションで使うものだけを指定する。
config/routes.rb# resourcesの後ろは、モデル名の複数形 resources :birds, only: :indexapp/controllers/birds_controller.rb... def index # インスタンス変数にBirdモデルのデータを格納します。 @birds = Bird.all end ...index.html.erbファイル作成
ビュー内でインスタンス変数を、eachメソッドを使って全て表示します。app/views/birds/index.html.erb<% @birds.each do |bird| %> <%= bird.name %> <div style= "background-image: url(<%= bird.pic %>); background-position: center center; background-size: cover; width: 300px; height: 300px; margin-bottom: 10px; "> </div> <%end%>新規投稿機能
流れは同じく、routes → controller → viewなのですが、投稿画面への移動(new)からのデータ追加(create)なので、一連の流れを二回やります!
routes.rbRails.application.routes.draw do # resourcesの後ろは、モデル名の複数形 # アクションが複数あるときは、配列の形にしてあげます resources :birds, only: [:index, :new] endindex.html.erb<%=link_to '新規投稿', new_bird_path, method: :get%> ...birds_controller.rb... def new # Birdモデルのインスタンス化したものを、インスタンス変数に格納します。 @bird = Bird.new end ...new.html.erb作成
app/views/birds/new.html.erb<%# 遷移先urlを記載しなくても、インスタンス変数に入れたモデルから判断して、遷移してくれます %> <%=form_with(model: @bird, local: true) do |form| %> <%= form.text_field :name, placeholder: "鳥の名前" %> <%= form.text_field :pic, placeholder: "鳥の写真のURL" %> <%= form.submit "投稿" %> <%end%>http://localhost:3000/birds/new
にアクセスすると投稿画面ができてます!
次は、入力情報を登録する機能です。
routes.rbRails.application.routes.draw do # resourcesの後ろは、モデル名の複数形 # アクションが複数あるときは、配列の形にしてあげます resources :birds, only: [:index, :new, :create] endbirds_controller.rb
formからのハッシュの形でデータが送られてきます。
全てのデータを受け取るのは危険です!
名前と写真のURLだけでいいのに、ログイン情報のキーを悪意で追加されて、他人のログイン情報を勝手に変更し乗っ取るということができてしまいます。
そこで、ストロングパラメータを使います。
また、privateを記述した行より下は、他のファイルから呼び出せないメソッドとなります。メソッドが増えたときに、見るべきファイルを減らせるメリットがあります。birds_controller.rb... def create # private下に定義したbird_paramで指定したパラメータを受け取り、保存する。 Bird.create(bird_param) end private def bird_param # params.require(:モデル名).permit(:カラム名,:カラム名,......) params.require(:bird).permit(:name, :pic) end ...create.html.erb作成
app/views/birds/create.html.erb<h3>投稿完了!</h3> <a href="/birds">一覧へ</a>今のままだと、何も入力しないで登録ができてしまいます。そこでバリデーションチェックするための記述をします。
app/models/birds.rbclass Bird < ApplicationRecord # 入力必須にしたいカラム名を書きます。 validates :name, presence: true validates :pic, presence: true endこれでからのデータを登録できなくなりました!!
削除機能
routes.rb... resources :birds, only: [:index, :new, :create, :destroy] ...削除機能へのリンクの調べかたは、まずコマンドで
rails routesとします。
すると、以下の出力がされます。
Prefixの値に「_path」をつけると、URIに記載されているURLが出力されます。
今回は削除なので、VerbにDELETEと描かれている行に注目します。Prefixは「bird」となっているので、「bird_path」とすればいいですね。メソッドは、「Verb」に「DELETE」とあるので「delete」とすれば良いですね。あとは、idも渡します!
index.html.erb... <%= bird.name %><%=link_to "削除", bird_path(bird.id), method: :delete%> ...bird_controller.rb... def destroy # 今回は抽出したデータをどこにも送らないので、@をつけません。 bird = Bird.find(params[:id]) bird.destroy end ...destroy.html.erb<a href="/birds">一覧画面</a>
- 投稿日:2020-06-27T17:27:16+09:00
【Rails】form_withを使ったフォロー機能の実装手順を解説します
概要
form_withを使ってフォロー機能を実装する手順を書いていきます!
以下の機能を実装していきます
1.ユーザーをフォロー/フォロー解除できる機能
2.フォローユーザー/フォロワーの一覧表示機能Railsでフォロー機能を作る方法の記事の方を参考にしており、特にフォロー機能に関するアソシエーションの詳しい解説などはこの記事が大変参考になりました!
前提
環境
・Rails 5.2.4.2
・Ruby 2.6.5
・Slim
・devise加えて、User系機能が実装されていることが前提となります。
上記の環境でのRailsアプリケーションのセットアップ方法
・Railsアプリケーションをセットアップ後にdeviseとSlimを導入する手順↓実装後はこんな感じになります。(見栄えが簡素ですみません…)↓
フォロー/フォロー解除機能の実装
1.Relationshipモデル作成
$ rails g model Relationship user:references follow:references・ここで作成される
relationshipsテーブル
が、フォローするユーザー&フォローされるユーザーにとっての中間テーブルとなる。・
user
をフォローする側のユーザーモデル、follow
をフォローされる側のユーザーモデルとして、中間テーブルには認識させます。
2.relationshipsマイグレーションファイルの編集
db/migrate/[timestamps]_create_relationships.rbclass CreateRelationships < ActiveRecord::Migration[5.2] def change create_table :relationships do |t| t.references :user, foreign_key: true t.references :follow, foreign_key: { to_table: :users } t.timestamps t.index [:user_id, :follow_id], unique: true end end end・userとfollowでそれぞれ参照先のモデルを分けているのは、
フォローする側のユーザ
とフォローされる側のユーザー
とでは別々のモデルとして考える必要があるからです。・
t.references :user, foreign_key: true
:userモデルに対する外部キーを張っています。・
t.references :follow, foreign_key: { to_table: :users }
:followモデルに対する外部キーを張っています。followモデルというのは実在しない架空のモデル名で、今この時勝手に作成してます。よって、{ to_table: :users }とする事で、参照元のモデルを教えてあげています。・
t.index [:user_id, :follow_id], unique: true
:user_idとfollow_idの組み合わせに一意性を持たせることでデータの重複を防ぎます、つまり同じユーザーを2度フォローされる事を防いています。
3.DBの内容を保存
$ rails db:migrate
4.RelationshipモデルにUserに対するアソシエーションを記述する。
app/models/relationship.rbclass Relationship < ApplicationRecord belongs_to :user belongs_to :follow, class_name: "User" validates :user_id, presence: true validates :follow_id, presence: true end・
belongs_to :user
:これはいつも通りのアソシエーションです。・
belongs_to :follow, class_name: "User"
:followクラスに所有される事を記述していますが、上述したようにfollowは架空のクラスですので、class_name: "User"
によって参照元のモデルを教えてあげます。
5.UserモデルファイルにRelationshipに対するアソシエーションを記述する。
app/models/user.rbclass User < ApplicationRecord #==============あるユーザーがフォローしているユーザーとのアソシエーション================= has_many :relationships, foreign_key: "user_id", dependent: :destroy has_many :followings, through: :relationships, source: :follow #========================================================================== #==============あるユーザーをフォローしてくれてるユーザーとのアソシエーション================ has_many :passive_relationships, class_name: "Relationship", foreign_key: "follow_id", dependent: :destroy has_many :followers, through: :passive_relationships, source: :user #=========================================================================== end・上記のRelationshipモデルファイルでは、
あるユーザーがフォローしているユーザーにアクセスするためのアソシエーション
と、あるユーザーがフォローされているユーザーにアクセスするためのアソシエーション
を記述しています。
つまりどういうことか、詳しくは図で後述します。5-1.まずは、あるユーザーがフォローしているユーザーとのアソシエーションの解説です!
・
has_many :relationships, foreign_key: "user_id", dependent: :destroy
:これは、あるUserが中間テーブルrelationshipsの外部キーuser_idにアクセスする事を可能にするアソシエーション
を宣言をしています。以下のようにイメージすると分かり易いかと思います!
・
has_many :followings, through: :relationships, source: :follow
:これは、あるUserが中間テーブルrelationshipsのfollow_idを通して、followingモデルにアクセスする事を可能にするアソシエーションを宣言をしています。イメージで言うと以下のような感じです!
・上記の2行により、
User.followings
とする事でUserがフォローしているユーザー達を取得できるようになります。これが取得できるようになると、あるUserのフォローユーザーの一覧表示などが簡単に行うことができます。5-2.次は、あるユーザーをフォローしてくれているユーザーとのアソシエーションの解説です!
・
has_many :passive_relationships, class_name: "Relationship", foreign_key: "follow_id", dependent: :destroy
:これは、あるUserがpassive_relationshipsのfollow_idへアクセスする事を可能にするアソシエーションを宣言をしています。
passive_relationshipsは、この場で勝手に作った架空のモデルなので、class_name: "Relationship"として参照元のモデルを教えています。イメージは以下の通りです!
・
has_many :followers, through: :passive_relationships, source: :user
:これは、あるUserがpassive_relationshipsのuser_idを通してfollowersへアクセスする事を可能にするアソシエーションの宣言をしています。イメージは以下の通りです!
・上記の2行により、User.followersとすれば、Userをフォローしてくれているユーザー達を取得することが可能になります。これを使えば、あるユーザーをフォローしてくれているユーザーの一覧表示などが簡単に行えるようになります!
6.Userモデルファイルに、フォロー関連のインスタンスメソッドを定義する
app/models/user.rbclass User < ApplicationRecord . . . #<アソシエーション関連の記述省略> . . . def following?(other_user) self.followings.include?(other_user) end def follow(other_user) unless self == other_user self.relationships.find_or_create_by(follow_id: other_user.id) end end def unfollow(other_user) relationship = self.relationships.find_by(follow_id: other_user.id) relationship.destroy if relationship end end・
following?(other_user)
:User.followings.include?(@user)
などとすることで、Userが@userのことをフォローしてるかどうかを確かめます。フォローボタン実装の際に、フォローするボタンを表示するのか、それともフォローを解除するボタンを表示するのかを条件分岐させるために使います。・
follow(other_user)
:User.follow(@user)
とすることで、@userをフォローすることができるようにします。unless self == other_user
で、自分のことをフォローできないようにしてます。find_or_create_byを使って、@userのidレコードが存在する場合はそれを参照し、なければ作成するようにしてます。・
unfollow(other_user)
:User.unfollow(@user)
とする事で、@userのフォローを解除できるようにします。・ユーザーをフォローするメソッド、フォロー解除するメソッドの定義ができたので、次はこれらのメソッドを使って実際にDBへアクセスしてフォロー・フォロー解除する機能をrelationshipsコントローラを作成して、実装していきます!
7.relationshipsコントローラを作成する。
$ rails g controller relationshipsapp/controllers/relationships_controller.rbclass RelationshipsController < ApplicationController before_action :set_user def create following = current_user.follow(@user) if following.save flash[:success] = "ユーザーをフォローしました" redirect_to @user else flash.now[:alert] = "ユーザーのフォローに失敗しました" redirect_to @user end end def destroy following = current_user.unfollow(@user) if following.destroy flash[:success] = "ユーザーのフォローを解除しました" redirect_to @user else flash.now[:alert] = "ユーザーのフォロー解除に失敗しました" redirect_to @user end end private def set_user @user = User.find(params[:relationship][:follow_id]) end end・relationshipsコントローラでは、ユーザーをフォローするcreateアクション、フォローを解除するdestroyアクションしか機能しないので、createアクション、destroyアクションだけを記述します。
・フォロー/フォロー解除ボタンフォームからは、params[:relationship][:follow_id]と言う形でデータが送信される
ので、find(params[:relationships][:follow_id])と言う形でデータを受け取ります。・
before_action :set_user
:それぞれのアクションでは必ず@userを取得する事になるので、set_userプライベートメソッドを定義してからbefore_actionを使ってあらかじめ取得させています。・
createアクション
:ログインしているユーザー(current_user)が、取得した@userをフォローしています。Userモデルファイルで定義したfollow(other_user)インスタンスメソッドを使用しています。・
destroyアクション
:ログインしているユーザーが、取得したユーザーのフォローを解除しています。Userモデルファイルで定義したunfollow(other_user)インスタンスメソッドを使用しています。・実際にDBへアクセスしてフォロー・フォロー解除する機能が仕上がったので、次は実際に
フォロー・フォロー解除するためのボタン
の実装を行います!
8.フォロー/フォロー解除ボタンをform_withで実装する。
・始めに、app/viewsディレクトリ配下に、relationships/_follow_form.html.slimファイルを作成してください。
app/views/relationships/_follow_form.html.slim- unless current_user == user #フォロー解除ボタン - if current_user.following?(user) = form_with model: @relationship, url: relationship_path, method: :delete, local: true do |f| = f.hidden_field :follow_id, value: user.id = f.submit "フォローを解除する" #フォローボタン - else = form_with model: @set_relationship, url: relationships_path, local: true do |f| = f.hidden_field :follow_id, value: user.id = f.submit "フォロー"・
- unless current_user == user
:自分自身のことをフォローできないよう、ボタン自体の表示を切り替えています。ここでのuserとは、このパーシャルを配置するビューが取得する@userのことを指します。8-1.フォロー解除ボタンの解説
・
- if current_user.following?(user)
:ログインユーザーが相手ユーザーをフォローしているかどうかを確かめています。フォローしていればフォロー解除ボタンを表示し、フォローしていなければフォローボタンを表示するようにしています。・
= form_with model: @relationship, url: relationship_path, method: :delete, local: true do |f|
:こちらはフォロー解除用のボタンです。
model: @relationship
の@relationshipはインスタンス変数で、ログインユーザーと相手ユーザーとのRelationshipを取得しています。
(app/views/users/show.html.slimにこのフォローボタンのフォームを配置するなら、app/controllers/users_controller.rbファイルのshowアクションに、@relationshipインスタンス変数を定義する事になります。)
url: relationship_path
は、relationshipsコントローラのdestroyアクションへのルーティングです。ルーティングの記述はあとで行います。
method: :delete
では、HTTPリクエストでDELETEメソッドを指定しています。
local: true
は、これを付けないとフォームの送信の仕様がAjaxになり、データを送信できなくなるのでform_withでは必ず記述します。・
f.hidden_field :follow_id, value: user.id
:この部分は、実際に完成形のソースコードを見てみるとわかりやすいです。__
:follow_id
を指定することで、params[:relationship][:follow_id]と言う形でデータが送信されます。relationshipsコントローラのset_userプライベートメソッドで定義した部分では、このデータを受け取っています。
value: user.id
を指定することで、:follow_idにフォローしている相手ユーザーのidが格納されて、データが送信されます。8-2.フォローボタン
・
= form_with model: @set_relationship, url: relationships_path, local: true do |f|
:相手ユーザーをフォローするためのフォームです。
model: @set_relationship
の@set_relationshipインスタンス変数では空のモデルを生成しており、この空のモデルを参照して、params[:relationship][:follow_id]と言う形でデータが送信されるようにします。
url: relationships_path
は、relationshipsコントローラのcreateアクションへのルーティングです。ルーティングの記述は後で行います。・
= f.hidden_field :follow_id, value: user.id
:これも実際の完成後のソースコードを確認してみます。フォロー解除ボタンと同じで、params[:relationship][:follow_id]と言う形でデータが送信されてます。
・フォロー/フォロー解除ボタンの作成が完了しましたので、次はこのデータが正常にアクションへ送られるようルーティングを設定します!
9.relationshipsコントローラへのルーティングを追加する。
config/routes.rbRails.application.routes.draw do . . #<他のルーティングは省略> . . resources :relationships, only: [:create, :destroy]・
$ rails routes | grep relationship
コマンドで、追加されたルーティングを確認してみます。$ rails routes | grep relationship relationships POST /relationships(.:format) relationships#create relationship DELETE /relationships/:id(.:format) relationships#destroy・これで
relationships_path
でフォロー/relationship_path
でフォロー解除できるようになってるのが確認できたかと思います。
10.フォロー/フォロー解除ボタンを配置する。
・相手ユーザーのページに飛んだ際にフォローしたり、フォロー解除できるようにする場合を考えて、app/views/users/show.html.slimファイルに配置していきたいと思います。
app/views/users/show.html.slimh1 ユーザープロフィールページ p = "名前 : " = @user.username #================フォローボタンの追加============================= = render 'relationships/follow_form', user: @user #============================================================ p= link_to "プロフィール編集", edit_user_path(current_user)・user: @user__:パーシャルに記述したuserが、showアクションで定義されている@userを参照しています。__
・ボタンの配置はできましたが、このままではボタンは機能しません。先ほども述べたように、@relationshipインスタンス変数と、@set_relationshipインスタンス変数を、showアクションに定義してあげなければならないからです。
11.app/controllers/users_controller.rbのshowアクションに、インスタンス変数を定義する。
app/controllers/users_controller.rbclass UsersController < ApplicationController . . . def show @user = User.find(params[:id]) @relationship = current_user.relationships.find_by(follow_id: @user.id) @set_relationship = current_user.relationships.new end . . . end・
@relationship = current_user.relationships.find_by(follow_id: @user.id)
:フォロー解除フォームで参照しているインスタンス変数です。ここでcurrent_userと@userとのrelationshipsを取得することで、フォームにてparams[:relationship][:follow_id]のようにデータを送信することができるようになります
。・
@set_relationship = current_user.relationships.new
:フォローするフォームで参照しているインスタンス変数です。params[:relationship][:follow_id]と言う形にしてデータを送信したいので、このように空のインスタンス変数を生成してます。・以上で、ユーザーをフォロー/フォロー解除する機能の実装が完了しました!
次は、フォローしているユーザー/フォローしてくれているユーザーを一覧で表示する機能を実装していきます!
フォローユーザー/フォロワーの一覧表示機能
1.始めにフォローユーザー/フォロワーの一覧ページへのリンクをビューに作成する
・app/views/users/show.html.slimに、そのユーザーのフォローユーザー/フォロワーの一覧ページへのリンクを追加します。
app/views/users/show.html.slimh1 ユーザープロフィールページ p = "名前 : " = @user.username #==============フォロー/フォロワー一覧ページへのリンク================= = link_to "フォロー: #{@user.followings.count}", followings_user_path(@user.id) = "/" = link_to "フォロワー: #{@user.followers.count}", followers_user_path(@user.id) #============================================================ = render 'relationships/follow_form', user: @user p= link_to "プロフィール編集", edit_user_path(current_user)・フォローユーザー/フォロワーへのアクセスは、先ほどUserモデルファイルで定義したfollowings/followersを使います。
・
@user.followings.count
:ユーザーのフォローしているユーザーの数を表示してます。
・followings_user_path(@user.id)
:URLがusers/:id/followings
となるパスを指定したいので、左記のように記述します。この形のパスを指定できるように、ルーティングの記述を後で行ってあげます。・フォロワー一覧ページへのアクセスも、フォローユーザー一覧ページへアクセスするのと同じような方法で記述しています。
2.フォロー/フォロワー一覧ページへのルーティングを追加する。
config/routes.rbRails.application.routes.draw do . . . resources :users do get :followings, on: :member get :followers, on: :member end . . . end・上記のように記述したら
$ rails routes | grep follow
コマンドでルーティングを確認してみます。$ rails routes | grep follow followings_user GET /users/:id/followings(.:format) users#followings followers_user GET /users/:id/followers(.:format) users#followers・
followings_user_path
はフォローユーザー一覧のページへ、followers_user_path
はフォロワー一覧ページへ飛ぶようになったので、これでOKです。・followingsアクションも、followersアクションもusersリソースにネストさせたので、usersコントローラで2つのアクションを定義します。
3.usersコントローラにfollowingsアクション、followersアクションを追加する。
app/controllers/users_controller.rbclass UsersController < ApplicationController . . . def followings @user = User.find(params[:id]) @users = @user.followings.all end def followers @user = User.find(params[:id]) @users = @user.followers.all end . . .・followingsアクション内では、@userのフォローしているユーザー全てを、followersアクション内では、@userのフォロワー全てを取得しています。
4.フォローユーザー/フォロワー一覧を表示するビューを作成する。
・app/views/usersディレクトリに、
followings.html.slim
ファイルとfollowers.html.slim
ファイルを作成します。app/views/users/followings.html.slimh2 フォローしている方々 - @users.each do |user| hr = "username: " = link_to user.username, user_path(user.id)app/views/users/followers.html.slimh2 フォロワーの皆さん - @users.each do |user| hr = "username: " = link_to user.username, user_path(user.id)・以上で、フォローユーザー/フォローユーザー一覧ページの作成が完了しました!
参考にさせて頂いた記事
- 投稿日:2020-06-27T16:26:49+09:00
[Rails] ヘルパーメソッドform_withについて[基礎]
学習中に理解が浅かったところなのでアウトプットしながら整理したいと思います。
基礎から丁寧に書いていきます。もし指摘あればお願いいたします。ヘルパーメソッドとは
そもそもrailsでは、viewでHTMLタグを出現させたりテキストを加工するためのメソッドが予め用意されています。これらをヘルパーメソッドといいます。
form_withはヘルパーメソッドの一種です。*ヘルパーメソッドの一例
ヘルパーメソッド 使用用途
form_tag 投稿ページなどにおけるフォームの実装
link_to リンクの実装
simple_format 投稿した文章を自動で見やすく整形する
submit_tag submit機能をつけたいときに使う。
text_field_tag
などなど、、
自作することも可能らしいです。
【参考記事】
https://qiita.com/Yukaaaah/items/19e524fd0c0e4a3451f1form_withについて
Rails 5.1というバージョンから推奨されているフォーム実装のためのヘルパーメソッドです。
form_tag/form_forは非推奨となっているらしいです。(この2つのメソッドについては説明を省略します。)<!-- form_tagを使用した例 --> <%= form_tag('/posts', method: :post) do %> <input type="text" name="content"> <input type="submit" value="投稿する"> <% end %><!--form_withを使用した例--> <%= form_with model: @post, local: true do |form| %> <%= form.text_field :content %> <%= form.submit '投稿する' %> <% end %>form_withの特徴としては、
①自動でパスを選択してくれて、HTTPメソッドを指定する必要が無いこと
②コントローラーから渡された、ActiveRecordを継承するモデルのインスタンスが利用できること(上記では@postがそれに該当)またこの場合は、
①新規投稿の場合
②既存の投稿を呼び出した場合
で処理が変わってきます。①新規投稿の場合
posts_controller.rbdef new @post = Post.new end投稿ボタンをクリックしたらcreateアクションに送られます。
new.html.erb<%= form_with model: @post, class: :form, local: true do |form| %> <%= form.text_field :title, placeholder: :タイトル, class: :form__title %> <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %> <%= form.submit '投稿する', class: :form__btn %> <% end %>②既存の投稿を呼び出した場合
posts_controller.rbdef edit @post = Post.find(params[:id]) endedit.html.erb<%= form_with model: @post, class: :form, local: true do |form| %> <%= form.text_field :title, placeholder: :タイトル, class: :form__title %> <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %> <%= form.submit '投稿する', class: :form__btn %> <% end %>投稿ボタンをクリックしたらupdateアクションに送られます。
まとめ
比較してみるとnew.html.erbとedit.html.erbのフォーム部分は同じです!!!
そのためフォーム部分を部分テンプレート化できて記述量を減らせます。
モデルの@postの中身があるかどうかで自動で判断して送ってくれるのです。このほかにも
例えばform_with model: [@post, @comment]のように複数モデルを渡すことができます。
それは別記事で書ければと思います、、
- 投稿日:2020-06-27T15:59:59+09:00
Railsのdevise導入でdefault_url_optionsの設定でハマった
deviseのユーザー認証設定
デフォルトの場合以下のように
config.action_mailer.default_url_options
で{ host: 'localhost', port: 3000 }
と設定されると多います。development.rb# default url config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # mail setting config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :user_name => Rails.application.credentials.gmail[:user_name], :password => Rails.application.credentials.gmail[:password], :authentication => :plain, :enable_starttls_auto => true }その場合認証メールで送られてくるURLは
http://localhost:3000/users/confirmation?confirmation_token=-XiHyA_1xCxhk846ae9G
みたいな形になる思います。Dockerでrootディレクトリが3000ポートを付けない形で構築したのでポート番号がない形でURLを作成しなければいけないので以下のように設定を変更しました。
development.rbconfig.action_mailer.default_url_options = { host: 'localhost' }URLが変更されない
本来であれば
http://localhost/users/confirmation?confirmation_token=-XiHyA_1xCxhk846ae9G
のような形でURLが生成されるはずがされず数時間ハマりました。結論
Dockerの再起動で反映された。
Web開発をしていると常にハマるポイントがありますが、再起動で動くことが多々あるので「ハマったら再起動してみる」ということをもっと心がけて行きたいです。
- 投稿日:2020-06-27T15:06:57+09:00
調べる力 各方法まとめ プロジェクト内とGemで定義されている場合
動機
新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じまとめてみました。
調べ方も一長一短あります。プロジェクト内で定義されているメソッドの場合
1.
git grep -n <メソッド名>
- エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
- -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。
2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
- エディタを使いたくない時にいいです
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる
3. pry-docsのshow-source機能を使う
- Railsコンソールを開き、
$ <メソッド名>
ORshow-source <メソッド名>
- 定義箇所のdefからendまでを表示してくれます!
- パスも出るので、コマンド + クリックでエディタで表示できます。
- デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。
4. Pryの"step"機能を使う
- binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
- 処理の流れと変数の定義も見ることができ、深い理解ができます。
- Gemなど深いところも見れます。
- デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。
Gemなどプロジェクト外で定義されている場合
1. Dashで検索する
- Dashとは、素のRuby、Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
- ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
- VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
- デメリット:たまに調べたいGemがなかったりする
2.
bundle show <gem名>
- Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
- デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。
3. Pryの"step"機能を使う
- プロジェクト内で定義されているメソッドの場合と同じです。
4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- プロジェクト内で定義されているメソッドの場合と同じです。
その他
1. Rubymineの定義元ジャンプを使う
- VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
- デメリット:高い、遅い
2. VSCodeの拡張機能 Solargraphを使う
- Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
- 補完機能もあり
- デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法
最後に
- 皆様の「調べ方」が知りたいです。
- 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
- 投稿日:2020-06-27T15:06:57+09:00
調べる力 各方法まとめ プロジェクト内とRails/Gem内で定義されている場合
動機
新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じ、先輩エンジニアにも聞きながらまとめてみました。
調べ方も一長一短あります。プロジェクト内で定義されているメソッドの場合
1.
git grep -n <メソッド名>
- エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
- -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。
2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
- エディタを使いたくない時にいいです
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる
3. pry-docsのshow-source機能を使う
- Railsコンソールで、
$ <メソッド名>
ORshow-source <メソッド名>
- 定義箇所のdefからendまでを表示してくれます!
- パスも出るので、コマンド + クリックでエディタで表示できます。
- デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。
4. Pryの"step"機能を使う
- binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
- 処理の流れと変数の定義も見ることができ、深い理解ができます。
- Rails/Gemなど深いところも見れます。
- デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。
Gemなどプロジェクト外で定義されている場合
1. Dashで検索する
- Dashとは、素のRuby、Rails, Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
- ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
- VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
- デメリット:たまに調べたいGemがなかったりする
2.
bundle show <gem名>
- Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
- デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。
3. Pryの"step"機能を使う
- プロジェクト内で定義されているメソッドの場合と同じです。
4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- プロジェクト内で定義されているメソッドの場合と同じです。
その他
1. Rubymineの定義元ジャンプを使う
- VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
- デメリット:高い、遅い
2. VSCodeの拡張機能 Solargraphを使う
- Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
- 補完機能もあり
- デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法
最後に
- 皆様の「調べ方」が知りたいです。
- 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
- 投稿日:2020-06-27T15:06:57+09:00
メソッドの定義場所 調べる方法まとめ プロジェクト内とRails/Gem内で定義されている場合
動機
新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じ、先輩エンジニアにも聞きながらまとめてみました。
調べ方も一長一短あります。プロジェクト内で定義されているメソッドの場合
1.
git grep -n <メソッド名>
- エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
- -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。
2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
- エディタを使いたくない時にいいです
- デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる
3. pry-docsのshow-source機能を使う
- Railsコンソールで、
$ <メソッド名>
ORshow-source <メソッド名>
- 定義箇所のdefからendまでを表示してくれます!
- パスも出るので、コマンド + クリックでエディタで表示できます。
- デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。
4. Pryの"step"機能を使う
- binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
- 処理の流れと変数の定義も見ることができ、深い理解ができます。
- Rails/Gemなど深いところも見れます。
- デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。
Gemなどプロジェクト外で定義されている場合
1. Dashで検索する
- Dashとは、素のRuby、Rails, Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
- ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
- VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
- デメリット:たまに調べたいGemがなかったりする
2.
bundle show <gem名>
- Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
- デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。
3. Pryの"step"機能を使う
- プロジェクト内で定義されているメソッドの場合と同じです。
4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索
- プロジェクト内で定義されているメソッドの場合と同じです。
その他
1. Rubymineの定義元ジャンプを使う
- VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
- デメリット:高い、遅い
2. VSCodeの拡張機能 Solargraphを使う
- Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
- 補完機能もあり
- デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法
最後に
- 皆様の「調べ方」が知りたいです。
- 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
- 投稿日:2020-06-27T14:51:55+09:00
検索機能の実装 学習メモ(ポートフォリオ 作成編)
はじめに
ポートフォリを作成にあたり、検索機能を実装しましたのでそのメモとして記事を投稿します
検索の入力欄とボタンを作成
app/views/tweet/index
= form_with(url: search_tweets_path, local: true, method: :get, class: "search-form") do |form| = form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" = form.submit "検索", class: "search-btn"searchアクションのルーティングを設定
routes
resources :tweets do resources :comments, only: :create collection do #追加 get 'search' #追加 end #追加 〜省略〜 endポイント
railsでは、7つのアクション以外でルーティングを設定する方法にcollectionとmemberがあります
- - collection ルーティングに:idがつかない member ルーティングに:idがつく 今回は、を使いいます。
定義しましたら、rails routesをして実際にお確かめてみてください。検索するメソッドをTweetモデルに定義
モデルにテーブルから検索するためのメソッドsearchを定義します。
MVCの観点で検索するなどのロジックは、メソッドとしてモデルにまとめて定義するようにします。
searchメソッドをTweetモデルに定義します。
検索したキーワードが含まれている投稿を取得するために、whereメソッドとLIKE句を利用します。モデルを編集
app/models/tweet.rbに以下を追加。
def self.search(search) if search Tweet.where('text LIKE(?)', "%#{search}%") else Tweet.all end endポイント
whereメソッドについて
ActiveRecordメソッドの1つです。モデル.where(条件)のように引数部分に条件を指定することで、テーブル内の条件に一致したレコードのインスタンスを配列の形で取得できます。
引数の条件には、「検索対象となるカラム」を必ず含めて、条件式を記述します。
モデル.where('検索対象となるカラムを含む条件式')LIKE句について
LIKE句は、曖昧(あいまい)な文字列の検索をすることができるもので、whereメソッドと一緒に使います。
例えば、1文字目に'a'という文字列が入ったデータや最後の文字に'b'が入っているデータ、文字列の途中に'c'が入ったデータなどを検索したい時に、曖昧文字列というものを使って検索することです。
実行例 実行例 where('title LIKE(?)', "a%") aから始まるタイトル where('title LIKE(?)', "%b") bで終わるタイトル where('title LIKE(?)', "%c%") cが含まれるタイトル where('title LIKE(?)', "d_") dで始まる2文字のタイトル where('title LIKE(?)', "_e") eで終わる2文字のタイトル searchアクションをコントローラーに定義
モデルに書いたsearchメソッドを呼び出します。seachメソッドの引数にparams[:keyword]と記述して、検索結果を渡しています。
また、未ログイン状態にトップページへリダイレクトされてしまうことを回避するため、before_actionのexceptオプションに:searchを追加しています。
app/controllers/tweets_controller.rb
before_action :move_to_index, except: [:index, :show, :search] def search @tweets = Tweet.search(params[:keyword]) end 〜省略〜 private def move_to_index redirect_to action: :index unless user_signed_in? end最後にhtmlを作成
記述は以下です
app/views/tweets/search.html.erb
= form_with(url: search_tweets_path, local: true, method: :get, class: "search-form") do |form| = form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" = form.submit "検索", class: "search-btn" .contents.row - @tweets.each do |tweet| = render partial: "tweet", locals: { title: tweet }tweet部分テンプレートの記述は自分の場合以下の様になています。
まだ未完成ですが、とりあえずこちらで問題なく動きました。
app/views/tweets/_tweet.html.erb.users_tweets - @tweets.each do |tweet| .users_tweets__lists .users_tweets__lists_list .users_tweets__lists_list__tweet_image = link_to tweet_path(tweet) do = attachment_image_tag tweet, :image = tweet.title .users_tweets__lists_list__user_image = link_to user_path(tweet.user.id) do = attachment_image_tag tweet.user, :profile_image, fallback: "no-image.png" = link_to tweet.user.username,user_path(tweet.user.id) .users_tweets__lists_list__user_name = tweet.updated_at.strftime("%Y-%m-%d %H:%M") 更新日 - if current_user.already_liked?(tweet) = link_to tweet_likes_path(tweet), method: :delete do %i.fas.fa-heart - else = link_to tweet_likes_path(tweet), method: :post do %i.far.fa-heart = tweet.likes.countこれで実装できると思います!
もしエラーが発生した場合などは、コメントください。
ありがとうございました!
- 投稿日:2020-06-27T12:36:03+09:00
RailsとDockerでよく使うコマンドまとめ
docker-compose build #コンテナの作成 docker-compose up #コンテナ立ち上げ docker-compose down #コンテナをダウン ctrl+cでも止められるがserver.pidでエラーになる docker-compose stop #コンテナをストップ docker-compose up --build #docker関連のファイルの変更を反映した上に、サーバーを再起動 docker images #イメージ一覧 docker ps #動いているコンテナ一覧 docker ps -a #動いていないコンテナも含めた一覧 docker rm `docker ps -a -q` #コンテナの一括削除 docker rmi `docker images -a -q` #イメージの一括削除 mysql -u root -p -h localhost -P 3306 --protocol=tcp #DockerからMYSQLへ docker-compose run web bundle install #bundle install docker-compose run web rails db:create #db作成 docker-compose run web rails db:migrate #マイグレート以上、RailsとDockerでよく使うコマンドたちでした!
参考になれば幸いです。
間違えなどありましたらご指摘お願いします。
- 投稿日:2020-06-27T10:45:18+09:00
【Rails】ブラウザのページタイトルをページごとに変更する方法
目標
開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina前提
下記実装済み。
実装
1.
application_helper.rb
を編集application_helper.rbmodule ApplicationHelper # 追記 def full_title(page_title = '') base_title = "Bookers" if page_title.empty? base_title else "#{ page_title } | #{ base_title }" end end end【解説】
◎ ベースとなるタイトル(アプリ名等)を設定し、変数へ代入する。
base_title = "Bookers"◎ 引数で受け取った各ページのタイトルが空の場合はベースタイトルのみを表示する。
if page_title.empty? base_title◎ 引数で受け取った各ページのタイトルが存在する場合は両タイトルを表示する。
else "#{ page_title } | #{ base_title }"
page_title
が「本一覧」だとすると、本一覧 | Bookers
と表示される。2.ビューを編集
①
application.html.slim
を編集する。application.html.slim/ 変更前 title | Bookers / 変更後 title = full_title(yield(:title))② 各ページのビューを編集する。
例として、
books/index.html.slim
のタイトルを設定します。books/index.html.slim/ 追記 = provide(:title, '本一覧')
- 投稿日:2020-06-27T10:08:08+09:00
【Rails】エラーメッセージを個別に表示する方法
目標
開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina前提
下記実装済み。
実装
1.
application.rb
を編集application.rbmodule Bookers2Debug class Application < Rails::Application config.load_defaults 5.2 # 追記 config.action_view.field_error_proc = Proc.new do |html_tag, instance| if instance.kind_of?(ActionView::Helpers::Tags::Label) html_tag.html_safe else class_name = instance.object.class.name.underscore method_name = instance.instance_variable_get(:@method_name) "<div class=\"has-error\">#{html_tag} <span class=\"help-block\"> #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")} #{instance.error_message.first} </span> </div>".html_safe end end end end【解説】
① エラーが出ていない場合はHTMLをそのまま表示する。
if instance.kind_of?(ActionView::Helpers::Tags::Label) html_tag.html_safe② エラーが出ている場合はエラーメッセージをフォームの下に表示する。
else class_name = instance.object.class.name.underscore method_name = instance.instance_variable_get(:@method_name) "<div class=\"has-error\">#{html_tag} <span class=\"help-block\"> #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")} #{instance.error_message.first} </span> </div>".html_safe◎ インスタンスのクラス名を変数へ代入
class_name = instance.object.class.name.underscore◎ インスタンスのメソッド名を変数へ代入
method_name = instance.instance_variable_get(:@method_name)◎ エラーメッセージ部分のHTMLを作成する。
"<div class=\"has-error\">#{html_tag} <span class=\"help-block\"> #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")} #{instance.error_message.first} </span> </div>".html_safe「タイトルを入力してください」と表示する場合、
#{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}
が、
「タイトル」にあたる部分で、
#{instance.error_message.first}
が、
「を入力してください」にあたる部分になる。2.エラーメッセージを日本語化
① Gemを導入
Gemfile# 追記 gem 'rails-i18n'ターミナル$ bundle②
application.rb
を編集application.rbmodule Bookers2Debug class Application < Rails::Application config.load_defaults 5.2 config.i18n.default_locale = :ja # 追記 config.action_view.field_error_proc = Proc.new do |html_tag, instance| if instance.kind_of?(ActionView::Helpers::Tags::Label) html_tag.html_safe else class_name = instance.object.class.name.underscore method_name = instance.instance_variable_get(:@method_name) "<div class=\"has-error\">#{html_tag} <span class=\"help-block\"> #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")} #{instance.error_message.first} </span> </div>".html_safe end end end end③
ja.yml
を作成し、編集ターミナル$ touch config/locales/ja.ymlja.ymlja: activerecord: attributes: book: title: タイトル body: 本文
- 投稿日:2020-06-27T09:03:19+09:00
Railsチュートリアル 第1章 ゼロからデプロイまで【やってみた】
Railsチュートリアル第1章をやってみて終えたので、やったことや失敗したことを振り返ります。細かく見ていくので、チュートリアルしながらみてもらえれば嬉しいです。
※こちらの記事ではRailsチュートリアル第4版を使用しています。
現在はRailsチュートリアル第6版が出ています。
下記リリース時のnote
https://note.com/yasslab/n/n1543187c3ed6?magazine_key=md778735d3f77ちなみに細かなことですが、読む字体を明朝体かゴシック体に変更することができます。自分はゴシック体にするととても読みやすくなったので、お好みに合わせて変更すると良いかと思います。
また2章で新しいアプリを作るので、この章でデプロイできなくても大丈夫です。
1.1.1 前提知識
RailsチュートリアルでProgeteの関連講座を紹介してますが、
これは本当にやった方がいいと感じます。
第1章でコマンドラインとGitコースが紹介されていますが、
「後ででもいけるだろ」と思って結局コマンドのミスで、アプリが作り直しになったりと、大変なことになりました!
始める前にこちらのコースをすることをおすすめします。1.2.1 開発環境
クラウドIDEである、Cloud9をインストールする
1.2.2 Railsをインストールする
$ printf "install: --no-document \nupdate: --no-document\n" >> ~/.gemrc $ gem install rails -v 5.1.6 # -vでバージョンを指定 $ cd ~/environment # 'environment' ディレクトリに移動する $ rails _5.1.6_ new hello_app1.3.1 Bundler
Gemfile内にあるデフォルトの内容をバージョンを指定して記述、インストールします。
$ cd hello_app/ $ bundle install上手くいかない時は
bundle update
を実行1.3.2 rails sever
$ cd ~/environment/hello_app/ #hello_appディレクトリへ移動 $ rails server #サーバーを起動rails serverを起動した後は、このターミナル内はリアルタイムで更新されていくので、注意すること。
画面をプレビューするとRailsのぺージが!!Yay!1.3.3 Model-View-Controller
モデル・ビュー・コントローラーがそれぞれ関わっているというお話。
model: アプリケーションデータ、ビジネスルール、ロジック、関数
view: グラフや図などの任意の情報表現
controller: 入力を受け取りmodelとviewへの命令に変換する
--Wikipediaより引用ここで一つの疑問が。
RailsはこのMVCアーキテクチャパターンを採用しているということ。「このパターンを採用している」ということは他のパターンもあるということ...?調べてみました。・フォームとコントロール
・プレゼンテーション・モデル
・アプリケーション・モデル
・MVVM
・モデル・ビュー・プレゼンタ(MVP)参考記事 https://www.atmarkit.co.jp/fdotnet/chushin/greatblogentry_10/greatblogentry_10_01.html
内容は上手く理解できなかったけど、MVC以外ももちろんあるよ!ということが分かりました。
構造からもう自由なんだなと思うとプログラミングはすごい
この詳しい内容もまた勉強していきます。1.3.4 Hello,wold!
ビューも通さずHello,world!を表示させるため、以下のようなコードを記述しています。
titleapp/controllers/application_controller.rbdef hello render html: "hello, world!" endrenderメソッドは
render("フォルダ名"/"ファイル名")
のように使うと思っていましたが、こんな使い方もできるんですねRailsガイド 2.2 renderを使用する
Railsガイドによると最早何でも出力できます、みたいな勢い。ルーティングを設定してブラウザで表示させます。
1.4.1インストールとセットアップ
ここからGitでのバージョン管理ができるようにしていきます。
$ git config --global user.name "Your Name" $ git config --global user.email your.email@example.comここでの名前とメールアドレスは今後一般に公開されるので注意。
リポジトリを作成するため、以下の内容をターミナル内で記述する
$ git init #Gitの初期化 $ git add -A #現在のディレクトリにあるすべてのファイルを追加 #ファイル追加後ステージングされ、コミット待ちになる $ git status #現在のステージングの確認 $ git commit -m "Initialize repository" #リポジトリにコメント付きで反映させる $ git log #コミットメッセージの履歴を見るいきなりリポジトリとは何だろう?という感じでしたが、こちらを読んだらとても分かりやすかったです。
【イラストで覚える】初心者のためのGitとGitHub用語集1.4.3 Gitbucket
Gitbucketにソースコードをアップデートします。
他の開発者と共同作業できるのが大きなメリットです。手順の中の、「公開鍵・暗号鍵」ってなんだろう?
とりあえず言われるがまま作ってみたけどよくわかっていないので、調べてみました。インターネットを経由してデータのやりとりをする上で、情報が盗まれないようにするために暗号化が必要です。
そのために生まれたのが公開鍵暗号方式です。
この鍵はお互いが対応しています。
・公開鍵で暗号化したデータは秘密鍵(暗号鍵)でしかもとに戻せない
・秘密鍵(暗号鍵)で暗号化したデータは公開鍵でしかもとに戻せない公開鍵は誰でも見れるようになっています。
データを送信する側は公開鍵で情報を取得、暗号化して送信します。
受信側は誰にも見せていない秘密鍵(暗号鍵)で暗号化された情報を元に戻し、見ることができます。
これで第三者に情報を見られることはありません。このような方式をとっているため、情報のやりとりのために公開鍵を入れることが必要です。
参考記事
https://railstutorial.jp/chapters/beginning?version=5.1#sec-bundler1.4.4 ブランチ、編集、コミット、マージ
$ git checkout -b modify-README #一時的に使うトピックブランチを作成 $ git branch #すべてのローカルブランチを確認 $ git commit -a -m "Improve the README file" #すべてのファイルの変更を一括でコミットする $ git checkout master #masterブランチへ移動 $ git merge modify-README #masterブランチにトピックブランチmodify-READMEをマージ $ git push #プッシュする1.5.1 Herokuのセットアップ
HerokuではPostgreSQLデータベースを使う、とありますが、
PostgreSQLってなんだろう...PostgreSQL は、オープンソースのリレーショナル・データベースです。商用データベ>ースに匹敵する本格的な機能と、オープンソースならではの利用の柔軟度が魅力です。
https://lets.postgresql.jp/map/introリレーショナルデータベースは、テーブル型で、関連付けられたデータの集合ということのようです。まだまだ難しい
https://aws.amazon.com/jp/relational-database/SQLiteを加え、インストールします。
また、Herokuをインストールしてデプロイします。
$ heroku --version #Herokuがインストールされているかとバージョンの確認 $ source <(curl -sL https://cdn.learnenough.com/heroku_install) #HerokuをクラウドIDEにインストール $ heroku login --interactive $ heroku keys:add $ heroku create #Herokuに新しいアプリケーションを作る $ git push heroku master #Herokuにデプロイここで1章は終わりです。
ログを載せたかったのですが、デプロイが上手くいきませんでした。
後から考えると、ブランチを作り間違えたのと、Herokuはheroku create
で生成されたアドレスからアクセスする必要があるということをいまいち理解していなかったためだと思います。長くなってしまいましたが、ここまで読んでいただきありがとうございます。
間違いなどありましたら、教えていただけると嬉しいです。