20201104のRailsに関する記事は30件です。

webサイトが画面が表示されるまでの仕組み

ウェブサイトの画面が映る仕組み

分かっていそうで理解しきれていなかった内容をまとめていきたいと思います。

重要ワード

  • IPアドレス
  • ドメイン
  • DNSサーバー
  • スキーム
  • パス
  • クライアント

基本の理解

https://qiita.com/questions/new
例えば今アクセスしているURLはこんな感じになっています!

この中で"https" この部分のことをスキームといいます。
簡単にいうとどんな手段で通信するの?
といった意味です。今では大体はhttpsではないのでしょうか?

ちなみにスマホとかでよく"このサイトは安全ではありません"とか出てくることがあるのは
httpsではなくhttpが使われている場合です!

httpsとはデータが送信される時にそのデータが暗号化されるようになっています。
これに大してhttpは通信が暗号化されていないと言ったセキュリティに関して少し甘いところがあります!

もしデータが抜き取られてしまった時に暗号化されていた方が安心ですよね。

つづいて"qiita.com"の部分はドメインと呼ばれています!
ドメインとは簡単にいうとインターネット上の住所なようなものです!

これは必ず他のドメインとは被ってはいけないというルールがあります。

つづいて"question/new"これはパスですね。
ドメインの中のどこにアクセスするかを表しています!!

これでURLが何を表しているかざっと理解できたかと思います!

本題

ではここからどうやってwebが画面上に映し出されるか説明していきたいと思います。

①まずはクライアントでURLにアクセスします!
(クライアントというのは端末のことです。主にスマホやPCのことを指します)

②クライアントはこれをDNSサーバーに対して、このドメインのIPアドレスを教えてとお願います!!
おっとここで色々なワードが出てきましたね笑

一つずつ説明していきます。
IPアドレスというのはネットワークにアクセスするために振り分けされる番号のようなものです。
このIPアドレスでもURLでもどちらでもネットにアクセスすることが可能になっています!

DNSサーバーというのはドメインに対してIPアドレスを調べることができるサーバーのことです!

つまりクライアントがqiita.com(ドメイン)のIPアドレスを教えて〜とDNSサーバーに投げると
DNSサーバーは"×××.+++.+++.---"だよ〜みたいな感じでIPアドレスをクライアント側に返します。

③クライアントはそのIPアドレスのwebサーバーに接続します!
さらにパス部分のquestion/newのページを開くようサーバーに要求します!

④これによりwebサーバーがクライアントに対してHTMLを発行し、URLの画面が映し出されます!

だいぶ駆け足になってしまいましたが以上がwebサービスが画面に映し出されるまでの仕組みです!

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

Ajax通信でコメントを削除する

Ajaxを用いたコメント削除機能を実装するにあたり詰まった点がいくつかあったので、記録として残しておきます。
実装イメージは以下の通りです。
ForGif.gif

詰まった点は以下の2点です。
1.ネストされているコメントに対する削除方法(引数をどのように渡せばいいか)
2.js.erbファイルの記述方法

この2点に対する解決方法を記述をします。(解決までに自分が行ったことも書いていくため、長くなります。)

環境は以下のとおりです。
ruby 2.6.5
Ruby on Rails 6.0.3.3

1.ネストされているコメントに対する削除方法(引数をどのように渡せばいいか)

まずは同期通信で削除ができるように実装を進めていきましたが、いきなり躓きました。ネストされている機能(今回の場合はコメントに関する機能)を削除したことがなかったからです。
とりあえず引数どうすればよいのかと考え以下の様にしました。
①コメントのidを渡す
単純にコメントのidを渡してどのような動きになるのか確認をしました。

  <% @comments.reverse.each do |comment| %>
    <div class="comment">
        <%=link_to image_tag("delete.png", class: "delete-btn"), laundry_comment_path(comment.id), method: :delete %>
      (略)
    </div>

結果
ecf4483bf2a896dc759a44d5c6266805.png

id=11のコメントなどないと怒られました。実際データベースを確認したところid=11のコメントはありませんでした。
不思議に思いつつbinding.pryで確認してみたところ何故か店舗のidとコメントのidが逆に渡されていました。098dc402f419712643eb7007203598a1.png
(本来はlaundry_idが11でid(コメントのid)が123でないといけません。)

何故このようになってしまったのかはわかりませんがとりあえず違うということで別の方法を考えます。
※結果として今でも分からずにいます。わかる方いましたらご教授ください。

②インスタンス変数を渡す
ネットで検索をしていたところ以下のQiita記事を発見しました。
[Rails]ネストしたコメントの削除機能の作成
まさに私が探し求めていたものだ!ということで早速実践。

 <%=link_to image_tag("delete.png", class: "delete-btn"), laundry_comment_path(@laundry,comment.id), method: :delete %>

引数を@laundry,commentの2つにしました。(@laundryはコントローラーで定義しています。)
実行してみましたがエラーがでてうまくいきませんでした。(そもそもブロック変数を用いる式の中でインスタンス変数を用いることってあるのか?という疑問が残りました)
ここでネストをしている時は2つ引数を渡す必要があると気づきパスを確認してみたところ/laundries/:laundry_id/comments/:idというパスになっていました。
普段も:idの部分がわかるように引数を渡しており、今回もlaundry_idとコメントのidが渡せればいいためブロック変数のcommentからlaundry_idを渡すために以下のように引数を指定して解決しました。

解決策

<%=link_to image_tag("delete.png", class: "delete-btn"), laundry_comment_path(comment.laundry_id, comment.id), method: :delete  %>

ネストがあったとしても基本は同じで、どこのパスにいくのか、そのためにはどの引数が必要なのかを判断する必要があると学びました。

2.js.erbファイルの記述方法

同期通信が実装できたため、次は非同期の実装に取り掛かります。
基本的にはlink_toに対してremote: trueのオプションをつけることで非同期通信が実行されてhoge.js.erbファイル(今回はdestroy.js.html)を探してレンダリングするという流れですが、destroy.js.html.js.erbの記述でまた躓きました。
そもそもjs.erbファイルを記述したことがなかったためjs.erbファイルとは?というところから始まったのですが要点をまとめると以下のような感じになるかと思います。

・js(ajax)リクエストが行わたときにレンダリングされるファイル。
・Javascriptを用いて記述ができる。
・ERBの記法(<%= >や<% >)が使用できる

Javascriptが使えるのか〜と思いながら削除機能に関するjs.erbファイルの書き方等を調べていると全部と言ってもいいぐらいネットの記事ではjQueryで書かれていました。
そこまで記述量多くなさそうだし素のJavascriptでやってみようと思い、Javascriptで記述をすることにしました。

const commentArea = document.getElementById('comment-area');
commentArea.innerHTML = "<%= render partial: 'index', locals: { comments: @comments } %>";

とりあえず必要な要素を取得してそこにrenderで部分テンプレートを埋め込めばOKそうだったのでこのように記述して実行をしてみました。

結果
Uncaught SyntaxError: Unexpected identifierエラーが表示される。
該当箇所を確認すると以下のように表示されました。
4c8eda50ceeb08012ef5bfae938fcf51.png

エラー内容とエラー箇所からダブルクォーテーションの中身がダブルクォーテーションなので構文エラーになっているのでは?とご指摘いただき修正したのですが同じエラーが表示されてしまいました。

解決方法

`<%= render partial: 'index', locals: { comments: @comments } %>`;

バッククオートで囲むことで解決しました。

とりあえずできるようにはなったのですが、続いて1回しか削除ができない(読み込みをしないで別のコメントを削除しようとするとエラーが表示)という事象が発生しました。

const commentArea = document.getElementById('comment-area');
commentArea.innerHTML = `<%= render partial: 'index', locals: { comments: @comments } %>`;

Identifier 'commentArea' has already been declared
commentAreaは宣言されているよということで「定数だから1回しか宣言できないのか」ということでconst→letに変更

let commentArea = document.getElementById('comment-area');
commentArea.innerHTML = `<%= render partial: 'index', locals: { comments: @comments } %>`;

変更後実行してみましたが再度同じエラーが出現。
「定数やめたのになんでよ」と思いましたが確かにletを使うのは変数を定義するときだけで値を変更する時はlet使わないよな〜ということでletを削除

commentArea = document.getElementById('comment-area');
commentArea.innerHTML = `<%= render partial: 'index', locals: { comments: @comments } %>`;

・・・これ、もう2行に分ける必要ないじゃんということで1つにまとめてようやく実装できました。

document.getElementById('comment-area').innerHTML = `
<%= render partial: 'index', locals: { comments: @comments } %>`;

すごく遠回りをした気がしますが、なんとが実装ができて一安心という感じです。
ですがとりあえずできた段階で理解には全然達していないため、理解を深めていきます!

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

deviseで記述があっているのに、ログアウトできない現象

はじめに

 deviseを導入してユーザー管理機能を実装していたが、予期せぬエラーに見舞わせたので、記録しておく。

エラー内容

Routing Error
No route matches [GET] "/users/sign_out"

<%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: :logout %>

もちろん、method: :deleteとビューファイルにも記述していた。なぜか、ルーティングでHTTPメソッドがGETのものを探し出していた。

ローカルサーバーを再起動させる

ターミナルで
「control + c」
入力。
再び、ターミナルで「rails s」実行

荒技!?

config/initializers/devise.rb/269行目
config.sign_out_via = :delete

:delete:getに変える。

最後に

 記述もあっているのに、なぜこのエラーが出たのか、いまだにわからない…。全く同じ記述でも、エラーが出る時と出ない時がある。

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

