- 投稿日:2019-05-12T23:11:07+09:00
Railsのattributeはバリデーションではなくキャスト
どうも、ITのんびりです
モデルに書くattributeの意味がいまいち分かっていなかったので、調べてみました
使用例
attribute :id, :integerこんな感じでパラメーター名と型を書きますよね
最近まで意味がよく分からず、「idのデータ型をintegerに限定しているのかなあ」と何となく思っていましたバリデーションではなくキャスト
attributeはバリデーションを設定しているのではなく、指定した型以外のデータが送られてきたら指定した型に変換(キャスト)します
attribute(name, cast_type = Type::Value.new, **options)cast_typeで指定したtypeではない型のデータが飛んできたら指定したtypeにcastします
attribute :id, :integerintegerで指定して10.1がきたら10にcastします
attribute :flag, :boolboolで指定して"false"という文字列が飛んできたらbooleanのfalseにcastします
バリデーションとの順番
attributeでキャストしてからvalidationが判定されるようです
attribute :id, :integer validates :id, numericality: { only_integer: true }idを整数にcastして、整数のバリデーションをかけるとします
idが10.1で送られてきたら、attributeで10にcastされます
idが10で整数なので、only_integer: trueのバリデーションは通ります整数のバリデーションをかけているのに、10.1が送られてきてバリデーションが通るのはよくないですね(10にcastされているとはいえ)
バリデーションをかけるときは、castの影響を考えてするようにしましょう
参考記事
- 投稿日:2019-05-12T21:40:59+09:00
[Rails] devise token auth を使う
devise token authのチュートリアルのような記事です。
大部分はこの記事を参考にしています。一部、バージョンの違い(?)によるのか、エラーが出る箇所があったため、この記事を作りました。セットアップ
はじめにRailsアプリを作ります。
terminalrails new devise_token_auth_testGemFileにdevise token auth用のgemを追記します。
Gemfile# devise関連 gem 'devise' gem 'devise_token_auth' # CORS設定 gem 'rack-cors'
gem 'rack-cors'を入れる理由については公式DocsのCross Origin Requests (CORS)に載っています。bundle install した後データベースを作成します。
terminalbundle install rake db:createdevise token auth の導入
installします。
通常のdeviseについては、公式Docsにインストールしてくださいとは載っていませんが、rails s時にエラーが出るのでインストールします。terminalrails g devise:install rails g devise_token_auth:install User auth次に、インストール時に作成された
db/migrate/~_devise_token_auth_create_users.rbにTrackableに関するカラムを追記します。
これがないと、サインイン時にUndefined method current_sign_in_at(current_sign_in_atはTrackableに関するカラム)とエラーが出ます。db/migrate/~_devise_token_auth_create_users.rbclass DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.2] def change create_table(:users) do |t| ## Required t.string :provider, :null => false, :default => "email" t.string :uid, :null => false, :default => "" ## Database authenticatable t.string :encrypted_password, :null => false, :default => "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at t.boolean :allow_password_change, :default => false ## Rememberable t.datetime :remember_created_at # ここを追記 -------------------------------------------- ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip # ----------------------------------------------------- ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at ## User Info t.string :name t.string :nickname t.string :image t.string :email ## Tokens t.text :tokens t.timestamps end add_index :users, :email, unique: true add_index :users, [:uid, :provider], unique: true add_index :users, :reset_password_token, unique: true add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end endでは、devise token auth の設定を追加します。
config/initializers/devise_token_auth.rbDeviseTokenAuth.setup do |config| # リクエストごとにトークンを更新するか # 扱いやすいようにFalseにします config.change_headers_on_each _request = false # トークンの有効期間 # デフォルトでは2週間です config.token_lifespan = 2.weeks # ヘッダーの名前対応 config.headers_names = {:'access-token' => 'access-token', :'client' => 'client', :'expiry' => 'expiry', :'uid' => 'uid', :'token-type' => 'token-type' } endまた、
application_controllerに以下を追記してください。controllers/application_controller.rbclass ApplicationController < ActionController::Base include DeviseTokenAuth::Concerns::SetUserByToken skip_before_action :verify_authenticity_token, if: :devise_controller? # APIではCSRFチェックをしない endこれで準備が出来ました。
試す
サーバーを起動して動かしてみましょう。
terminalrails sサインアップ
terminalcurl localhost:3000/api/auth -X POST -d '{"email":"example@example.com", "password":"password", "password_confirmation": "password"}' -H "content-type:application/json"curlを使ってUserを作成するAPI(
localhost:3000/api/auth)を叩いています。
引数の説明は以下の通りです。
-X POSTPOSTでリクエスト
-d 'foo:bar'Bodyに入れるデータ
-H 'foo:bar'ヘッダーに入れるデータ今回は、Bodyに
passwordを 、ヘッダーに、Bodyの中身がjson形式であることを示すための設定content-type:application/jsonを追加しています。サインアップに成功すると以下のようなレスポンスが返ってくるはずです。
terminal{"status":"success","data":{"id":1,"provider":"email","uid":"example@example.com","allow_password_change":false,"name":null,"image":null,"email":"example2@example.com","created_at":"2019-05-12T20:48:24.000+09:00","updated_at":"2019-05-12T20:48:24.000+09:00"}}サインイン
terminalcurl localhost:3000/api/auth/sign_in -X POST -d '{"email":"example@example.com", "password":"password"}' -H "content-type:application/json" -i
-iオプションを付けてレスポンスのヘッダーも表示されるようにしています。
(ここの情報をこの後使います。)成功すると以下のようなレスポンスが返ってくるはずです。
terminalHTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Download-Options: noopen X-Permitted-Cross-Domain-Policies: none Referrer-Policy: strict-origin-when-cross-origin Content-Type: application/json; charset=utf-8 access-token: xzTWhTURMmPdDjaKB2R9gw token-type: Bearer client: S2T6zY0aDB6VBXYVMdpjbg expiry: 1558871460 uid: example@example.com Cache-Control: no-store, must-revalidate, private, max-age=0 {"data":{"id":1,"email":"example@example.com","provider":"email","uid":"example@example.com","allow_password_change":false,"name":null,"image":null}}パスワードの変更
terminalcurl localhost:3000/api/auth/password -X PUT -d '{"password":"password_new", "password_confirmation": "password_new"}' -H "content-type:application/json" -H "access-token: xzTWhTURMmPdDjaKB2R9gw" -H "client: S2T6zY0aDB6VBXYVMdpjbg" -H "uid: example@example.com"パスワードの変更は
POSTではなくPUTを指定します。
また、ログイン時のレスポンスのヘッダーにあったaccess-tokenclientuidをヘッダーに追加しています。成功すると以下のようなレスポンスが返ってくるはずです。
terminal{"success":true,"data":{"id":1,"provider":"email","allow_password_change":false,"email":"example@example.com","uid":"example@example.com","name":null,"image":null,"created_at":"2019-05-12T20:30:50.000+09:00","updated_at":"2019-05-12T21:31:33.000+09:00"},"message":"パスワードの更新に成功しました。"}deviseのコントローラーを編集したい時
terminalrails g controller api/auth/registrationscontrollers/api/auth/registrations_controller.rbclass Api::Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController # hogehoge endconfig/routes.rbRails.application.routes.draw do namespace :api do mount_devise_token_auth_for 'User', at: 'auth', controllers: { registrations: 'api/auth/registrations' } end end
- 投稿日:2019-05-12T21:21:03+09:00
Railsの「annotate」gemが最高すぎる件
環境
- Docker version 18.09.2
- Ruby 2.4.5
- Rails 5.0.0
なぜ書いたのか
「あれ、テーブルってどんな構造してたっけ❓」
っていう時がかなりあった。今まで確認する時は、
- DBに入る
- コマンドぽちぽち入力
- DB抜ける
上記3点のプロセスだったのが、
- Modelのrbファイル確認
annotateをインストールするだけでここまで短縮できることに感動したから
導入、実装
1.Gemfileに追記
Gemfilegroup :development do gem 'annotate' end※基本的に開発環境でのみ使用するので、development内に書く
2.gemをインストール
ターミナル$ docker-compose build3.gemの実行
ターミナル$ docker-compose exec web bundle exec annotateこれでModelを確認すると、下記のような感じでコメントアウトでテーブルの構造が!すごい!
ただこれだとテーブルを作成、更新するたびに実行しなければならないので、
migrationをトリガーに実行するように設定ファイルを生成ターミナルdocker-compose exec web bundle exec rails g annotate:installこれでmigrationの度にannotateが実行されるように?
おまけ
routeにも適用できるらしいのでやってみた
ターミナル$ docker-compose exec web bundle exec annotate --routes参考
https://qiita.com/tsuchinoko_run/items/9bcc15e8992cb237e08a
https://www.udemy.com/share/1014LIB0sacllTQ3s=/
- 投稿日:2019-05-12T21:07:48+09:00
【rails】slickをを使ったスライダーの実装
前置き
今回かなり手こずったrailsでのslickの使用方法について説明します。環境によっては作成方法が違いますので、ご理解頂けますと幸いです。
今回作成したスライダーのGIFが下記URLに添付しております。(課題の成果物となります)
カルーセル・サンプル
かなり初心者向けに作成していますので、「こんにゃの知ってるにゃ!」
と思われる方々向けに作成していません。slickとは
jQueryのプラグインです。様々なスライダーを実装する事ができるので、カルーセル機能の定番としてよく使用されます。
なので、いろんな開発環境でも使用できるように、設置が簡単かつシンプルで便利なのが特徴だと思いました。
何より素晴らしのが、レスポンシブに対応している事だと思います。作成前の確認しておく事項
今回作成するスライダーの機能
・ 矢印をクリックする事で次のページに飛べるようにする
・ 下部分の丸をクリックする事でそのページに飛べるようにする
・ 自動スライダーを実装する
・ 横にスライドさせるとこんな感じになります。
公式サイトとREADMEとこちらのサイトを今回は参考にしています。
slick
slick README
http://uzurea.net/explanation-of-jquery-slider-slick/スライダーの実装
slick
公式サイトの
赤枠部分を使用します。まずファイルをダウンロードしてください。
ファイルの中身は(2019-05-12)の時点でこんな感じです
今回はこの赤枠の[slick]ファイルを使用します。
その中の赤枠ファイルを使用します。ファイルの格納場所
slick-theme.scss
slick.scss
は、これから使用する、Slider・Icons・Arrows・Dots のcssの設定が入っています。
app/assets/stylesheets
の中に格納してください。
slick.min.js
には動作の設定が入っているので
app/assets/javascripts
に格納してください。読み込み準備
app/views/layoutsに先ほどの
の下部分の赤枠のコードを貼り付けます。application.html.haml!!! %html %head = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %link{href: "//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css", rel: "stylesheet", type: "text/css"}/ = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %script{src: "//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js", type: "text/javascript"} %body = yield次にjqueryを使用する為に
gemを作成します。既に作成している場合は飛ばしてくださいませ〜gem 'jquery-rails'application.js//= require jquery //= require jquery_ujs //= require turbolinks //= require_tree . ←これが一番後ろにこないと、うまく動きませんscssのインポートを忘れずに
application.scss@import "slick-theme"; @import "slick";*作成する物が常に動的に更新される想定でしたら、CDNを勉強する事をお勧めします。
ビューの編集
スライダー部分の簡略した書き方が下記になります
index.html.haml%div.slick %div %h3 1 %div %h3 2 %div %h3 3 %div %h3 4 %div %h3 5 %div %h3 6jsの編集
jqの描き方は下記のようになります
carousel.js$(document).on('turbolinks:load', function(){ $('.slick').slick({ autoplay:true, ←自動再生 dots:true, ←ドットの描写 }); });これだけで、最低限必要な実装が作成されます。
buttonとか作成しなくても勝手に作成してくれます。
「便利にゃ〜」
上記で基本は作成できました。最終調整
次に、現在の画面では、矢印(今回はpreviousとnextの部分)やドットの表示が画面外にあるので、画面の中に配置する調整を行います。
この時気をつけて頂きたいことがあります。application.scss@import "slick-theme"; @import "slick"; @import "carousel";編集するscssはダウンロードファイルより後ろに作成してください。
cssは、上書き方式なので、ダウンロードファイルより前にインポートすると
反映されません。先ずは・・・
slick-theme.scss
を見てください。slick-theme.scss/* Arrows */ .slick-prev, .slick-next { position: absolute; display: block; height: 20px; width: 20px; line-height: 0px; font-size: 0px; cursor: pointer; background: transparent; color: transparent; top: 50%; -webkit-transform: translate(0, -50%); -ms-transform: translate(0, -50%); transform: translate(0, -50%); padding: 0; border: none; outline: none; &:hover, &:focus { outline: none; background: transparent; color: transparent; &:before { opacity: $slick-opacity-on-hover; } } &.slick-disabled:before { opacity: $slick-opacity-not-active; } &:before { font-family: $slick-font-family; font-size: 20px; line-height: 1; color: $slick-arrow-color; opacity: $slick-opacity-default; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } } .slick-prev { left: -25px; [dir="rtl"] & { left: auto; right: -25px; } &:before { content: $slick-prev-character; [dir="rtl"] & { content: $slick-next-character; } } } .slick-next { right: -25px; [dir="rtl"] & { left: -25px; right: auto; } &:before { content: $slick-next-character; [dir="rtl"] & { content: $slick-prev-character; } } }長いですが、この部分が矢印のscssになります。
[dir="rtl"]
は属性セレクターです。指定された属性が存在するかどうかや、その値に基づいて要素を選択します。上記は、HTMLのdir属性がrtlの時を選択します。
私は今回矢印を作成せずに実装する事にしています。なので、コンソールからコードを見てもらうとわかるのですが、擬似要素の::beforeは作成されていません。
矢印を作成したやり方でも問題ないのですが、私の場合はコードを短くしたかった・・・
ちなみに、、、slick.jsファイルのオプションの箇所に下記のように記述すれば好きな画像をarrowsに設定できます。
詳しくはこちらのサイトを参考にしてください。
実証していないので、不確かなのですが、css側でも下記の様に変更できるそうなので。試してみるのも良いかもしれません。詳しくはこちらslick.js$(function(){ $("#slider").slick({ prevArrow: '<img src="前への矢印画像のパス" class="slide-arrow prev-arrow">', nextArrow: '<img src="次への矢印画像のパス" class="slide-arrow next-arrow">' }); });carousel.scss.slick-prev{ left: 50px; height: 80px; width: 80px; background-image: image-url("hidari.png"); z-index: 100; background-size: 30px; background-repeat: no-repeat; background-position: center; }私の場合は矢印を背景画像で表示する編集をしました。
left: 50px;で画面の中に表示できる様になるので、先ずは先に・・・その後諸所調整した感じです。.slick-nextも同様に調整してください。HTMLを追加してimage_tagで対応する方法もあります。
&:hover, &:focus,&:beforeは今回は使用しないので削除しました。上記ができれば、ドット部分も簡単に調整できます。
私の作成したコードを添付しますcarousel.scss.slick-dots { bottom: 25px; li button{ &::before{ height: 15px; width: 15px; background-color: rgb(116, 116, 116); border-radius: 50%; } } }私は、、、こんな感じで作成したので、、
参考までに・・・参考にしないでくださいwヒント集
矢印の位置がうまく行かない時は
・position: relative;とposition: absolute;がどうなっているか確認すること。
・display: block;になっているか確認すること
・z-index: 100;が適応されていない可能性があるので、position: relative;から、z-index: 100;に至る間に、position: absolute;が全てに適応されているか確認すること。既存の「←」を消すには
・content: "";
何か間違っている箇所ございましたらコメントくださると大変助かります。
- 投稿日:2019-05-12T20:49:39+09:00
これは便利!1つのコマンドでHaml変換できる【Ruby on Rails】
目次
- gemを追記する(Hamlを導入する)
- html.erbファイルを一括で変換する
gemを追記する(Hamlを導入する)
Gemfilegem 'haml-rails' gem 'erb2haml'※(注意点)テスト環境と開発環境以外に追記する
ターミナル$ bundle installgemを記述したらこのプロセスを忘れてはいけませんでしたね!
html.erbファイルを一括で変換する
$ rake haml:replace_erbsこの処理が終わり、html.erbファイルが下記のようにHaml変換されていれば成功です。
!!! %html %head %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title ChatSpace = csrf_meta_tags = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %body = yieldEND
- 投稿日:2019-05-12T19:18:48+09:00
Railsでアプリ開発〜1日目〜
どんなアプリ?
名前
- schedun(スケジュン)
内容
- 勉強などのスケジュールアプリ。
- 親と子アカウントを作成し、親が出したスケジュールに対して子供が行動。
- 親との会話も可。
今日の目標
環境構築
様子
- どのように環境を構築していくか
- Docker
- CentOS
- Ubuntu
ubuntu
- 参考書通りにインストール → できない!エラー発生! → なし
docker
- dockerのwindows版いれる→ Proじゃないのでできない → なし
centos
- これしかないので頑張ってインストール
1つ目のエラー
Postgresqlを無事にインストールして、
Rails new schedun -d postgresql
としてみました。
そうすると、
pgがありません
と言われたので書いてある通りインストール → まだエラー
小1時間考えて見て、結局、sqliteにしました。次にやること
- それぞれのビュー、コントローラーなどを作成
- 画面のレイアウトを作成
- 投稿日:2019-05-12T18:55:19+09:00
gretelをつかわずにRailsでパンくずリストを作成
パンくずリストとは
gretelというgemを使うとスッキリ作れるみたいなのですが、当時このgemを知らなかったたま力技で作りました。
ページを判断するメソッド
ページごとにリストが違うため、現在どのページなのかを判断するメソッドをつくりました。
app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :request_path def request_path @path = controller_path + '#' + action_name def @path.is(*str) str.map{|s| self.include?(s)}.include?(true) end end endアプリケーションコントローラーにbefore_actionとして設定しているので、ページが遷移するたびにこのメソッドが発動されます。
コードの説明
ページ遷移する際のコントローラー名とアクション名を取得し、@pathに"コントローラー名#アクション名"という形で格納します。
次に引数を配列で取得し、その配列の中に@pathであるものがあるか確かめます。
配列の中にひとつでもtrueがあれば、tureを返しています。メソッドの呼び出しは例えばこのような形です。
- unless @path.is('products#index') .breadcrumbs %ul %li引数に「products#index」を渡しているので、productコントローラーのindexアクションの遷移先であるビューでなければ、breadcrumbsクラスが表示されるようになっています。
完成したパンくずリスト
- unless @path.is('products#index') .breadcrumbs %ul %li = link_to "○○", root_path - if @path.is('categories#index') %li = icon 'fas', 'chevron-right' %li %p.last カテゴリー一覧 - if @path.is('categories#show') && @category.indirect_ids.present? %li = icon 'fas', 'chevron-right' %li = link_to "カテゴリー一覧", categories_path, data: {"turbolinks": false} %li = icon 'fas', 'chevron-right' %li %p.last #{@category.name} - if @path.is('categories#show') && @category.indirect_ids.empty? && @category.children.present? %li = icon 'fas', 'chevron-right' %li = link_to "カテゴリー一覧", categories_path %li = icon 'fas', 'chevron-right' %li = link_to "#{@category.parent.name}", category_path(@category.parent) %li = icon 'fas', 'chevron-right' %li %p.last #{@category.name} - if @path.is('categories#show') && @category.children.empty? %li = icon 'fas', 'chevron-right' %li = link_to "カテゴリー一覧", categories_path %li = icon 'fas', 'chevron-right' %li = link_to "#{@category.parent.parent.name}", category_path(@category.parent.parent) %li = icon 'fas', 'chevron-right' %li = link_to "#{@category.parent.name}", category_path(@category.parent) %li = icon 'fas', 'chevron-right' %li %p.last #{@category.name} - if @path.is('users#show') %li = icon 'fas', 'chevron-right' %li %p.last マイページ - if @path.is('users#edit') %li = icon 'fas', 'chevron-right' %li = link_to "マイページ", user_path(@user) %li = icon 'fas', 'chevron-right' %li %p.last プロフィール - if @path.is('addresses#edit') %li = icon 'fas', 'chevron-right' %li = link_to "マイページ", user_path(@user) %li = icon 'fas', 'chevron-right' %li %p.last 住所変更 - if @path.is('users#credit') %li = icon 'fas', 'chevron-right' %li = link_to "マイページ", user_path(@user) %li = icon 'fas', 'chevron-right' %li %p.last 支払い方法 - if @path.is('users#logout') %li = icon 'fas', 'chevron-right' %li = link_to "マイページ", user_path(@user) %li = icon 'fas', 'chevron-right' %li %p.last ログアウト - if @path.is('searches#index') %li = icon 'fas', 'chevron-right' %li %p.last = "#{@keyword}"ページごとにリストを作っているので、やっぱりコードが長くなってしまいました。
また、categories#showが3つあったので、それぞれ条件を足しています。まとめ
次回からはgemを使おうと思います。
ただ、「コントローラー#アクション」であるかを確かめるメソッドはどこかで役に立つときがきそうです。
- 投稿日:2019-05-12T17:57:56+09:00
Ajax通信によってセレクトボックスを出現させる
概要
Ajax通信を利用し、あるアクションによってセレクトボックスを出現させます。
困ったポイントは、選択するものによってselectタグのoptionの数が違うということです。
今回でいうと、
レディース トップス/パンツ の2つ
メンズ トップス/パンツ/バッグ/靴 の4つ条件によって、ビューに挿入する要素の長さを変えなければいけません。
完成コード
まずは完成コードがこちらです。
category$("#range").change(function(){ $("#range-second").remove(); $("#range-third").remove(); var category = $('option:selected').val(); function buildHTML(children){ var option = `` children.forEach(function(child){ option += `<option value="${child.id}">${child.name}</option>` }); var html = `<select name="range-second" id="range-second"> <option value label=" "></option> ${option} </select>` return html; }; $.ajax({ url: '/users/1/products/new', type: "GET", data:{category: category}, dataType: 'json' }) .done(function(data){ var html = buildHTML(data); $(".products_new_container__content__select__box__category").append(html); }) .fail(function(){ alert('error'); }) });このなかで、今回のポイントとなるコードはこの部分です。
function buildHTML(children){ var option = `` children.forEach(function(child){ option += `<option value="${child.id}">${child.name}</option>` }); var html = `<select name="range-second" id="range-second"> <option value label=" "></option> ${option} </select>` return html; };一行目のbuildHTML関数の引数「children」には、selectタグの選択肢が配列として格納されています。
二行目で、まず空の変数optionを宣言します。
その次でforEachメソッドを使用し、childrenに含まれた要素の数だけ変数optionの中にoptionタグを追加していきます。
あとは、必要なだけのoptionタグの入った変数optionを、ビューに挿入する変数htmlのなかに入れるだけです。
まとめ
まず空の箱を用意して、配列の要素をeachでひたすら詰めていくというのは、Rubyでもよくやりました。
- 投稿日:2019-05-12T15:46:12+09:00
Rails・Haml・Ajaxでいいね機能を実装
いいね機能とは
ユーザーが投稿した記事や商品に対して、「いいね」をつけます。
以下が完成形です。実装方法
今回はRails、Hamlという環境で実装します。
ページのリロードはせず、非同期通信で画面を変化させます。ポイントは以下の通りです。
・ユーザーはひとつの記事や商品に対し、一度しか「いいね」できない。
・「いいね」するとボタンの色が変わり数字が増え、もう一度押すと取り消され元のボタンに戻る。実装の準備
では、順をおって実装していきましょう。
今回はフリーマーケットサイトにおいて、ユーザーが出品した商品に対しての「いいね」になります。各モデルの準備
ユーザー(user)が商品(product)に対し「いいね(like)」するので、
・users
・products
・likes
以上三つのモデル、テーブルが必要です。ですが、今回は「いいね」の実装ということで、userとproductがすでに存在している前提でのお話になります。
いいね(like)は、それをしたユーザー(user)とそれをされた商品(product)に紐づいているので、それに応じたアソシエーションを組む必要があります。
では、それぞれをつくっていきましょう。
ターミナル$ rails g model likeModelの命名規則は単数形なので、ここでも単数形で書くのが良さそうです(複数形で書いても勝手に単数形モデルになりますが・・・)。
アソシエーションを組む
ユーザー(user)と商品(product)は「いいね(like)」をたくさん持っていて、「いいね(like)」はそれらに属しています。
ですので、カラムをつくるときは、likesテーブルに「user_id」と「product_id」を持たせます。
db/migrateclass CreateLikes < ActiveRecord::Migration[5.2] def change create_table :likes do |t| t.references :product t.references :user t.timestamps end end endターミナル$ rake db:migrateアソシエーションは、
app/models/user.rbclass User < ApplicationRecord has_many :likes, dependent: :destroy endapp/models/product.rbclass Product < ApplicationRecord has_many :likes, dependent: :destroy endapp/models/like.rbclass Like < ApplicationRecord belongs_to :product belongs_to :user validates :user_id, presence: true validates :product_id, presence: true validates_uniqueness_of :product_id, scope: :user_id end「dependent: :destroy」
userやproductが削除された際は、それに関連するlikeも一緒に消してねという意味です。「validates :user_id, presence: true」「validates :product_id, presence: true」
likeを生成するときはuser_idとproduct_idが空ではだめだよというバリデーション。「validates_uniqueness_of :product_id, scope: :user_id」
ひとつのproductにひとりのuserが複数回いいねできないよう、product_idとuser_idの組み合わせはひとつだけだよというバリデーションです。とりあえずの紐づけができました。
Likesコントローラーの作成
コントローラーも作ります。
「いいね(like)」は「生成」と「削除」のふたつが想定されるので、createアクションとdestroyアクションがありますね。ターミナル$ rails g controller likesコントローラーの命名規則は複数形です。
app/controllers/likes_controller.rbclass LikesController < ApplicationController def create end def destroy end endルーティング
「いいね(like)」はproductに対してのもので、likesコントローラーで持ち主のproduct_idが必要なので、productにネストさせます。
しかし、今回はすでにproductがuserにネストされているので、禁断の2段階ネストになっています。
config/routes.rbresources :users, only: [:show, :edit, :update] do resources :products, only: [:new, :create, :show] do resources :likes, only: [:create, :destroy] end endここまでが準備です。
実装する
では、実装していきましょう。
まず、全体のイメージをざっくり説明です。
実装は大きく分けて三つ、
1.ユーザーがすでに「いいね」しているかどうかで、ボタンのスタイルが違う。
2.ボタンを押したら数字が増え、もう一度押すと数字が減る。
3.ボタンが押されるというアクションによって、ボタンの状態を切り替える。まず、1
これはビューにおいてボタンのスタイルを2つつくり、ifを使って場合分けします。次に、2
「いいね」の数はテーブルにあるレコードの数なので、「いいね」するとレコードが増え、その分数字が増える。減るのは、レコードが削除されるため。最後に、3
これは非同期通信によるものです。
具体的には、1で説明したビューが部分テンプレートになっており、ボタンが押されたことにより、この部分テンプレートに送る値や条件を変化させています。この説明ではわかりづらすぎるので、実際に手順を追っていきます。
カウント機能の作成
likesテーブルにあるproduct_idごとのレコードを集計します。
まずは、likeモデルに追加。app/models/like.rbclass Like < ApplicationRecord #counter_cache: :likes_count を追加 belongs_to :product, counter_cache: :likes_count belongs_to :user end「counter_cache: :likes_count」
リレーションされたlikeの数を数え、productのlikes_countカラムに入れるという意味。likeがcreateされるとlike_countが+1、destroyされると-1します。よって、productsテーブルにlikes_countカラムを追加しなければなりません。
ターミナル$ rails g migration AddClomunToLikesdb/migrateclass AddClomunToLikes < ActiveRecord::Migration[5.2] def change add_column :products, :likes_count, :integer end endターミナル$ rake db:migrate実際に「いいね」していくと、このように数字がカウントされます。
ビューの作成
ビューの作成ですが、まずはユーザーがすでに「いいね」しているのかどうかのメソッドをつくります。
app/models/product.rbclass Product < ApplicationRecord ... #以下を追加 def like_user(user_id) likes.find_by(user_id: user_id) end end引数として渡されたuser_idが、likesテーブルのuser_idカラムにすでに存在しているかを確かめ、true、falseを返します。
app/views/products/show.html.haml... .item-button-container .item-button-container__left = render partial: 'likes/like', locals: { product: @product, products: @products, likes: @likes, like: @like} ...app/views/likes/_like.rb#ユーザーがサインインしているかどうか - if user_signed_in? #ログインしているユーザーがすでに「いいね」しているかどうか - if product.like_user(current_user.id) .item-button-container__left__dislike =link_to user_product_like_path(current_user, product, like), method: "DELETE", remote: true do %i.fas.fa-heart %span いいね! %span = product.likes_count - else .item-button-container__left__like =link_to user_product_likes_path(current_user, product), method: "POST", remote: true do %i.fas.fa-heart %span いいね! %span = product.likes_count -else .item-button-container__left__like %i.fas.fa-heart %span いいね! %span = product.like_count「= product.likes_count」で、さきほどのいいねのカウント数が表示されます。
Ajax通信
ボタンを押すというアクションによってAjax通信をします。
といっても、link_toメソッドにremote: trueを記述しているので、jsファイルに通信のあれこれを定義することはありません。すでにこのlink(今回のボタン)はAjaxになっています。
「いいね」を押した先のコントローラー
app/controllers/likes_controller.rbclass LikesController < ApplicationController def create @like = Like.create(user_id: current_user.id, product_id: params[:product_id]) @likes = Like.where(product_id: params[:product_id]) get_product end def destroy @like = Like.find_by(user_id: current_user.id, product_id: params[:product_id]) @like.destroy @likes = Like.where(product_id: params[:product_id]) get_product end def get_product @product = Product.find(params[:product_id]) end endボタンによってcreate、destroyをするとlikes_countの数値が変わるので、数値の変わったlikesとproductを改めて取得してビューに渡します。
最後に、これらの値が渡ったjsファイルをつくります。
app/views/likes/create.js.haml$(".item-button-container__left").html("#{escape_javascript(render partial: 'like', locals: { product: @product, products: @products, likes: @likes, like: @like})}")app/views/likes/destroy.js.haml$(".item-button-container__left").html("#{escape_javascript(render partial: 'like', locals: { product: @product, products: @products, likes: @likes, like: @like})}")ここでは部分テンプレートのみを書き換えています。
つまり、部分テンプレートに渡す値を「いいね」が追加、削除された後のものに置き換えているということです。
これにより、現在のユーザーの「いいね」も追加されたり削除されるので、条件分岐によりボタンのスタイルが変わります。まとめ
Ajax通信で部分テンプレートを差し替えるというテクニックは他にも応用ができそうです。
仕組みが複雑で説明がとても難しく散漫な記事となってしまいましたので、参考記事とともにご覧いただければ幸いです。
https://qiita.com/YuitoSato/items/94913d6a349a530b2ea2
https://www.prime-architect.co.jp/myblog/ruby-on-rails-1559
- 投稿日:2019-05-12T14:01:24+09:00
OSSで政治系のことについて議論できるChatスペースを作りたい
課題
日本ではSNSで政治のことは呟かれない
日本人は周りの人に合わせる癖があるので、SNSの投稿は基本的にキラキラしたものが多いです。自分がキラキラした生活を送っていることを投稿すればするほど、みんなからのいいねがたくさん来たり、みんなと同じような投稿をしたりすると共感が得られるシステム、それがSNSです。だからいつもSNSでフォロワーの多い人や友達のツイートを見て、同じような投稿をし、共感をえて、いいねをもらう。一つのゲームみたいなもので、ポケモンGOとかとあまり変わりありません。
キラキラしたものをinstagramに載せるのが悪いと言ってる訳ではなく、社会にはまだまだ問題がたくさんあり、不幸な人もたくさんいるのでその人たちの意見も届いたり、解決策を出せる場所が必要。解決策
政治系のSNSを作っちゃお!!!
やること
オープンソースのSpectrumを公開サーバーに移す
https://github.com/withspectrum/spectrum/blob/alpha/docs/deployments.md
- 投稿日:2019-05-12T10:12:12+09:00
ポートフォリオ『CodeZine』使用解説
CodeZine
これまでに読んだ技術本を
・ タイトル ・ おすすめポイント ・ 画像
の3点で簡潔にまとめてユーザー間でシェアするアプリです。使い方の説明
・テストユーザーでログイン
・記事を投稿するために「新規登録」ボタンをクリック
→新規記事投稿ページの入力フォームに、
おすすめの読んだ本のタイトル、おすすめポイント、画像の3点を書き込む。
・投稿者一覧ページに新規記事が追加される。・記事を編集・削除するには
→記事一覧ページの編集ボタンをクリック・記事の検索するには
→名称もしくは登録日時を入力し検索ボタンをクリック。
主な機能
■ユーザー管理機能
・新規ユーザー登録機能
・ユーザー詳細表示機能
・ユーザー一覧表示機能
・ユーザーアカウント編集・削除機能
・ユーザーログイン、ログアウト機能
■投稿記事管理機能
・新規記事投稿機能
・記事詳細表示機能
・記事への画像アップロード機能
・記事編集・削除機能
・記事一覧表示機能
・記事検索機能
■ユーザー・記事・検索結果一覧のページネーション機能使用技術
・開発環境
ローカル for Mac
・テスト環境
RSpec(システムテスト、結合テスト・機能テスト)
・本番環境
Heroku
・DB
Postgresql
・フロント開発
Bootstrap
Slim
・画像アップロード機能
Active Storange
・ページネーション機能
kaminari
・検索機能
ransack
・CSV形式のファイルのインポート、エクスポート
・Ajax
投稿記事削除依存関係
Rails: 5.2.3
Ruby: 2.5.1
Bootstrap: 4.3.1
RSpec 3.8
- 投稿日:2019-05-12T10:12:12+09:00
ポートフォリオ『CodeJin_BOOK123』使用解説
*アプリ名が登録商法に引っかかる可能性があるようなので変更しました。
ご指摘ありがとうございます!
Codejin_BOOK123
これまでに読んだ技術本を
・ タイトル ・ おすすめポイント ・ 画像
の3点で簡潔にまとめてユーザー間でシェアするアプリです。使い方の説明
・テストユーザーでログイン
・記事を投稿するために「新規登録」ボタンをクリック
→新規記事投稿ページの入力フォームに、
おすすめの読んだ本のタイトル、おすすめポイント、画像の3点を書き込む。
・投稿者一覧ページに新規記事が追加される。・記事を編集・削除するには
→記事一覧ページの編集ボタンをクリック・記事の検索するには
→名称もしくは登録日時を入力し検索ボタンをクリック。主な機能
■ユーザー管理機能
・新規ユーザー登録機能
・ユーザー詳細表示機能
・ユーザー一覧表示機能
・ユーザーアカウント編集・削除機能
・ユーザーログイン、ログアウト機能
■投稿記事管理機能
・新規記事投稿機能
・記事詳細表示機能
・記事への画像アップロード機能
・記事編集・削除機能
・記事一覧表示機能
・記事検索機能
■ユーザー・記事・検索結果一覧のページネーション機能使用技術
・開発環境
ローカル for Mac
・テスト環境
RSpec(システムテスト、結合テスト・機能テスト)
・本番環境
Heroku
・DB
Postgresql
・フロント開発
Bootstrap
Slim
・画像アップロード機能
Active Storange
・ページネーション機能
kaminari
・検索機能
ransack
・CSV形式のファイルのインポート、エクスポート
・Ajax
投稿記事削除依存関係
Rails: 5.2.3
Ruby: 2.5.1
Bootstrap: 4.3.1
RSpec 3.8















