20200521のRubyに関する記事は26件です。

Rails5.2系+webpackerでログアウト機能を実装しようとしたらno route matches [get] "/logout"のエラーが出てしまい、postメソッドが上手く動作しなかった件について

はじめに

投稿系のアプリケーションを作成中、Rails5.2にwebpackerを導入してログアウト機能を実装しようとした際、no route matches [get] "/logout"エラーが出てdeleteリクエストを送信できなくなってしまったので、これを解決します。

環境

Ruby 2.6.5
Rails 5.2.4.2
webpacker 5.1.1

エラー発生地点

webpackerビルド後に、application.html.slimにwebpackerのスクリプトタグを読み込んだ瞬間から。

app/views/layouts/application.html.slim
head
    .
    .
    .
    - = javascript_include_tag 'application','data-turbolinks-track': 'reload'
    + = javascript_pack_tag 'application'
    .
    .
    .


解決策

1.まずはyarnのパッケージファイルにrails-ujsを追加します。そしてpackage.jsonにこれが追加されていることを確認します。

$ yarn add rails-ujs
package.json
{
  "name": ,
  "dependencies": {
    "@rails/webpacker": "5.1.1", 
    "rails-ujs": "^5.2.4-3",
  },
}


2.app/javascript/packsディレクトリ配下にrails-ujs.jsファイルを作成し、下記の記述を追加します。

app/javascript/packs/rails-ujs.js
import Rails from 'rails-ujs';
Rails.start();


3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。

app/javascript/packs/application.js
require("./rails-ujs.js");



4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。

app/views/layouts/application.html.slim
head
    .
    .
    .
    = javascript_pack_tag 'application'
    .
    .
    .

以上で、DELETEリクエストを受けてくれるようなったかと思います。

何故これで動作するのか

色々調べた結果、辻褄を合わせただけのため断言はできませんが、こうだろうみたいな予想を立ててみました。

もし間違っていたら、ご指摘して頂けるととてもありがたいです!

まず前提として、デフォルトの状態ではHTMLはGET,POSTのリクエストを送信することしかできません(なのでログインなどは通常通り行えます)

しかしRailsにてDELETE,PATCHなどのRESTful APIを発行する場合は、JavaScriptに処理を任せているためgem rails-ujsもしくはgem jquery-railsを用いています。

Rails5.1以降ではJavaScriptの統括ファイルにこのrails-ujsがデフォルトで読み込まれています(下記ファイルを参照)。

なので我々は特に気にしなくとも、ただRails側でmethod: :deleteなどのオプションを記述すれば、あとはJavaScriptが勝手に処理してくれるため、HTMLからDELETEリクエストを送信することができるようになっています。

ちなみにjquery-ujsはRails5.1以降サポートが廃止されています(Railsガイドより。)

app/assets/javascript/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .



しかしwebpackerを導入すると、デフォルトの状態ではrails-ujsがそもそも導入されていないので、サポートを受けることができません。

なのでrails-ujsを導入し、packs/application.jsファイルに新たにrails-ujs読み込みさせてあげなければ、RESTfulなAPIの処理を使用できないのは当然だったのかなと思いました。

参考

 http://docs.komagata.org/5456
 https://www.inodev.jp/entry/2019/12/03/234210

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

Rails5.2系にwebpackerを導入した途端元から実装してあったログアウト機能がno route matches [get] "/logout"エラーで動かなくなった

はじめに

投稿系のアプリケーションを作成中、Rails5.2にwebpackerを導入した途端、元から実装してあったログアウト機能が動作しなくなりました。
no route matches [get] "/logout"エラーが出てdeleteリクエストを送信できなくなってしまったので、これを解決します。

環境

Ruby 2.6.5
Rails 5.2.4.2
webpacker 5.1.1

エラー発生地点

webpackerビルド後に、application.html.slimにwebpackerのスクリプトタグを読み込んだ瞬間から。

app/views/layouts/application.html.slim
head
    .
    .
    .
    - = javascript_include_tag 'application','data-turbolinks-track': 'reload'
    + = javascript_pack_tag 'application'
    .
    .
    .


解決策

1.まずはyarnのパッケージファイルにrails-ujsを追加します。そしてpackage.jsonにこれが追加されていることを確認します。

$ yarn add rails-ujs
package.json
{
  "name": ,
  "dependencies": {
    "@rails/webpacker": "5.1.1", 
    "rails-ujs": "^5.2.4-3",
  },
}


2.app/javascript/packsディレクトリ配下にrails-ujs.jsファイルを作成し、下記の記述を追加します。

app/javascript/packs/rails-ujs.js
import Rails from 'rails-ujs';
Rails.start();


3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。

app/javascript/packs/application.js
require("./rails-ujs.js");



4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。

app/views/layouts/application.html.slim
head
    .
    .
    .
    = javascript_pack_tag 'application'
    .
    .
    .

以上で、DELETEリクエストを受けてくれるようなったかと思います。

何故これで動作するのか

色々調べた結果、辻褄を合わせただけのため断言はできませんが、こうだろうみたいな予想を立ててみました。

もし間違っていたら、ご指摘して頂けるととてもありがたいです!

まず前提として、デフォルトの状態ではHTMLはGET,POSTのリクエストを送信することしかできません(なのでログインなどは通常通り行えます)

しかしRailsにてDELETE,PATCHなどのRESTful APIを発行する場合は、JavaScriptに処理を任せているためgem rails-ujsもしくはgem jquery-railsを用いています。

Rails5.1以降ではJavaScriptの統括ファイルにこのrails-ujsがデフォルトで読み込まれています(下記ファイルを参照)。

なので我々は特に気にしなくとも、ただRails側でmethod: :deleteなどのオプションを記述すれば、あとはJavaScriptが勝手に処理してくれるため、HTMLからDELETEリクエストを送信することができるようになっています。

ちなみにjquery-ujsはRails5.1以降サポートが廃止されています(Railsガイドより。)

app/assets/javascript/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .



しかしwebpackerを導入すると、デフォルトの状態ではrails-ujsがそもそも導入されていないので、サポートを受けることができません。

なのでrails-ujsを導入し、packs/application.jsファイルに新たにrails-ujs読み込みさせてあげなければ、RESTfulなAPIの処理を使用できないのは当然だったのかなと思いました。

参考

 http://docs.komagata.org/5456
 https://www.inodev.jp/entry/2019/12/03/234210

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

個人アプリの制作日記3

今日の実装

viewの作成 tewwt #index

見た目を整え、教えてほしい問題の連絡記事を閲覧できるようにした。
7割は終わった。tewwt indexをroot_pathにあて、1ページですべての種類の記事を見られるようになった。1ページにまとめることでstu-suppo(アプリケーション)がどんなサイトなのかひと目で見られうようにするのが狙いだ。

users_controller #edit #updeta サインアウト

deviseにあるもとのカラム以外にname,image,introduction,subjectカラムを追加したのでfrom-forを用いでデーターベースに更新できるようにした。

苦戦したこと
railsのversionが5.0なのにform withメソッドを使うことを使おうとしていた。
後でわかったことだがform withメソッドはrailsのversionが5.0は非対応。

tewwt index で未実装なもの

SCSSでおしゃれなレイアウトにする
動画の埋め込み
インクリメンタルサーチ

まとめ

今日はだいぶ手間取ってしまったから、朝にしっかりタスクを分けて計画的進む必要あり。
また、睡眠時間をしっかり確保し体調を整える。

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

Railsアプリケーションにfullcalendarを導入

本投稿について

現在作成しているRailsアプリケーションに予定を書き込めるカレンダーを表示させるために、jQureryのFullCallendarを導入しました。カレンダーの表示方法を探している方の助けになればいいなと思います。

前提条件

ビューはhamlとSCSSで書いております。
Rails "5.0.7.2"を使用しています。

コマンド

scaffold で Eventモデルを作成しました。
FullCalenderで予定を書き込む場合、Eventというモデル名じゃないと機能しないらしいです。

$ rails g scaffold event title:string body:string start_date:datetime end_date:datetime
$ rails db:migrate RAILS_ENV=development

導入するGemFile

以下の3つを書いてbundle install

gem 'jquery-rails'
gem 'fullcalendar-rails'
gem 'momentjs-rails'

SCSSへの記入

application.scssに以下を記入

app/assets/stylesheets/application.scss
*= require_tree .
 *= require_self
 *= require fullcalendar
 */

JavaScript

コピーしてそのまま貼り付けてください。これによりカレンダーが日本語で表示されます。

app/assets/javascripts/application.js
//= require jquery
//= require moment
//= require fullcalendar