Ruby縦書き

rubyで縦書きする方法

結論

a = gets.chars
puts a

例えば下記のようにgetsにtokyoを当てはめると

a = Tokyo.chars
puts a

出力結果

T
o
k
y
o

という感じになります

以上

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

ActiveStorageを用いた画像表示で陥ってしまったミス

本記事は正しく機能を実装できている方には無縁かもしれません・・・
ActiveStorageを用いたメッセージ投稿機能のようなものを作成していました。投稿したメッセージを表示するビューファイルを作成したので挙動を確認しようとしたところエラーが発生しました。ロジックは合っているのになぜ・・・

原因

開発途中で挙動確認をしていた際、画像を添付していなくても投稿できたことがあったことを思い出しました。
has_one_attachedによるアソシエーションがうまく組めていなかった時にメッセージだけの投稿データがテーブルに登録されていました。前述の通りアソシエーションを組んでいたため今回作成したビューファイルを開く際にエラーが発生しました。

解決方法

私の場合はテーブルの状態を確認できるSequelProを導入していたので、当該レコードのみを直接削除して対応しました。本来であればテーブルに対してresetを行う方が正しいとは思いますが、個人の開発であればこちらでも良いのかな、、、(良くない)

以上、ロジックだけではなく違う側面も考えられる広い視点が必要だと感じた出来事でした。

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

AWSで80ポートを開いてるのに【このサイトにアクセスできません】が出る

image-2.png

先日Railsアプリをsslで通信したいと考えRailsでconfig.force_sslを設定してからなぜかアプリが表示されなくなったため、その時の対処法をまとめました。

アプリの状態

アプリ名 my-app
server EC2
web server nginx
application server unicorn
解放ポート 80, 443, 22

まずはセキュリティグループを確認

いつものことかと思い

AWS→セキュリティグループ→対象のパブリックなセキュリティグループを確認

しかし特に異常は見つからず80番と443番はしっかり開いてました。
普通なら通信はできるはずなのですが…

ちなみにlocal環境からEC2インスタンスへssh通信はできているようです。

nginxが80ポートにlistenしているかを確認

続いてnginxの設定ファイルを確認し80ポートにlistenされているか。

$ssh USER@MY-ELASTIC-IP

$ cat /etc/nginx/conf.d/my-app.conf


upstream unicorn {
          server unix:/var/www/my-app/shared/tmp/sockets/unicorn.sock;
}
server {
  listen 80; <-----------
  server_name ELASTIC-IP;
  root /var/www/my-app/current/public;

  access_log /var/log/nginx/nginx_access.log;
  error_log  /var/log/nginx/nginx_error.log;


  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }
}

ポート指定も問題ないようです。

コンソールからポートが開いているかチェック

$ netstat -tln

# 接続しているものでlisten済みのものを表示

tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::3306                 :::*                    LISTEN
tcp6       0      0 :::111                  :::*                    LISTEN

確認してみると確かに接続を許していないようです。なぜ22ポートだけが開いているのかわかりません。

試しにこの状態でセキュリティグループをいじくっても結果は変わらず…

セキュリティグループの他にもネットワークインターフェースやネットワークACL、インターネットゲートウェイ、ルートテーブルなども確認してみたのですが結果は変わりませんでした、

結論 ngixnの不調でした

結局nginxを調べてみることに。
すると…

$ sudo nginx -s reload

----------------------------------------------------------------------

nginx: [error] open() "/var/run/nginx.pid" failed (2: No such file or directory)

capistranoでデプロイした際は特に問題なくunicornは起動していたので見落としてしまっていましたがここに原因があったようです。

nginxpidファイルが消失してしまっていたため作り直します。

ngixn 再起動

$ sudo service nginx stop

$ sudo touch /run/nginx.pid     

$ nginx -t

$ sudo service ngixn start  

再び確認

$ sudo service ngix status

----------------------------------------------------------------------
Redirecting to /bin/systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2020-10-30 06:05:36 UTC; 3h 28min ago
  Process: 22103 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 22100 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 22099 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 22106 (nginx)
   CGroup: /system.slice/nginx.service
           ├─22106 nginx: master process /usr/sbin/nginx
           └─22111 nginx: worker process

Oct 30 06:05:36 ip-10-0-0-178.us-east-2.compute.internal systemd[1]: Starting The nginx HTTP and reverse proxy server...
Oct 30 06:05:36 *p-10-0-0-178.us-east-2.compute.internal nginx[22100]: nginx: the configuration file /etc/nginx/ngi...ok
Oct 30 06:05:36 ip-10-0-0-178.us-east-2.compute.internal nginx[22100]: nginx: configuration file /etc/nginx/nginx.c...ul
Oct 30 06:05:36 ip-10-0-0-178.us-east-2.compute.internal systemd[1]: Failed to read PID from file /run/nginx.pid: ...ent
Oct 30 06:05:36 ip-10-0-0-178.us-east-2.compute.internal systemd[1]: Started The nginx HTTP and reverse proxy server.
Hint: Some lines were ellipsized, use -l to show in full.

その後

$ netstat -tlnt

tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::3306                 :::*                    LISTEN
tcp6       0      0 :::111                  :::*                    LISTEN
tcp6       0      0 :::80                   :::*                    LISTEN

解決しました!

nginxが上手く機能していないとすると80ポートが閉じてしまうのは知らなかったですね…

原因はおそらくインスタンスの再起動

rails で config.force_ssl = trueを使用

sslに必要な設定を行っていなかったため表示エラー

その際EC2インスタンスを再起動したため上手くnginxが機能していなかったのだと思います。

config.force_sslエラーについてはこちらの記事にまとめました。
>>【config.force_ssl】を使用したらページが表示されなくなった

次回同じようなエラーに遭遇したらまずはnginxからポートが開いているかを確認してからawsコンソールを弄ろうと思います。

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

ライブラリの導入

前回の記事でアプリを作成したらライブラリを導入する。
ライブラリとはGemの事で、開発を迅速に進めるための拡張機能。備忘録として残す。

Gemfileの中にアプリで使う名前、バージョンを管理。

ファイルに記載したらターミナルにて bundle install をする。

Gemfile

# Use mysql as the database for Active Record
gem 'mysql2', '0.5.3'  追加
.
.
.
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

gem 'pry-rails'  追加

追加後、bundle update
今回はすでにあるバージョンの更新のため update

rails s をし起動すればOK

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

学習アウトプット〜11/3〜

生成したコードを元に戻す方法

「ああ、あの頃に戻れればなあと」自分の人生のある地点の後悔を引きずっている人は多いと思います。私にもそのような後悔はあります。

でも人生は元には戻れません。今に目を向けましょう。周りにいる人を見てみてください。その人たちと一緒に今を必死に生きていきましょう。

ということで、人生は元には戻せませんが、プログラミングは別です。
何か間違いがあれば何度でもやり直せます。笑

コントローラ

$ rails geneate controllar motipuyo

というようにmotipuyoという名前のコントローラを作成したけど、無かったことにしたいよって時は

$ rails destroy controllar motipuyo

で削除されます。

コントローラは学校で言うとクラスの担任です。
modelやviewに指示を出すことで各クラスをまとめ、アプリケーションという一つの学校を成り立たせる。
今のところはこのようなイメージです笑。

データベース

$ rails db:migrate

上記はrailsのデータベースをマイグレイト(移行する、新しく更新する的な意味)するコマンドですが、それを取り消したい時は、

$ rails db:rollback
$ rails db:migrate VERSION=0

というコマンドを使うと削除できます。

上の"rollback""コマンドは一つ前に戻したい時、
下の"VERSION=0"のコマンドは最初の状態に戻したい時に使用します。

今は理解が難しいですが、
データベースを"migrate"で更新していくのを"rollback"で戻ることができる、
という程度に理解しておきます。

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

Rubyの復習

本投稿の目的

Rubyの復習の自分用の議事録です。
以前,inputした知識のうち忘れたことを羅列して書いていきます。
そのため,自己満で書いただけなので脳に叩き込めたら消す予定です。


学習に使った教材

Udemyの "はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう" を教材として使用しました。

購入目的は以下の2つです。
①Cloud9上でRailsアプリを作ること
②デプロイすること


①配列とハッシュ

○配列

qiita.rb
a = [変数1,変数2,変数3]

・添字は0から始まる
・変数2を取り出すには a[1]

○空判定

qiita.rb
a.empty

⇨判定が空なら true

○検索文字判定

qiita.rb
a.include?('検索文字')

⇨検索文字を配列に含む場合はtrueを返す

○連続値の配列への格納

qiita.rb
aa = (0..100).to_a

⇨指定したn個以上の情報を配列に格納

○配列への操作

qiita.rb
配列名.pop
配列名.shift
配列名 << 
配列名.join (' ')
配列名.sort!.reverse!

上から順に
・一番最後の値を消去
・一番最初の値を消去
・一番最後に値を追加
・添字ごとに空白一個あけで結合
・配列の中身を反対にする

○ハッシュ

キーを指定することで,2変数の配列を作れる

qiita.rb
ハッシュ = {キー1 => 1,キー2 => 2,キー3 => 3}
qiita.rb
puts ハッシュ['キー1']
ハッシュ['キー4'] = 4
ハッシュ['キー1'] = 1
ハッシュ.delete('キー1')     

上から順に
・値の取り出し
・値の追加
・値の上書き
・値の削除

○シンボル

ソースコード 上では文字列
内部では整数として扱われる
⇨ハッシュ内部のアクセスが早くなる

