20201020のRailsに関する記事は29件です。

【Rails】ページネーションをAjax対応にする

1.はじめに

 現在作成中のWebアプリケーション(育児サイトーSUKUSUKUー)のホーム画面のページネーションについて、別ページに移動する際、いちいちリロードされて、ページ上部に移動されるのがストレスに感じました。なのでページネーションをAjax対応にして、使いやすくしたいなと考えました。
ezgif-7-71eb2ec20490.gif

  この記事でわかること
    ・Ajax通信の基本的な仕組み
    ・代表的なJavaScriptライブラリ
    ・RailsにおけるAjax
    ・ページネーションをAjax対応にする

2.そもそもAjaxとはなにか?

 Ajax(エイジャックス、アジャックス)とは、wikipediaによると、

ウェブブラウザ内で非同期通信を行いながらインターフェイスの構築を行うプログラミング手法である。
XMLHttpRequest(HTTP通信を行うためのJavaScript組み込みクラス)による非同期通信を利用し、通信結果に応じてダイナミックHTML (DHTML) で動的にページの一部を書き換えるというアプローチを取る。
AjaxはAsynchronous JavaScript + XML の略で、2005年2月18日に米国のインフォメーションアーキテクトであるJesse James Garrettにより名付けられた。

とのこと。もう少し詳しく説明していきます。

非同期通信とは?

 同期通信あっての非同期通信なので、まずは同期通信について説明します。同期通信では下記の図のようにユーザーインターフェイス上でなんらかの操作し、そのリクエストをWebサーバーへ送ります。そして、Webサーバーはそのレスポンスを受取り、データベース等で処理を行い、その結果をHTML,CSS等のレスポンスでブラウザ側へ送信します。ブラウザはリロードされ、結果が表示されます。

名称未設定ファイル.png

 同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザはリロードされていました。

r20imasara03_01.gif

 非同期通信では、JavaScriptを使用しウェブブラウザ内で動的な処理が行なえます。下記の図のようにユーザーインターフェイス上で何らかの処理を行った際に、直接サーバーへ通信を行うのではなく、ブラウザ内のAjaxエンジンへJavaScriptの呼び出しを行います。AjaxエンジンはレスポンスとしてHTML,CSSをユーザーインターフェイスへ送り、DOMがブラウザへ表示されます。この時点ではブラウザとサーバー同じ状態ではないため、AjaxエンジンよりWebサーバーへXMLHTTPリクエスト(XHR)を送ります。リクエストを受け取った結果をサーバー側はXML、JSONとしてレスポンスします。

非同期通信.png

 非同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザ側でリロードされずに動的な処理ができます。

ezgif-4-050dab99fd38.gif

つまり、

同期通信:サーバーの通信を待ってからブラウザがリロードされ動的な処理ができる。

非同期通信:サーバーの通信を待たずに、動的な処理ができる。その際ブラウザはリロードされない。

ということです。

XMLとは?

 Extensible Markup Languageの略。マークアップ言語の一つ。HTMLはWebページを表示するための言語に対して、データを交換するためや設定ファイルを書くときに使用する言語です。タグなどを自由に編集できるため、「拡張可能なマークアップ言語」とよばれます。

<?xml version="1.0" encoding="UTF-8" ?>
<食料品>
  <食料>
    <名前>リンゴ</名前>
    <色></色>
  </食料>
</食料品>

このようにタグは日本語でも記述可能です。

JSONとは?

 JavaScript Object Notationの略。 XMLと同様の共通データ定義言語です。人間にとって読み書きが容易で、マシンにとっても簡単にパースや生成を行なえる形式です。

参考にしました→JSONの紹介

DOMとは?

 Document Object Modelの略。HTMLやXML文書のためのプログラミングインターフェイスです。DOMはHTMLやXMLをツリー構造として展開します。AjaxではDOMを使用してユーザーインターフェイスの一部分だけ書き換えます。

参考にしました→DOMの紹介

3.Ajaxを使用するメリット、デメリット

メリット

 ・サーバーからのレスポンスを待たずに、高速に動的な処理ができる。

 ・受信するデータ量が減らせる。

 ・サーバーに負担がかからない。

デメリット

 ・開発の際、ブラウザがリロードされないため、エラーメッセージが画面に表示されない。エラーがどこに出てるか分かりづらい。

 ・クロスブラウザ対策1が必要

 ・バリデーションをJS,サーバーサイド両方で記載しないといけない。

主に、メリットはユーザー側にあり、デメリットは開発側にありそうですね。

4.JavaScriptのフレームワーク

 Ajaxを使用するためにはJavaScriptを学ぶ必要があります。JavaScriptを書きやすくするフレームワークがいくつかありますので、ここでは代表的なものを紹介します。

jQuery

 Ajax処理を簡単にするために最もよく利用されてきたフレームワーク。
 文法も簡単でシンプルなので覚えやすい。ブラウザの種類に依存せず、アニメーションから効果、非同期通信に関わる部分まで幅広く対応することができます。

Angular

 Googleと個人や企業のコミュニティによって開発されているフレームワーク。主に大規模なアプリケーションに向いている。
 独自の概念や機能が多く有るので、学習コストは高め。

React

 Facebookとコミュニティによって開発されているフレームワーク。
 UI部分のみのライブラリなので、他のフレームワークと併用してしようする。
 その代わり動作は早い。

Vue.js

 シンプル・軽量・高速。強力なコンポーネント機能により、データや処理を分けることができ、サーバーとの通信も高速で行うことができる。

5.RailsにおけるAjax

 Railsでは、JavaScriptをDOMに追加する際の手法を「UJS: Unobtrusive(控えめな)JavaScript」と呼んでるそうです。
 これはHTML上にJavaScriptを混入させると、DRY2の原則に反することが多くなる上に、煩雑になり保守がしづらくなります。そのためJavaScriptを正しく分離し表に出さない手法をRailsではとっています。Railsでは、こうした「最小化」と「連結」によって、あらゆるJavaScriptを実行できます。
 実際にリンクにJavaScriptを組み込むには、ビュー上で下記のようにremoteオプションをオンにします。

<%= link_to "記事", @article, remote: true %>

これでJavaScriptを組み込む事ができます。
 
参考→Rails で JavaScript を使用する

6.ページネーションをAjax対応にする

環境

  • MacBook Catalina ver.10.15.7

  • Rails 6.0.3

  • ruby 2.7.1

  • gem "kaminari"

  • jQuery

今回は学習コストを考慮しフレームワークはjQueryを使用することとしました。

RailsにjQueryを入れる

 まず、WebpackとYarnこの両方にjQueryを追加する必要があります。

ターミナル
$ yarn add jquery
config/webpack/environment.js
const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery'
  })
)

module.exports = environment

application.jsファイルでjQueryをrequireします。

app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")

これで、RailsでjQueryを使用できます。

ビュー(HTML)

 最初にビューをAjax対応できるようにします。

app/views/static_pages/home.html.erb
ー ページネーション部分だけ記載 ー
  .
  .
<div class="question-index">
  <%= paginate @questions, remote: true %>
    <%= render partial: 'questions/question', collection: @questions %>
  <%= paginate @questions, remote: true %>
</div>
  .
  .

pagenateremote: trueを記載します。
このhome.html.erbで生成されるページネーションのHTMLが下記です。

HTML
<ul class="pagination">
  <li class='active'>
    <a data-remote="true">1</a>
  </li>
  <li>
    <a rel="next" data-remote="true" href="/?page=2">2</a>
  </li>
  <li>
    <a rel="next" data-remote="true" href="/?page=2">&rsaquo;</a>
  </li>
  <li>
    <a data-remote="true" href="/?page=2">最後 &raquo;</a>
  </li>
</ul>

すべてのページネーションのリンクにdata-remote="true"が付きました。
この事により、JavaScriptによるフォーム操作を許可することをRailsに知らせることができます。


↑生成されたページネーション。

コントローラー

次にコントローラーでAjaxリクエストに応答できるようにします。

app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
    @questions = Question.page(params[:page]).per(10).all
    respond_to do |format|
      format.html
      format.js
    end
  end

end

respond_toメソッドはブロック内のコードの内いずれか一つがが実行されます。
普段はレスポンスとしてformat.htmlが呼び出されますが、今回ページネーションのリンクはdata-remote="true"としてremoteオプションがオンになっているため、format.jsがよびだされます。

ビュー(HTML)

最後にjQueryを使用して、Ajax対応させます。
その前にjQueryで呼び出しできるようhome.html.erbに記載されているコードをパーシャルに移します。

app/views/static_pages/home.html.erb(見直し)
<div id="home_pagenate">
  <%= render partial: 'index_page' %>
</div>

元あったhome.html.erbのコードをパーシャルへ貼り付けます。

app/views/static_pages/_index_page.html.erb
ー ページネーション部分だけ記載 ー
  .
  .
<div class="question-index">
  <%= paginate @questions, remote: true %>
    <%= render partial: 'questions/question', collection: @questions %>
  <%= paginate @questions, remote: true %>
</div>
  .
  .

JSファイルを作成し、jQueryのコードを記載します。

home.js.erb
 $('#home_pagenate').html("<%= escape_javascript(render 'index_page') %>");

これで、data-remote="true"の記載があるページネーションが実行された際に、コントローラーでJSファイルが呼び出され、JSファイルで元あったページがレンダリングされます。これでAjax対応は完成です。

完成形

ezgif-6-bfbd4116adbc.gif

もっと簡単なやり方がありそうですが、とりあえずうまく動きました!!

7.まとめ

 Rubyの勉強はそこそこやってましたが、JavaScriptはよく知らなかったのでまとめました。そこそこ学習コストは高い上に、初学者にとって保守が難しそうなので、必要な部分を見極めたいです。


  1. すべてのブラウザで動作する保証。 

  2. Don't Repeat Yourself「繰り返しを避けること」 

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

アセットパイプライン

Railsは、どのようなWebアプリケーションを開発おする際にも共通して作成する必要があるものを、自動的につくることができます。Webアプリケーションフレームワークのひとつです。

Webアプリケーションフレームワークは、みなさんが日常的に使っている「メールテンプレート文」とその役割が同じです。どちらも作業を効率化するために使う点では、同じことです。メールテンプレート文は、メールを新たに作成する都度使います。Webアプリケーションフレームワークは、Webアプリケーションを開発する都度使います。

Ruby on Railsにはアセットパイプラインという機能が実装されています。その役割をひと言で説明すると、「Web画面上の色塗りを楽にする機能」です。

比喩として”色塗り”と言っていますが、具体的にはWebブラウザに画面のデザインを認識させるためにHTMLに紐付くCSSやJavaScript、Imageファイルを記述・作成することです。

