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

Railsで新しいプロジェクトを始めた時に必ずやっておきたい2つの設定

こんにちは、とくめいチャットサービス「ネコチャ」運営者のアカネヤ(@ToshioAkaneya)です。

今回はRailsで新しいプロジェクトを始めた時にやっておきたい2つの設定を紹介します。
重要ですので、必ずやっておきましょう。

Railsで新しいプロジェクトを始めた時にやっておきたい2つの設定

turbolinksを無効化する

詳しくはこちらの記事で解説していますのでご覧下さい。
Railsでページ遷移後にJavaScriptが実行されない問題の解消法
これを行わないと、JavaScriptが思うように動作しないことがあります。

rails consoleが動くようにする

$ rails consoleはとても重要なコマンドですが、最近のバージョンではエラーが発生してしまうことがあります。

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rb-readline'
end

Gemfileにgem 'rb-readline'を追加して、$ bundleを実行して下さい。

以上です。

はてなブックマーク・Pocketはこちらから

はてなブックマークに追加
Pocketに追加

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

プログラミング学習記録19〜突っ走る力〜

今日やったこと

  • Udemyの「Web開発入門完全攻略コース - プログラミングをはじめて学び創れる人へ!未経験から現場で使える開発スキルを習得!」のセクション12の139~150
  • セクション13の151~194

以下、パートごとに書いたメモです。

セクション12AWS Cloud9による開発環境構築

139.イントロダクション-AWS Cloud9による開発環境構築-

AWS Cloud9を使うとwebアプリケーションの開発がwebブラウザでできるようになります。
1年間無料で利用できます。

※ローカルPC上に環境構築して運用する方法もあります。

AWS Cloud9利用に必要なものは、

  • メールアドレス
  • クレジットカード
  • ユーザー認証用の電話番号

です。

実行環境はGoogle chromeで行います。

140.AWSの無料利用枠について

具体的に結論としては、
新規アカウント作成から12ヶ月。月間750時間まで無料利用枠で利用可能。

1年間の無料期間が終わってからは、(ドル円相場にもよるが)1ヶ月に90時間あたりだいたい210円くらいかかるみたいです。安いですね。