qiita.rb
ハッシュ = {キー1 : 1,キー2 : 2,キー3 : 3}
qiita.rb
puts ハッシュ[:キー1]   
ハッシュ[:キー4] = 4  
ハッシュ[:キー1] = 1`     
ハッシュ.delete(:キー1)      
ハッシュ.has_key?(:探したいキーn)  
ハッシュ.size                                         

上から順に
・値の取り出し
・値の追加
・値の上書き
・値の削除
・探したいキーを持っているか?確認
・いくつキーを持つか?確認

②each配列とeachハッシュ

○each配列

qiita.rb
numbers = [1,2,3,4,5]
numbers.each do |number|

<別の記載方法>
numbers = [1,2,3,4,5]
numbers.each { |number| puts number}

○eachキャッシュ

qiita.rb
scores = {luke: 100,jack: 90,robert: 70}
scores.each do |k,v|
     puts v
     puts "#{k},#{v}"
end

③クラス作成

qiita.rb
class Car
  def initialize(name)
    puts 'initialize'
    @name = name
  end

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

boxter = Car.new('boxter')
boxter.hello

carrera = Car.new('carrera')
carrera.hello

【解説】
・class作成時の変数は1文字目は大文字
・インスタンス作成時に指定した引数をinitialzeの戻り値に指定できる
・インスタンス作成 とは,クラスを動かす際のcloneの作成のようなもの
 ⇨宣言時は 変数 = クラス名.new()
・def initialize はインスタンス作成時に実行される処理
・インスタンス変数は,initialize内で定義した@変数
 ⇨Class内でどこでも使用可能

④インスタンスメソッド

qiita.rb
class Car

  def initialize(name)
    @name = name
  end

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

def name
  @name
end

def name = (value)
  @name = value
end

boxter = Car.new('boxter')
boxter.hello
boxter.name
boxter.name = '718boxter'
puts boxter.name

【解説】
・def name部分がない場合,class外から@nameへアクセスできない
(インスタンス変数(@name)はクラス外からアクセスできない)
・ インスタンス作成後の.nameメソッドを実行したことでアクセス可能
・.nameメソッドに対応する値@nameを取得可能
・これらを'インスタンスメソッド'と呼ぶ(ゲッターメソッド)

・def nama = (value) について
⇨インスタンス作成後にboxter.name = 'boxter' とすることで
@name に任意のvalueを指定可能.それを出力も可能
(ruby特有の記述方法)

⑤アクセサメソッド

qiita.rb
def name
  @name
end

def name = (value)
  @name = value
end

を全て

qiita.rb
attr_accessor :name 

で実現可能

attr_accessorメソッド
⇨自作インスタンスメソッドを開発者が書かなくて良い方法
 (他言語ではプロパティーと呼ぶ)

【読み込みのみ実行する場合】

qiita.rb
attr_reader :name 

def name 部分を1行で記述可能
@nameをただ拾うことができる

【書み込みのみ実行する場合】

qiita.rb
attr_writer :name 

def name = (value) 部分を1行で記述可能
@nameへの書き込みおのみができる

⑥クラス変数

qiita.rb
class Car
  @@count
  def initialize(name)
    puts 'initialize'
    @name = name
    @@count += 1
  end

  def self.info
    puts "Test"
  end

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

・Class内で使用する変数
・開始文字は@@2文字から始まる
・def ごとにリセットされたくない変数を扱う時に必要な変数
⇨インスタンス生成回数をcountしたい時などに有効
⇨メソッドの実行回数をcountしたい時などに有効

⑦クラスメソッド

クラスから直接呼び出せるメソッド

qiita.rb
class Car
  def initialize(name)
    puts 'initialize'
    @name = name
  end

  def self.info
    puts "Test"
  end

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

Car.info
boxter = Car.new('boxter')
boxter.hello

carrera = Car.new('carrera')
carrera.hello

【解説】
・self.メソッド名 としてメソッドを記述する
・Car.info のようにインスタンス生成なしで使用可能なメソッド
・通常のメソッドなら boxter.info としないと使えないはず


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

検索を行うメソッド

はじめに

検索機能をrailsで実装する際に、必要なメソッドについて解説しています。

1.whereメソッド

モデルが使用できる、ActiveRecordメソッドの1つです。
このメソッドにより、引数に記述した条件に一致するデータを取り出すことが出来ます。

モデル.where('検索対象となるカラムを含む条件式')

2.LIKE句

LIKE句は、曖昧(あいまい)な文字列の検索をするときに使用するもので、whereメソッドと一緒に使います。

cが含まれる名前を取り出す際の記述

where('name LIKE(?)', "%c%")    

%・・・任意の文字列

3.検索を行う際のコード

ここで、実際にコードを記述する場面を考えてみましょう。
例えば、ツイートテーブルのnameカラムに太郎という文字を含むデータを取り出す場合は、どんなコードとなるでしょうか?
もし、時間がある方は、自分で考えてみてください。

実際に記述は以下となります。

Tweet.where('name LIKE(?)', "%太郎%")

以上の記述により、検索を行えるメソッドを作成することが出来ました。
カラムやどんな値を含んだものを取り出したいのかを考えて実際に使用してみてください!

最後に

最後までお読みいただきありがとうございます!
同じ悩みや課題を抱えている方のお役に立てれば幸いです!

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

RailsでPAY.JP APIを利用する 〜カード登録編〜(payjp.js v2)

概要

PAY.JP APIでのカード登録/変更/削除機能実装の覚書です。
今回は取得したトークンからカード情報を登録するまでの手順をまとめていきます。
※簡潔にするため実装のために必要な最低限のコードのみ書いています。

なにか間違い等あれば編集リクエストをいただけると幸いです。

ちなみにpayjp.jsはv1に関する記事は多いですが現行のv2とは異なる部分が多いので注意が必要です。

環境

# OS Version
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H2

# Ruby Version
ruby:           2.6.5p114
Rails:          6.0.3.3

前提条件

PAY.JP APIを用いてトークン情報を取得するところまでできている。
前回の記事参照。

手順

ビューを編集

JSでエラーメッセージや隠しフォームを仕込むための追記をします。

_card_form.html.haml
.CardForm
  カード番号
  .CardForm__outer{id: "number-form"}
  有効期限
  .CardForm__outer{id: "expiry-form"}
  セキュリティコード
  .CardForm__outer{id: "cvc-form"}
  #message    -# ここにエラーメッセージが表示されます
  = form_with model: @card, id: "card_form", local: true do |form|
    #card_token   -# ここに隠しフォームが追加されます
    = form.submit "カードを登録", class:"CardForm__button", id: "create_card"

コントローラーを編集

トークンを使用してPAY.JP APIから顧客情報を作成し、Cardテーブルに保存するようにします。
※最低限の記述に削っているので実際は条件分岐をしたほうが良いです。

card_controller.rb
class CardController < ApplicationController
  require 'payjp'

  def new
  end

  def create
    Payjp.api_key = ENV['PAYJP_SECRET_KEY']  # APIを初期化
    customer = Payjp::Customer.create(card: params['payjp_token'])  # 顧客IDから顧客データを作成
    @card = Card.new(
      user_id: current_user.id,          # ユーザーIDと紐付け
      customer_id: customer.id,          # 顧客IDを保存
      card_id: customer.default_card     # 顧客のカード情報を保存
    )
    @card.save
  end
end

JSファイルを編集

カード情報送信ボタンを押すとトークンを使用して顧客IDとカードIDが入った隠しフォームを作成、送信するようにします。

card_form.js
// DOM読み込みが完了したら実行
document.addEventListener('DOMContentLoaded', () => {
  // 公開鍵を登録し、起点となるオブジェクトを取得します
  var payjp = Payjp('pk_test_hogehoge')

  // elementsを取得します。ページ内に複数フォーム用意する場合は複数取得ください
  var elements = payjp.elements()

  // element(入力フォームの単位)を生成します
  var numberElement = elements.create('cardNumber', {style: style})
  var expiryElement = elements.create('cardExpiry', {style: style})
  var cvcElement = elements.create('cardCvc', {style: style})

 // elementをDOM上に配置します
  numberElement.mount('#number-form')
  expiryElement.mount('#expiry-form')
  cvcElement.mount('#cvc-form')

  // ボタンが押されたらtokenを生成する関数を用意します
  create_card.addEventListener("click", function(e) {
    e.preventDefault();
    payjp.createToken(numberElement).then(function(r) {
      if (r.error) {  // 登録失敗
        document.querySelector('#message').innerText = r.error.message
        regist_card.prop('disabled', false)
      } else {
        $("#card_token").append(  // #card_token部分に隠しフォームを追加
          `<input type="hidden" name="payjp_token" value=${r.id}>
          <input type="hidden" name="card_token" value=${r.card.id}>`
        );
        $("#card_form")[0].submit();  // フォームを送信
      }
    })
  });
});

参考

結局の所公式APIとガイドが最強です。
ただ読み慣れないうちはQiita等で実際の実装手順を見ながら感覚を掴むと理解が早まると思います。

PAY.JP API リファレンス
https://pay.jp/docs/api/

PAY.JP API 利用ガイド | PAY.JP
https://pay.jp/docs/started


公式ブログも参考になりますが、情報が古くv1準拠で書かれている場合が多いので注意が必要です。

ごく基本的なPAY.JPの使い方(Ruby編) - PAY.JP Engineering Blog
https://payjp.hatenablog.com/entry/2017/11/21/191916


その他参考にした記事等

Railsで Payjp.js V2 でクレジットカード登録機能実装 フリマアプリ - Qiita
https://qiita.com/ta9301/items/6b736390c49c3f40edb6

[HowTo]Pay.jpを用いた商品購入機能実装から商品購入後の設定まで
https://qiita.com/Tatsu88/items/eb420e372077939a4627

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

投稿したものを日付の新しい順に表示したい

何かしらの投稿機能、例えばTwitterのような新しい投稿が先に表示される機能を実装していたところ、考え方が凝り固まっていた部分があったので備忘として記載します。

私がやりそうになったのは以下です。
ハッシュを含む配列に対し、受け取った側で降順ソートして表示しようとした

しかしよく考えると、これは保守性が高くないことに気付きました。
表示は表示という機能に集中することなど、各機能はそれぞれに特化すべきです。メソッドも同様です。
そのため、受け渡す際にソートした状態で受け渡す、これを行う必要がありました。

以下は例です。
Itemテーブルに存在するデータを@itemsに格納する際、「.order('created_at DESC')」というオプションを追加しました。

controller
  def index
    @items = Item.includes(:user).order('created_at DESC')
  end

こうすることで、ビューファイルの表示、という機能に手を加えることなく制御することができました。

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

【Ruby】投稿したものを日付の新しい順に表示したい

何かしらの投稿機能、例えばTwitterのような新しい投稿が先に表示される機能を実装していたところ、考え方が凝り固まっていた部分があったので備忘として記載します。

私がやりそうになったのは以下です。
ハッシュを含む配列に対し、受け取ったビューファイル側で降順ソートして表示しようとした

しかしよく考えると、これは保守性が高くないことに気付きました。
表示は表示という機能に集中することなど、各機能はそれぞれに特化すべきです。メソッドも同様です。
そのため、受け渡す際にソートした状態で受け渡す、これを行う必要がありました。

以下は例です。
Itemテーブルに存在するデータを@itemsに格納する際、「.order('created_at DESC')」というオプションを追加しました。

controller
  def index
    @items = Item.includes(:user).order('created_at DESC')
  end

こうすることで、ビューファイルの表示、という機能に手を加えることなく制御することができました。

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

Basic認証導入時、Heroku上でログインができない。

スクリーンショット 2020-11-04 17.42.50.png

背景

Basic認証を導入して、必要最低限のセキュリティを設けたい。
過去に2度Basic認証の練習として導入したことがあり、その時はスムーズにできたため、今回も導入したいと考えた。
しかし、実際に導入してみると、ローカル環境では正常に動作しログインできるものの、本番環境では正しいユーザー名とパスワードでも弾かれてしまった。

行ったこと

まず、現状を確認するため、もう一度git push heroku masterでどう表示されるかを確認した。

% git push heroku master
Everything up-to-date

全て最新の状態であることから環境変数を設定する前に、github上でコミットしてしまったり、git push heroku masterなどのコマンドを実行したと仮説を立てた。空のコミットをしてから、再度git push heroku masterをする必要があると考え、実行した。

% git commit --allow-empty -m "コミット名"

% git push heroku master
しかし、ログインできませんでした。

原因

application_controller.rbに記述している、private内の記述と、heroku上に設けた環境変数の名前が一致していなかった。

app/controllers/application_controller.rb
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
% heroku config

=== アプリ名 Config Vars
BASIC_AUTH_PASSWORD:      *****
BASIC_AUTH_USERNAME:      *****

対処法

双方の環境変数が紐付くように、同じ名前に変えてあげれば良い。
今回の場合はUSERNAMEかUSERに統一させてHerokuに上げれば、正常にログインできました。

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

RubyのThread#[]=メソッドの注意点

RubyのThread#[]=メソッドについて

RubyのThread#[]= メソッドでは、スレッドごとに固有の値を保持することができます。(JavaでいうThreadLocal のようなものです)
これを利用すると、1スレッド1リクエストに紐づくアプリケーションサーバ(Passengerなどほぼ全てのAPサーバ)では、
1リクエスト中のみ閉じて使用できるスレッドローカル変数として使用することができます。

RubyのThread.current[]メソッドの使用例

以下のように Thread.current[:foo] と現在のスレッドに代入することで、スレッドローカル変数 foo を定義できます。

def self.foo
  Thread.current[:foo] ||= 0
end

また、Ruby on Railsフレームワークにレコードの操作者(作成者、更新者、削除者)を記録する機能を提供するRecordWithOperatorという gem においても
この Thread#[]= を使用して実装されています。
https://github.com/nay/record_with_operator/blob/4389d077b0303b956cc211ef439a46a216ae2cc0/lib/record_with_operator/operator.rb#L4

Thread#[]=メソッドの注意点

PassengerやPumaのような一度作成したスレッドを再利用するようなアプリケーションサーバを使用している場合、
スレッドローカル変数が再利用前提のスレッドに紐づくため、アプリケーション側で明示的に破棄しないと
以前のリクエスト(スレッド)で定義した変数が別のリクエストで参照できてしまうので注意が必要です。

Redmineにも以前、Thread#[]= を使用して現在のユーザを扱うコードがあり、
エラーメッセージに別ユーザの名前が表示されるセキュリティ的なバグの発生事例がありました。
http://www.redmine.org/issues/16685

解決策

アプリケーション側で明示的にThread.currentの値を破棄することで回避できます。
RequestStoreというgemを使用すると Rack::Middlewareの層で、リクエスト毎に
Thread.current の値をクリアするようになります。

使用例は以下の通りです:

def self.foo
  RequestStore.store[:foo] ||= 0
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unicode正規表現サンプル

unicode正規表現サンプル

毎回調べるのが面倒なので、自分が使いそうなunicode正規表現を記事にしておく...

概要 unicode正規表現
漢字ひらがな \u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf
全角スペース \u3000
全角アルファベット大文字 \uFF41-\uFF5A
全角アルファベット小文字 \uFF21-\uFF3A
全角数字 \uFF10-\uFF19

参考

https://www.tamasoft.co.jp/ja/general-info/unicode.html

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

VSCodeでSQLiteを使うなら拡張機能を使う(sqlite3のバイナリファイルを見る方法)

最近Ruby on Railsを勉強中でSQLiteをよく使います。

そこで出てくる.sqlite3というバイナリファイルをVSCodeで使いたかったので、やり方を調べました。

環境

  • VScode 1.50.1
  • SQLite

sqlite3が開けない?

それはRailsチュートリアルの6章をやっていたときに訪れました。

対象のRailsチュートリアル→https://railstutorial.jp/chapters/modeling_users?version=5.1

モデルを作って…

$ rails generate model User name:string email:string

マイグレーションを適用すると…

$ rails db:migrate

db/development.sqlite3というファイルができていました。

スクリーンショット 2020-11-04 15.10.43.png

ただ、これがバイナリファイルでVSCodeでは開けない。。。

拡張機能SQLiteのインストール

そこで『SQLite』という拡張機能を導入します。

VScodeのマーケットプレースからいつもどおりインストールしてみてください。
スクリーンショット 2020-11-04 15.12.27.png

SQLiteの使い方

使い方はカンタンです。

コマンドパレットを開いて「sql」と入力してください。

(コマンドパレットを開く方法)
Windows:Ctrl+Shift+P
Mac:Command+Shift+P

検索結果の「SQLite: Open Database」を選択します。
スクリーンショット 2020-11-04 15.17.55.png

ファイルの選択になるので、対象のファイルを選んでください(今回はdb/development.sqlite3を選びます)
スクリーンショット 2020-11-04 15.19.32.jpg

画面上は特になにも起こっていないように見えるのですが、実はエクスプローラータブをよく見ると「SQLITE EXPLORER」という項目が増えており、ここに各テーブルとカラムが表示されています。
スクリーンショット 2020-11-04 15.27.13.png

ええ感じですね。

テーブルに登録されているデータを見たい場合は、各テーブル名の横にある「▶」をクリックしてみましょう。
スクリーンショット 2020-11-04 15.33.51.png

お、見れましたね。

これで他のツールを開かずとも、VSCode上でSQLite使えますね!

余談

もちろんVScodeを使わなくても、DB Browser for SQLiteという素晴らしいツールもあるので、どちらを使うかはお好みでどうぞ。

公式サイト:https://sqlitebrowser.org/

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

rails new を実行するとbundle installの箇所でフリーズする

タイトルの通りで以下のようなエラーが表示される

Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Could not fetch specs from https://rubygems.org/

このエラーが起きる要因は、Free wi-fiを使っていたり、ウィルスバスターによってよく起きるようです。

この場合自動で他のライブラリとの連動ができていない可能性があるので、手動で整えていく必要があります。

それでは以下の手順で実行していきましょう。

rubygemsのapiを確認する

% host api.rubygems.org

これを実行すると以下の内容が表示されます。

------------ここから------------
rubygems.org has address XXX.XXX.XXX.XX
rubygems.org has address XXX.XXX.XXX.XX
rubygems.org has address XXX.XXX.XXX.XX
rubygems.org has address XXX.XXX.XXX.XX
-----------ここまで使う-----------
rubygems.org has IPv6 address 2a04:4e42::70
rubygems.org mail is handled by 10 mxa.mailgun.org.
rubygems.org mail is handled by 10 mxb.mailgun.org.

XXX.XXX.XXX.XXの部分がapiの部分で様々な数字が並んでいるかと思います。この部分を使っていきます。

上記apiを使ってパスを通す

以下のファイルを開く

% sudo vim /etc/hosts

ここからパスを通していくので、先ほどの上記XXX.XXX.XXX.XX(api)を一つコピーしては、以下のように追加していく

XXX.XXX.XXX.XXX   rubygems.org
XXX.XXX.XXX.XXX   rubygems.org
XXX.XXX.XXX.XXX   rubygems.org
XXX.XXX.XXX.XXX   rubygems.org

このあとにbundle installを実行したところ解決しました。

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

Rails で画像アップロード実装

参考資料

carrierwaveuploader/carrierwave

画像(複数)アップロードにCarrierWaveを使ったRspec - Qiita

【Rails】複数画像を保存したい時のcarrierwaveとMiniMagickの導入 - Qiita

導入編

gem 'carrierwave', '~> 2.0'

bundle install した後、アップローダーの作成

be rails g uploader Images

app/uploaders/images_uploader.rb が作成される。

中身

class ImagesUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  # include CarrierWave::MiniMagick

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

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

  # Provide a default URL as a default if there hasn't been a file uploaded:
  # def default_url(*args)
  #   # For Rails 3.1+ asset pipeline compatibility:
  #   # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  #
  #   "/images/fallback/" + [version_name, "default.png"].compact.join('_')
  # end

  # Process files as they are uploaded:
  # process scale: [200, 300]
  #
  # def scale(width, height)
  #   # do something
  # end

  # Create different versions of your uploaded files:
  # version :thumb do
  #   process resize_to_fit: [50, 50]
  # end

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  # def extension_whitelist
  #   %w(jpg jpeg gif png)
  # end

  # Override the filename of the uploaded files:
  # Avoid using model.id or version_name here, see uploader/store.rb for details.
  # def filename
  #   "something.jpg" if original_filename
  # end
end

Image テーブルに images カラム追加。(Image テーブル作成時に Post テーブルとの紐付け済み)

複数アップロードする場合、json 形式で保存するらしいので、指定しておく。

be rails g migration add_images_to_image images:json

migrate 中身

class AddImagesToImage < ActiveRecord::Migration[6.0]
  def change
    add_column :images, :images, :json
  end
end

rails db:migrate してカラムを追加

テストの実装

models/image.rb

class Image < ApplicationRecord
  belongs_to :post
  validates :images, presence: true

  mount_uploaders :images, ImagesUploader
end

spec/factories/images.rb

FactoryBot.define do
  factory :image do
    images { [ Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/test.jpg'), 'spec/fixtures/test.jpg') ] }
    post
  end
end

spec ディレクトリ下に fixtures ディレクトリを作成。test.jpg を疑似的に作成し入れてます。

spec/models/images.rb

require "rails_helper"

RSpec.describe Image, type: :model do
  let(:pic_path) { Rails.root.join('spec/fixtures/test.jpg') }
  let(:pic) { Rack::Test::UploadedFile.new(pic_path) }

  context "画像が指定されれば" do
    it "画像がアップロードされる" do
      image = create(:image)
      expect(image).to be_valid
    end

    it "画像(複数)でもアップロードされる" do
      image = create(:image, images: [pic, pic])
      binding.pry
      expect(image).to be_valid
    end
  end

  context "画像が指定されていないと" do
    it "エラーになる" do
      image = build(:image, images: nil)
      expect(image).to be_invalid
      expect(image.errors.messages[:images]).to eq ["can't be blank"]
    end
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】MySQLによる開発

今までいくつかWebアプリケーションを制作したり学習をしてきましたが、
全てDBはデフォルトのSQLiteを使用してきました。

今回新しくアプリケーションを制作する上で本番環境をAWSにしようとするとMySQLの設定が必要・・・
ということで、MySQLを使用したいと思います!
まだまだ初心者なので、間違っているところがあればご指摘お願いします。

MySQLとは

世界で最も利用されているデータベースで、以下のような特徴があります。

  • 大容量のデータベースを高速かつ安定的に利用できる
  • 関連のあるデータ項目が、事前に定義されている
  • データベースは、テーブル単位にデータを保持し、テーブル間の関係も保持する
  • データ項目は列として表現され、関連のあるデータ項目の集合体が行として表現されたテーブルが形成される(Excelのタイトル付きの表のようなイメージ)
  • リレーショナルデータベースとのやりとりには、SQLが使用される
  • 日本語に対応している

SQLiteとの違い

Railsをインストールして、デフォルトで使用されるRDBMSはSQLite
SQLiteは以下のような特徴がある

  • それほど大きなデータベースではなく、個人利用レベルのデータベースの利用を想定している
  • リレーショナルデータベースとのやりとりには、SQLが使用される
  • 著作権が放棄されパブリックドメインで誰もが自由に利用できる
  • 消費メモリも少ないため、処理能力が低い小型デバイスでも組み込める

巨大なDBを扱う場合はMySQLを使用した方が良いらしい

MySQLで開発をしてみる

RailsはデフォルトではSQLiteを使用するため、
MySQLを使用するためには立ち上げの際に指定してあげる

rails new アプリケーション名 --database=mysql

省略もできる

rails new アプリケーション名 -d mysql

こうすることで、RDBMSをMySQLに変更することができるらしい。
念のためGemfileを確認・・・
指定しない場合は

Gemfile
# Use sqlite3 as the database for Active Record
gem 'sqlite3'

となるのに対して、指定してあげると

Gemfile
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.3.18', '< 0.5'

となりました!!

しかし、問題が・・・。
rails new アプリケーション名 -d mysqlをした段階でエラーが発生。。。

・
・
(たくさんのログ)
・

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mysql2-0.5.3/ext/mysql2
/Users/mariko/.rbenv/versions/2.6.5/bin/ruby -I /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r ./siteconf20201104-33105-i9fwuc.rb extconf.rb
checking for rb_absint_size()... yes
checking for rb_absint_singlebit_p()... yes
checking for rb_wait_for_single_fd()... yes
-----
Using mysql_config at /usr/local/opt/mysql@5.7/bin/mysql_config
-----
checking for mysql.h... yes
checking for errmsg.h... yes
checking for SSL_MODE_DISABLED in mysql.h... yes
checking for SSL_MODE_PREFERRED in mysql.h... yes
checking for SSL_MODE_REQUIRED in mysql.h... yes
checking for SSL_MODE_VERIFY_CA in mysql.h... yes
checking for SSL_MODE_VERIFY_IDENTITY in mysql.h... yes
checking for MYSQL.net.vio in mysql.h... yes
checking for MYSQL.net.pvio in mysql.h... no
checking for MYSQL_ENABLE_CLEARTEXT_PLUGIN in mysql.h... yes
checking for SERVER_QUERY_NO_GOOD_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_NO_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_WAS_SLOW in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_ON in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_OFF in mysql.h... yes
checking for my_bool in mysql.h... yes
-----
Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load
-----
-----
Setting libpath to /usr/local/opt/mysql@5.7/lib
-----
creating Makefile

current directory: /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mysql2-0.5.3/ext/mysql2
make "DESTDIR=" clean

current directory: /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mysql2-0.5.3/ext/mysql2
make "DESTDIR="
compiling client.c
compiling infile.c
compiling mysql2_ext.c
compiling result.c
compiling statement.c
linking shared-object mysql2/mysql2.bundle
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mysql2-0.5.3 for inspection.
Results logged to /Users/mariko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/mysql2-0.5.3/gem_make.out

An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.

In Gemfile:
  mysql2
         run  bundle binstubs bundler
Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile.
         run  bundle exec spring binstub --all
Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.
       rails  webpacker:install
Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

とりあえず翻訳してみる
「mysql2(0.5.3)のインストール中にエラーが発生し、Bundlerを続行できません。」
・・・わからない・・・。
とりあえずやっつけないと・・・!

解決方法

とりあえずログに書いてあることを実行

$ gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'

ERROR:  While executing gem ... (OptionParser::InvalidArgument)
    invalid argument: --source https://rubygems.org/

Mac へ MySQL をインストール

MySQLをインストールするという部分をすっかり見落としていました。
下記記事を参考にインストール!
Mac へ MySQL を Homebrew でインストールする手順

下記コマンドでインストール

$ brew update
$ brew install mysql

インストールができているか確認

$ brew info mysql

mysql: stable 8.0.22 (bottled)
Open source relational database management system
https://dev.mysql.com/doc/refman/8.0/en/
Conflicts with:
  mariadb (because mysql, mariadb, and percona install the same binaries)
  percona-server (because mysql, mariadb, and percona install the same binaries)
/usr/local/Cellar/mysql/8.0.22 (294 files, 296.5MB) *
  Poured from bottle on 2020-11-04 at 13:55:11
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/mysql.rb
License: GPL-2.0
==> Dependencies
Build: cmake ✘
Required: openssl@1.1 ✔, protobuf ✔
==> Caveats
We've installed your MySQL database without a root password. To secure it run:
    mysql_secure_installation

MySQL is configured to only allow connections from localhost by default

To connect run:
    mysql -uroot

To have launchd start mysql now and restart at login:
  brew services start mysql
Or, if you don't want/need a background service you can just run:
  mysql.server start
==> Analytics
install: 85,249 (30 days), 214,834 (90 days), 813,126 (365 days)
install-on-request: 83,371 (30 days), 209,508 (90 days), 784,193 (365 days)
build-error: 0 (30 days)

バージョン8.0.22がインストールされていた!

MySQLを起動してみる

$mysql.server start
Starting MySQL
 SUCCESS! 

接続して動作確認をしてみる

$mysql -uroot

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

・・・・またまたエラー(泣)
パスワードがないからダメのか?と思い、とりあえず設定します。

セキュリティ設定

$mysql_secure_installation

Enter password for user root:(パスワードを入力する)

パスワードの強度をチェックしてくれるプラグインのセットアップをする?「y」

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: y

検証レベルを選ぶ。

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary                  file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
  • LOW:8文字以上
  • MEDIUM:8文字以上 + 数字・アルファベットの大文字と小文字・特殊文字を含む
  • STRONG:8文字以上 + 数字・アルファベットの大文字と小文字・特殊文字を含む + 辞書ファイルでのチェック

本番環境ようなので、2を選択。
先ほど入力したパスワードの強度が合致していないと再度入力を求められます。
2度パスワードを打ち込むと、これで確定していいんだね?と言われるので「y」

Estimated strength of the password: 50 
Change the password for root ? ((Press y|Y for Yes, any other key for No) : y

New password: 

Re-enter new password: 
Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y

匿名ユーザ( anonymous user )を削除する?「y」

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.

リモートから root ユーザでログインできないようにします。ログインを禁止する?「y」

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

デフォルトで作成されている test という名前のデータベースを削除する?「y」

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.
 - Removing privileges on test database...
Success.

すぐに権限テーブルをリロードして変更を有効にする?「y」

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

とりあえず、設定はできたかな?

もう一度動作確認

$mysql -u root -p
Enter password:(パスワード入力)

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.32 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

う、、動いた!!!泣

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

中身も空になっていました!

一度mysqlからはexitして・・・
停止もして・・・

$ mysql.server stop
Shutting down MySQL
. SUCCESS! 

アプリに戻ってbundle install

同じエラーが・・・・・・・泣

下記記事を参考に進めました!
【Rails】MySQL2がbundle installできない時の対応方法

$ brew info openssl
openssl@1.1: stable 1.1.1h (bottled) [keg-only]
Cryptography and SSL/TLS Toolkit
https://openssl.org/
/usr/local/Cellar/openssl@1.1/1.1.1g (8,059 files, 18MB)
  Poured from bottle on 2020-05-21 at 18:53:48
/usr/local/Cellar/openssl@1.1/1.1.1h (8,067 files, 18.5MB)
  Poured from bottle on 2020-11-01 at 12:26:48
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/openssl@1.1.rb
License: OpenSSL
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
  /usr/local/etc/openssl@1.1/certs

and run
  /usr/local/opt/openssl@1.1/bin/c_rehash

openssl@1.1 is keg-only, which means it was not symlinked into /usr/local,
because macOS provides LibreSSL.

If you need to have openssl@1.1 first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"' >> /Users/mariko/.bash_profile

For compilers to find openssl@1.1 you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"

For pkg-config to find openssl@1.1 you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"

==> Analytics
install: 835,013 (30 days), 1,906,254 (90 days), 7,237,126 (365 days)
install-on-request: 136,062 (30 days), 257,990 (90 days), 1,007,070 (365 days)
build-error: 0 (30 days)

この部分が重要らしい。

export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"

下記の二つのコマンドを実行

$ bundle config --local build.mysql2 "--with-cppflags=-I/usr/local/opt/openssl@1.1/include"
You are replacing the current local value of build.mysql2, which is currently nil

$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl@1.1/lib"
You are replacing the current local value of build.mysql2, which is currently "--with-cppflags=-I/usr/local/opt/openssl@1.1/include"

再度bundle install
インストールできました・・・・・!!!嬉泣

まとめ

アプリにMySQLを導入するまでかなり右往左往してしまいました。
読みづらかったらすみません。
少しでも誰かのお力に慣れたら嬉しいです。

参考にさせていただいた筆者の方々、本当にありがとうございました。

途中でSQLiteに変更する手順もあるみたいなので、今まで制作したアプリで試してみようと思います。

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

Railsでアプリを新規作成する

備忘録としてRailsでアプリを新規作成する手順をまとめる。

まずアプリケーション作成するのに Rails new コマンドを使用する。

データベースはMySQL使用。コマンドオプションに -d mysql を末尾に書く
6.0.0はバージョン

ターミナル
% rails _6.0.0_ new アプリ名 -d mysql

これで新規アプリができる。

config/database.yml 

# encoding: utf8mb4を

encoding: utf8  に変更
これは保存形式の変更をする為

データベース作成
アプリと紐ずくデータベースを作るために以下を実行

ターミナル
% rails db:create

rails s で作動すればOK

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

sql_modeのonly_full_group_byを守っていないためにエラーになった件

問題

スクリーンショット 2020-11-04 午後1.44.09.png

グラフを表示しようとしたら下記のエラーが表示された。

Mysql2::Error: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'pfc-master_production.posts.created_at' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by: SELECT `posts`.`created_at` FROM `posts` WHERE `posts`.`user_id` = 8 GROUP BY date(created_at)
charts_controller.rb
@dates = dates_calorie.map { |dates| dates.created_at }

エラーログを読み解く

Mysql2::Error: Expression #1 of SELECT list is not in GROUP BY clause and 
Mysql2 :: Error:SELECTリストの式#1がGROUP BY句になく、

contains nonaggregated column 'pfc-master_production.posts.created_at' 
which is not functionally dependent on columns in GROUP BY clause;
GROUPBY句の列に機能的に依存していない非集計列 
'pfc-master_production.posts.created_at'が含まれています

this is incompatible with sql_mode=only_full_group_by: 
これはsql_mode = only_full_group_byと互換性がありません

SELECT SUM(`posts`.`calorie`) AS sum_calorie, date(created_at) AS date_created_at FROM `posts` WHERE `posts`.`user_id` = 8 GROUP BY date(created_at) ORDER BY created_at DESC

mapメソッドとは?

・mapメソッドを一言で表すと「各要素へ順に処理を実行してくれるメソッド」です。
・要素それぞれにアクセスし、指示した処理を行ってくれる。
【Rails入門】mapメソッドを完全攻略!配列操作の基礎を学ぼう

配列変数.map {|変数名| 具体的な処理 }

今回の場合
@dates = dates_calorie.map(配列変数) { |dates(変数名)| dates.created_at(処理) }

dates_calorieという配列には何が入っているのか?

dates_calorie = current_user.posts.group("date(created_at)").select(:created_at)

ログイン中ユーザーの投稿が1日に複数ある場合、その日ごとでグループ化しcreated_atカラムを取得したものが入っている。
すなわち、1日につき1つのcreated_atカラムが取得され、1日ごとのcreated_atカラム情報たちが配列の中に入っている。

このような情報を取得する理由は、1日ごとの体重を表示するグラフを実装するためだ。
1日に複数の投稿があった時に、グラフに表示するのは1日につき1つだけにしたいためだ。

✖️ [2020-11-09 00:00:00, 2020-11-09 00:11:00, 2020-11-10 00:12:00]
○ [2020-11-09 00:00:00, 2020-11-10 00:12:00, 2020-11-11 00:12:00]

mapメソッドを使ってどのような処理をしているのか?

@dates = dates_calorie.map(配列変数) { |dates(変数名)| dates.created_at(処理) }

上記で説明したとおり、配列dates_calorieには1日ごとのcreated_atたちが配列の中に入っていて、それをmapメソッドを使って配列の中の各要素をdatesという変数で1つずつ取り出し、created_atを取得している。

それらをgonでグラフを表示させるchart.jsに渡してグラフで使うために、インスタンス変数に代入している。

なぜ今回エラーになってしまったのか?

only_full_group_byがわからなかったため、リファレンスでonly_full_group_byについて調べた。

sql_mode = only_full_group_byとは?

GROUP BY 句で名前が指定されていない非集約カラムを、選択リスト、HAVING 条件、または (MySQL 5.6.5 以降で) ORDER リストが参照するクエリーを拒否します。

ONLY_FULL_GROUP_BY が有効な場合、次のクエリーは無効です。1 番目は、選択リスト内の非集約の address カラムが GROUP BY 句で名前を指定されておらず、2 番目は、HAVING 句の max_age が GROUP BY 句で名前を指定されていないため、ともに無効になります。

MySQLリファレンス ONLY_FULL_GROUP_BYとは?

sql_modeとは、MySQLの公式ルールのようなもので、only_full_group_byはMySQL5.6.5以降で新たに設定されたルールらしい。

・only_full_group_byというルールを守っていないためにエラーになったと考えられる。
・GROUP BY句で名前をしていないためエラーになってしまったと考えられる。

GROUP BY句とは?

・「GROUP BY」は、グループ化を行うために使用される命令。
・主に「種類ごとに集計関数を使用する」などといった形で使用するケースが多い。
・例えば「チームごとの人数を調べる」という場合など。
【SQL】GROUP BYで自在に集計!集計関数やHAVINGと合わせて使おう

select [表示する要素名] from [テーブル名] GROUP BY [グループ化する要素名];
SELECT team, COUNT(team) FROM user GROUP BY team;

対策

上記のことから、考えられる対策は2通りだ。

① SQLモードのonly_full_group_byを遵守するため、GROUP BY句を使ったコードに変更する。
② my.cnf(MySQLの設定ファイル)を変更し、only_full_group_byという制約を外す。

今回は②の方法を選択した。
理由は、時間がないためだ。

現在webエンジニアへの転職活動を始めるところで、とにかく一刻も早く企業の採用担当者の方にアプリを見せられる状態にしなくてはならない。
本当は①が望ましいが、今回は時間優先のため②を選択した。

今回の解決方法

my.confの場所を探す
[naota@ip-10-0-0-32 ~]$ mysql --help | grep my.cnf
                      order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf 

my.confを開く
[naota@ip-10-0-0-32 ~]$ vi /etc/my.cnf

読み込み専用ファイルを上書き保存する時に使う
:w !sudo tee %

my.confに下記の記述を追加した。

my.conf
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

NO_ZERO_IN_DATENO_ZERO_DATEといったsql_modeを調べてみたが、これら自体にonly_full_group_byをOFFにする意味はないとわかった。
おそらく上記の設定でonly_full_group_byにしたのOFFにしたのではなく、only_full_group_byを含んで書いていないから設定されていないのだと考えられる。

only_full_group_by追加したら冒頭のエラーが発生したので、この仮説は正しかった。

上記の設定を反映させるためにMySQLを再起動した。

MySQL5.7にアップデートしたらonly_full_group_byでエラーになった

MySQLを停止
[naota@ip-10-0-0-32 ~]$ sudo systemctl stop mysqld.service

MySQLを起動
[naota@ip-10-0-0-32 ~]$ sudo systemctl start mysqld.service

グラフが表示されました!
スクリーンショット 2020-11-04 午後1.45.51.png

ソースコード

charts_controller.rbはグラフを表示させるためのコントローラー。

charts_controller.rb
def index
    # カロリー
    @sampleuser = User.find_by(id: 3)
    if user_signed_in?
      # 日付ごとで分けてカロリー合計を算出
      sum_calorie = current_user.posts.group("date(created_at)").sum(:calorie)
      # 日付ごとのカロリー合計がハッシュの形なので値を取得して配列に入れて変数に代入
      array_calorie = sum_calorie.values
    else
      sum_calorie = @sampleuser.posts.group("date(created_at)").sum(:calorie)
      array_calorie = sum_calorie.values
    end
    # gonを使ってデータをjs側に渡す
    gon.data = []
    # mapメソッドで日付ごとのカロリー合計を1つずつ取り出す
    # mapメソッドの使い方 → 配列変数.map {|変数名| 具体的な処理 }
    gon.data = array_calorie.map { |calorie| calorie }

    if user_signed_in?
      # 日付ごとにまとめてそのうちcreated_atカラムだけ取得。配列の形で格納されている
      dates_calorie = current_user.posts.group("date(created_at)").select(:created_at)
    else
      dates_calorie = @sampleuser.posts.group("date(created_at)").select(:created_at)
    end

    gon.date = []
    @dates = dates_calorie.map { |dates| dates.created_at }
    # each文で日付の表記を1つずつ取り出して変える
    @dates.each do |a|
      gon.date << a.strftime("%Y年%m月%d日")
    end


    # 体重
    if user_signed_in?
      gon.weight = current_user.posts.group("date(created_at)").select(:weight).map { |weight| weight[:weight] }
    else
      gon.weight = @sampleuser.posts.group("date(created_at)").select(:weight).map { |weight| weight[:weight] }
    end
  end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】配列内の偶数or奇数のカウント方法

配列内にある値について、偶数もしくは奇数の数のカウントをするメソッドを作成します。

値が偶数の場合はeven?メソッドを使用し、奇数の場合はodd?メソッドを使用します。
※以下は偶数パターンのみ記載しています。evenをoddに変換することで奇数のカウントに使用できます。

使用例

Ruby
number = 2
number.even?

// →trueが返却される

解答例

Ruby
def count(number)
  count = 0
  number.each do |num|
    if num.even?
      count += 1
    end
  end
  puts count
end

解説

今回はメソッド作成のため、まず一行目にメソッド名と仮引数のnumberを記述しています。

Ruby
def count(number)

上述の通りeven?メソッドはtrueもしくはfalseのみ返却するため単体ではカウントできません。
そのため以下のように配列に対してeven?メソッドを使用するとエラーが発生します。

失敗例

Ruby
hairetsu = [1,2,6,8,10]
hairetsu.even?

// NoMethodError (undefined method `even?' for [1, 2, 6, 8, 10]:Array)