初学者は、Ruby on Railsのフレームワークの役割とディレクトリ構造を覚えることに苦心します。そのため、”Webブラウザ上に画面が表示される仕組み” については見落とされがちです。

インターネット上の情報は、webブラウザを介してみることがほとんど。そのwebブラウザ上に画面が表示されるのは、”webブラウザがHTMLとCSS、javascriptを認識する必要があることを、認識せずに Ruby on Rails の学習を進める初学者が多いです。

webブラウザでh表示されるものは、HTMLとCSS、javascriptが紐付くことによって、描かれていると認識する。この仕組みがアセットパイプラインによって実現されるようになっている。

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

【rails】partialをrenderする時(部分テンプレートを読み込む時)のcollectionについて

今回railsで部分テンプレートを呼び込む際、collectionを使うと、ローカル変数をより簡単に渡せることを学習したため、アウトプットいたします。

renderメソッドについてわからない方は検索してみてください。

collectionを使うと部分テンプレートにローカル変数を送る際にとても楽にできる

まずは前提としてanswerモデル、コントローラーがあります。

def index
@answers = Answer.all
end

これでそれぞれの人の答えが@answerに入りましたね。

ここでlocalを使って部分テンプレートに変数を送ると

<% @answers.each do |answer| %>
  <%= render partial: "answer", locals: { answer: answer } %>
<% end %>

となります。

ただ、上記では、@answersに10個情報が入っていたなら、1回づつrender particalが呼び出されてしまいます(合計10回)。
つまり速度が遅くなってしまうのです。

そこで簡単に部分テンプレートに変数を遅れるのがcollectionです。

collectionを使うと便利

<%= render partial: "answer", collection: @tweets %>

たったこれだけで部分テンプレートにローカル変数を送ることができます。
例え10個情報が@answersに入っていたとしても、部分テンプレートにまとめて渡されて、10個のビューが作成されます。

私も学習段階になるため、間違い等が発生する場合があります。
その際はお手数ですが、ご指摘いただけると幸いです。

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

with_optionsを使って、同じバリデーションを一括りにしちゃえ!

はじめに

 今のアプリケーション作成で使ったバリデーションについて、まとめておく。

共通のバリデーションをまとめる

with_options

よく使うバリデーションを例に

model
validates :user, presence: true
validates :item, presence: true
validates :price, presence: true
validates :email, presence: true

なんか同じことばかり繰り返していて、長くなり、可読性が…
そんなときは、with_optionsでまとめてバリデーションを設定できる。

model
with_options presence: true do
  validates :user
  validates :item
  validates :price
  validates :email
end

with_optionsをさらに発展的に使って

さらに個別にバリデーションを追加

model
with_options presence: true do
  validates :user, length: { minimum: 6 }
  validates :item
  validates :price, format: { with: /\A[0-9]+\z/ }
  validates :email
end

do~endの中に個別でバリデーションを付けられる。
ちなみに、lengthオプションは、文字数を限定できる。
formatオプションは正規表現についての指定。

さらにまとめる

model
with_options presence: true do
  validates :user
  validates :item
  with_options uniqueness: true do
    validates :price
    validates :email
  end
end

with_optionsの中にwith_optionsを入れ子にできる。
上の例だと、
presence: trueは:user, :item, :price, :emailに設定され、
さらに、uniqueness: trueは:price, :emailに設定されている。
ちなみに、uniquenessオプションは同じ値を保存できないようにする。(一意性)

最後に

 with_optionsでまとめて記述するのは、中学の数学で出てきた、因数分解の学習あたりの共通項で括る感覚に近い。

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

nil? empty? blank? present? の違い

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。
nil? empty? blank?の区別をはっきりさせたかったので書きました。

nil?

  • 「何も存在しない」状態です。器も中身も何もありません。
  • だからnilという言葉でしか表せないのですね。
name = nil 
name.nil? #=> true
  • 例えば、以下の例は全てnilにはならないので注意ですね。
array = [] #=> false
zero = 0 #=> false
name = ""  #=>false
hash = {} #=> false

empty?

  • 「器はあるけど中身がない」状態です。これはイメージしやすいですね。 空のお皿という状況でしょうか。
  • ただし、nilにempty?メソッドを使うとエラーが出てしまうので、よく見極めてから使いましょう。
array = [] 
array.empty? #=> true

animal = ""
animal.empty? #=> true

name = nil
name.empty? #=> false

blank?

  • nil?とempty?を合体させたようなメソッドです。
  • nilとemptyのどちらでもtrueを返すのですね。
array =[]
array.blank? #=> true

name = nil
name.nil? #=> true

present?

  • 「器があって、中身がある」状態です。 つまり、中身があればOKということですね。
age = 24
age.present? #=> true

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

route53 最新版 レコード作成画面

現在ポートフォリオ等のために 初めてRoute53を
触る人用の設定案内です。

あまり2020年10月現在の最新バージョンの
レコード作成画面を載せている人がいなくて とまどったので
誰かのために載せておきます。

レコード作成画面

これは単純にシンプルルーティングでいいと思います。

246d994571f2690802c0d45cce166745.png

次に シンプルなレコードを作成 の画面

レコード名は お名前.com 等でドメインをとっている人は空白でいいと思います。

fb0e53e6e793b794bad53cb0eaa83153.png

次に エンドポイントを選択 の部分は
 レコードタイプに応じたIPアドレスまたはその別の値 を選択。
そのしたの空欄には EC2で割り当てた elasticIPを入力してください。

3ca2f3095ea77fe1242359fa64c3d89a.png

その後のTTLはデフォルトの300で大丈夫です。

かなり簡単な内容ですが、AWSに慣れてない方は戸惑うかもしれないので
参考になればと思います(もちろん私も戸惑いました)3ca2f3095ea77fe1242359fa64c3d89a.png
3ca2f3095ea77fe1242359fa64c3d89a.png
3ca2f3095ea77fe1242359fa64c3d89a.png

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

EC2 デプロイ時にエラー

エラー内容

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

あと一歩でデプロイ完了というところでこのようなエラーが出てしまいました...

原因

おそらくEC2上のMariaDBが起動していないことが原因でした。
以下の方法で私は起動ができましたが、起動するコマンドを直接うったわけではないので、もしかしたら特例かもしれません。

なので、起動コマンドをうってもうまくいかない人
などは試してみてください。

解決方法

とりあえずMySQLがインストールされていることを確認するために以下のコマンドで確認しましょう

mysql -v

インストールされていたら以下のコマンドをうって
私の場合はMariaDBの設定をしたので
その設定を行った時のパスワードをうって入って下さい。

mysql -u root -p

パスワードを入力するとこのようになったら成功です。

[root@ip-172-31-46-255 ~]# mysql -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 7
Server version: 5.5.64-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> 

次に以下のコマンドでテーブル一覧があるか確認してみてください。テーブルが現れたら正しくできているということになります。

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| petter_production  |
+--------------------+
4 rows in set (0.00 sec)

テーブルが現れたら、次は、私の場合はpetter_production(自信のアプリにテーブル)に
テーブル一覧があるか確認します。

mysql -u root -D petter_production -p

MariaDB [petter_production]> show tables;
+-----------------------------+
| Tables_in_petter_production |
+-----------------------------+
| active_storage_attachments  |
| active_storage_blobs        |
| ar_internal_metadata        |
| comments                    |
| likes                       |
| pets                        |
| plans                       |
| relationships               |
| schema_migrations           |
| tweet_tag_relations         |
| tweet_tags                  |
| tweets                      |
| users                       |
+-----------------------------+
13 rows in set (0.00 sec)

ここまで確認したら、エラーが解決されました。
確認の一連でテーブル一覧が出てこなかったら設定がうまくいっていないので他のコマンドを探しみてください。

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

rails データベース作成から追加&保存

はじめに

一番基本的なツイッターアプリのような、CRUD(create, read, update, delete)ができるwebアプリを作る。

テーブルの作成、内容の反映

$ rails g model Post content:text
//rails g model テーブル名(単数形) カラム名:データ型(どの様なデータが入るか)

$ rails db:migrate
//マイグレーションファイルの内容を反映 = テーブル(DB)ができる

テーブル名は単数形にする決まりになっている。

$ rails console

> post = Post.new(content:"Hello World")
//contentがHello Worldの 「postインスタンス」を生成 

> post.save  

> posts = Post.all
> posts[0] //posts[0]が確認できる

postsコントローラーの中のアクションindexで、
テーブルからブログの内容を受け取る関数indexを作る。

class PostsController < ApplicationController
  def index
    @posts = Post.all
    # @posts = [
    #   "今日からRailsの勉強するよー!",
    #   "投稿一覧ページ作成中!",
    #   "今日はおやつを食べました!",
    #   "チョコレート食べたい",
    #   "今日は久々の晴れだった"
    # ]
  end
end

変数に@をつけると他のファイルからもアクセスできるので、@posts とする。htmlでこんな感じで、表示できる。

  <div class="container">
    <% @posts.each do |post| %>
        <div class="posts-index-item">
            <%=post.content %>
        </div>
    <% end %>

css を整えるとこんな感じになる。rails consoleから、追加できたー!!!!
スクリーンショット 2020-10-20 20.24.50.png

htmlをまとめる

どのページでも表示したいHTMLはapplication.html.erbに書いておく
ヘッダーとか。

簡単にリンクを作ってくれるメソッド

html内に<li></li>でこれを書いておく。

<%=link_to("TweetApp","/") %>

新規投稿画面を作ろう

id: 3の投稿を確認する

$ rails console

> post = Post.find_by(id: 3)
> post.content
> post.created_at #投稿時刻

ルーティングする

  # 投稿詳細ページのルーティング
  get "posts/:id" => "posts#show"

投稿フォームから送信する

form_tagメソッド

<%=form_tag(url)%>
<%end%>

redirect_to メソッド

  def create
    redirect_to("/posts/index")
  end
    <%=form_tag("/posts/create")%>
        <div class="form">
        <div class="form-body">
            <textarea name="content"></textarea>
            <input type="submit" value="投稿">
        </div>
        </div>
    <%end%>

paramsというハッシュテーブルに、content: (投稿内容)として保存されるので、
以下のようにnewしてsaveしてリダイレクトする

  # createアクションを追加
  def create
    @post = Post.new(content: params[:content]) 
    @post.save
    redirect_to("/posts/index")
  end

投稿の編集と削除

編集機能

rails console
> post = Post.find_by(id: 1)
> post.content = "apple" #上書き
> post.save

削除機能

rails console
> post = Post.find_by(id: 1)
> post.destroy
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分で作った.scssファイルはどのようにRailsに読み込まれるのか?

