- 投稿日:2019-10-26T20:15:19+09:00
Rails チュートリアル 第11章 まとめ
メールアドレスを使った認証の方法
8,9章の認証は
----.authenticate?(=====)
とするなら
sessionを使った認証では、----の部分がDBにあるpassword_digestであり、===の部分はUserに入力してもらったpasswordだった
cookiesを使った認証では、---はDBにあるremember_digestであり、===はcookiesに保存されているremember_token今回の場合、
---の部分は
まず、DBに認証用のdigestを保存するために、開発側で平文を用意し、ハッシュ値をかけてDBに保存するユーザーに送ったメールにはactivation_tokenとユーザーのemail情報を含めている
ユーザーがlinkをクリックすると、GETリクエストが送られたとき、userのemail情報がparamsに格納されているので、それを使って、User情報を取得する、そのUser情報からDBのactivation_digestを取得するそして、===にはactivation_tokenがそのまま入る、ここはsessionやcookiesと変わらない
そのactivation_digestとactivation_tokenを認証するsignup (必要事項の記入) =
before_action (tokenの発行、ハッシュ化してactivation_digestとしてDBに保存) ->
create action (User情報を格納、Userに認証用メールを送信,emailとactivation_tokenが送信) =
User.mailer (account_activationメソッドが呼び出される、controllerに相当) ->
app/views/user_mailer/account_activation.text.erb(メール文面,viewに相当) ->
Userがリンクをクリック = GETリクエストが送られる ->
ルーティングが作動 ->
accout_activation controllerのedit action (実際の認証)リクエストが送られた時のルーティングとコントローラを準備する必要がある
ルーティング
GET /accout_activations/:id/edit
:id = token
コントローラー
find_byでparams[:emailを取得
authenticated?で認証
params[:id]でtokenを受け取るattr_accessor activation_token
一時的に保存し、忘れる前にメールで送る
メソッド参照
メソッドをあとで作る
before_create signupの時にだけ反応するコールバック処理
before_save 保存する前に必ず行うコールバック処理
このコールバック処理をメソッド参照で行うことができるuser.rb#before_createなので、signupする直前に実行 def create_activation_digest self.activation_token = User.new_token #ランダムなトークンを発行し、attr_accessorで作った仮想的な属性に入れておく self.activation_digest = User.digest(activation_token) #そのtokenをハッシュ化させたものをDBのdigestに保存 #正確にはdigestに代入されたあと、createで反映されるのでDBが保存される endメールを送信 - GETリクエストを送る
app/mailers/application_mailer.rbdef account_activation @greeting = "Hi" mail to: "to@example.org" # => return mail object #中身はapp/views/user_mailer/account_activation.text.erbと #app/views/user_mailer/account_activation.html.erbとなり #account_ativationを呼び出した何かがmail objectを実行する ??? endtextとhtml.erbはviewに似ている
Mailer = Controller
Mailの文面 = ViewメールのURLには平文とともにメールアドレスも入れないと誰の平文か分からない
リンクを作る(メールの文面)時に
edit_user_url(user)
が
http://www.example.com/users/1/editになるらしいが、どこでやった?その考え方を使い、
http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit
としたいさらに、メールアドレスも含めた文面にしたいので、
account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com
としたいので、
引数にemailも加えて、最終的にedit_account_activation_url(@user.activation_token, email: @user.email)となる
コントローラーの実装
create controller -
User登録をする時に、アカウント認証を行いたいのでcreateアクションに組み込む
今まではsignup(userに必要事項を記入)したら、すぐにloginできる仕様だったuser_controllerdef create @user = User.new(user_params) if @user.save log_in @user flash[:success] = "Welcome to the Sample App!" redirect_to user_path(@user) else render "new" end end今回は、これを修正して
user_controllerdef create @user = User.new(user_params) if @user.save UserMailer.account_activation(@user).deliver_now #user_mailer.rbのaccout_activationが呼び出されている #deliver_nowは返ってきたメールオブジェクトを実際に送信する flash[:info] = "Please check your email to activate your account." redirect_to root_url else
edit actionの実装
accout_activation_controller@user = User.find_by(email: params[:email]) # メールからUser情報を取得 @user.authenticated?(params[:id]) #DBのactivation_digestとメールから取得したactivation_tokenを照合するしかし、authenticatedメソッドはrememberで固定してしまっているので、DRYの観点からauthenticated?メソッドを抽象化しなければ使えない
元々のauthenticated?メソッドは
def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) endだったが、以下のように変更
def authenticated?(attribute, token) digest = self.send("#{attribute}_digest") #sendを使えば動的にメソッドを変更できる、式展開をより実際的に使うことができる return false if digest.nil? BCrypt::Password.new(digest).is_password?(token) endこれでeditアクションでauthenticated?を使うことができる
account_activation.cotrollerdef edit user = User.find_by(email: params[:email]) if user && !user.activated? && user.authenticated?(:activation, params[:id]) #userはnilチェック #!user.activated?は何回もクリックされないために、activatedがfalseである時だけ実行 #authenticated?は認証 user.update_attribute(:activated, true) #activatedを有効に user.update_attribute(:activated_at, Time.zone.now) log_in user flash[:success] = "Account activated!" redirect_to user else flash[:danger] = "Invalid activation link" redirect_to root_url end end
- 投稿日:2019-10-26T19:18:04+09:00
railsアプリに投稿されたYouTubeURLを自動的に埋め込み表示させる方法~無理やり編~
1概要
ユーザーがYouTubeにアップロードされた動画のURLを投稿し、それを自動で埋め込み動画としてそのサイトに表示させたい。これやりたい人地味に多いのでは?
実装しようとして意外と記事がなかったので僕がやったやり方を共有。
今回はrailsアプリに投稿されたYouTubeURLがたとえ埋め込みURLでなくても自動でサイトに埋め込み表示させる方法を紹介します。環境ruby 2.5.6 Rails 6.0.0こんな感じのものができる予定
1.1. YouTubeURLの種類
YouTubeURLは僕がパッと見た感じ3種類のURLが存在します
PCでのYouTube閲覧時のURL 一番スタンダード?
https://www.youtube.com/watch?v=DOEk-0MeQbI
共有用URL (スマホで視聴中の動画を友達に共有するときはこれ)
埋め込みURL (これをHTMLに貼り付ければそのページで埋め込み動画になる)
https://www.youtube.com/embed/DOEk-0MeQbI
1.2. どう実装するか
この3つのURLには共通して末尾に11桁の数字があります。これはGoogleがYouTubeに投稿された全ての動画に割り振っている通し番号でvideo_idと呼ばれます。
つまり上の2種類のURLが投稿されたら末尾の11桁を取り出し、HTMLに設置した以下の埋め込みURLの末尾にその数字を反映させれば埋め込み動画になるはずです。埋め込みURLの末尾11桁に投稿されたurlの末尾11桁を代入→ https://www.youtube.com/embed/<11桁のvideo_id>
2 実装
2.1 事前準備
それではまずここで使うYouTube動画を投稿するだけのサンプルアプリを作っていきます。
cd cd desktop rails new YouTubeApps投稿周りの機能をscaffoldを利用して一瞬で作りましょう。
cd YouTubeApps rails generate scaffold post body:text youtube_url:string rails db:migrateこれで投稿周りの機能ができたと思います。それではここからpostsテーブルのyoutube_urlカラムにどんなYouTubeURLが投稿されても埋め込みURLに変えていく記述を書いていきます。
2.2 本題
それではここからyoutubeURLから末尾のvideo_idのみを取り出し埋め込みURLにする実装をしていくのですが、Postsコントローラーのcreateアクションへrubyのコードを記述して行きます。
以下のように、scaffoldによって作成されたコントローラーのcreateアクションにコードを追記してください。posts_controller.rbdef create @post = Post.new(post_params) #追記した部分ここから url = params[:post][:youtube_url] url = url.last(11) @post.youtube_url = url #ここまで respond_to do |format| if @post.save format.html { redirect_to @post, notice: 'Post was successfully created.' } format.json { render :show, status: :created, location: @post } else format.html { render :new } format.json { render json: @post.errors, status: :unprocessable_entity } end end endここで追記した以下の部分について解説をします。
url = params[:post][:youtube_url] url = url.last(11) @post.youtube_url = urlparamsとは
まず一行目のparamsを含む一文について
url = params[:post][:youtube_url]paramsとはrailsに用意されたgetやpostで送られてきた値を格納するためのメソッドです。
paramsのわかりやすい解説記事
ここでは投稿する際にformによってcreateアクションにpostの内容が[:body][:youtube_url]として送られています。
posts_controllerurl = params[:post][:youtube_url]よって、上記のコードにより変数urlに対してparamsに格納している中身であるformによって送られてきた[:youtube_url]の値を格納します。
現在の変数urlの中身は送られてきたurl全文が格納されている状態です。現在の変数urlの中身
https://www.youtube.com/watch?v=DOEk-0MeQbIlastメソッドについて
ここから二文目のコードで語尾の11桁のvideo_idを取り出します。
url = url.last(11)rubyの配列にはlastというメソッドがあります。これを呼ぶことで配列に対してその最後の要素を取得することができます。また、(n)の中に引数として0以上の値を渡すことで、最後のn個の要素を取得することができます。
a=[0,1,2,3] a.last(2) #=>[2,3]つまりこの
url = url.last(11)で変数urlに格納されていたyoutube_urlの値のなかで最後の11桁の値のみを取り出し、改めて変数urlに格納するという処理をしています。
最後の三文目
@post.youtube_url = urlここではviewで使用するインスタンス変数@postへ↑の変数urlの値を格納することでviewで@post.youtube_urlの値を呼び出すとvideo_idの値を返すようにします。
viewの処理
viewではposts/showおよびposts/indexページに埋め込みurlを用意しておきます。
posts/show.html.erb<p id="notice"><%= notice %></p> <p> <strong>Body:</strong> <%= @post.body %> </p> <p> <strong>Youtube url:</strong> <%= @post.youtube_url %> </p> <%= link_to 'Edit', edit_post_path(@post) %> | <%= link_to 'Back', posts_path %>上記の<%= @post.youtube_url %>の部分を以下のように書き換えます。
<iframe width="560" height="315" src="https://www.youtube.com/embed/<%= @post.youtube_url%>" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>YouTubeの埋め込みurlを用意し末尾のvideo_idが入る部分に@post.youtube_urlの値が入るようにすることで、viewの中でどんなYouTubeURLが来ても埋め込み動画にするようにしています。
posts/index.html.erbも同様に
posts/index.html.erb<% @posts.each do |post| %> <tr> <td><%= post.body %></td> <td><%= post.youtube_url %></td> <td><%= link_to 'Show', post %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %>の<%= post.youtube_url %>部分に以下のコードを代入します
<iframe width="560" height="315" src="https://www.youtube.com/embed/<%=post.youtube_url%>" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>これでこのように表示されていれば完璧です。
終わり
以上です。少し無理やりですが、YouTubeURLを投稿されたら埋め込み動画にするrailsアプリを作ってみました。
謝辞
ありがとうまこっちゃん
- 投稿日:2019-10-26T18:50:45+09:00
RailsのFactoryBot で transient / association / before create / aftter create などを使わずに ほぼtrait だけで関連データを生成する例 ( #Rails )
- コードはイメージである
- DSLなので?わりと上から順に評価してくれる気がする
- transient や after や before やらをさんざんとこねくり回したあげく、attributes を上から一つずつ順に書いていって、if else などで分岐させる方法が落ち着きや良さそうなことに気づいた状態
FactoryBot.define do factory :user do trait :student do state { :student } end friend do if state == :student create :friend else nil end end friend_name { friend ? friend.name : (create :friend).name } end endFactoryBotというDSLはなかなか手ごわい
- 柔軟なことをしようとすると途端に行き詰まる感じがする
- ひとつが成り立つと1つが成り立たず、関連テストがばったばったと落ちたりする。
- 評価順は trait -> transient -> before -> after だろうか?そんな単純な話ではないのかもしれないし、あるのかもしれない。
- DSL なので do end の中で binding.pry しても、実行時のデバッグができるわけではない。
Original by Github issue
- 投稿日:2019-10-26T18:40:24+09:00
rake db:migrate実行するとrake aborted!とはじかれてしまうエラー対処法
[結論(解決方法)]
1〜3:現在起動されてるrakeのバージョンをアンインストールし、Gemfileが必要としているrakeのバージョンをインストール
4:bundle update rakeでアップデート
5・6:再度rake db:migrateでマイグレーションし、rails sでサーバー再起動参考記事:
https://qiita.com/ARTS_papa/items/41e2868273dd344cd317[エラー背景]
ツイート投稿Webアプリを作成中、ログイン機能を実装したく、devise Gemをインストールし、usersデータベースを作成しようとrake db:migrateしたところ、下記エラー出現・・・!
[エラー文]
rake aborted!
Gem::LoadError: You have already activated rake 13.0.0, but your Gemfile requires rake 12.3.3. Prependingbundle execto your command may solve this.
[訳]
rakeが中断されたお!
Gemのロードエラー:君が起動してるrakeはバージョン13.0.0だけど、君のGemfile君は12.3.3のrakeを求めてるぜベイベ。bundle execコマンドでこのすれ違い問題解決してちょ!13.0.0だろうが12.3.3だろうがどっちでもいいだろうがああああ!プログラミングちゃんはほんとわがままなんだから★
エラー文に出ていた、bundle execコマンド諸々は試しましたが歯が立たず、下記の手順で試したところうまくいきました![解決手順]
1:現在gemで使われてるrakeのバージョンを確認
(ターミナル上でgem list rakeコマンド実行)
$ gem list rake *** LOCAL GEMS *** rake (13.0.0, 12.3.3, 12.3.0)ふむふむ。今回必要な12.3.3も入ってるけど、13.0.0が起動されちゃってる感じね。
2:現在起動されちゃってるバージョンのrake(今回の場合は13.0.0)をアンインストールする。
$ gem uninstall rake -v 13.0.0 Successfully uninstalled rake-13.0.0これでめでたく13.0.0の野郎とおさらばだぜ、パチパチ
3:gemファイルで必要とされているバージョンのrakeをインストールする。(私のようにすでに必要なバージョン(今回は12.3.3)がインストール済みなら、3は行わず4へ)
$ gem install rake -v 12.3.34:bundle update rake コマンドでアップデート!
$ bundle update rake5:ようやく念願のrake db:migrateコマンド実行!
$ rake db:migrate6:サーバーの再起動は忘れずに★
$ rails s
- 投稿日:2019-10-26T17:19:53+09:00
http://localhost:3000/でサーバー立ち上げてもYay! You're on Rails!の画面から動かない
Version: Rails 5.2.3
[結論(解決方法)]
config/routes.rbのファイルにホーム画面(該当viewファイル)へのルート(root 'コントローラー名#viewファイル名')を設定する。Before
config/routes.rbRails.application.routes.draw do get 'tweets' => 'tweets#index' #ツイート一覧画面 get 'tweets/new' => 'tweets#new' #ツイート投稿画面 post 'tweets' => 'tweets#create' #ツイート投稿機能 endAfter
config/routes.rbRails.application.routes.draw do root 'tweets#index' get 'tweets' => 'tweets#index' #ツイート一覧画面 get 'tweets/new' => 'tweets#new' #ツイート投稿画面 post 'tweets' => 'tweets#create' #ツイート投稿機能 end[エラー背景]
Railsのサーバー立ち上げるぞ!ホーム画面見れるかな・・・ドキドキ。
という期待も儚く、下記Yay! You're on Rails!という画面から自分のアプリのホーム画面に移動しない、という経験ありますでしょうか?ええ、わたくしは結構あります←
そしてその都度、何がYay!だよゴルああああと叫んでおります。これはですね、Railsの大元と言えるroutes.rbルートファイルに、どこの画面にいけばいいかのルート(getやpostなどのhttpメソッド含む)を定義していないからなんですね。
なので、単純に
root 'コントローラー名#viewファイル名'
で初期画面として表示させたいviewファイル名+そのviewファイルへと導いてるコントローラー名を上記の形で記入してあげれば良いのです!私はツイート一覧画面のビューファイルを表示させたいので、
index.html.erbファイルをホームへのルートとして設定したい。→このindexビューファイルはtweetsコントローラに属しているので、root 'tweets#index'と記載してあげればオーケーとなります!
※シングルクオーテーション('')でrootの中身を囲うのを忘れずに〜ではでは、良いプログラミングライフを!アディオス!
- 投稿日:2019-10-26T16:27:35+09:00
【Ruby on Rails】rails generate devise:install で terminal が止まる
現象
https://qiita.com/Hal_mai/items/350c400e8763ce0487a3
上記の記事を参考にdeviseを導入した際に、以下のコマンドを叩いた時に、 Railsコマンドが動かなくなった。
rails generate devise:install解決策
Rails application preloader の
springを一旦停止させる。bundle exec spring stop参考
https://qiita.com/Hal_mai/items/350c400e8763ce0487a3
https://github.com/rails/spring
- 投稿日:2019-10-26T16:22:27+09:00
Uncaught TypeError: Cannot read property 'observe' of undefined
Uncaught TypeError: Cannot read property 'observe' of undefined
mutation observerを使用するところでエラーが発生。
解決策:
$(document).ready(function () {~~~~の記述を追加する。
readyがないと動きませんでした。
- 投稿日:2019-10-26T16:03:41+09:00
NameError in Users#edit ユーザー編集画面にならない!
[結論(解決方法)]
viewファイル(views/users/edit.html.haml)のhttpメソッドに:が抜けていたので足すことで解決しました。before
= link_to "ログアウト", destroy_user_session_path, method: delete, class: 'btn'after
= link_to "ログアウト", destroy_user_session_path, method: :delete, class: 'btn'[エラーが起きた背景]とあるプログラミングスクールでチャット投稿Webアプリを実装中のところ、歯車のアイコンを押すとログアウト編集画面に切り替わるはずが、本エラーが!
1:rake routesでちゃんとパスがあるか・Httpメソッド名があってるか確認。
→確認したところ、destroy_user_session_pathもちゃんとあるしメソッドもdeleteメソッドでちゃんと名前あってるぞ。。。むむ・・・・
2:元のエラーファイルを確認。
views/users/edit.html.haml#account-page.account-page .account-page__inner.clearfix .account-page__inner--left.account-page__header %h2 Edit Account %h5 アカウントの編集 = link_to "ログアウト", destroy_user_session_path, method: delete, class: 'btn' = link_to "トップページに戻る", :back, class: 'btn'・・・・およ? httpメソッド(delete)の色がどうやら変だぞ。
・・・あ!コロンが足りないやないか!!!= link_to "ログアウト", destroy_user_session_path, method: :delete, class: 'btn'method: :deleteで修正。→エラー解決しましたああ!!!!
今回の教訓
httpメソッドは:(コロン)で囲むべし!
- 投稿日:2019-10-26T11:48:24+09:00
docker-composeでBundler::GemNotFoundになる
- 投稿日:2019-10-26T10:13:29+09:00
erbをhamlに変えよう
ryoです。AP午後の自己採点が58点、部分点次第で合格ワンチャンなせいで気持ち悪い2ヶ月になりそうです。
昨日は新しくアプリケーションを立ち上げるセットを覚えました。
通っているスクールではhtmlはhamlを使って記述しています。
しかし、昨日scaffoldで出来上がったビューファイルはerbです。
そこで今日は、erbからhamlに自動変換をするコマンドを使います。この記事で出来る様になること
ビューファイルをerbからhamlに変換する。
目次
- コマンドと記述一覧
- 各コマンドと記述の説明
- よく聞くエラーとか
コマンドと記述一覧
gemをインストールするため、Gemfileに下記を記述します。
Gemfilegem 'haml-rails' gem 'erb2haml'ターミナルコマンドは下記だけです。
ターミナルbundle install rake haml:replace_erbsこれだけです。手抜きな感じです。
各コマンドと記述の解説
Gemfileです。
haml-railsがhamlを使える様にするGemです。
erb2hamlが変換できる様にするGemです。*erb2hamlは、haml-railsに備わっているため、不要であるとの話も聞いたことあります。
俺は実際に確かめていないです。
むしろ誰か試してみて、結果がわかったら教えてください。Gemfilegem 'haml-rails' gem 'erb2haml'上記二つをインストールします。
ターミナルbundle install rake haml:replace_erbこれが、ergを全てhamlに変換するコマンドです。
ターミナルrake haml:replace_erbs出力結果は下記のようになります。
出力Converting: app/views/users/registrations/edit.html.erb... Done! Removing: app/views/users/registrations/edit.html.erb... Removed! Converting: app/views/users/registrations/new.html.erb... Done! Removing: app/views/users/registrations/new.html.erb... Removed! Converting: app/views/users/sessions/new.html.erb... Done! Removing: app/views/users/sessions/new.html.erb... Removed! Converting: app/views/users/shared/_error_messages.html.erb... Done! Removing: app/views/users/shared/_error_messages.html.erb... Removed! Converting: app/views/users/shared/_links.html.erb... Done! Removing: app/views/users/shared/_links.html.erb... Removed! Converting: app/views/users/unlocks/new.html.erb... Done! Removing: app/views/users/unlocks/new.html.erb... Removed!*たまに、途中でYorN?みたいに聞かれることはありますが、これはhamlファイル作ったけど、元のerbのファイルは消しちゃっていい?的なことを聞かれています。基本的に使わないはずなのでYesのYで大丈夫です。
これで、変換終了です。
よく聞くエラーとか
変換して早速ビューを見てみようとしたら、エラーが出たよ!って聞くことがあります。
templateがmissingしてtext/html的な・・・スクショしておけばよかったorz結論、rails sを再起動すれば大抵治ります。焦らず再起動しましょう。
一つだけerbをhamlに変換したいけど、コマンドとか面倒くさい・・・って人もいるかもしれないです。
Convert HTML to HAMLというhtmlをコピペしただけでhamlに変換してくれるサイトもあります。載せておきますね。終わりです。お疲れ様でした。
- 投稿日:2019-10-26T04:37:38+09:00
【Ruby on Rails】Google Map APIの導入
はじめに
つくるもの
Google Map APIを導入しページに地図を表示させます。
今回は検索した住所にピンを立て、そこの緯度経度を表示させるまでを実装します。コードを書く前に
Google Maps API を利用するには、API キーという許可証のようなものを取得する必要があります。
API キーを取得する為にGoogle アカウントが必要になるので、事前に準備しておきましょう。そしてGoogle Cloud Platformへアクセスして、APIキーを取得しましょう。
プロジェクトの作成
APIの有効化
APIキーの作成
移行ツール
課金設定をしていないと、いざ地図を表示しようとしてもエラーが出てしまいます。
エラー文は以下の通りです。
You have exceeded your request quota for this API.
See https://developers.google.com/maps/documentation/javascript/error-messages?utm_source=maps_js&utm_medium=degraded&utm_campaign=billing#api-key-and-billing-errors移行ツール にアクセスし、手順にしたがって登録します。
アプリケーション作成
長きにわたる諸々の設定おつかれさまでした。
ここから早速アプリケーションを作っていきましょう。ファイルを作成する前に先ほど作成したAPIキーを用意しておいてください。
登場人物
今回作成・編集するページは
- routes.rb
- maps_controller.rb
- index.html.erb
この3つです。それぞれのファイルに書き込んでください。
ルーティング
routes.rbRails.application.routes.draw do get 'maps/index' root to: 'maps#index' resources :maps, only: [:index] endコントローラー
maps_controller.rbclass MapsController < ApplicationController def index end endビューページ
ビュー作成
地図を表示させる
では以下のコードをファイルにコピーしてみましょう。
index.html.erb<h2>gmap</h2> <div id='map'></div> <style> #map { height: 600px; width: 600px; } </style> <script> let map function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('map'), { center: {lat: 40.7828, lng:-73.9653}, zoom: 12, }); marker = new google.maps.Marker({ position: {lat: 40.7828, lng:-73.9653}, map: map }); } </script> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
<style>タグでマップの大きさを指定していますが、ここを省いてしまうと地図が表示されないので必ず書きましょう。<style>#map {height: 600px;width: 600px;}</style>次に地図の初期設定についてですが、
centerで初期位置の緯度経度を指定し、zoomで表示領域の大きさを決めます。function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('map'), { //latが緯度、lngが経度を示します center: {lat: 40.7828, lng:-73.9653}, //数値は0〜21まで指定できます。数値が大きいほど拡大されます zoom: 12, }); //positionに指定した座標にピンを表示させます marker = new google.maps.Marker({ position: {lat: 40.7828, lng:-73.9653}, map: map }); }そして最後の行の
YOUR_API_KEYの部分に自分で作成したキーを入れてください。<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>ではここで実際にブラウザで地図が表示されるか確認してみましょう。
以下のように表示されたでしょうか?検索フォームから住所を特定しピンを刺す
次に検索フォームを作成します。
index.html.erb<h2>gmap</h2> <!-- ここから追加 --> <input id="address" type="textbox" value="GeekSalon"> <input type="button" value="Encode" onclick="codeAddress()"> <!-- ここまで追加--> <div id='map'></div> <style> #map { height: 600px; width: 600px; } </style> <script> let map function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('map'), { center: {lat: 40.7828, lng:-73.9653}, zoom: 12, }); marker = new google.maps.Marker({ position: {lat: 40.7828, lng:-73.9653}, map: map }); } // ここから追加 let geocoder function codeAddress(){ let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress}, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); } else { alert('該当する結果がありませんでした:' + status); } }); } // ここまで追加 </script> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>2箇所追加しました。
ビューに関する部分では検索フォームとボタンを作成しました。<input id="address" type="textbox" value="GeekSalon"> <input type="button" value="Encode" onclick="codeAddress()">次に
<script>タグ内に検索機能の処理を追加しました。let geocoder //検索フォームのボタンが押された時に実行される function codeAddress(){ //検索フォームの入力内容を取得 let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress}, function(results, status) { //該当する検索結果がヒットした時に、地図の中心を検索結果の緯度経度に更新する if (status == 'OK') { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); } else { //検索結果が何もなかった場合に表示 alert('該当する結果がありませんでした:' + status); } }); }では早速ブラウザを開いて見てみましょう。
以下のように検索結果に応じて結果が処理される機能をつけました。検索結果から緯度経度を取得し、表示させる
さて初めてのGoogleマップ講座もいよいよ大詰めです。
最後に検索結果の緯度経度をページに表示させましょう。コードは以下の通りです。
index.html.erb<h2>gmap</h2> <input id="address" type="textbox" value="GeekSalon"> <input type="button" value="Encode" onclick="codeAddress()"> <!-- 下の1行を追加 --> <div id="display">何かが表示される、、、、!</div> <div id='map'></div> <style> #map { height: 600px; width: 600px; } </style> <script> let map let geocoder // 下の1行を追加 const display = document.getElementById('display') function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('map'), { center: {lat: 40.7828, lng:-73.9653}, zoom: 12, }); marker = new google.maps.Marker({ position: {lat: 40.7828, lng:-73.9653}, map: map }); } function codeAddress(){ let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress}, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); // 下の1行を追加 display.textContent = "検索結果:" + results[ 0 ].geometry.location } else { alert('該当する結果がありませんでした:' + status); } }); } </script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB9Uo-0D2PoQaAECrt9UXceHO7S1gveNd0&callback=initMap" async defer></script>ビューに関して1箇所、
<script>タグ内に2箇所追加しました。
それぞれ追加する場所を確認し、追加して見てください。
追加した要素・処理は
- 検索結果を表示させるためのディスプレイ
- ディスプレイの情報を取得
- 検索結果をディスプレイに表示
の3つです。なので追加箇所も3箇所です。
これでコードの書き込みは完了です。ではでは最後にブラウザを開いて確認してみましょう。
検索結果がディスプレイに表示されれば完成です。次回に続く!
- 投稿日:2019-10-26T02:35:28+09:00
Sidekiqの遅延実行エクステンション [翻訳]
Sidekiqの遅延実行エクステンション
最終更新 2019/09/03 編集者 Mike Perham
遅延実行エクステンションを使えば非同期実行を非常に簡単かつシンプルに実装することができます。
デフォルトでは、すべてのクラスメソッドとActionMailerを非同期に実行することができます。Sidekiq 5+からこの機能はデフォルトで無効化されています。有効にする場合は
Sidekiq::Extensions.enable_delay!を使用してください。ActionMailer
メールを非同期に送信するには
delayを使ってください。日時を指定して送る場合はdelay_for(interval)またはdelay_until(time)を利用してください。UserMailer.delay.welcome_email(@user.id) UserMailer.delay_for(5.days).find_more_friends_email(@user.id) UserMailer.delay_until(5.days.from_now).find_more_friends_email(@user.id)上記のメソッドの引数にオブジェクトのインスタンスを渡すことは避けてください。 代わりに、ベストプラクティスに従って、オブジェクトのIDを渡しメソッド内で再度インスタンス化を行ってください。これは
ActiveRecord
ActiveRecordを使って実装した任意のメソッド呼び出しを非同期に実行するためには、
delay、delay_for(interval)、delay_until(time)のいずれかを使用してください。ser.delay.delete_old_users('some', 'params') User.delay_for(2.weeks).whatever User.delay_until(2.weeks.from_now).whateverレコードのインスタンスから上記の遅延実行メソッドを呼ぶことは可能な限り避けてください。 そうしないと、古くなったオブジェクトの状態がRedisに保存されてしまい、stale data問題を引き起こしてしまう可能性があります。
クラスメソッド
どのようなクラスメソッドも上記の方法で遅延実行することができます。
MyClass.delay.some_method(1, 'bob', true)メソッドの引数は単純なものだけにし、複雑なRubyのオブジェクトを渡さないようにすることを忘れないでください。
高度なオプション
.delayに渡す引数を利用してワーカーのオプションを指定することができます。MyClass.delay(retry: false).some_method(1, 2, 3) MyClass.delay(queue: 'low').some_method(1, 2, 3) MyClass.delay_for(10.minutes, retry: false).some_method(1, 2, 3)ノート
この拡張機能には2つの欠点があります。
この機能は引数のシリアライズにYAML形式を利用するため、複雑なRubyオブジェクトを引数として渡すとジョブのペイロードが簡単にとても大きくなってしまいます。
この機能は
Classにメソッドを追加します。これらの理由から、この拡張機能はデフォルトで無効にされています。
- 投稿日:2019-10-26T02:15:42+09:00
Sidekiqの時間指定ジョブ [翻訳]
Sidekiqの時間指定ジョブ
最終更新 2018/02/22 編集者 Mike Perham
Sidekiqにはジョブが実行される日時を指定する機能があります。通常の
perform_async(*args)に代わってperform_in(interval, *args)またはperform_at(timestamp, *args)を使ってください。MyWorker.perform_in(3.hours, 'mike', 1) MyWorker.perform_at(3.hours.from_now, 'mike', 1)この機能を使えば、「サインアップの三時間後にメールを送信する」といった機能を簡単に実現することができます。
過去の日時を指定されたジョブは即座に実行されます。タイムゾーン
Sidekiqのスケジューリング機能はタイムゾーンに非依存です。Sidekiqはタイムゾーン引数に対して
.to_fをコールすることでタイムゾーンに関する混乱を回避しています。
さらに詳細についてはこのイシューを読んでください。新しいジョブをチェックする
Sidekiqのスケジューラーは秒単位の精度で動きません。スケジュールされたジョブのチェック間隔はデフォルトでおよそ5秒毎です。(Sidekiq 5.1より前は15秒でした)
このインターバルは設定で変更できます。Sidekiq.configure_server do |config| config.average_scheduled_poll_interval = 15 end定期実行ジョブ
Sidekiq Enterpriseは定期実行ジョブをサポートしています。その他のサードパーティgemもcronに似た機能を提供しています。
- 投稿日:2019-10-26T01:48:16+09:00
Railsでポートフォリオを作ってみよう! vol.1 後編 (docker-compose導入〜rails構築編)
前回、Docker導入を行ったが、今回はdocker-composeの導入を行う。
(前回のDocker導入編をまだご覧になっていない人は→こちら)docker-composeとは
Docker-composeとは、複数のコンテナをまとめて管理してくれるツールの名称。コンテナで作成した複数のサービスを構築・実行する手順を自動化し、管理を容易にしてくれる優れもの。
一つの専用ファイル(docker-compose.yml)を書いて、コマンドを叩くだけで使えるのも非常に便利。
Rails開発では複数のコンテナを使用する事が多々ある為Dockerを使うとなったら必須となってくる。
日本語訳版公式文章つまりは覚えて損はない。
環境
今回の環境は以下の通り
・ruby 2.5.1
・rails 5.2.1
・MySQL 5.7必要ファイルの確認
今回作成していくファイルは以下の4つ
・Dockerfile
・docker-compose.yml
・Gemfile
・Gemfile.lock構築
下記から構築を行っていくが、作業の区切り区切りは中タグで区切っている為、途中から作業を再開したい場合などは右の一覧から探すのがオススメ。
作業ディレクトリの作成
作業ディレクトリの作成を行う。mkdirでファイルを作成しその中に入っていく。
今回「exampleApp」で作成を行っていく。$ mkdir exampleApp $ cd exampleAppDockerfileの作成
まずDockerfileをexampleApp直下に置く
vimが使える場合 $ vi Dockerfile vimが使えない場合 $ touch Dockerfile を行いDockerfileを作成し、Atomなどで開いて編集しても良いそしてDockerfileを編集する
exampleApp/Dockerfile#rubyの指定、今回は環境欄で言った通り2.5.1を指定している。 FROM ruby:2.5.1 #shellの実行を行い、パッケージのインストールを行う。 RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs #RUN mkdirでファイルを作成し、ENVで環境変数の設定を行っている。 #WORDIDは起点ディレクトリの設定を行っている。 #ちなみに下記に書いてある[/app_name]の名前は変更しなくても良い RUN mkdir /app_name ENV APP_ROOT /app_name WORKDIR $APP_ROOT #Gemfileの作成 ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock #Gemfileのbundle installを走らせる RUN bundle install ADD . $APP_ROOT編集を行ったら保存してDockerfileは終了。
docker-compose.ymlの作成
Dockerfileと同じようにこちらもexampleApp直下に置く。
vimが使える場合 $ vi docker-compose.yml vimが使えない場合 $ touch docker-compose.yml を行いdocker-composeを作成し、Atomなどで開いて編集しても良いこちらも開いて編集を行う。
examleApp/docker-compose.yml#dbにある[MYSQL_ROOT_PASSWORD]のパスワードは後ほど使用する為覚えておく事 #インデントに注意する(インデントがずれている場合、エラーになる事が極めて高い) #インデント表示無しVer #versionは最新の3を使用 version: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: root ports: - "3306:3306" web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/app_name ports: - "3000:3000" links: - db #インデント表示有りVer(こちらはインデント確認用として使用) version: '3' services: ..db: ....image: mysql:5.7 ....environment: ......MYSQL_ROOT_PASSWORD: password ......MYSQL_DATABASE: root ....ports: ......- "3306:3306" ..web: ....build: . ....command: bundle exec rails s -p 3000 -b '0.0.0.0' ....volumes: ......- .:/app_name ....ports: ......- "3000:3000" ....links: ......- db編集を行ったら保存してdocker-compose終了。
Gemfileの作成
今回railsは5.2.1を利用
vimが使える場合 $ vi Gemfile vimが使えない場合 $ touch GemfileGemfilesource 'https://rubygems.org' gem 'rails', '5.2.1'編集して保存。
現状はrailsだけ入っていれば大丈夫。Gemfile.lockの作成
Gemfile.lockはただ単に作成だけ行う。
中の編集などは行わないので、$ touch Gemfile.fileで完了。
これで必要なファイルの作成は終了。rails newを行う
docker-composeコマンドを使用し、rails newを実行する。
$ docker-compose run web rails new . --force --database=mysql --skip-bundledatabase.ymlの修正を行う
作成されたconfigファイルの中にあるdatabase.ymlの編集を行う。
vimが使える場合 $cd config $vi database.yml vimが使えない場合は直接configファイルに行き編集を行う。 exampleApp/config/database.ymldatabase.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # MYSQL_ROOT_PASSWORDを記入 host: db # docker-compose.ymlのservice名を記入保存する。
docker-composeを起動する
ターミナルを確認。もしこの時点で
$ PC名: config $になっていたら [cd] を使用しexampleAppまで戻っておく
$ PC名: config $ cd .. $ PC名: exampleApp $コンテナを建てる
# コンテナをビルド $ docker-compose build # コンテナの作成&起動(別ターミナルが必要になる) $ docker-compose up # コンテナの作成&起動(バックグラウンド) $ docker-compose up -d起動しおわったら localhost:3000に飛びいつものページ(Yay! You're on Rails!)が出ていることを確認して完了。
その他
サーバーの終了方法
いつものctrl + cだけでは、railsサーバーは閉じたけどdb(mysql)サーバーは閉じていないなどの不具合が発生する事があるため、以下のいずれかの方法で終了させる。
コンテナを停止だけさせたい場合 $ docker-compose stop コンテナを停止させる + 削除する $ docker-compose downもしミスってctrl + cを行ってしまった場合はtmp/pids/server.pidファイルを削除し再度起動を行う。
そんなファイルないやんって時は、一度docker-composeを起動し、その状態で再度ファイルを探して削除を行い再起動すると治る場合がある。なぜ2つの停止方法があるのか
他の理由もあるとは思うが、コンテナがある状態で新しくgemをインストールすると、サーバーの再起動を行っても反映されない事象がある。
原因としては簡単でDockerfileを見ればわかるようにRUN bundle installを行っている。
docker-composeをstopさせただけでは以前のコンテナが残りっぱなしの為、docker-compose upを行ってもgemが対応しない。
なので、一回一回コンテナを削除して読み込ませないといけない。そのため
gemは入れないけどとりあえず停止だけさせたい → $ docker-compose stop gem新しく入れます、再度コンテナを作り直す理由が他にもあります → $ docker-compose downと分けて使用する。
DBでもエラーが出た場合
docker-compose down を行い build したのはいいけど、dbが無いよなどのエラーが発生する場合がある。
その時は、docker-compose run web rails db:createを行う。
ちなみに
先ほど実行したdocker-compose run、
docker-composeではよく使うので覚えた方が吉。docker-compose run [docker-compose.ymlで設定したサービス名] (使用したいコマンド) → docker-compose run web rails db:create ローカルでbundle installしたい docker-compose run web bundle install gitコマンド使いたい docker-compose run web git add . RSpec使いたい docker-compose run web bin/rspec
- 投稿日:2019-10-26T00:52:57+09:00
Linux&Rails環境構築の基礎知識
この記事では以下3つの内容を記載しています。
- Linux
- 環境構築
- コマンド一覧
Linux
- ターミナルとは
- カーネルとシェル
- .bash_profile
- 環境変数
- 権限
- パーミッション
ターミナルとは
ターミナルとはパソコンの操作をするためのアプリ。
プログラミングで使うアプリみたいなイメージがあるがそうではなく本来はパソコンを操作するためのもの。
そうなるとパソコンの操作の仕方は2種類ある。
1. マウスを使っての操作。
2. ターミナルを使っての操作。マウスを使っての操作
1つ目はおなじでみのマウスを使った操作。
例えばフォルダを作成する時、Macであればファインダーで右クリックでフォルダの作成を選んでフォルダを作成する。
つまりボタンをクリックしてパソコンに命令を与える。ターミナルを使っての操作
次に2つ目がターミナルを使ったパソコン操作。
マウスでの操作に対して、ターミナルを使ったパソコンを操作する場合はコマンドでパソコンに命令を与えることになる。
例えばフォルダの中身を見たい時はlsコマンドを使ってパソコンにフォルダを中身を見せてくれと要求する。
他にもファイルを作成する時はtouchコマンドでファイルを作成してくれと命令する。
このようにコマンドで命令を与えるためのアプリがターミナルです。カーネルとシェル
カーネルとはLinuxにおけるOSの根幹となるパーツのことで、実際に処理を行う部分のこと。
シェルはユーザーから送られてきたコマンドを受け取って、機械語に翻訳しカーネルに渡す役割がある。
処理をするのはカーネルです。
ではシェルを通さずにカーネルに直接コマンドで命令を与えればいいのでは?となります。
しかしそれはできません。
なぜならカーネルは機械語しか理解できないからです。
しかしコマンドは言い換えるなら人間がわかるようにしたもの、いわば人間語です。
なので人間語であるコマンドでカーネルに直接命令してもカーネルは命令が理解できません。
そこでシェルの登場、シェルはコマンドの意味も機械語もどちらも扱えます。
そのためコマンドを一度シェルに渡して機械語に翻訳してもらってカーネルに渡してもらう、こうすることで人間からパソコンに命令を与えることが可能となるわけです。環境変数
環境変数はシェルに定義する変数のことです。
概念自体は普通の変数と同じで変数という箱に値を入れます。
ですが環境変数とは何かと聞かれると、シェルの設定をするためというのが正しい答えです。
どういうことかというと、シェルの設定は環境変数の中身の値によって設定することができます。
例えば、ホームディレクトリの設定はHOMEという環境変数を使います。~ > ls bin/ cPractice/ Development/ test.rb* ~ > cd # cdでホームディレクトリに移動 ~ > pwd # 現在のホームディレクトリが/home/mintであることを確認 /home/mint ~ > HOME=/home/mint/Development # 環境変数HOMEに/home/mint/Developmentのパスを代入 /home/mint > cd # cdでホームディレクトリに移動 ~ > pwd # ホームディレクトリが/home/mint/Developmentに変わっていることを確認 /home/mint/Development環境構築とは
環境構築とは開発をするための準備。
具体的には開発に必要なソフトをインストールすること。
- homebrew
- rbenv
- mysql
homebrew
homevrewはソフトをインストールするソフト。
環境構築などでソフトをインストールする時はhomebrewを使ってインストールする。
以下はgitをインストール時の例$ brew install gitそしてhomebrewはインストールしたソフトを入れておく箱の役割もある。
homebrewでインストールしたソフトは/usr/local/Cellarに保存される。
まとめるとhomebrewには2つの機能がある。
1. ソフトのインストール
2. インストールしたソフトを入れておく箱の役割rbenv
rbenvの機能は3つ
1つ目はrubyのインストール
2つ目はrubyを保存する箱の役割
3つ目はrubyのバージョンの切り替えコマンド一覧
■ファイル操作系
ファイル情報の詳細表示
$ ls -llsコマンドに-lオプションをつけることでファイルの詳細情報を表示できる。
ファイルの作成
$ touch test.rbファイルの削除
$ rm test.rbフォルダの作成
$ mkdir testフォルダの削除
$ rmdir testフォルダの削除(フォルダの中身がある場合)
$ rm -rf testファイル名の変更
$ mv test.rb ruby.rb # test.rbが変更したいファイル、ruby.rbが変更後の名前ファイルの移動
$ mv test.rb test # test.rbをtestフォルダに移動ファイルの中身の表示
$ cat test.rbファイルを開く
$ open test.rb補足: openコマンドでzipを解凍したり、アプリやURLを開くこともできる。
■Homebrew
インストール
$ brew install gitアンインストール
$ brew uninstall gitインストール済みソフトの一覧表示
$ brew listインストール済みのソフトの検索
$ brew search gitbrewに異常がないか調べる
$ brew doctor■rbenv
rubyのインストール
$ rbenv install 2.3.1rubyのアンインストール
$ ruby uninstall 2.3.1インストール済みのrubyの一覧
$ rbenv versionsシステム全体のrubyバージョンの切り替え
$ rbenv global 2.3.1アプリごとのrubyバージョンの切り替え
$ rbenv local 2.3.1■Mysql
mysqlの起動(mac)
$ sudo mysql.server startmysqlの起動(ec2)
$ sudo service mysqld startmysqlの停止(mac)
$ sudo mysql.server stopmysqlの停止(ec2)
$ sudo service msyqld stoprootユーザーでmysqlへのログイン
$ mysql -u rootrootユーザーでmysqlへのログイン(パスワードあり)
$ mysql -u root -p