なお、二行目でcount = oと記載していますが、繰り返し処理の前に記載することでカウントの初期化をしています。

カウントするためにはeachメソッドによる繰り返し処理で行います。
流れとしてはtrueだった場合にはカウントアップし、falseの場合は何もせず次の要素の判定に進む、といったものです。

Ruby
number.each do |num|
  if num.even?
    count += 1
  end
end

最後にカウントしたものを出力するためにputsメソッドを使用しています。

Ruby
puts "count"

これで汎用的な配列内の偶数or奇数のカウントメソッドを作成することができました。
メソッドを呼び出す際はメソッド外から実引数に配列を指定してコールすることをお忘れなく。

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

ユーザー登録でなぜメールアドレスが必要か

大体の流れ

多くのwebサイトでは以下のような手順が取られている
1 : 新規登録画面でメールアドレスの入力を要求

2 : そのメールアドレスに対して本登録用のリンクを記載したメールを送信

3 : リンクから遷移した先のフォームに必要事項を記入

4 : 登録完了

なぜemailを使った認証が必要なのか?

不正登録を防ぐ

もし、メール受信なくユーザー登録が完了してしまった場合、無限にアカウントが作れてしまう。
その場合、不正に利用されてしまう可能性があるので手順を複雑化し、不正利用を防ぐのが狙い。

