20200709のRubyに関する記事は18件です。

find_or_create_by

find_or_create_by

引数の条件に指定するデータがあった場合はそのデータを返します。無かった場合は新規作成します。

def find_or_create_by(attributes, &block)
  find_by(attributes) || create(attributes, &block) 
end

メリット

結果が冪等になる。
冪等とは何度行っても得られる結果が等しいこと。

すでに指定するデータが存在した場合は新規作成をしないので、
find_or_create_byを繰り返したとしても、得られる結果が等しいです。

対してcreateメソッドだった場合は、
繰り返した分だけ新規作成が行われるので冪等ではないと言えます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsとAjaxを使ったいいね機能の非同期通信

転職活動用に個人アプリを開発中です。
今回、RailsとAjaxを使って、いいね機能の非同期化を行いました。

AjaxではjQueryを使うため、jQueryを使えるようにしておく事前準備が必要です。それは参考記事を見てください。以下の記述はそれが設定済みのうえでの話です。

PFCMASTERいいね機能Ajax.png

実現した機能

・「いいね」ボタンを押すとリロードせずに「いいねを取り消す」に表示が変わる
・「いいね」ボタンを押すとリロードせずにlikesテーブルにデータが1つ追加される
・「いいね」ボタンを押すとリロードせずにいいね数が1つ増える
※その逆もしかり

このコードでうまくいきました

コントローラー(likes_controller.rb)

likes_controller.rb
class LikesController < ApplicationController

  def create
    @post = Post.find(params[:post_id])
    @like = current_user.likes.build(post_id: params[:post_id])
    @like.save
    @likeCounts = Like.where(post_id: params[:post_id])
  end

  def destroy
    @post = Post.find(params[:post_id])
    @like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
    @like.destroy
    @likeCounts = Like.where(post_id: params[:post_id])
  end

end

個別の投稿ページ(上記の画像のページ)

show.html.haml
.like
  = render partial: "likes/like", locals: {post: @post}

部分テンプレート(_like.html.haml)

_like.html.haml
- if user_signed_in?
  - if current_user.already_liked?(post)
    = button_to 'いいねを取り消す', post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true
  - else
    = button_to 'いいね', post_likes_path(post.id), method: :post, remote: true
.likeCounts
  いいね数:
  = post.likes.count

更新したい部分のビュー(いいねしたとき)

create.js.erb
$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

更新したい部分のビュー(いいねを取り消すとき)

destroy.js.erb
$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

JavaScriptが動く仕組み

link_tobutton_toには:remoteオプション(remote: true)がある。button_toremote: trueを追加することで、js形式のリクエストを送信できるようになる。

= button_to 'いいね', post_likes_path(post.id), method: :post, remote: true

参考:Railsガイド

非同期通信の流れ

1.「いいね」ボタンを押す
2.下記のリンクでlikes#createにjs形式でリクエストが飛ばされる

= button_to 'いいね', post_likes_path(post.id), method: :post, remote: true

3.likesコントローラーのcreateアクションが動き、いいねが保存される。Likeモデルを介してデータベースに追加される(リロードせずに)
4.更新したい部分のページcreate.js.erb,destroy.js.erbがレスポンスとして返される。.html(jQueryのhtmlメソッド)は、.like(likeクラス)の部分をhtmlの後ろの( )内に置き換える役割をはたす。

今回苦労したところ

1.部分テンプレートの理解

create.js.erb
$('.like').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

partialオプション:部分テンプレートの呼び出しを行う。今回はlikesフォルダの_like.html.hamlを呼び出したいのでlikes/likeとなる。

localsオプション:部分テンプレート内で{ }内の左辺が変数として使えるようになる。今回でいうとpostが変数として使えるようになる。右辺の@postは何かというと、右辺の@postが左辺のpostに代入して、それが変数として使えるようになる。右辺の@postはどこからきているかというと、postsコントローラのshowアクションで定義しているので、そこからきている。

posts_controller.rb
def show
  @post = Post.find(params[:id])
  Like.new
end

2._like.html.hamlbutton_toのpathの設定
ずっとこのエラーに悩まされていました。

ActionView::Template::Error No route matches (中略) missing required keys: [:id])

この記事を見つけてようやく下記のように記述して解決できました。

= button_to 'いいねを取り消す', post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true

解決はしたものの、この部分id: post.likes[0].idがまだちゃんと理解できていません。
1つの投稿に複数のいいねがついていたとして、その最初のいいねのidを取得している?

今考えてみると、確かに1つの投稿に複数のいいねがある場合、「どのいいねを取り消すの?指定してくれないとわからないよ」と言われても無理ないなと思いました。

だとすると、必ずしもcurrent_userの付けたいいねではなく、他の人の付けたいいねを取り消してしまう?
まだ修正する必要があるかもしれません?

(追記)
Sequel Proで確認したところ、他の人のいいねを取り消してしまうことはなく、ちゃんとcurrent_userのいいねが取り消されていました。

一応いいねを消せることは消せます。
この点が明らかになったらまた追記します。

参考記事

Railsで remote: true と js.erbを使って簡単にAjax(非同期通信)を実装しよう!(いいね機能のデモ付)
【Rails×Ajax】いいね機能ハンズオン
Railsでいいね機能を実装。Ajaxを使い非同期対応。で

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Digdag公式ドキュメントからDigdagを学ぶ-アーキテクチャ

#目次

Getting started
Architecture
Concepts
Workflow definition
Scheduling workflow
Operators
Command reference
Language API -Ruby
REST API
Internal architecture
Release Notes

Digdag Architecture

Automating workflow with Digdag

ワークフローは手動の動作を自動化します。
一連のタスクをワークフローとして定義します。
Digdagは実行し続けます。タスクはオペレータープラグインを使用して定義されるため、中央のワークフローエンジンから多数の種類のシステムをコントロールできます。

プラグインのランタイムフレームワークとして、Digdagはワークロードの自動化に関する残りの問題を処理し、自動化に集中できるようにします。
タスクが失敗した場合、Digdagはアラートを送信します。ワークフローが想定時間内に完了しない場合、Digdagは通知を送信します。
タスクは、ローカルマシン、分散サーバー、またはDockerコンテナーで実行できます。

グループによるタスクの組織化

複雑なワークフローを自動化すると定義はすぐに複雑になります。 Digdagを使用するとタスクをグループに整理できます。
定義を確認する時に概要から見て詳細に移ると思います。
図の上の部分が概要部分でやろうとするのがデータ準備・分析・評価の流れだとすぐわかります。その後各グループの詳細を見ることで開発中デバッグとレビューが簡単になります。管理者は本番環境で何が起こっているのか問題を修正する方法は何かすぐわかります。

データ準備グループ、分析グループ、評価クループ
Untitled Diagram.png

依存する兄弟タスクがない場合、またはすべての兄弟タスクが正常に完了した場合にタスクが開始されます。
グループの親タスクが実行されるとその子タスクが実行されます。それらのすべてが正常に完了すると親タスクも正常に完了します。子タスクが失敗すると失敗した子タスクとその親タスクも失敗します。
ルートタスクが完了するかもしくは失敗すると実行全体が終了します。

Parameters

タスクのグループ化はタスク間でパラメーターを渡すためにも使用されます。
親タスクは子タスクのため変数をエクスポートできます。(例:UNIXシェルのエクスポートコマンドが環境変数を設定するように)。親タスクは実行時に子タスクを生成できるため前のタスクの結果に応じてさまざまなタスクを実行できます。

Workflow as code