$(function () {
    // 画面遷移を検知
    $(document).on('turbolinks:load', function () {
        if ($('#calendar').length) {

            function Calendar() {
                return $('#calendar').fullCalendar({
                });
            }
            function clearCalendar() {
                $('#calendar').html('');
            }

            $(document).on('turbolinks:load', function () {
                Calendar();
            });
            $(document).on('turbolinks:before-cache', clearCalendar);

            //events: '/events.json', 以下に追加
            $('#calendar').fullCalendar({
                events: '/events.json',
                //カレンダー上部を年月で表示させる
                titleFormat: 'YYYY年 M月',
                //曜日を日本語表示
                dayNamesShort: ['', '', '', '', '', '', ''],
                //ボタンのレイアウト
                header: {
                    left: '',
                    center: 'title',
                    right: 'today prev,next'
                },
                //終了時刻がないイベントの表示間隔
                defaultTimedEventDuration: '03:00:00',
                buttonText: {
                    prev: '',
                    next: '',
                    prevYear: '前年',
                    nextYear: '翌年',
                    today: '今日',
                    month: '',
                    week: '',
                    day: ''
                },
                // Drag & Drop & Resize
                editable: true,
                //イベントの時間表示を24時間に
                timeFormat: "HH:mm",
                //イベントの色を変える
                eventColor: '#87cefa',
                //イベントの文字色を変える
                eventTextColor: '#000000',
                eventRender: function(event, element) {
                    element.css("font-size", "0.8em");
                    element.css("padding", "5px");
                }
            });
        }
    });
});

カレンダーの表示方法

"#calendar"だけで表示されます(HTMLで書く場合はidがcalendarのdiv要素を書いてください)。

app/views/events/index.html.haml
%p#notice= notice
%h1 Events
%table
  %thead
    %tr
      %th Title
      %th Body
      %th Start date
      %th End date
      %th{:colspan => "3"}
  %tbody
    - @events.each do |event|
      %tr
        %td= event.title
        %td= event.body
        %td= event.start_date
        %td= event.end_date
        %td= link_to 'Show', event
        %td= link_to 'Edit', edit_event_path(event)
        %td= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' }
%br/
= link_to 'New Event', new_event_path
//今回は既にあるコードの下に書きました。
#calendar

※注意点
turbolinksの機能をOFFにしてください。

app/views/layouts/application.html.haml
%body{"data-turbolinks" => "false"}
    = yield

ルーティングの設定

起動してすぐに確認できるようにルーティングを設定しておきます。

Rails.application.routes.draw do
  root "events#index"
  resources :events
end

"rails s"でアプリを起動すると以下のようにカレンダーが表示されるようになるはずです。

スクリーンショット 2020-05-21 20.35.06.png

New eventsをクリックして入力することでカレンダー上にイベントが表示されます。スクリーンショット 2020-05-21 20.43.11.png
カレンダー上のイベントをクリックするとイベントの詳細を確認できます。
スクリーンショット 2020-05-21 20.43.18.png

感想

jQureryのライブラリを利用することで、簡単にカレンダーを表示させることができました。
今後ともライブラリを積極的に使っていきたいものです。

参考文献

https://qiita.com/syukan3/items/68280ce4ff45aa336363

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

100日後に1人前になる新人エンジニア(1日目)

100日後に1人前になる新人エンジニア(1日目)

どうもこんばんは今日が1日目になります。
ちなみに昨日から始まっていますのでよかったらこちらからどうぞ
100日後に1人前になる新人エンジニア(0日目)

今日やったこと

本日より本格的に業務に取り組みました。
さっそくissueに取りかかり頑張るぞ...
と思ってましたが、

Dockerの使い方がわからん...
ということでまずそこで悪戦苦闘した前半。

その後Railsでフォームの改修に取り組みましたが...
プロジェクトのフォルダ多すぎて何が何だかわからんぞ:joy:

という始まり方でした。

少しずつ理解を深めていきアーキテクチャとかクラスごとの繋がりも分かってきたら
面白いんだろうなあって思っています。

Dockerあれこれ

今日はここからでした。いくつか使ったコマンドを

dockerコマンド
docker-compose build    #サービス(web,DB)のビルドを実行
docker-compose up       #コンテナを作成して、起動
docker-compose down     #コンテナを停止し、そのコンテナとネットワークを削除
docker-compose ps       #コンテナの一覧を表示 コンテナの状態がわかるよ

きっと他にもいっぱいあるので少しずつキャパを増やして行こう。

あれ..ファイルの変更時に情報が反映されないぞ...そんな時は

dockerコマンド
docker-compose build    #サービス(web,DB)のビルドを実行
docker-compose up -d    #デタッチド・モード:バックグラウンドでコンテナを実行し、新しいコンテナ名を表示

なるほどねー。これで大丈夫みたい。

なんで情報の変更を反映時はバックグラウンドで起動するんだろ?
あと変更した時にこれを行わないで画面リロードで良い時もあったけど何が違うんだ?
本日の疑問点です。明日までに解決します。

そしてコツコツとドキュメントを読んでいきます

--追記--
docker-compose.yml or ./docker/rails/Dockerfile に書かれている内容に変更があったら 再度buildする必要あり。疑問点は無事解決

Rails あれこれ

プロジェクトがとても大きい...
どんなroutesになってるのかこんがらがっちまったよ

・僕が知っていた方法...
 config/routes見れば全部書いてあるだろ!!
 って思ってたけど大変そうでした。よって以下の方法で確認

$ rails routes

他にも色々とあるようでして
Tipsを先輩社員の方からいただきました。

・localhost:3000/rails/info
 すげーroutes全部出てきたしちゃんと検索もできる!!

・docker-compose upから
ログが出力されているのでどのコントローラを使って
画面が遷移しているのかが確認できるよ

ありがたきアドバイスでした。

デバッグって

コードの途中にraiseを入れると...
アプリを立ち上げるとエラー画面になっているけど
その画面でコンソール?を打ち込むと各情報が得られました。すげー

例えばform controllerのコードの途中にraiseを入れておいて
アプリを立ち上げ→エラー画面からコンソールを入力
params と入力してみるとどんな値が送られているのかちゃんと確認できました。
明日から使います。ありがたや...:joy_cat:

他にもやり方は色々とあるはずなのでそこは確認してみます。

formに関してはまだ完成していないので続きは明日に回します。

今日のさいごに

今日が連載初日でした。
何事も積み重ねだと思うので一日ひとつづつ。

フィルヒースの言葉をいただきます。
Winning is habitualですね

1人前のエンジニアになるまであと99日

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

【Rails】Carrierwaveを導入する

タイトルと本文のみの投稿ができるブログアプリに
写真の投稿機能を追加した時の、Carrierwaveを導入する
手順に付いてまとめました。

1. Gemfileファイルを編集する

Gemfile
gem 'carrierwave'
gem 'mini_magick'
ターミナル
% bundle install

2. アップローダーを作成する

ターミナル
rails g uploader image

実行後、app/uploaders 下に image_uploader.rbが作成されます。

3. アップローダーをマウントする

app/models/message.rb
class Message < ApplicationRecord
  belongs_to :group
  belongs_to :user
  belongs_to :heven

  validates :content, presence: true, unless: :image?

  mount_uploader :image, ImageUploader  ⬅️ この1行
end

4. 画像のリサイズを可能にする

include CarrierWave::MiniMagick のコメントアウトを外し、
任意の行にprocess resize_to_fit: [800, 800]と追記。
これにより、縦横比を維持したまま、縦横を800px以内にリサイズ可能になります。

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

# 〜省略〜

  process resize_to_fit: [600, 600]  ⬅️ 追記

# 〜省略〜
end

以上で画像のアップロードの準備ができました。

ご覧いただきありがとうございました。

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

Ruby 技術者認定試験 Silver に合格しました

はじめに

2020-05-21 に Ruby技術者認定試験 Silver に合格しました。
20200521B.png
この記事が受験を考えている方の参考になれば幸いです。

勉強方法

勉強は次の3つを行いました。

  • 試験対策本
  • 模擬試験
  • AtCoder

試験対策本

試験対策本は次の本を使用しました。
Ruby技術者認定試験合格教本 Silver/Gold対応 Ruby公式資格教科書
購入:4月中旬

改訂2版も出ていますが、こちらもv2.1に対応していますので問題ないと思います。中古価格が安い
気になる方は正誤表のダウンロードをお勧めします。
サポートページ
私自身は、分からないところや気になったところを辞書的に引いたくらいなので、正誤表をダウンロードしましたが見比べなどはしなかったと思います。

使用頻度:中

模擬試験

模擬試験問題は次のページを使用しました。
+ REx - Ruby Examination
+ DIVE INTO EXAM
+ 模擬問題(Silver試験用)

上の2つは繰返し解くことができますので、学習効果が高いと思います。
20200521C.png
最初は、試験対策本を見ながら若しくはVScode等で実際にコードを実行しながらで構わないと思います。
Ruby AtCoder向けVSCode設定

問題を覚えすぎない程度の回数でいいと思います。

使用頻度:高

AtCoder

私自身は、繰返し実行して覚えるタイプの人間ですので、AtCoder さんの問題を解くことでかなり勉強させていただきました。
20200521D.png
9月末に Java でスタートし、2020年頃から Perl に切り替え、更に4月頃に もっとプログラマ脳を鍛える数学パズル を読んでから Ruby に切り替えと Ruby 歴は浅いです。
しかし、灰色diff問題を解くことで文法を覚え、茶色diff問題を解くことで String/Array/Hashクラス等のメソッドを覚えました。
AtCoder 茶diff 一覧

使用頻度:特高

まとめ

試験勉強に一番のお薦めは、AtCoder さんです。
AtCoder Problems さんを併用して活用することにより学習の進捗を確認しながら勉強を進めることができます。

但し、使用するクラス・メソッドの幅が狭くなる(例えば、key?を使用する人はinclude? has_key? member?を覚えない、ヒアドキュメントって何?美味しいの)と思われますので、模擬試験サイトや試験対策本で補う必要はあると思います。