メールアドレスが有効か確認する

ユーザーとの連絡手段を確立するため。
もし、入力されたメールアドレスが間違っていた場合、ユーザーとの連絡が確立できず「ユーザー登録が完了した」事すらも伝えることができない。
よって、メールアドレスが正常に受信できるかどうか確認している

Railsでの認証

Railsでログイン認証をするにはdeviseというgemを使って実装するみたい。

[Rails] deviseの使い方(rails5版)

参考

参考にしたブログ

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

【Ruby on Rails】DB保存時のカラムの制約(代表4つ)

この記事を書いた背景

データ保存時の制約の指定の仕方が、マイグレーションファイルとモデルファイルとそれぞれに書く方法があることを知ったので、改めて代表的なバリデーションをまとめてみました。(結構前に勉強したことですが、改めてqiitaにまとめていこうと思います。)

実施環境

macOS Catalina 10.15.7
VS Code 1.50.0
Ruby 2.6.5
Rails 6.0.0

①NOT NULL制約

→データが空の状態では保存ができない制約

【マイグレーションファイルへの書き方】
null: false

20200919092740_create_item.rb
t.string :address, null: false

【モデルファイルへの書き方】
presence: true

item.rb
validates :address, presence: true

②一意性制約

→重複して同じデータを保存できない制約

