- 投稿日:2019-03-27T23:58:20+09:00
起動中のrailsコンテナに入るシェルスクリプトのメソッド
起動中のrailsコンテナに入るシェルスクリプトのメソッド
zshrc.function dbash() { id=$(docker ps -q --filter "name=web") command docker exec -it $id bash }参考
- 投稿日:2019-03-27T22:42:51+09:00
Amazon LinuxでRMagick 3.0.0 がインストールできない
bundle updateの際にrmagickのインストールで詰まってしまったため、対処法のメモ。
発生した問題
Capistranoでrails製アプリケーションのデプロイ中、rmagickのインストールでエラーが発生した。
00:14 bundler:install 01 $HOME/.rbenv/bin/rbenv exec bundle install --path /var/www/taimee-rails-api/shared/bundle --jobs 4 --without development test --deployment --quiet 01 Gem::Ext::BuildError: ERROR: Failed to build gem native extension. 01 01 current directory: 01 /var/www/app/shared/bundle/ruby/2.6.0/gems/rmagick-3.0.0/ext/RMagick 01 /home/client/.rbenv/versions/2.6.1/bin/ruby -I 01 /home/client/.rbenv/versions/2.6.1/lib/ruby/2.6.0 -r 01 ./siteconf20190326-11939-1i8on2k.rb extconf.rb 01 checking for gcc... yes 01 checking for Magick-config... yes 01 checking for outdated ImageMagick version (<= 6.8.9)... *** extconf.rb failed 01 *** 01 Could not create Makefile due to some reason, probably lack of necessary 01 libraries and/or headers. Check the mkmf.log file for more details. You may 01 need configuration options. 01 01 Provided configuration options: 01 --with-opt-dir 01 --without-opt-dir 01 --with-opt-include 01 --without-opt-include=${opt-dir}/include 01 --with-opt-lib 01 --without-opt-lib=${opt-dir}/lib 01 --with-make-prog 01 --without-make-prog 01 --srcdir=. 01 --curdir 01 --ruby=/home/client/.rbenv/versions/2.6.1/bin/$(RUBY_BASE_NAME) 01 01 To see why this extension failed to compile, please check the mkmf.log which can 01 be found here: 01 01 /var/www/app/shared/bundle/ruby/2.6.0/extensions/x86_64-linux/2.6.0-static/rmagick-3.0.0/mkmf.log 01 01 extconf failed, exit code 1 01 01 Gem files will remain installed in 01 /var/www/app/shared/bundle/ruby/2.6.0/gems/rmagick-3.0.0 for 01 inspection. 01 Results logged to 01 /var/www/app/shared/bundle/ruby/2.6.0/extensions/x86_64-linux/2.6.0-static/rmagick-3.0.0/gem_make.out 01 01 An error occurred while installing rmagick (3.0.0), and Bundler cannot continue. 01 Make sure that `gem install rmagick -v '3.0.0' --source 'https://rubygems.org/'` 01 succeeds before bundling. 01 01 In Gemfile: 01 rmagickエラーログの指示通り、
/var/www/app/shared/bundle/ruby/2.6.0/extensions/x86_64-linux/2.6.0-static/rmagick-3.0.0/mkmf.logを確認$ cat /var/www/app/shared/bundle/ruby/2.6.0/extensions/x86_64-linux/2.6.0-static/rmagick-3.0.0/mkmf.log find_executable: checking for gcc... -------------------- yes -------------------- find_executable: checking for Magick-config... -------------------- yes -------------------- Detected ImageMagick version: 6.7.8 Can't install RMagick 3.0.0. You must have ImageMagick 6.8.9 or later. Can't install RMagick 3.0.0. You must have ImageMagick 6.8.9 or later.ImageMagickのバージョンが古いのが原因のようですね。バージョンを上げましょう
ImageMagickのバージョンを上げる
RMagickはImageMagick7系をサポートしていないので、6.9系をインストールします。
Remiのリポジトリをインストール
ImageMagickの6.9系のバージョンが配布されているRemiリポジトリをインストールします。
//yumリポジトリにEPELを追加 yum install epel-release //Remiのリポジトリ設定パッケージをダウンロード wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm //Remiのリポジトリのインストール rpm -Uvh remi-release-7.rpm古いパッケージを削除
$ yum remove -y libtiff $ yum remove -y graphviz $ yum remove -y libwebp libwebp-devel $ yum remove -y ImageMagick ImageMagick-devel ImageMagick-libs新しいパッケージをインストール
$ yum install -y libtiff --enablerepo=remi,epel,base $ yum install -y graphviz --enablerepo=remi,epel,base $ yum install -y ImageMagick6 ImageMagick6-devel ImageMagick6-libs --enablerepo=remi,epel,baseバージョンの確認
$ convert --version Version: ImageMagick 6.9.10-34 Q16 x86_64 2019-03-18 https://imagemagick.org Copyright: © 1999-2019 ImageMagick Studio LLC License: https://imagemagick.org/script/license.php Features: Cipher DPC Modules OpenMP Delegates (built-in): bzlib cairo djvu fftw fontconfig freetype gslib gvc jbig jng jp2 jpeg lcms ltdl lzma openexr pangocairo png ps raw rsvg tiff webp wmf x xml zlib6.9.10をインストールできました。この後
bundle installを実行すると無事RMagickのインストールができました。
以上です。参考
http://webdew.hateblo.jp/entry/2016/05/19/044105
https://qiita.com/iharakenji/items/dc9a2ce254dcdd2e97a0
- 投稿日:2019-03-27T21:44:52+09:00
OSS初心者がRailsにプルリクエストを送ってマージされるまでの一部始終
はじめに
本稿はOSSにプルリクエストを送ったことがない方に対する「自分はこういう方法でプルリクエストを送ったぞ!」という体験記として投稿させて頂きます。初心者であってもOSSに興味がある方にはご一読頂ければ嬉しいです。
前提
Githubを使って開発をしたことがある前提の内容が一部含まれます
(これからの方にもなるべく分かるように書きますが念のため)今回出来たこと
OSSにプルリクエストを送る手順
OSSごとにコントリビューション(貢献)におけるガイドラインがあるので、まずそちらに目を通しましょう
![]()
※ Railsの場合は以下を参照!
https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html目次
- プルリクエストを送りたいリポジトリをForkする
- 自分の手元のリポジトリを確認
- 自分の手元にForkしたリポジトリをローカルにcloneする
- ローカルでブランチを切る
- 修正をコミット
- テストが通ることを確認
- git remote -vでリモートとして登録されているリポジトリを確認する
- 本体をupstream,自分のリポジトリをoriginとして登録する
- もう一度remoteとして登録されているリポジトリを確認
- origin(自分の作業リポジトリ)へプッシュする
- 本体のリポジトリを開いてプルリクを出す
- マージを待つ
1. プルリクエストを送りたいリポジトリをForkする
※ Forkが分からない方はこちらを参照 :リポジトリのcloneとforkの違い
2. 自分の手元のリポジトリを確認
3. 自分の手元にForkしたリポジトリをローカルにcloneする
$ git clone git@github.com:Shigeyuki-fukuda/rails.git4. ローカルでブランチを切る
$ git checkout -b ブランチ名5. 修正をコミット
$ git commit -m "英語のコミットメッセージ"6. テストが通ることを確認
テストを実行したところテストが落ちてたのですが...
修正と関係ないところだったので、今回は一旦、無視しました![]()
(この修正の時master自体のCI落ちてた?のもあり一旦スルー)
通常は注視した方がいいかもです
$ rake test7. git remote -vでリモートとして登録されているリポジトリを確認する
Shigeyuki-fukuda/railsがremoteのoriginとして登録されていることを確認。※ Git初心者の方へ
remoteというのはコミットして修正をプッシュする目的地というイメージでOKです
![]()
例えば、git push origin masterのorigin部分のこと$ git remote -v origin git@github.com:Shigeyuki-fukuda/rails.git (fetch) origin git@github.com:Shigeyuki-fukuda/rails.git (push)8. 本体をupstream,自分のリポジトリをoriginとして登録する
OSSの慣習で、元のリポジトリをupstreamとし、自分の手元をoriginとして登録するみたいです。
$ git remote add upstream https://github.com/rails/rails.git $ git remote add origin git@github.com:Shigeyuki-fukuda/rails.git9. もう一度remoteとして登録されているリポジトリを確認
これでFork元の
rails/railsの方がupstreamならOKです。$ git remote -v origin git@github.com:Shigeyuki-fukuda/rails.git (fetch) origin git@github.com:Shigeyuki-fukuda/rails.git (push) upstream https://github.com/rails/rails.git (fetch) upstream https://github.com/rails/rails.git (push)10. origin(自分の手元の作業リポジトリ)へプッシュする
originへプッシュする$ git push origin 修正ブランチ名11. 本体のリポジトリを開いてプルリクを出す
キャプチャ忘れてしまったのですが、本体のリポジトリを開くとプルリクを出せるぞ!という表示が通常通り出ます。
Fork先にプッシュしても本体にプルリク出せるよ!の表示が出るのでそこからプルリクを作ります。こんな感じのやつ↓↓↓
当たり前ですが説明文は英語なので頑張りましょう
※ ブラウザで出来る英文チェッカー
http://lifeiscolourful.hatenablog.com/entry/grammarly-n-writing12. マージを待つ
無事にプルリクエストがマージされたことを確認しました
補足:remoteの最新をoriginに取り込みたいとき
git fetchでupstream(元のリポジトリ)の最新を取ってくる
$ git fetch upstreamupstream(元のリポジトリ)の最新をorigin(自分の手元)にマージする
$ git merge upstream/masterremoteの自分管理のリポジトリを最新にしたい場合はそのままプッシュする
※ 絶対に元のリポジトリ(upstream)のmasterにプッシュしてはいけません(ダメゼッタイ)
$ git push origin master最新の状況になったことを確認します
![]()
最後に
はじめる前は、すごい複雑で一部の「選ばれし人々」によって行われる活動がOSSだと思ったのですが、やること自体はシンプルでした。「ここ直した方がいいかも?」と思ったところを修正して、それがマージされるのは皆んなの公園を綺麗にしたみたいで気分がいいですね!!
皆さんもぜひOSSに一歩踏み出してみてはいかがでしょうか?
最後に宣伝になりますが、私が運営メンバーの一人をしているTama.rbという地域RubyコミュニティでもOSSのコードを読んで皆んなでワイワイする活動をはじめました。
OSS活動はじめたいけど一人だけだと、どうして良いのか自信ない...って方はTama.rbのslackだけでも参加頂けると嬉しいです!皆んなでワイワイOSS活動を楽しみましょう
- 投稿日:2019-03-27T20:07:15+09:00
プログラミング学習記録39〜Herokuにデプロイ〜
今日やったこと
- Progate Railsのレッスンで作ったアプリをHerokuにデプロイする
以前、Udemyの講座で作ったToDoアプリをHerokuにデプロイしたことがあったので、同じ手順でやればできるはずだと思ってやったらできました。
1回目はデータベース関連の操作で何かでミスって動かなくなってしまったのですが、2回目でコマンドを注意深く確認しながらやったらうまくいきました。
これでCloud9で環境構築してコードを書いてからHerokuにデプロイするまでの流れはだいぶ掴めました。
やっぱり実際に手を動かしてやっていくのがいいですね。
簡単なアプリとはいえ、デプロイまでできたらだいぶ達成感があるのでオススメです。
明日からは今までやってきた基礎を復習しつつ、オリジナルアプリ制作に入っていきたいと思います。
SQLとGitもなんとなく触っているので、もう少しじっくりやってみる必要がありそうです。
ということで、明日からも引き続きプログラミング学習頑張ります。
おわり
- 投稿日:2019-03-27T19:57:35+09:00
railsチュートリアル 第十四章
はじめに
この記事では、railsチュートリアル第14章の復習として行程をようやくしたものとなっています。
ユーザーをフォローする
第14章では他のユーザーをフォロー (およびフォロー解除) できるソーシャルな仕組みの追加と、フォローしているユーザーの投稿をステータスフィードに表示する機能を追加していく。
Relationshipモデル
ユーザーをフォローする機能を実装する第一歩は、データモデルを構成することとなる。
「1人のユーザーが複数のユーザーをhas_manyとしてフォローし、1人のユーザーに複数のフォロワーがいることをhas_manyで表す」
といった方法でも実装できそうだが、これでは問題が出てきてしまう。
上記のようにテーブル構造すると、非常に無駄が多く更新も大きな手間となってしまう事になる。
この問題の根本は、必要な抽象化を行なっていないことであり、正しいモデルを見つけ出す方法の1つは、Webアプリケーションにおける
followingの動作をどのように実装するかを観察することである。ここを観察すると、フォローまたはアンフォローで作成または削除されるのは、つまるところ2人のユーザーの「関係 (リレーションシップ)」であることがわかる。
リレーションシップを経由することによって1人のユーザーは1対多の関係を持つことができ、さらにユーザーは多くのfollowing (またはfollowers) と関係を持つことができるということになる。
また、Facebookのような友好関係 (Friendships) では本質的に左右対称のデータモデルが成り立つが、Twitterのようなフォロー関係では左右非対称の性質がある。
このような左右非対称な関係性を見分けるために、それぞれを能動的関係 (Active Relationship)と受動的関係 (Passive Relationship)と呼ぶことにする。
まずは、フォローしているユーザーを生成するために、能動的関係に焦点を当てていく。
フォローしているユーザーはfollowed_idがあれば識別することができるので、
active_relationshipsテーブルを作り、そこを経由することによって効率的なモデル構造を作る。このデータモデルを実装するために、まずは次のようなマイグレーションを生成する。
$ rails generate model Relationship follower_id:integer followed_id:integerこのリレーションシップは今後
follower_idとfollowed_idで頻繁に検索することになるから、それぞれのカラムにインデックスを追加しておく。db/migrate/[timestamp]_create_relationships.rbclass CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end↑のマイグレーションファイルの、
add_index :relationships, [:follower_id, :followed_id], unique: trueという複合キーインデックスは、follower_idとfollowed_idの組み合わせが必ずユニークであることを保証する仕組みであり、これにより、あるユーザーが同じユーザーを2回以上フォローすることを防ぐ。
relationshipsテーブルを作成するために、いつものようにデータベースのマイグレーションを行う。$ rails db:migrateUser/Relationshipの関連付け
フォローしているユーザーとフォロワーを実装する前に、UserとRelationshipの関連付けを行う。
1人のユーザーにはhas_many (1対多) のリレーションシップがあり、このリレーションシップは2人のユーザーの間の関係となるから、フォローしているユーザーとフォロワーの両方に属す。 (belongs_to)
今回のケースではフォローしているユーザーを
follower_idという外部キーを使って特定しなくてはならない。また、followerというクラス名は存在しないので、ここでもRailsに正しいクラス名を伝える必要がある。
能動的関係に対して1対多 (has_many) の関連付けを実装する
app/models/user.rbclass User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy . . . end上記では、ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要あるため、関連付けに
dependent: :destroyも追加している。followerの関連付けについては現段階では使わないが、
followerとfollowedを対称的に実装しておくことで、構造に対する理解は容易になる。app/models/relationship.rbclass Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end上記で定義した関連付けにより下のようなメソッドが使用できるようになった。
メソッド 用途 active_relationship.follower フォロワーを返す active_relationship.followed フォローしているユーザーを返す user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力) user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す Relationshipのバリデーション
ここで存在性のバリデーションを与えておく。
app/models/relationship.rbclass Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true endフォローしているユーザー
followingとfollowersに関して今回は
has_many throughを使う。デフォルトのhas_many throughという関連付けでは、Railsはモデル名 (単数形) に対応する外部キーを探す。
has_many :followeds, through: :active_relationships上のコードの場合、Railsは「followeds」というシンボル名を見て、これを「followed」という単数形に変え、 relationshipsテーブルのfollowed_idを使って対象のユーザーを取得してくる。
ただ、user.followedsという名前は英語として不適切となる。
代わりに、
user.followingという名前を使うことにする。
そのためには、Railsのデフォルトを上書きする必要があり、ここでは:sourceパラメーターを使って「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝える必要がある。
Userモデルにfollowingの関連付けを追加する↓app/models/user.rbclass User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy #追加 has_many :following, through: :active_relationships, source: :followed . . . end次に、followingで取得した集合をより簡単に取り扱うために、
followやunfollowといった便利メソッドを追加する。
"following" 関連のメソッド ↓app/models/user.rbclass User < ApplicationRecord . . . def feed . . . end # ユーザーをフォローする def follow(other_user) following << other_user end # ユーザーをフォロー解除する def unfollow(other_user) active_relationships.find_by(followed_id: other_user.id).destroy end # 現在のユーザーがフォローしてたらtrueを返す def following?(other_user) following.include?(other_user) end private . . . endフォロワー
ここでuser.followersメソッドを追加していく。
これは上のuser.followingメソッドと対になり、フォロワーの配列を展開するために必要な情報は、
relationshipsテーブルに既にある。よって、follower_idとfollowed_idを入れ替えるだけで、フォロワーについてもフォローする場合と全く同じ方法が活用できる。
受動的関係を使ってuser.followersを実装する↓app/models/user.rbclass User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy #追加 has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed #追加 has_many :followers, through: :passive_relationships, source: :follower . . . end
:followers属性の場合、Railsが「followers」を単数形にして自動的に外部キーfollower_idを探してくれるから:sourceキーを省略してもイイがhas_many :followingとの類似性を強調させるため残しておく。[Follow] のWebインターフェイス
この節では、フォロー/フォロー解除の基本的なインターフェイスを実装、また、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成する。
フォローのサンプルデータ
前の章のときと同じように、サンプルデータを自動作成する
rails db:seedを使って、データベースにサンプルデータを登録しておく。先にサンプルデータを自動作成できるようにしておけば、Webページの見た目のデザインから先にとりかかることができ、バックエンド機能の実装を後に回すことができるというメリットがある。
サンプルデータにfollowing/followerの関係性を追加する↓
db/seeds.rb#省略 # リレーションシップ users = User.all user = users.first following = users[2..50] followers = users[3..40] following.each { |followed| user.follow(followed) } followers.each { |follower| follower.follow(user) }ここでは、最初のユーザーにユーザー3からユーザー51までをフォローさせ、それから逆にユーザー4からユーザー41に最初のユーザーをフォローさせる。
データベース上のサンプルデータを作り直すために、いつものコマンドを実行する。
$ rails db:migrate:reset $ rails db:seed統計と [Follow] フォーム
これでサンプルユーザーに、フォローしているユーザーとフォロワーができた。
プロフィールページとHomeページを更新して、これを反映する。
次にUsersコントローラにfollowingアクションとfollowersアクションを追加する↓
config/routes.rbRails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' get '/signup', to: 'users#new' get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' #追加 resources :users do member do get :following, :followers end end resources :account_activations, only: [:edit] resources :password_resets, only: [:new, :create, :edit, :update] resources :microposts, only: [:create, :destroy] end↑によって生成されるルーティングテーブルを下に示す
HTTPリクエスト URL アクション 名前付きルート GET /users/1/following following following_user_path(1) GET /users/1/followers followers followers_user_path(1) ルーティングを定義したので、統計情報のパーシャルを実装する。
app/views/shared/_stats.html.erb<% @user ||= current_user %> <div class="stats"> <a href="<%= following_user_path(@user) %>"> <strong id="following" class="stat"> <%= @user.following.count %> </strong> following </a> <a href="<%= followers_user_path(@user) %>"> <strong id="followers" class="stat"> <%= @user.followers.count %> </strong> followers </a> </div>
<% @user ||= current_user %>
このコードは、@userがnilでない場合 (つまりプロフィールページの場合) は何もせず、nilの場合 (つまりHomeページの場合) には@userにcurrent_userを代入するコードとなる。また、
<strong id="following" class="stat">
...
</strong>
こうしておくと、Ajaxを実装するときに便利となる。これをHomeページに表示する。
app/views/static_pages/home.html.erb<% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> #追加 <section class="stats"> <%= render 'shared/stats' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div> </div> <% else %> . . . <% end %>また必要に応じてSCSSにスタイルを加える。
次の行程として[Follow] / [Unfollow] ボタン用のパーシャルも作成しておく。
app/views/users/_follow_form.html.erb<% unless current_user?(@user) %> <div id="follow_form"> <% if current_user.following?(@user) %> <%= render 'unfollow' %> <% else %> <%= render 'follow' %> <% end %> </div> <% end %>このコードは、followとunfollowのパーシャルに作業を振っているだけになるからRelationshipsリソース用の新しいルーティングを追加し、フォロー/フォロー解除用のパーシャルを個別に用意する必要がある。
Relationshipリソース用のルーティングを追加する↓
config/routes.rb#省略 resources :relationships, only: [:create, :destroy] endユーザーをフォローするフォーム↓
app/views/users/_follow.html.erb<%= form_for(current_user.active_relationships.build) do |f| %> <div><%= hidden_field_tag :followed_id, @user.id %></div> <%= f.submit "Follow", class: "btn btn-primary" %> <% end %>ユーザーをフォロー解除するフォーム↓
app/views/users/_unfollow.html.erb<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete }) do |f| %> <%= f.submit "Unfollow", class: "btn" %> <% end %>これでパーシャルとしてフォロー用フォームをプロフィールページに表示できるようになった。
プロフィールページにフォロー用フォームとフォロワーの統計情報を追加する↓
app/views/users/show.html.erb<% provide(:title, @user.name) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1> </section> #追加 <section class="stats"> <%= render 'shared/stats' %> </section> </aside> <div class="col-md-8"> #追加 <%= render 'follow_form' if logged_in? %> <% if @user.microposts.any? %> <h3>Microposts (<%= @user.microposts.count %>)</h3> <ol class="microposts"> <%= render @microposts %> </ol> <%= will_paginate @microposts %> <% end %> </div> </div>この[Follow] / [Unfollow] ボタンの実装には標準的な方法とAjaxを使う方法の2つがある。
その前に、フォローしているユーザーとフォロワーを表示するページをそれぞれ作成してHTMLインターフェイスを完成させる。
[Following] と [Followers] ページ
フォローしているユーザーを表示するページと、フォロワーを表示するページは、いずれもプロフィールページとユーザー一覧ページを合わせたもののようになる。
ここでの最初の作業は、フォローしているユーザーのリンクとフォロワーのリンクを動くようにすることとなる。
Twitterに倣って、どちらのページでもユーザーのログインを要求するようにする。
app/controllers/users_controller.rbclass UsersController < ApplicationController before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers] . . . def following @title = "Following" @user = User.find(params[:id]) @users = @user.following.paginate(page: params[:page]) render 'show_follow' end def followers @title = "Followers" @user = User.find(params[:id]) @users = @user.followers.paginate(page: params[:page]) render 'show_follow' end private . . . endRailsは慣習に従って、アクションに対応するビューを暗黙的に呼び出す。
上記でも、renderを明示的に呼び出し、show_followという同じビューを出力している。したがって、作成が必要なビューはこれ1つですむ。フォローしているユーザーとフォロワーの両方を表示するshow_followビュー↓
app/views/users/show_follow.html.erb<% provide(:title, @title) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= gravatar_for @user %> <h1><%= @user.name %></h1> <span><%= link_to "view my profile", @user %></span> <span><b>Microposts:</b> <%= @user.microposts.count %></span> </section> <section class="stats"> <%= render 'shared/stats' %> <% if @users.any? %> <div class="user_avatars"> <% @users.each do |user| %> <%= link_to gravatar_for(user, size: 30), user %> <% end %> </div> <% end %> </section> </aside> <div class="col-md-8"> <h3><%= @title %></h3> <% if @users.any? %> <ul class="users follow"> <%= render @users %> </ul> <%= will_paginate %> <% end %> </div> </div>[Follow] ボタン (基本編)
フォローとフォロー解除はそれぞれリレーションシップの作成と削除に対応しているため、まずは
Relationshipsコントローラが必要となる。$ rails generate controller Relationships次に、
logged_in_userフィルターをRelationshipsコントローラのアクションに対して追加する。app/controllers/relationships_controller.rbclass RelationshipsController < ApplicationController before_action :logged_in_user def create end def destroy end end[Follow] / [Unfollow] ボタンを動作させるためには、フォームから送信されたパラメータを使って、
followed_idに対応するユーザーを見つけてくる必要がある。その後、見つけてきたユーザーに対して適切に
follow/unfollowメソッドを使う。Relationshipsコントローラ↓
app/controllers/relationships_controller.rbclass RelationshipsController < ApplicationController before_action :logged_in_user def create user = User.find(params[:followed_id]) current_user.follow(user) redirect_to user end def destroy user = Relationship.find(params[:id]).followed current_user.unfollow(user) redirect_to user end endこれにより、フォロー/フォロー解除の機能が完成した。
[Follow] ボタン (Ajax編)
上記ではRelationshipsコントローラの
createアクションとdestroyアクションを単に元のプロフィールにリダイレクトしていた。ここで、
Ajaxを使えば、Webページからサーバーに「非同期」で、ページを移動することなくリクエストを送信することができる。WebフォームにAjaxを採用するのは今や当たり前になりつつあるので、RailsでもAjaxを簡単に実装できるようになっている。
form_for
というコードを
form_for ..., remote: true
と置き換えるだけで、Railsは自動的にAjaxを使うようになる。
Ajaxを使ったフォローフォーム↓app/views/users/_follow.html.erb<%= form_for(current_user.active_relationships.build, remote: true) do |f| %> <div><%= hidden_field_tag :followed_id, @user.id %></div> <%= f.submit "Follow", class: "btn btn-primary" %> <% end %>Ajaxを使ったフォロー解除フォーム↓
app/views/users/_unfollow.html.erb<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete }, remote: true) do |f| %> <%= f.submit "Unfollow", class: "btn" %> <% end %>フォームの更新が終わったので、今度はこれに対応する
Relationshipsコントローラを改造して、Ajaxリクエストに応答できるようにする。こういったリクエストの種類によって応答を場合分けするときは、
respond_toメソッドというメソッドを使うようにする。respond_to do |format| format.html { redirect_to user } format.js end上の (ブロック内の) コードのうち、いずれかの1行が実行される。
RelationshipsコントローラでAjaxに対応させるために、respond_toメソッドをcreateアクションとdestroyアクションに追加する。
app/controllers/relationships_controller.rbclass RelationshipsController < ApplicationController before_action :logged_in_user def create @user = User.find(params[:followed_id]) current_user.follow(@user) #追加修正 respond_to do |format| format.html { redirect_to @user } format.js end end def destroy @user = Relationship.find(params[:id]).followed current_user.unfollow(@user) #追加修正 respond_to do |format| format.html { redirect_to @user } format.js end end endビューで変数を使うため、userが@userに変わっている
今度はブラウザ側でJavaScriptが無効になっていた場合 (Ajaxリクエストが送れない場合) でもうまく動くようにする。
config/application.rbrequire File.expand_path('../boot', __FILE__) . . . module SampleApp class Application < Rails::Application . . . # 認証トークンをremoteフォームに埋め込む config.action_view.embed_authenticity_token_in_remote_forms = true end end.js.erbファイル
JavaScriptが有効になっていても、まだ十分に対応できていない部分がある。
というのも、Ajaxリクエストを受信した場合は、Railsが自動的にアクションと同じ名前を持つJavaScript用の埋め込みRuby (.js.erb) ファイル (create.js.erbやdestroy.js.erbなど) を呼び出指定しまうからである。
なのでファイルを新たに作成する必要がある。
JS-ERbファイルの内部では、DOM (Document Object Model) を使ってページを操作するため、RailsがjQuery JavaScriptヘルパーを自動的に提供している。
JavaScriptと埋め込みRubyを使ってフォローの関係性を作成する↓
app/views/relationships/create.js.erb$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); $("#followers").html('<%= @user.followers.count %>');Ruby JavaScript (RJS) を使ってフォローの関係性を削除する↓
app/views/relationships/destroy.js.erb$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); $("#followers").html('<%= @user.followers.count %>');これらのコードにより、プロフィールページを更新させずにフォローとフォロー解除ができるようになった。
ステータスフィード
現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示する。
ステータスフィードを実装するには現在のユーザーによってフォローされているユーザーに対応するユーザーidを持つマイクロポストを取り出し、同時に現在のユーザー自身のマイクロポストも一緒に取り出すようにする必要がある。
フィードに必要な3つの条件を下に示す。
・フォローしているユーザーのマイクロポストがフィードに含まれていること。
・自分自身のマイクロポストもフィードに含まれていること。
・フォローしていないユーザーのマイクロポストがフィードに含まれていないこと最初に、このフィードで必要なクエリについて考えてみる。
ここで必要なのは、micropostsテーブルから、あるユーザー (つまり自分自身) がフォローしているユーザーに対応するidを持つマイクロポストをすべて選択 (select) することである。このクエリを模式的に書くと次のようになる。
SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>↑を参考に、今回必要になる選択は、上よりも少し複雑で、例えば次のような形になる。
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)この
following_idsメソッドは、has_many :followingの関連付けをしたときにActive Recordが自動生成したものである。これにより、user.followingコレクションに対応するidを得るためには、関連付けの名前の末尾に_idsを付け足すだけで済む。
app/models/user.rbclass User < ApplicationRecord . . . # パスワード再設定の期限が切れている場合はtrueを返す def password_reset_expired? reset_sent_at < 2.hours.ago end # ユーザーのステータスフィードを返す def feed Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id) end # ユーザーをフォローする def follow(other_user) following << other_user end . . . endサブセレクト
問題点として上記のフィードの実装は、投稿されたマイクロポストの数が膨大になったときにうまくスケールしない、つまり、フォローしているユーザーが5,000人程度になるとWebサービス全体が遅くなる可能性がある。
上に示したコードの問題点は、following_idsでフォローしているすべてのユーザーをデータベースに問い合わせし、さらに、フォローしているユーザーの完全な配列を作るために再度データベースに問い合わせしている点である。
このような問題は、SQLのサブセレクト(subselect)を使うと解決できる。
まずはコードを若干修正し、フィードをリファクタリングすることから始める。
whereメソッド内の変数に、キーと値のペアを使う↓
app/models/user.rbclass User < ApplicationRecord . . . # ユーザーのステータスフィードを返す def feed Micropost.where("user_id IN (:following_ids) OR user_id = :user_id", following_ids: following_ids, user_id: id) end . . . end前者の疑問符を使った文法も便利だが、同じ変数を複数の場所に挿入したい場合は、後者の置き換え後の文法を使う方がより便利になる。
上記ではfollowing_idsをSQLのサブセレクトとして使う。
つまり、「ユーザー1がフォローしているユーザーすべてを選択する」というSQLを既存のSQLに内包させる形になり、結果としてSQLは次のようになる。
SELECT * FROM microposts
WHERE user_id IN (SELECT followed_id FROM relationships
WHERE follower_id = 1)
OR user_id = 1
これでもっと効率的なフィードを実装する準備がきた。
フィードの最終的な実装↓app/models/user.rbclass User < ApplicationRecord . . . # ユーザーのステータスフィードを返す def feed following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id) end . . . end(ここに記述されているコードは生のSQLを表す文字列であり、following_idsという文字列はエスケープされているのではなく、見やすさのために式展開している)
これでステータスフィードの実装は完了。
さいごに
これでrailsチュートリアルを全て完走した。
ここで出てくる知識はサービスを作る上で基本になるところだろうからしっかり復習して自分の知識にしようと思う。ここにプラスアルファで返信機能などもつけることを演習として推奨されているのでまた挑戦してみようと思う。
- 投稿日:2019-03-27T15:58:59+09:00
bcryptがインストールしてるのに出来てないと出るエラーと対策
概要
Railsチュートリアルを進行中に躓いたbcryptに関するエラーと対策を記載していきます。
内容
まずは、インストール手順を説明します。
Gemfileに追加したいライブラリとそのバージョンを追加する。
Gemfilesource 'https://rubygems.org' gem 'rails', '5.0.0.1' gem 'bcrypt', '3.1.11' #追加 . . .そしてコマンドを実行してインストール
$ bundle installここまでは、問題ない。rails test などのコマンドを実行すると
bcryptをインストールしたにも関わらず、インストールしてないと出てくる。You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install ・ ・ ・チュートリアル進行中にこのエラーで嵌る人は結構いるみたいです。
対策
色んな方のQiitaを参考にして改善に試みてみましたが、全く原因も対策も分からず
次の日になってbundle installしてrails testを実行してみると正常に動作しました...。ただ、IDEはCloud9を使用していて、30分でサーバーが切れるので
サーバーの再起動などが関わっている可能性もあるので実行してみる価値はあると思います。Weblickの再起動などの対策方法もあるみたいなので調べてみてください。
参考URLに自分が実行していなかった方法が記載されていたので、是非試してみてください。参考URL
- 投稿日:2019-03-27T15:35:27+09:00
もそ、プログラミングを学ぶ【Ruby & Rails復習編】〜本引数と仮引数の違いってなに?〜
相変わらずエラー問題に苦戦中の もそ。
やさしい先輩たちから「この部分はRubyの復習をしてみるといいかも」というアドバイスをもらい早速Rubyを復習しています。すると早速、
「げっ、ここって意味がよくわからなくてスルーしたところじゃん...」
となっていたところが出てきました。
Railsを学んだのでなんとな〜く分からんでもないのですが、理解を深めるべくリベンジしました。今日はそんなRubyの本引数と仮引数について書いていきます。
引数について
まずは引数(ひきすう)についておさらいします。
引数は、プログラムでメソッドなどに渡すことのできる値です。
変数の値には使える範囲が決まっていて、これをスコープと呼びます。
たとえば、あるdef~end内で定義した変数は、ほかのメソッドでそのまま使うことはできません。引数を使うことでメソッドの外にある変数、つまりスコープ外にある変数をメソッドの中で使うことができます。
そして引数には、メソッドを呼び出す部分に記述する本引数とメソッドを定義している部分に記述する仮引数の2つがあります。
コードを実際に見てみましょう。① def multi(input)←()内は仮引数 puts input * input end ② puts "何か数字を入力してください" input = gets.to_i ③ multi(input)←()内は本引数こんな感じです。
コードが読み込まれて処理される順番は③→②→①となります。
③と②でスコープを定義して、引数を使うことで①に変数を渡しています。ひとつポイントなのが、仮引数の名前は本引数と違ってもいいということ。
先ほどのコードの流れは、
③のinputという本メソッド内に②gets.to_iという数値を入れて、①で結果を表示する
というものでした。
これは方程式と同じで、仮の名前に数値を代入しているので、「x=aとして x+1*10=aを解きなさい」という数学の問題と同じような感覚です。なので先ほどのコードは、
def multi(number) puts number * number return number * number end puts "何か数字を入力してください" value = gets.to_i multi(value)と表すことができます。
スコープの範囲に注意
次に、このようなコードで考えてみましょう。
① def rename(name) name = "もそと#{name}" end ② name = "ポチ" rename(name) puts nameこのコードを実行すると、出力結果は「ポチ」だけになります。
では、「もそとポチ」と出力するにはどうすればいいでしょうか?このコードのポイントは、何回も出てきている"name"の記述。
同じ名前が出てきているのですが、実は①と②で中身が違います。...意味分かんないですよね。私もわからないです。
というわけで、LINEでポチに泣きつきました。
「え、これ外側で名前変えてるだけだからでしょ(即答)」
ここでポチのいう外側は②の部分。
① def rename(name) name = "もそと#{name}" end ② name = "ポチ" rename(name) puts name②のコードでは、
「name=ポチとして、rename(ポチ)にします。ではnameを出してください」
と記述しています。
最初にname=ポチと言っているので、ここで指すnameの中身は「ポチ」になります。では①の部分はどうでしょうか。
「rename(name)を定義します。name=”もそとポチ”です」よくよく考えてみてください。
これ、②と①でnameで指示しているnameの中身が違いますよね。なので出力結果を「もそとポチ」にするには、
① def rename(name) name = "もそと#{name}" end ② name = "ポチ" name=rename(name) puts nameこのように記述すればよいのです!なるほど〜!
--
ふわふわしていた引数の考え方が、やっときちんと理解できた...気がします(理解したとは言っていません)
配列と本引数/仮引数はややこしいとポチも言っていたので、ここは焦らず少しずつ理解を深めていこうと思います。
果たして理解できる日は来るのか...もその修行は続く。
- 投稿日:2019-03-27T15:23:41+09:00
Rails Tutorialの次に8 Beautiful Ruby on Rails Apps in 30 Days & TDD 〜Udemy感想その1〜
はじめに
4月からエンジニアとして新社会人になります
内定もらってから1年くらいUdemyでプログラミング学習(主にWeb系)をしていて、
修了した講座も増えてきました
(大学での専攻は情報技術とは全く関係なく、内定頂いた時点でもバリバリ初心者でした)メンターの方に感想をQiitaに書いてみたらと言われたので当時を思い出しながら書いていきます
同じように初心者からWeb系の勉強始める人の参考なれば嬉しいなあ
使った講座
勉強したのはこれ↓
8 Beautiful Ruby on Rails Apps in 30 Days & TDD - Immersiverailsを使ってinstagramやHacker News等のウェブアプリを8個作る
時間は計16時間、なのにまさかの無料
音声は英語、英語字幕はついてるけど自動生成なのでちょくちょく間違いアリ
ただ動画を見ながらなので言ってることが全部理解できてなくても何がしたいか、何言ってるのかはだいたいわかる
この講座に限らず、英語の講座の方が日本語の講座より内容が盛りだくさん(単純に1講座の時間が長い)なイメージ
セールになるとどちらも同じ値段になるので英語の教材選んどいた方がコスパはいいと思います
その時の自分
コードを書きはじめて1年くらい(ほぼPythonのみ、Rubyはちょっとだけ)
Rails Tutorialは最後までやった、ただ理解できないとこは飛ばした
仕事ではrailsを使うと聞いてたの、Rails Tutorialの次の教材を探してた
良かった点
- 無料!!
無料です、ああ素晴らしい。。
Webの勉強興味はあるけどお金払ってまでやりたいかって言われるとなーって人はとりあえずやってみてください
railsの有料の講座もいくつかやってみましたが遜色ないと思います
- たくさんのアプリが作れる
初心者あるあるかどうかはわからないんですが
僕の場合フレームワークのチュートリアルが終わった段階で
「うん、なんとなく言われた通りのものはできた。。。
で、これを使ったら何が作れるの?」ってなったんですよね
この講座は8つアプリを作るので多くの機能を触るので
「画像のアップロードはこうやるのか。。」
「動画のカテゴライズって確かによく見るな」
「星5つのレーティングも付けれるのか!」
などrailsを使ってこんな機能が実装できるんだという気づきが多くありましたせっかくrails勉強したから何か作りたい、でも何を作ったらいいかわからない
って人もこの講座で8個もアプリ作ったら何かヒントがあるかも知れません今ひとつな点
- rail触ったことないとキツいかも。。
これは僕の英語力が足りないのかも知れませんがrailsを全く触ったことない状態で
挑むのは厳しい印象でした講座の最初にRuby on railsの説明、インストールの仕方、MVCの説明等もしてくれていますが
やっぱり初めてrails触る人は一旦Rails Tutorial等の日本語の教材をやった方がいいとおもいます英語得意な方はガンガンいっちゃってください!
- 長い。。笑
今ひとつなわけじゃないんですが、、
盛りだくさんだと褒めといてあれなんですが、やっぱり長いです笑
最後までやるにはかなり時間がかかるので余裕を持った計画で取り組むといいとおもいます
(絶対16時間の倍以上かかると思うので)
講師も30日でって言ってますしねただ1つ1つのアプリの作成はそんなに時間かからないので
そういう意味でも初心者には優しいですちなみに
この講座なんですが講師がもう古いって言ってサポートが修了してます笑
ただ講座の受講はまだできるみたいですし基本的な部分を勉強するには
初心者にはオススメできると思いますまとめ
半年ほど前に使った講座なのでうろ覚えのところもありますが
無料とは思えないほど盛りだくさんな講座でしたRails Tutorial終わってさあ、どうしよっかなーって人はぜひやってみてください
- 投稿日:2019-03-27T15:18:09+09:00
数分経ってもCloud9でherokuコマンドが使えるようにする
環境
Railsチュートリアルの推奨環境
Cloud9のIDE
heroku
原因
Cloud9のIDEは30分アクセスが無いとサーバーが落ちるようになっている。(恥ずかしながら全く知りませんでした。)
また、ターミナル毎に秘密鍵やherokuが設定されているため
サーバーを立ち上げ直したり、別ターミナルを開くと登録されていない状態に戻る$ heroku create bash: heroku: command not foundRailsチュートリアル 第1章 1.5.1 herokuのセットアップ で紹介されている通り
cloud9上でherokuをインストールする事が出来ますが、毎回無駄な処理が必要となる。source <(curl -sL https://cdn.learnenough.com/heroku_install)さらに詳しい原因については参考URLをご覧ください。
現在の自分の知識では、理解が追いつかなかったので説明が出来ません。
日々精進をして理解出来るようになりたいと思います。
対策
ターミナル起動時にPOSTが自動で設定されるようにする必要がある。$ vi ~/.bashrc # 以下をファイルの最後に追記 PATH=/usr/local/heroku/bin:$PATH # こちらの記載でも可 source $HOME/.profile > /dev/null参考URL
https://qiita.com/tathuhi10/items/281c0d8e03438e0e752c
https://railstutorial.jp/chapters/beginning?version=5.1#sec-heroku_setup
- 投稿日:2019-03-27T11:55:55+09:00
railsチュートリアル 第十三章
はじめに
この記事は、railsチュートリアル第十二章の行程を復習しやすいように要約したものとなっています。
ユーザーのマイクロポスト
この章では短いメッセージを投稿できるようにするためのリソースマイクロポストを追加していく。
Micropostモデル
はじめにUserモデルの関連付けを含む
Micropostモデルを生成する。Micropostモデルは、マイクロポストの内容を保存するcontent属性と、特定のユーザーとマイクロポストを関連付けるuser_id属性の2つの属性だけを持つ。
$ rails generate model Micropost content:text user:referencesある程度の量のテキストを格納する場合はtext型が望ましいのでcontentにはtext型をあてている。
上記のコマンドを実行するとMicropostモデルが生成され、ユーザーと1対1の関係であることを表す
belongs_toのコードも追加されている。これは
user:referencesという引数を含めたことにより、自動的にインデックスと外部キー参照付きのuser_idカラムが追加され、UserとMicropostを関連付けする下準備をしてくれるから。app/models/micropost.rbclass Micropost < ApplicationRecord belongs_to :user endまたUserモデルのときと同じで、Micropostモデルのマイグレーションファイルでも
t.timestampsという行 (マジックカラム) が自動的に生成されている。db/migrate/[timestamp]_create_microposts.rbclass CreateMicroposts < ActiveRecord::Migration[5.0] def change create_table :microposts do |t| t.text :content t.references :user, foreign_key: true t.timestamps end #インデックスを付与 add_index :microposts, [:user_id, :created_at] end end上のファイルではuser_idとcreated_atカラムにインデックスが付与されている。
こうすることで、user_idに関連付けられたすべてのマイクロポストを作成時刻の逆順で取り出しやすく*なる。
ここでいつも通りマイグレーションで、データベースを更新しておく。
Micropostのバリデーション
以下のようにバリデーションで存在性、最大文字を検証する。
app/models/micropost.rbclass Micropost < ApplicationRecord belongs_to :user validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } endUser/Micropostの関連付け
Webアプリケーション用のデータモデルを構築するにあたって、個々のモデル間での関連付けを十分考えておくことが重要となる。
今回の場合は、それぞれのマイクロポストは1人のユーザーと関連付けられ、それぞれのユーザーは (潜在的に) 複数のマイクロポストと関連付けられる。
user/micropost関連メソッドのまとめは以下のようになる。
メソッド 用途 micropost.user Micropostに紐付いたUserオブジェクトを返す user.microposts Userのマイクロポストの集合をかえす user.microposts.create(arg) userに紐付いたマイクロポストを作成する user.microposts.create!(arg) userに紐付いたマイクロポストを作成する (失敗時に例外を発生) user.microposts.build(arg) userに紐付いた新しいMicropostオブジェクトを返す user.microposts.find_by(id: 1) userに紐付いていて、idが1であるマイクロポストを検索する
@user.microposts.buildのようなコードを使うためには、 UserモデルとMicropostモデルをそれぞれ更新して、関連付ける必要がある。app/models/micropost.rbclass Micropost < ApplicationRecord #マイクロポストがユーザーに所属する関連付け belongs_to :user validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } endapp/models/user.rbclass User < ApplicationRecord #ユーザーがマイクロポストを複数所有する (has_many) 関連付け has_many :microposts . . . endマイクロポストを改良する
この項では、具体的には、ユーザーのマイクロポストを特定の順序で取得できるようにしたり、マイクロポストをユーザーに依存させて、ユーザーが削除されたらマイクロポストも自動的に削除されるようにしていく。
デフォルトのスコープ
user.micropostsメソッドはデフォルトでは読み出しの順序に対して何も保証しないが、ブログやTwitterの慣習に従って、作成時間の逆順、つまり最も新しいマイクロポストを最初に表示するように改良する。
これを実装するためには、
default scopeというテクニックを使用する。投稿はSQL文で、
order('created_at DESC')
を引数に与えたい。よって以下のようになる。
app/models/micropost.rbclass Micropost < ApplicationRecord belongs_to :user #追加 default_scope -> { order(created_at: :desc) } validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } endDependent: destroy
サイト管理者はユーザーを破棄する権限を持つ。ユーザーが破棄された場合、ユーザーのマイクロポストも同様に破棄されるようにしたい。
この振る舞いは、
has_manyメソッドにオプションを渡してあげることで実装できる。app/models/user.rbclass User < ApplicationRecord has_many :microposts, dependent: :destroy . . . end
dependent: :destroyというオプションを使うと、ユーザーが削除されたときに、そのユーザーに紐付いた (そのユーザーが投稿した) マイクロポストも一緒に削除されるようになる。マイクロポストを表示する
Web経由でマイクロポストを作成する方法は現時点ではないが、ここでは、Twitterのような独立したマイクロポストのindexページは作らずに、ユーザーのshowページで直接マイクロポストを表示させることにする。
マイクロポストの描画
ここで、ユーザーのプロフィール画面 (show.html.erb) でそのユーザーのマイクロポストを表示させたり、これまでに投稿した総数も表示させたりしていく。
まずは、Micropostのコントローラとビューを作成するために、コントローラを生成する。
$ rails generate controller Microposts以前は_user.html.erbパーシャルを使って自動的に@users変数内のそれぞれのユーザーを出力していた。
これを参考に、_micropost.html.erbパーシャルを使ってマイクロポストのコレクションを表示しようとすると、次のようになる。
<ol class="microposts"> <%= render @microposts %> </ol>よって対応するパーシャルを以下に示す。
app/views/microposts/_micropost.html.erb<li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"><%= micropost.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. </span> </li>上記では一度にすべてのマイクロポストが表示されてしまう潜在的問題に対処している。
前回のユーザー一覧では
<%= will_paginate %>
のようにコードは引数なしで動作していた。
これはwill_paginateが、Usersコントローラのコンテキストにおいて、@usersインスタンス変数が存在していることを前提としているためとなる。このインスタンス変数は
ActiveRecord::Relationクラスのインスタンスで、今回の場合はUsersコントローラのコンテキストからマイクロポストをページネーションしたいため (つまりコンテキストが異なるため)、明示的に@microposts変数をwill_paginateに渡す必要がある。したがって、そのようなインスタンス変数をUsersコントローラのshowアクションで定義しなければならない。
app/controllers/users_controller.rbclass UsersController < ApplicationController . . . def show @user = User.find(params[:id]) #追加 @microposts = @user.microposts.paginate(page: params[:page]) end . . . endマイクロポストの投稿数
投稿数の表示はcountメソッドを使うことで可能となる。
user.microposts.count以上のことを踏まえてプロフィール画面にマイクロポストを表示させてみる。
app/views/users/show.html.erb<% provide(:title, @user.name) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1> </section> </aside> #追加 <div class="col-md-8"> <% if @user.microposts.any? %> <h3>Microposts (<%= @user.microposts.count %>)</h3> <ol class="microposts"> <%= render @microposts %> </ol> <%= will_paginate @microposts %> <% end %> </div> </div>マイクロポストのサンプル
現時点ではマイクロポストがない状態なので、サンプルを追加する。
すべてのユーザーにマイクロポストを追加しようとすると時間が掛かり過ぎるので、
takeメソッドを使って最初の6人だけに追加することにする。
User.order(:created_at).take(6)この6人については、1ページの表示限界数 (30) を越えさせるために、それぞれ50個分のマイクロポストを追加するようにする。
db/seeds.rb. . . users = User.order(:created_at).take(6) 50.times do content = Faker::Lorem.sentence(5) users.each { |user| user.microposts.create!(content: content) } end$ rails db:migrate:reset $ rails db:seedこのコマンドをうち、railsサーバーを再起動するとプロフィールにマイクロポストのサンプルが表示される。
ここに CSSを加えてスタイルを整える必要があるが、ここでは省略。
マイクロポストを操作する
データモデリングとマイクロポスト表示テンプレートの両方が完成したので、次はWeb経由でそれらを作成するためのインターフェイスを整えていく。
従来のRails開発の慣習と異なり、Micropostsリソースへのインターフェイスは、主にプロフィールページとHomeページのコントローラを経由して実行されるから、Micropostsコントローラには
newやeditのようなアクションは不要でcreateとdestroyがあれば十分ということになる。したがって、Micropostsのリソースは以下になる。
config/routes.rbRails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' get '/signup', to: 'users#new' get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' resources :users resources :account_activations, only: [:edit] resources :password_resets, only: [:new, :create, :edit, :update] #追加 resources :microposts, only: [:create, :destroy] end
HTTPリクエスト URL アクション 名前付きルート POST /microposts create microposts_path DELETE /microposts/1 destroy micropost_path(micropost) マイクロポストのアクセス制御
関連付けられたユーザーを通してマイクロポストにアクセスするので、createアクションやdestroyアクションを利用するユーザーは、ログイン済みでなければいけない。
以前の章では、beforeフィルターのlogged_in_userメソッドを使って、ログインを要求した。
あのときはUsersコントローラ内にこのメソッドがあったので、beforeフィルターで指定していたが、このメソッドはMicropostsコントローラでも必要となるので、このメソッドを
Applicationコントローラに移す。ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper private # ユーザーのログインを確認する def logged_in_user unless logged_in? store_location flash[:danger] = "Please log in." redirect_to login_url end end endコードが重複しないよう、このときUsersコントローラからlogged_in_userを削除しておく。
マイクロポストを作成する
logged_in_userメソッドにより、createアクションやdestroyアクションに対するアクセス制限が、beforeフィルターで簡単に実装できるようになった。
createアクションを定義していく。
app/controllers/microposts_controller.rbclass MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else render 'static_pages/home' end end def destroy end private def micropost_params params.require(:micropost).permit(:content) end end上記では
micropost_paramsでStrong Parametersを使っていることにより、マイクロポストのcontent属性だけがWeb経由で変更可能になっている。次にマイクロポスト作成フォームを構築するために、サイト訪問者がログインしているかどうかに応じて異なるHTMLを提供するコードを使う。
app/views/static_pages/home.html.erb#追加 <% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> </div> <% else %> <div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails logo"), 'http://rubyonrails.org/' %> #追加 <% end %>上のコードを動かすにはいくつかのパーシャルを作る必要がある。
サイドバーで表示するユーザー情報のパーシャル
app/views/shared/_user_info.html.erb<%= link_to gravatar_for(current_user, size: 50), current_user %> <h1><%= current_user.name %></h1> <span><%= link_to "view my profile", current_user %></span> <span><%= pluralize(current_user.microposts.count, "micropost") %></span>↑ではユーザーが投稿したマイクロポストの総数が表示されるようになっている。
pluralizeメソッドを使って “1 micropost” や “2 microposts” と表示するように調整している。マイクロポスト投稿フォームのパーシャル
app/views/shared/_micropost_form.html.erb<%= form_for(@micropost) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <% end %>上記のフォームを動かすには、2箇所の変更が必要となる。
一つ目はhomeアクションに@micropostを定義すること。
app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def home @micropost = current_user.microposts.build if logged_in? end def help end def about end def contact end endcurrent_userメソッドはユーザーがログインしているときしか使えないから、@micropost変数もログインしているときのみ定義されるようになる。
もう一つの変更はエラーメッセージのパーシャルを再定義すること。
前回はエラーメッセージパーシャルが@user変数を直接参照していたが今回は代わりに@micropost変数を使う必要がある。
これらのケースをまとめると、フォーム変数fをf.objectとすることによって、関連付けられたオブジェクトにアクセスすることができる。
app/views/shared/_error_messages.html.erb<% if object.errors.any? %> <div id="error_explanation"> <div class="alert alert-danger"> The form contains <%= pluralize(object.errors.count, "error") %>. </div> <ul> <% object.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %>この修正により、ユーザー登録・パスワード再設定・ユーザー編集のそれぞれのビューも変更する必要がある。
app/views/users/new.html.erb<%= render 'shared/error_messages', object: f.object %>app/views/users/edit.html.erb<%= render 'shared/error_messages', object: f.object %>app/views/password_resets/edit.html.erb<%= render 'shared/error_messages', object: f.object %>これによりマイクロポスト投稿フォームが動くようになる。
フィードの原型
Homeページにまだマイクロポストを表示する部分が実装されていないから今の段階では投稿した内容をすぐに見ることができない。
ユーザー自身のポストを含むマイクロポストのフィードがないと不便となるので
feedメソッドをUserモデルで作る。フィードの原型では、まずは現在ログインしているユーザーのマイクロポストをすべて取得する。
app/models/user.rbclass User < ApplicationRecord . . . # 試作feedの定義 # 完全な実装は次章の「ユーザーをフォローする」を参照 def feed Micropost.where("user_id = ?", id) end private . . . end↑のコードは↓のコードと本質的に同等となるが、↑の方が応用が効きやすいため↑を採用する。
def feed microposts endサンプルアプリケーションでフィードを使うために、現在のユーザーのページ分割されたフィードに@feed_itemsインスタンス変数を追加し、次にフィード用のパーシャルをHomeページに追加する。
app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def home if logged_in? @micropost = current_user.microposts.build #追加 @feed_items = current_user.feed.paginate(page: params[:page]) end end def help end def about end def contact end endapp/views/shared/_feed.html.erb<% if @feed_items.any? %> <ol class="microposts"> <%= render @feed_items %> </ol> <%= will_paginate @feed_items %> <% end %>後は、いつものようにフィードパーシャルを表示すればHomeページにフィードを追加できる。
app/views/static_pages/home.html.erb<% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> #追加 <%= render 'shared/feed' %> </div> </div> <% else %> . . . <% end %>これでマイクロポストの作成はうまくいくようになった。
ただマイクロポストの投稿が失敗すると、 Homeページは@feed_itemsインスタンス変数を期待しているため、現状では壊れてしまう。
よって空の配列を渡しておく。
app/controllers/microposts_controller.rbclass MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else #追加 @feed_items = [] render 'static_pages/home' end end def destroy end private def micropost_params params.require(:micropost).permit(:content) end endマイクロポストを削除する
最後の機能として、マイクロポストリソースにポストを削除する機能を追加する。
これはユーザー削除と同様に、
"delete"リンクを使用する。ユーザーの削除は管理者ユーザーのみが行えるように制限されていたのに対し、今回は自分が投稿したマイクロポストに対してのみ削除リンクが動作する。
最初のステップとして、マイクロポストのパーシャルに削除リンクを追加。
app/views/microposts/_micropost.html.erb<li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"><%= micropost.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <% if current_user?(micropost.user) %> #追加 <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li>次に、Micropostsコントローラの
destroyアクションを定義する。app/controllers/microposts_controller.rbclass MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] before_action :correct_user, only: :destroy . . . def destroy @micropost.destroy flash[:success] = "Micropost deleted" redirect_to request.referrer || root_url end private def micropost_params params.require(:micropost).permit(:content) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end end上記では
request.referrerというメソッドを使っている。このメソッドはフレンドリーフォワーディングのrequest.url変数と似ていて、一つ前のURLを返す。(今回の場合、Homeページになる)
ちなみに、元に戻すURLが見つからなかった場合でも、||演算子でroot_urlをデフォルトに設定している。
マイクロポストの画像投稿
マイクロポストの基本はこれで実装できた。
これにプラスで画像投稿もできるようにする。投稿した画像を扱ったり、その画像をMicropostモデルと関連付けするために、今回は
CarrierWaveという画像アップローダーを使う。まずはcarrierwave gemをGemfileに追加し、mini_magick gemとfog gemsも含めて追加しておく。
これらのgemは画像をリサイズしたり、本番環境で画像をアップロードするために使う。
source 'https://rubygems.org' . . gem 'carrierwave', '1.2.2' gem 'mini_magick', '4.7.0' . . group :production do gem 'fog', '1.42' .次に、いつものように
bundle installを実行する。$ bundle installCarrierWaveを導入すると、Railsのジェネレーターで画像アップローダーが生成できるようになる。
$ rails generate uploader Picture次に必要となるpicture属性をMicropostモデルに追加するために、マイグレーションファイルを生成し、開発環境のデータベースに適用させる。
$ rails generate migration add_picture_to_microposts picture:string $ rails db:migrateCarrierWaveに画像と関連付けたモデルを伝えるためには、
mount_uploaderというメソッドを使う。このメソッドは、引数に属性名のシンボルと生成されたアップローダーのクラス名を取る。
app/models/micropost.rbclass Micropost < ApplicationRecord belongs_to :user default_scope -> { order(created_at: :desc) } #追加 mount_uploader :picture, PictureUploader validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } endシステムによっては、ここで一旦Railsサーバーを再起動させる必要がある。
マイクロポスト投稿フォームに画像アップローダーを追加するにはfile_fieldタグを含める必要があり、以下のようにする。
app/views/shared/_micropost_form.html.erb<%= form_for(@micropost) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <span class="picture"> #追加 <%= f.file_field :picture %> </span> <% end %>最後に、Webから更新できる許可リストにpicture属性を追加する。
追加すると、micropost_paramsメソッドは次の通り。app/controllers/microposts_controller.rbclass MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] before_action :correct_user, only: :destroy . . . private def micropost_params params.require(:micropost).permit(:content, :picture) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end end一度画像がアップロードされれば、Micropostパーシャルのimage_tagヘルパーでその画像を描画できるようになる。
また、画像の無い (テキストのみの) マイクロポストでは画像を表示させないようにするために、
picture?という論理値を返すメソッドを使う。このメソッドは、画像用の属性名に応じて、CarrierWaveが自動的に生成してくれるメソッドとなる。
app/views/microposts/_micropost.html.erb<li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"> <%= micropost.content %> #追加 <%= image_tag micropost.picture.url if micropost.picture? %> </span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <% if current_user?(micropost.user) %> <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li>画像の検証
上記のままではアップロードされた画像に対する制限がないため、もしユーザーが巨大なファイルを上げたり、無効なファイルを上げると問題が発生してしまう。
この欠点を直すために、画像サイズやフォーマットに対するバリデーションを実装し、サーバー用とクライアント (ブラウザ) 用の両方に追加するようにする。
最初のバリデーションでは、有効な画像の種類を制限していくが、これはCarrierWaveのアップローダーの中に既にヒントがある。
生成されたアップローダーの中にコメントアウトされたコードがありますが、ここのコメントアウトを取り消すことで、画像のファイル名から有効な拡張子 (PNG/GIF/JPEGなど) を検証することができるようになる。
app/uploaders/picture_uploader.rbclass PictureUploader < CarrierWave::Uploader::Base storage :file # アップロードファイルの保存先ディレクトリは上書き可能 # 下記はデフォルトの保存先 def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # アップロード可能な拡張子のリスト def extension_whitelist %w(jpg jpeg gif png) end end2つ目のバリデーションでは、画像のサイズを制御する。
これは
Micropostモデルに書き足してく。
先ほどのバリデーションとは異なり、ファイルサイズに対するバリデーションはRailsの既存のオプション (presenceやlengthなど) にはない。
したがって、今回は手動でpicture_sizeという独自のバリデーションを定義する。app/models/micropost.rbclass Micropost < ApplicationRecord belongs_to :user default_scope -> { order(created_at: :desc) } mount_uploader :picture, PictureUploader validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } #追加 validate :picture_size private # アップロードされた画像のサイズをバリデーションする def picture_size if picture.size > 5.megabytes errors.add(:picture, "should be less than 5MB") end end endこのvalidateメソッドでは、引数にシンボル (:picture_size) を取り、そのシンボル名に対応したメソッドを呼びだす。
次に上で定義したバリデーションをビューに読み込むために、クライアント側に2つの処理を追加する。
まずfile_fieldタグにacceptパラメータを付与し、大きすぎるファイルサイズに対して警告を出すために、ちょっとしたJavaScript(jQuery)を書き加えることでバリデーションが動くようになる。
app/views/shared/_micropost_form.html.erb<%= form_for(@micropost) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <span class="picture"> #追加 <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %> </span> <% end %> #追加 <script type="text/javascript"> $('#micropost_picture').bind('change', function() { var size_in_megabytes = this.files[0].size/1024/1024; if (size_in_megabytes > 5) { alert('Maximum file size is 5MB. Please choose a smaller file.'); } }); </script>###画像のリサイズ
ファイルサイズに対するバリデーションはできたから、さいごに画像サイズ (縦横の長さ) に対する制限を追加する。
画像をリサイズするためには、画像を操作するプログラムが必要になる。
今回はImageMagickというプログラムを使うので、これを開発環境にインストールする。$ sudo yum install -y ImageMagick次に、
MiniMagickというImageMagickとRubyを繋ぐgemを使って、画像をリサイズを試みる。今回は
resize_to_limit: [400, 400]という方法を使用しこれは、縦横どちらかが400pxを超えていた場合、適切なサイズに縮小するオプションとなる。app/uploaders/picture_uploader.rbclass PictureUploader < CarrierWave::Uploader::Base #追加 include CarrierWave::MiniMagick process resize_to_limit: [400, 400] storage :file # アップロードファイルの保存先ディレクトリは上書き可能 # 下記はデフォルトの保存先 def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # アップロード可能な拡張子のリスト def extension_whitelist %w(jpg jpeg gif png) end endこれで画像に対する制御もすみ、マイクロポスト機能を実装することができた。
さいごに
今回はマイクロポスト機能の実装がメインとなりプラスアルファで画像投稿機能を実装できた。
残るは14章のフォロー機能だけとなる。
次↓
https://qiita.com/jonnyjonnyj1397/items/91c50bb5ac1d48bc29d3
- 投稿日:2019-03-27T08:29:34+09:00
14日目:Scaffoldで作成したサイトにgem devise等を組み合わせていく
今週からは、scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。
やった事
- Railsの命名規則(単数形と複数形)
- DBのカラム定義を後から変更
- render partial: 部分テンプレの参照
- validation
- UNSIGNEDという型が存在しないPostgreSQL
使用環境
- ホストOS: Windows10 Home
- 仮想環境: Ubuntu Bento/Bionic
- Ruby:2.51
- Rails: 5.2.2
- gem 'devise' : ログイン等の機能用
- gem 'kaminari' : ページネーション
- DB: PostgreSQL
Railsの命名規則(単数形と複数形)
rails gコマンドで、controller名やmodel名を指定する際に、混乱した。
# rails generate scaffold model名の単数形 フィールド名の型と並び # rails g controller controller名の複数形 # カラムの追加 # rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
- model名は単数形で、頭文字を大文字にする
- scaffoldの場合、modelが基準
- modelは設計書であり、(テーブル1つに付き)1つなため
- controller名は複数形で、頭文字を大文字にする。
- 1つのcontrollerに複数のactionが含まれるため
DBのカラム定義を後から変更
rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。
修正方法:app/db/migrate下のファイルを修正
app/db/migrate/20190326030303_create_club_students.rbclass CreateClubStudents < ActiveRecord::Migration[5.2] def change create_table :club_students do |t| #スペリングミス #t.refereces :student #訂正分 t.references :student t.references :club, foreign_key: true t.timestamps end end endなお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。mysql# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義 ALTER TABLE ClubStudent MODIFY COLUMN student referencesつまり、原因の根本的な部分を修正できないので、駄目
render partial: 部分テンプレの参照
render レンダリング(render) - railsドキュメント
全てのページのヘッダー(上部)に、ログアウトや他のstudentやclubs等のリンクを乗せる
共通して表示させるので、/app/views/layouts/application.html.erb を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり/app/views/layouts/application.html.erb<body> # <%= render :partial => '部分テンプレ名' %> <%= render :partial => 'shared/header' %> </body>表示させたいリンクを書きこむ。
/app/views/shared/_header.html.erb<%= link_to 'Student list', students_path %> <%= link_to 'subjects list', subjects_path %> <%= link_to 'clubs list', clubs_path %> <%= link_to 'exam_result list', exam_results_path %> <%= link_to 'club_stdent list', club_students_path %> <%= link_to 'Log Out', destroy_student_session_path, method: :delete %>validation
参考リンク Active Record Validations
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。validate条件
空でないこと
validates :name, presence: true # 因みに、空が条件ならば # validates :name, absence: true入力文字の長さ
文字の最大長は、データ型を要参照。varcharなら255文字まで
/app/models/club.rb# 2文字以上 validates :name, length:{minimum:2} # 255文字以上 validates :name, length:{maximum:255}exclusion含まない
授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる
/app/models/club.rbvalidates :name, exclusion: { in: %w(部 サークル) } # 『含む』ならinclusion実装結果
空白や文字列長、『サークル』という語には、validatesが発動した
ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわしClub was successfully updated. Name: サークル部type "unsigned" does not exist (※Postgresql)
validatesの実装していく最中に、エラーに気づいた
studentのeditページで更新すると、
ActiveRecord::StatementInvalid in StudentsController#show PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.idと、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showはapp/controllers/students_controller.rb#showdef show @students = Student.joins(:subjects) .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id') .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score') .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio') .where(id: params[:id]) avg_result = Student.joins(:subjects) .select('subjects.id as subject_id') .select('CAST(AVG(exam_results.score) as unsigned) as avg_score') .select('MAX(exam_results.score) as max_score') .select('MIN(exam_results.score) as min_score') .group('subjects.id') .order('subjects.id') (以下略)因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。unsigned (MySQL)
- MySQLにおいては正と負の整数を扱うことができる。
- unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
- unsignedにした値が負になると、エラーを起こす
- UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
- CASTで一時的に型を変える事で回避は可能。
Postgresqlにはunsined型は存在しない(最重要)対応策
まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども
- unsignedをint等の型に置き換える
- 今回は試験点数を扱っていて、intで事足りると思われる。
- ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
- 扱う数によっては、intより1つ上ののbigintに変える必要がある
- CAST as unsignedの部分を消す
- MySQLでCAST as unsingedは、一時的に型を指定している
前回の大学データに倣って、今回はcast as intに変更した
app/controllers/students_controller.rb# (該当部分だけ抜き出し) .select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio') .select('CAST(AVG(exam_results.score) as int) as avg_score')正常に、studentデータのedit、updateが機能した。
データ入力にはpassword情報が必要
deviseの関係上、パスワード情報入りのデータでないと、コンソールから入力できない。
passwordカラムの追加
deviseのモデル等がある、Studentテーブルに、パスワードカラムを追加した。
terminal# rails generate migration AddカラムToモデル名の複数形 フィールド名と並び rails g migration AddPasswordToStudents password:stringdb/migrate下にファイルが生成される
/db/migrate/20190327144825_add_password_to_students.rbclass AddPasswordToStudents < ActiveRecord::Migration[5.2] def change add_column :students, :password, :integer end endこれで、パスワード情報入りの生徒データをDBに入力できる。
データ入力
未だデータの無い、生徒データと試験結果データをコンソールで入力した。
console(1..100).each do |num| if num % 2 == 0 && num % 3 ==0 gen = 0 ag = 1 elsif num % 2 == 0 gen = rand(2) ag = rand(3) else gen = 1 ag = 0 end op = (1..10).map{('あ'..'わ').to_a[rand(26)]}.join nm = (1..3).map{('あ'..'わ').to_a[rand(26)]}.join user = Student.create!(name: "#{nm}", email: "#{nm}-#{rand(98)}@gmail.com", gender: gen, age: ag, opinion: op,password: 'password') endconsole(1..100).each do |i| student = Student.find(i) 1.upto(rand(0..4)) do student.clubs << Club.find(rand(1..14)) student.save end end
- 投稿日:2019-03-27T08:29:34+09:00
大学データとログイン機能gem deviseを組み合わせる
ここ3週間の一区切りとして、今週は
scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。前回までのdeviseを使ったサイトに、大学データを一部コピーして、作成する予定だったが、
Active Record等がエラーを起こしたため、今回はrails newから作成した。
やってることの多くは以前のと重複しているので、詰まった所、初めてな所を、今日は書く。やった事
- #Railsの命名規則(単数形と複数形)
- DBのカラム定義を後から変更
- render partial: 部分テンプレの参照
- validation
- Postgresqlにはunsined型は存在しない
使用環境
- ホストOS: Windows10 Home
- 仮想環境: Ubuntu Bento/Bionic
- Ruby:2.51
- Rails: 5.2.2
- gem 'devise' : ログイン等の機能用
- gem 'kaminari' : ページネーション
- DB: PostgreSQL
Railsの命名規則(単数形と複数形)
rails gコマンドで、controller名やmodel名を指定する際に、混乱した。
# rails generate scaffold model名の単数形 フィールド名の型と並び # rails g controller controller名の複数形 # カラムの追加 # rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
- model名は単数形で、頭文字を大文字にする
- scaffoldの場合、modelが基準
- modelは設計書であり、(テーブル1つに付き)1つなため
- controller名は複数形で、頭文字を大文字にする。
- 1つのcontrollerに複数のactionが含まれるため
DBのカラム定義を後から変更
rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。
修正方法:app/db/migrate下のファイルを修正
app/db/migrate/20190326030303_create_club_students.rbclass CreateClubStudents < ActiveRecord::Migration[5.2] def change create_table :club_students do |t| #スペリングミス #t.refereces :student #訂正分 t.references :student t.references :club, foreign_key: true t.timestamps end end endなお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。mysql# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義 ALTER TABLE ClubStudent MODIFY COLUMN student referencesつまり、原因の根本的な部分を修正できないので、駄目
render partial: 部分テンプレの参照
render レンダリング(render) - railsドキュメント
全てのページのヘッダー(上部)に、ログアウトや他のstudentやclubs等のリンクを乗せる
共通して表示させるので、/app/views/layouts/application.html.erb を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり/app/views/layouts/application.html.erb<body> # <%= render :partial => '部分テンプレ名' %> <%= render :partial => 'shared/header' %> </body>表示させたいリンクを書きこむ。
/app/views/shared/_header.html.erb<%= link_to 'Student list', students_path %> <%= link_to 'subjects list', subjects_path %> <%= link_to 'clubs list', clubs_path %> <%= link_to 'exam_result list', exam_results_path %> <%= link_to 'club_stdent list', club_students_path %> <%= link_to 'Log Out', destroy_student_session_path, method: :delete %>validation
参考リンク Active Record Validations
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。validate条件
空でないこと
validates :name, presence: true # 因みに、空が条件ならば # validates :name, absence: true入力文字の長さ
文字の最大長は、データ型を要参照。varcharなら255文字まで
/app/models/club.rb# 2文字以上 validates :name, length:{minimum:2} # 255文字以上 validates :name, length:{maximum:255}exclusion含まない
授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる
/app/models/club.rbvalidates :name, exclusion: { in: %w(部 サークル) } # 『含む』ならinclusion実装結果
空白や文字列長、『サークル』という語には、validatesが発動した
ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわしClub was successfully updated. Name: サークル部type "unsigned" does not exist (※Postgresql)
validatesの実装していく最中に、エラーに気づいた
studentのeditページで更新すると、
ActiveRecord::StatementInvalid in StudentsController#show PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.idと、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showはapp/controllers/students_controller.rb#showdef show @students = Student.joins(:subjects) .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id') .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score') .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio') .where(id: params[:id]) avg_result = Student.joins(:subjects) .select('subjects.id as subject_id') .select('CAST(AVG(exam_results.score) as unsigned) as avg_score') .select('MAX(exam_results.score) as max_score') .select('MIN(exam_results.score) as min_score') .group('subjects.id') .order('subjects.id') (以下略)因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。unsigned (MySQL)
- MySQLにおいては正と負の整数を扱うことができる。
- unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
- unsignedにした値が負になると、エラーを起こす
- UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
- CASTで一時的に型を変える事で回避は可能。
Postgresqlにはunsined型は存在しない(最重要)対応策
まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども
- unsignedをint等の型に置き換える
- 今回は試験点数を扱っていて、intで事足りると思われる。
- ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
- 扱う数によっては、intより1つ上ののbigintに変える必要がある
- CAST as unsignedの部分を消す
- MySQLでCAST as unsingedは、一時的に型を指定している
前回の大学データに倣って、今回はcast as intに変更した
app/controllers/students_controller.rb# (該当部分だけ抜き出し) .select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio') .select('CAST(AVG(exam_results.score) as int) as avg_score')正常に、studentデータのedit、updateが機能した。
- 投稿日:2019-03-27T08:29:34+09:00
Scaffoldで作成したサイトにgem devise等を組み合わせていく
今週からは、scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。
やった事
- Railsの命名規則(単数形と複数形)
- DBのカラム定義を後から変更
- render partial: 部分テンプレの参照
- validation
- UNSIGNEDという型が存在しないPostgreSQL
使用環境
- ホストOS: Windows10 Home
- 仮想環境: Ubuntu Bento/Bionic
- Ruby:2.51
- Rails: 5.2.2
- gem 'devise' : ログイン等の機能用
- gem 'kaminari' : ページネーション
- DB: PostgreSQL
Railsの命名規則(単数形と複数形)
rails gコマンドで、controller名やmodel名を指定する際に、混乱した。
# rails generate scaffold model名の単数形 フィールド名の型と並び # rails g controller controller名の複数形 # カラムの追加 # rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
- model名は単数形で、頭文字を大文字にする
- scaffoldの場合、modelが基準
- modelは設計書であり、(テーブル1つに付き)1つなため
- controller名は複数形で、頭文字を大文字にする。
- 1つのcontrollerに複数のactionが含まれるため
DBのカラム定義を後から変更
rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。
修正方法:app/db/migrate下のファイルを修正
app/db/migrate/20190326030303_create_club_students.rbclass CreateClubStudents < ActiveRecord::Migration[5.2] def change create_table :club_students do |t| #スペリングミス #t.refereces :student #訂正分 t.references :student t.references :club, foreign_key: true t.timestamps end end endなお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。mysql# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義 ALTER TABLE ClubStudent MODIFY COLUMN student referencesつまり、原因の根本的な部分を修正できないので、駄目
render partial: 部分テンプレの参照
render レンダリング(render) - railsドキュメント
全てのページのヘッダー(上部)に、ログアウトや他のstudentやclubs等のリンクを乗せる
共通して表示させるので、/app/views/layouts/application.html.erb を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり/app/views/layouts/application.html.erb<body> # <%= render :partial => '部分テンプレ名' %> <%= render :partial => 'shared/header' %> </body>表示させたいリンクを書きこむ。
/app/views/shared/_header.html.erb<%= link_to 'Student list', students_path %> <%= link_to 'subjects list', subjects_path %> <%= link_to 'clubs list', clubs_path %> <%= link_to 'exam_result list', exam_results_path %> <%= link_to 'club_stdent list', club_students_path %> <%= link_to 'Log Out', destroy_student_session_path, method: :delete %>validation
参考リンク Active Record Validations
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。validate条件
空でないこと
validates :name, presence: true # 因みに、空が条件ならば # validates :name, absence: true入力文字の長さ
文字の最大長は、データ型を要参照。varcharなら255文字まで
/app/models/club.rb# 2文字以上 validates :name, length:{minimum:2} # 255文字以上 validates :name, length:{maximum:255}exclusion含まない
授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる
/app/models/club.rbvalidates :name, exclusion: { in: %w(部 サークル) } # 『含む』ならinclusion実装結果
空白や文字列長、『サークル』という語には、validatesが発動した
ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわしClub was successfully updated. Name: サークル部type "unsigned" does not exist (※Postgresql)
validatesの実装していく最中に、エラーに気づいた
studentのeditページで更新すると、
ActiveRecord::StatementInvalid in StudentsController#show PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.idと、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showはapp/controllers/students_controller.rb#showdef show @students = Student.joins(:subjects) .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id') .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score') .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio') .where(id: params[:id]) avg_result = Student.joins(:subjects) .select('subjects.id as subject_id') .select('CAST(AVG(exam_results.score) as unsigned) as avg_score') .select('MAX(exam_results.score) as max_score') .select('MIN(exam_results.score) as min_score') .group('subjects.id') .order('subjects.id') (以下略)因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。unsigned (MySQL)
- MySQLにおいては正と負の整数を扱うことができる。
- unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
- unsignedにした値が負になると、エラーを起こす
- UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
- CASTで一時的に型を変える事で回避は可能。
Postgresqlにはunsined型は存在しない(最重要)対応策
まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども
- unsignedをint等の型に置き換える
- 今回は試験点数を扱っていて、intで事足りると思われる。
- ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
- 扱う数によっては、intより1つ上ののbigintに変える必要がある
- CAST as unsignedの部分を消す
- MySQLでCAST as unsingedは、一時的に型を指定している
前回の大学データに倣って、今回はcast as intに変更した
app/controllers/students_controller.rb# (該当部分だけ抜き出し) .select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio') .select('CAST(AVG(exam_results.score) as int) as avg_score')正常に、studentデータのedit、updateが機能した。
- 投稿日:2019-03-27T08:23:10+09:00
NginxでSSLとロードバランサーの設定
はじめに
Ruby on Railsのフロントウェブサーバとしてnginxを使用するときのSSLとロードバランサー(upstream)の設定について記述します。
ブラウザ --https--> nginx --http--> Ruby on Rails(unicorn,pumaなど)
前提
OSはCentOS7を使用
SSL証明書はあらかじめ用意しておきます。/etc/pki/tls/certs/server.crt /etc/pki/tls/private/server.keynginxのインストールはこちらを参考させていただきました。
nginxのインストール
上記の参考のとおり、yumでインストールしました。
yum.repos.dの設定
$ sudo vi /etc/yum.repos.d/nginx.repo ...インストール
$ sudo yum install nginx ...インストールしたnginxのバージョンは
nginx.x86_64 1:1.15.10-1.el7_4.ngx
です。nginxの起動と自動起動設定
systemctlで自動起動設定と起動します。
$ sudo systemctl enable nginx Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service. $ sudo systemctl start nginx $ここから本題
タイトルのとおり、SSLとロードバランサーの設定を行います。
$ sudo vi /etc/nginx/conf.d/ssl_lb.conf --- upstream backend { server localhost:3000; #バックエンドプロセスは同じサーバ内で起動している前提、ポート3000 } server { listen 443 ssl; server_name sample.co.jp; ssl_certificate /etc/pki/tls/certs/server.crt; ssl_certificate_key /etc/pki/tls/private/server.key; root /path/to/root; #コンテンツのルートパスを設定 location / { try_files $uri @backend; } location @backend { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://backend; } }upstream
upstreamに振り分ける対象のサービスを記載します。(ロードバランサー)
複数記述することでロードバランスされます。
また、unix socket接続の記載も可能です。server
ssl_certificate、ssl_certificate_keyにSSL証明書のファイルを指定します。
upstreamで設定したbackendは"location @backend"に設定します。nginxの再起動
nginxを再起動することで上記の設定が反映されます。
$ sudo systemctl restart nginx
- 投稿日:2019-03-27T02:50:02+09:00
gitを使用したブランチ作成からpushまでの簡単な流れ
ずっとgithub desktopを使用していましたが、さすがにコマンドでも操作できた方がいいだろうと思い、色々コマンドを調べて、ざっくりとした使い方がわかってきたので流れを簡潔に書きます。
gitを使用したブランチ作成からpushまでの簡単な流れ
作業を開始するために、、、
git branch -a #今いるブランチを確認 (-aをつけることでリモートブランチも見れる)git branch ブランチ名 #ブランチ作成git checkout ブランチ名 #ブランチ移動ファイルを編集したら、、、
git status #ファイルの編集状態確認git add . #コミットしたいファイル追加("."ではなくディレクトリを指定する事で個別に追加する事ができる) # 例git add app #appディレクトリのファイル変更分のみコミット対象にするgit commit -m "コミットコメント" #addしたファイルをコミットgit push origin HEAD git push origin ブランチ名 #コミットをリモートにpushする。HEADと記述するとわざわざブランチ名を書かなくてよくなるpushしたcommitをmasterにマージできたら、、、
git checkout master #masterブランチに移動git pull #リモートからマージしたファイルを取得する備忘録として書きました。細かい説明はいらないけど、とりあえず使いたいという人向けにこれからブラッシュアップしていくつもりです。
最初、commitとpushの違いがわからなかったのでそれについても記載したいです。
- 投稿日:2019-03-27T02:50:00+09:00
Rails勉強ネタ resources で名前付きヘルパー変わること確認
普段何気なくroutes.rbにてresourcesメソッドを使用しているが、
色々確認する。usersのコントローラーを作成(定番の7つのメソッド)
$ rails g controller users index new show create edit update destroyusers_controller.rbclass UsersController < ApplicationController def index end def new end def show end def create end def edit end def update end def destroy end endRails.application.routes.draw do get 'users/index' get 'users/new' get 'users/show' get 'users/create' get 'users/edit' get 'users/update' get 'users/destroy' endこの状態で
rails routesを実行すると。$ rails routes Prefix Verb URI Pattern Controller#Action users_index GET /users/index(.:format) users#index users_new GET /users/new(.:format) users#new users_show GET /users/show(.:format) users#show users_create GET /users/create(.:format) users#create users_edit GET /users/edit(.:format) users#edit users_update GET /users/update(.:format) users#update users_destroy GET /users/destroy(.:format) users#destroy全部users_'アクション名'という名前付きヘルパーになって、メソッドは全部GETです。
(空気を読んでPOSTとかDELETEなどになるのかと思っていた・・・)resoureceメソッド使う
routes.rbRails.application.routes.draw do # get 'users/index' # get 'users/new' # get 'users/show' # get 'users/create' # get 'users/edit' # get 'users/update' # get 'users/destroy' resources :users endこの状態で
rails routes実行すると$ rails routes WARNING: Nokogiri was built against LibXML version 2.9.7, but has dynamically loaded 2.9.4 Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroyRestfulな形になった。
ニュアンス的にcreateが user_path のPOSTなイメージがあるが、
users_pathである事に注意(間違えそう)。
- 投稿日:2019-03-27T02:50:00+09:00
Rails勉強ネタ resources での名前付きヘルパー確認
普段何気なくroutes.rbにてresourcesメソッドを使用しているが、
色々確認する。usersのコントローラーを作成(定番の7つのメソッド)
$ rails g controller users index new show create edit update destroyusers_controller.rbclass UsersController < ApplicationController def index end def new end def show end def create end def edit end def update end def destroy end endroutes.rbRails.application.routes.draw do get 'users/index' get 'users/new' get 'users/show' get 'users/create' get 'users/edit' get 'users/update' get 'users/destroy' endこの状態で
rails routesを実行すると。$ rails routes Prefix Verb URI Pattern Controller#Action users_index GET /users/index(.:format) users#index users_new GET /users/new(.:format) users#new users_show GET /users/show(.:format) users#show users_create GET /users/create(.:format) users#create users_edit GET /users/edit(.:format) users#edit users_update GET /users/update(.:format) users#update users_destroy GET /users/destroy(.:format) users#destroy全部users_'アクション名'という名前付きヘルパーになって、メソッドは全部GETです。
(空気を読んでPOSTとかDELETEなどになるのかと思っていた・・・)resoureceメソッド使う
routes.rbRails.application.routes.draw do # get 'users/index' # get 'users/new' # get 'users/show' # get 'users/create' # get 'users/edit' # get 'users/update' # get 'users/destroy' resources :users endこの状態で
rails routes実行すると$ rails routes Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroyRestfulな形になった。
ニュアンス的にcreateが user_path のPOSTなイメージがあるが、
users_pathである事に注意(間違えそう)。
- 投稿日:2019-03-27T01:16:13+09:00
技術選定まとめ
技術選定まとめ
おはこんにちばんわ
マンハッタンコードエンジニアのがーたろーです。今回プロジェクトで技術選定を行いました。
どのような観点、要素で技術を選定するべきなのかを学んだのでまとめたいと思います。技術選定に必要なもの
観点:エンジニア、開発チームが作業するのに最適だと思えるものを選定すること
上記の最適だと思えるものには
- 目的
- メリット
- できることの比較
これらを満たしていることが前提になります。
今回選定の候補に選ばれた言語は
1. Kotlin/SpringBoot
2. ruby/Ruby on Rails
3. php/Laravelの三つから選びます。
目的
API通信、管理画面、複数のサーバーとデータ通信をするためのサーバーを作る
オニオンアーキテクチャ、DDDの理解、学習
GraphQL使いたい
テスト駆動メリット&デメリット
kotlin/SpringBoot
メリット
今回のプロジェクトではアプリ側(スマートフォン)の開発も行うため、Andoroidの開発にKotlinを用いる。
そのため学習する言語がSwift+Kotlinの2言語で済む。
DDD(ドメイン駆動開発)と親和性が高いデメリット
インフラにかかる影響の判断が他の2言語と比べて手間がかかる。(tomcatの設定周り)
サーバーでKotlin(Java)を使う場合、Tomcatが必須になる。
Tomcatを利用する場合、Nginxとは異なりユーザーの最大同時接続数の管理がスレッドをいくつまで立てられるかの設定になる。
何スレッドまで利用するのかはサーバースペックを元に試算しなければ行けないためサーバーのスペック調査
設定数のMaxギリギリまで接続した場合の挙動のテストをして性能を担保しないといけない(やること増える)Ruby/Ruby on Rails
メリット
プロジェクトにRuby on Railsを触ったことのあるエンジニアが3人いる。
ディレクトリ構造など規約があるため、設計の話し合いコストが減る。
ライブラリがとにかく便利(使ったことあるやつがそのまま使えそう)デメリット
DDDとrailsの作りがおそらくケンカする。
DDDでやりたいことがrailsに合わせないと、そもそも作れなくなるためサーバー側だけ全体と作りが変わってきてしまう。PHP/Laravel
メリット
自由
DDDと親和性がある
railsを参考に作っているためライブラリの使い方が似ていそう。
ローカルでの環境作成が容易なため、開発に入るまでのスピードが早い。デメリット
規約、設計を設けないと開発現場が魔界と化す。できることの比較
候補 サーバ設定の難易度 GraphQL DDD 規約 テスト Kotlin/SpringBoot 性能試験が必要 チューニング OK ◉ ktlint Spek Mockit Ruby/Ruby on Rails nginx(むずかしくない) OK △ Rubocop Rspec PHP/Laravel nginx(むずかしくない) OK ○ PSR phpUnit phpspefc ミドルウェアはnginxを全部使う想定。
Kotlinだけ考えること増える。
規約、テストは使うものが違うだけで問題はない。ぶっちゃけどれ使いたい?
ここまで議論した結果、要件はどれでも実現可能
学習コスト的にはKotlinサーバーの書き方はAndroidとは異なる
やったことある人が多いのはメリットだが少ないのはデメリットではない(世界で使っている人が少ないのは別)
DDD・オニオンアーキテクチャに沿った開発をしたい。やらないともったいない。
↑これに力を入れたいのでデメリット=インフラ課題を増やしたくはない上記のことを考慮した結果
選ばれたのはPHP/Laravelでした
まとめ
今回私は開発チームの一員として使用する言語の技術選定を行いました。
その中で、何を観点に持たないといけないのか、懸念点、優先すべき事柄、そういった判断基準を身に着けるいい機会を頂けました。
この話をするときにもっといろんな知識があったらもっと別の視点でも話ができたんだろうなぁ、と次にする際にはいろんな知識を身につけてから挑戦したいなぁ、と思いました。インフラって全くワカンねぇなぁ。
ファンを増やしたいのでもしこの記事いいなぁ、お前やるじゃんとか思ったらファンメッセージください!
もし会社のHPに載っけてもいいぜって人いたらどうぞよろしくお願いします!
- 投稿日:2019-03-27T00:58:32+09:00
[備忘録] RDSで外部サーバからアクセス許可する方法
経緯
開発環境でVagrantを使用していて、EC2を経由せず、ローカル環境からRDSを接続し
データベースの操作を行いたいと思ったから。
今回はMySQLを例に書いていきます。RDSとは
「Amazon Relational Database Service (通称:RDS)」はAmazonの提供する、リレーショナルデータベース構築サービスことです。
より詳しい情報は下にリンクを貼っておきます。
https://dev.classmethod.jp/cloud/aws/cm-advent-calendar-2015-aws-re-entering-rds/#overviewRDSの設定
設定にパブリックアクセシビリティという項目があるので「はい」に変更する。
新規設定のときは「[詳細設定]の設定」、既存DBの設定変更のときは設定変更を選択後の一覧画面に表示される。
下の画像は新規設定の時の画面です。
セキュリティグループの設定
MySQLに紐づくセキュリティグループの設定を行う。
VPCセキュリティグループのリンクをクリックする。
セキュリティグループに接続許可するサーバーや接続したい場所のIPアドレスを追加する。
※今回は、「0.0.0.0」にしてますが、これは全てのIPアドレスからの接続許可することになるので、現在使用しているIPのみ許可する方がセキュリティ的にいいと思います。
- 投稿日:2019-03-27T00:27:06+09:00
CentOS7にRubyをインストール
いつも忘れがちなので、自分用のメモも兼ねてインストール方法を投稿します。
前準備
CentOS7はMinimal ISOでインストール済みであること
必要なパッケージのインストール
$ sudo yum -y install git bzip2 gcc gcc-c++ openssl-devel readline-devel zlib-devel $ sudo yum -y install epel-release $ sudo yum -y install nodejsrbenvをダウンロード
$ sudo git clone https://github.com/rbenv/rbenv.git /usr/local/rbenv $ sudo git clone https://github.com/rbenv/ruby-build.git /usr/local/rbenv/plugins/ruby-buildrbenvの環境設定
/etc/profile.d/rbenv.shを作成し、以下の内容を追加します。
export RBENV_ROOT=/usr/local/rbenv export PATH=${RBENV_ROOT}/bin:$PATH eval "$(rbenv init --no-rehash -)"rootユーザで以下の実行が必要
# source /etc/profile.d/rbenv.shsudoでrbenvを実行するためにsudorderにRBENV_ROOTとsecure_pathの追記が必要
# visudo ... Defaults env_keep += "RBENV_ROOT" Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/rbenv/bin:/usr/local/rbenv/shims ...Rubyのインストール
バージョンをチェック
$ rbenv install -l ... 2.6.0-rc1 2.6.0-rc2 2.6.0 2.6.1 2.6.2 2.7.0-dev jruby-1.5.6 ...インストール実行
バージョン2.6.2をインストールしてみます。
$ sudo rbenv install 2.6.2使用するRubyのバージョンを設定します。
$ sudo rbenv global 2.6.2 $ sudo rbenv rehashこれでインストール完了です。
後処理
必要なgemをインストール
bundlerはRailsを動かすために最低限必要なのでインストールします。
$ sudo gem install bundler --no-doc