また、Qiita に投稿されている先輩諸氏の記事も参考になりましたので、一読をお勧めします。

最後に、投稿した記事にコメントをいただいた皆様に感謝を申し上げます。

ruby.rb
begin
  print "Happy Programming\n".chomp
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 基礎

current_userメソッド

deviseでログイン機能を実装すると使えるようになるメソッド。
idが1のレコードの場合、User.find(1)と同じ意味を持ちます。
current_user.nameなどで値を取得できます。

ルーティングでユーザーごとに異なるページを表示するには普通とは違う記述方法を使います。

routes.rb
get 'users/:id' => 'users#show'

whereメソッド
Active Recordの1つ
モデル.where(条件)で引数にとった条件に一致したレコードのインスタンスを配列型で取得できます。
条件を連続で記述すれば複数の条件にあったレコードを取得できます。

Sample.where('id < 3').where('user_id: 1')

アソシエーション

モデル間の関係を定義することで、モデルを跨いだデータの呼び出しが簡単になります。

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

AtCoderでRuby学習9【第一回アルゴリズム実技検定 3番目】配列要素の並び替え

はじめに

Ruby学習の一環として「アルゴリズム実技検定」に挑戦します。
そのための学習の中で学んだことをアウトプットしていきます。
今回は「第一回アルゴリズム実技検定」の三問目(3番目)より。
第一回アルゴリズム実技検定 過去問

自分の解答と、解答する中で学んだ表現・メソッドなどを紹介していきます。

問題

6つの相異なる整数 A, B, C, D, E, Fが与えられる。
このうち 3番目に大きい数を調べるプログラムを作成せよ。

制約
・1≦A,B,C,D,E,F≦100
・A,B,C,D,E,Fは全て異なる
・入力中の値はすべて整数である。

入力は以下の形で与えられる。

A B C D E F

入力例
4 18 25 20 9 13
出力例
=> 13

解答

まずは僕が提出した解答から。

ary = gets.split(" ").map(&:to_i)
r_ary = ary.sort
print r_ary[3]

入力される文字列を整数として配列で受け取り、
sortメソッドを使って並び替え、3番目に大きな整数を出力しています。

ここからは今回初めて使用したsortメソッドについてまとめていきます。

sortメソッド

配列の内容をソート(並び替え)をします。
要素同士の比較は <=> 演算子を使って行い、sort はソートされた配列を生成して返します。

文字列の並び替え

文字列が入った配列に対してソートすると、「a,b,c...」や「あ、い、う…」といった順番に並び替えをしてくれます。

ary1 = [ "d", "a", "e", "c", "b" ]
p ary1.sort
#=> ["a", "b", "c", "d", "e"]

数字が文字列の要素として配列に入っている場合は、以下のような感じ。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort
#=> ["10", "11", "7", "8", "9"]                        

整数の並び替え

初めから整数の要素として配列に入っている場合は、解答のように簡潔に書けますが、
文字列として配列に入っている場合は、ブロック{}を使って以下のようにします。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort{|a, b| a.to_i <=> b.to_i }
#=> ["7", "8", "9", "10", "11"]

ただ、ブロックの中で比較を行うためにto_iメソッドを使って整数に変換しているのであって、
生成された配列では文字列に戻っているところに注意したいです。

これと同じことがsort_byメソッドでも行えます。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort_by{|a| a.to_i}
#=> ["7", "8", "9", "10", "11"]

最後に

以上、「第一回アルゴリズム実技検定」の三問目(3番目)を解く中で学んだソートについてまとめました。

もし間違いなどございましたら、ご指摘いただけると嬉しいです。

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

AtCoderでRuby学習9【第一回アルゴリズム実技検定 3番目 】配列要素の並び替え

はじめに

Ruby学習の一環として「アルゴリズム実技検定」に挑戦します。
そのための学習の中で学んだことをアウトプットしていきます。
今回は「第一回アルゴリズム実技検定」の三問目(3番目)より。
第一回アルゴリズム実技検定 過去問

自分の解答と、解答する中で学んだ表現・メソッドなどを紹介していきます。

問題

6つの相異なる整数 A, B, C, D, E, Fが与えられる。
このうち 3番目に大きい数を調べるプログラムを作成せよ。

制約
・1≦A,B,C,D,E,F≦100
・A,B,C,D,E,Fは全て異なる
・入力中の値はすべて整数である。

入力は以下の形で与えられる。

A B C D E F

入力例
4 18 25 20 9 13
出力例
=> 13

解答

まずは僕が提出した解答から。

ary = gets.split(" ").map(&:to_i)
r_ary = ary.sort
print r_ary[3]

入力される文字列を整数として配列で受け取り、
sortメソッドを使って並び替え、3番目に大きな整数を出力しています。

ここからは今回初めて使用したsortメソッドについてまとめていきます。

sortメソッド

配列の内容をソート(並び替え)をします。
要素同士の比較は <=> 演算子を使って行い、sort はソートされた配列を生成して返します。

文字列の並び替え

文字列が入った配列に対してソートすると、「a,b,c...」や「あ、い、う…」といった順番に並び替えをしてくれます。

ary1 = [ "d", "a", "e", "c", "b" ]
p ary1.sort
#=> ["a", "b", "c", "d", "e"]

数字が文字列の要素として配列に入っている場合は、以下のような感じ。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort
#=> ["10", "11", "7", "8", "9"]                        

整数の並び替え

初めから整数の要素として配列に入っている場合は、解答のように簡潔に書けますが、
文字列として配列に入っている場合は、ブロック{}を使って以下のようにします。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort{|a, b| a.to_i <=> b.to_i }
#=> ["7", "8", "9", "10", "11"]

ただ、ブロックの中で比較を行うためにto_iメソッドを使って整数に変換しているのであって、
生成された配列では文字列に戻っているところに注意したいです。

これと同じことがsort_byメソッドでも行えます。

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort_by{|a| a.to_i}
#=> ["7", "8", "9", "10", "11"]

最後に

以上、「第一回アルゴリズム実技検定」の三問目(3番目)を解く中で学んだソートについてまとめました。

もし間違いなどございましたら、ご指摘いただけると嬉しいです。

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

Rails5でherokuにデプロイ後にWebサイトの画像が表示されない問題の解決方法

自作のWebサービスで画像がHerokuにデプロイ後に読み込まれなかった問題を解決したので方法を紹介していきます。

環境

・Rails 5.2.2

・Ruby 2.5.3

・Amazon Web Service

・Windows 10

問題点

Herokuにデプロイ後にWebサイトの画像が読み込まれないこと

実現したい事

Webページの画像を表示したい

手順

config/environmentsの中にあるproduction.rbファイルを開く

production.rbファイルのコードを修正する。

修正前

production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

修正後

production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = true

修正後のproduction.rbの全体のコード

production.rb
Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
  # config.require_master_key = true

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

  # Compress JavaScripts and CSS.
  config.assets.js_compressor = :uglifier
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = true

  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.action_controller.asset_host = 'http://assets.example.com'

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

  # Store uploaded files on the local file system (see config/storage.yml for options)
  config.active_storage.service = :local

  # Mount Action Cable outside main process or domain
  # config.action_cable.mount_path = nil
  # config.action_cable.url = 'wss://example.com/cable'
  # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  # config.force_ssl = true

  # Use the lowest log level to ensure availability of diagnostic information
  # when problems arise.
  config.log_level = :debug

  # Prepend all log lines with the following tags.
  config.log_tags = [ :request_id ]

  # Use a different cache store in production.
  # config.cache_store = :mem_cache_store

  # Use a real queuing backend for Active Job (and separate queues per environment)
  # config.active_job.queue_adapter     = :resque
  # config.active_job.queue_name_prefix = "scheduleapplication_#{Rails.env}"

  config.action_mailer.perform_caching = false

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new

  # Use a different logger for distributed setups.
  # require 'syslog/logger'
  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')

  if ENV["RAILS_LOG_TO_STDOUT"].present?
    logger           = ActiveSupport::Logger.new(STDOUT)
    logger.formatter = config.log_formatter
    config.logger    = ActiveSupport::TaggedLogging.new(logger)
  end

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false
end

参考サイト・記事

アセットパイプライン Railsガイド Version6.0
Rails4 asset pipeline関連設定まとめ

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

Rubyで「角度」や「座標」を扱う際に必要な、最低限の知識 + Aさんから見てB君はどの方向にいる?のアルゴリズム

先日Rubyで座標や角度を扱う機会があったので、それらを扱う上で必要となる全知識をコンパクトにして、丁寧にまとめてみました。
後半には例として「Aさんから見て、B君はどの方角にいるか」みたいなのを8方位単位で調べられる簡単なコードを載せています。参考になれば幸いです。(バージョン:ruby 2.5.1)

su.png

角度には、よく使われる2つの単位がある

長さを表す単位にも「メートル」や「フィート」等、規格の異なる表し方があるように、角度にも複数の表し方があります。私達が角度を表す際、一般的によく使うのは90°(90度)みたいな感じですよね。しかしRubyが用意しているモジュール上では、ラジアン(rad)という単位が用いられています

度( ° )の考え方

この単位は、1周を360°としています。なので、例えば「右(or左)に360°回転」すると、元と全く同じ方向に戻ることが出来ます。