Railsはあまりにも過保護なので、だいたい勝手にやってくれている。そのせいで時々理解が困難になることがある。

単純なHTMLとCSSならわかりやすい。HTMLファイルの<head>タグの間に
<link rel="stylesheet" href="application.css">

このようにスタイルシートを呼び出す呪文を書く。直感的にもapplication.cssを参照していることが直感的にわかりやすい。

しかし、Railsの場合は少し違う。Railsが気を利かせすぎているので、記述しなくても読み込んでくれる。

これは便利機能なのだろう。しかし、初学者からしたらどのファイルがどこで読み込まれているのかわかりにくい。

今回はscssのスタイルシートは、どういうメカニズムでRailsに読み込まれているのかを解説する。

結論:app/asset/stylesheetにscssファイルをぶち込んでおけば万事解決

読み込み読み込み読み込み.jpg

application.html.erbは、application.cssを読み込んでいて、application.cssを読み込みさえすれば、asset/stylesheetの中の全てのscssファイルは読み込まれる。

その仕組みを解説する。

全てのページで共通するHTMLファイルをまとめているのが「application.html.erb」だ。こいつの

でCSSファイルを読み込んでいる。
<%= stylesheet_link_tag    'application', media: 'all','data-turbolinks-track':'reload' %>

(ちなみにstylesheet_link_tagヘルパーは、簡単にいうと読み込むCSSファイルを指定しているやつで、とほとんど同じだ。これはパスを頑張って指定したりしなくて済んで非常に楽なのだが、初学者からしたらわかりにくくなってありがた迷惑である。この手のRailsの便利なんだけど初学者殺しはたくさんある。)

次に読み込まれたスタイルシートの「application.css」を見ていこう。

application.cssについて

/*
 .
 .
 .
 *= require_tree .
 *= require_self
*/

と書かれている。この

 *= require_tree .
 *= require_self

が肝である。

これの意味は、
*= require_tree .が、
「app/assets/stylesheet/以下のcssファイルは全部読み込みまっせ」
って意味で、

*= require_self
「もちろん、俺(application.css)の内容も読み込んでや!」
って意味だ。

「俺を読み込んどけば、俺と俺以外の/stylesheet/ディレクトリ以下全部まとめて読み込めます」

ってことが書かれている。

ちなみに、こういうファイルを「マニフェストファイル」って言う。

先ほどの見せた図のような感じになる。
読み込み読み込み読み込み.jpg

このように「application.html.erb」が「application.css」を読み込んだら、他の「session.scss(例です)」をも読み込んだことになる。

application.html.erbには、「session.scssを読み込みますわ!」みたいな記述は書かれない。

<%= stylesheet_link_tag    'application', media: 'all','data-turbolinks-track':'reload' %>

この記述だけで、全て読み込んだことになる。
慣れたらありがたいけど、初学者的には直感的に受け入れ難い概念である。

補足1

上は初学者のイメージであり、実際のところは「application.css」が他のcssファイルやscssファイルをまとめて圧縮してくれているイメージだ。

application.cssを読み込んだら、他のsession.scssも読み込んでくれるというよりは、application.cssにsession.scssも含まれているイメージだ。

補足2

昔はpublicディレクトリにこのようなアセットは入れられていたものの、今ではassetに入れられている。

プロゲートで学習していた時に、publicから画像を表示するという学習があった。その時やけに厳し目にパスには"/"が必須だということを書かれていた。

画像のパスに"/"が必要なのはpublicから呼び出す時だ。逆にassetの時は"/"を付けない。この/の有無で呼び出しディレクトリが指定されている。

assetにおくことで、アセットパイプラインで圧縮されて表示されやすくなるようだ。

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

自分が作った.scssファイルはどのようにRailsに読み込まれるのか?

Railsはあまりにも過保護なので、だいたい勝手にやってくれている。そのせいで時々理解が困難になることがある。

単純なHTMLとCSSならわかりやすい。HTMLファイルの<head>タグの間に
<link rel="stylesheet" href="application.css">

このようにスタイルシートを呼び出す。HTMLとCSSを学習しただけならこれでOKだ。直感的にもわかりやすい。

しかし、Railsの場合は少し違う。Railsが気を利かせすぎているので、言われなくても読み込むのだ。

おそらく、これは便利機能なのだろう。しかし、初学者からしたらどのファイルがどこで読み込まれているのかわかりにくい。

今回はscssのスタイルシートは、どういうメカニズムでRailsに読み込まれているのかを解説する。

結論:app/asset/stylesheetにscssファイルをぶち込んでおけば万事解決

読み込み読み込み読み込み.jpg

application.html.erbは、application.cssを読み込んでいて、application.cssを読み込みさえすれば、asset/stylesheetの中の全てのscssファイルは読み込まれる。

その仕組みを解説する。

全てのページで共通するHTMLファイルをまとめているのが「application.html.erb」だ。こいつの

でCSSファイルを読み込んでいる。
<%= stylesheet_link_tag    'application', media: 'all','data-turbolinks-track':'reload' %>

(ちなみにstylesheet_link_tagヘルパーは、簡単にいうと読み込むCSSファイルを指定しているやつで、とほとんど同じだ。これはパスを頑張って指定したりしなくて済んで非常に楽なのだが、初学者からしたらわかりにくくなってありがた迷惑である。この手のRailsの便利なんだけど初学者殺しはたくさんある。)

次に読み込まれたスタイルシートの「application.css」を見ていこう。

application.cssについて

/*
 .
 .
 .
 *= require_tree .
 *= require_self
*/

と書かれている。この

 *= require_tree .
 *= require_self

が肝である。

これの意味は、
*= require_tree .が、
「app/assets/stylesheet/以下のcssファイルは全部読み込みまっせ」
って意味で、

*= require_self
「もちろん、俺(application.css)の内容も読み込んでや!」
って意味だ。

「俺を読み込んどけば、俺と俺以外の/stylesheet/ディレクトリ以下全部まとめて読み込めます」

ってことが書かれている。

ちなみに、こういうファイルを「マニフェストファイル」って言う。

先ほどの見せた図のような感じになる。
読み込み読み込み読み込み.jpg

このように「application.html.erb」が「application.css」を読み込んだら、他の「session.scss(例です)」をも読み込んだことになる。

application.html.erbには、「session.scssを読み込みますわ!」みたいな記述は書かれない。

<%= stylesheet_link_tag    'application', media: 'all','data-turbolinks-track':'reload' %>

この記述だけで、全て読み込んだことになる。
慣れたらありがたいけど、初学者的には直感的に受け入れ難い概念である。

補足

昔はpublicディレクトリにこのようなアセットは入れられていたものの、今ではassetに入れられている。

プロゲートで学習していた時に、publicから画像を表示するという学習があった。その時やけに厳し目にパスには"/"が必須だということを書かれていた。

画像のパスに"/"が必要なのはpublicから呼び出す時だ。逆にassetの時は"/"を付けない。この/の有無で呼び出しディレクトリが指定されている。

assetにおくことで、アセットパイプラインで圧縮されて表示されやすくなるようだ。

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

Ruby:アカウント編集機能

【概要】

1.結論

2.どのようにコーディングするか

3.開発環境

補足

1.結論

コントローラーにeditアクション・updateアクションをコーディングし、edit.html.erbに必要なヘルパーメソッドをコーディングする!

2.どのようにコーディングするか

※devise gemを使用しております。

config/roites.rb
Rails.application.routes.draw do
  devise_for :users
  root to:'XXXX#index'

  resources :users, only: [:edit, :update] #---❶
end

❶:usersコントローラーに編集と更新アクションのみを命令したいのでresourcesにonlyをコーディングしています。

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def edit #---❶
  end

  def update #---❶
    if current_user.update(user_params) #---❷
      redirect_to root_path #---❸
    else
      render :edit #---❸
    end
  end

  private

  def user_params #---❹
    params.require(:user).permit(:name, :email)
  end

end

❶:ルーティングでも設定したようにアカウント(今回の場合はnameとemail)を編集させたいので、編集と編集したあとの更新機能命令を受け取ってmodel,viewに命令したいのでdefを使ってアクションを定義しています。

❷:どのアカウントに対して更新させたいかが不明なので、現在ログインしているuserに対して行っています。current_userはdevise gemを導入することで使用できます。

❸:更新が成功すればトップページに、更新が失敗すれば編集画面に戻るようにしています。簡単に説明すると更新されあとはuserの情報を更新したいので(=元のインスタンス変数の値が上書きをしたい)redirect toです。失敗した場合は、userの誤った情報を更新されないまま編集画面にただ戻ればいいのでrenderを使用しています。

※render/redirect toの違いは下記URLにて自分の記事でも説明していますが、検索すればたくさん出てきます。詳細が知りたければご自分で検索されたり、下記を参照されてください!
render と redirect_to の使い分け基準

❹:ストロングパラメーターを設定し、意図しないuserの編集更新を防いだままデータを受け取ります。requireメソッドを使用し、userモデルから情報を持ってきています。さらに詳細にパラメーターを指定したいのであればカラム名をキー名にします。

params.require(:モデル名).permit(:カラム名(キー名になる), ・・・・ ) 

ヘルパーメソッドのform_withを使用して現在ログインしている人の情報を送れば簡単なアカウント編集機能は完了です。

app/views/users/edit.html.erb
<%= form_with model: current_user, local: true do |f|%>
 <%= f.text_field :name %>
 <%= f.email_field :email  %>
 <%= f.submit "更新"%>
<% end %>


3.開発環境

Ruby 2.6.5
Rails 6.0.3.3
MySQL 5.6.47
SequelPro 1.1.2
Visual Studio

補足

2.❹でmergeメソッドを使用すると、ハッシュを結合させることができ、情報を追加することができます。

params.require(:user).permit(:name, :email).merge(user_id: current_user.id)


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

ruby get.chomp.to_iが動かない、読み込まない

はじめに

ruby初心者、get.chomp.to_iが読み込んでくれない。

$ ruby index.rb class.rb
# index.rb
require "./class"

count = get.chomp.to_i
puts count

と実行すると、count = get.chomp.to_i としているcountに勝手に0が入ってしまう。

解決方法

実行方法が違った。require "/menu" と書いているので、
 

$ ruby index.rb

のみでOKでした。ちゃんと読み込んでくれた。

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

Ruby on Rails メールの自動送信機能 ①設定(gmail利用)

やる事

Googleアカウントで2段階認証用のパスワード取得する

手順

Googleのアカウント取得が必要になります。
gmailアカウントの2段階認証をオンにして、専用の認証パスワードを取得した上で設定を行いました。

①送信用に利用するGoogleのアカウントでブラウザを開き、右上にタブを開きアカウントを開きます。

