- 投稿日:2020-01-13T22:44:04+09:00
【Rails】destroyメソッドを使用しようとしたら"ArgumentError (wrong number of arguments (given 0, expected 1)):"
はじめに
Railsの
destroy
メソッドを使用しようとしたら以下のように引数が1つ必要なのに見つかりませんとエラーが発生しました。
凡ミスですが、記録として残します。ArgumentError (wrong number of arguments (given 0, expected 1)):環境
OS: macOS Catalina 10.15.1 Ruby: 2.6.5 Rails: 6.0.2.1結論:解決法
今回のコードは以下のようになっていました。
def destroy posts = Post.where(user_id: 1) posts.destroy #ここでエラー発生 end↓
def destroy post = Post.find_by(user_id: 1) post.destroy #これは通る endこのように、
find_by
にすると通ります。もしくは、該当データが複数ある場合は
def destroy Post.destroy_by(user_id: 1) #これも通る endのように
destroy_by
メソッドを使えば該当データをまとめて削除することが出来ます。原因:
destroy
は配列を処理できない
where
だと該当するデータが1件であっても配列で返すようになっています。そのため、配列を処理できない
destroy
は引数が見つかりませんとエラーを吐いてしまっていたんですねおわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2020-01-13T21:27:25+09:00
はじめてAWSでデプロイする方法⑦(EC2にgemをインストール)
EC2のメモリを増強する
Swap(スワップ)領域を設定
Swapは、EC2のメモリが限界に達したとき、補う形でメモリの容量を増やす機能です。
デフォルトではSwap領域が設定されていないので、設定しましょう手順
ホームディレクトリに移行
[ec2-user@ip-172-31-25-189 ~]$ cd下記のコマンドを実行
[ec2-user@ip-172-31-25-189 ~]$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=512うまくいくと、下記の表示が出ます
512+0 レコード入力 512+0 レコード出力 536870912 バイト (537 MB) コピーされました、 5.19011 秒、 103 MB/秒次は権限に制限をかけましょう(chmodコマンド)
[ec2-user@ip-172-31-25-189 ~]$ sudo chmod 600 /swapfile1スワップ(swap)領域を作成する - mkswap
[ec2-user@ip-172-31-25-189 ~]$ sudo mkswap /swapfile1 #成功すると下記の表示が出ます スワップ空間バージョン1を設定します、サイズ = 524284 KiB ラベルはありません, UUID=74a961ba-7a33-4c18-b1cd-9779bcda8ab1スワップ(swap)領域を有効化する - swapon
[ec2-user@ip-172-31-25-189 ~]$ sudo swapon /swapfile1エラーが出なければ成功です。
エラー「swapon: /swapfile1: スワップヘッダの読み込みに失敗しました: 無効な引数です」が表示された場合は、一つ前の手順に戻ってmkswapコマンドを実施してください。下記のコマンドを実施してください。
?長いので、気をつけてください[ec2-user@ip-172-31-25-189 ~]$ sudo sh -c 'echo "/swapfile1 none swap sw 0 0" >> /etc/fstab'これで完了
gemのインストール
まずは、EC2にダウンロードしたWEB Appを開く
[ec2-user@ip-172-31-23-189 www]$ cd /var/www(作成したディレクトリ)/アプリ名Rubyのバージョンを確認する
[ec2-user@ip-172-31-23-189 <アプリ名>]$ ruby -v指定したrubyのバージョンが表示されれば成功です。
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]ローカル上のターミナルでbundlerのバージョンを確認する
EC2ではなく、ローカル環境のWEB Appを開き、下記コマンドを実施
$ bundler -vするとバージョンが表示されます
Bundler version 2.0.2これと同じバージョンをEC2で入れます。
?下記のバージョンをそのまま (2.0.1)を入れるとエラーが発生するので注意[ec2-user@ip-172-31-23-189 <アプリ名>]$ gem install bundler -v 2.0.1EC2でbundle installをして、gemをインストール
[ec2-user@ip-172-31-23-189 <アプリ名>]$ bundle installエラーがなければ、gemのインストール完了です。
エラーが発生した場合
下記のエラーが表示された場合、インストールするべき bundler -vが間違っています
Traceback (most recent call last): 2: from /home/ec2-user/.rbenv/versions/2.5.1/bin/bundle:23:in `<main>' 1: from /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:308:in `activate_bin_path' /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': can't find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)エラーがある場合は、アプリ側の bundlerのバージョンを確認してください
$ bundler -v >Bundler version 2.0.2 [ec2-user@ip-172-31-23-189 <アプリ名>]$ bundler -v >Bundler version 2.0.1 >バージョンが違うので、エラーがおきますインストール完了後、下記の表示があった場合
Post-install message from chromedriver-helper: +--------------------------------------------------------------------+ | | | NOTICE: chromedriver-helper is deprecated after 2019-03-31. | | | | Please update to use the 'webdrivers' gem instead. | | See https://github.com/flavorjones/chromedriver-helper/issues/83 | | | +--------------------------------------------------------------------+gem'chromedriver-helper'のサポートが終了しているので、代わりとなるgem 'webdrivers'をインストールすることを推奨しているメッセージとなります。
group :test do # ... - gem 'chromedriver-helper' + gem 'webdrivers'
- 投稿日:2020-01-13T21:10:39+09:00
【初心者向け】超簡単ドリンク注文アプリケーション(ドリンクに値段をつけるver)
超簡単ドリンク注文アプリケーションに値段をつけてみる
こちらは前回の続きです。
前回のアプリケーションはこちら
https://qiita.com/pontarou194/items/708452b47464bfcad122条件
・実行時、どのようなドリンクがあるかを表示させる
・ドリンクには番号をつける
・選ばれた番号と同じドリンク名を表示させる
・ドリンクに値段をつける
・ドリンクの注文数を訪ねて、入力された値に対して合計額を表示させる擬似コード
def お茶が選ばれた場合、値段を表示 注文数を入力 合計額を表示 end def コーヒーが選ばれた場合、値段を表示 注文数を入力 合計額を表示 end def ビールが選ばれた場合、値段を表示 注文数を入力 合計額を表示 end def サイダーが選ばれた場合、値段を表示 注文数を入力 合計額を表示 end #それぞれのドリンクメニューの値段を残す otya = 100 coffee = 200 beer = 300 saida = 400 #ここから先、変数をつけたの以外は前回と一緒なので、そのまま記述します puts "何を飲みたいですか?" drink_menu = ["お茶","コーヒー","ビール","三ツ矢サイダー"] drink_menu.each.with_index(1) do |drink_name, number| puts "#{number}:#{drink_name}" end case gets.to_i when 1 otya(otya) when 2 coffee(coffee) when 3 beer(beer) when 4 saida(saida) else puts "無効な入力値です" end実際にコーディングしてみる
def otya(otya) puts "あなたが選んだのはお茶" puts "お茶の値段は100円です" puts "いくつ注文しますか?" order_quantity = gets.to_i total_price = order_quantity * otya puts "合計#{total_price}円です" end def coffee(coffee) puts "あなたが選んだのはコーヒー" puts "コーヒーの値段は200円です" puts "いくつ注文しますか?" order_quantity = gets.to_i total_price = order_quantity * coffee puts "合計#{total_price}円です" end def beer(beer) puts "あなたが選んだのはビール" puts "ビールの値段は300円です" puts "いくつ注文しますか?" order_quantity = gets.to_i total_price = order_quantity * beer puts "合計#{total_price}円です" end def saida(saida) puts "あなたが選んだのは三ツ矢サイダー" puts "サイダーの値段は300円です" puts "いくつ注文しますか?" order_quantity = gets.to_i total_price = order_quantity * saida puts "合計#{total_price}円です" end otya = 100 coffee = 200 beer = 300 saida = 400 puts "何を飲みたいですか?" drink_menu = ["お茶","コーヒー","ビール","三ツ矢サイダー"] drink_menu.each.with_index(1) do |drink_name, number| puts "#{number}:#{drink_name}" end case gets.to_i when 1 otya(otya) when 2 coffee(coffee) when 3 beer(beer) when 4 saida(saida) else puts "無効な入力値です" end変数及び、引数の勉強になるかと思って使ってみました。
otya(otya)が呼び出された際に、otya(otya)メソッドに飛んで、otya(otya)メソッドではotyaの値段を参照しているため、
otyaの値段と入力された数をかけた合計額が表示される。動作確認
何を飲みたいですか? 1:お茶 2:コーヒー 3:ビール 4:三ツ矢サイダー 3 あなたが選んだのはビール ビールの値段は300円です いくつ注文しますか? 4 合計1200円です今回は以上となります。
引数便利ですよね。ここは違う、このようにしたほうがセンスが良い等ございましたら、ご指摘いただけますと幸いです。
最後までみていただきありがとうございます。
- 投稿日:2020-01-13T18:27:22+09:00
ハッシュから値だけを取り出し、配列にしてください。(valuesメソッドは使用しない)
- 投稿日:2020-01-13T16:52:51+09:00
【実践】SolidusでRails製ECサイトを作ってみる
SolidusはSpreeの後継のRails製オープンソースECサイトのプロジェクト。
OSSを使わないとしても、ECに最低限の機能が入ってるので、自分たちで機能の洗い出しするより、これを参考にした方が早い。準備
以下をインストールしていない人はインストール
Rubyはバージョンが低いとfiniteのエラーが出ます。
テスト環境ではv2.5.1を使用しました。brew install sqlite3 imagemagick1. Railsアプリを作成
rails new myshop --skip_webpack_install cd myshop2. Gemfileに以下を追加
Gemfilegem 'solidus' gem 'solidus_auth_devise''solidus'をrequireすると、以下がまとめてインストールされます。個別にインストールすることも可能なよう。
solidus_core
solidus_api
solidus_frontend
solidus_backend
solidus_sample
bundle install
3. gemのinitialize & マイグレーション
bundle exec rails generate spree:install # ここでadmin / パスワードを設定します bundle exec rails generate solidus:auth:install bundle exec rake railties:install:migrations bundle exec rake db:migratespreeはsolidusの元のOSS eCommerceプロジェクトです。
マイグレーションも作成されます。4.Viewをオーバーライドする
bundle exec rails generate solidus:views:override # bundle exec rails generate solidus:views:override --only products/show # 一部だけカスタマイズする場合はこのようなコマンド起動してみる
bundle exec rails server管理画面について
adminユーザーでログインし、
/admin
にアクセスすると、管理画面が表示されます。APIについて
APIリファレンスはこちら
https://solidus.docs.stoplight.io/商品のレスポンス例:
{ "count": 18, "total_count": 18, "current_page": 1, "pages": 1, "per_page": 25, "products": [ { "id": 1, "name": "Ruby on Rails Tote", "description": "Soluta sed error debitis repellendus et. Voluptates unde enim qui velit. Libero earum tenetur nulla similique temporibus quod repellendus quibusdam.", "available_on": "2020-01-13T07:32:59.433Z", "slug": "ruby-on-rails-tote", "meta_description": null, "meta_keywords": null, "shipping_category_id": 1, "taxon_ids": [ 3, 10, 13, 20 ], "meta_title": null, "total_on_hand": 10, "price": "15.99", "display_price": "$15.99", "has_variants": false, "master": { "id": 1, "name": "Ruby on Rails Tote", "sku": "ROR-00011", "weight": "0.0", "height": null, "width": null, "depth": null, "is_master": true, "slug": "ruby-on-rails-tote", "description": "Soluta sed error debitis repellendus et. Voluptates unde enim qui velit. Libero earum tenetur nulla similique temporibus quod repellendus quibusdam.", "track_inventory": true, "cost_price": "17.0", "price": "15.99", "display_price": "$15.99", "options_text": "", "in_stock": true, "is_backorderable": true, "total_on_hand": 10, "is_destroyed": false, "option_values": [], "images": [] }, "variants": [], "option_types": [], "product_properties": [ { "id": 25, "product_id": 1, "property_id": 9, "value": "Tote", "property_name": "Type" }, { "id": 26, "product_id": 1, "property_id": 10, "value": "15\" x 18\" x 6\"", "property_name": "Size" }, { "id": 27, "product_id": 1, "property_id": 11, "value": "Canvas", "property_name": "Material" } ], "classifications": [ { "taxon_id": 3, "position": 1, "taxon": { "id": 3, "name": "Bags", "pretty_name": "Categories -> Bags", "permalink": "categories/bags", "parent_id": 1, "taxonomy_id": 1, "taxons": [] } }, { "taxon_id": 10, "position": 1, "taxon": { "id": 10, "name": "Rails", "pretty_name": "Brand -> Rails", "permalink": "brand/rails", "parent_id": 2, "taxonomy_id": 2, "taxons": [] } }, { "taxon_id": 13, "position": 1, "taxon": { "id": 13, "name": "Bags", "pretty_name": "Categories -> Bags", "permalink": "categories/bags", "parent_id": 1, "taxonomy_id": 1, "taxons": [] } }, { "taxon_id": 20, "position": 1, "taxon": { "id": 20, "name": "Rails", "pretty_name": "Brand -> Rails", "permalink": "brand/rails", "parent_id": 2, "taxonomy_id": 2, "taxons": [] } } ] }, ...まとめ
詳しくはもう少しいじって追記していく予定。
- 投稿日:2020-01-13T16:42:25+09:00
rails: 絶対に分かるhas_one :through関連付ける方法
前提
ユーザー・予約・住所の3つのレコードで
has_one :through
を使用し、アソシエーション関係を作りたいと思います。実際のコード
class Booking < ApplicationRecord # ユーザーテーブルに対して一対一の関係を示している has_one :user # has_one :addressはユーザーテーブルにある has_one :addressのことである。 # throughは上記に書かれているhas_one :userのことである has_one :address, through: :user endclass User < ApplicationRecord has_one :address, dependent: :destroy endclass Address < ApplicationRecord belongs_to :user end要約
要約すると、
has_one through
のhas_one
はアソシエーション先のhas_one :address
のことであり、through
は自モデル内に書かれているhas_one :user
のことである出力結果
@booking = Booking.find_by(id: 1) @booking.address.zip_code # => 150-0002
- 投稿日:2020-01-13T16:36:12+09:00
初心者向けVue.js × Railsでのアプリ作成(ToDoリスト編)
はじめに
最近、Vue.jsとRailsでアプリを作っているのですが、Vue.jsとRailsでアプリを作る記事が少なく、勉強するのに少し不便でした。
Vue.js × Railsの記事が少ないと言っても探せばそれなりに見つかるのですが、私みたいなフロントエンドの事よくわかってない人間には、理解するのに時間がかかったりします。
ネットで記事をあさったり、そもそもJavaScriptが良くわかってないので、JavaScriptから勉強し直してみたり、Vue.js × Railsでアプリを作るだけにしては非常に遠回りしてしまいました。
この記事について
この記事は、私みたいにVue.js × Railsのアプリ作成で遠回りな勉強をしている人をなくす事を目的としています。
初心者向けにVue.js × Railsでアプリを作る記事を書いて、実装のイメージを掴んでもらえれば、私のような遠回りはなくなるはず。。。1度小さいアプリを作ってしまえば、理解度がグッと上がり、他の記事も読みやすくなるのできっと大丈夫!
また、この記事は私がRubyエンジニアなので、Rubyエンジニアから見てVue.jsをどう実装しようかという視点になってます。
この記事を読む対象のレベル
Vue.js × Rails両方ともチュートリアルをやったくらいのレベルを対象としています。
Vue.jsもRailsもだいたいこんな感じというのがわかっていれば作れると思います。どんなものを作るか? Vue.js × Railsそれぞれの役割とは?
この記事では、Vue.js × RailsでミニマムなToDoリストアプリを作っていきます。定番ですね。
私はToDoアプリを作ろうとした時、Vue.js × Railsがそれぞれどんな役割をしているのかよくわからず、実装のイメージが掴めなかったのですが、いろんな記事を読んだ結果、RailsでAPIを作り、APIへのリクエストと返ってきたデータの表示をVue.jsで行うというのが多かったです。今回もこの役割分担でアプリを作っていきます。
WEB業界での経験が浅い、もしくはこれからWEB業界を目指す方はAPIのイメージがつかみにくいかもしれませんが、一言で言うとURLのリクエストを受けたら、URLに応じたデータを返すものです。
この役割のイメージが分かればRailsの部分をFirebaseに置き換えようとか、Vue.jsをReactに置き換えようとか応用が効くような気がします。
(注)私は現時点でFirebaseもReactも詳しくないので応用が効く気がするというだけ。。。詳しい方がいたら教えて下さると助かります。
完成後のイメージ
- テキストボックスにタスクを入力して追加ボタンを押すとリストに追加されて表示される。
- チェックボックスをチェックすると取り消し線が引かれる
- 削除ボタンを押すと削除
実際に作ってみよう
RailsでAPIを作る
では、実際にアプリを作ってきます。まずはRailsでAPIを作るところから。
以下のコマンドで、Railsアプリを作ります。--webpack=vue
をするとwebpackとVue.jsのインストールが出来ます。rails new todo_list --webpack=vue「webpackとかまた新しい言葉出すなよ!」って方は以下のリンクを見てください!
【5分でなんとなく理解!】Webpack入門
Webpackとは、js、cssなどフロントで作るファイルをバンドリングしてくれるものです。ToDoリストを表示するHome画面を作成
ToDoを表示するHome画面を作るため、コントローラーを作成します。
rails g controller home作成したコントローラーにindexだけ追加しておきましょう。
/app/controllers/home_controller.rbclass HomeController < ApplicationController def index end endviewからVue.jsが呼び出せるか試しますために追加します。
/app/views/home/index.erb<%= javascript_pack_tag 'hello_vue' %>routes.rbに以下を追加。
/config/routes.rbRails.application.routes.draw do root to: 'home#index' end
rails s
してlocalhost:3000
にアクセスします。
以下のような画面が表示されてればOKです。ちなみにVue.jsを変更したら
bin/webpack
で更新してあげる必要があります。(重要)APIの処理を作る
まずは、ToDoリストにタスクを追加するためにモデルを作っていきます。
rails generate model Task name:string is_done:booleanルーティングに以下を追加します。
表示用のhomeとデータを返すAPI用のapi::tasksを追加。/config/routes.rbRails.application.routes.draw do root to: 'home#index' namespace :api, format: 'json' do resources :tasks, only: [:index, :create, :destroy, :update] end endリクエストを受けたらデータを返すため、Tasksのコントローラーを作ります。
rails g controller Api::Tasksマイグレーションします。
rails db:migrateコントローラーの中身は以下のような感じ。
/app/controllers/api/tasks_controller.rbmodule Api class TasksController < ApplicationController skip_before_action :verify_authenticity_token def index @tasks = Task.order('created_at DESC') end def create @task = Task.new(task_params) if @task.save render json: @task, status: :created else render json: @task.errors, status: :unprocessable_entity end end def destroy Task.find(params[:id]).destroy! end def update Task.find(params[:id]).toggle!(:is_done) end private def task_params params.require(:task).permit(:name, :is_done) end end endAPIを返す時は、htmlではなくJSONで返してあげたいので以下を追加します。
自分は実際にWEB業界に入るまでJSONに馴染みがなかったのですが、以下の形で書きます。/app/views/api/tasks/index.json.jbuilderjson.set! :tasks do json.array! @tasks do |task| json.extract! task, :id, :name, :is_done, :created_at, :updated_at end endAPIの動作確認
DBにデータを入れて確認してみましょう。
コンソールを立ち上げます。rails cTaskモデルにデータを追加してみましょう。
Task.create(name: 'テスト用タスク')もう一度サーバー立ち上げ
rails s以下のアドレスで追加したデータがJSONでデータが返ればOK。
http://localhost:3000/api/tasks.json
Vue.jsでフロント作成
コンポーネント
コンポーネントとは、名前付きの再利用可能な Vue インスタンスです。
再利用出来そうなパーツごとにコンポーネントを区切って実装するのが、どうやら重要らしい。
今回、最小限の構成でアプリを構成するためコンポーネントについては省こうか迷ったのですが、重要なので組み込みます。わかりやすいイメージで言うと、ヘッダー、フッター、サイドナビ等は色んなページで再利用するのでコンポーネントを分けて実装するといった感じでしょうか。
今回もヘッダーとToDoリストを表示するボディ部分でコンポーネントを分けて実装したいと思います。では、まず以下のようにToDoリスト表示部分を作って下さい。
/app/views/home/index.erb<div id="app"> <navbar></navbar> </div> <%= javascript_pack_tag 'todo' %> # todoに変更する事に注意はヘッダーのコンポーネントを表示します。
<%= javascript_pack_tag 'todo' %>でapp/javascript/packs配下のtodo.jsファイルを読み込みます。ヘッダーの作成
まずはヘッダー部分のコンポーネントを用意します。
/app/javascript/packs/components/header.vue<template> <h1>ToDoリスト</h1> </template>次に
/app/views/home/index.erb
から呼び出されているapp/javascript/packs/todo.js
にコンポーネントの設定をしていきます。
以下のように書くとapp/views/home/index.erb
の<navbar></navbar>
に/app/javascript/packs/components/header.vue
をマウントして表示してくれるようです。/app/javascript/packs/todo.jsimport Vue from 'vue/dist/vue.esm.js' import Header from './components/header.vue' var app = new Vue({ el: '#app', components: { 'navbar': Header } });Vue.jsの読み込み設定
webpackはVue.jsの読み込み方がわからないので以下を実行します(重要)
/config/loaders/stylus.jsmodule.exports = { test: /\.styl$/, use: [ 'style-loader', 'css-loader', 'stylus-loader' ] }/config/webpack/environment.jsconst { environment } = require('@rails/webpacker') const { VueLoaderPlugin } = require('vue-loader') const vue = require('./loaders/vue') const stylus = require('../loaders/stylus') // 作ったstylusをrequireする environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin()) environment.loaders.prepend('vue', vue) module.exports = environment environment.loaders.prepend('stylus', stylus) // 作ったstylusをロードwebpackを再読み込みしてからサーバーを再起動しましょう(重要)
bin/webpackrails srails sしてヘッダーが表示されて入ればOKです
ToDoリストを表示するボディ部分
axiosというライブラリを使って、フロントエンドからHTTPリクエストをします。
以下のコマンドでyarnでaxiosを追加して下さい。yarn add axiosToDoアプリのメイン部分の実装です。解説は後ほど詳しく説明します。
/app/javascript/packs/components/index.vue<template> <div> <div> <input v-model="newTask" placeholder="to doを追加して下さい"> <div v-on:click="createTask"> <i>追加</i> </div> </div> <ul> <li v-for="(task, index) in tasks"> <input type="checkbox" v-model="task.is_done" v-on:click="update(task.id, index)"> <span v-bind:class="{done: task.is_done}">{{ task.name }}</span> <button v-on:click="deleteTask(task.id, index)">削除</button> </li> </ul> </div> </template> <script> import axios from 'axios'; export default { data: function () { return { tasks: [], newTask: '' } }, mounted: function () { this.fetchTasks(); }, methods: { fetchTasks: function () { axios.get('/api/tasks').then((response) => { for(let i = 0; i < response.data.tasks.length; i++) { this.tasks.push(response.data.tasks[i]); } }, (error) => { console.log(error, response); }); }, createTask: function () { if(this.newTask == '') return; axios.post('/api/tasks', { task: { name: this.newTask } }).then((response) => { this.tasks.unshift(response.data); this.newTask = ''; }, (error) => { console.log(error, response); }); }, deleteTask: function (task_id, index) { axios.delete('/api/tasks/' + task_id).then((response) => { this.tasks.splice(index, 1); }, (error) => { console.log(error, response); }); }, update: function (task_id) { axios.put('/api/tasks/' + task_id).then((response) => { }, (error) => { console.log(error); }); } } } </script>作ったindex.vueを読み込んであげましょう。
/app/javascript/packs/todo.jsimport Vue from 'vue/dist/vue.esm.js' import Header from './components/header.vue' import Index from './components/index.vue' // 追加 var app = new Vue({ el: '#app', components: { 'navbar': Header, 'contents' : Index // 追加 } });ヘッダーのしたにindex.vueを表示するため、
<navbar></navbar>
の下に<contents></contents>
を追加します。/app/views/home/index.erb<div id="app"> <navbar></navbar> <contents></contents> </div> <%= javascript_pack_tag 'todo' %>チェックボックスが押されたら取り消し線を表示するためcss追加。
app/assets/stylesheets/home.scss#app li > span.done { text-decoration: line-through; }
rails s
して動くか確認してみて下さい。
実際にToDoリストを追加してみましょう。ToDoアプリのコード解説メモ
学習し始めだと、Vue.jsのどの行が何をやっているのかわからない事があったのでメモ付きのコードをのせます。
まずはtemplate
<template> <div> <div> <input v-model="newTask" placeholder="to doを追加して下さい"> # 追加ボタンを押すとcreateTaskを実行する <div v-on:click="createTask"> <i>追加</i> </div> </div> <ul> # fetchしたタスク一覧(tasks)から一つずつtaskとindexを取り出す処理 <li v-for="(task, index) in tasks"> # チェックボックスが押されたらv-modelのis_doneを変更して取り消し線を引く # updateでAPI側のデータも更新 <input type="checkbox" v-model="task.is_done" v-on:click="update(task.id, index)"> # タスクの表示。v-bind:classでis_doneを参照して取り消し線が引かれるかどうか判定 <span v-bind:class="{done: task.is_done}">{{ task.name }}</span> # 削除ボタンを押すとdeleteTaskを実行 <button v-on:click="deleteTask(task.id, index)">削除</button> </li> </ul> </div> </template><script> import axios from 'axios'; export default { data: function () { return { tasks: [], newTask: '' } }, mounted: function () { this.fetchTasks(); }, methods: { // APIからタスク一覧を取得 fetchTasks: function () { axios.get('/api/tasks').then((response) => { for(let i = 0; i < response.data.tasks.length; i++) { this.tasks.push(response.data.tasks[i]); } }, (error) => { console.log(error, response); }); }, // 新しいタスク作成 createTask: function () { // テキストボックスが空の場合はreturnして終了 if(this.newTask == '') return; // apiへ追加リクエスト axios.post('/api/tasks', { task: { name: this.newTask } }).then((response) => { // unshiftで現在のtasksの先頭にタスクを追加 this.tasks.unshift(response.data); // 追加したらテキストボックスを空にする this.newTask = ''; }, (error) => { console.log(error, response); }); }, // タスク削除 deleteTask: function (task_id, index) { // apiへ削除リクエスト axios.delete('/api/tasks/' + task_id).then((response) => { this.tasks.splice(index, 1); }, (error) => { console.log(error, response); }); }, // タスク更新。今回はis_doneのみ更新だが、タスク名とか色々更新するようカスタムしても良いと思う update: function (task_id) { // apiへ更新リクエスト axios.put('/api/tasks/' + task_id).then((response) => { }, (error) => { console.log(error); }); } } } </script>まとめ
小さいアプリをとりあえず作ってみると理解度がかなり深まると思うので、今回のようなToDoアプリを作ってみると良いと思います。
かけ足で記事を書いてしまったのですが、これで私みたいな人間を救えるのか...???
今後も私のようにVue.js × Railsでアプリを作りたい人向けに記事を改訂して行きたいです。
- 投稿日:2020-01-13T15:09:37+09:00
【Rails】Rails g model書き方
- 投稿日:2020-01-13T10:16:39+09:00
Ruby on Rails APIモードでいいね機能を実装する【初学者のReact×Railsアプリ開発 第6回】
やったこと
- ログイン中のユーザーが、ポスト(投稿)に対して、「好き」「嫌い」「興味ない」を投票でき、更新もできるようにした。
- likesテーブルをpostsテーブル、usersテーブルとリレーションさせた。
- counter_cultureを使って、関連レコードの集計(postsテーブルのsuki_countなど)を行った
- PostgreSQL 12の新機能であるGenerated Columnを使用して、関連レコードの集計結果の計算(postsテーブルのall_count, suki_percent)を行わせた。
完成したデータベースのイメージ(dbconsoleを使って確認)
app_development=# select * from likes; id | user_id | post_id | suki | created_at | updated_at ----+---------+---------+------+----------------------------+---------------------------- 1 | 1 | 2 | 1 | 2020-01-12 23:54:28.193197 | 2020-01-12 23:54:28.193197 (1 row) app_development=# select * from posts; id | content | user_id | created_at | updated_at | suki_count | kirai_count | notinterested_count | all_count | suki_percent ----+---------+---------+--------------------------+--------------------------+------------+-------------+---------------------+-----------+-------------- 2 | bbbbd | 1 | 2020-01-12 12:41:03.4942 | 2020-01-12 12:41:03.4942 | 1 | 0 | 0 | 1 | 100実装手順
counter_cultureのインストール
counter_cultureは、各ポストに対して何件の「好き」「嫌い」が投票されたのか、など関連レコード数の集計に使うモジュールです。
Gemfilegem 'counter_culture'gemfileにcounter_cultureを追加。
$ docker-compose build --no-cacheモデルとコントローラーの作成
$ docker-compose run api rails g model like suki:integer $ docker-compose run api rails g controller api/v1/likesXXX_create_likes.rbclass CreateLikes < ActiveRecord::Migration[5.2] def change create_table :likes do |t| t.integer :user_id, null: false t.integer :post_id, null: false t.integer :suki, null: false t.timestamps t.index :user_id t.index :post_id t.index [:user_id, :post_id], unique: true end end end
- suki:0ならば「嫌い」、suki:1ならば「好き」、suki:2なら「興味なし」とする。
- user_idとpost_idの組み合わせがユニークであることを書く。重複データを避けるため。
追加のマイグレーションファイルの作成
- 関連レコードの集計のためのカラムやそれをパーセント表記するための列を追加します。(あるポストに対して「好き」が何票か、「嫌い」が何票か、「好き」と「嫌い」の合計は何票か、「好き」は何%か)
$ docker-compose run api rails g migration AddLikesCountToPosts $ docker-compose run api rails g migration AddAllCountToPosts $ docker-compose run api rails g migration AddSukipercentToPostsXXX_add_likes_count_to_posts.rbclass AddLikesCountToPosts < ActiveRecord::Migration[5.2] class MigrationUser < ApplicationRecord self.table_name = :posts end def up _up rescue => e _down raise e end def down _down end private def _up MigrationUser.reset_column_information add_column :posts, :suki_count, :integer, null: false, default: 0 unless column_exists? :posts, :suki_count add_column :posts, :kirai_count, :integer, null: false, default: 0 unless column_exists? :posts, :kirai_count add_column :posts, :notinterested_count, :integer, null: false, default: 0 unless column_exists? :posts, :notinterested_count end def _down MigrationUser.reset_column_information remove_column :posts, :suki_count if column_exists? :posts, :suki_count remove_column :posts, :kirai_count if column_exists? :posts, :kirai_count remove_column :posts, :notinterested_count if column_exists? :posts, :notinterested_count end end
- suki_count, kirai_count, nointerested_countというカラムをpostsテーブルに追加しています。counter_cultureを使って、likesテーブルに「好き」「嫌い」「興味なし」が投票されたときに、+1され、likesテーブルのデータが更新されたら-1、+1がされます。
XXX_add_all_count_to_posts.rbclass AddAllCountToPosts < ActiveRecord::Migration[5.2] def up execute "ALTER TABLE posts ADD COLUMN all_count real GENERATED ALWAYS AS (suki_count+kirai_count) STORED;" add_index :posts, :all_count, unique: false end def down remove_column :posts, :all_count end end
- postsテーブルにall_countカラムを追加。
- PostgreSQL 12の新機能「Generated Column」を使って、suki_countとkirai_countの合計をall_countに自動計算させるように記述しています。
Generated Columnとは
“generated column” を使うと、”(同じテーブル内の) 他の列の値を利用した計算結果” を、特定の列に格納することが可能になります。https://tech-lab.sios.jp/archives/17098
XXX_add_sukipercent_to_posts.rbclass AddSukipercentToPosts < ActiveRecord::Migration[5.2] def up execute "ALTER TABLE posts ADD COLUMN suki_percent real GENERATED ALWAYS AS ( CASE WHEN (suki_count+kirai_count) = 0 THEN NULL ELSE suki_count*100/(suki_count+kirai_count) END ) STORED;" add_index :posts, :suki_percent, unique: false end def down remove_column :posts, :suki_percent end end
- suki_percentカラムをpostsテーブルに追加。
- ここでもgenerated columnを使って、「好き」の票数の「好き」と「嫌い」の合計に対するパーセンテージを求めてsuki_percentに格納するように記述しています。
モデルの編集
user.rbclass User < ActiveRecord::Base has_many :posts, dependent: :destroy has_many :likes, dependent: :destroy # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:twitter] include DeviseTokenAuth::Concerns::User end
- テーブル間のリレーションシップの追加
post.rbclass Post < ApplicationRecord belongs_to :user has_many :likes, dependent: :destroy end
- テーブル間のリレーションシップの追加
like.rbclass Like < ApplicationRecord belongs_to :user belongs_to :post validates :user_id, presence: true validates :post_id, presence: true counter_culture :post, column_name: -> (model) {"#{model.like_type_name}_count"} def like_type_name if suki == 1 then return 'suki' elsif suki == 0 then return 'kirai' elsif suki == 2 then return 'notinterested' end end end
- counter_cultureの実装はここで行っている。
- likesテーブルにデータがcreateされたとき「好き(suki==1)」ならばsuki_countを+1するようにしている...のような処理を記述している。
likesコントローラーの編集
likes_controllermodule Api module V1 class LikesController < ApplicationController before_action :authenticate_api_v1_user! before_action :set_like, only: [:show, :destroy, :update,] def index likes = Like.order(created_at: :desc) render json: { status: 'SUCCESS', message: 'Loaded posts', data: likes } end def show if @like.nil? then data = { updated_at: 3, suki: 3 } render json: { status: 'SUCCESS', message: 'Loaded the like', data: data } else render json: { status: 'SUCCESS', message: 'Loaded the like', data: @like } end end def create like = Like.new(like_params) if like.save @post = Post.find(params[:post_id]) @user = @post.user @like = Like.find_by(user_id: @user.id, post_id: params[:post_id]) json_data = { 'post': @post, 'user': { 'name': @user.name, 'nickname': @user.nickname, 'image': @user.image }, 'like': @like } render json: { status: 'SUCCESS', data: json_data} else render json: { status: 'ERROR', data: like.errors } end end def destroy @like.destroy render json: { status: 'SUCCESS', message: 'Delete the post', data: @like} end def update data = { 'user_id': @user.id, 'post_id': params[:post_id], 'suki': params[:suki] } if @like.update(data) @post = Post.find(params[:post_id]) @user = @post.user json_data = { 'post': @post, 'user': { 'name': @user.name, 'nickname': @user.nickname, 'image': @user.image }, 'like': @like } render json: { status: 'SUCCESS', message: 'Updated the post', data: json_data } else render json: { status: 'SUCCESS', message: 'Not updated', data: @like.errors } end end private def set_like @user = User.find_by(id: current_api_v1_user.id) @like = Like.find_by(user_id: @user.id, post_id: params[:post_id]) end def like_params params.require(:like).permit(:post_id, :user_id, :suki) end end end end
- 一般的なCRUD用のAPIの記述かな...
- showでは、該当するlkeのデータが無かったときはエラーになるのを避けるために「suki: 3」として返している。まだ投票していないのかアクセスエラーなのかを区別するために...
Postmanを使ってテスト
- likeをPOSTしたときに、suki_countが+1され、自動的にsuki_percent, all_countが計算されているのがわかる。
- 投稿日:2020-01-13T08:24:56+09:00
はじめてAWSでデプロイする方法⑤(EC2の環境構築、Ruby, MySQL)
前回までの記事
はじめてAWSでデプロイする方法①(インスタンスの作成)
はじめてAWSでデプロイする方法②(Elastic IPの作成と紐付け)
はじめてAWSでデプロイする方法③(AWSセキュリティグループの設定)
はじめてAWSでデプロイする方法④(EC2インスンタンスにSSHログイン)EC2インスタンス(サーバー)を作成し、パブリックIPをElastic IPで固定。
一般ユーザーがアクセスできるように、セキュリティグループの設定を追加(入り口を作成)
IDとPWを使って、EC2にログインざっくり説明すると、こんなところです。
今回の内容
ssh接続でログインをして、環境構築をする。
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y update[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install git make gcc-c++ patch libyaml-devel libffi-devel libicu-devel zlib-devel readline-devel libxml2-devel libxslt-devel ImageMagick ImageMagick-devel openssl-develNode.jsをインストール
[ec2-user@ip-172-31-25-189 ~]$ sudo curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash -[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install nodejsrbenvとruby-buildをインストール
[ec2-user@ip-172-31-25-189 ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv[ec2-user@ip-172-31-25-189 ~]$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile[ec2-user@ip-172-31-25-189 ~]$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile[ec2-user@ip-172-31-25-189 ~]$ source .bash_profile上記をしないと エラー『 -bash: rbenv: コマンドが見つかりません 』が表示
[ec2-user@ip-172-31-25-189 ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build[ec2-user@ip-172-31-25-189 ~]$ rbenv rehashRubyをインストール
[ec2-user@ip-172-31-25-189 ~]$ rbenv install 2.5.1ここでエラー
「-bash: rbenv: コマンドが見つかりません」が表示された場合、rbenvとruby-buildをインストールを見直してください。
[ec2-user@ip-172-31-25-189 ~]$ rbenv global 2.5.1[ec2-user@ip-172-31-25-189 ~]$ rbenv rehash[ec2-user@ip-172-31-25-189 ~]$ ruby -v無事にインストールされています
MySQLをインストール
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install mysql56-server mysql56-devel mysql56(mysql56は、MySQLのバージョン5.6をインストールすることを意味)
MySQLを起動
[ec2-user@ip-172-31-25-189 ~]$ sudo service mysqld startMySQLの起動確認
[ec2-user@ip-172-31-25-189 ~]$ sudo service mysqld statusMySQLのrootパスワードの設定
この操作はPW設定をします
>PWを入力してから、コマンドを実行してください[ec2-user@ip-172-31-25-189 ~]$ sudo /usr/libexec/mysql56/mysqladmin -u root password 'PWを入力'ここでエラーが表示された場合
EC2 MySQL 初期設定 root にパスワードの設定MySQLに接続(ターミナルでコマンド操作可能)
[ec2-user@ip-172-31-25-189 ~]$ mysql -u root -pここでPWが要求されます。
>登録したPWを入力してみて、無事に開かれるか確認しましょう!PWが正しく登録できたことを確認できたので、
$ exitでMySQLの接続を解除しましょう
- 投稿日:2020-01-13T08:22:16+09:00
#Ruby or #Rails で unixtimestamp の数値を Time.zone の日時に変換する
In Ruby need require
require 'active_support/core_ext'Time
Time.at(1580655600) # => 2020-02-02 15:00:00 +0000Timezone UTC
Time.use_zone('UTC') { Time.zone.at(1580655600) } # => Sun, 02 Feb 2020 15:00:00 UTC +00:00Timezone JST
Time.use_zone('Tokyo') { Time.zone.at(1580655600) } # => Mon, 03 Feb 2020 00:00:00 JST +09:00Original by Github issue
- 投稿日:2020-01-13T08:22:15+09:00
#Ruby on #Rail / Convert unix timestamp to date with Time.zone ( UTC / JST Tokyo examample )
In Ruby need require
require 'active_support/core_ext'Time
Time.at(1580655600) # => 2020-02-02 15:00:00 +0000Timezone UTC
Time.use_zone('UTC') { Time.zone.at(1580655600) } # => Sun, 02 Feb 2020 15:00:00 UTC +00:00Timezone JST
Time.use_zone('Tokyo') { Time.zone.at(1580655600) } # => Mon, 03 Feb 2020 00:00:00 JST +09:00Original by Github issue
- 投稿日:2020-01-13T07:54:43+09:00
Rails6 rails consoleからカラムのデータ型を確認する
- 投稿日:2020-01-13T07:02:21+09:00
初心者によるプログラミング学習ログ 209日目
100日チャレンジの209日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
209日目は
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) January 12, 2020
209日目
progateでjquery上級編、rubyをⅢまで
技術書でwebサイトコーディング#100DayOfCode#駆け出しエンジニアと繋がりたい #早起きチャレンジ