20190307のRailsに関する記事は17件です。

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で続きを読む

【Rails】boolean型のカラムのバリデーション方法

boolean型のカラムのバリデーションをするときは

validates :status, presence: true

これではfalseの時にエラーが出てしまいます。
presenceというバリデーションはblank?が偽であることを検証するためfalseで反応してしまうからです。

正しいboolean型のバリデーション

validates :status, inclusion: {in: [true, false]}

これでtrueとfalse以外に反応するバリデーションができます。

  • このエントリーをはてなブックマークに追加
  • 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でのSEOに関してのメモ

SEOとは

SEO(Search Engine Optimization)とは検索エンジン最適化または検索エンジン最適化ツールのことである。Googleなどの検索エンジンの検索結果の上位に自分のサイトを出したいときにこのSEO対策はすると良い。

SEO対策はなぜ必要か

Googleなどの検索エンジンにて検索を行った際に、検索結果の上位に自分で作成したものを持ってきて、より多くの人に見てもらうため。良い記事、良いサービスであったとしても検索結果の上位に出てこなかった場合にはユーザーに閲覧してもらえない。Internet Marketing Ninjasの行った調査によると、検索結果の一番上に出てくるサイトは21.1%のクリック率で、2番目に出てくる検索結果は、10.65%、4番目以下は5%を下回る。このことからもわかるように閲覧(クリック)されるためには検索結果の上位に自分のサイトが表示されなければならない。

対象とする読者

Railsでの開発を行っていて、SEO対策に関して初心者の人

対策として行うべきこと

  • Sitemapの登録
  • meta tagsの設置
  • Canonicalの設置
  • Page speed
  • CSS, JavaScriptの最適化
  • 画像の圧縮(主にモバイル用)

Sitemapについて

sitemapを登録するときにはRailsではsitemap_generatorwheneverを用いる

  • sitemapを生成するときにはconfig/sitemap.rbの中にsitemapとして追加したいものをaddを使って記述していく
  • 重要なのはsitemapを生成するときにはrails routesで調べるんじゃなくて、ページベース(つまりは、実際にユーザーが検索でアクセスしたいと思われるページ)で追加していく
  • 管理者のページとかだと追加しないのが好ましい

meta-tagsについて

使うツール

チェックする項目

  • title(lighthouseで確認可能)
  • meta description(lighthouseで確認可能)
  • canonical

Meta descriptionについて

Meta descriptionとは

ページ内の概要を表すテキスト情報。検索結果にサマリーとして表示される。ただSEO上の効果はほぼない

Canonicalについて

Canonicalとは

ページ内容が類似もしくは重複しているURLが複数存在する場合に、検索エンジンからのページが評価が分散されないよう、正規のURLがどれなのかを検索エンジンに示すために用いる記述

同じページなのに別ページとみなされるURLの例

上記のようにちょっとした違いでも検索エンジンに別ページとしてみなされるので、SEOの順位を上げるためにはcanonical(正規)を設定することが必要

railsだとmeta-tagで、定義することが可能である

ページの読み込み順について

ページがどのように表示されるか

Page speedについて

Webサイトの表示速度を早くするためにやることとしては主に3個ある

  • 表示する画像の圧縮
  • CSS, JavaScriptの最適化をする
  • ブラウザのキャッシュを利用する

使うツール

PageSpeed Insights

表示する画像の圧縮について

表示する画像の大きさによって、レスポンスとして送る情報量が変わり、それは表示スピードにも影響する。なので、画像の圧縮は必要である

対策

  • 拡張子がpngのものを用いたほうが圧縮率が高くなるので良い
  • spriteなどを用いて複数の画像をまとめて送信する

具体的には以下のリンクを参考にすると良い

画像ファイルの最適化とスプライト画像の作り方

CSS, JavaScriptの最適について

ページを描画する際には、まずブラウザはHTMLからDOMを作成してそれを元に随時読み込まれた情報を描画していく。基本的にはHTMLファイルの上から順番に描画を行っていく。ページスピードとして測られるのはHTMLの描画についてなので、なるべく早くこの描画を終わらせることが表示速度を上げることにつながる

対策

  • ユーザーが最初に目に触れる箇所(例えば下に長く続いているページであれば、ページの上の部分だけ)を最初にスタイルが当たるようにする。具体的には、stylesheet link rel = "ほにゃらら"のように外部のCSSファイルを読み出すのではなく、HTMLのmeta headの中に最初に目に触れそうだと思われる部分のCSSだけ書いておく
  • headタグの中にstyle sheetやjsの記述を書いてしまったら、それを読み込み終わるまでHTMLのbody部分の描画が行われないのでこれを避けるために、JavaScriptとCSSの読み込みを</body>の一個前の行に書く。具体的にはこれを行うためには少し変わった処理(JavaScriptでCSSを読み込む手法)が必要である。

JavaScriptでCSSを読み込む手法