②画面左のメニューから[セキュリティ]を選択し、セキュリティの設定画面を開きます。

③[Googleへのログイン]にある[2段階認証プロセス]を選択し、画面に沿って設定をします。
[使ってみる]⇨[パスワード]⇨[続行]⇨[携帯に確認連絡が来る]⇨[バックアップ方法を登録]⇨[携帯に届いたコードを入力]⇨[有効にする]
3.png

④画面を戻り[Googleへのログイン]に[アプリ パスワード]が表示されるので、画面に沿って設定をします。
4.png

⑤パスワードが発行される (16文字)
このパスワードをメールアカウントの認証として、利用します。

Railsのconfigファイル編集

メール送信設定はconfig/environment配下にある各環境ごとの設定ファイルに定義します。

【フォルダ構成】
 config
  ∟environments
   ∟development.rb ・・・ 開発環境
   ∟product.rb ・・・  本番環境

Rails.application.configure do
 #中略#
  config.action_mailer.smtp_settings = {
    address: 'smtp.gmail.com',
    domain: 'smtp.gmail.com',
    port: 587,
    user_name: Rails.application.credentials.gmail[:user_name],
    password: Rails.application.credentials.gmail[:password],
    authentication: 'login',
    enable_starttls_auto: true
  }
end

環境変数の設定

$ sudo EDITOR=vim rails credentials:edit

環境変数に下記を追加する。

gmail:
  user_name: test@gmail.com 
  #先ほど登録したGoogleアカウントのアドレスを入力
  password: aaaabbbbccccdddd
  #発行されたパスワードを記載する(16文字)

次回

Ruby on Railsでの実装を行っていきます。

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

docker-compose コマンド一覧(Rails用)

忘れないために書きます

docker起動

 #foregroundで起動 
 docker-compose up #

 #backgroundで起動
 docker-compose up -d

docker停止

 #foregroundで起動時
 Ctr + C

 #backgroundで起動時
 docker-compose stop 

Gemfile変更時

 docker-compose build

コントローラーを作成

docker-compose run web bundle exec rails g controller <コントローラー名> <アクション名>

モデルの作成

docker-compose run web bundle exec rails g model <モデル名> <カラム名:型>

テーブルの作成

docker-compose run web bundle exec rake db:migrate

全部消すとき

docker-compose down --rmi all --volumes

また新しいこと覚えたときに追加します。

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

【Docker】開発環境構築 Rails6 / Ruby2.7 / MySQL8

はじめに

DockerでRails6, MySQLにて環境構築をする際になかなかてこずってしまったので、備忘録もかねて記事に残します。

つまずいた部分として、
Rails5 とは異なり Rails6 ではモダンなフロントエンド開発を強力にサポートするWebpackをRuby on Railsで使うためのgemパッケージであるWebpackerの導入が必須となったことがある。

※Docker初心者であり各ファイルの記述についても理解できていない部分も多いです。

環境・バージョン

  • Macbook Air Catalina
  • Ruby 2.7
  • Rails 6
  • MySQL 8.0

環境構築完了までの手順

  1. プロジェクトのフォルダを用意
  2. 5つのファイルを用意
  3. 各ファイルを記述
  4. Rails プロジェクトの開始(rails new)
  5. Dockerイメージのビルド
  6. database.yml の編集
  7. DBを作成(db:create)
  8. コンテナを起動

実際に環境構築してみる

1. プロジェクトのフォルダを用意

任意の名前のフォルダを作成する(コマンドでなくてもOK)

mkdir dockerSampleApp 

2. 5つのファイルを用意

1.で作成したフォルダ内にファイルを作成する

  • Dockerfile
  • docker-compose.yml
  • Gemfile
  • Gemfile.lock
  • entrypoint.sh

3. 各ファイルを記述

Dockerfile

プロジェクトのフォルダ名に関係なくmyappの箇所はmyappのままでOKです。

Dockerfile
FROM ruby:2.7

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
    && apt-get update -qq \
    && apt-get install -y nodejs yarn \
    && mkdir /myapp

WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose.yml

docker-compose.yml
version: '3'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
    stdin_open: true
    tty: true
volumes:
  mysql-data:
    driver: local

Gemfile

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'

Gemfile.lock

Gemfile.lock
# このファイルには何も記述しない

entrypoint.sh

entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

4. Rails プロジェクトの開始(rails new)

コマンドを実行

$ docker-compose run web rails new . --force --no-deps --database=mysql --skip-test --webpacker --api

今回使用オプションについて

  • --force 既存のGemfileを上書きする
  • --no-deps リンクしたサービスを起動しない
  • --database=mysql データベースにMySQLを指定
  • --skip-test Minitest のインストールをスキップ(テストはRSpecを導入予定のため)
  • --webpacker webpacker をインストール(Rails6 では必須のパッケージ管理ツール)
  • --api APIだけ作りたいためAPIモードで実行。これにより不要なView・UI関連のライブラリがインストールされない。

※オプションについては適時カスタムして使用する

5. Dockerイメージのビルド

イメージビルドとは、各種依存ライブラリやミドルウェアをインストールしたり、自分のアプリケーションをインストール・設定したりすること。

コマンドを実行

$ docker-compose build

6. database.yml の編集

database.ymlの該当部分を修正。
docker-compose.ymlservicesMYSQL_ROOT_PASSWORDの設定に合わせて該当箇所を修正。

config/database.yml
# ~ 省略 ~

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password  # -> 空欄を password に修正
  host: db  # -> localhostから db に修正

# ~ 省略 ~

7. DBを作成(db:create)

コマンドを実行してDBを作成

$ docker-compose run web rails db:create

8. コンテナを起動

最後にコマンドを実行してコンテナを起動する

$ docker-compose up

少し時間がかかるかもですが、ターミナルにこんなログが流れればOK