Digdagワークフローはコードで定義されます。これによりソフトウェア開発のベストプラクティス(バージョン管理、コードレビュー、テスト、プルリクエストを使用したコラボレーション)がもたらされます。ワークフローをgitリポジトリにプッシュすると誰でもそれをプルして同じ結果を再現できます。

Running with local mode

Digdagは、単一ファイルの実行可能コマンドです。
新しいワークフローの作成と実行はMakefileと同じくらい簡単です。

*.dig 拡張子ファイルはワークフロー定義に使用されます。
digdag run my_workflow.digコマンドはワークフローを実行します。

ローカルマシンでワークフローを開発してテストが終わったらワークフローを定期的に実行するためにサーバーにプッシュする必要があります。

Running on a server

同じディレクトリに存在する* .digファイルとその他のファイルはプロジェクトと呼ばれます。
全てのプロジェクトをDigdagサーバーにプッシュしてその結果サーバー上でワークフローを実行できるようになります。
少し早いですが、前回作ったWorkflowをサーバーにPushしたら以下のようになります。

Digdagサーバー起動
$ digdag init mydag
$ cd mydag
$ digdag run mydag.dig
$ digdag server --memory
WorflowにProject追加
$digdag push mydag

2020-07-09 20:39:20 +0900: Digdag v0.9.41
Creating .digdag/tmp/archive-3560803829245476890.tar.gz...
  Archiving mydag.dig
Workflows:
  mydag.dig
Uploaded:
  id: 1
  name: mydag
  revision: b8b9abb8-b156-4089-a009-a01aa0337d9f
  archive type: db
  project created at: 2020-07-09T11:39:21Z
  revision updated at: 2020-07-09T11:39:21Z

Use `digdag workflows` to show all workflows.

スクリーンショット 2020-07-09 20.39.46.png

Running tasks on Docker

Dockerを利用してコンテイナー中のタスクを実行できます。
dockerオプションが設定されていたらタスクはDockerコンテイナーで実行されます。

まだ詳細タスクについて学んでないので以下のコードの説明はWorkflow definition部分で説明する予定

_export:
  docker:
    image: ubuntu:14.04

+step1:
  py>: tasks.MyWorkflow.step1

Digdagは、プルされたイメージをキャッシュして再利用します。デフォルトでDigdagは更新があった場合でもキャッシュされたイメージを一貫して使用します。 pull_always:trueオプションを設定してDigdagが更新をチェックしタスクが開始されるたびにタグの最新のイメージをプルすることができます。

_export:
  docker:
    image: ubuntu:latest
    pull_always: true

+step1:
  py>: tasks.MyWorkflow.step1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Digdag公式ドキュメントからDigdagを学ぶ-Getting started

Digdagについて

DigdagはOpenソースのマルチクラウドワークフローエンジンの事です。
簡単に言うとなんなかの仕事を順序に処理する流れを手伝ってくれるツールかと思いました。

目標

Digdagについての本などがないためDigdagの公式サイトのドキュメントを翻訳しながらQiitaに整理したいと思います。
http://docs.digdag.io/architecture.html
ドキュメントは英語になっています。
低い英語力から小学生レベルの日本語に翻訳しながら勉強するので誤訳などあると思います。
今日はGetting Started部分からやります。
DigdagのRubyを使ってRailsにバッチを作るまでが最後の目標になります。

#目次

以下のような内容になります。
Getting started
Architecture
Concepts
Workflow definition
Scheduling workflow
Operators
Command reference
Language API -Ruby
REST API
Internal architecture
Release Notes

Getting started

1. 最新バージョンのDigdagダウンロードと設置

Digdagは実行ファイルです。curlコマンドを使って~/binにファイルを設置できます。

$ curl -o ~/bin/digdag --create-dirs -L "https://dl.digdag.io/digdag-latest"
$ chmod +x ~/bin/digdag
$ echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

digdag --helpが実行できれば環境設定完了です。

zshを使う方は~/.bashrc~/.zshrcに変更してください。

Windows

PowerShell -Command "& {[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12; mkdir -Force $env:USERPROFILE\bin; Invoke-WebRequest http://dl.digdag.io/digdag-latest.jar -OutFile $env:USERPROFILE\bin\digdag.bat}"
setx PATH "%PATH%;%USERPROFILE%\bin"

2. サンプルを実行してみる

digdag init <dir>コマンドはサンプルWorkflowを生成する。

$ digdag init mydag
$ cd mydag
$ digdag run 

digdag init mydagコマンドで以下のようなサンプルコード(mydag.dig)が生成される

mayday.dig
timezone: UTC

+setup:
  echo>: start ${session_time}

+disp_current_date:
  echo>: ${moment(session_time).utc().format('YYYY-MM-DD HH:mm:ss Z')}

+repeat:
  for_each>:
    order: [first, second, third]
    animal: [dog, cat]
  _do:
    echo>: ${order} ${animal}
  _parallel: true

+teardown:
  echo>: finish ${session_time}

digdag runWorkflowが実行された
実行結果
スクリーンショット 2020-07-09 13.19.57.png

次回はArchitecture

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【徹底解説】初心者必見の関数(メソッド)の理解の仕方


元の投稿

https://wp.me/pakwHz-cr


プログラミングの入門をする上で最も大事と言っても過言ではない関数またはメソッド(以下、どちらも関数と呼びます)

皆さんは関数と聞くと何を思い浮かべますか?

一般的には、数学の授業で聞く言葉だと思います。

ではどうして、プログラミングの世界でメソッドの事を関数と呼ぶことになったのでしょうか?

それは数学の関数と概念が非常に似ているからです。

そこで、まずは数学的な関数を見てみましょう。数学アレルギーの人も拒絶せず難しい話はしませんので、読み進めてもらえると嬉しいです。

まず、関数とは何でしょう?

関数とはある任意のxを与えると必ず1つのy(解)を返してくれるものです。ちょっと言葉だけでは難しいですね。

それでは、数学的な関数とは代表的なものに何があるでしょうか?

私が一番はじめに思い浮かべる関数は

y = ax + b

です。これは一次関数と呼ばれるものですがaとbは定数(任意で数を決めれる)ので具体的に

y = 2x + 3

を見てみましょう。

この関数はxを指定するとある数字が答えを出してくれます。例えば、x = 3であれば yの値は9になります。これはいろんな数字に当てはめれますよね。

なので、関数とは特定のXを与えると1つのYを返してくれるものです。

image

この関数のようなものは数学に限られてるものではありません。

例えば、食材を与えると美味しい料理を作るシェフがいるとします。このシェフはある意味、関数のような働きをしていると言えましょう。

具体的にはこのシェフに「人参、じゃがいも、牛肉、カレー粉」をXとして、渡してあげると、このシェフはカレーというYの値を返してくれます。

image

このようなXを与えるとYを返すというのが、一般的な関数の概要です。

なので、プログラミング的な関数(メソッド)も同じだと言えます。

ただ、プログラミングでは上記のXを引数と呼び、Yを戻り値のといいます。

image

それでは実際のコードを見ていきましょう。

今回はRubyコードを使用していますが、主要なプログラミング言語ではこの概念は変わりません。

それでは与えられた2つの数字(引数)を足し算をするメソッドを作成しましょう

# メソッド名はadd
# 引数はa, b

# どんなメソッドを作るか、どんな引数を引き受けるかは自分で決めるので、
# メソッド名と引数の名前は自由に名前を決めることが出来ます。

# メソッドの定義(作成
def add(a, b)
  return a + b
end

# メソッドの呼び出し(使用)
add(3, 2)
# =>戻り値は5になる

このコードを図式化すると以下のようになります

ここで新しく紹介した概念が

「呼び出しのコードそのものが戻り値に変わる」