【マイグレーションファイルへの書き方】
unique: true

20200919092740_create_item.rb
t.string :address, unique: true

【モデルファイルへの書き方】
uniqueness: true

item.rb
validates :address, uniqueness: true

③主キー制約

→データが空の状態では保存ができない、かつ、重複して同じデータを保存できない制約
(NOT NULL制約かつ一意性制約)
(idカラムには自動で設定される制約)

【マイグレーションファイルへの書き方】
primary_key: true

20200919092740_create_item.rb
t.string :address, primary_key: true

※モデル名のID以外を主キーにわざわざ設定することは少ないと思うので、あまり使わないと思います。
※モデルファイルへの書き方は調べた限りどうやらなさそうです。

④外部キー制約

→外部キーとなるデータが必ず存在しないとデータ保存できない制約

【マイグレーションファイルへの書き方】
foreign_key: true

20200919092740_create_item.rb
t.string :address, foreign_key: true

※モデルファイルへの書き方は調べた限りどうやらなさそうです。

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

充電器を刺したらエラーが解決した話を頭の片隅に置いて欲しい

arienai.jpg

グリード「ありえないなんてことは、ありえない」 ーーー鋼の錬金術師

はじめに

正直しょーもない記事ですが、万が一でも誰かのためになればいいなと思いまとめました。