web_1  | => Booting Puma
web_1  | => Rails 6.0.3.4 application starting in development 
web_1  | => Run `rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Version 4.3.6 (ruby 2.7.2-p137), codename: Mysterious Traveller
web_1  | * Min threads: 5, max threads: 5
web_1  | * Environment: development
web_1  | * Listening on tcp://0.0.0.0:3000
web_1  | Use Ctrl-C to stop

localhost:3000にアクセスすると無事ページが表示される。はず!!

最後に

なんとか環境構築ができました!
今後Dockerなどインフラ面についても学習して理解を深めて行きたいです!

参考資料

大変参考にさせていただきました!

ありがとうございました!!

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

ruby基本的な構文メモ

はじめに

ruby on railsを勉強することになったので、基本的な構文を自分がわかる程度にメモしておこうと思います。

rubyとは

rubyとは、

  • インタプリタ言語(<->コンパイラ言語)
  • 動的型付き言語(<->静的型付き言語)
  • オブジェクト指向(<->)

が特徴の言語。(他にもいろいろあると思いますが)

基本的な構文

出力

count = 10
hello = "Hello"

puts count #42
puts hello #"Hello"

puts "#{hello} #{42}tokyo"  #Hello 42tokyo

ハッシュと配列

# 配列

colors = [red, blue, green, pink, white]

# ハッシュ

# 以下三行全部同じ
exam1 = {"subject" => "math", "score" => 100}
exam1 = {:subject=> "math", :score => 100}
exam1 = {subject: "math", score: 100}

exams = [
    {subject: "math", score: 100},
    {subject: "english", score: 40},
    {subject: "japanese", score: 60},
    {subject: "science", score: 90}
]

for, while

for i in [0,1,2,3] do
    puts i
end

index = 0
while index <= 10 do
    puts index
end

each

colors = [red, blue, green, pink, white]

colors.each do |color|
    puts color
end

メソッド(関数)

def hello
    puts "Hello World"
end

def hello(num, name)
   puts "Hello #{num}#{name}"
end

#hello(42, tokyo) -> Hello 42tokyo

料理注文システム

require "./menu"

menu1 = Menu.new(name: "sushi", price: 1000)
menu2 = Menu.new(name: "apple", price: 120)
menu3 = Menu.new(name: "banana", price: 100)
menu4 = Menu.new(name: "lemon", price: 80)

menus = [menu1, menu2, menu3, menu4]

puts "=== this is menu ==="
index = 0
menus.each do |menu|
    puts "#{index} : #{menu.info}"
    index += 1
end
puts "===================="

puts "choose menu : "

order = gets.chomp.to_i

selected_menu = menus[order]

puts "selected menu : #{selected_menu.name}"

puts "how many?"

count = gets.chomp.to_i

puts "your check is #{selected_menu.get_total_price(count)}"
class Menu
    attr_accessor :name
    attr_accessor :price

    def initialize(name:, price:) #ここにインスタンスの変数が入る
      self.name = name
      self.price = price
    end

    def info
      return "#{self.name} #{self.price}円"
    end

    def get_total_price(count)
      total_price = self.price * count
      if count >= 3
        total_price -= 100
      end
      return total_price
    end
  end

=== this is menu ===
0 : sushi 1000円
1 : apple 120円
2 : banana 100円
3 : lemon 80円
====================
choose menu : 
3
selected menu : lemon
how many?
20
your check is 1500
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者向け]GoogleMapJavascript APIを用いたマップと検索ボックスの表示方法

googlemapAPIを使ってアプリ上にマップを追加する方法です。

基本的には公式ドキュメントをコピペに沿って記述すれば実現できますが、Javascript初心者の私は解読するのにやたらと時間がかかったので同じ様なことを始める方のために共有しておきます。

出来ること

・マップの表示
・検索ボックスの表示
・マップ、検索ボックスの装飾
ezgif.com-gif-maker.gif

少し手を加えれば以下のように
装飾することもできます。
ezgif.com-gif-maker (1).gif

手順

1.マップを配置したいhtmlファイルにidを記述
2.javascriptファイルにWebAPIに関する記述をする
3.CSSをいじってマップを装飾する

これだけ。

手順1:マップを配置したいhtmlファイルにidを記述

以下公式ドキュメントからの引用です。
ポイントはAPI呼び出し用scriptに libraries=places を記述すること。
これにより検索&検索位置へ画面を切り替えるライブラリを読み込めます。

sample.html
<!DOCTYPE html>
<html>
  <head>
    <title>Places Search Box</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    #「key=YOUR_API」の部分に取得したAPIキーを記述
    #今回は検索機能を追加するのでPlacesライブラリを使う為「libraries=places」を追記
    #「v=weekly」はマップ自体の更新頻度を表しています。何も記述しなければ四半期に一度の更新となり、この記述だと毎週更新で最新状態を保てます。なので別に記述しなくても大丈夫です。
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initAutocomplete&libraries=places&v=weekly"
      defer
    ></script>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script src="./index.js"></script>
  </head>
  <body>
  #ここで指定したidにjavascriptでマップを表示させます。
    <input
      id="pac-input"
      class="controls"
      type="text"
      placeholder="Search Box"
    />
    <div id="map"></div>
  </body>
</html>

この状態でブラウザを開くとただの検索ボックスだけが存在すると思います。
次へ行きましょう。

手順2:javascriptファイルにWebAPIに関する記述をする

以下も公式ドキュメントからの引用ですが、記述の出どころがどこなのか一つ一つ探していたら時間がかかりました。。

初心者向けに簡単にjsの記述について超簡単に説明しておくと
const ~~ はそこで「~~」という定数を定義しています。
function ~~ は関数です。
google.maps はマップそのものを指定するオブジェクトです。

index.js
function initAutocomplete() {
  //マップの初期設定です。
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: -33.8688, lng: 151.2195 },
    zoom: 13,
    mapTypeId: "roadmap",
  });
  const input = document.getElementById("pac-input");
  const searchBox = new google.maps.places.SearchBox(input);
 ////"SearchBoxクラス"はPlacesライブラリのメソッド。引数はinput(ドキュメント上ではinputFieldとある)。
 ////[https://developers.google.com/maps/documentation/javascript/reference/places-widget#SearchBox]

  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
  ////"ControlPosition"クラスはコントローラーの位置を定める。
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/ControlPosition/
  ////https://developers.google.com/maps/documentation/javascript/examples/control-positioning

  map.addListener("bounds_changed", () => {
    searchBox.setBounds(map.getBounds());
  });
  ////"bound_changed"イベントは(見えてる範囲の地図・ビューポートに変化があったときに発火)
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/bounds_changed/ 
  ////"getBounds"メソッドはビューポートの境界を取得。Mapクラスのメソッド。
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/getBounds/

  let markers = [];
  searchBox.addListener("places_changed", () => {
  ////"place_chaged"イベントはAutoCompleteクラスのイベント.
  ////https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete.place_changed

    const places = searchBox.getPlaces();
    ////"getPlaces"メソッドはクエリ(検索キーワード)を配列(PlaceResult)で返す。
    ////https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete.place_changed

    if (places.length == 0) {
      return;
    }
    // Clear out the old markers.
    markers.forEach((marker) => {
      //"forEach"メソッドは引数にある関数へ、Mapオブジェクトのキー/値を順に代入・関数の実行をする。
        //Mapオブジェクト:
          //https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map
      marker.setMap(null);
      ////setMapメソッドはMarker(Polyline,Circleなど)クラスのメソッド。Markerを指定した位置に配置する。引数nullにすると地図から取り除く。
    });
    markers = [];
    // For each place, get the icon, name and location.
    const bounds = new google.maps.LatLngBounds();
    ////"LatLngBounds"クラスは境界を作るインスンタンスを作成。引数は左下、右上の座標。
    ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/#:~:text=LatLngBounds%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AF%E5%A2%83%E7%95%8C(Bounding,%E4%BD%9C%E3%82%8B%E3%81%93%E3%81%A8%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82
    places.forEach((place) => {
      if (!place.geometry) {
        ////"geometry"はplaceライブラリのメソッド。

        console.log("Returned place contains no geometry");
        return;
      }
      const icon = {
        url: place.icon,
        ////"icon"はアイコンを表すオブジェクト。マーカーをオリジナル画像にしたいときなど。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Icon/
        size: new google.maps.Size(71, 71),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(17, 34),
        ////"Point"クラスはマーカーのラベルなどの位置を決めるインスタンスメソッド。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Point/

        scaledSize: new google.maps.Size(25, 25),
      };
      // Create a marker for each place.
      markers.push(
        new google.maps.Marker({
          map,
          icon,
          title: place.name,
          position: place.geometry.location,
        })
      );

      if (place.geometry.viewport) {
        ////viewport"メソッド
        // Only geocodes have viewport.
        bounds.union(place.geometry.viewport);
        ////"union"メソッドはLatLngBoundsクラスのメソッド。自身の境界に指定した境界を取り込んで合成する。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/union/
      } else {
        bounds.extend(place.geometry.location);
        ////"extend"メソッドはLatLngBoundsクラスのメソッド。自身の境界に新しく位置座標を追加する。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/extend/
      }
    });
    map.fitBounds(bounds);
    ////"fitBounds"メソッドはmapクラスのメソッド。指定した境界を見えやすい位置にビューポートを変更する。
    ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/fitBounds/#:~:text=Map.fitBounds()%E3%81%AFMap,%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%A6%E3%81%8F%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

  });
}
style.css
#map {
  height: 100%;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#description {
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
}

#infowindow-content .title {
  font-weight: bold;
}

#infowindow-content {
  display: none;
}

#map #infowindow-content {
  display: inline;
}

.pac-card {
  margin: 10px 10px 0 0;
  border-radius: 2px 0 0 2px;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  outline: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  background-color: #fff;
  font-family: Roboto;
}

#pac-container {
  padding-bottom: 12px;
  margin-right: 12px;
}

.pac-controls {
  display: inline-block;
  padding: 5px 11px;
}

.pac-controls label {
  font-family: Roboto;
  font-size: 13px;
  font-weight: 300;
}

#pac-input {
  background-color: #fff;
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
  margin-left: 12px;
  padding: 0 11px 0 13px;
  text-overflow: ellipsis;
  width: 400px;
}

#pac-input:focus {
  border-color: #4d90fe;
}

#title {
  color: #fff;
  background-color: #4d90fe;
  font-size: 25px;
  font-weight: 500;
  padding: 6px 12px;
}

#target {
  width: 345px;
}

これでマップと検索ボックスが表示されたと思います。

cssは自由にいじって自分好みにしてみましょう。

尚、同名の定数や関数を別に記述していると上手く動かないので
エラーの際にはチェックしてみてください。

手順3:CSSをいじってマップを装飾する

手順2のままだと検索ボックスが無粋な位置にいるので、ボックスの装飾を行います。

jsファイルの

  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

で検索ボックスの位置をTOP_LEFTに配置してしまっています。

javascriptはcss読み込み後に読み込ませるので、
cssの記述を上書きする形で指定されてしまいます。

この一行を削除して、後は好きなようにCSSでinputタグのidやクラスに対して装飾しましょう。

以上です。
上手く行かないときは公式ドキュメントに沿って記述してみてください。

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

【Rails】Rakeタスクを用いたステータス更新

本記事ではまず「公開」「下書き」の2種類の記事のステータスがあるブログアプリに「公開待ち」という新しいステータスを追加します。そして「公開待ち」のステータスで保存された記事の公開予定日時が現在の日時より過去になった場合に自動でバッチを回し「公開」ステータスへ更新する処理について記載します。

上記の更新を可能にするRakeタスクを作成し、合わせてwheneverを用いてその処理を走らせる方法について書いていきます。

そもそもRakeタスクとは?

RakeはRubyで処理内容を記載することができるシンプルなビルドツールです。
このRubyで書かれた処理内容をRakeタスクと呼び、Rakefileに定義することで実行できます。

Rakeタスクの作成と実装

$ rails generate task タスク名 とすることでファイルがlib/tasksに作成されます。

$ rails generate task status_task

article.rbと作成したファイルに実行したい処理を記入します。

article.rb
enum state: { draft: 0, published: 1, publish_wait: 2 }

scope :past_published, -> { where('published_at <= ?', Time.current) }

article.rbには元々「下書き」と「公開」の2種類のenumしか定義していなかったため「公開待ち」のpublish_waitを追記。
scopeでpast_publishedの範囲を現時刻より過去の公開日時になっているものと定義。

status_task.rake
namespace :status_task do
  desc '公開待ちの中で、公開日時が過去になっているものがあれば、ステータスを「公開」に変更されるようにする'
  task update_status_task: :environment do
    Article.publish_wait.past_published.find_each(&:published!)
  end
end

Article.publish_waitで「公開待ち」の記事を取得し、past_publishedの対象をpublished!でステータスを「公開」に更新します。
find_eachではデフォルトで1000件ずつレコードを取得して取得したデータを1件ずつ処理するので全てのデータを取得してから処理するfindと比較して処理の実行速度が落ちづらい。

wheneverの導入

次にこれまでに作成したRakeタスクを1時間毎に実行させるようにwheneverを導入します。
wheneverとは定期実行バッチの処理時間を管理するためのrailsに用意されたライブラリで、本来は「○○時になったら○○を実行」するcronというプロセスを自前で用意する必要があるのですが、wheneverを使えばcronに対して命令を行うcrontabに記述する内容をruby言語で書けるようになります。

これによりRakeタスクを1時間ごとに走らせるように設定することが可能です。

まずは導入方法から書きます。

gem 'whenever'

bundle installをし下記を実行するとcofig/schedule.rbというファイルが作成されます。

$bundle exec wheneverize .

このschedule.rbにRakeタスクを1時間毎に走らせる処理を書きます。

config/schedule.rb
# Rails.rootを使用するために必要
require File.expand_path(File.dirname(__FILE__) + "/environment")
# cronを実行する環境変数
rails_env = ENV['RAILS_ENV'] || :development
# cronを実行する環境変数をセット
set :environment, rails_env
# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"
# rakeタスクを1時間ごとに実行
every :hour do
  rake 'article_state:update_article_state'
end

公開日時を1時間ごとにしか設定できないようにする。

app/assets/javascripts/admin.js
format: 'YYYY-MM-DD HH:00'

cronのアップデート

schedule.rbに書いた設定を反映する前にまずcronを書き換えずに更新内容を確認しましょう。

$ bundle exec whenever

設定内容に問題がなければ下記でcrontabを更新しましょう。

$ bundle exec whenever --update-crontab 

下記コマンドでRakeタスクが問題なく実行できているかを確認。

$ rake status_task:update_status_task

以上終了です。

参照したページの一覧

【Ruby2.7.0リファレンスマニュアル/ライブラリ一覧/Rakeライブラリ】
https://docs.ruby-lang.org/ja/latest/library/rake.html

【Railsドキュメントfind_each】
https://railsdoc.com/page/find_each

【Railsで大量のデータをまとめて更新するならfind_each使うよね】
http://blog.livedoor.jp/sasata299/archives/51882704.html

【Rails whenever指定方法あれこれ】
https://taker.hatenablog.com/entry/2019/12/02/195950

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

Railsチュートリアル第4版:第1章 ゼロからデプロイまで

Railsチュートリアル第4版:第1章 ゼロからデプロイまで

1.1 はじめに

1.1.1 前提知識

  • 演習

    • 1. Ruby on Railsで使うRuby gemはどのWebサイトにありますか?
    • 2. 現時点でのRailsの最新バージョンはいくつですか?
    • 3. Ruby on Railsはこれまでに何回ダウンロードされたでしょうか?調べてみてください。

1.2 さっそく動かす

省略

1.3 最初のアプリケーション

Railsチュートリアルには下記とあった。

Terminal
$ cd                  # ホームディレクトリに移動する
$ mkdir environment     # 'environment' ディレクトリを作成する
$ cd environment/       # 'environment' ディレクトリに移動する
$ cd ~/environment
$ rails _5.1.6_ new hello_app

environmentというディレクトリは作成済みだったので、別名で作って、 rails newするときもバージョン指定しなかった。

image.png

Railsチュートリアルはクラウドの何かを使っているぽかったが、VS Codeでコーディング等やっていく。途中でRubyMineに切り替えるかもしれないが。
VS Codeを立ち上げて、ファイルを開くをして、hello_appを開く。

image.png

VS Codeのターミナル(TERMINAL)を開けば、アプリケーションのルートディレクトリにいるはず。

image.png

1.3.1 Bundler

Terminal
-> % bundle install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Using rake 13.0.1
Using concurrent-ruby 1.1.7
Using i18n 1.8.5
・
・
・
Using webpacker 4.3.0
Bundle complete! 17 Gemfile dependencies, 74 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

1.3.2 rails server

Terminal
-> % rails s  
=> Booting Puma
=> Rails 6.0.3.4 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.6 (ruby 2.7.1-p83), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop

http://localhost:3000/ を開けば、下図のようになっている。

image.png
  • 演習

    • 1. デフォルトのRailsページに表示されているものと比べて、今の自分のコンピュータにあるRubyのバージョンはいくつになっていますか? コマンドラインでruby -vを実行することで簡単に確認できます。
      • 2.7.1
    • 2. 同様にして、Railsのバージョンも調べてみましょう。調べたバージョンはリスト 1.1でインストールしたバージョンと一致しているでしょうか?
      • 6.0.3.4
        image.png

1.3.3 Model-View-Controller (MVC)

1.3.4 Hello, world!

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "hello, world!"
  end
end
config/routes.rb
Rails.application.routes.draw do
  root 'application#hello'
end
image.png
  • 演習

    • 1. helloアクションを書き換え、「hello, world!」の代わりに「hola, mundo!」と表示されるようにしてみましょう。
    • 2. Railsでは「非ASCII文字」もサポートされています。「¡Hola, mundo!」にはスペイン語特有の逆さ感嘆符「¡」が含まれています (図 1.17)19 。「¡」文字をMacで表示するには、Optionキーを押しながら1キーを押します。この文字をコピーして自分のエディタに貼り付ける方が早いかもしれません。
    • 3. リスト 1.7のhelloアクションを参考にして、2つ目のアクションgoodbyeを追加しましょう。このアクションは、「goodbye, world!」というテキストを表示します。リスト 1.9のルーティングを編集して、ルートルーティングの割り当て先をhelloアクションからgoodbyeアクションに変更します
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "hola, mundo!"
  end

  def goodbye
    render html: "goodbye, world!"
  end
end
config/routes.rb
Rails.application.routes.draw do
  root 'application#goodbye'
end
image.png

1.4 Gitによるバージョン管理

参考

1.4.1 インストールとセットアップ

省略

1.4.2 Gitのメリット

省略

1.4.3 Bitbucket

今回はGithubを使いました。

Terminal
-> % git init
Reinitialized existing Git repository in /Users/fukadashigeru/environment_2/hello_app/.git/

-> % git add -A

-> % git commit -m "Initialize repository"
[master (root-commit) 02fb3dd] Initialize repository
 92 files changed, 9246 insertions(+)
 create mode 100644 .browserslistrc
 create mode 100644 .gitignore
 create mode 100644 .ruby-version
 ・
 ・
 ・
 create mode 100644 yarn.lock

-> % git remote add origin https://github.com/**********/hello_app.git

-> % git push origin master
Enumerating objects: 107, done.
Counting objects: 100% (107/107), done.
Delta compression using up to 4 threads
Compressing objects: 100% (89/89), done.
Writing objects: 100% (107/107), 149.16 KiB | 3.47 MiB/s, done.
Total 107 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), done.
To https://github.com/**********/hello_app.git
 * [new branch]      master -> master

1.4.4 ブランチ、編集、コミット、マージ

Terminal
[~/environment_2/hello_app] [master]
-> % git checkout -b modify-README
Switched to a new branch 'modify-README'

*********************************************
README.mdファイルを修正
*********************************************

[~/environment_2/hello_app] [modify-README]
-> % git commit -a -m "Improve the README file"
[modify-README ba451c3] Improve the README file
 1 file changed, 4 insertions(+), 22 deletions(-)

[~/environment_2/hello_app] [modify-README]
-> % git checkout master
Switched to branch 'master'

[~/environment_2/hello_app] [master]
-> % git merge modify-README
Updating 02fb3dd..ba451c3
Fast-forward
 README.md | 26 ++++----------------------
 1 file changed, 4 insertions(+), 22 deletions(-)

[~/environment_2/hello_app] [master]
-> % git branch -d modify-README
Deleted branch modify-README (was ba451c3).

[~/environment_2/hello_app] [master]
-> % git push origin master

1.5 デプロイする

1.5.1 Herokuのセットアップ

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.4'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # development,test,productionの全体の設定部分にあったものを、developmentとtestのここに移動
  # Use sqlite3 as the database for Active Record
  gem 'sqlite3', '~> 1.4'
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

# producitionにpgを追加
group :production do
  gem 'pg'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Terminal
-> % heroku login --interactive
 ›   Warning: heroku update available from 7.35.1 to 7.46.0.
heroku: Enter your login credentials
Email [***********@gmail.com]: 
Password: ***********
Logged in as ***********@gmail.com

-> % heroku keys:add
 ›   Warning: heroku update available from 7.35.1 to 7.46.0.
Found an SSH public key at /Users/***********/.ssh/id_rsa.pub
? Would you like to upload it to Heroku? Yes
Uploading /Users/***********/.ssh/id_rsa.pub SSH key... done

-> % heroku create
 ›   Warning: heroku update available from 7.35.1 to 7.46.0.
Creating app... done, ⬢ thawing-brook-84469
https://thawing-brook-84469.herokuapp.com/ | https://git.heroku.com/thawing-brook-84469.git

1.5.2 Herokuにデプロイする (1)

Terminal
-> % git push heroku master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 332 bytes | 332.00 KiB/s, done.
・
・
・
remote: Verifying deploy... done.
To https://git.heroku.com/thawing-brook-84469.git
   48ef406..7fc2c4e  master -> master

1.5.3 Herokuにデプロイする (2)

https://thawing-brook-84469.herokuapp.com/

image.png

あれ、hellow worldになってない。どこかで手順ミスあったかも。まぁ、いいや。

  • 演習

 省略

1.5.4 Herokuコマンド

Terminal
-> % bin/rails c
Running via Spring preloader in process 5709
Loading development environment (Rails 6.0.3.4)
irb(main):001:0> ('a'..'z').to_a.shuffle[0..7].join
=> "cntwqkdr"
irb(main):002:0> exit

-> % heroku rename cntwqkdr            
Renaming ravudstc to cntwqkdr... done
https://cntwqkdr.herokuapp.com/ | https://git.heroku.com/cntwqkdr.git
Git remote heroku updated
 ▸    Don't forget to update git remotes for all other local checkouts of the app.

-> % heroku open
image.png
  • 演習
    • 1. heroku helpコマンドを実行し、Herokuコマンドの一覧を表示してみてください。Herokuアプリのログを表示するコマンドはどれですか?
Terminal
-> % heroku help
CLI to interact with Heroku

VERSION
  heroku/7.46.0 darwin-x64 node-v12.16.2

USAGE
  $ heroku [COMMAND]

COMMANDS
  access          manage user access to apps
  addons          tools and services for developing, extending, and operating your app
  apps            manage apps on Heroku
  auth            check 2fa status
  authorizations  OAuth authorizations
  autocomplete    display autocomplete installation instructions
  buildpacks      scripts used to compile apps
  certs           a topic for the ssl plugin
  ci              run an application test suite on Heroku
  clients         OAuth clients on the platform
  config          environment variables of apps
  container       Use containers to build and deploy Heroku apps
  domains         custom domains for apps
  drains          forward logs to syslog or HTTPS
  features        add/remove app features
  git             manage local git repository for app
  help            display help for heroku
  keys            add/remove account ssh keys
  labs            add/remove experimental features
  local           run Heroku app locally
  logs            display recent log output
  maintenance     enable/disable access to app
  members         manage organization members
  notifications   display notifications
  orgs            manage organizations
  pg              manage postgresql databases
  pipelines       manage pipelines
  plugins         list installed plugins
  ps              Client tools for Heroku Exec
  psql            open a psql shell to the database
  redis           manage heroku redis instances
  regions         list available regions for deployment
  releases        display the releases for an app
  reviewapps      manage reviewapps in pipelines
  run             run a one-off process inside a Heroku dyno
  sessions        OAuth sessions
  spaces          manage heroku private spaces
  status          status of the Heroku platform
  teams           manage teams
  update          update the Heroku CLI
  webhooks        list webhooks on an app
  • 演習
    • 2. 上の演習で見つけたコマンドを使って、Herokuアプリの最近のログ (log) を調べてみましょう。直近に発生したイベントは何でしたか? (このログを調べるコマンドを覚えておくと、本番環境の不具合を見つけるときに役立ちます)
Terminal
> % heroku logs
2020-10-19T13:29:19.529499+00:00 app[web.1]: => Run `rails server --help` for more startup options
2020-10-19T13:29:20.730825+00:00 app[web.1]: Puma starting in single mode...
2020-10-19T13:29:20.730870+00:00 app[web.1]: * Version 4.3.6 (ruby 2.7.1-p83), codename: Mysterious Traveller
2020-10-19T13:29:20.730871+00:00 app[web.1]: * Min threads: 5, max threads: 5
2020-10-19T13:29:20.730871+00:00 app[web.1]: * Environment: production
2020-10-19T13:29:20.731100+00:00 app[web.1]: * Listening on tcp://0.0.0.0:41511
2020-10-19T13:29:20.731446+00:00 app[web.1]: Use Ctrl-C to stop
2020-10-19T13:29:21.035771+00:00 heroku[web.1]: State changed from starting to up
・
・
・

1.6 最後に

省略

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

IPアドレスやEC2のアドレスでは入れるのに、独自ドメインでは「応答時間が長すぎます。」となってしまう現象の解決。

環境

Mac
EC2
ELB
Route53
ACM(https化)
Amazon Linux2
unicorn
Nginx
Capistrano

突然独自ドメインのサイトに入れなくなった!

発端

今まで普通に表示できていた開発中のhttps://<独自ドメイン>のサイトが突然表示されなくなり、応答時間が長すぎます。という画面で止まってしまう。

確認

まず原因がサーバーなのか、ドメインなのか、ブラウザなのかなど特定する必要があります。

①→サーバー(EC2)が起動しているか確認
AWS EC2のインスタンスに記載されているパブリック IPv4 アドレスパブリック IPv4 DNSで表示できるか試したところここでは表示されるので、サーバーやブラウザの問題ではなく、ドメインの通し方だけが問題だとわかる。

②→ターミナルで、% dig ○○.com(←独自ドメイン) あるいは % dig ns ○○.comを入れてANSERが返るか確認すると帰ってきているので、ドメイン発行元は問題ない。

③→HTTPSがネットワーク中で何かうまくいっていないところがあるかもしれないので、ACMで証明書を当てている(https通信の発生元)ところはロードバランサー(ELB)になるのでそこを確認、通信を制限するのはセキュリティグループのインバウンドルール→httpsのソースがマイIPになっている※もしくはカスタムでIPアドレス指定になっている
2ce6c750f6fa48f08ba3cdb75ecc8ca0.png

解決

ロードバランサーのセキュリティグループが自宅のIPアドレスになっていたため、wifi接続場所を移動するとサイトにはいれなかった。ソースを「任意の場所」と変更すると0.0.0.0/0となるので、これで接続先を制限することなく、サイトの表示が可能になるということでした。

参考
https://teratail.com/questions/220615

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

【Rails】 "rails routes"による行き先の決め方

"rails routes"こちらをターミナルに表示させたのはよいがどの様に使うのかイマイチ理解ができなかたった
のでノートに書いていることを纏めました。

rootメソッド

プログラミングにおいてMVCという概念は必要になります。
その際にクライアントからリクエストがあった際に、リクエストに対応した行き先を定義します。

image.png

今回は最初のrootメソッドの設定をしたいと思います。

sample.rb
Rails.application.routes.draw do
  root to: "posts#index"
end
ターミナル
rake routes(rails routesでも可)

Prefix Verb   URI Pattern    Controller#Action    

root   GET       /            posts#index                                                                           

rootの書き方は以下になります

root to: 'コントローラー名#アクション名'

解説

'コントローラー名#アクション名'
これはrails routesのController#Actionをそのまま引用。
更に""(ダブルコーテーション)を使って出来上がりです。

root to: "posts#index"
これにより、googleとかで検索してHPをクリックしたらトップページが表示される様になります。

【Rails】link_toメソッドで指定したviewファイルに飛ぶ方法

書き方

link_to.rb
= link_to prefix_path, HTTPメソッド名 クラス名  do
sample.rb
 link_to root_path do
 link_to "Our Blog", root_path, class: "header__title--text" do
 link_to "新規投稿", new_post_path, class: "header__right--btn" do

下はrails routesのターミナルによる結果

Prefix     Verb      URI Pattern      Controller#Action
  root     GET          /                posts#index
  posts    POST     /posts(.:format)     posts#create
  new_post GET     /posts/new(.:format)   posts#new

HTTPメソッドとは

GETはフォームを表示し,DELETEは削除するという意味がある。以下は参考して下さい。
場所はrails routesでVerbに記述している。
“スクリーンショット” 2020-10-20 15.55.07.jpg

但し、Prefixが同じ場合は2行目から省略される
“スクリーンショット” 2020-10-20 15.02.03.jpg

この場合はmethod: :HTTPメソッド名を指定しないとエラーが起きるので必ず記述すること。

sample.rb
<%= link_to '編集', tweet_path(@tweet.id), method: :get %>
<%= link_to '削除', "/tweets/#{@tweet.id}", method: :delete %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CSSを変更しても、サイズが変わらない設定方法

CSSを変更しても、サイズが変わらない設定方法

自分への備忘録として、たぶん忘れてしまいそうだから。

.cssのpaddingを変更したことにより、
大きさが変更されてしまった。固定したい場合の記述。

paddingやborderの設定変えても、
要素の大きさはwidthやheightで指定したサイズのまま変更しない設定方法

app/assets/stylesheets/application.css
* {
  box-sizing: border-box;
}

上記を忘れないよにすれば、大丈夫。

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

Rails 土台作り

個人アプリを作成する際に必要なことをプログラミングノートに纏めていたのでQiitaでも投稿してみようと思います。

OS macOS Catalina バージョン 10.15.7

1 ターミナルにて cd ~/作成したい場所(フォルダ名)
2"rails new"
3 "rails db:create"
4 "rails s"

今回は省力形を記載致します。

ターミナルにて 
貴方のPC名 ~ % cd ~/作成したい場所(フォルダ名)

rails _5.2.3_ new my_first_app -d mysql
※ macOS Catalina のため頭に"$"は不要です。

rails db:create

rails s

解説

"5.2.3"これはrailsのバージョンを指定しています。
my_first_app こちらは好きな名前を決めれますが
単語と単語の間には"_"(アンダーバー)
を入れて下さい。

"-d mysql" これはmysqlを作成して下さいと命令を出しています。
mysqlを作成しないとユーザー登録や投稿内容が保存されないので必ず作りましょう。

"rails db:create" こちらがDBにどの様な物を保存ができるかを指定できます。こちらも必ず必要となります。

最後に"rails s" これで自分のローカルホストに反映されるか確認しましょう。

image.png

反映が完了したら上記の写真が表示されるか確認しましょう。

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

rails joinの使い方

複数個のテーブルを結合する

①モデル(単数).joins(:②モデル(複数))

①はUser
Schedule

前提条件

  • Userは、idを持っている
  • ②モデルは、user_idを持っている

joinは、共通項で結合する

②モデルのテーブルのuser_idとUserのidがマッチしているレコードのみを取得しています。
結合条件にマッチしないレコードは削除されて1つのテーブルを構成します。

カラムの取得仕方

userのカラムだけ取得
User.joins(:schedules)
両方のカラムを取得
User.joins(:schedules).select("users.*, schedules.*")

補足

whereなどのメソッドを使う際

where(モデル名(複数形):{カラム:})

Userのモデルか?
あるいは、scheduleのモデルかを指定する必要がある

注意

selectで取得した値はすべて文字列に変更される。
僕の場合は、datetimeが文字列に変更したために、timeメソッドを使えなくなり、time.parseを使用し文字列型からdate型に変更した。

さいごに

初めての投稿です。分かりづらいところばかりですみませんでした。
少しでも誰かのお役に立てればと思います。

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

DockerでRailsの環境構築で出てきたエラーyaml.scanner.ScannerError: while scanning for the next tokenの対処法

Dockerでrails newした際に出てきたエラーです

yaml.scanner.ScannerError: while scanning for the next token

docker-compose.ymlファイルではタブを使って改行するとこのエラーが出るみたいです
空白部分をスペースで変えて実行したらできました。

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

【Ruby】injectメソッドを基本からしっかり

injectメソッド

  • Enumerable#inject
  • たたみ込み演算を行うメソッド

リファレンス:https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/inject.html

そもそもEnumerableとは?

module。繰り返しを行うクラスのためのmix-inである。お仲間(インスタンスメソッド)で有名なのは下記の通り。

  • map
  • find
  • select

リファレンス:https://docs.ruby-lang.org/ja/latest/class/Enumerable.html

RubyではEnumerableモジュールのおかげで便利な繰り返し処理を伴うメソッドを使うことができます。

基本的な使い方

ary = [1,2,3,4,5]
ary.inject(1){ |sum, n| sum + n }
# => 16
  • ブロック内の毎回の処理の戻り値がsumに入る。
  • メソッドの引数が初期値(最初の処理のブロック引数) に入る。
  • 最後の要素まで繰り返し、最後のブロックの戻り値が全体の戻り値になる。

、、、わかりにくい。

ではわかりやすくしましょう。

ary = [1,2,3,4,5]
ary.inject(1) do |sum, n| 
  puts sum
  sum + n 
end

=begin
=>1 (injectの引数に指定した初期値"1"がsumに入る)
=>2 (初期値(sum)+配列の最初の要素(n)の合計)
=>4 (前回の処理の戻り値(2)がsumに入る+二番目の要素の合計)
=>7 (以下略)
=>11
=>16(全体の戻り値)
=end

なんとなく、イメージできましたかね。これがたたみ込みですね。

ちなみにメソッドの引数(inject(hoge)hogeの部分)を省略した場合、配列の最初の要素が初期値に設定されます。

ary = [1,2,3,4,5]
ary.inject do |sum, n| 
  puts sum
  sum + n 
end

=begin
=>1 
=>3 
=>6 
=>10 
=>15(全体の戻り値)
=end

何が嬉しいのか

きちんと理解すれば、複雑な処理をわかりやすく書くことができます。ブロック内の処理をたたみ込みで繰り返すことができるので、eachメソッドよりも短く書くことができます。

つまり、繰り返し処理のリファクタリングによく使われます。

下記に実践的なコードを書いてみますね。

実際の使われ方

 配列の合計をだす

eachの場合

sum = 0
ary = [100,200,300,42,52]
ary.each { |n| sum += n }
puts sum
# =>694

injectの場合

ary = [100,200,300,42,52]
sum = ary.inject { |s, n| s += n }
puts sum
# =>694

一行減りましたね。スッキリ。

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

【Rails TMDB】APIで映画情報を取得しよう【保存版】

【Rails TMDB】APIで映画情報を取得しよう

なぜこの記事を僕が書くのか

端的に、Railsを用いてAPI経由で映画情報を取得できるようになる、参考記事がなかったから。
じゃあ僕が書こうというねっ

この記事のゴール

TMDBのAPIを用いて世界範囲で映画情報を取得し、そのデータを自分のオリジナルサービスで扱えるようになる。
スクリーンショット 2020-10-20 2.13.54.png

この記事で扱わないこと

・APIとは何か
・TMDBのAPIで、映画以外の情報を取得する方法

そもそもTMDBとは

スクリーンショット 2020-10-20 0.21.03.png
The Movie Database (TMDb) is a community built movie and TV database. Every piece of data has been added by our amazing community dating back to 2008.

映画データベース(TMDb)は、コミュニティで構築された映画およびTVデータベースです。すべてのデータは、2008年にさかのぼる私たちの素晴らしいコミュニティによって追加されました。

とのことです!コミュティ型で成長させてきたプロダクトのようですね
だからなのか、結構APIも容易に取得できました

TMDBに登録(サインアップ)& API取得

APIの登録の時に、プロダクトのURLが必要なので、Railsアプリはリリースしておいてくださいね(^○^)/
(厳密にいくと、別にリリースする必要はないのですが、リリースしておいた方がスムーズなので今回は、リリースしてる前提で進めていきます!)※リリースしたい方

この記事を参考に、TMDBに開発者用のAPIの使用許可を得ましょう!

themoviedb-apiというGemをインストール

こちらから、themoviedb-apiというGemの情報をご覧ください!

Provides a simple and intuitive interface for the Movie Database API making use of OpenStruct.

何やら直感的なインターフェイスを提供してくれるそう笑。
開発者の方、感謝します

OpenStructとは
→OpenStructは、要素を動的に追加・削除できる手軽な構造体を提供する、rubyの標準ライブラリ。

例としてはこんな感じ。

index.rb
require 'ostruct'
# 構造体を作って動的にpropertyを追加
son = OpenStruct.new
son.name = "Thomas"
son.age = 3

わかんない笑
一旦先に進みましょう
んじゃあ早速インストール

Gemfile
#(省略) 以下の1行をGemfileの最後に書きましょう!
gem 'themoviedb-api', '~> 1.3'
terminal
> bundle install --without production # 別にbundle installだけでも良いです

これで、TMDBを利用するために必要な、直感的なインターフェイスを提供してくれるGemはインストールできた。

自分のAPIを自分のアプリに記述する

自分の場合、movies_controllerを作成し、searchアクション、並びにshowアクションを用意しました。
モデルは、映画情報を引っ張ってくるだけの範囲ならば、こちらの方でモデルの作成は不要です。

terminal
> rails g controller movies search show
movies_controller
class MoviesController < ApplicationController
  require 'themoviedb-api'
  Tmdb::Api.key("ご自身のAPI Key")
  Tmdb::Api.language("ja") # こちらで映画情報の表示の際の言語設定を日本語にできます

  def search
  end

  def show
  end
end

一旦、この辺で、これらの変更を本番環境にあげましょうか

terminal
> git add .
> git commit -m "commit"
> git push heroku master #masterで打ってできなかったらmainで打ってみてくださいな

現時点で何ができるか

ここまでできると、自分のターミナル上で、映画情報が取得できます

terminal
> heroku run rails concole
#対話モードになる

> Tmdb::Search.movie("Harry") 
#Tmdb::Search.movie()で、()に書いた文字列で映画情報を検索できる。

上のコマンドでこのようなアウトプットが出ます

スクリーンショット 2020-10-20 0.54.23.png

先ほど書きましたが、このアウトプットはOpenStructというRubyライブラリによって、OpenStruct型で出力されていて、馴染みが僕にはなかったので、いったんjson形式に変換します。

to_jsonによって、実現しています。

(Tmdb::Search.movie("Harry")).to_json #まだターミナルに打たないでね

次に、JSON.parseを用いて、与えられた JSON 形式の文字列を Ruby オブジェクトに変換して返します。

JSON.parse((Tmdb::Search.movie('Harry')).to_json)

このコマンドでアウトプットは以下のようになります。
スクリーンショット 2020-10-20 1.26.56.png

分かりにくい笑
冒頭だけみますね
Before
スクリーンショット 2020-10-20 1.28.51.png

After
スクリーンショット 2020-10-20 1.29.32.png
分かりますでしょうか
データの対応関係がわかりやすくなってたでしょう

最後に、さらにこれをみやすくするために、
JSON.pretty_generateメソッドを使ってみやすくします

# 一旦moviedataという変数にアウトプットを格納
> moviedata = JSON.parse((Tmdb::Search.movie("Harry")).to_json)
# そして
> puts JSON.pretty_generate(moviedata)

これによってアウトプットが劇的に見易くなります泣

スクリーンショット 2020-10-20 1.37.06.png
データのまとまりが視覚的によくわかりますね。
最終的にはjson形式でデータが整形されましたね笑
自分的にはjson形式が一番分かりやすかったのでこうしました!
もっと効率的な方法があれば教えてくださいね笑

あとはViewにアウトプットを表示するだけ

さて終盤です。

ポイントはjson形式のデータの取り出し方ですね
この記事をご覧になればわかると思います?
例えば以下のアウトプットから、映画のタイトルの情報を取得したいならば、

スクリーンショット 2020-10-20 1.44.57.png

moviedata['table']['results'][0]['table']['title']

と書きます?
確かに取得できていますね?
スクリーンショット 2020-10-20 1.48.01.png

これを参考にViewを作っていきましょう!
仕様としては、
・検索バーに検索したい映画を入力すれば、目的の映画情報が取得、表示できる
・検索してない時(アプリに最初アクセスする時)には、人気映画を取得、表示する
というものです!

search.html.erb
<h1>映画検索</h1>

<div>
  <%= form_tag(root_path, method: :get) do %>
    <%= search_field_tag :looking_for, nil, placeholder: 'Movie Title...' %>
    <%= submit_tag '検索' %>
  <% end %>
</div>

<%# 検索された時にコンテンツを表示する %>
<%if params[:looking_for].present? %>
    <% movieinfo = JSON.parse((Tmdb::Search.movie(params[:looking_for])).to_json) %>
    <h2><%= params[:looking_for] %>の検索結果</h2>
    =====================
    <% i = 0 %>
    <div class="card-wrapper">
        <% while i < movieinfo['table']['results'].count %>
            <div class="card">
                <div class="card-upperinfo">
                    <%if movieinfo['table']['results'][i]['table']['title'].present?%>
                        <h3><%= link_to movieinfo['table']['results'][i]['table']['title'],detail_path(movieinfo['table']['results'][i]['table']['id']) %></h3>
                    <%end%>

                    <% if movieinfo['table']['results'][i]['table']['release_date'].present? %>
                        <p class="release-date"><%= movieinfo['table']['results'][i]['table']['release_date'] %></p>
                    <%end%>
                </div>


                <% if movieinfo['table']['results'][i]['table']['poster_path'].present? %>
                    <p><%= image_tag 'https://image.tmdb.org/t/p/w1280' + movieinfo['table']['results'][i]['table']['poster_path'],class: "card-img" %></p>
                <%end%>
            </div>
            <% i += 1%>
        <%end%>
    </div>
<%else%>
    <%# 検索されていない時にトレンドコンテンツを表示する %>
    <h2>本日の世界のトレンド</h2>
    ====================
    <% movieinfo = JSON.parse((Tmdb::Movie.popular).to_json) %>
    <% i = 0 %>
    <div class="card-wrapper">
        <% while i < movieinfo['table']['results'].count %>
            <div class="card">
                <div class="card-upperinfo">
                    <%if movieinfo['table']['results'][i]['table']['title'].present?%>
                        <h3><%= link_to movieinfo['table']['results'][i]['table']['title'], detail_path(movieinfo['table']['results'][i]['table']['id'])%></h3>
                    <%end%>

                    <% if movieinfo['table']['results'][i]['table']['release_date'].present? %>
                        <p class="release-date"><%= movieinfo['table']['results'][i]['table']['release_date'] %></p>
                    <%end%>
                </div>

                <% if movieinfo['table']['results'][i]['table']['poster_path'].present? %>
                    <p><%= image_tag 'https://image.tmdb.org/t/p/w1280' + movieinfo['table']['results'][i]['table']['poster_path'],class: "card-img" %></p>
                <%end%>
            </div>

            <% i += 1%>
        <%end%>
    </div>
<%end%>

CSSは、参考程度に記しておきますね

movie.scss
.card {
    // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.5);
    border-radius: 10px;
    width: 28%;
    margin-bottom: 100px;
    padding: 20px;
    box-sizing: border-box;
}

.card-wrapper {
    width: 80%;
    margin: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
}

.release-date {
    text-align: right;
}

.card-img {
    width: 100%;
    border-radius: 16px;
}

.card-upperinfo {
    height: 139px;
    overflow-y: scroll;
}

今回は、
・検索したい映画情報で映画情報を取得するのに、

Tmdb::Search.movie()

・世界のトレンド映画情報を取得する際に、

Tmdb::Movie.popular

を使いましたが他にも様々な魔法があるので、ぜひ以下よりご覧ください!
htemoviedb-api Githubレポジトリ

できなかったりエラーが出たりしたらいつでもご連絡ください?‍♂️
Twitter
Github

参考

OpenStructとは

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

[Rails] form_withでパラメーターを取得できないときの対処法

解決法

  • urlかモデルのみを記述する。(今回はurlのみ記述)

編集前

_form.html.erb
<%= form_with(model: @set, url: "/sets/create", local: true) do |f| %>
  <div class="form-title">
    <%= f.label :title, "タイトル" %>
    <%= f.text_field :title %>
  </div>
<% end %>

編集後

_form.html.erb
<%= form_with(url: "/sets/create", local: true) do |f| %>
  <div class="form-title">
    <%= f.label :title, "タイトル" %>
    <%= f.text_field :title %>
  </div>
<% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データベース (DB)からseedsファイルにdumpする方法

 そもそも、データベース(DB)からseedsファイルにdumpとは?
 私は初めdumpとはどういうことかわかりませんでした。。
 そして、以前なんとなくコマンドやコードを丸写ししていて、エラーやトラブルが起こった時に、どうしていいか分からなく先輩に助けてもらったことがあります。
 こういう時は、自分で考えられるように、何が起こっているのか、何が原因なのか考えられるように、コードが何をやっているのかちゃんと理解しておいた方がいいです。

dumpとは、要するにバックアップのことです。
データベースは様々なデータを保管しておく場所です。
そのデータは一度消してしまうと二度と元に戻せないような重要なものかもしれません。
トラブルが起こったときに与える影響の範囲が非常に大きく、深刻なものになりかねないので、データベースのdumpは重要なのです。

データベースに含まれているテーブルなどの情報を SQL 文の形で出力することをダンプと呼びます。
ダンプを行うとテーブルの構造やテーブルに格納されている 1 つ 1 つのデータについて SQL 文の形で出力されます。
どんな時に使うか?
・マスタデータ(都道府県、市区町村など)
・初期データ(ユーザアカウントなど)
などのデータを利用する時に使います。

 コマンドは以下の通りです。
bin/rake db:seed:dump MODELS=モデル名 FILE=db/seeds/ファイル名(~.rb)
 これはあるファイルのデータをseedsファイルに反映させる時のコマンドです。
 全てのレコードを空にしたい場合はrake db:resetを行います。
 これは全てのテーブルを dropし、"db/schema.rb"を元にテーブルの再作成を行います。

もう一つseedsファイルからDBに反映する方法は以下の通りです。
bundle exec rails r db/seeds/ファイル名(~.rb)

良かったら参考にしてみてください。

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