ということです。これは非常に大事な概念なのでしっかりと理解してください。

つまり、

result = add(2, 3)

というコードは

result = 5

というコードとしているのと全く同じです。

ここで、どうして 5 と直接書くのではなく add(2,3) と書くのかというといくつか理由があります

まず、この例題では足し算をするメソッドを使用しているので

頭の中で5とすぐに戻り値を予想できますがこれがすごく複雑な処理だと

戻り値が予想できないのでメソッドを使用しないといけません。

次に、今回の場合では引数(a, b)が予め決められていますが

これは実際には変数になっていたりしますのでメソッドを使用する前に答えを予想することができなくなります。

ここまでで、メソッドの基本的な概念は終わりです。

プログラミングではたくさんの関数(メソッド)を使用します。

書いているもののほとんどが関数と言っても過言ではありません。

関数を定義することも多々ありますが、大きくは他人が作成した関数をしようするということがほとんどです。

その際に、引数として何が必要でどのような戻り値をくれるのかという概念で関数をみると

簡単に理解して関数の詳細を理解しなくても使用することが出来ます。

なので、関数(メソッド)を使用または定義する際は引数と戻り値に注目してみましょう。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Enumの設定ミス。なぜかEnum指定の列からデータが取れない…

発生した背景

※備忘録として記録します(ネタ的にはしょぼいです…)
以下のようなモデルにEnum設定したとき、データが取り出せない。

table.rb
class ColorManage < ApplicationRecord
  enum color_type: { single: 0, double: 1, graphic: 2 }
(省略)

(前提)singleというラジオボタンを押すと、0が入力されるようにする。

color-edit.html.erb
  <!--  カラータイプ、どの設定にするか。-->
  <%= color_manage_record.radio_button :color_type, :single ,class:"color-form__button" %>
  <%= color_manage_record.label :color_type, "single-color", {class: "color-form__tag"} %>

問題点> 画像のとおり、color_typeには"ゼロ"で入力されていますが、モデル抽出して、列値を取得しようとしてもnullで返ってくる

image.png

環境

項目 内容
OS.Catalina v10.15.4
Ruby v2.5.1
Ruby On Rails v5.2.4.3
MySQL V5.6

対応手順

原因)テーブルの定義がstringだったので、enumを以下のように定義しなおしました。ポートフォリオなので、恥ずかしながら、設計側を変えた次第です。

test.rb
  enum color_type: { single: "single", double: "double", graphic: "graphic" }

これで解決しました。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Timeオブジェクトの次の値ってどうやってだすのが正しい?

概要

RubyでTimeオブジェクトを扱ってると境界問題にぶち当たる。
普通にやってるとRangeオブジェクトなりを使ってそこまで問題になることはないが、普通じゃないやり方をしていて躓いた。

環境

Ruby: 2.6.2
Rails: 5.1.2

そもそもハマった問題

Time.now.end_of_day
=> 2020-05-15 23:59:59 +0900

これの次の値を取りたい。
※ end_of_dayはわかりやすいから使ってるだけであって、とある日の先端、終端が欲しいわけではない。飽くまでもTimeオブジェクトの次の値

真っ先に思いついたのがこれ

Time.now.end_of_day + 1
=> 2020-05-16 00:00:00 +0900

ただ、これは間違い

(Time.now.end_of_day + 1).iso8601(3)
=> "2020-05-16T00:00:00.999+09:00"

+ 1 は飽くまでも1秒加算であって、次の値に移行するわけではないので使えない。

Timeクラスを漁ってるとすごくそれっぽいメソッドを見つけた
https://docs.ruby-lang.org/ja/latest/method/Time/i/succ.html

Time.now.end_of_day.succ
(pry):109: warning: Time#succ is obsolete; use time + 1
=> 2020-05-16 00:00:00 +0900

しかし、時代遅れだから使うなって怒られる上 + 1 と同じことしてるだけらしい。だめ。

解決方法

結局良さげな方法はわからなかったからゴリ押し

例の問題だと少数9桁までしか見ていない

(Time.now.end_of_day + 1).iso8601(10)
=> "2020-05-16T00:00:00.9999999990+09:00"

つまり、1ナノ秒加算してやればいいわけだ

(Time.now.end_of_day + 1/1000000000.0).iso8601(9)
=> "2020-05-16T00:00:00.000000000+09:00"

できた!

おまけ

Time.now.iso8601(10)
=> "2020-05-15T19:56:55.4979370000+09:00"

Time.new(2020, 5, 16, 16, 59, 59.3).iso8601(9)
=> "2020-05-16T16:59:59.299999999+09:00"

Time.parse("2020-05-16T16:59:59.3+09:00").iso8601(9)
=> "2020-05-16T16:59:59.300000000+09:00"

Time.parse("2020-05-16T16:59:59.35555555555555555555+09:00").iso8601(20)
=> "2020-05-16T16:59:59.35555555555555555555+09:00"

だめだった

結論

  • Timeオブジェクト内の小数秒の桁数は無限に持てる
  • そのため次の値という概念がそもそも無い
  • ただ、Timeオブジェクトの生成の仕方に依存するので、DBから引っ張ってくるならその有効桁数を考慮してやれば無理やり作れなくはない
  • そもそも素直にRangeオブジェクトを使うなりして比較すべき
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails5でECサイトを作る⑥ ~seedデータ投入~

はじめに

架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る⑤の続きです。
この後の実装を続けていく上で、初期データを入れておいた方が何かと都合が良さそうなので、一旦そちらを優先することにしました。

複数のseedファイルを管理する