詳細は以下のページで確認してください。
AWS クラウド無料利用枠 | AWS(https://aws.amazon.com/jp/free/)

よくある質問 - AWS クラウド無料利用枠 | AWS(https://aws.amazon.com/jp/free/faqs/)

141.AWS利用時の注意事項!!必ず確認してください!!

アカウント情報の管理は厳重にしましょう。

以下のような被害も実際に出てきています↓
『AWSで不正アクセスを受けたので、そのときの対応を記録しておく(返金されました)』
https://qiita.com/kojiro_ueda/items/503d24b313a3a80b8ecc

あと、知識がない状態でAWSのいろいろなサービスを利用するのは、セキュリティ面や費用の面で危険だということです。

知識をつけてから、わかるものから少しづつ手をつけていきましょう。

142.AWSアカウント作成

AWSアカウント作成についてです。

以下のページに具体的な流れが書かれています。
AWS アカウント作成の流れ | AWS(https://aws.amazon.com/jp/register-flow/)

143.IAMの設定

AWS Identity and Access Management (IAM) は、ユーザーに対して AWS へのアクセスを安全に制御するための仕組みです。

AWS Identity and Access Management (IAM - ユーザのアクセスを安全に制御)| AWS(https://aws.amazon.com/jp/iam/) より引用

この講座ではIAMユーザーとしての利用で進めていくようです。

144.無料利用枠の使用のアラートの受信設定

AWSの不正な請求を防ぐためにあらかじめ行っておくべき設定です。

145.Cloud9のセットアップ

IAMユーザーでCloud9のセットアップを行いました。

これでIDE(統合開発環境)の準備ができました。

146.Cloud9の画面説明

linuxコマンド
$ ruby -v
でインストールされているrubyのバージョンを確認しました。

147.補足:AWS Cloud9のテキストエディタの設定について

AWS Cloud9の設定に関する補足資料です。
AWS Cloud9のテキストエディタの設定について
(https://programmingnavi.com/1690/)

Tabキーでスペース2個作れるようにするのと、不可視文字を表示する設定です。

148.新・Rubyのバージョン管理1

複数の開発案件を抱えていると、複数のrubyのバージョンを扱うことがあります。

なので、複数のrubyのバージョンを管理する方法を学びましょう、という話です。

Rubyのバージョン管理ソフトウェアはいくつかありますが、この講座ではrvmを使って進んでいきます。

Cloud9にインストールされているrvmのバージョンを確認しました。

$ rvm help

でrvmのヘルプを出すことができ、コマンドを調べることができます。

149.Rubyのバージョン管理2

鍵のインストールについてです。

150.Rubyのバージョン管理3

Rubyの最新バージョンは2.6.0ですが、動画に合わせてrvmで2.5.0をインストールし、デフォルトに指定しました。

このパートで実際に入力したコマンドです↓

$ rvm list
でインストールされているrubyのバージョンが確認できます。

$ rvm install 2.◯.◯
で指定したバージョンのrubyがインストールできます。

$ rvm use rubyのバージョン
でインストールされているrubyの中から使いたいバージョンを選ぶことができます。

$ rvm remove rubyのバージョン
で指定したバージョンのrubyをアンインストールできます。

$ ram —default use rubyのバージョン
でインストールされているrubyの中から指定したいものをデフォルトに指定することができます。

セクション12AWS Cloud9による開発環境構築 全体の感想

Cloud9についてより詳しくなれました。

Cloud9を使うのは初めてではないのですが、複数のバージョンを管理する方法等は知らなかったので、知れてよかったです。

とりあえず開発環境構築まで来れて一安心という感じですね。

いよいよ次のセクションではRubyに入ります。

楽しみです。

ということで、気を引き締めて次のセクションに進みたいと思います。


セクション13Ruby入門

151.イントロダクション-Ruby入門-

このセクションの概要。

152.Rubyとは

Rubyはプログラミング言語でruby on railsはフレームワーク。

詳しい情報は公式HPで確認できます。
Rubyとは
https://www.ruby-lang.org/ja/about/)

153.動作確認環境について


この講座ではAWS Cloud9でRubyのバージョンは2.5.0で行います。

154.はじめてのRubyプログラミング

Rubyを動かす方法はirb(対話型評価環境)を使う方法とファイルに保存したプログラムを実行する方法の2つがあります。

このパートでは両方の方法を試してみました。

Irbはコマンド
$ irb
で起動します。

元の画面に戻るときは
exit
です。

ファイルに保存したプログラムを実行する方法ではProgateのコマンドラインでやったコマンドを使いました。

kinu:~/environment/ruby_projects $ pwd
/home/ec2-user/environment/ruby_projects
kinu:~/environment/ruby_projects $ ruby hello.rb
Hello World!

Rubyのファイルは実行したいファイルの親ディレクトリに移動してから$ ruby 〇〇.rb で実行します。

155.コメント

Atomと同じようにCloud9でもcommandキー+/キーでコメントアウトします。

156.ローカル変数

price1=100や_price=100のような変数は原則使わないようにしましょう。

price_costのように_で区切るのは使われますが、priceCostのような書き方はあまり使いません。

あと、Rubyの予約語として指定されているものは変数名に使えません。

Ruby 予約語

157.定数

定数は全て大文字で書くことが多いです。

TAX=1.08
TAX_RATE=1.08

定数への代入は一度きり。

158.リテラル

Rubyのプログラムの中に直接記述できる値のことをリテラルと言います。

数字や文字列、配列、ハッシュはリテラルです。

159.オブジェクト指向プログラミング言語イントロ

オブジェクトとは、データや処理の集まりのことです。

今は言葉をざっくり知っていればいいそうです。

クラス、インスタンス、メソッドについてのざっと学びました。

160.数値(Numeric)

基本的な計算方法と数値を扱うメソッドのついての動画です。

161.文字列(String)

文字列を途中で改行したい場合は、\nを間に入れてダブルクオーテーションで囲む必要があります。

特殊文字や式展開を行うときは、ダブルクオーテーション。それ以外はシングルクオーテーション、という使い分がオススメらしいです。

162.空白文字について

出力結果は変わらないが、10 + 1 - 3 のように空白を入れると見やすくなるので、空白を入れて書きましょう。

163.演算子による値の比較

2つの値の大小や同じかどうかを調べる方法についてです。
関係が成り立つ場合は真(true)、成り立たない場合は偽(false)が返ってきます。

例えば、
puts 1<2
と書いたら、trueが返ってきます。

164.演算子の優先順位

どの順番で計算されるのか?という話です。

詳しくはこちらでご確認ください。
演算子の優先順位(https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html)

165.数値と文字列は暗黙的に変換されない

原則、数値と文字列をつなげることはできないので、.to_iや.to_f、.to_sを使って変換する必要があります。

166.インクリメントとデクリメント

インクリメントとは、値に1を足すことです。

デクリメントとは、値から1を引くことです。

Rubyには、JavaScriptでいうところの++、—はありません。

なので、インクリメントはn=n+1、n+=1のように、デクリメントはn=n-1、n-=1のように表現します。

167.真偽値

Rubyの真偽値のルール
・falseまたはnilであれば偽(false)
・それ以外は全て真(true)

その他真偽値に関係するメソッドにも軽く触れました。

168.条件分岐if

点数によって異なる評価を示すプログラムと動物の種類によって、ターミナルに出力する鳴き方を変えるプログラムを作りました。

169.条件分岐 unless

unlessは条件式がfalseの時に実行したい処理を書きます。

unlessにelsifに相当するものはないので、無理に使う必要はありません。

n=1
unless n.zero?
  puts 0ではありません’
end

を実行すると「0ではありません」が出力される。

170.条件分岐 case

複数の条件を指定する場合は、elsifを何個も使うよりもcaseを使った方がわかりやすくなります。

誕生石から、誕生月を出力するプログラムをcaseを使って作りました。

stone = 'ruby'
case stone
when 'ruby'
  puts '7月'
when 'peridot'
  puts '8月'
when 'sapphire'
  puts '9月'
else
  puts 'データが登録されていません'
end

171.演習:条件分岐

年齢によってテーマパークの入場料を出し分けてターミナルに出力するプログラムを作る演習です。

これは簡単でした。

172.演習回答:条件分岐

模範回答と同じでした。

age = 5
if age >= 12
  puts "5000円"
elsif age >=6
  puts "2500円"
else
  puts "1000円"
end

173.メソッド

メソッドは複数の処理をまとめて、扱いやすくしたものです。

Progateでは戻り値にreturnを使っていたのですが、この講座ではreturnを使っていません。

returnを使わなくても、大丈夫みたいです。

174.演習:FizzBuzz

有名な問題ですね。
会社の採用面接で問われることもあるそうです。

なんとかクリアできてよかったです。

175.演習回答:FizzBuzz

ポイントは先に15で割り切れる数の処理を書くことですね。

def fizz_buzz(n)
  if n%15==0
    'FizzBuzz'
  elsif n%3==0
    'Fizz'
  elsif n%5==0
    'Buzz'
  else
    n.to_s
  end
end
puts fizz_buzz(1)
puts fizz_buzz(2)
puts fizz_buzz(3)
puts fizz_buzz(4)
puts fizz_buzz(5)
puts fizz_buzz(6)
puts fizz_buzz(7)
puts fizz_buzz(8)
puts fizz_buzz(9)
puts fizz_buzz(10)
puts fizz_buzz(11)
puts fizz_buzz(12)
puts fizz_buzz(13)
puts fizz_buzz(14)
puts fizz_buzz(15)

3でも5でも割り切れない数字は文字列に変えて出力してください、ということで、n.to_sにしてます。

参考

  • to_i→文字列を数字に変えるメソッド
  • to_s→数字を文字列に変えるメソッド

176.puts,print,p

putsは改行されてnulになりますが、printは改行されずにnulが出ます。

Pメソッドはデバック用のメソッドです。

177.配列

配列の基本と配列に関する様々なメソッドについて学習しました。

具体的に、どの場面でどのメソッドを使うかはわからないので、さらっと流して次に進みます。

(0..25).to_a
で0から25の数字を含んだ配列を作ることができます。

178.ハッシュ

連想配列とも呼ばれるものです。

each文の書き方がProgateでやったものとは違っていましたが、基本的な考え方に大きな違いは’ないので、このまま突き進みます。

179.繰り返し処理

繰り返し処理の概要。

180.繰り返し処理 each, for

原則、for文は書かないそうです。

配列とハッシュそれぞれでeach文を書いてみました↓

numbers = [1,2,3,4,5]
numbers.each do |number|
  puts number
end


scores = {nakamura:90,sato:80,suzuki:70}
scores.each do |name,score|
  puts "#{name},#{score}"
end

181.繰り返し処理 times

配列を使わずに指定した回数繰り返したい時に使います。

5.times do
  puts 'hello'
end

5.times do |i|
  puts "#{i}.hello"
end

この時、iは1からではなく、0から始まります。

182.繰り返し処理while

指定した条件が真である限り繰り返す処理です。

条件を指定せずに無限ループにならないように注意しましょう。

183.繰り返し処理 upto,downto

Uptoは数値を1ずつ増やしながら何かしらの処理をしたい時に使い、downtoは1ずつ減らしながら何かしらの処理をしたい時に使います。

184.繰り返し処理 step

指定した数字から指定した数字まで指定した数字ずつ増えていく処理をしたい時に使います。

構文は、開始式.step(上限値、一度に上限する大きさ)

185.繰り返し処理 loop, break, next

Loopはあえて無限ループを作りたい時に使います。

Breakで指定したところで中断させることができます。

Nextを使うと、同じブロック内のnext以降の処理が行われずに次の繰り返しが行われます。

186.クラス

クラスは設計図のようなもので、インスタンスは実体と考えることもできます。

用意されているクラスもたくさんあるのですが、自分でクラスを作る方法を学びました。

Progateではinitializedメソッドの中のインスタンス変数はself.name=nameのように記述していたのですが、この講座では@name=nameのように記述していました。

違いはありますが、基本的な考え方は一緒ですね。

class User

  def initialize(name)
    puts 'initialize!!'
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}."
  end
end

emma = User.new('Emma')
emma.hello

olivia = User.new('Olivia')
olivia.hello

187.アクセサ

attr_accessorなどのアクセサについてです。

インスタンス変数の値を読み書きするメソッドのことをアクセサメソッドと言います。

アクセサメソッドには読み取り専用のもの、書き込み専用のものもあります。

具体的な使用場面についてはまだよくわかっていませんが、切った貼ったができる便利なものだというニュアンスはわかった気がします。

188.訂正:クラスメソッド、クラス変数

189のスペルミスのお詫びです。

189.クラスメソッド、クラス変数

クラスメソッドは、インスタンスを生成せずにクラスから直接呼ぶことができるメソッドです。

クラス変数は、クラス自体に値を保持することができる変数です。

190.クラスの継承

継承とは、親クラスを受け継いで使うことです。

継承を使うことで、共通部分を繰り返し書く必要がなくなるので、楽にコーディングできますね。

子クラスの方でオーバーライド(上書き)すると子クラスの方が優先されます。

191.メソッドのアクセス権

メソッドのアクセスできる条件を指定することができます。

メソッドのアクセス権には3つの種類があります。

Public→クラスの外部からでも自由に呼び出せる(デフォルト)

Private→クラスの内部でのみ使えます

Protected→これはあまり使われないそうです。

メソッドの上にprivateと書くだけでprivate化できます。

192.モジュール

モジュールはクラスのようにメソッドや定数をまとめられるものです。

クラスのようにインスタンスを作ることはできませんが、関連するメソッドなどをまとめてグループ化したい時に手軽に使えるため、便利です。

193:例外

実務では例外が発生した時の処理を記述しておくことが大事だそうです。

194.Rubyのコーディングルール

プロジェクトメンバーと一緒にコーディングするときに参考にする共通のルールです。

The Ruby Style Guide (日本語)(https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md)

RuboCop(https://github.com/bbatsov/rubocop)

セクション13 Ruby入門 全体の感想

パソコンのバッテリー残量の関係でかなり急ぎ足で終わらせました。

まだまだ理解できたいないところだらけですが、Progateをやっていたこともあり、理解できる部分も多くありました。

ひとまず完走することを第一に考え、このまま突き進みます。

きっと2回、3回と繰り返し触れていけば簡単になるはず!!!



あと残り87パートになりました。

このまま突っ走ります。

ということで、引き続き明日からもプログラミング学習頑張ります。

おわり

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

winmergeの使いかたメモ

備忘録です

Excelファイルに出力した差分を確認したい。その手順

①ダウンロード 64の方

②比較したいファイルをドラッグ&ドロップ

③エクセルなので、「展開プラグイン」の項目を
 「CompareMSExcelFile.sct」に設定

④「OK」をクリック

以下のURLに書いてあります。
参考:http://bashalog.c-brains.jp/18/04/03-180000.php

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

プログラミング学習 記録 Railsチュートリアル11,12章

11、12章終わりました

学習時間は4時間程。一周目なので完全に理解できていない。

11章

  • アカウントの有効化
  1. Userモデルに有効化トークンや有効化ステータスを付与する
  2. ユーザー登録
  3. ユーザー認証
  4. アカウント有効化のメールを送信

覚えたこと

どうやらActivartionトークンが必要らしい
既存のユーザーモデルにアカウント有効化のコードを追加

class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }

メソッドも

# メールアドレスをすべて小文字にする
def downcase_email
  self.email = email.downcase
end

# 有効化トークンとダイジェストを作成および代入する
def create_activation_digest
  self.activation_token  = User.new_token
  self.activation_digest = User.digest(activation_token)
end

そしてテストの準備としてサンプルユーザーを最初から有効にしておく
seed.rbにactivated: true, acivated_at: Time.zone.nowを追記

アカウント有効化のメール送信

送信する為にはAction MailerというライブラリをUserのメイラーに追加するらしい
Userメイラーの生成をして

rails generate mailer UserMailer account_activation password_reset

作られたtextbビューとhtmlビューに表示する内容を書いていく

ちなみに生成されたメイラーのレイアウトはapp/views/layoutsで確認できる

あとはuser_mailer.rbでアカウント有効化リンクを定義して、developmentで試して終了

authenticated?メソッド等重要な点もあるがあくまで記録なので省略

production環境でも出来るらしいが2周目に回します

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

Rails開発で最低限気をつけること(=気をつけてほしいこと)

概要

Rails開発で最低限気をつけること(=気をつけてほしいこと)をメモ
少し長いですがRailsを覚え始めの人に読んでいただければと。

全般

タイポをしない

当たり前ですが、タイポの原因はタイピングをミスすることです
残念ながらrubyは動的型付け言語であり、タイポによるエラーはプログラム実行時に発生します
タイポを防ぐ方法として、

  • Lintを活用する
  • 変数はコピペする
  • スペルが間違っていないか辞書 or Google で検索するようにする

などが挙げられます
上記を徹底し、 視覚的にタイポを防ぐタイピングをやめる ことでタイポ0を目指しましょう

インデントを揃える

PullRequestを作成する前には、必ずコードのフォーマットを直しましょう
インデントが揃っていることはソフトウェアエンジニアのマナーの一つだと思います

String vs Symbol

Sybmolの方がパフォーマンスが良いため、できるだけStringではなくSymbolを使うようにしてください
理由は下記の記事を参考ください
Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい - Qiita

if vs if not vs unless

rubyでは条件式の判定に、 if if not unless などがあります
条件式が複雑かつ、否定演算子などが組み合わされると一気に可読性が低下し、バグの原因につながるため、
条件式では「常に正常系を」書くようにしています
いくつか例を挙げてみます

例: if ~ else 構文

if @record.save # 条件式は正常系
  # 成功時の処理を書く
else
  # 失敗時の処理を書く
end

unless でGurad構文(=早期リターン)

def process(record)
  return false unless record.valid? # 早期リターン(=条件式は正常系)
  # 以下、正常系の処理を実行
end

「常に正常系を」を記述することのを推奨する理由については下記リンクを参考にしてみてください
ifとunlessの使い分け

boolean型のメソッド名には末尾に ? をつける

一般的に、rubyではbooleanを返すメソッドには末尾に ? をつける習慣があります
メソッド名の末尾に ? がついていることで boolean を返すことがわかり可読性が上がります

ぼっち演算子 &. をつける

rubyでは、2.3から &. 演算子(safe navigation operator = ぼっち演算子)が導入されました
実行時に nil なオブジェクトに対してメソッドチェーンを実行すると undifined method error for nil:NilClass
なってしまうため、nil になる可能性のあるメソッドチェーン呼び出しには全て &. をつけるようにすべきです

ソースコードの責任の範囲を意識する

OOPの思想に沿って、ソースコードの責任の範囲を考えて設計&実装する
Rails開発はMVCに依存しがちになますが、もっと柔軟にクラスを切った方が良いと思います
また、アプリケーションやビジネスロジックに依存せず汎用的に使えるクラスは lib 配下に記述することもおすすめします

例として、下記のようにディレクトリを切ると良いかなと思います

- app
  - controllers
  - decorators
  - forms # form経由の値を扱う(Controllerをファットにさせない)
  - helpers
  - jobs
  - listeners # wisperなど、Observerパターンでcallbackを処理する
  - mailers
  - models
  - operators # ビジネスロジックを扱う(ControllerやModelをファットにさせない)
  - policies # punditなどで権限管理
  - validators # 独自定義したバリデーション
  - values # Valueオブジェクト
- lib
  - xxx
  - xxx
  - xxx

Controller

before_action でインスタンス変数への代入を控える

before_action を複数記述した場合、記述した順番に実行されます
before_action 内で、他の before_action で代入された変数を呼び出すと、
プログラムが正しく動作するかどうかが before_action を呼び出す順番に依存してしまい、
スパゲッティコードにつながってしまいます
可能であれば変数を取得できるメソッドを作成することをおすすめします

× before_action を使う

class UsersController < ActionController::Base
  before_action :set_user, only: [:show]
  def show
  end
  def set_user
    @user = User.find(params[:id])
  end
end

◯ 専用のメソッドを用意する

class UsersController < ActionController::Base
  def show
  end
  def user
    @user = User.find(params[:id])
  end
  helper_method: user
end

helper_method :user とすることで、viewファイルでも変数を参照できるようになります

インスタンス変数をメモ化する

ActiveRecord 経由で取得するデータは、変数をメモ化することで値のキャッシュができます
展開された変数はリクエスト終了時にクリアされるため、可能な限りキャッシュすることをおすすめします

class UsersController < ActionController::Base
  def show
  end
  def user
    # @note @user がnilの場合のみ代入され、nilでなければキャッシュされたデータを返す
    @user ||= User.find(params[:id])
  end
  helper_method: user
end

レコードの保存処理を行う場合は flash を表示する

レコードの保存処理を行う場合は成功時&失敗時に合わせて適切なflashメッセージを表示するようにしてください

def create
  if @record.save
    redirect_to records_path
    flash[:success] = 'レコードの保存に成功しました'
  else
    render :new
    flash.now[:error] = 'レコードの保存に失敗しました'
  end
end

def destroy
  if @record.destroy
    redirect_to records_path, flash: { success: 'レコードを削除しました' }
  else
    redirect_to records_path, flash: { error: 'レコードを削除できませんでした' }
  end
end

また、レコードの更新に失敗し render を実行する場合は flash.now を使うようにしてください
参考: 【Rails】flashとflash.nowの違い - avosalmonのブログ

Model

belongs_to には required: true または presence: true をつける

belongs_to で必ず親となるレコードが存在する場合は、 required: true または presence: true を記述してください

has_one, has_many には class_name をつける

has_one または has_many を使う場合は、 class_name オプションをつけることで、
モデルが取得できずエラーとなってしまうことを防ぐことができる場合があるため、記述するようにするべきです
必要に応じて foreign_key も指定することをおすすめします

has_one, has_many には dependent オプションをつける

削除時に has_one または has_many 先の子モデルに対する振る舞いを dependent オプションで定義できます
不要なレコードはDBのパフォーマンスにも影響が出るので、適切に設定することをおすすめします
dependent オプションについては、下記リンクで詳しく説明されていたので参照してみてください

ActiveRecord::has_manyのdependentオプション

バリデーションを記述する

DB層でのバリデーションが必要なカラムに対しては、validates メソッド等を使って
適宜バリデーションを記述する
また、必要に応じて適宜カスタムバリデーションを作成して再利用できるようにすることもおすすめします
カスタムバリデーションを実行する

View

form_with を使う

Rails5.1からは form_forform_tagform_with というインターフェースに統合され、
URLベース、スコープ、モデルを指定してformタグを生成できるようになりました

Ruby on Rails 5.1リリースノート

form_for を使う場合は local: true を設定してください
https://qiita.com/bluegirl_beer/items/a361171f653edcd888ad

form_forform_with ではモデルの指定でaction先を絞る

railsで自動生成されるpathって読みづらいですよね
form_forform_with では、渡すオブジェクトが保存されているかどうかで
create のアクションを実行するか、 update のアクションを実行するかを自動的に判別してくれます
また、必要に応じて methodaction オプションを渡すことで、アクション先を絞ることができます

DB

migrationファイルでカラムを追加するときは comment をつける

comment オプションをつけることで、生成されるスキーマファイルにコメントを追加できます
可能な限りコメントの追加をお願いします

class CreateBlogs < ActiveRecord::Migration[5.1]
  def change
    create_table :blogs, comment: 'ブログ' do |t|
      t.string :title, comment: 'タイトル'
      t.text :body, comment: '本文'
      t.timestamps
      t.datetime :deleted_at
    end
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

heroku run rails db:migrateのエラー発生時の解決法

初投稿です。
Railsチュートリアル等でもよく見るエラーなので、メモとして残しておきます。

環境

・cloud9

herokuを走らせようとしたらエラーが出た

cloud9上で

$ heroku run rails db:migrate

と入力すると

$ heroku run rails db:migrate
WARNING
WARNING Node version must be >=8.0.0 to use this CLI
WARNING Current node version: 6.15.0
WARNING
/home/ec2-user/.nvm/versions/node/v6.15.0/lib/node_modules/heroku-cli/node_modules/@oclif/command/lib/command.js:28
    async _run() {
          ^^^^

「今バージョン6.15.0だけど8.0.0に上げてね」ということらしい。

1、node.jsアップデート

こちらを参照してversionを上げてく。

①今のversion確認

$ node --version
v6.15.0

②v8.0.0にアップデート

$ nvm install v8.0.0
v8.0.0 is already installed.
Now using node v8.0.0 (npm v5.0.0)

③今のversion確認

$ node --version
v8.0.0

アップデート成功。

④再度走らせてみる

$ heroku run rails db:migrate
module.js:487
    throw err;
    ^

Error: Cannot find module 'typescript'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/home/ec2-user/.nvm/versions/node/v8.0.0/lib/node_modules/heroku-cli/node_modules/@oclif/config/lib/ts-node.js:5:22)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)

違う問題が発生…

2、moduleを入れる

上から4行目に

Error: Cannot find module 'typescript'

とあるので、こちらを参照。

⑤足りないmoduleを入れてみる。

$ npm install -g npm-install-missing
/home/ec2-user/.nvm/versions/node/v8.0.0/bin/npm-install-missing -> /home/ec2-user/.nvm/versions/node/v8.0.0/lib/node_modules/npm-install-missing/bin/npm-install-missing
added 2 packages, removed 2 packages and updated 12 packages in 10.625s


   ╭─────────────────────────────────────╮
   │                                     │
   │   Update available 5.0.0 → 6.9.0    │
   │     Run npm i -g npm to update      │
   │                                     │
   ╰─────────────────────────────────────╯
bash: ource: command not found
$ npm-install-missing
npm-install-missing: No modules seem to be missing.  Huzzah!

できた?

⑥再度実行

$ heroku run rails db:migrate
module.js:487
    throw err;
    ^

Error: Cannot find module 'typescript'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/home/ec2-user/.nvm/versions/node/v8.0.0/lib/node_modules/heroku-cli/node_modules/@oclif/config/lib/ts-node.js:5:22)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)

うーん、変わらず…

3、npm install

その後heroku loginやheroku -v等をしても同じエラーが出て断念。
過去に僕が書いたこちら(teratail)を参考に色々実行してみる。

⑦$ npm install

$ npm install
npm notice created a lockfile as package-lock.json. You should commit this file.
up to date in 0.098s

⑧$ npm install -g heroku-cli

$ npm install -g heroku-cli
npm WARN deprecated heroku-cli@7.0.9: 'heroku-cli' has been renamed 'heroku'
npm WARN deprecated cross-spawn-async@2.2.5: cross-spawn no longer requires a build toolchain, use it instead
/home/ec2-user/.nvm/versions/node/v8.0.0/bin/heroku -> /home/ec2-user/.nvm/versions/node/v8.0.0/lib/node_modules/heroku-cli/bin/run
added 1 package and updated 14 packages in 26.604s

⑨$ heroku run rails db:migrate

$ heroku run rails db:migrate
 ›   Warning: heroku-cli update available from 7.0.9 to 7.22.2.
Running rails db:migrate on ⬢ hukumiru... up, run.9955 (Free)
D, [2019-03-07T04:59:18.403914 #4] DEBUG -- :    (65.5ms)  CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
D, [2019-03-07T04:59:18.417964 #4] DEBUG -- :    (10.8ms)  CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
D, [2019-03-07T04:59:18.420152 #4] DEBUG -- :    (0.9ms)  SELECT pg_try_advisory_lock(4540128404952329490)
D, [2019-03-07T04:59:18.449576 #4] DEBUG -- :    (1.4ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2019-03-07T04:59:18.457883 #4] DEBUG -- :   ActiveRecord::InternalMetadata Load (1.1ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", "environment"], ["LIMIT", 1]]
D, [2019-03-07T04:59:18.466155 #4] DEBUG -- :    (0.8ms)  BEGIN
D, [2019-03-07T04:59:18.468570 #4] DEBUG -- :   SQL (1.1ms)  INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key"  [["key", "environment"], ["value", "production"], ["created_at", "2019-03-07 04:59:18.466682"], ["updated_at", "2019-03-07 04:59:18.466682"]]
D, [2019-03-07T04:59:18.470396 #4] DEBUG -- :    (1.5ms)  COMMIT
D, [2019-03-07T04:59:18.471489 #4] DEBUG -- :    (0.9ms)  SELECT pg_advisory_unlock(4540128404952329490)

通った…

まとめ

とりあえず、これで走るようになりました。

正直どれが原因でどれが解決に繋がったのかまだよく分かってないですが、個人的によく見るエラーでしたので残しておいて、詳しく分かり次第、また追記します。

詳しい方、教えていただけると助かります。

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

Factory_girlからFactory_botに変更したときに詰まった話

ちょっと古いシステムの改修担当になり
環境整えたときにもろもろチェックしていたらFactory_girlを使っていたので
これをFactory_botに変更するときにすこし躓いたので備忘録程度のメモ。

何はともあれ、Gemの修正

Factory_girl_railsがGemfileにかかれていたので
これをFactory_bot_railsに変更してbundle install

- gem "factory_girl_rails"
+ gem "factory_bot_rails"

※うちのプロジェクトではユニコーン周りでいろいろ干渉したため
bundle install --without productionを実行

正直これだけで大丈夫だと思ってた。

無事にgemのインストールが完了したのでテストしてみると
なにやらfactories以下のファイルでエラーが発生。

コードも何も修正してないのになんで?って思っていろいろググる
そしたら伊藤さんの記事に出会う。
factory_bot 4.11で非推奨になった静的属性(static attributes)

まさかと思って自分のGemfile.lockを確認しにいくと・・・

factory_bot (5.0.2)
  activesupport (>= 4.2.0)
factory_bot_rails (5.0.1)
  factory_bot (~> 5.0.0)
  railties (>= 4.2.0)

Oh...4.11どころか更に上の5.0.2まで上がってるのね。。
(2019/2/9にアプデされた様子)

ということでコードを静的属性から動的属性に修正

記事内容にも書かれているように、4.11からファクトリ定義が
静的属性のままでは警告が出る仕様に変更になっている
(そしてバージョン5で完全に削除されている)ので、
これを自分のコードにも反映。

FactoryGirl.define do
  factory :test do
    trait :default do
      hogehoge     "TEST_TEMP" 
      fugafuga     "テスト用デフォルトテンプレート" 
      delete_flg   0 
    end
  end
end

から

FactoryBot.define do
  factory :test do
    trait :default do
      hogehoge    { "TEST_TEMP" }
      fugafuga    { "テスト用デフォルトテンプレート" }
      delete_flg  { 0 }
    end
  end
end

定義に{}をつけるだけの簡単なお仕事。
※1行目のGirlからBotへの修正も忘れずに・・!

これで、テストが通るようになったのでめでたし。

今回は、修正箇所がそこまで多くなかったので
手作業で変更していったがrubocop-rspecのgemをインストールして
以下のコマンドを実行すれば一括で修正してくれるみたい(便利

rubocop \
  --require rubocop-rspec \
  --only FactoryBot/AttributeDefinedStatically \
  --auto-correct
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Openbd APIを使って本レビューアプリを作成する。 ( jQuery,Ajax 使用 )

初めまして。qiita初投稿になります。
業務未経験なため、間違いがある箇所もあると思いますがご指摘頂ければと思います。
今回書くことは基礎的なことですが、私のようなrails初心者のお力になれれば幸いです。

作成するレビューアプリの概要

  • openbdというAPIを使用し、ajaxを利用して非同期で本の情報を取得します。
  • あらかじめ用意したフォームに取得した任意の情報が自動入力され、レビューを入力し、投稿ボタンを押す事でreviewが投稿されます。
  • DBには画像はURLのみ保存し、画像自体は保存しません。
  • 取得する情報がない場合(画像や紹介文)は、「情報がありません」旨を表示させます。
  • bootstrap4を使用しviewを作成します。

アプリ構築環境

  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • Rails 5.2.2

アプリの作成の流れ

1.アプリを作成し、必要なgem(bootstrap4等)を導入します。
2.reviewsコントローラを作成します。
3.本情報およびレビューを格納するためのReviewモデルを作成します。
4.フォームを表示するためのviewを作成します。
5.非同期(ajax)で本情報を取得するためのjsファイルを作成します。
6.投稿したレビューを表示させるためのviewを作成します。
7.railsサーバを起動します。

1.アプリ作成

以下のコマンドでアプリを作成します。

rails new book_review  

アプリが作成出来たらGemfileを編集します。

cd book_review
vi Gemfile

以下のGemをファイルの一番下に記述します。

Gemfile
gem 'haml-rails' #erbではなくhamlで記述するために導入します。
gem 'erb2haml' #既存のerbファイルをhamlに変換します。
gem 'jquery-rails' #railsでjqueryを利用するために導入します。
gem 'bootstrap' #versionをしていないのでbootstrap4がインストールされます。
gem 'bootstrap_form' #formを自動でbootstrapレイアウトにしてくれます。

いつものようにbundleインストールします。

bundle install

bootstrap4とbootstrap_formを使用するために以下のファイルに記述します。

book_review/app/assets/stylesheets/ 配下のファイルを

application.cssからapplication.scssに名前変更してください。

application.scss
@import "bootstrap";
application.scss
*= require rails_bootstrap_forms

javascriptも使用するため、以下のファイルにも以下を記述します。
//= require_tree .の上に記述してください

app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3 //追加
//= require bootstrap-sprockets //追加
//= require bootstrap //追加
//= require_tree .

以上で、bootstrap4とjavascriptが利用可能になります。

いよいよ、アプリ作成に入っていきます。
まずはデフォルト状態ではapplicationはerbで書かれているのでhamlに変更します。以下のコマンドを入力してください。

rake haml:replace_erbs

2.reviewsコントローラ作成

以下のコマンドでreviewsコントローラを作成します。

rails g controller reviews show new
# showとnewを記述することで、show.html.haml,new.html.hamlも同時に作成してくれます。

上記コマンドでエラーが出た場合はGemfileを編集し、sqlite3のバージョンを指定してください

Gemfile
gem 'sqlite3', '~> 1.3.6'

作成できたらreviews_controllerを編集します。

app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
    def show
       @review = Review.find(params[:id])
    end
    def new
        @review = Review.new
        respond_to do |format|
          format.html
          format.json ##jsonで出力します。
          end
    end
    def create
        @review = Review.new(review_params)
        if @review.save
          redirect_to root_path
        else
          redirect_to new_review_path
        end
    end
    def review_params #ストロングパラメータで制限する。
        params.require(:review).permit(:name, :author, :review, :image_url, :introduction)
    end
end

合わせてレビューを投稿するためのルーティングも編集します。

config/routes.rb
  root 'reviews#new' # 今回はindexページがないので、review投稿ページをrootにします。
  resources :reviews

  #resourcesとすることで自動的にルーティングを作成してくれます。
  #作成されたrouteは rails routesコマンドで確認可能です。

3.Reviewモデル作成

レビューを格納するためにreviewモデルを作成します。

rails g model Review name:string author:string review:text image_url:string introduction:text

上記コマンドで以下のテーブルとカラムが作成されます。

reviewsテーブル

Column Type Options
name string
author string
review text
introduction text
image_url string

作成したmigrationファイルをdbにmigrateします。

rails db:migrate

4.本情報取得フォーム作成

以下のviewファイルを作成し、編集します。
bootstrap_formというgemを使用しているため、form_forをbootstrap_form_forとしています。
自動でbootstrapのスタイルが適用されます。

app/views/reviews/new.html.haml
.container
  .result
  .panel-title
    本情報取得フォーム
    .form-group{id:"get-book"}
      %input{type:"text",class:"form-control",placeholder:"ISBNを入力して下さい",id:"isbn"}
    .form-submit
      %button{type:"submit",id:"submit",class:"btn btn-outline-dark"} 本検索
  .result-image
  = bootstrap_form_for @review ,id:"form-result" do |f|
    = f.text_field :name,label: "タイトル",placeholder:"本のタイトルを入力して下さい",id:"book-name"
    = f.text_field :author,label: "著者",placeholder:"著者の名前を入力して下さい",id:"author-name"
    = f.text_area :introduction,type:"hidden",hide_label: true,id:"introduction",style:"display:none"
    = f.text_field :image_url,type:"hidden",hide_label: true, id:"image_url"
    = f.text_area :review,label: "レビュー",placeholder:"レビューを入力して下さい",size: "20x10",id:"review"
    = f.submit "投稿",class:"btn btn-outline-dark"

5.本情報取得jsファイル作成

book_review/app/assets/javascripts/の配下にreviews.js.erbを作成します。
拡張子にerbを入れているのは画像がなかった際のno_image画像をrailsのimagesフォルダから参照するためです。

app/assets/javascripts/reviews.js.erb
$(document).on('turbolinks:load', function() {
    //画像のHTMLを生成する。
    function buildImage(book) {
        var no_image = '<div class="book_image"><img "width="200" height="200" src="<%= image_path('no_image.jpg')%>"></div>';
        var image = '<div class="book_image img-thumbnail"><img "width="250" height="250" + src="' + book[0].summary.cover + '"></div>';
        if (!book[0].summary.cover){
            var image = no_image; //画像がなかった場合の処理
        }else{
            image;
        }
        return image;
    }
    //画像URLを生成する。
    function imageUrl(book){
        var no_image_url = '<%= image_path('/assets/no_image.jpg')%>';
        var image_url = book[0].summary.cover;
        //".jpg"に"_0"を加える。最大サイズの画像を取得できるようになる。
        var image_url = image_url.replace(".jpg", "_0.jpg");        
        if (!book[0].summary.cover){
            var image_url = no_image_url; //画像がなかった場合の処理
        }else{
            image_url
        }
        return image_url;
    }
    function bookDetail (book){
        var bookDetail = $.isEmptyObject(book[0].onix.CollateralDetail);
        if (bookDetail != true){
            $('#introduction').val(book[0].onix.CollateralDetail.TextContent[0].Text);
        }else{
            $('#introduction').val("情報がありません");
        }
    }
    //著者の情報がなかった場合の処理
    function authorName (book) {
        var bookAuthor = $.isEmptyObject(book[0].summary.author);
        if (bookAuthor != true){
            $('#author-name').val(book[0].summary.author);
        }else{
            $('#author-name').val("情報がありません");
        }
    }
    //本の情報がなかった場合のalert
    function noAppendBook(){
        var book = `<div class="alert alert-warning">
        <strong>本情報を取得できませんでした。</strong>
        </div>`
        return book
    }
    //本の情報取得に成功した時のalert
    function appendBook(){
        var book = `<div class="alert alert-primary">
        <strong>本情報の取得に成功しました。</strong>
        </div>`
        return book
    }
    //情報取得後、再度検索バーに入力が開始されたらフォームに入力されている取得済み情報を削除する。
    $('#get-book').on("keyup",function(){
        $('#submit').prop('disabled', false);
        $('.alert').remove();
        $('#book-name').val("");
        $('#author-name').val("");
        $('#image_url').val("");
        $('#introduction').val("");
        $('.book_image').empty();
    });
    //submitタグをクリックするとajaxで処理が開始される。
    $('#submit').on("click",function(e) {
        e.preventDefault();
        var bookName = $('#get-book').find('#isbn').prop('value');
        var requestUrl = 'https://api.openbd.jp/v1/get?isbn=';
        requestUrl += bookName + '&pretty';
        $.ajax({
            type:"GET",
            url:requestUrl,
            dataType:"json"
        })
            //通信が成功したときの処理
            .done(function(data) {
                if (data[0] != null){
                    var image = buildImage(data);
                    // resultに成功失敗のalart表示
                    $('.result').append(appendBook);
                    // 本のタイトル表示
                    $('#book-name').val(data[0].summary.title);
                    //hiddenタグであるimage_urlに取得した画像urlを格納
                    $('#image_url').val(imageUrl(data));
                    // 取得した本の画像を表示
                    $('.result-image').append(image);
                    // 本の著者表示
                    authorName(data);
                    // 本の紹介文表示
                    bookDetail(data);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                } else {
                    // 本情報取得に失敗した際のalert表示
                    $('.result').append(noAppendBook);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                }
            })
            // 通信に失敗した際のalert表示
            .fail(function() {
                alert('情報の取得に失敗しました');
            });
    });
});

本の画像が取得できなかった場合に代わりの画像を表示するため、以下のパスに以下の名前で代替画像を配置してください。

パス: app/assets/images/
ファイル名: no_image.jpg

ちなみに私は以下のフリー素材サイトからとりました。
http://design-ec.com/?p=55

これで、ブラウザでlocalhost:3000/reviews/newにアクセスすると本情報入力フォームが現れ、
「本情報取得フォーム」にISBN番号を入力する事で本の情報を取得する事ができるようになりました。

もし、フォームにISBNを入力しても反応がなかった場合、coffeeファイルが呼び出されてしまっている恐れがあるので、以下のファイルを削除してください。

app/assets/javascripts/reviews.coffee

6.投稿したレビューを表示

上記の作業で、review投稿ページおよび本の情報を取得するためのjsファイルを作成しましたが、
まだレビューを表示するためのviewがないので作成します。

app/views/reviews/show.html.haml
%section.book-images-frame.col-md-3.col-xs-12
  - if @review.image_url?
    = image_tag "#{@review.image_url}",class: "book-images-cover",style:"height:200px;width:150px;"
  - else
    = image_tag "no_image.jpg",class: "book-images-cover",style:"height:200px;width:150px;"
%section.book-content.col-md-6.col-xs-12
  .book-title-frame
    .book-title-block
      = @review.name
  .book-attribute-frame
    %h2 著者
    .book-author
      = @review.author
  .book-content
    %h2 紹介
    = simple_format (@review.introduction)
    %h2 レビュー
    = simple_format (@review.review)

7.railsサーバを起動

rails s #サーバ起動

 完成

これで本のレビューを投稿後、自動的にレビューページに遷移してくれる簡単なアプリが作成できました。

が、
まだまだ未完成なので修正していって頂ければと思います。

この記事は随時修正していこうと思いますが、
記事の間違いや、もっとこうしたほうがいいよ等ございましたら、コメント頂ければ幸いです。

参考にさせて頂いた記事

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

Openbd APIを使って簡単な本レビューアプリを作成する。 ( jQuery,Ajax 使用 )

初めまして。qiita初投稿になります。
業務未経験なため、間違いがある箇所もあると思いますがご指摘頂ければと思います。
今回書くことは基礎的なことですが、私のようなrails初心者のお力になれれば幸いです。

作成するレビューアプリの概要

  • openbdというAPIを使用し、ajaxを利用して非同期で本の情報を取得します。
  • あらかじめ用意したフォームに取得した任意の情報が自動入力され、レビューを入力し、投稿ボタンを押す事でreviewが投稿されます。
  • DBには画像はURLのみ保存し、画像自体は保存しません。
  • 取得する情報がない場合(画像や紹介文)は、「情報がありません」旨を表示させます。
  • bootstrap4を使用しviewを作成します。

アプリ構築環境

  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • Rails 5.2.2

アプリの作成の流れ

1.アプリを作成し、必要なgem(bootstrap4等)を導入します。
2.reviewsコントローラを作成します。
3.本情報およびレビューを格納するためのReviewモデルを作成します。
4.フォームを表示するためのviewを作成します。
5.非同期(ajax)で本情報を取得するためのjsファイルを作成します。
6.投稿したレビューを表示させるためのviewを作成します。
7.railsサーバを起動します。

1.アプリ作成

以下のコマンドでアプリを作成します。

rails new book_review  

アプリが作成出来たらGemfileを編集します。

cd book_review
vi Gemfile

以下のGemをファイルの一番下に記述します。

Gemfile
gem 'haml-rails' #erbではなくhamlで記述するために導入します。
gem 'erb2haml' #既存のerbファイルをhamlに変換します。
gem 'jquery-rails' #railsでjqueryを利用するために導入します。
gem 'bootstrap' #versionをしていないのでbootstrap4がインストールされます。
gem 'bootstrap_form' #formを自動でbootstrapレイアウトにしてくれます。

いつものようにbundleインストールします。

bundle install

bootstrap4とbootstrap_formを使用するために以下のファイルに記述します。

book_review/app/assets/stylesheets/ 配下のファイルを

application.cssからapplication.scssに名前変更してください。

application.scss
@import "bootstrap";
application.scss
*= require rails_bootstrap_forms

javascriptも使用するため、以下のファイルにも以下を記述します。
//= require_tree .の上に記述してください

app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3 //追加
//= require bootstrap-sprockets //追加
//= require bootstrap //追加
//= require_tree .

以上で、bootstrap4とjavascriptが利用可能になります。

いよいよ、アプリ作成に入っていきます。
まずはデフォルト状態ではapplicationはerbで書かれているのでhamlに変更します。以下のコマンドを入力してください。

rake haml:replace_erbs

2.reviewsコントローラ作成

以下のコマンドでreviewsコントローラを作成します。

rails g controller reviews show new
# showとnewを記述することで、show.html.haml,new.html.hamlも同時に作成してくれます。

上記コマンドでエラーが出た場合はGemfileを編集し、sqlite3のバージョンを指定してください

Gemfile
gem 'sqlite3', '~> 1.3.6'

作成できたらreviews_controllerを編集します。

app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
    def show
       @review = Review.find(params[:id])
    end
    def new
        @review = Review.new
        respond_to do |format|
          format.html
          format.json ##jsonで出力します。
          end
    end
    def create
        @review = Review.new(review_params)
        if @review.save
          redirect_to review_path(@review)
        else
          redirect_to new_review_path
        end
    end
    def review_params #ストロングパラメータで制限する。
        params.require(:review).permit(:name, :author, :review, :image_url, :introduction)
    end
end

合わせてレビューを投稿するためのルーティングも編集します。

config/routes.rb
  root 'reviews#new' # 今回はindexページがないので、review投稿ページをrootにします。
  resources :reviews

  #resourcesとすることで自動的にルーティングを作成してくれます。
  #作成されたrouteは rails routesコマンドで確認可能です。

3.Reviewモデル作成

レビューを格納するためにreviewモデルを作成します。

rails g model Review name:string author:string review:text image_url:string introduction:text

上記コマンドで以下のテーブルとカラムが作成されます。

reviewsテーブル

Column Type Options
name string
author string
review text
introduction text
image_url string

作成したmigrationファイルをdbにmigrateします。

rails db:migrate

4.本情報取得フォーム作成

以下のviewファイルを作成し、編集します。
bootstrap_formというgemを使用しているため、form_forをbootstrap_form_forとしています。
自動でbootstrapのスタイルが適用されます。

app/views/reviews/new.html.haml
.container
  .result
  .panel-title
    本情報取得フォーム
    .form-group{id:"get-book"}
      %input{type:"text",class:"form-control",placeholder:"ISBNを入力して下さい",id:"isbn"}
    .form-submit
      %button{type:"submit",id:"submit",class:"btn btn-outline-dark"} 本検索
  .result-image
  = bootstrap_form_for @review ,id:"form-result" do |f|
    = f.text_field :name,label: "タイトル",placeholder:"本のタイトルを入力して下さい",id:"book-name"
    = f.text_field :author,label: "著者",placeholder:"著者の名前を入力して下さい",id:"author-name"
    = f.text_area :introduction,type:"hidden",hide_label: true,id:"introduction",style:"display:none"
    = f.text_field :image_url,type:"hidden",hide_label: true, id:"image_url"
    = f.text_area :review,label: "レビュー",placeholder:"レビューを入力して下さい",size: "20x10",id:"review"
    = f.submit "投稿",class:"btn btn-outline-dark"

5.本情報取得jsファイル作成

book_review/app/assets/javascripts/の配下にreviews.js.erbを作成します。
拡張子にerbを入れているのは画像がなかった際のno_image画像をrailsのimagesフォルダから参照するためです。

app/assets/javascripts/reviews.js.erb
$(document).on('turbolinks:load', function() {
    //画像のHTMLを生成する。
    function buildImage(book) {
        var no_image = '<div class="book_image"><img "width="200" height="200" src="<%= image_path('no_image.jpg')%>"></div>';
        var image = '<div class="book_image img-thumbnail"><img "width="250" height="250" + src="' + book[0].summary.cover + '"></div>';
        if (!book[0].summary.cover){
            var image = no_image; //画像がなかった場合の処理
        }else{
            image;
        }
        return image;
    }
    //画像URLを生成する。
    function imageUrl(book){
        var no_image_url = '<%= image_path('/assets/no_image.jpg')%>';
        var image_url = book[0].summary.cover;
        //".jpg"に"_0"を加える。最大サイズの画像を取得できるようになる。
        var image_url = image_url.replace(".jpg", "_0.jpg");        
        if (!book[0].summary.cover){
            var image_url = no_image_url; //画像がなかった場合の処理
        }else{
            image_url
        }
        return image_url;
    }
    function bookDetail (book){
        var bookDetail = $.isEmptyObject(book[0].onix.CollateralDetail);
        if (bookDetail != true){
            $('#introduction').val(book[0].onix.CollateralDetail.TextContent[0].Text);
        }else{
            $('#introduction').val("情報がありません");
        }
    }
    //著者の情報がなかった場合の処理
    function authorName (book) {
        var bookAuthor = $.isEmptyObject(book[0].summary.author);
        if (bookAuthor != true){
            $('#author-name').val(book[0].summary.author);
        }else{
            $('#author-name').val("情報がありません");
        }
    }
    //本の情報がなかった場合のalert
    function noAppendBook(){
        var book = `<div class="alert alert-warning">
        <strong>本情報を取得できませんでした。</strong>
        </div>`
        return book
    }
    //本の情報取得に成功した時のalert
    function appendBook(){
        var book = `<div class="alert alert-primary">
        <strong>本情報の取得に成功しました。</strong>
        </div>`
        return book
    }
    //情報取得後、再度検索バーに入力が開始されたらフォームに入力されている取得済み情報を削除する。
    $('#get-book').on("keyup",function(){
        $('#submit').prop('disabled', false);
        $('.alert').remove();
        $('#book-name').val("");
        $('#author-name').val("");
        $('#image_url').val("");
        $('#introduction').val("");
        $('.book_image').empty();
    });
    //submitタグをクリックするとajaxで処理が開始される。
    $('#submit').on("click",function(e) {
        e.preventDefault();
        var bookName = $('#get-book').find('#isbn').prop('value');
        var requestUrl = 'https://api.openbd.jp/v1/get?isbn=';
        requestUrl += bookName + '&pretty';
        $.ajax({
            type:"GET",
            url:requestUrl,
            dataType:"json"
        })
            //通信が成功したときの処理
            .done(function(data) {
                if (data[0] != null){
                    var image = buildImage(data);
                    // resultに成功失敗のalart表示
                    $('.result').append(appendBook);
                    // 本のタイトル表示
                    $('#book-name').val(data[0].summary.title);
                    //hiddenタグであるimage_urlに取得した画像urlを格納
                    $('#image_url').val(imageUrl(data));
                    // 取得した本の画像を表示
                    $('.result-image').append(image);
                    // 本の著者表示
                    authorName(data);
                    // 本の紹介文表示
                    bookDetail(data);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                } else {
                    // 本情報取得に失敗した際のalert表示
                    $('.result').append(noAppendBook);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                }
            })
            // 通信に失敗した際のalert表示
            .fail(function() {
                alert('情報の取得に失敗しました');
            });
    });
});

本の画像が取得できなかった場合に代わりの画像を表示するため、以下のパスに以下の名前で代替画像を配置してください。

パス: app/assets/images/
ファイル名: no_image.jpg

ちなみに私は以下のフリー素材サイトからとりました。
http://design-ec.com/?p=55

これで、ブラウザでlocalhost:3000/reviews/newにアクセスすると本情報入力フォームが現れ、
「本情報取得フォーム」にISBN番号を入力する事で本の情報を取得する事ができるようになりました。

もし、フォームにISBNを入力しても反応がなかった場合、coffeeファイルが呼び出されてしまっている恐れがあるので、以下のファイルを削除してください。

app/assets/javascripts/reviews.coffee

6.投稿したレビューを表示

上記の作業で、review投稿ページおよび本の情報を取得するためのjsファイルを作成しましたが、
まだレビューを表示するためのviewがないので作成します。

app/views/reviews/show.html.haml
%section.book-images-frame.col-md-3.col-xs-12
  - if @review.image_url?
    = image_tag "#{@review.image_url}",class: "book-images-cover",style:"height:200px;width:150px;"
  - else
    = image_tag "no_image.jpg",class: "book-images-cover",style:"height:200px;width:150px;"
%section.book-content.col-md-6.col-xs-12
  .book-title-frame
    .book-title-block
      = @review.name
  .book-attribute-frame
    %h2 著者
    .book-author
      = @review.author
  .book-content
    %h2 紹介
    = simple_format (@review.introduction)
    %h2 レビュー
    = simple_format (@review.review)

7.railsサーバを起動

rails s #サーバ起動

 完成

これで本のレビューを投稿後、自動的にレビューページに遷移してくれる簡単なアプリが作成できました。

が、
まだまだ未完成なので修正していって頂ければと思います。

この記事は随時修正していこうと思いますが、
記事の間違いや、もっとこうしたほうがいいよ等ございましたら、コメント頂ければ幸いです。

参考にさせて頂いた記事

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

【Rails】openBD APIを使って簡単な本レビューアプリを作成する。【jQuery,Ajax 】

初めまして。qiita初投稿になります。
業務未経験なため、間違いがある箇所もあると思いますがご指摘頂ければと思います。
今回書くことは基礎的なことですが、私のようなrails初心者のお力になれれば幸いです。

openBD APIについて

書誌情報・書影を、だれでも自由に使える、高速なAPIで提供しているプロジェクトです。

24,747社の約76万タイトルの本の書誌情報、書影、ためし読み、書評掲載情報を利用でき、
書誌情報1件あたり1ミリ秒以下での応答と非常に高速なAPIです。

公式サイト
https://openbd.jp/

作成するレビューアプリの概要

  • openbdというAPIを使用し、ajaxを利用して非同期で本の情報を取得します。
  • あらかじめ用意したフォームに取得した任意の情報が自動入力され、レビューを入力し、投稿ボタンを押す事でreviewが投稿されます。
  • DBには画像はURLのみ保存し、画像自体は保存しません。
  • 取得する情報がない場合(画像や紹介文)は、「情報がありません」旨を表示させます。
  • bootstrap4を使用しviewを作成します。

アプリ構築環境

  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • Rails 5.2.2

アプリの作成の流れ

1.アプリを作成し、必要なgem(bootstrap4等)を導入します。
2.reviewsコントローラを作成します。
3.本情報およびレビューを格納するためのReviewモデルを作成します。
4.フォームを表示するためのviewを作成します。
5.非同期(ajax)で本情報を取得するためのjsファイルを作成します。
6.投稿したレビューを表示させるためのviewを作成します。
7.railsサーバを起動します。

1.アプリ作成

以下のコマンドでアプリを作成します。

rails new book_review  

アプリが作成出来たらGemfileを編集します。

cd book_review
vi Gemfile

以下のGemをファイルの一番下に記述します。

Gemfile
gem 'haml-rails' #erbではなくhamlで記述するために導入します。
gem 'erb2haml' #既存のerbファイルをhamlに変換します。
gem 'jquery-rails' #railsでjqueryを利用するために導入します。
gem 'bootstrap' #versionをしていないのでbootstrap4がインストールされます。
gem 'bootstrap_form' #formを自動でbootstrapレイアウトにしてくれます。

いつものようにbundleインストールします。

bundle install

bootstrap4とbootstrap_formを使用するために以下のファイルに記述します。

book_review/app/assets/stylesheets/ 配下のファイルを

application.cssからapplication.scssに名前変更してください。

application.scss
@import "bootstrap";
application.scss
*= require rails_bootstrap_forms

javascriptも使用するため、以下のファイルにも以下を記述します。
//= require_tree .の上に記述してください

app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery3 //追加
//= require bootstrap-sprockets //追加
//= require bootstrap //追加
//= require_tree .

以上で、bootstrap4とjavascriptが利用可能になります。

いよいよ、アプリ作成に入っていきます。
まずはデフォルト状態ではapplicationはerbで書かれているのでhamlに変更します。以下のコマンドを入力してください。

rake haml:replace_erbs

2.reviewsコントローラ作成

以下のコマンドでreviewsコントローラを作成します。

rails g controller reviews show new
# showとnewを記述することで、show.html.haml,new.html.hamlも同時に作成してくれます。

上記コマンドでエラーが出た場合はGemfileを編集し、sqlite3のバージョンを指定してください

Gemfile
gem 'sqlite3', '~> 1.3.6'

作成できたらreviews_controllerを編集します。

app/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
    def show
       @review = Review.find(params[:id])
    end
    def new
        @review = Review.new
        respond_to do |format|
          format.html
          format.json ##jsonで出力します。
          end
    end
    def create
        @review = Review.new(review_params)
        if @review.save
          redirect_to review_path(@review)
        else
          redirect_to new_review_path
        end
    end
    def review_params #ストロングパラメータで制限する。
        params.require(:review).permit(:name, :author, :review, :image_url, :introduction)
    end
end

合わせてレビューを投稿するためのルーティングも編集します。

config/routes.rb
  root 'reviews#new' # 今回はindexページがないので、review投稿ページをrootにします。
  resources :reviews

  #resourcesとすることで自動的にルーティングを作成してくれます。
  #作成されたrouteは rails routesコマンドで確認可能です。

3.Reviewモデル作成

レビューを格納するためにreviewモデルを作成します。

rails g model Review name:string author:string review:text image_url:string introduction:text

上記コマンドで以下のテーブルとカラムが作成されます。

reviewsテーブル

Column Type Options
name string
author string
review text
introduction text
image_url string

作成したmigrationファイルをdbにmigrateします。

rails db:migrate

4.本情報取得フォーム作成

以下のviewファイルを作成し、編集します。
bootstrap_formというgemを使用しているため、form_forをbootstrap_form_forとしています。
自動でbootstrapのスタイルが適用されます。

app/views/reviews/new.html.haml
.container
  .result
  .panel-title
    本情報取得フォーム
    .form-group{id:"get-book"}
      %input{type:"text",class:"form-control",placeholder:"ISBNを入力して下さい",id:"isbn"}
    .form-submit
      %button{type:"submit",id:"submit",class:"btn btn-outline-dark"} 本検索
  .result-image
  = bootstrap_form_for @review ,id:"form-result" do |f|
    = f.text_field :name,label: "タイトル",placeholder:"本のタイトルを入力して下さい",id:"book-name"
    = f.text_field :author,label: "著者",placeholder:"著者の名前を入力して下さい",id:"author-name"
    = f.text_area :introduction,type:"hidden",hide_label: true,id:"introduction",style:"display:none"
    = f.text_field :image_url,type:"hidden",hide_label: true, id:"image_url"
    = f.text_area :review,label: "レビュー",placeholder:"レビューを入力して下さい",size: "20x10",id:"review"
    = f.submit "投稿",class:"btn btn-outline-dark"

5.本情報取得jsファイル作成

book_review/app/assets/javascripts/の配下にreviews.js.erbを作成します。
拡張子にerbを入れているのは画像がなかった際のno_image画像をrailsのimagesフォルダから参照するためです。

app/assets/javascripts/reviews.js.erb
$(document).on('turbolinks:load', function() {
    //画像のHTMLを生成する。
    function buildImage(book) {
        var no_image = '<div class="book_image"><img "width="200" height="200" src="<%= image_path('no_image.jpg')%>"></div>';
        var image = '<div class="book_image img-thumbnail"><img "width="250" height="250" + src="' + book[0].summary.cover + '"></div>';
        if (!book[0].summary.cover){
            var image = no_image; //画像がなかった場合の処理
        }else{
            image;
        }
        return image;
    }
    //画像URLを生成する。
    function imageUrl(book){
        var no_image_url = '<%= image_path('/assets/no_image.jpg')%>';
        var image_url = book[0].summary.cover;
        //".jpg"に"_0"を加える。最大サイズの画像を取得できるようになる。
        var image_url = image_url.replace(".jpg", "_0.jpg");        
        if (!book[0].summary.cover){
            var image_url = no_image_url; //画像がなかった場合の処理
        }else{
            image_url
        }
        return image_url;
    }
    function bookDetail (book){
        var bookDetail = $.isEmptyObject(book[0].onix.CollateralDetail);
        if (bookDetail != true){
            $('#introduction').val(book[0].onix.CollateralDetail.TextContent[0].Text);
        }else{
            $('#introduction').val("情報がありません");
        }
    }
    //著者の情報がなかった場合の処理
    function authorName (book) {
        var bookAuthor = $.isEmptyObject(book[0].summary.author);
        if (bookAuthor != true){
            $('#author-name').val(book[0].summary.author);
        }else{
            $('#author-name').val("情報がありません");
        }
    }
    //本の情報がなかった場合のalert
    function noAppendBook(){
        var book = `<div class="alert alert-warning">
        <strong>本情報を取得できませんでした。</strong>
        </div>`
        return book
    }
    //本の情報取得に成功した時のalert
    function appendBook(){
        var book = `<div class="alert alert-primary">
        <strong>本情報の取得に成功しました。</strong>
        </div>`
        return book
    }
    //情報取得後、再度検索バーに入力が開始されたらフォームに入力されている取得済み情報を削除する。
    $('#get-book').on("keyup",function(){
        $('#submit').prop('disabled', false);
        $('.alert').remove();
        $('#book-name').val("");
        $('#author-name').val("");
        $('#image_url').val("");
        $('#introduction').val("");
        $('.book_image').empty();
    });
    //submitタグをクリックするとajaxで処理が開始される。
    $('#submit').on("click",function(e) {
        e.preventDefault();
        var bookName = $('#get-book').find('#isbn').prop('value');
        var requestUrl = 'https://api.openbd.jp/v1/get?isbn=';
        requestUrl += bookName + '&pretty';
        $.ajax({
            type:"GET",
            url:requestUrl,
            dataType:"json"
        })
            //通信が成功したときの処理
            .done(function(data) {
                if (data[0] != null){
                    var image = buildImage(data);
                    // resultに成功失敗のalart表示
                    $('.result').append(appendBook);
                    // 本のタイトル表示
                    $('#book-name').val(data[0].summary.title);
                    //hiddenタグであるimage_urlに取得した画像urlを格納
                    $('#image_url').val(imageUrl(data));
                    // 取得した本の画像を表示
                    $('.result-image').append(image);
                    // 本の著者表示
                    authorName(data);
                    // 本の紹介文表示
                    bookDetail(data);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                } else {
                    // 本情報取得に失敗した際のalert表示
                    $('.result').append(noAppendBook);
                    // 本情報取得に成功後 submitタグを押せないようにする。
                    $('#submit').prop('disabled', true);
                }
            })
            // 通信に失敗した際のalert表示
            .fail(function() {
                alert('情報の取得に失敗しました');
            });
    });
});

本の画像が取得できなかった場合に代わりの画像を表示するため、以下のパスに以下の名前で代替画像を配置してください。

パス: app/assets/images/
ファイル名: no_image.jpg

ちなみに私は以下のフリー素材サイトからとりました。
http://design-ec.com/?p=55

これで、ブラウザでlocalhost:3000/reviews/newにアクセスすると本情報入力フォームが現れ、
「本情報取得フォーム」にISBN番号を入力する事で本の情報を取得する事ができるようになりました。

もし、フォームにISBNを入力しても反応がなかった場合、coffeeファイルが呼び出されてしまっている恐れがあるので、以下のファイルを削除してください。

app/assets/javascripts/reviews.coffee

6.投稿したレビューを表示

上記の作業で、review投稿ページおよび本の情報を取得するためのjsファイルを作成しましたが、
まだレビューを表示するためのviewがないので作成します。

app/views/reviews/show.html.haml
%section.book-images-frame.col-md-3.col-xs-12
  - if @review.image_url?
    = image_tag "#{@review.image_url}",class: "book-images-cover",style:"height:200px;width:150px;"
  - else
    = image_tag "no_image.jpg",class: "book-images-cover",style:"height:200px;width:150px;"
%section.book-content.col-md-6.col-xs-12
  .book-title-frame
    .book-title-block
      = @review.name
  .book-attribute-frame
    %h2 著者
    .book-author
      = @review.author
  .book-content
    %h2 紹介
    = simple_format (@review.introduction)
    %h2 レビュー
    = simple_format (@review.review)

7.railsサーバを起動

rails s #サーバ起動

 完成

これで本のレビューを投稿後、自動的にレビューページに遷移してくれる簡単なアプリが作成できました。

が、
まだまだ未完成なので修正していって頂ければと思います。

この記事は随時修正していこうと思いますが、
記事の間違いや、もっとこうしたほうがいいよ等ございましたら、コメント頂ければ幸いです。

参考にさせて頂いた記事

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

[学習用]Rails5で画像投稿 CarrierWaveで最速保存

うっかり忘れたので構築した手順を忘れない様にメモ。

scaffold で一括作成

$ rails g scaffold shop title:string content:text name:string cat_id:integer user_id:integer image:string

まず、scaffold で一括作成。

ハマったポイント

このときimage:string も一緒に作ってしまう。
ここを忘れてて、画像が登録されないerrorに悩まされる。

もし追加し忘れたら

$ rails g migration add_image_column_to_shops image:string
$ rails db:migrate

これで大丈夫。

Gem CarrierWaveの追加

gem 'carrierwave'

Gemfile に上記を追加して

$ bundle

アップローダークラス生成

$ rails g uploader image

app/uploader/image_uploader.rb が作成される

CarrierWave編集

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  storage :file
  # storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_whitelist
    %w(jpg jpeg gif png)
  end
  # 画像名をリネームさせる(日付時間はダメ絶対)
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end

end

はまったポイント

"#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.jpg" if original_filename.present?
タイムゾーンにするとリサイズ時にエラーが出てサイズの変更ができなくなる。
エラー名が全然違うので、全くわからなかった。
RMagick入ってないよとかMiniMagick入ってないよとかそんなerrorが出てテンパる。

追加

/app/models/shop.rb
class Shop < ApplicationRecord
  mount_uploader :image, ImageUploader
end

パラメータの確認

/app/controllers/posts_controller.rb
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_shop
      @shop = Shop.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def shop_params
      params.require(:shop).permit(:title, :content, :cat_id, :name, :user_id, :image)
    end

image をscaffoldより後から追加したなら :imageを追加する。
image をscaffoldで追加したなら入っているか確認する。

画像が表示できる様にする

画像を登録

/app/views/_form.html.erb
  <%= form.label :image %>
  <%= form.file_field :image %>

変更、新規、編集の時に追加できるように。

表示側の追加

/app/views/show.html.erb
<% if @shop.image? %>
  <%= image_tag @shop.image.url %>
<% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[学習用]Rails5:CarrierWaveで画像投稿+rmagickでサムネイル保存+画像名ランダム化

うっかり忘れたので構築した手順を忘れない様にメモ。
※ローカルテスト用。Herokuには保存できないので、AWSのS3に保存するときは
 保存先を違うものに変更する必要がある。

scaffold で一括作成

$ rails g scaffold shop title:string content:text name:string cat_id:integer user_id:integer image:string

まず、scaffold で一括作成。

ハマったポイント

このときimage:string も一緒に作ってしまう。
ここを忘れてて、画像が登録されないerrorに悩まされる。

もし追加し忘れたら

$ rails g migration add_image_column_to_shops image:string
$ rails db:migrate

これで大丈夫。

Gem CarrierWaveの追加

gem 'carrierwave'

Gemfile に上記を追加して

$ bundle

アップローダークラス生成

$ rails g uploader image

app/uploader/image_uploader.rb が作成される

CarrierWave編集

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  storage :file
  # storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_whitelist
    %w(jpg jpeg gif png)
  end
  # 画像名をリネームさせる(日付時間はダメ絶対)
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end

end

はまったポイント

"#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.jpg" if original_filename.present?
タイムゾーンにするとリサイズ時にエラーが出てサイズの変更ができなくなる。
エラー名が全然違うので、全くわからなかった。
RMagick入ってないよとかMiniMagick入ってないよとかそんなerrorが出てテンパる。

追加

/app/models/shop.rb
class Shop < ApplicationRecord
  mount_uploader :image, ImageUploader
end

パラメータの確認

/app/controllers/posts_controller.rb
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_shop
      @shop = Shop.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def shop_params
      params.require(:shop).permit(:title, :content, :cat_id, :name, :user_id, :image)
    end

image をscaffoldより後から追加したなら :imageを追加する。
image をscaffoldで追加したなら入っているか確認する。

画像が表示できる様にする

画像を登録

/app/views/_form.html.erb
  <%= form.label :image %>
  <%= form.file_field :image %>

変更、新規、編集の時に追加できるように。

表示側の追加

/app/views/show.html.erb
<% if @shop.image? %>
  <%= image_tag @shop.image.url %>
<% end %>

画像のリサイズ

gem 'rmagick'
$ bundle

CarrierWave編集

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  include CarrierWave::RMagick
  # include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # ファイルサイズに制限をつける
  def size_range
    1..5.megabytes
  end

  # 画像の上限を640x480にする
  process :resize_to_limit => [640, 640]

  # サムネイル保存する
  version :thumb do
    process :resize_to_limit => [320, 320]
  end

  # 保存形式
  process :convert => 'jpg'

  def extension_whitelist
    %w(jpg jpeg gif png)
  end

  # 拡張子が同じでないとGIFをJPGとかにコンバートできない
  def filename
    super.chomp(File.extname(super)) + '.jpg' if original_filename.present?
  end

  # ファイル名を日付にすると不具合が出る
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end
end

Viewにサムネイル追加

/app/views/show.html.erb
<% if @shop.image? %>
  <%= image_tag @shop.image.url %>
<% end %>

<%= image_tag @shop.image.url(:thumb) %>

これで640画像と320のサムネイル画像が表示される
これで忘れても大丈夫なはず。

参考にしたサイト
https://nyoken.com/rails-carrierwave

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

Ruby | 配列の奇数番目と偶数番目を取り出す方法

配列の奇数番目や偶数番目を取り出して新たな配列にする

配列の中から偶数番目または奇数番目の要素を取り出したいときに役に立ったので共有します。
今回は、each_sliceメソッドとmapメソッドを使用してやってみました。

参考記事
each_slice (Enumerable) - Rubyリファレンス
ある配列を任意の要素数の配列に分割したい - Qiita

結論はこんな感じです。

ary = [1,2,3,4,5,6,7,8,9,10]
odd = ary.each_slice(2).map(&:first)
even = ary.each_slice(2).map(&:last)

p odd #=>[1,3,5,7,9]
p even #=>[2,4,6,8,10]

少し分解して考えてみた

結論の書き方だと何をやっているのかイマイチ分かりにくいと思いますので、自分なりに分解してみました。

ある配列を任意の要素数の配列に分割したい - Qiita
each_sliceについては、上の記事が分かりやすく解説していました。

each_slice(数): 数の部分には何分割するかを決める数値を入れる

なので、数の部分を2とすると、配列を2つずつ取り出して、それをさらにmapメソッドで配列に入れている感じです。

ary.each_slice(2).map {|n| n}
#=>[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

これを利用して、以下のようにします。

odd = ary.each_slice(2).map {|n| n.first} #[1, 2].firstみたいなことを繰り返している
#=>[1,3,5,7,9]

even = ary.each_slice(2).map {|n| n.last}
#=>[2,4,6,8,10]
  • 取り出した配列nの要素の1番目を取り出すと、奇数番目の配列が完成する。
    つまりfirstメソッドを使用する。
  • 取り出した配列nの要素の2番目を取り出すと、偶数番目の配列が完成する。
    つまりlastメソッドを使用する。



初心者なりに考えたやり方になりますので、他にも色々なやり方がたくさんあると思います。
もしこんなやり方もあるよ!って方がいればぜひ教えていただければと思います。
読んでいただきありがとうございました。

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

無限ループ∞最短選手権

さぁみんな無限ループしよう。

最近、無限ループが流行りらしいので
各プログラミング言語(その辺にあった10個の言語)の
無限ループを比べてみます。
果たしてどの言語が1位に輝くのか!?

※改行は1文字としてカウント。
(一応、全て実行してチェックしています)

C (24文字)

int main(void){for(;;);}

C⋕ (37文字)

class a{static void Main(){for(;;);}}

C++ (20文字)

int main(){for(;;);}

D (22文字)

void main(){for(;;){}}

Go (31文字)

package main
func main(){for{}}

Java (54文字)

class a{public static void main(String[] a){for(;;);}}

JavaScript (8文字)

for(;;);

PHP (14文字)

<?php
for(;;);

Python (9文字)

while 1:0

Ruby (11文字)

while 1
end

【優勝】JavaScript

チャンピオンは"JavaScript"でした。さすが"JS"
"JavaScript"は無限ループ界において最有力候補であると考えられますね。

???「JSが優勝だと思っていたのか。」

【真の優勝】Ruby ※追記

Ruby(6文字)
loop{}

Ruby、6文字で無限ループが出来るとは……。
恐るびー

最後に

もっと文字数減らせるよ!とか
もっと文字数が少ない言語あるぜ!最強だぜ!

などなどありましたらコメントまたは編集リクエストでお願い致します。

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

?‍♀️無限ループ∞最短選手権?‍♂️

さぁみんな無限ループしよう。

最近、無限ループが流行りらしいので
各プログラミング言語(その辺にあった10個の言語)の
無限ループを比べてみます。
果たしてどの言語が1位に輝くのか!?

※改行は1文字としてカウント。
(一応、全て実行してチェックしています)

C (24文字)

int main(void){for(;;);}

C⋕ (37文字)

class a{static void Main(){for(;;);}}

C++ (20文字)

int main(){for(;;);}

D (22文字)

void main(){for(;;){}}

Go (31文字)

package main
func main(){for{}}

Java (53文字)

class a{public static void main(String[]a){for(;;);}}

JavaScript (8文字)

for(;;);

PHP (14文字)

<?php
for(;;);

Python (9文字)

while 1:0

Ruby (11文字)

while 1
end

【優勝】JavaScript

チャンピオンは"JavaScript"でした。さすが"JS"
"JavaScript"は無限ループ界において最有力候補であると考えられますね。

???「JSが優勝だと思っていたのか。」

【真の優勝】Ruby ※追記

Ruby(6文字)
loop{}

Ruby、6文字で無限ループが出来るとは……。
恐るびー

???「6文字ごときが優勝だと思っていたのか。」

【本当の真の優勝】L00P ※追記(番外編)

L00P(0文字)

0文字……圧巻です。
言語名からして、無限ループ界の頂点に君臨していると思われる風貌をしてますね……。

このように上記で比べていた10個の言語以外の言語では、もっと文字数が少ないものがありました。
無限ループは奥が深い。

最後に

もっと文字数減らせるよ!とか
もっと文字数が少ない言語あるぜ!最強だぜ!

などなどありましたらコメントまたは編集リクエストでお願い致します。

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

引数の削除(Remove Parameter)

image.png

1つずつリファクタリング技法まとめ
個人的に簡単かつ取り入れ易いと思うものから

目的

すぐ引き出せるようにする

基本作業サイクル

  • システムを動かして仕様を精査
  • テストメソッドを作成
  • テストの失敗を確認
  • テストの成功を確認
  • 小さい変更、随時テスト実行(パターン追加失敗確認->成功確認)
  • 最後テスト実行
  • 最後動作確認

引数の削除(Remove Parameter)とは

メソッド本体が引数を使わなくなり、引数を削除するもの
引数の追加の逆

ポイント

  • 必要じゃなくなったものは消す。
  • インスタンス変数として扱うべきでないか検討する
  • 同じシグネチャのメソッドが存在しないか確認する(同じように削除する必要がある可能性がある)

def setting(url, id, password, date, logger)
  @date = date
  @logger = logger
  login = {
    url: url,
    id: id,
    password: password
  }
end

   ↓

def initialize(url, id, password, date, logger)
  @date = date
  @logger = logger
end

def setting(url, id, password)
  login = {
    url: url,
    id: id,
    password: password
  }
end

書籍情報

Jay Fields (著), Shane Harvie (著), Martin Fowler (著), Kent Beck (著),
長尾 高弘(訳), リファクタリング:Rubyエディション
https://amzn.to/2VlyWML

雑感

「引数の追加」と「引数の削除」と「引数オブジェクトの導入」は一度にまとめて行えそう

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

Rubyのあれこれ

if分でエラー出た話

C言語と一緒とか書いたけど
これでエラーがでた

# まぁC言語と一緒
if score > 80
  puts "good"
else if score > 20
  puts "good!"
else
  puts "so...daiso.."
end

エラーの意味は読み取ったけど、どこも可笑しくないよな!?
って思ってた
✖ else if
〇 els if

慣れって怖いよねってお話です

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

[備忘録]プログラミング学習_2(webエンジニアの将来、言語概念の学習)

投稿目的

個人的な学習記録
同じ初心者の技術力・モチベーションの向上

本日の学習内容(webエンジニアの将来、言語概念の学習)

昨日(_1)、jsやReactの文法などの学習を行ったが、技術の需要や将来性、用途について一度考えようと思い今日は調べごとを中心に行った。

調べた事

1.Reactはjsのライブラリ。フレームワークじゃない。
ライブラリは各用途の便利グッズまとめ、フレームワークは起承転結楽々テンプレートって理解になった。
参考URL:(https://qita.com/azuki8/items/ad7710fdefaedc63e3f7)


2.KENTAさん動画にてキャリア考察
年齢・勉強・好奇心のリスクについて考えさせられた。業界の成熟的に50歳以上の正社員さんの数が現状少ないが、今の30代が20年後どのようなキャリアになっているのか気になる。何も考えずに楽しそうだけでWeb系エンジニアになるのは危険かもしれない。

(Web系エンジニアという職業の3つのリスクである「年齢」「勉強」「好奇心」)
参考URL: (https://www.youtube.com/watch?v=YuOYjxpMI4w)


3.KENTAさん動画にて「わらしべ長者戦略」
私自身、新卒+テックエキスパートの手札で現在の内定先を獲得したので、業務を通して自分自身の手札を増やしつつステップアップする方法は正しいと感じた。一つの言語に固執して、廃れる可能性を日々考えるのは精神的にもよくないしね。
参考URL: (https://www.youtube.com/watch?v=eElCAwuDwsk)

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