<noscript id="deferred-styles">
<%= stylesheet_link_tag "スタイルシート名前" %>
</noscript>
<script>
  var loadDeferredStyles = function() {
    var addStylesNode = document.getElementById("deferred-styles");
    var replacement = document.createElement("div");
    replacement.innerHTML = addStylesNode.textContent;
    document.body.appendChild(replacement)
    addStylesNode.parentElement.removeChild(addStylesNode);
  };
  var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
  else window.addEventListener('load', loadDeferredStyles);
</script>
</body>

上のプログラムのような感じでJavaScriptを読み込むようにするのが良いと現状では言われている。

参考・引用

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

Rubyで使用する各ツールの用途について

Rubyで活躍する代表的なツール

まずはツールの名前だけ先に上げていくことにします。
詳しくは以下でまとめていきますので一緒に確認していきましょう!

・Gem
・Ruby On Rails(gemの中の1つ)
・bundler
・sequel pro

Gemとは

Gemはライブラリの一種です。
Gemはどういった用途があるかというと、例えばとある機能を実装しようとした時に自分で1から実装しようとすると、かなりの労力と時間がかかってしまう可能性があるのですが、Gemではすでに他の誰かが開発済みの機能を借りて来ることができてしまうんです。

つまり、アプリ作成において自分で実装する部分を最小限に抑えることができるというかなりの優れものです。

Gemについての詳細はこちらを参考にしてみてください!
https://blog.codecamp.jp/rails-gem

Sequelproとは

次にSequelproについてですがこちらはGUIツールとよばれるものの1つです。
簡単にいうと、データベースにつなげるためのものです。
データベース(DB)はMySQLというものがよく知られています。

MySQLへの接続方法の詳細についてはこちらを参考にしてみてください!
https://qiita.com/iwaseasahi/items/e245e61da4b7613494c5

bundlerとは

bundlerとはGem同士の互換性を保ちながら、パッケージの種類やバージョンを管理してくれる仕組みを持つものです。
実はbundlerもGemの1つです。
このbundlerがないと以下のようなことが起きてしまい、動作がうまくいかない現象が多発してしまいます。

例えば、いくつもライブラリを使用している中でAのライブラリは最新の状態で保持されているが、ライブラリBは1つ前のバージョンであった場合に互換性の問題でうまく動作しない。

こういった現象が起きないように管理してくれるのがbundlerというわけです。
bundlerをインストールしておくだけで無意識にこういったことが原因で起きるエラーを回避できるということなんです。

インストールしておいて損はないので是非インストールしましょう!

インストールについては以下を参考にしてください!

https://www.sejuku.net/blog/19426

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

has_many :through時のdependent: :destroyの挙動

RailsのActive Recordで多対多の関連を使う時の話。ここではClubとStudentの関係を例とする。
普通に書くと、こうなると思う。

class Club < ApplicationRecord
  has_many :club_students, dependent: :destroy
  has_many :students, through: :club_students
end

class Student < ApplicationRecord
  has_many :club_students, dependent: :destroy
  has_many :clubs, through: :club_students
end

ただ、このようにも書ける。

class Club < ApplicationRecord
  has_many :club_students
  has_many :students, through: :club_students, dependent: :destroy
end

class Student < ApplicationRecord
  has_many :club_students
  has_many :clubs, through: :club_students, dependent: :destroy
end

この書き方だとClubを削除した時に関連するStudentも削除されるのではないか?と思われそうだが、throughを使った場合は中間レコードのみ削除されるので、結果的に前者と同様の挙動となる。

どちらで書くかは好みの問題だろうが、個人的には後者の方が多対多の関連を1行で明示できるので、最近はこちらを使うことが多い。ただ、毎度心配になるのと、ググっても情報が出てこないため、備忘録として残しておく。

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む

【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で続きを読む

【Rails】1つの検索フォームで複数カラムをまたいで検索する方法

検索フォームの実装は簡単

検索フォームは意外と簡単に実装出来ます。下記の記事など参考にすればすぐ出来ます。
railsで検索フォームを作ろう!!
Ruby on Rails 検索機能拡張 (railsチュートリアル)
【開発メモ】Railsアプリに検索機能を実装する方法をわかりやすくまとめてみた。

ここからが本題

上記の記事はいずれも1つのカラムに対して検索をかける方法です。
複数のカラムを1つの検索フォームだけで検索したい場合は下記のようにすれば出来ます。これも知っていれば簡単です。

適切なオブジェクト名.where(['検索したいカラム名 ? OR 検索したいカラム名 LIKE ? OR 検索したいカラム名 LIKE ?', "%#{search}%", "%#{search}%", "%#{search}%"])

検索したいカラムが3つある場合は、"%#{search}%"も3つつけましょう。そうすれば1つのフォームで複数カラムまたいで検索出来ます!

  • このエントリーをはてなブックマークに追加
  • 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で続きを読む