あくまで表示が正しくできるか確認するためのデータなので、最小限作っておけば良いと思います。しかし、Model数が多い分、やはり一つでファイルで全て済まそうとすると記述量が膨大になってしまいます。
そこで、新たにseedsというフォルダを作り、その中に各Model毎の初期データを入れることにしました。(一つのseeds.rbファイルで済ませたい人はこちら

$ cd work/fumizuki/db
$ mkdir seeds
$ cd seeds
$ touch customer.rb
$ touch address.rb
$ touch genre.rb
$ touch product.rb
$ touch order.rb
$ touch order_item.rb

ファイル群の読み込み、Adminデータ作成

作成したファイル群をseeds.rbファイルから読み込めるようにします。これでrails db:seedを叩いた時にseedsフォルダ内のデータも認識されます。
ここで注意が必要な点は、親Modelを上に記述することです。ファイルの読み込みは上の行から順番に行われるため、子Modelを先にしてしまうと「Customerデータがありません!」とエラーになります。

db/seeds.rb
require './db/seeds/customer.rb'
require './db/seeds/address.rb'
require './db/seeds/genre.rb'
require './db/seeds/product.rb'
require './db/seeds/order.rb'
require './db/seeds/order_item.rb'

Admin.create!(
  id: 1,
  email: 'admin@user',
  password: 'adminuser',
  password_confirmation: 'adminuser'
  )

Adminのデータもこちらに書いておきました。管理者のデータは1件あれば十分で、わざわざ別ファイルに分けるほどのコード量ではないので。

データをたくさん作る

架空の人名架空の地名はいつものサイトで調達します。

Address

db/seeds/.rb
Address.create!(
  [
    {
      id: 1,
      customer_id: 1,
      addressee: '稲継亜矢子',
      post_code: '1111111',
      address: '月川県岩青山町四南寺2-15',
    },

    {
      id: 2,
      customer_id: 2,
      addressee: '鈴鹿由美子',
      post_code: '2222222',
      address: '細野県城見市世史が丘3-1-7',
    },

    {
      id: 3,
      customer_id: 3,
      addressee: '関沢智恵美',
      post_code: '3333333',
      address: '赤田県初山市十越智町6-12',
    },

    {
      id: 4,
      customer_id: 4,
      addressee: '巻譲',
      post_code: '4444444',
      address: '根野県羽島市後目台8-12',
    },

    {
      id: 5,
      customer_id: 5,
      addressee: '岡崎弥生',
      post_code: '5555555',
      address: '古岡県紫波市刈唯山3-5-2',
    },
  ]
  )

Customer

db/seeds/.rb
Customer.create!(
  [
    {
      id: 1,
      email: '1@1',
      family_name: '長岡', family_name_kana: 'ナガオカ',
      first_name: '聡美',   first_name_kana: 'サトミ',
      post_code: '1111111',
      address: '北岡県乙西川市馬城寺2-6-1',
      tel: '11111111',
      is_active: true,
      password: '111111',
      password_confirmation: '111111',
    },

    {
      id: 2,
      email: '2@2',
      family_name: '野崎', family_name_kana: 'ノザキ',
      first_name: '健吾',   first_name_kana: 'ケンゴ',
      post_code: '2222222',
      address: '鳥川県盤上山市升沖ヶ丘1-15',
      tel: '22222222',
      is_active: true,
      password: '222222',
      password_confirmation: '222222',
    },

    {
      id: 3,
      email: '3@3',
      family_name: '妹尾', family_name_kana: 'セオ',
      first_name: '千代美',   first_name_kana: 'チヨミ',
      post_code: '3333333',
      address: '仁田県那珂和町山巻学園3-2',
      tel: '33333333',
      is_active: true,
      password: '333333',
      password_confirmation: '333333',
    },

    {
      id: 4,
      email: '4@4',
      family_name: '塚越', family_name_kana: 'ツカゴシ',
      first_name: '貴美',   first_name_kana: 'タカミ',
      post_code: '4444444',
      address: '笠川県冬田市志林川町5-2-7',
      tel: '44444444',
      is_active: true,
      password: '444444',
      password_confirmation: '444444',
    },

    {
      id: 5,
      email: '5@5',
      family_name: '鎌田', family_name_kana: 'カマタ',
      first_name: '幸宏',   first_name_kana: 'ユキヒロ',
      post_code: '5555555',
      address: '早田県響山市大字威初2-16',
      tel: '55555555',
      is_active: true,
      password: '555555',
      password_confirmation: '555555',
    },

  ]
  )

Genre

db/seeds/.rb
Genre.create!(
  [
    {
      id: 1,
      name: '食パン',
      validity: true
    },

    {
      id: 2,
      name: '総菜パン',
      validity: true
    },

    {
      id: 3,
      name: '菓子パン',
      validity: true
    },

    {
      id: 4,
      name: '限定',
      validity: true
    },

    {
      id: 5,
      name: 'その他菓子',
      validity: true
    },
  ]
  )

Order

db/seeds/.rb
Order.create!(
  [
    {
      id: 1,
      customer_id: 1,
      addressee: '稲継亜矢子',
      post_code: '1111111',
      send_to_address: '月川県岩青山町四南寺2-15',
      how_to_pay: true,
      order_status: 0,
    },

    {
      id: 2,
      customer_id: 2,
      addressee: '鈴鹿由美子',
      post_code: '2222222',
      send_to_address: '細野県城見市世史が丘3-1-7',
      how_to_pay: true,
      order_status: 2,
    },

    {
      id: 3,
      customer_id: 3,
      addressee: '関沢智恵美',
      post_code: '3333333',
      send_to_address: '赤田県初山市十越智町6-12',
      how_to_pay: true,
      order_status: 1,
    },

    {
      id: 4,
      customer_id: 4,
      addressee: '巻譲',
      post_code: '4444444',
      send_to_address: '根野県羽島市後目台8-12',
      how_to_pay: false,
      order_status: 2,
    },

    {
      id: 5,
      customer_id: 5,
      addressee: '岡崎弥生',
      post_code: '5555555',
      send_to_address: '古岡県紫波市刈唯山3-5-2',
      how_to_pay: true,
      order_status: 0,
    },
  ]
  )

OrderItem

db/seeds/.rb
OrderItem.create!(
  [
    {
      id: 1,
      order_id: 1,
      product_id: 10,
      quantity: 4,
      order_price: 270,
      make_status: 2,
    },

    {
      id: 2,
      order_id: 1,
      product_id: 7,
      quantity: 6,
      order_price: 230,
      make_status: 1,
    },

    {
      id: 3,
      order_id: 2,
      product_id: 2,
      quantity: 2,
      order_price: 260,
      make_status: 0,
    },

    {
      id: 4,
      order_id: 3,
      product_id: 3,
      quantity: 6,
      order_price: 180,
      make_status: 1,
    },

    {
      id: 5,
      order_id: 4,
      product_id: 4,
      quantity: 8,
      order_price: 370,
      make_status: 1,
    },

    {
      id: 6,
      order_id: 5,
      product_id: 5,
      quantity: 10,
      order_price: 160,
      make_status: 2,
    },
  ]
  )

Product

db/seeds/.rb
Product.create!(
  [
    {
      id: 1,
      name: 'ブリオッシュ',
      introduction: 'バターの風味豊かな食パンです。',
      genre_id: 1,
      price: 500,
      status: true,
    },

    {
      id: 2,
      name: 'バゲット',
      introduction: '国産小麦100%のフランスパン。',
      genre_id: 1,
      price: 260,
      status: true,
    },

    {
      id: 3,
      name: 'カレーパン',
      introduction: '中辛のカレーを、甘いパン生地と組み合わせました。',
      genre_id: 2,
      price: 180,
      status: true,
    },

    {
      id: 4,
      name: 'ハンバーガー',
      introduction: '自家製のバンズに具材をたくさん挟み込みました。',
      genre_id: 2,
      price: 370,
      status: true,
    },

    {
      id: 5,
      name: 'あんぱん',
      introduction: 'どこから食べても美味しい斬新なあんぱんです。',
      genre_id: 3,
      price: 160,
      status: true,
    },

    {
      id: 6,
      name: 'クリームパン',
      introduction: '当店こだわりのカスタードをお楽しみください。',
      genre_id: 3,
      price: 170,
      status: true,
    },

    {
      id: 7,
      name: 'パン・オ・ショコラ',
      introduction: '当店の一番人気です♪',
      genre_id: 3,
      price: 230,
      status: true,
    },

    {
      id: 8,
      name: 'ラウゲン・ブロートヒェン',
      introduction: 'ドイツ風のもっちりパン。',
      genre_id: 4,
      price: 200,
      status: true,
    },


    {
      id: 9,
      name: 'いちごのお花パン',
      introduction: 'ふんわりとした生地に苺の風味を加えました。',
      genre_id: 4,
      price: 180,
      status: true,
    },

    {
      id: 10,
      name: 'モンブラン',
      introduction: 'ブリオッシュにマロンクリームを合わせてどうぞ。',
      genre_id: 5,
      price: 270,
      status: true,
    },

    {
      id: 11,
      name: 'パンケーキ',
      introduction: '強力粉からつくるしっとりしたパンケーキです。',
      genre_id: 5,
      price: 150,
      status: true,
    },
  ]
  )

データをまとめて作成

もし見た目にこだわりがなく、「文月太郎1」「文月太郎2」のようなデータで構わない場合は、複数のデータを一気に作ることもできます。

db/seeds.rb
Admin.create!(
  id: 1,
  email: 'admin@user',
  password: 'adminuser',
  password_confirmation: 'adminuser'
  )

50.times do |n|
  Customer.create!(
                   email: "1@#{n}",
                   family_name: "文月#{n}",
                   family_name_kana: "フミヅキ#{n}",
                   first_name: "太郎#{n}",
                   first_name_kana: "タロウ#{n}",
                   post_code: "1111111",
                   address: "横岡県氷川市絵向寺#{n}丁目",
                   tel: "11111111111",
                   is_active: true,
                   password: "111111",
                   password_confirmation: "111111",
                   )

  Address.create!(
                  customer_id: 1,
                  addressee: "文月なな#{n}",
                  post_code: '1111111',
                  address: '三橋県東里見町松林2-15-#{n}'
                   )

  Genre.create!(
                name: "ジャンルその#{n}",
                validity: true
                )

  Product.create!(
                  name: "自家製パン#{n}",
                  introduction: "#{n}倍美味しくなりました!(当社比)",
                  genre_id: 1,
                  price: 260,
                  status: true,

  Order.create!(
                customer_id: 1,
                addressee: "文月花子#{n}",
                post_code: "1111111",
                send_to_address: "遊明県鳥窪町",
                how_to_pay: true,
                order_status: 1,
               )

  OrderItem.create!(
                    order_id: 1,
                    product_id: 5,
                    quantity: 10,
                    order_price: 600,
                    make_status: 1,
                   )

end

後記

seedデータはいつ入れるのが適切なのか、よく分かりませんがとりあえず入れました。Modelの設定を終えた段階で一通り作ってしまうのが、一番スムーズかも知れません。データが入っていなくてもアプリ自体は作れるので、余計に迷うところです。

アプリ開発をする上で、どのような順序で進めるのが最も効率的なのか判断しきれずに、かなりめちゃくちゃな順番でやっているのではという気がしています。このECサイトを仕上げたら、解説記事たちも順番を入れ替えて、サクサク実装できるチュートリアルにまとめたいと思います。

一番の問題は「完成までこぎつけるかどうか」ですけどね! 次回へ続く!

参考

railsのseedの書き方いろいろ
Rails・seedファイルを分割して管理する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 二次元配列

二次元配列とは?

2次元配列とは、プログラムで利用される配列において、配列の中に配列が入っている配列のことである。

ソースコード

fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]] # 配列Key 合計額算出