背景

メンターとしてプログラミングに関するエラー対応をしていた際に、以下のようなエラーに遭遇しました。

Rails5.2.4を使用し、RSpecの実行をしようとした際、cannot load such file -- spec_helper (LoadError)と詳しくは覚えていませんが、何かしらのNameErrorが同時に起こりました。

生徒さんが設定したファイル・フォルダ名やディレクトリの位置などが全て合っていることを確認し、当該アプリケーションを一度githubに上げて頂いたのち、私のPCでclone、テストを通してみました。

皮肉にも自分の環境では無事にテストが通りました。
どうしたこと、何か設定が違うのかと頭を悩ませていました。。。

解決へ・・・

その際、Zoomで会話をしていたのが生徒さんのPCの充電が残り1ケタで、自分の方がほぼフル充電であることに気づきました。
生徒さんは充電器を取りに行かれて、ご自身のPCに差し込んでもう一度テストを実行しました。

その結果、なんとエラー無くテストが実行されたのです・・・。

まさかーと思いましたが、私以外にももう一人同時にチェックしていた方がいらしたので、コーディングは間違いなく一切しておりません。

PCのパフォーマンスなど、そういった影響もあるのかな・・・?と思いました。
もし、何をどうやってもダメな場合、充電を十分にした上でプログラムを実行してみるのも最終手段として良いのかなと思いました。

似たような記事が見つからなかったので、一応残しました。
何か詳細が分かる方がいらしたら、是非コメントにお願いします。

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

Next.jsアプリをChromeで開きっぱにするとRailsアプリで "/_next/webpack-hmr" のRoutingエラーになる話

問題

rails s でローカルサーバーを起動したところ、20秒ごとにNo route matches [GET] "/_next/webpack-hmr" のエラーが出た。

1日前まで普通に動いていたのでおかしいなあと思いつつググってみるとこんなIssueがGitHubに。
ActionController::RoutingError (No route matches [GET] "/_next/webpack-hmr") every 20secs

原因と対策