ラジアン( rad )の考え方

この単位は、1周を2×π rad としています。なので、例えば「右(or左)に2×π rad 回転」すると、元と全く同じ方向に戻ることが出来ます。(π = 円周率 ≒ 3.14)

私のように学生の頃の記憶を失ってしまった方のために申し上げると、πというのは、円の直径に対して円周の長さがどれくらいあるか?の比率であり、その比率は約3.14倍と決まっています。円の直径が1cmだとしたら、その円の円周は 1×π cm 、つまり約3.14cmになります。
π(円周率)の詳細についてはコチラ

よって

以下のように度(°)をラジアン(rad)に変換することが出来ます。

= 0 × π = 0.00000… rad
45° = (2 × π) ÷ 8 = 0.78539… rad (45°は、"2×π"の8等分)
90° = (2 × π) ÷ 4 = 1.57079… rad (90°は、"2×π"の4等分)
180° = (2 × π) ÷ 2 = 3.14159… rad (180°は、"2×π"の2等分)
270° = (2 × π) ÷ 4 × 3 = 4.71238… rad (270°は、"2×π"の3/4)
360° = (2 × π) = 6.28318… rad (360°は、"2×π")

少々回りくどい説明になってしまいましたが、180(°) = π (rad) という基準を抑えておけば覚えやすいですね。 

Rubyの世界では π = 「 Math::PI 」

ここでひとつRubyの必須知識を抑えましょう。π(≒3.14)をコード上で表現するには、「Math」というモジュールの助けを借りる必要があります。
モジュールを呼び出す方法は、コード上部にてinclude Mathを表記します。モジュールを呼び出しておけば、PIと表記するだけで、πを表現することが出来ます。

include Math

puts PI
# 出力結果::  3.141592653589793

ファイル全体ではなく単発でモジュールを呼び出したいときは、以下の方法で呼び出します(include Math不要バージョン)。

puts Math::PI
# 出力結果::  3.141592653589793

参考… 以下リンクでも、度(°)⇔ラジアン(rad)の相互変換方法について紹介されてます。
「ラジアンと度の変換をするRubyコード」by @niwasawa さん

座標から角度を求める「 Math::atan2 」の使い方

location.png

上記のようにx軸とy軸が用意されている中で、座標(xとyの値)が与えられていれば、前述のMathモジュールが用意している別の関数atan2を利用して、角度(rad)を求めることができます。

include Math

def radian(x, y)
  return atan2(y, x)
  # 引数xと引数yの順序が逆なことに注意
end

puts radian(5, 3)
# 出力結果::   0.5404195002705842

これだとあまり馴染みのないラジアン(rad)による出力結果なので、以下では度(°)に変換すべく「÷ π × 180」をメソッド内に追記し、最後に四捨五入もしてみます。

include Math

def degree(x, y)
  return atan2(y, x)/PI*180
end

puts degree(5, 3).round
# 出力結果::   31

無事に、31度という角度を求めることが出来ました。

Aさんから見て、B君はどの方角にいるか?

a_b.png

上の図のようにAさんの座標とB君の座標を与えられた場合に、8方位上どの方角にいるかをアンサー出来るよう、コードを作ってみます。ついでに、2人の間の距離も図ってみたいと思います。(距離の単位: m とします)

仕様

Aさんの座標(x, y)と、B君の座標(x, y)を入力

Aさんから見てB君がいる方角と、距離を出力する

入力
Aさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Aさんの座標は? ◯ ◯ ←ココに入力
B君の座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
B君の座標は? ◯ ◯ ←ココに入力

出力
B君はAさんから見て◆◆の方向にいて、▲▲m離れています。
例外処理
二人は同じ場所にいます。

ポイント1 Aさんを起点(中心)にして考える

以下イメージのように、"Aさんから見て"どの方向にいるのかを調べる為には、Aさんを起点(中心)にして考えます。
slide.mov.gif
この場合だとAさんを中心に持っていく為にはAさんをx方向に-3m、y方向に+2m移動させればOKなので、同じくB君にも同距離を移動してもらいます。

A(3, -2) xを-3、yを+2する → A(0, 0) = 中心にこれました!
同様に…
B(-2, -4) xを-3、yを+2する → B(-5, -2) この座標の方角をMath::atan2で調べればOK!

ポイント2 Math::atan2の返り値の範囲

今回は全方位を調べます。atan2の活用によって、0°〜360°の間の値が得られることを期待しましたが、実はatan2が返してくる値の範囲は -π 〜 π (度数でいうと-180°〜180°)と決まっています。時計の針でいう「3時〜9時の間」の場合だと、マイナスの値が帰ってきちゃう…ということですね。

この仕様に合わせてアルゴリズムを組んでも問題は無いのですが、勉強の為、得られた角度値をこちらの都合の良い値に変換する方法をとってみたいと思います。
参考:「Rubyで角度を(0〜359度の)正の角度に変換する」by @masassiez さん

# -180 〜 180 の間にある値を、 0 〜 360 の間の値に変換する方法(-120を例にした場合)

(-120).modulo(360)
# もしくは
-120 % 360

# これにより、240 に変換される

上記の%moduloも、やっていることは全く同じです。360で割った際の剰余(あまり)を求めることで、自動的に 0〜360 の範囲内に変換されることになります。(ちなみにmoduloは、include Mathしなくても使えます)

ポイント3 距離は三平方の定理を用いる

2点間A(x, y)とB(x, y)の距離を求める方法は、三平方の定理を使いましょう。

AB= \sqrt{(x_b−x_a)^2+(y_b−y_a)^2}

これをRubyでやると、以下みたいになります。

def distance(a_x, a_y, b_x, b_y)
  return ((b_x - a_x)**2 + (b_y - a_y)**2)**(1/2.0)
end

#  **2 ← 2乗しています
#  **(1/2.0) ← ルート(平方根)しています

完成コード

sampl.rb(完成)
# 今回は atan2 や PI を使いたいので、最初に include Math を宣言します
include Math

# 角度を求めるメソッド
def degree(x, y)
  (atan2(y, x)/PI*180)%360
end

# 方角を求めるメソッド
def compass(degree)
  if degree > 15 && degree < 75
     "北東"
  elsif degree >= 75 && degree <= 105
     "北"
  elsif degree > 105 && degree < 165
     "北西"
  elsif degree >= 165 && degree <= 195
     "西"
  elsif degree > 195 && degree < 255
     "南西"
  elsif degree >= 255 && degree <= 285
     "南"
  elsif degree > 285 && degree < 345
     "南東"
  else
     "東"
  end
end

# 2点間の距離を求めるメソッド
def distance(a_x, a_y, b_x, b_y)
   ((b_x - a_x)**2 + (b_y - a_y)**2)**(1/2.0)
end


puts "Aさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください"
print "Aさんの座標は? "
a_x, a_y = gets.split.map(&:to_i)

puts "Bさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください"
print "Bさんの座標は? "
b_x, b_y = gets.split.map(&:to_i)

if a_x == b_x && a_y == b_y
  puts "二人は同じ場所にいます。"
  exit
end

direction = compass(degree(b_x - a_x, b_y - a_y))
distance = distance(a_x, a_y, b_x, b_y).round(1)

puts "B君はAさんから見て#{direction}の方向にいて、#{distance}m離れています。"
# 2メートル以内は、密です
distance > 2 ? (puts "ソーシャルディスタンスが保たれています。") : (puts "密です!")

出力結果

Aさん(3,-2)、B君(-2,-4)の場合
Aさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Aさんの座標は? 3 -2
Bさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Bさんの座標は? -2 -4

↓ 結果 ↓

B君はAさんから見て南西の方向にいて、5.4m離れています。
ソーシャルディスタンスが保たれています。
Aさん(-1,-7)、B君(1,-7)の場合
Aさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Aさんの座標は? -1 -7
Bさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Bさんの座標は? 1 -7

↓ 結果 ↓

B君はAさんから見て東の方向にいて、2.0m離れています。
密です!
Aさん(4,3)、B君(4,3)の場合
Aさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Aさんの座標は? 4 3
Bさんの座標(x, y)を、半角数字で2つ、間に半角スペースを入れて入力してください
Bさんの座標は? 4 3

↓ 結果 ↓

二人は同じ場所にいます。

以上、「Rubyで「角度」や「座標」を扱う際に必要な、最低限の知識 + Aさんから見てB君はどの方向にいる?のアルゴリズム」でした。ご覧いただき、ありがとうございました!

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

RSpecとFactoryBotを利用してRailsアプリの単体テストを実施してみた

概要

ポートフォリオとして作成した個人開発アプリのテストを実施したので
個人的なアウトプットとしてテストの流れを振り返りたいと思います。
今回はモデルのバリデーションに関するテストの基礎的な内容になります。

前提

プログラミングにおけるテストとは、「プログラムが意図した通りに動くことを確かめる」ことを指します。

環境

Ruby 2.5.1
Rails 5.2.3

gem 'devise'を使用してユーザーモデル作成済み

はじめに

まずはテストに使用するGemをインストールしていきます。

今回使用するGem

  • rspec-rails ▶︎RSpecというテストに特化した言語のRails用gem
  • factory_bot ▶︎簡単にダミーのインスタンスを作成できるgem