fruits_price.each do |fruit|
  sum = 0
  fruit[1].each do |price|
    sum += price
  end
  puts "#{fruit[0]}の合計金額は#{sum}円です"
end

結果

appleの合計金額は670円です
orangeの合計金額は300円です
melonの合計金額は2700円です

説明

keyにフルーツの名前、valueにそのフルーツの値段が配列で複数

[["apple", [200, 250, 220]]

Ruby each文

オブジェクト.each do |変数|
  実行する処理1
  実行する処理2
end

こちらで要素を一つづつ取り出しています。

fruits_price.each do |fruit|
  sum = 0
  fruit[1].each do |price|
    sum += price
  end

ここで、配列の中の価格が入っている配列に対して要素を取り出し、
合計(sum)に格納します。

これで完成です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでyoutubeの動画を埋め込み表示させる方法(編集でも入力したURLを取得できる)

1 概要

 railsでyoutubeの動画を埋め込み表示させたいなと思い、様々な記事を参考にして実装を試みました。ただ、参考記事通りでは自分の思い描いたような実装はできませんでした。それから試行錯誤を繰り返し、バグ等が出ず、編集画面でも入力したURLが生成されるやり方が分かったので、その実装方法を記載します。

1.1 YouTubeURLの種類

 youtubeの動画は11桁の文字列で動画の識別を行っております。その11桁の文字列をyoutubeの埋め込み用のURLに貼り付けてあげれば、railsでも埋め込み動画を表示されるます。

youtubeのURL
https://www.youtube.com/watch?v=0CqFEDzDkIA&t
この文字列で動画の識別を行う  →→ 「0CqFEDzDkIA&t」

ただ、ここに大きな落とし穴があります。
上記URLが生成される条件として、youtubeの動画を0秒から開始する場合のみ、上記のURLが生成されます。
この条件を満たしていないと、さらに長いURLが生成されるので、参考記事 通り作成しても、埋め込み動画が表示されないバグが発生します。

基準を満たしたURLであれば、参考記事でも正しく再生されます

下記の方法でyoutubeのURLを取得すると、動画識別の11文字のコードより長いURLが生成される

- 動画の再生を途中で止めた動画のURL
- 特殊な状況で動画を再生している動画のURL
(例:自身のお気に入り動画の再生画面からURLを取得する等)

(その他にもあるかもしれませんが、どちらにせよURLは長くなる方向で生成されます)

これらを踏まえ、実装の段取りを説明していきます。

2. 実装

著者のバージョン

  • ruby 2.5.1
  • rails 5.2.3
  • mysql 5.7

2.1 事前準備

まず、YouTubeの動画を投稿するだけのサンプルアプリを作っていきます。

ターミナル操作
$ mkdir projects // ディレクトリは各自用意
$ cd projects
$ mkdir sample_app
$ cd sample_app
$ $ rails new youtubetest -d mysql

バージョン指定したい場合は下記のように入力してください
rails -5.2.3- new youtubetest -d mysql

$ rails s

サーバーを起動します。
サーバー立ち上げることができたか確認するため、http://localhost:3000 にアクセスする。いつもの画像が出れば起動は完了です。
aa42578496e362131452b38405a24c75.png

2.2 Gem導入

私は、Hamlで書くことが多いので、gem 'haml-rails'を導入します。

gemfile
gem 'haml-rails'

導入後はrailsをたち下げbundle installを行ってください。

2.3 アプリの作成

ルーティングを記述します。

routes.rb
Rails.application.routes.draw do
  root to: 'products#index'
  resources :products, only: [:index, :new, :create,:edit ,:update, :destroy]
end

コントローラーを作成します。

ターミナル
$ rails g controller products

productsコントローラーに以下を追記してください。

class ProductsController < ApplicationController

  def index
    @products = Product.all
  end

  def new
    @product = Product.new
  end

  def create
    product.create(product_params)
    redirect_to root_path
  end

  def destroy
    product = Product.find(params[:id])
    product.destroy
  end

  def edit
    @product = Product.find(params[:id]) 
  end

  def update
    product = Product.find(params[:id])
    product.update(product_params)
  end

  private
  def product_params
    params.require(:product).permit(:name, :youtube_url)
  end
end

モデルを作成します。

ターミナル
$ rails g model product

マイグレーションファイルに以下のように追加して下さい

db/migrate/xxxxxxxxxxxxxx_create_products.rb
class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :products do |t|
      t.text :name # 追加
      t.string :youtube_url # 追加
      t.timestamps
    end
  end
end

rails:db:migrateをしましょう。

ターミナル
$ rails db:migrate

下準備は終了です
ここからが一番重要な工程になります!!

トップ画面のビューファイルを作ります。
下記コマンド入力してください。

ターミナル
$ touch app/views/products/index.html.haml
$ touch app/views/products/new.html.haml
$ touch app/views/products/edit.html.haml
※ 上から順番に打ち込んでください

トップ画面のビューファイルを作ります。
index.html.hamlに下記のコードを追記してください。

index.html.haml
.contents
  - @products.each do |product|
    .content_post
      = product.name
    .content_post
      %iframe{src: "https://www.youtube.com/embed/#{product.youtube_url[32..42]}",allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture", allowfullscreen: "", frameborder: "0", height: "315", width: "560"}

下記コードは、埋め込みコードの基盤となるコードです。
https://www.youtube.com/embed/
このコードの後ろに、#{post.youtube_url[32..42]} を追記してあげることで、変則的な状態で生成されたURLでも正常に再生できるようになります。

補足説明
 %iframe{src: "https://www.youtube.com/embed/#{product.youtube_url[32..42]}"、以下省略}
# 下記のコードを追記する
"#{product.youtube_url[32..42]}"
# 動画の識別をする11桁の文字列を取得している

続いて、投稿画面と編集画面のviewを作成します。
new.html.hamledit.html.haml`に下記コード追記してください
(ここの工程ではパーシャルテンプレートを用いて大丈夫です)

new.html.haml&edit.html.haml
.new-contents
  .new-contents__content

      = form_with(model: @product, local: true) do |form|
      contents-group
        = form.label :name, "名前"
        = form.text_field :name, placeholder: 'name'

      contents-group
        = form.label :youtube_url, "YouTubeのURL"
        = form.text_field :youtube_url, placeholder: 'youtube_url'
      = f.submit '投稿する'

以上で完成となります!!
少し埋め込みコードに記述を加えるだけで、どんな状態のURL(youtubeに限る)でも動画の埋め込みが正しくでき、動画が再生できると思います。

参考記事

最後に

以上で、「Railsでyoutubeの動画を埋め込み表示させる方法」の説明を終わりたいと思います。間違っている点がありましたらご指摘頂けると助かります。

最後までご覧いただき、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【EMMET 記法】 基本 よく使うやつ エメット記法 まとめ

【メリット】

■ コーディングが早くなります。
■ ミスが少なくなります。

【開発環境】

■ Mac OS catalina
VS code

【やっていきましょう】

HTML版

div.hoge

hoge.html
<div class="hoge"></div>

div#hoge

hoge.html
<div id="hoge"></div>

div*3

hoge.html
<div></div>
<div></div>
<div></div>

div>h2

hoge.html
<div>
  <h2></h2>
</div>

CSS版

h100

hoge.css
heigh: 100px;

w100

hoge.css
width: 100px;

bc

hoge.css
background-color: #fff;

c

hoge.css
color: #fff;

ta

hoge.css
text-align: center;

m10

hoge.css
margin: 10px;

p10

hoge.css
padding: 10px;

【まとめ】

■基本的に使うやつなので、他にもたくさんあります。
■実装5倍くらいのスピードになるので、是非

【合わせて読みたい】

【メソッド集】 rails メソッド まとめ 基礎 随時追加
https://qiita.com/tanaka-yu3/items/89abad875187494bec53

【Javascript】 メソッド まとめ 基礎基本コード メモ
https://qiita.com/tanaka-yu3/items/2438798d159fa402b1d5

【Rails new】Ruby on rails アプリケーション作成
https://qiita.com/tanaka-yu3/items/3fe1ed2852c6513d3583

■参考 emmet記法
https://tracpath.com/works/development/emmet-for-web-developers/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

結局【rails db:migrate】は何をやっているのか?

Railsで当たり前のように使っているrails db:migrateですが実は何となく使っていて実際に何をしているか分かっていない方もいらっしゃるのではないでしょうか?

自分もつい最近までは何となく、あーmigrationファイルをデータベースに反映してるのね~程度にしか理解していませんでした。

しかし深くまで勉強し行くうちに、「あれ、結局Railsのそれぞれのコマンドって何をしてるんだろう」と思ったので今回記事にまとめました。

migrateはmigrationファイルの内容をDBに反映する行為

結論から言うと
migrateはmigrationファイルの内容をDBに反映する行為
になります。

しかしこれだけでは弱いのでのでもう少し深堀していこうと思います。

MVCモデルについて

まずは基礎認識の部分ですが、Ruby on RailsはというフレームワークはMVCというモデルを使用しております。

Untitled Diagram.jpg

ここでは詳細を省かせていただきますが、

  • サーバーからのリクエストに応じてアクションを実行するController
  • データベース(DB)とのやり取りを行うModel ## rails g model
  • ブラウザ表示を担うView

という3つの部分から構成されます。

rails g model

migration(マイグレーション)を理解する前にまずはモデルの作成コマンドから紐解いていきましょう。

rails g modelコマンドを実行すると大きく分けてmodelファイルとmigrationファイルの2種類が作成されます(厳密にいうとさらにtestファイルなども自動作成されます)。

これらのファイルはそれぞれ

  • migrationファイル→DBに変更を加える内容
  • modelファイル→DBとRailsのアプリケーションを繋ぐ

といった役割があります。

Untitled Diagram.3png.png

特にここで作成されるモデルは全てApplicationRecordというクラスを継承しております。

models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
end

さらにこのApplicationRecordクラスの親クラスはActiveRecord::Baseを継承しています。

models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

このActiveRecordクラスはDBとやり取りする際に必要なSQL構文を翻訳する機能を持っています。

そのためいちいちSQL構文を書かずとも簡単にDBにアクセスしデータをいじくることができるのです。

Railsから入るとあまりなじみがないかもしれませんが、通常DBにアクセスしデータを操作するためにはSQLという別の言語を使って指示を飛ばさなくてはなりません

SQL文の例
-- テーブルを作成
CREATE TABLE USERS (
    ID    INT NOT NULL PRIMARY KEY,
    NAME  VARCHAR NOT NULL 
    AGE INT NOT NULL
);

-- データを作成(railsではtable.create(value1, value2…))
INSERT INTO USERS VALUES (1,'イチハラ','');

-- データをすべて選択(railsではmodel.all)
SELECT * FROM USERS

しかしRailsではこのActiveRecordを使用することで自動でSQLに翻訳し、より直感的で簡単な構文でDBを操作することができるのです。

またこのActiveRecord::Baseクラスでは自動的にゲッターとセッターが定義されているため、意図的にattr_accessorなどを定義せずともインスタンスの値を参照することができます。

なるほど…
確かに便利だけどこれがゲッターとセッターを理解するのに苦しんだ訳か…

>> セッターとゲッターってそもそも何?

rails db:migrate

rails db:migrateを実行すると作成されたmigrationファイルを基にDBに変更を加えます。今回の場合はmodel作成時のmigrateの為、DB上に新たにテーブルを作成します。

xxxxxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end

ActiveRecordによって作成されたテーブルは以下のような特徴を持ちます。

  • テーブル名はmodelの複数形(Post → posts)
  • id,created_atが自動作成

ちなみにrails g migrationによってmigrationファイルを作成した場合は自動的にchangeメソッドが定義されており、すでに作成されたテーブルに変更を加えることができます。

xxxxxxxx_oooo.rb
class PasswordDigestToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :password_digest, :string
  end
end

終わり

以上がマイグレーションについてです。

あまり自信があるわけではないので、間違っていた李解説がおかしい部分があればご指摘いただけると幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(初心者向け)【Rails】時短テク!slimの導入方法と使い方

はじめに

Rubyのテンプレートエンジンslimの導入方法と使い方をご説明します:relaxed:
Railsでは、html.erbというファイルでhtmlを書きますが、<h1></h1><% %>を毎回記入するのが少し面倒です。
slimを利用すると、htmlをより簡潔に書けるため、開発にかかる手間を減らすことができます。

erbとは
embedded Rubyのこと。Viewファイルの中の好きな場所でRubyのコードを実行できるようになります。

1. slim 導入方法

  • railsのディレクトリにあるgemfileに、2つのgemを追加します。
gem 'slim-rails'
# railsでslimを利用するためのgem

gem 'html2slim'
# html.erbをhtmle.slimに変換するためのgem
  • ターミナルでbundle installと打ち、gemの追加を反映させます。
bundle install
  • 以下のコマンドをターミナルに打ち、app/viewsディレクトリにある既存のhtml.erbファイルをhtml.slimに切り替えます。
bundle exec erb2slim app/views app/views
  • slimファイルが追加されたことを確認したら、既存のerbファイルは邪魔なので削除します。
bundle exec erb2slim app/views app/views -d

念の為、git commitを使ってファイルの状態を記録することをオススメします。ファイルを削除して問題が生じても、git commit前の状態に戻すことができます。

git log
#ハッシュ値を調べる
git reset --hard ハッシュ値
#出てきたハッシュ値を--hardの後に入力すると、指定したコミットまで戻ります。

2. slim 書き方

基本的なslimの使い方を列挙します。

<% %>の書き方

  • html
<% if %>
  • slim -
- if

<%= %>の書き方

  • html
<%= puts hoge %>
  • slim =
= puts hoge

テキストの書き方

  • html
<p>hoge<p>
  • slim |
p 
 | hoge

見出しタグの書き方

  • html
<h2>hogehoge</h2>
  • slim <>は必要なし
h2 hogehoge

idの書き方

  • html
<div id = id></div>
  • slim #
# id

クラスの書き方

  • html
<div class = hoge></div>
  • slim .
.hoge

コメントの書き方

  • html
<!-- hoge -->
  • slim /
/hoge

htmlとslimを見比べると、非常にシンプルに書けることが分かります。

参考記事

より詳しい情報について知りたい方は、以下のURLをご参照ください。

  • 【爆速で習得】Railsでslimを使う方法から基本文法まで

https://qiita.com/ngron/items/c03e68642c2ab77e7283

  • git commitを使って変更内容をコミットする方法【初心者向け】

https://techacademy.jp/magazine/10172

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]カテゴリを選択するフォームを作りたい

こんにちは。
現在Railsで掲示板をつくっています。

掲示板のスレッドをカテゴリで分類して表示したかったので、スレッドをたてる時にカテゴリーを選択するようにしようと思って実装しました。

最近Railsを触り始めたので詳しい人いたらマサカリおねがいします。

バージョンとか

Ruby 2.5
Rails 5.1

DB・準備

カテゴリにたくさんのスレッドが紐づいている設計です
スクリーンショット 2020-07-09 12.08.06.png

モデル間の設定も忘れずに...

thread.rb
class Thread < ApplicationRecord
    belongs_to :categories
end
category.rb
class Category < ApplicationRecord
    has_many :thread
end

まずはカテゴリを登録する

カテゴリがないと始まらないので、登録しましょう。コンソールからでもいいのですが、今後カテゴリを追加していくと考えて、登録フォームを作ってしまいます。登録したら一覧ページに飛ぶ様にしてあります。

はじめに、routes,controllerを書いていく

  • ルーティング
routes.rb
Rails.application.routes.draw do
  root 'thread#index'
  resources :thread #スレッドのルーティングも書いちゃいます
  resources :categories
end
  • コントローラ
categories.controller.rb
class CategoriesController < ApplicationController
    def new
        @category = Category.new
    end

    def create
        @category = Category.new(category_params)
        if @category.save
            redirect_to categories_path, notice: "登録しました"
        else
            render :new
        end
    end

    def index
        @categories = Category.all
    end

    private
    def category_params
        params.require(:category).permit(:name)
    end
end

  • 登録フォーム

スクリーンショット 2020-07-09 12.26.28.png

new.html.erb
<div class="col-sm-12">
    <h2 class="text-center">カテゴリの追加</h2>
    <%= form_with model: @category, local: true do |f| %>
        <div class="form_input">
            <%= f.label :name %>
            <%= f.text_field :name, class:"form-control" %>
        </div>
        <div class="form_action row">
            <%= f.submit "登録する", class: "btn col-sm-12 submit_btn" %>
        </div>
    <% end %>
</div>
  • 一覧ページ

スクリーンショット 2020-07-09 12.28.24.png

これはとりあえず表示できればいいかなと

index.rb
<div>
    <% @categories.each do |category| %>
        <%= category.name %>
    <% end %>
</div>

スレッド投稿フォームをつくる

登録ができたので、あとはスレッドの投稿フォームでカテゴリを選べる様にします。

  • コントローラ
threads_controller.rb
class ThreadsController < ApplicationController
  def new
    @thread = Thread.new
  end

  def create
    @Thread = Thread.new(board_params)
    if @thread.save
      redirect_to thread_path(@thread), notice: "投稿が完了しました"
    else
      render :new
    end
  end

  def show
    @thread = Thread.find(params[:id])
  end

  private
  def board_params
    params.require(:thread).permit(:title,:body)
  end
end

  • 投稿ページ

スクリーンショット 2020-07-09 12.36.23.png

collection_selectで選択フォームを作ることができます。

使い方としては、こんな感じに使うのですが...
collection_select(オブジェクト名, メソッド名, 要素の配列, value属性の項目, テキストの項目 [, オプション or HTML属性 or イベント属性])

今回の例だと、Category.allが「要素の配列」の部分にあたりますね。
もしかしたら、Category.allって書くよりもコントローラの方で変数に入れちゃってviewで使用。という様な形の方がいいのかもしれません。

ここはだれか指摘していただけると助かります。

threads/new.index.erb
<div class="col-sm-12">
    <h2 class="text-center">スレッド立てる</h2>
    <%= form_with model: @thread, local: true do |f| %>
        <div class="form_input">
            <%= f.label :title %>
            <%= f.text_field :title, class: "form-control" %>
        </div>
        <div class="form_input">
            <%= f.label :body %>
            <%= f.text_area :body, class: "form-control" %>
        </div>
        <div class="form_input">
            <%= f.label :category_id %>
            <%= f.collection_select :category_id, Category.all, :id, :name,
                                                            :include_blank => "カテゴリを選択してください" %>
        </div>
        <div class="form_action row">
            <%= f.submit "投稿する", class: "btn col-sm-12 submit_btn" %>
        </div>
    <% end %>
</div>

以下の様な感じで選択できるようになります。

スクリーンショット 2020-07-09 12.37.45.png

おわりに

最後までみてくれてありがとうございました。
これからたくさん記事書いて、どんどんアウトプットしていこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mysql2::Error::ConnectionErrorを解決します

環境/バージョン

ローカルでやってます
Ruby v2.5.1
Rails v5.2.4
MySQL v8.0.19

何が起こった?

railsプロジェクトを作成したあと、`rails s'してみたらこんなエラーに遭遇

Mysql2::Error::ConnectionError (Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)):

どうやって解決した?

エラー文からみてわかる通り「あ、MySQL」かとすぐわかります。

ConnectionErrorとあるので「繋がっていませんね〜」ってすぐわかります。

database.ymlを調べて見る。

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  socket: /tmp/mysql.sock

  ---以下省略---

ymlファイルのpasswordが抜けてる。
この間、MySQLにpassword設定するの忘れていたからでした。

以前までは設定してなかったのでなくても接続できていましたが、設定したのもあって今回はうまく繋がってくれなかったみたいです。

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password #追記
  socket: /tmp/mysql.sock

この画面出るとホッとする。かわいい子供たち

スクリーンショット 2020-07-09 9.32.37.png

最後まで読んでくれてありがとうございました。
少してもお役に立てれば嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

基礎から学ぶ 人工知能の教科書 第2章 章末問題

はじめに

機械学習の勉強として、「基礎から学ぶ 人工知能の教科書」 を読んでいます。

この本の特徴は、章末問題にPythonの簡単なプログラムが載っていてるところです。

第2章 章末問題

ELIZA.rb
LIMIT = 20
CYCLE = 5

count = 0
endcount = 0
puts('Dr>私はDoctor、お話を伺います')
while endcount < LIMIT
  print('あなた>')
  inputline = gets.chomp.encode("UTF-8", "CP932", :invalid => :replace)
  if count >= CYCLE
    puts 'Dr>' << inputline << '、ですか...'
    count = 0
  elsif inputline.include?('先生')
    puts 'Dr>私のことでなくあなたのことを話しましょう'
  elsif inputline.include?('母')
    puts 'Dr>あなたのお母さんについて話してください'
  elsif inputline.include?('父')
    puts 'Dr>あなたのお父さんについて話してください'
  elsif inputline.include?('意見')
    puts 'Dr>私の意見を聞きたいのですか?'
  elsif inputline.include?('が心配です')
    puts 'Dr>' << inputline.sub(/が心配です/, 'は心配ですか?')
  else
    puts 'Dr>続けてください'
  end
  count += 1
  endcount += 1
end
puts('Dr>それではそろそろ終了しましょう。おつかれさまでした。')

簡易版ELIZAを作成する問題です。

CP932.rb
  inputline = gets.chomp.encode("UTF-8", "CP932", :invalid => :replace)

Windows10の場合、文字コードを変換する処理が必要になっています。
AtCoderでは、こういう処理が無いので新鮮です。
但し、コマンドプロンプトのバグ対応で、レガシ コンソールを選択する必要があります。
20200709.png

まとめ

  • 第2章まで読んだ
  • G検定は難しかった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

def initializeでちょっとだけハマった話

initializeはnewした時点で値がsetされる

クラスメソッドでinitializeにsetした値を参照したい時ハマってしまった

class Hoge
  attr_accessor :aa
  def initialize
    @aa = 'aa'
  end
  def self.bb
    @aa
  end
end

p Hoge.bb
# => nil

Hoge.newしていないので値をセットできていません、
よってnilが返る

class Hoge
  attr_accessor :aa
  def initialize
    @aa = 'aa'
  end
  def bb
    @aa
  end
end

hoge = Hoge.new
p hoge.bb

インスタンスメソッドを利用すれば値を参照できる(当たり前ですよね)
クラスメソッドで値を使いたい時は
initialize使わず直接インスタンス変数に値をセットしましょう。

class Hoge
  @aa = 'aa'
  def self.bb
    @aa
  end
end

p Hoge.bb
#=> "aa"

クラスメソッドで使うのかインスタンスメソッドで使うのか、
用途に合わせて、initializeの中身は意識しないといけないんだなと学んだハマりでした。

日々精進

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのハッシュとシンボルについて

プログラミングの勉強日記

2020年7月9日 Progate Lv.162
RubyⅠ RubyⅡ
今日からRubyの勉強を始める。1ヶ月後にでRuby on Railsを用いてアプリを作れるように勉強していく。

Rubyとは

 Qiitaでは初めてRubyについて触れるので、最初に簡単にRubyの説明と簡単な使い方について説明する。
 RubyはWebアプリケーションのシステムを作るためのプログラミング言語で、似たような言語ではPHPやPythonなどがある。
 本題のハッシュに入る前に簡単にRubyの使い方について説明する。

変数の定義

 変数名=値で変数の定義を行うことができる。基本的な使い方は他のプログラミング言語と同じ。変素名は何の値が代入されているかわかりやすい名前にする。2語以上の組み合わせる変数名にはアンダーバー_を用いる。

変数展開

 文字列の中で#{変数名}とすることで変数を代入している値に置き換えて文字列を含めることを変数展開という。変数展開を行う場合は必ずダブルクオーテーション"で囲む。シングルクォーテーションの場合、変数展開が行われず、そのまま文字列として扱われてしまう。また、数字と文字列は+で文字連結できないので、そうゆう場合でも変数展開を用いる。

index.rb
name="田中"
puts "名前は#{name}です"
puts '名前は#{name}です'
コンソール
名前は田中です
名前は#{name}です

 

if文の書き方

 処理はifendで囲む。C言語ではelse ifが使われるがRubyではelsifを用いる。

繰り返しの処理

 each文を使う。配列.each do |変数名|と書き、endまでの間に実行したい処理を書く。

index.rb
names=["田中","山本","山田"]
names.each do |name|
  puts name
end
コンソール
田中
山本
山田

 each文内の変数名(name)は好きな名前でいいが、配列の変数名(names)の単数形にすることが多い。
 |変数|はeach文のdoからendまでしか使えない。each文の外で定義した変数はeach文の中でも使うことができる。それぞれの変数の使用できる範囲をスコープという。

ハッシュとは

 複数の値を管理する方法の1つ。配列は複数の値を並べて管理するのに対して、ハッシュはそれぞれの値にキープという名前をつけて管理する。
0709.png

index.rb
user={"name"=>"田中", "age"=>16}
puts user
user={"name"=>"田中", "age"=>16}
(↑定義したハッシュがそのまま出力される)

ハッシュの要素を用いる

 ハッシュの各要素の値は対応するキーを使ってハッシュ[キー]とすることで用いられる。また、ハッシュ[キー]=値で更新できる。ハッシュ[新しいキー]=値で追加することもできる。

index.rb
user={"name"=>"田中", "age"=>16}
puts user["name"]

#キー"age"に対する値を更新
user["age"]=17
puts user

#キー"gender"に対する値を追加
user["gender"]=male
puts user
コンソール
田中
user={"name"=>"田中", "age"=>17}
user={"name"=>"田中", "age"=>17, "gender"="male"}

シンボル

 ハッシュのキーの部分を文字列ではなく先頭にコロン:を付けた書き方。つまり、クォーテーションで囲む代わりに先頭にコロンをつける。クォーテーションで囲む場合と先頭にコロンをつける場合で出力結果は同じになる。文字列とシンボルは厳密には違うけど、基本的には同じように使える。
 ハッシュのキー部分でシンボルを用いたらその値を用いるときはシンボルで指定しなければいけない。

index.rb
user={"name"=>"田中", "age"=>17}
puts user["name"]

user={:name=>"田中", :age=>17}
puts user[:name]

 【一般的】ハッシュのキーにシンボルを用いるときは省略して書くことができる。この書き方が一般的である。省略した場合でも、シンボルを用いて要素を取得する。

index.rb
user={name:"田中", age:17}
puts user[:name]

nilの扱い

 ハッシュから存在しないキーの値を取り出したときの「何もない」という値のこと。読み方は「ニル」。putsしても何も表示されない。

index.rb
user={name:"田中", age:17}
puts user[:height]

要素がハッシュである配列

 配列の要素にはハッシュを使うことができる。
 配列[インデックス番号]でハッシュを用いることができるので、そのハッシュを代入した変数を使って変数[キー]でハッシュの要素の値を用いられる。

index.rb
users=[
  {name:"田中", age:17}
  {name:"山本", age:20}
]
puts users[0]

user=users[0]
puts user[:name]
コンソール
{:name=>"田中", :age=>17}
田中

 上のを省略して書くことができる。配列[インデックス番号][キー]で特定のハッシュの値を用いることができる。

index.rb
users=[
  {name:"田中", age:17}
  {name:"山本", age:20}
]

puts users[1][:name]
コンソール
山本

 each文を使って書くこともできる。

index.rb
users=[
  {name:"田中", age:17}
  {name:"山本", age:20}
]

users.each do |user|
  puts user
end

users.each do |user|
  puts user[:name]
end
コンソール
{:name=>"田中", :age=>17}
{:name=>"山本", :age=>20}
田中
山本
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む