別で作業をしていたNext.jsのアプリのChromeタブが原因ぽいです。

解決方法はNext.jsアプリのChromeタブを閉じるだけですね。

所感

このIssueなかったらこの人みたいにめちゃくちゃ時間かかったかも。。。

ということで秒速で解決できますが、一見不可思議な問題なので一応日本語ソースとして投稿しておきました。

お役にたてれば幸いです。

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

【Rails】TwitterAPI使用時に、httpで送られてくる画像のURIをhttpsに変換した方法

はじめに

yukiと申します。DMMWEBCAMP にお世話になって、今は WEB エンジニアとして勤務しつつ、自分で仲間を集めてサービス開発したり、プログラミングの家庭教師したり毎日エンジニアライフをエンジョイしています。

未経験から目指している方向けのサポートやエラー質問なども引き受けておりますので、気になった方は DM へお願いします。

前提と問題

RailsをAPIとして活用していて、gem twitterを使って画像収集をしていた際に問題が起こりました。
twitter APIが取得してくる画像のURIがhttpから始まっており、せっかくhttps通信ができているサイトに「安全ではありません」の警告が出てしまいました。

ちなみに、その時コンソールにはこんなエラーが出ていました。

Mixed Content: The page at 'https://hogehoge.com' was loaded over HTTPS, but requested an insecure image 'http://hogehoge.net/hoge.jpg. This content should also be served over HTTPS.

mixed contentとか言うらしいです。
最初は今回まとめる方法が思いつかず、フロントの方で何とかしていましたが、本来バックでやるべきなので修正しました。

対策

examples_controller.rb
  def show
    client = Authorization.init # 独自クラスです。クライアントの情報を入れています。
    @data = client.search("#収集したいハッシュタグ", result_type: "recent").take(4).collect do |tweet|
      {
        "image": "#{tweet.user.profile_image_url.to_s.sub('http', 'https')}",
        "name": "#{tweet.user.name}",
        "text": "#{tweet.full_text}",
        "tweet_link": "#{tweet.uri}"
      }
    end
    render json: {tweet: @data}
  end

何をしているか一応ご説明すると、特定のハッシュタグのついたツイートを4件収集し、jsonデータとして返しています。
今回重要なのは"image": "#{tweet.user.profile_image_url.to_s.sub('http', 'https')}",の部分です。

これは他のエンジニアの方に教えていただいたのですが、rails consoleでuser.profile_image_urlを見てみると、ちょっとよくわからないデータの形(?)で結果が返りますが、to_sすることで綺麗なURLになりました。

そこに、subメソッドを当てて変換している感じです。

注意としてはgsubではなくsubを使っていることです。
理由としては、万が一画像のURLの一部(先頭以外)に"http"と言う文字列があった場合に誤変換して、画像が表示されなくなってしまうからです。

誰かの参考になれば幸いです。

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

【Bootstrap】基本のクラスまとめ

概要

最近、Bootstrapを使ってフロントを作っています。
自分への備忘録としてこれまでに使ってきたクラスをまとめておきます。

参照
Bootstrap (ver. 4.5) : https://getbootstrap.jp/


ブレークポイント

  • sm : 576px
  • md : 768px
  • lg : 992px
  • xl : 1200px


レイアウト

container

  • 固定幅のコンテナ
  • 各ブレークポイントごとに最大幅が変わる


row

  • グリッドシステムの行を決める要素
  • 子にcolを持つことで真価を発揮


col

  • グリッドシステムの列を決める要素
  • rowの子要素として使う
  • col-sm-3
    • sm : 576px未満で縦並び
    • 3 : (3/12 = 25% の幅)


ボタン

btn

  • ボタンを作る


btn-primary

  • 青ベースのボタンを作る
    • danger : 赤ベース
    • secondary : 灰色ベース


padding, margin

p, m

  • p : padding
  • m : margin


方向

  • t : top
  • b : bottom
  • l : left
  • r : right
  • x : left and right (x軸方向)
  • y : top and bottom (y方向)
  • (blank) : 4方向
  • 例) : pt-○ : padding-top: ○;


サイズ

  • $spacerに対する比で決まる。デフォルトで1rem=16px
  • 0 : $spacer * 0 (=0)
  • 1 : $spacer * 0.25 (=4px)
  • 2 : $spacer * 0.50 (=8px)
  • 3 : $spacer * 0.75 (=12px)
  • 4 : $spacer * 1.00 (=16px)
  • 5 : $spacer * 1.25 (=20px)
  • auto : margin: auto;
  • 例) : pt-1 : padding-top: 4px;

Bootstrapの公式リファレンスで示されている代表的な例は下記のとおりです。

.mt-0 {
  margin-top: 0 !important;
}

.ml-1 {
  margin-left: ($spacer * .25) !important;
}

.px-2 {
  padding-left: ($spacer * .5) !important;
  padding-right: ($spacer * .5) !important;
}

.p-3 {
  padding: $spacer !important;
}

ちなみに!important をつけると、CSSファイルの読み込み順に関係なく、スタイルを適用させることができます


  • primary : 青
  • secondary : ライトグレー
  • success : 緑
  • danger : 赤
  • warning : 黄
  • info : 水色
  • light : かなり薄いグレー
  • dark : ダークグレー


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

【Rails×TwitterAPI】任意のハッシュタグがついたツイートを集める

自己紹介

プログラミングスクールDMMWEBCAMPを卒業し、物流×ITの自社開発企業でWebエンジニアをしておりますyukiと申します。
現在5ヶ月目に突入しました。

こんな記事も書いています。

現在は、勉強会の主催や副業として同校のメンター、簡単なプログラミングの家庭教師、趣味としてチームでサービス開発などをしています。

読むとわかること

こちらのアプリを作った際に、任意のハッシュタグ付きのツイートを収集する必要があったので、記事としてまとめてみました。本記事を読むと、Railsを使ってハッシュタグ付きのツイートを集めることができます。

Twitterのデベロッパーとして登録する

まず、そもそもTwitterのAPIを触らせてもらうために、承認してもらう必要があります。

私は以下の記事を参考に行いました。
https://qiita.com/kngsym2018/items/2524d21455aac111cdee

英語はそんなに得意ではないので、日本語のメール+Google翻訳とチャット手直しした英語のテキストを送り、3回ほどメールでやりとりしたのち、申請から4日ほどで承認されました。早い方だと思うのですが、「危険なことをしない」「趣味として使うだけ」「収益をあげない」このポイントを押したら許可していただけたように思います。

デベロッパーのページから、APIを叩く際のクライアントとして必要な情報を発行、コピーする

無事認証にされましたら、デベロッパーのページからAPIを叩く際に必要な情報(あとで.envとかに置きます)を取りに行きましょう。
登録ができていれば、https://developer.twitter.com/en/portal/dashboard のページの、自分のアプリ名を押し、Keys and Tokensという場所をクリックすれば表示されます。

  • API Key
  • API Secret Key
  • Access Token
  • Access Token Secret

以上の4つです。あとで任意の名前で.envに入れておいてください。

APIを叩く際に、クライアントのインスタンスを作成する

さて必要な情報も揃ったし、APIを叩くリクエストのコードを書こう!という気持ちですが、そのまま叩きに行くとクライアントの情報がないためにエラーが出ます。
叩きに行く際に、クライアントの情報を持っていけるようにしておきましょう。
こんな感じにしてみました。

twitter_service.rb
module TwitterService
  extend ActiveSupport::Concern
  class Authorization
    def self.init
      client = Twitter::REST::Client.new do |config|
        config.consumer_key        = ENV["API_KEY"]
        config.consumer_secret     = ENV["API_SECRET"]
        config.access_token        = ENV["ACCESS_TOKEN"]
        config.access_token_secret = ENV["ACCESS_TOKEN_SECRET"]
      end
      return client
    end
  end
end 

これをconcernなどに切り出して、毎回呼ばれるようにしています。

実際に収集している記述

まずはコードです。
```ruby:twitter_controller.rb
include TwitterService

def show
client = Authorization.init

data = client.search("#プログラミング", result_type: "recent").take(5).collect do |tweet|
  {
    "image": "#{tweet.user.profile_image_url.to_s.sub('http', 'https')}",
    "name": "#{tweet.user.name}",
    "text": "#{tweet.full_text}",
    "tweet_link": "#{tweet.uri}"
  }
end
render json: {tweet: data}

end
```

解説します。
client = Authorization.init
さっきのクライアント情報を呼び出します。APIを叩く許可ですね。

data = client.search("#プログラミング", result_type: "recent").take(5).collect do |tweet|
クライアントの情報を生かして、tweetを検索します。検索結果はrecenttake(5)つまり、新しいもの順で上から5件、そしてそれを集めて変数に入れます。

"image": "#{tweet.user.profile_image_url.to_s.sub('http', 'https')}",などの情報
tweetのなかの取得できる情報はいくつかあるのですが、imageやnameと言ったものを収集してフロントに渡したいので、このように取得しています。
こうやって取得すれば、依然廃止されてしまったtwitterのハッシュタグ収集ウィジェットみたいなのが作れますよ。
参考

tips

.to_s.sub('http', 'https')の部分ですが、フロントに渡すimageのurlがhttpだと、https通信をしているサイトでも、まれに安全なページではないという警告が出ます。
改善のために、urlのhttpという文字をhttpsにしています。これでも画像はちゃんと表示されます。(根本的な解決になっているかは不明)

実装の際に調べつつやって苦労したのでまとめてみました。
詳しくは、ちゃんとTwitter APIとgemのドキュメントを読んでください!

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