Gemfile
group :development, :test do
 省略

  gem 'factory_bot_rails'
  gem 'rspec-rails'
end
ターミナル
$ bundle install

これでgemのインストールが完了。

RSpecの設定をする

まずはRSpec用のファイルを生成する必要があるので下記のコマンドを実行する。

ターミナル
$ rails g rspec:install

これで下記のファイルが生成される。

ターミナル
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb

.rspecに以下の記述を追加する

.rspec
--format documentation



ここまでくれば下記のコマンドでテストは実行できます。

ターミナル
$ bundle exec rspec

モデルクラスのテストコードを書く

いよいよテストコードを書いていくわけですが
テストコードを書くspecファイルは途中で生成されたspecという名前のディレクトリに配置します。
また、モデルのspecファイルはモデル用のディレクトリにまとめておくため、テキストエディタでディレクトリとファイルを作成していきましょう。

  1. specディレクトリの配下にmodelsというディレクトリを作成する。
  2. spec/modelsディレクトリの中にモデル用specファイルを作成する。

※specファイルの命名規則はspecファイルは、対応するクラス名_spec.rbという名前になります。

今回はUserモデルのバリデーションに関するテストを実施するという想定で進めていくのでuser_spec.rbを作成します。

まずは新規ユーザー登録時の各バリデーションが適用されるか、一つずつテストしていきます。
今回、Userモデルのバリデーションは以下の条件です。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  validates :nickname, :email, :password, :password_confirmation, presence: true
end

validatesのpresence: trueが適用されている4カラムをテストしていきます。



まずは基本となる記述でnicknameのバリデーションをテストします。

spec/models/user_spec.rb
require 'rails_helper'
describe User do
  describe '#create' do
    it "nicknameがない場合は登録できないこと" do
     user = User.new(nickname: "", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567")
     user.valid?
     expect(user.errors[:nickname]).to include("can't be blank")
    end
  end
end

1行目はテストする上で読み込むファイルを記載。
2行目はモデルクラスを記載。
3行目はテストするアクション名を記載。

4~8行目のitからendの括りが一つあたりのテストコードになります。
4行目はテストする内容を記載。
5行目はインスタンスの作成を記載するのですが、nicknameが空という事以外はリアルな想定で記載。
6行目は作成したインスタンスに大してvalid?メソッドを記載すると、ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存ができない状態であるか」を確かめることができます。
7行目は6行目の内容に対して予想されるテスト結果を記載します。
valid?メソッドの返り値はtrue or falseですが、valid?メソッドを利用したインスタンスに対してerrorsメソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができます。
今回の場合、to以下のincludeマッチャを利用して「"can't be blank"というエラーメッセージが出るだろう」という予想をします。

ザックリ日本語で言うと
「ユーザーのニックネームが空だから、"空にはできませんよ"というエラーが返ってくるだろうと予想を書く」
といった感じです

一旦ここでテストを実行してみましょう

ターミナル
$ bundle exec rspec

1 example, 0 failures

上の表示のように、"1つのテストに対して失敗が0"というメッセージが表示されればOKです。

あとは他のバリデーションもテストしていくのですが、
最初に導入したfactory_botを使用することでuserインスタンス作成部分のテストコードを共通化できるので、かなり便利です。

factory_botを使用する

まずはspecディレクトリ配下にfactoriesというディレクトリを作成しましょう。
その中にusers.rbというファイルを作成します。
※ファイルの命名はモデル名複数形で統一

そして空のファイルにモデルのインスタンス作成で共通となる部分を記述していきます。

spec/factories/users.rb
FactoryBot.define do

  factory :user do
    nickname              {"田中太郎"}
    email                 {"test-account@gmail.com"}
    password              {"1234567"}
    password_confirmation {"1234567"}
  end

end

特に説明することもありませんが
テストコードでユーザーモデルのインスタンスを作成すると記載した内容で毎回実行してくれます。

#これが
user = User.new(nickname: "田中太郎", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567")
#これで実現できる
user = FactoryBot.build(:user)

なんとさらにこのコードを簡略化できます。
user_spec.rbの中に'rails_helper'を読み込む記述がしてありますが、
このrails_helper.rbファイルの中に少し手を加えます。

spec/rails_helper.rb
#省略
RSpec.configure do |config|
  #下記の記述を追加
  config.include FactoryBot::Syntax::Methods

  #省略

end

これで準備は完了です。
そうするとですね...。

#これが
user = User.new(nickname: "田中太郎", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567")
#これで実現できる
user = FactoryBot.build(:user)
#さらに省略できる
user = build(:user)

かなりコード量が減りますね...。

自分は最初これを知ったとき、全部のカラムに値を入れてるけど、バリデーションのテストする上で空のカラムとか再現できるの?
という疑問がありました➡︎問題なくできます

残りのテストコードで見ていきましょう。

spec/models/user_spec.rb
require 'rails_helper'

describe User do
  describe '#create' do
    it " nicknameがない場合は登録できないこと" do
      user = build(:user, nickname: "")
      user.valid?
      expect(user.errors[:nickname]).to include("can't be blank")
    end

    it "emailがない場合は登録できないこと" do
      user = build(:user, email: "")
      user.valid?
      expect(user.errors[:email]).to include("can't be blank")
    end

    it "passwordがない場合は登録できないこと" do
      user = build(:user, password: "")
      user.valid?
      expect(user.errors[:password]).to include("can't be blank")
    end

    it "passwordが存在してもpassword_confirmationがない場合は登録できないこと" do
      user = build(:user, password_confirmation: "")
      user.valid?
      expect(user.errors[:password_confirmation]).to include("doesn't match Password")
    end

    it " passwordが5文字以下であれば登録できないこと " do
      user = build(:user, password: "00000", password_confirmation: "00000")
      user.valid?
      expect(user.errors[:password]).to include("is too short (minimum is 6 characters)")
    end

    #登録ができる場合のテストも実施
    it "nicknameとemail、passwordとpassword_confirmationが存在すれば登録できること" do
      user = build(:user)
      expect(user).to be_valid
    end

    it " passwordが6文字以上であれば登録できること " do
      user = build(:user, password: "000000", password_confirmation: "000000")
      user.valid?
      expect(user).to be_valid
    end
  end
end

上記の解説です

user = build(:user)
user = build(:user, nickname: "")

2行目のように再度カラム名と値を指定することで
事前にセットした値を上書きできます。
この例の場合はnickname: "田中一郎" をnickname: "" に上書きしています。
こうすることで事前にセットした値を柔軟に変えることができます(nilでも可)

it "passwordが存在してもpassword_confirmationがない場合は登録できないこと" do
      user = build(:user, password_confirmation: "")
      user.valid?
      expect(user.errors[:password_confirmation]).to include("doesn't match Password")
    end

また、上記のincludeマッチャ部分ですが
"can't be blank"もそうだけど
"doesn't match Password"っていうエラーメッセージは
どこから来たの?
それっぽいことを書こうとは思うんだけど
最初からわからないとエラーメッセージの予想とかできなくね...?

そう思っていた時期が私にもありました(注:まだ初学者です)



これらのエラーメッセージはRailsのGemで元々用意されているモノですが
極論、これでもテストは実行できます

.to include("")

そうすると、テストの実行結果がターミナルに表示されるわけですが
あなたは、""というエラーメッセージが出ると予想していたけど実際出たエラーメッセージは"doesn't match Password"だったわよ!
と返ってきます。


そうすると、
ああ!そうそう!ワイがテストで書きたかったのはそういうことなんや!
と、わかるわけですね

コンソールを開いて流れを検証して確認するということもできますが
初学者レベルで個人開発アプリのテスト量が少ない場合はこの方法で十分な気がします。
(怒られそう)

また、最後の2つのテストはユーザー登録ができる場合のテストをしているわけですが
be_validマッチャというのが出てきます。
これは"全てのバリデーションをクリアするだろう"という場合に使用します。

最後にテストを実行してみて

ターミナル
$ bundle exec rspec

7 example, 0 failures

テストの数に対して失敗が0ならOKです。

まとめ

RSpecとFactoryBotを利用して、Railsの簡単なモデルテストを振り返ってみました。
今回実施したテストはほんの一部ですが、基本的な流れと小ネタを書いたので学習初めてまもない方の一助となりましたら幸いです。


また、記載している内容で不備等ございましたら教えていただけると助かります。
最後に私の学習中に参考にさせていただいたQiita記事を記載しておきます。
ありがとうございました。

参考記事

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

【学習記録】Rubyで現在時間を取得して、時間毎に違う挨拶文を出力してみた。

はじめに

ProgateでRubyの学習を始めて約1か月。インプット過多になり始めていたので、
初めてテーマを決めてプチ成果物を作ってみました。
記事投稿練習を兼ねて投稿します!
所要時間は3時間ほどかかりました。。

簡単な流れとしては、
現在時間を取得 → 出力を変えたい時間をグループ分け → if文で挨拶文を出力
といった感じです。

time = Time.now

まず現在時間を取得します。

require "time"
# morning = m
    m1 = Time.parse("6:00:00")
    m2 = Time.parse("10:59:59")
# afternoon = a
    a1 = Time.parse("11:00:00")
    a2 = Time.parse("16:59:59")
# evening = e
    e1 = Time.parse("17:00:00")
    e2 = Time.parse("1:59:59")

#上記以外の時間帯は(2:00:00 ~ 5:59:59) zzz...で出力

if文で現在時刻と指定時刻を比較したいので、朝・昼・夜でそれぞれ時間指定をします。
この時、Time.parse("時:分:秒")とすることで、ただの文字列からTimeオブジェクトに変換しています。
詳しくは下記に参考文献を載せていますのでご覧ください。

if m1 <= time && time <= m2
    puts "おはようございます"
elsif a1= time && time <= a2
    puts "こんにちは"
elsif time <= e2 || e1 <= time 
    puts "こんばんは"
else
    puts "Zzz..."

グループ分けした指定時刻と、現在時刻を比較して挨拶文を出力します。
少しわかりにくいですが、

朝(6:00:00)~(10:59:59)の時間帯→ おはようございます
昼(11:00:00)~(16:59:59)の時間帯→ こんにちは
夜(17:00:00)~(1:59:59)の時間帯→ こんばんは
その他(2:00:00 ~ 5:59:59)の時間帯→ zzz...

と出力されます。

感想(主につまづいたところ)

  • 現在の"時間"のみの取得
  • 指定時刻を文字列から変換
  • if文の作成(時間軸の関係で最後のelsifだけ"または"になる)

参考文献

Ruby入門 14. 日付と時刻を扱う(全パターン網羅)

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

Ruby on rails を初心者向けに解説④ ~命名規則とform_Tagの使い方について~

はじめに

今回は前回の記事の続きになります。

よろしければ以前の記事も御覧ください。

Ruby on rails を初心者向けに解説①
Ruby on rails を初心者向けに解説② ~リンクの作成~
Ruby on rails を初心者向けに解説③ ~データベースの作成~

Railsの命名規則について

ここまで、コントローラーやモデルを作成してきました。

それらには、命名規則があります。学んでいきましょう。

Modelの命名規則

モデルとは、データベースを作成するための設計図です。

設計図は一つであるため、モデルクラス名は単数形で作成します。

テーブルは、そのモデルのデータが複数あるため、自動的に複数形で表されます。

前回の記事でuserという名前でモデルを作成しました。

そうすると、usersという名前でテーブルが作成されます。

また、クラス名はUserというふうに、最初が大文字になります。

Viewの命名規則

Viewフォルダは配下に複数のファイルを持つため、複数形になります。

Controllerの命名規則

Controllerは複数のアクションを持つため、複数形で作成します。

データベースにデータを登録

以前の記事では、railsのコンソールを使用してデータベースにデータを登録しました。

今回は、ブラウザをユーザーに操作してもらうことでデータベースにデータを登録しましょう。

その前に、一度データベースの確認を行います。

ターミナルに以下のコマンドを打ち込みましょう。

rails dbconsole

次のコードでカラム名の表示をONにできます。

sqlite> .headers on

この状態で、次のSQLの文を書くと中身を確認することができます。

sqlite> select * from users;
id|name|password|created_at|updated_at
1|poco|maru|2020-05-20 10:50:13.177731|2020-05-20 10:50:13.177731

現在は、usersテーブルにnameカラムとpaswordカラムが格納されていることが分かりますね。

それでは、入力フォームを作成してユーザーから送られてくるデータをデータベースに格納してみましょう。

/users/new.html.erbファイルに以下のコードを書いてください。

new.html.erb
<%= form_tag("/users/create") do %>
  <p>ユーザー名</p>
  <input name="name">
  <p>password</p>
  <input name="password">
  <input type="submit" value="送信">
<% end %>

form_tagは、viewファイルからコントローラーに何か値を送ったり、削除したりするときに使います。いわゆるPOSTリクエストと呼ばれるものです。getリクエストとpostリクエストの違いについては、こちらの記事を参考にしてください。
type="submit"となっているボタンがユーザーから送られると、users/createに対応するpostリクエストが実行されます。

今回は、以下のようにルーティングしています。

routes.rb
post "users/create" => "users#create"

つまり、上記のデータはusersコントローラーのcreateアクションに送信されます。createアクションでは以下のコードを書きます。

users_controller.rb
def create
  user = User.new(name: params[:name], password: params[:password])
  user.save
end

このコードで、userモデルのUserクラスを用いて、データベースのusersテーブルにデータを格納しています。

form_tagで送られてきたデータにおいて、name属性が指定されているタグが存在した場合、コントローラー内においてparams[name属性]として値を扱うことができます。

この場合、<input name="name">内の値がparams[:name]に、<input name="password">の値がparams[:password]に格納されます。

その送られてきたデータに対してUser.new(name: params[:name], password: params[:password])を用いることでモデルを作成し、データベースに格納しています。

ユーザーからは以下のようになっています。

image.png

この状態で送信を押してみましょう。すると、usersコントローラーのcreateアクション内において、thisという文字列がparams[:name]に格納され、testという文字列がparams[:password]に格納されます。

以下のようにデータを格納することができました。

3|this|test|2020-05-21 05:30:36.718523|2020-05-21 05:30:36.718523

しかし、このままだと送信を押しても特に変化はありません。退屈なので、他のファイルにリダイレクトさせましょう。

送信を押した場合にリダイレクト

送信を押すとusersコントローラーのcreateアクションが実行されるので、このcreateアクション内にリダイレクトのコードを書けば、他のファイルにリダイレクトされるようになります。

usersコントローラーに以下のようにコードを追記しましょう。

users_controller.rb
def create
  user = User.new(name: params[:name], password: params[:password])
  user.save
  redirect_to("/users/index")
end

このように書くと、送信を押した場合に/users/indexに対応するファイルにリダイレクトされます。

ちなみに、リダイレクトを書く場合は、パスの書き始めに/を入れることを忘れないようにしてください。

終わりに

今回の記事はここまでになります。

お疲れさまでした。

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

google-api-ruby-clientを0.8.3からバージョンアップ

障害対応時にgoogle-api-ruby-clientをバージョンアップして解決できたのでメモしておく。

2020/5/11-12の間を境にGoogle Custom Search APIの呼び出しが失敗するようになったので調査。

Rubyのバージョン

ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]

google-api-clientのバージョン

google-api-client (0.8.3)

APIサーバー側で問題になっていそうな箇所を抜き出して
検証用コードを作成

#!/usr/bin/env ruby

require 'google/api_client'

API_KEY = 'XXXXX'
CSE_ID = 'xxxxx' # カスタム検索エンジンID

def google_api_client
  client ||= Google::APIClient.new.tap do |cli|
      cli.authorization = nil
  end
end

def custom_search_api
  api ||= google_api_client.discovered_api('customsearch', 'v1')
end

query = {
  num: 3,
  start: 1,
  total_num: 25,
  q: '検索ワード',
  cx: CSE_ID
}

json = google_api_client.execute(
  api_method: custom_search_api.cse.list,
  key: API_KEY,
  parameters: query
).body

puts json

実行結果。404が返っている。

W, [2020-05-21T12:31:15.200344 #19166]  WARN -- : Google::APIClient - Please provide :application_name and :application_version when initializing the client
W, [2020-05-21T12:31:15.236307 #19166]  WARN -- : Google::APIClient - Please provide :application_name and :application_version when initializing the client
<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 404 (Not Found)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>404.</b> <ins>That’s an error.</ins>
  <p>The requested URL <code>/discovery/v1/apis/customsearch/v1/customsearch/v1?cx=012429992898212448897%3Aqfb0vzczcjq&amp;key=AIzaSyDUnb5XYQ4ziNU6Li0o2_8oKgMMTIWhCFg&amp;num=3&amp;q=%E6%A4%9C%E7%B4%A2%E3%83%AF%E3%83%BC%E3%83%89&amp;start=1&amp;total_num=25</code> was not found on this server.  <ins>That’s all we know.</ins>

バージョンアップして正しく動くか確認。
以下を参考に0.8.3から最新の0.32.1にバージョンアップして検証。
https://github.com/googleapis/google-api-ruby-client/blob/master/MIGRATING.md
https://github.com/googleapis/google-api-ruby-client/blob/master/generated/google/apis/customsearch_v1/service.rb#L284

#!/usr/bin/env ruby

require 'google/apis/customsearch_v1'

API_KEY = 'XXXXX'
CSE_ID = 'xxxxx'

custom_search_api = Google::Apis::CustomsearchV1::CustomsearchService.new
custom_search_api.key = API_KEY

query = {
  num: 3,
  start: 1,
  cr: 25,
  cx: CSE_ID
}

json = custom_search_api.list_cses('検索ワード', query).to_json

puts json

これはjsonが返ってきた。
旧バージョンで動いていたときの出力結果がもはやわからないがツラいところだが、正しく動いてそう。

{"context":{"title":"
... 
json","type":"application/json"}}

Google APIのv0.8.3がサポートされなくなった?

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

【Ruby/Rails】Rubocopでupdate_allのLintを非活性にする

rubocop.yml
Rails/SkipsModelValidations:
  Whitelist:
    - update_all

参考:https://github.com/ManageIQ/manageiq-schema/pull/441/files

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

RubyでJWTトークンを検証する

AWS Cognitoのウェブトークンの検証を実施したときのメモです。

JSON ウェブトークンの検証 - Amazon Cognito

Cognitoではユーザプールごとに検証用のJWKセットをダウンロードできます

https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

使用するGem

jwt

jwt/ruby-jwt: A ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.

通常のJWTトークンのデコードに使用

json-jwt

nov/json-jwt: JSON Web Token and its family (JSON Web Signature, JSON Web Encryption and JSON Web Key) in Ruby

パブリック JSON ウェブキー (JWK) から公開鍵を作成するために使用

実装

# JWKセットをダウンロード
uri = "https://cognito-idp.ap-northeast-1.amazonaws.com/example/.well-known/jwks.json"
response = Net::HTTP.get_response(URI.parse(uri))
jwks = JSON.parse(response.body)

# JWTを検証無しでデコード
token = JWT.decode(jwt, nil, false)

# JWKセットの中からkidが一致するものを取得
jwk = jwks["keys"].find { |obj| obj["kid"] == token[1]["kid"] }

# 公開鍵を作成
public_key = JSON::JWK.new(jwk).to_key

# 公開鍵を利用してデコード 検証が不正の場合は例外となる
JSON::JWT.decode(jwt, public_key)

参考になりましたら LGTM お願いします

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

Ruby on rails を初心者向けに解説③ ~データベースの作成~

はじめに

今回は、以前の記事の続きになっています。

よろしければ以前の記事も御覧ください。

Ruby on rails を初心者向けに解説①

Ruby on rails を初心者向けに解説② ~リンクの作成~

データベースとは

データベースとは、データを保存しておく場所のことです。

Ruby on Railsにおいては、コントローラーからモデルを作成することによりデータベースを操作することができます。
image.png

コントローラーの作成

今回は、ユーザーのIDとパスワードを保存するデータベースを作成することを考えます。

データベースを作成する前に、一度コントローラーとアクションを作成してみましょう。

ターミナルで以下のコードを実行してください。

rails g controller users index

これでuserコントローラーを作成し、その中にindexアクションを追加することができました。rails g コマンドにおいて、コントローラー名とアクション名の2つを設定することができます。

以下のようにルーティングしてください。

routes.rb
get 'users/index' => "users#index"

これで、ユーザーからuser/indexというURLが送られてきたときに、userコントローラーのindexアクションを行うことができるようになりました。

userコントローラーを確認してみましょう。今回は、最初からアクションが追加されています。

users_controller.rb
class UsersController < ApplicationController
  def index
  end
end

実はviewファイルも自動で生成されています。

image.png

以下のようにindex.html.erbファイルを書きましょう。

index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

以下のURLでこのファイルにアクセスしましょう。

http://localhost:3000/users/index

以下の画面がでてきます。

image.png

アクションから変数をviewファイルに渡す

image.png

コントローラーがviewファイルを探してきて、それをユーザーに返します。

その時、コントローラーの中のアクションに変数を定義することで、その変数をviewファイル内で用いることができます。

以下のように変数を定義しましょう。

users_controller.rb
class UsersController < ApplicationController
  def index
    @users = ["maru", "poco"]
  end
end

@userのように、変数の前に@を用いることでその変数はインスタンス変数になります。こちらに解説がありました。

このように、コントローラーからviewファイルに変数を渡すときは、ローカル変数ではなくインスタンス変数を用います。

Railsのコントローラでインスタンス変数を使用するのは、以下の場合です。
・メソッド間でのデータの受け渡し(典型的には、before_actionでデータをロードしておくとか)
・ビューへのデータの受け渡し

このように、@変数で作成したインスタンス変数はviewファイル内で利用することができます。

index.html.erb
<% @users.each do |user| %>
  <div>
    <%= user %>
  </div>
<% end %>

image.png

@usersは配列が格納されているので、.each do ~ end で取り出すことができます。この部分は全てRubyのコードなので、<%%>ではさみましょう。ブラウザに表示する必要がないので、<%=%> ではなく <%%>ではさむところがポイントです。

<%= user %>の部分で、ブラウザに表示させます。

今回は、コントローラーのアクション内で配列を定義して、それをviewファイルに渡すことで利用しました。

今度は、データベースからデータをアクション内に持ってきて、それをviewファイルに渡すことを考えてみましょう。

モデルの作成

image.png

データベースを操作するためには、モデルを作成する必要があります。

モデルとは、データベースの情報を操作する仕組みのことです。または、データベースとのやり取りを行うクラスとも言うことができます。

以下のコードでモデルと。マイグレーションファイルを作成します。

rails g model user name:string password:string

Userがモデル名で、id、passwordがカラムになります。カラムとは、データベースの縦のデータ、つまり列のことです。

このコードで作成したデータベースは、以下のような表になっています。

image.png

この表全体をテーブルと呼び、縦のデータをカラム、横のデータをレコードと呼びます。モデルとは、このデータベースに対応するRubyのクラスであり、モデルクラスのインスタンスは一つの行(レコード)を表すオブジェクトであり、テーブルの列(カラム)に対応する属性をもちます。

上記のコードを実行すると、データベースを作成するためのmigrationファイルと、モデルが作成されます。

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :name
      t.string :password

      t.timestamps
    end
  end
end
user.rb
class User < ApplicationRecord
end

上記の作成したマイグレーションファイル以下のコードで取り込むことで、データベースを作成することができます。

rails db:migrate

ここまでで、データベースを作成することができました。

それでは、マイグレーションファイルとは何なのかについてまとめていきましょう。

マイグレーションファイルとは

マイグレーションファイルとは、データベースを生成する際の設計図のようなものです。

マイグレーションファイルを実行することで、その内容に基づいたデータテーブルを作成することができます。

railsでは、rails g modelコマンドを実行すると、マイグレーションファイルとモデルの両方が生成されます。

マイグレーションファイルはデータベースの枠組みを作成するための道具で、モデルはデータベースとコントローラーを橋渡しする道具のようなものだと覚えておいてください。

データをデータベースに保存

以下のコードでRailsのコンソールを起動しましょう。

rails console

コンソールを起動したら、モデルを使ってデータベースにデータを保存してみましょう。

user = User.new(name: "poco", password: "maru")
user.save

これでデータベースにデータを保存することができました。

データベースの確認

それでは作成したデータベースを確認していきましょう。

データベースクライアントの起動

以下のコードでデータベースクライアントを起動できます。

rails dbconsole

テーブルの一覧表示

データベースクライアントを起動した後は、以下のコードでテーブル一覧を表示することができます。

sqlite>.table

ar_internal_metadata schema_migrations users

スキーマの表示

以下のコードでスキーマを表示することができます。スキーマとは、構造という意味です。指定したデータベースの構造を確認することができます。

sqlite>.schema users

CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "password" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);

データベースクライアントの停止

以下のコードでデータベースクライアントを停止することができます。

sqlite>.quit

ここまでで、データベースを作成することができました。

それでは、実際にデータベースを利用してみましょう。

データベースの利用

データベースには、モデルを用いてアクセスします。

image.png

データベースを利用する際は、コントローラーからモデルを用いてアクセスします。

users_controller.rb
class UsersController < ApplicationController
  def index
    @user = User.first
  end
end

これで、index.html.erbファイル内でインスタンス変数@userを使用することができるようになりました。

@userには、usersテーブルの一番最初の列のレコードが格納されています。

以下のように、index.html.erbファイル内で使用します。

index.html.erb
<p><%= @user.name %></p>
<p><%= @user.password %></p>

image.png

終わりに

今回はここまでになります。

ここまでお付き合い頂きありがとうございました。

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

userを削除した際に紐づいたtweetを同時に削除する方法

某プログラミングスクールに約3ヶ月通い今回はその時に学習した事を書きます!!

今回はブログアプリを作成した際に、userを削除し紐づいたtweetも同時に削除する方法を書きます。

dependent: :destory

dependent: :destoryとはuserを削除した際に紐づいたtweetも同時に削除してくれる機能で、これのおかげでuserを削除してもtweetだけが残るといった事を回避できます:point_up_tone2:

実装

user.rb
Class User < ApplicationRecord
  has_many :tweets, dependent: :destroy
end
tweet.rb
Class Tweet < ApplicationRecord
  belongs_to :user
end

上記の記述でuserを削除した際に紐づいたtweetも削除する様になりました。

注意点

tweetモデル側にdependent: :destroyを記述するとtweetを削除した際に紐づいたuserが削除されてしまいますので注意して下さい!!

最後まで見て頂きありがとうございます!!

この記事が少しでも参考になれば嬉しいです:pray_tone2:

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

ログインしていないユーザーをnewやeditページに遷移しない様にする方法

今回のやりたい事は

ログインしていないユーザーは、index,showページのみ遷移できて、newページやeditページに遷移しようとすると強制的にindexページに遷移されるようにします。

方法

以下の処理を行うことで、ユーザーがログインしていない状態でindex,showページ以外に遷移しようとすると、強制的にindexページに遷移されるようになります。

controller.rb
class PracticeController < ApplicationController
  before_action :move_to_index, except: [:index, :show]

  ---省略---

  private

  def  move_to_index
    redirect_to action: :index unless user_signed_in?
  end

説明

コントローラー内で繰り返し使用されるコードは、private以下でメソッド化します。

最後まで見て頂きありがとうございます!!

この記事が少しでも参考になれば嬉しいです:pray_tone2:

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

RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleしてみよう

困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い

RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。

実際のスピードについては以下のツイートに載せた動画を参考にしてください。

問題が発生した実行環境

僕の実行環境は以下のとおりです。

  • RubyMine 2020.1.1
  • macOS 10.15.4
  • MacBook Pro (13-inch, 2017)
  • Memory 16GB

RubyMineが遅い原因

RubyMineが遅い理由はどうやら4Kディスプレイにあるようです。
僕は普段Mac本体のディスプレイではなく、USB-Cで接続した4Kディスプレイ(EIZO EV2785)を使っているのですが、RubyMineのウインドウを4Kディスプレイで表示したときだけ、この現象が発生しました。
Mac本体のディスプレイに表示したときは、必要十分なスピードで入力できました。

20200426090206.jpg

同じような現象はIntelliJ IDEAやPhpStorm、PyCharmといった他のJetBrains製IDEでも発生しているようです。

解決策(というか、軽減策)

下記の情報を参考にして、IDEとEditorのアンチエイリアス設定をSubpixelからGreyscaleに変更すると、2019.3とほぼ同等の入力スピードに戻すことができました。

Maybe it will help someone: the integrated terminal was extremely slow, I was able to fix the performance issue by changing the Antialiasing to Greyscale for both the editor and IDE.

https://youtrack.jetbrains.com/issue/JBR-526#focus=streamItem-27-4144635.0-0

アンチエイリアス設定はPreferences > Appearance & Behavior > Appearanceで変更できます。

Screen Shot 2020-05-21 at 7.45.04.png

こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。

NyO7vIOBwK.gif

ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。

Subpixelモード↓
Screen Shot 2020-05-21 at 7.47.58.png

Greyscaleモード↓
Screen Shot 2020-05-21 at 7.47.40.png

また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。

No antialiasingモード↓
Screen Shot 2020-05-21 at 7.48.20.png

できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。

参考:あまり効果がなかった対策

JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。

ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。

情報募集!

その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?

謝辞

本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!

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

RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleにしてみよう

困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い

RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。

実際のスピードについては以下のツイートに載せた動画を参考にしてください。

問題が発生した実行環境

僕の実行環境は以下のとおりです。

  • RubyMine 2020.1.1
  • macOS 10.15.4
  • MacBook Pro (13-inch, 2017)
  • Memory 16GB

RubyMineが遅い原因

RubyMineが遅い理由はどうやら4Kディスプレイにあるようです。
僕は普段Mac本体のディスプレイではなく、USB-Cで接続した4Kディスプレイ(EIZO EV2785)を使っているのですが、RubyMineのウインドウを4Kディスプレイで表示したときだけ、この現象が発生しました。
Mac本体のディスプレイに表示したときは、必要十分なスピードで入力できました。

20200426090206.jpg

同じような現象はIntelliJ IDEAやPhpStorm、PyCharmといった他のJetBrains製IDEでも発生しているようです。

解決策(というか、軽減策)

下記の情報を参考にして、IDEとEditorのアンチエイリアス設定をSubpixelからGreyscaleに変更すると、2019.3とほぼ同等の入力スピードに戻すことができました。

Maybe it will help someone: the integrated terminal was extremely slow, I was able to fix the performance issue by changing the Antialiasing to Greyscale for both the editor and IDE.

https://youtrack.jetbrains.com/issue/JBR-526#focus=streamItem-27-4144635.0-0

アンチエイリアス設定はPreferences > Appearance & Behavior > Appearanceで変更できます。

Screen Shot 2020-05-21 at 7.45.04.png

こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。

NyO7vIOBwK.gif

ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。

Subpixelモード↓
Screen Shot 2020-05-21 at 7.47.58.png

Greyscaleモード↓
Screen Shot 2020-05-21 at 7.47.40.png

また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。

No antialiasingモード↓
Screen Shot 2020-05-21 at 7.48.20.png

できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。

参考:あまり効果がなかった対策

JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。

ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。

情報募集!

その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?

謝辞

本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!

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

rubyにおける変数の種類

rubyにおける変数の種類

変数の種類が色々あってどう使い分けるかわからなかったので,自分用にメモを残す.

目次

  1. [ローカル変数]
  2. [インスタンス変数]
  3. [クラス変数]
  4. [グローバル変数]

ローカル変数

  • 小文字or_(アンダーバー)で始まる.
  • メソッドや,ブロック内のローカルなスコープで参照できる.
local.rb
def local()
  a = 3
end
p a

# =>Traceback (most recent call last):
local.rb:5:in `<main>': undefined local variable or method `a' for main:Object (NameError)

localメソッドで定義されたaは,メソッドの外からは呼び出せない.

インスタンス変数

  • @で始まる.
  • 同じクラス内であれば,違うメソッドからも参照可能.
  • インスタンスごとに値を保持できる.
instance.rb
class Instance
  def set_val(val)
    @val = val
  end

  def put_val
    p @val
  end
end

test1 = Instance.new
test1.set_val("aaa")
test1.put_val

test2 = Instance.new
test2.put_val


# =>
"aaa"
nil

インスタンスtest1の@valに,set_valメソッドを用いて,"aaa"を入れる.
その後,インスタンスtest2において,put_valを呼び出すが,nilが返ってくる.
ここから,各インスタンスごとに値を保持していることがわかる.

instance.rb
class Instance
  def set_val(val)
    @val = val
  end

  def put_val
    p @val
  end
end

test1 = Instance.new
test1.set_val("aaa")
test1.put_val

test2 = Instance.new
test2.set_val("bbb")
test2.put_val
# =>
"aaa"
"bbb"

インスタンスtest2に対してもset_valメソッドを用いる必要がある.

クラス変数

  • @@で始まる.
  • 同じクラスのすべてのインスタンスで共有される.
class.rb
class Instance
  def set_val(val)
    @@val = val
  end

  def put_val
    p @@val
  end
end

test1 = Instance.new
test1.set_val("aaa")
test1.put_val

test2 = Instance.new
test2.put_val
# =>
"aaa"
"aaa"

先ほどのインスタンス変数とは違い,値が同じクラスのすべてのインスタンスで共有されている.

class.rb
class Instance
  def set_val(val)
    @@val = val
  end

  def put_val
    p @@val
  end
end

test1 = Instance.new
test1.set_val("aaa")
test1.put_val

test2 = Instance.new
test2.set_val("bbb")
test2.put_val

test1.put_val

# => 
"aaa"
"bbb"
"bbb"

グローバル変数

  • $で始まる.
  • プログラムのどこからでも参照できる.
global.rb
class Global
  def set_val(val)
    $val = val
  end
end

test1 = Global.new
test1.set_val("aaa")

p $val

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

コメントの投稿者名を表示したい

User,Post,Commentの3つのリレーションが、以下の時にコメント一覧でコメントの投稿者名を表示させたい。

user.rb
has_many :posts
has_many :comments
post.rb
belongs_to :user
fas_many :comments
comment.rb
belongs_to :user
belongs_to :post
show.html.erb
<p>コメント一覧</p>
<% @comments.each do |c| %>
    <hr>
    <a href="/users/#{c.user_id}"><%= c.user.name %></a>
    <%= c.content %>
<% end %>

c.user.nameのnameがNoMethodErrorとなるので、posts.controllerに以下の1行追加することでcommentに関係したuserの情報を取り入れることができる。

posts.controller.rb
def show
   @comments = @post.comments.includes(:user).all
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人アプリ制作2 WEBフォントの導入

今日の実装

① controller users tweet
② model user(devise) tweet
③ haml scssの導入
④ viewの作成 tewwt index

webフォントの導入 Google Web Fonts

今回はおしゃれフォントをにしするためにGoogle Web Fontsを使用した。
study-support.png

導入方法

今回はscssで導入方法を記述する。

1 使いたいフォントを探す

google fonts から使いたいフォントを探す。
Google Web Fonts
google-fonts.png
右上にタブの

link href="https://fonts.googleapis.com/css2?family=Anton&display=swap" rel="stylesheetのURLをコピーする。

2 SCSSに読み込ませる

app/assets/stylesheets/application.scss
@import url(https://fonts.googleapis.com/css?family=Anton);

これでSCSS内でfontfamilyでAntonを使用することができる。

3 SCSSに使う。

app/assets/stylesheets/modules/_tweets.scss
font-family: 'Anton', sans-serif;

すると 下のstudy-supportにAntonフォントが適用される。
study-support.png

注意

上のサイトgoogle fontsは日本語が対応していないものがほとんどなので日本語は適用されない場合が多い。
私は、日本語だけ適用されないことに気づかず40分ほど時間を浪費した笑

今回はapplication.scssに読み込ませたが他の導入方法をあります。

参考サイト

Google Web Fontsを使ってみよう

今日のアイディア

投稿機能に動画を埋め込む

投稿機能(アウトプット)を今日勉強したことの自分のアウトプット動画をいれる

経緯

昨今はyoutubeなどの動画をみて学習したり、Twitter等でアウトプットをすることが動画でアウトプットをしているのが少なく見えた。時代の流れから推測するにSNSは、文字による表現➯文字+写真(インスタグラム)➯動画(youtubeにより個人の発信)となり、時代は動画にあると感じる。今よりももっと個人で発信できることを踏まえ、個人アプリstudy-supportでは、投稿機能に動画によりアウトプットもできるようにしたい。人に何かを伝えることが1番の勉強である。動画という選択肢はアウトプットの幅を広げることだと感じる。

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