- 投稿日:2019-05-21T23:46:46+09:00
rubyのメソッド一覧
model.rbUser.group('name, age') #取得した値をグループ化 #SELECT * FROM users GROUP BY name, age Page.order("category_id ASC") #取得した値を並び替え # SELECT "pages".* FROM "pages" ORDER BY category_id ASC User.count #検索結果の行数を取得 # SELECT count(*) AS count_all FROM userscontroller.rbarray.map {|item| block } #要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。 #collectと同じ #map!は各要素の値をブロックの値で上書きコマンド文字列.split("") #文字列を配列化 配列.join #配列を文字列化 文字列.chomp #文字列の末尾にある改行を削除グローバル変数は変数前にドルマークをつける。
controller.rb$x = 10 def foo() p $x end
- 投稿日:2019-05-21T23:04:34+09:00
Rails(Ruby)初心者がつまづいたことまとめ
rails初心者というより、rubyすら初心者。
意外とハマった云々というより普通にハマったことを書いていく。
同じことでつまづいた人いたら共感して!ハマったこと
- ruby
- rubocopが機能しない
- case~文
- rails
- resourceとresoucesを間違える
- ルーティングのprefixがわからない
- Associationsが便利だったということ
- なぞのLoadError
ruby
rubyというカテゴリで良いのだろうか?
細かいところですが、地味に時間かかった部分を書いておきます。rubocopが機能しない
ale使って非同期Lintしたかったという話。
bundleでgemの管理をプロジェクト毎にした場合、bundle execで動くrubocopが上手いこと起動してくれません。
5分くらい色々ググりはしたけど出てこなかったので、:h rubocop
(エディタがvimなので)で調べてみた。
一発で出ましたね。
初めからそうしとけばよかった。。。g:ale_ruby_rubocop_executable *g:ale_ruby_rubocop_executable* *b:ale_ruby_rubocop_executable* Type: String Default: 'rubocop' Override the invoked rubocop binary. Set this to 'bundle' to invoke 'bundle exec rubocop'.be rubocop使うためには'bundle'を設定しなさいってよ。
なんで、設定ファイルにlet g:ale_ruby_rubocop_executable = 'bundle'
書いたらちゃんとlintしてくれました。case文
erbファイルでcase使う話です。
まじハマった。
構文間違って覚えてるのかと思って、普通にrubyで書いてみたら上手く行くし。
つか、実際使う必要なかったので諦めようかと思ったけど、いざ使うってなった時困るから頑張ろうと諦めなかった。
調べてもあんま出てこなかったけど解決しました。<% # NG %> <% case パラメータ %> <% when 定数1 %> << syntax error, unexpected tIDENTIFIER, expecting when 何か表示 << syntax error, unexpected when, expecting end <% when 定数2 %> 何か表示 << syntax error, unexpected when, expecting end-of-input <% else %> 何か表示 <% end %>これではダメです。
rubocopは上の3つのエラーを出してきます。
うん。おしえてくれてありがとおおお。
rubocop言葉足りなくないっすか?<% # OK %> <% case パラメータ # ここのと下の部分を繋げたコードが正しい when 定数1 %> 何か表示 <% when 定数2 %> 何か表示 <% else %> 何か表示 <% end %>rails
アホを晒す。
resourceとresoucesを間違える
例のごとくprogateではscaffoldを使わないでルーティングを直書きして教えてくれてたので
(config/routes.rb)get 'hoge/index' => 'hoge#index'
みたいに直書きしてました。
resources使いましょうとのことなので、resources :hoge
みたいにすれば終わりだった。
はずだった。
URLにidの情報ほしいなーって思ってたのですが、resources使えばそれも準備してくれるとのこと。
便利だなあ。よっしゃ。書き換えるで。config/routes.rbRails.application.routes.draw do resource :hoge end # -------------------------------------------------- # # terminalでbe rails routes | grep hoge # -------------------------------------------------- # # new_hoge GET /hoge/new(.:format) hoges#new # edit_hoge GET /hoge/edit(.:format) hoges#edit # hoge GET /hoge(.:format) hoges#show # PATCH /hoge(.:format) hoges#update # PUT /hoge(.:format) hoges#update # DELETE /hoge(.:format) hoges#destroy # POST /hoge(.:format) hoges#createは?idないやんけ。(憤怒)
まあ、もし同じエラーになった人、もしくは知ってる人知ってた人なら気付くでしょうけどresource's'が正しい。
というか、resourceというのも存在してるんですね。config/routes.rbRails.application.routes.draw do resources :hoge # resource => resource's'へ変更 end # -------------------------------------------------- # # terminalでbe rails routes | grep hoge # -------------------------------------------------- # # hoge_index GET /hoge(.:format) hoge#index # POST /hoge(.:format) hoge#create # new_hoge GET /hoge/new(.:format) hoge#new # edit_hoge GET /hoge/:id/edit(.:format) hoge#edit # hoge GET /hoge/:id(.:format) hoge#show # PATCH /hoge/:id(.:format) hoge#update # PUT /hoge/:id(.:format) hoge#update # DELETE /hoge/:id(.:format) hoge#destroyまじくそ。
ルーティングのprefixがわからない
form_withとかで、urlを
/hoge/index
みたいに直書きしてました。
というか上でもそうだけど、直書きとかやってる時点でなんかないんかなあって気づいて調べろよって話でもあるんだけど。
railsではPrefix_pathでURLを作ってくれて、このメソッドを使いましょうとのことでした。
よし、書き換えるか。
GET /hoge を hoge_indexへ変更
POST /hogeは...あれ?Prefixなくね?
これ。
たぶん当たり前のことすぎて検索しても出てこない。
「rails prefix」、 「rails prefix どれ」
Progateで見落としてたのか?と思って戻ってもかいてない。。。
時間は過ぎていく。お、俺が悪いってのか…?俺は…俺は悪くねえぞ。だって仙人が言ったんだ…そうだ、ひつじ仙人がやれって!
こんなことになるなんて知らなかった!誰も教えてくんなかっただろっ!はい。
こんな感じで参考記事を見つけました。
同じ気持ちになっている人1がいてすごく嬉しいんですけど、タイトルを考えて欲しかった。
見つけにくい。
全部丁寧に書くとこんな感じ。
Prefix Verb URI Pattern URI Pattern hoge_index GET /hoge(.:format) hoge#index hoge_index(ここ空白) POST /hoge(.:format) hoge#create new_hoge GET /hoge/new(.:format) hoge#new edit_hoge GET /hoge/:id/edit(.:format) hoge#edit hoge GET /hoge/:id(.:format) hoge#show hoge (ここ空白) PATCH /hoge/:id(.:format) hoge#update hoge (ここ空白) PUT /hoge/:id(.:format) hoge#update hoge (ここ空白) DELETE /hoge/:id(.:format) hoge#destroy 一番上に書かれているPrefixを新しいPrefixが出てくるまで参照し続ける書き方がrails routesで出力されます。
Associationsが便利だったということ
これはつまづいたというより、純粋にAssociationsが便利だなって思ったことです。
例えば
posts (1 : 多) comments
の構造があったとして、それぞれのモデルクラスでpost.rbhas_many :commentscomment.rbbelongs_to :postsと記述すると、
@post = Post.find(params[:id]) @comments = Comments.where(post_id: @post)とかしなくて、
@post = Post.find(params[:id]) # @comments = Comments.where(post_id: @post)は # @post.commentsと等価でいける。
なんかよくわかってないけどActiveRecordすげえ。なぞのLoadError
Associationsでハマった?というのかわからんけど、引っかかったのはこっち。
前の日まで動いていたコードが次の日には動かなくなっていたーーー!!
エラーの内容はこれ。> Unable to autoload constant Comment, > expected [Project root]/app/controllers/concerns/comment.rb to define it <% @post.comments.each do |comment| %> <-- ここエラーですよ。みたいなCommentっていう定数が見つからんので、
concerns/comment.rb
で定義しろみたいな。
concernsで共通処理書こうとして、とりあえずcomment.rbでいっかーみたいにしたのがだめらしい。
らしいというのはよくわかっていなから。
名前被ってるとダメみたいなので、試しに、comment_common.rbみたいに名前変えたらうまくいった。おわりに
多分もっとあったけど、思い出せる範囲でこんなもん。
以上!
- 投稿日:2019-05-21T21:51:04+09:00
RubyonRailsでtwitter風webアプリケーションの作成 STEP1:ユーザーの登録
こんにちはジャムです。
Railsを一通り学んだので、自分自身のポートフォリオ用にTwitter風のwebアプリケーションを作成して行きます。
今回はSTEP1として、プロジェクトフォルダの立ち上げからユーザーの新規登録までやっていきます。
でわ、早速
プロジェクトフォルダの作成
今回はそのまんまでtwitterという名にします。
コマンドライン$ rails new twitter #twitterという名のプロジェクトフォルダ作成ユーザーを登録するモデル、テーブルの構築
Userモデルの作成
今回のWEBアプリのユーザー情報はメールアドレスとパスワードを設定
コマンドライン$ rails g model User email:string password_digest:string #userモデルの作成 $ rails db:migrate #データベースへの反映、usersテーブルの作成Gemfileにbcryptを追記
bycryptはusersテーブルにパスワードを保存する際に暗号化し、セキュリティを高めることができるgemです。
Gemfilegem 'bcrypt'インストール
コマンドライン$ bundle installuserモデルにパスワード暗号化用のおまじないとバリデーションを追記
app/models/user.rbclass User < ApplicationRecord validates :email, presence: true,uniqueness: true validates :password, presence: true has_secure_password endpresence:Eメールを登録する際に空白だった場合エラーを返す
ルーティングの設定
config/routes.rbRails.application.routes.draw do resources :users root "users#index" endユーザーコントローラーの作成
index new createコントローラーとビュー作成
コマンドラインrails g controller Users index new createコントローラーの修正
app/controller/users_controller.rbclass UsersController < ApplicationController def index @user = User.all end def new @user = User.new end def create @user = User.new(user_params) @user.save redirect_to "/" end private def user_params params.require(:user).permit(:email,:password,:password_confirmation) end endView関連を作成
全体のhtmlを構成するviewを若干修正
app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>Twitter</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= render "shared/header" %> #ここを追記 <div class="container"> <%= yield %> </div> </body> </html>ヘッダーを作成
先ほど記載した<%= render "shared/header" %>で読み込まれるファイル
app/views/shared/_header.html.erb<nav class="navbar navbar-inverse"> <div class="navbar-header"> <%= link_to "Twitter style Web application","/", class: "navbar-brand" %> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#gnavi"> <span class="sr-only">メニュー</span><!--音声ブラウザにメニューがあることを伝えています--> <span class="icon-bar"></span><!--ハンバーガーメニューの三本線を設定--> <span class="icon-bar"></span><!--ハンバーガーメニューの三本線を設定--> <span class="icon-bar"></span><!--ハンバーガーメニューの三本線を設定--> </button> </div> <div id="gnavi" class="collapse navbar-collapse"><!--ハンバーガーメニュー内に折り畳まれる部分の設定--> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "新規登録",new_user_path %></li> </ul> </div> </nav>ユーザーの一覧を表示するビューを作成
app/views/users/index.html.erb<h1>twitter</h1> <% @user.each do |user| %> <p><%= user.email %></p> <% end %>新規登録用のビューを作成
app/views/users/new.html.erb<div class="row"> <div class="col-md-6 col-md-offset-2"> <h1>Sign up</h1> <%= form_for(@user) do |f| %> <p><%= f.label :email , "メールアドレス"%></p> <p><%= f.email_field :email %></p> <p><%= f.label :password,"パスワード" %></p> <p><%= f.password_field :password %></p> <p><%= f.label :password_confirmation, "パスワード(再入力)" %></p> <p><%= f.password_field :password_confirmation %></p> <p><%= f.submit "登録する", class: "btn btn-primary" %></p> <% end %> </div> </div>bootstrapの導入
Gemfaiに追記
Gemfilegem 'bootstrap-sass' gem 'jquery-rails'インストール
コマンドライン$ bundle installcustom.scssを作成し、以下の内容を追記
app/assets/stylesheets/custom.scss@import "bootstrap-sprockets"; @import "bootstrap"; input { width:600px; } .btn { margin-top:20px; }jqueryの設定追記
app/assets/javascripts/application.js//= require jquery3 //= require bootstrap-sprockets一旦、ブラウザで確認
コマンドライン$ rails s -b 192.168.33.11 -d #192.168.33.11はipアドレスを確認してくださいブラウザを開いて192.168.33.11:3000にアクセス
新規登録をクリック
登録したユーザーが表示される
今日はここまで次回はコチラ
- 投稿日:2019-05-21T21:16:35+09:00
[Rails]文字数バリデーション
- 投稿日:2019-05-21T17:43:42+09:00
Twitter認証を二つのモデルでログインする際の注意(OmniAuth+devise)
deviseを複数のモデルでのTwitter認証を実装しようとすると、うまくいかない。
自分は開発中のuserモデルとartistモデルがあるケースでつまったのでまとめていきます。twitterapiの設定
Twitterのdeveloperアカウントは2018年の秋頃からしようが変更になり、英語でサービスがどのように使われるかを明示しなければならなくなりました。
https://qiita.com/tdkn/items/521686c240b0c5bc6207
承認されない時は、twitter社からメールが届き、いくつかの質問がされます。自分は2回ほどのメールのやり取りを経て、やっとアカウントが承認されました。(サービスをもっと具体的に教えろなど...)APIKEYを設定してdeviseとomniauthの設定
gem 'devise' gem 'omniauth-twitter'を入れて、
下の記事のようにdeviseの設定とomniauthの設定をします
https://qiita.com/daigou26/items/d84e64af775a3054e950複数モデルに適用させる
問題はここからです。omniauth+deviseだと複数モデルの時に適用されないのです。
実は公式のドキュメントに、現時点では、DeviseのOmniauthableモジュールはそのままでは1つのモデルでしか利用できません。しかしご安心ください。
OmniauthableはOmniAuthの単純なラッパーにすぎないので、方法はあります。と、記載があります。
手順にそって、複数modelにおいてもsns認証ができるようにしていきましょう。まず、userとartistのcontrollerにある
1. deviseにおいてomniauthを使わない。
devise :omniauthableを削除します。
2.ルーティング作成
また、
routes.rb
に、get "/auth/twitter/callback" => "omniauth_callbacks#twitter" get '/auth/another/twitter/callback' => 'anothers#twitter'と記述し、deviseを使わずにそれぞれのルーティングを作成します。
3. controller設定
次に
controllers/omniauth_callbacks.rb
というコントローラーを作成し、class OmniauthCallbacksController < ApplicationController def twitter callback_from :twitter end def failure @notice = "failure" redirect_to events_path, notice: 'failure' end private def callback_from(provider) provider = provider.to_s @user = User.find_for_oauth(request.env['omniauth.auth']) if @user.persisted? print("persisted true") @notice = "persisted true" flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize) sign_in_and_redirect @user, event: :authentication else print("persisted false") session["devise.#{provider}_data"] = request.env['omniauth.auth'] @notice = "user is not persisted." redirect_to root_path, notice: 'user is not persisted.' end end endと記述します。
次に別のanothers_controller
というコントローラーを作成し、class AnothersController < ApplicationController def twitter callback_from :twitter end def failure @notice = "failure" redirect_to events_path, notice: 'failure' end private def callback_from(provider) provider = provider.to_s @artist = Artist.find_for_oauth2(request.env['omniauth.auth']) if @artist.persisted? print("persisted true") @notice = "persisted true" flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize) sign_in_and_redirect @artist, event: :authentication else print("persisted false") session["devise.#{provider}_data"] = request.env['omniauth.auth'] @notice = "user is not persisted." redirect_to root_path, notice: 'user is not persisted.' end end endとし、別のモデルからの設定をします。
4.プロバイダ作成
二つのルーティングができるようにプロバイダを変更します。
config/initializers/omniauth.rbOmniAuth.config.full_host = "https://hogehoge.com" Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'],info_fields:"nickname, image", request_path: '/auth/twitter', callback_path: '/auth/twitter/callback' provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'],info_fields:"nickname, image", request_path: '/auth/another/twitter', callback_path: '/auth/another/twitter/callback' end5.viewの設定
これで、
<%= link_to 'twitterでログイン', '/auth/twitter', :id => "twitter" %> <%= link_to 'twitterでログイン', '/auth/another/twitter', :id => "twitter" %>と記述すると、無事ルーティングされます。
参考記事
https://qiita.com/RyosukeKawamura/items/ff1116b0a1d861a24c62
- 投稿日:2019-05-21T16:42:31+09:00
You must use Bundler 2 or greater with this lockfile.
- 投稿日:2019-05-21T15:23:34+09:00
RubyDep: WARNING: Your Ruby is outdated/buggy. RubyDep: WARNING: Your Ruby is: 2.3.0 (buggy). Recommendation: upgrade to 2.3.1. RubyDep: WARNING: (To disable warnings, see:http://github.com/e2/ruby_dep/wiki/Disabling-warnings ) rails aborted!
- 投稿日:2019-05-21T15:13:33+09:00
Paperclip ~私的メモ~
動機
プロジェクトでコードの改修をするにあたりPaperclipの仕様を
理解する必要が出てきたため。Paperclipとは
・Railsに入っているActiveRecord用のライブラリ
・ファイルアップロード用のgemhas_attached_fileメソッド
has_attached_file( name, options = {} )・基本的にはアップロードしたファイルの保存先をオプションで指定するもの
指定できるオプション
①サイズ指定
:styles => { medium: "300x300>", thumb: "100x100>" }②保存先URL
:url => "/assets/photo/:id/:style/:basename.:extension"③サーバ上の画像保存先パス
:path => "#{Rails.root}/public/assets/photo/:id/:style/:basename.:extension"所感
初めての記事になりますがこれを機にアウトプットを続けて行きたいと思います。
新しい発見があった際は適宜追記予定。
- 投稿日:2019-05-21T11:57:09+09:00
いつも忘れるRailsでの nil? empty? blank? present? の使い分け
前置き
Railsで、StringやArrayの値のチェックをする際、
nil? empty? blank? present?
の使い分けをいつも調べ直しているので、共有メモとして投稿します。 (調べればたくさん記事は出てきますが...)nil?
値がnilかどうか判定するメソッドです。
nilの場合はtrue
空のオブジェクト、または何かしら値が存在する場合はfalse
となります。nil.nil? => true "".nil? => false "hoge".nil? => falseempty?
空のオブジェクトかどうかを判定するメソッドです。
空の場合はtrue
値が存在する場合はfalse
となります。
また、nilだった場合は、NoMethodError
が発生します。"".empty? => true "hoge".empty? => false nil.empty? => Traceback (most recent call last): 1: from (irb):1 NoMethodError (undefined method `empty?' for nil:NilClass)blank?
空のオブジェクト、またはnilのオブジェクトかどうかを判定するメソッドです。
(empty? || nil?
と同等)
空のオブジェクト、またはnilのオブジェクトの場合はtrue
値が存在する場合はfalse
となります
ただし、TAB文字・スペースはtrue
が返ります。nil.blank? => true "".blank? => true "hoge".blank? => false "\t".blank? #TAB文字 => true " ".blank? #半角スペース => true " ".blank? #全角スペース => truepresent?
値が存在するかどうかを判定するメソッドです。
(blank?の反転のため!blank?
と同等)
値が存在する場合はtrue
空のオブジェクト、またはnilのオブジェクトの場合はfalse
となります"hoge".present? => true nil.present? => false "".present? => false
- 投稿日:2019-05-21T10:52:40+09:00
Ruby on Rails Tutorial 12章
12.1.1
演習
この時点で、テストスイートが greenになっていることを確認してみましょう。
表 12.1の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: アカウント有効化で行った演習 (11.1.1.1) と同じ理由です。2絶対パスでないといけないから
12.1.2
演習
リスト 12.4のform_forメソッドでは、なぜ@password_resetではなく:password_resetを使っているのでしょうか? 考えてみてください。Railsが自動でURLを割り当ててくれるから
演習
試しに有効なメールアドレスをフォームから送信してみましょう (図 12.6)。どんなエラーメッセージが表示されたでしょうか?コンソールに移り、先ほどの演習課題で送信した結果、(エラーと表示されてはいるものの) 該当するuserオブジェクトにはreset_digestとreset_sent_atがあることを確認してみましょう。また、それぞれの値はどのようになっていますか?
1
2
>> user = User.find_by(email: "example@railstutorial.org") User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example@railstutorial.org"], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-02 05:51:57", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5W...", reset_sent_at: "2019-05-02 05:51:57"> >> user.reset_digest => "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5WPn7MvmhApe" >> user.reset_sent_at => Thu, 02 May 2019 05:51:57 UTC +00:00 ちなみにサーバーのログ User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example@railstutorial.org"], ["LIMIT", 1]] (0.1ms) begin transaction SQL (1.5ms) UPDATE "users" SET "reset_digest" = ?, "updated_at" = ? WHERE "users"."id" = ? [["reset_digest", "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5WPn7MvmhApe"], ["updated_at", "2019-05-02 05:51:57.721207"], ["id", 1]] (6.3ms) commit transaction (0.0ms) begin transaction SQL (0.6ms) UPDATE "users" SET "updated_at" = ?, "reset_sent_at" = ? WHERE "users"."id" = ? [["updated_at", "2019-05-02 05:51:57.732073"], ["reset_sent_at", "2019-05-02 05:51:57.731546"], ["id", 1]]12.2.1
演習
ブラウザから、送信メールのプレビューをしてみましょう。「Date」の欄にはどんな情報が表示されているでしょうか?
パスワード再設定フォームから有効なメールアドレスを送信してみましょう。また、Railsサーバーのログを見て、生成された送信メールの内容を確認してみてください。
コンソールに移り、先ほどの演習課題でパスワード再設定をしたUserオブジェクトを探してください。オブジェクトを見つけたら、そのオブジェクトが持つreset_digestとreset_sent_atの値を確認してみましょう。1
2
----==_mimepart_5ccb78403955f_129d244fd0c7932 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit To reset your password click the link below: https://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/password_resets/4oduySTFZbn_7NUZ3Ey0vw/edit?email=example%40railstutorial.org This link will expire in two hours. If you did not request your password to be reset, please ignore this email and your password will stay as it is. ----==_mimepart_5ccb78403955f_129d244fd0c7932 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> /* Email styles need to be inline */ </style> </head> <body> <h1>Password reset</h1> <p>To reset your password click the link below:</p> <a href="https://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/password_resets/4oduySTFZbn_7NUZ3Ey0vw/edit?email=example%40railstutorial.org">Reset password</a> <p>This link will expire in two hours.</p> <p> If you did not request your password to be reset, please ignore this email and your password will stay as it is. </p> </body> </html>3
>> user = User.find_by(email: "example@railstutorial.org") User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example@railstutorial.org"], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-02 23:07:44", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$QtdXtxoGt9IjlNLbDREja.0XNQ6snBrkZO8krSIIPsS...", reset_sent_at: "2019-05-02 23:07:44"> >> user.reset_digest => "$2a$10$QtdXtxoGt9IjlNLbDREja.0XNQ6snBrkZO8krSIIPsS9W9FKJah62" >> user.reset_sent_at => Thu, 02 May 2019 23:07:44 UTC +00:00 >>12.3.1
演習
12.2.1.1で示した手順に従って、Railsサーバーのログから送信メールを探し出し、そこに記されているリンクを見つけてください。そのリンクをブラウザから表示してみて、図 12.11のように表示されるか確かめてみましょう。
先ほど表示したページから、実際に新しいパスワードを送信してみましょう。どのような結果になるでしょうか?1
12.3.2
演習
12.2.1.1で得られたリンク (Railsサーバーのログから取得) をブラウザで表示し、passwordとconfirmationの文字列をわざと間違えて送信してみましょう。どんなエラーメッセージが表示されるでしょうか?
コンソールに移り、パスワード再設定を送信したユーザーオブジェクトを見つけてください。見つかったら、そのオブジェクトのpassword_digestの値を取得してみましょう。次に、パスワード再設定フォームから有効なパスワードを入力し、送信してみましょう (図 12.13)。パスワードの再設定は成功したら、再度password_digestの値を取得し、先ほど取得した値と異なっていることを確認してみましょう。ヒント: 新しい値はuser.reloadを通して取得する必要があります。1
2
>> user = User.find_by(email: "example@railstutorial.org") User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example@railstutorial.org"], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:42:31", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> user.password_digest=> "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tGGcCLafcp2O" >> user.reload User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31"> >> user.password_digest => "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzut2pjqYfddy" >>13.3.3
演習
リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習 (リスト 11.39) の解答も含まれています。
リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐 (リスト 12.16) を統合テストで網羅してみましょう (12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます (なお、大文字と小文字は区別されません)。
2時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の (または共有された) コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、2時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます (しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう5。
リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。1
def create_reset_digest self.reset_token = User.new_token update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now ) end2
test "expired token" do ~中略~ assert_match /expired/i, response.body end3
password_resets_controller.rbdef update if params[:user][:password].empty? @user.errors.add(:password, :blank) render 'edit' elsif @user.update_attributes(user_params) log_in @user @user.update_attribute(:reset_digest, nil) flash[:success] = "Password has been reset." redirect_to @user else render 'edit' end end4
password_resets_test.rbassert_match /expired/i, response.body patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "foobaz" } } assert_nil user.reload.reset_digest assert is_logged_in? assert_not flash.empty? assert_redirected_to user12.4
演習
production環境でユーザー登録を試してみましょう。ユーザー登録時に入力したメールアドレスにメールは届きましたか?
メールを受信できたら、実際にメールをクリックしてアカウントを有効化してみましょう。また、Heroku上のログを調べてみて、有効化に関するログがどうなっているのか調べてみてください。ヒント: ターミナルからheroku logsコマンドを実行してみましょう。
アカウントを有効化できたら、今度はパスワードの再設定を試してみましょう。正しくパスワードの再設定ができたでしょうか?1スパム扱いされているらしくメールが届かない
2サーバーのログから実行
- 投稿日:2019-05-21T10:27:16+09:00
[環境開発]Rails で scss が廃止された 話 by @penguin_fuyuno
結論
書き換える
# gem 'sass-rails', '~> 5.0' gem 'sassc-rails'理由
ただし、2019年3月に廃止予定であるため、乗り換えなければならないとのこと
作業向上labo
link: http://creative-agure.conohawing.com/efficiency_labo/?p=82Github
link: https://github.com/sass/sassc-railsエラー内容
Ruby Sass has reached end-of-life and should no longer be used. * If you use Sass as a command-line tool, we recommend using Dart Sass, the new primary implementation: https://sass-lang.com/install * If you use Sass as a plug-in for a Ruby web framework, we recommend using the sassc gem: https://github.com/sass/sassc-ruby#readme * For more details, please refer to the Sass blog: https://sass-lang.com/blog/posts/7828841環境
ruby '2.5.3' gem 'rails', '~> 5.2.2' gem 'mysql2', '>= 0.4.4', '< 0.6.0'
- 投稿日:2019-05-21T10:02:42+09:00
Ruby on Rails Tutorial 11章
11.1.1
演習
現時点でテストスイートを実行すると greenになることを確認してみましょう。
表 11.2の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: 私達はこれからメールで名前付きルートを使います。1確認
2メールからリンクを開いてアクセスするため11.1.2
演習
本項での変更を加えた後、テストスイートが green のままになっていることを確認してみましょう。
コンソールからUserクラスのインスタンスを生成し、そのオブジェクトからcreate_activation_digestメソッドを呼び出そうとすると (Privateメソッドなので) NoMethodErrorが発生することを確認してみましょう。また、そのUserオブジェクトからダイジェストの値も確認してみましょう。
リスト 6.34で、メールアドレスの小文字化にはemail.downcase!という (代入せずに済む) メソッドがあることを知りました。このメソッドを使って、リスト 11.3のdowncase_emailメソッドを改良してみてください。また、うまく変更できれば、テストスイートは成功したままになっていることも確認してみてください。1確認
2
>> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil, remember_digest: nil, admin: false, activation_digest: nil, activated: false, activated_at: nil> >> user.create_activation_digest Traceback (most recent call last): 1: from (irb):2 NoMethodError (private method `create_activation_digest' called for #<User:0x00000000032badb0>) Did you mean? restore_activation_digest!3
def downcase_email self.email.downcase! endselfをつけなくてもテストでエラーは出ないが
modelクラスの中でのselfの使い方
によるとちなみに使い分けですが、インスタンスメソッドはそれぞれのインスタンスに対して参照・更新するようなメソッドとして使います。一方クラスメソッドは、modelクラスのレコードを検索するとか、作成されたインスタンスの数をカウントするとか、クラスの新しいインスタンスを作る等、個々のインスタンスには紐づけずクラスに対して働きかけるメソッドになります。
とあるのでクラスメソッドにした方がいいのではないかなと思う
create_activation_digestもself.ついてるので
しかしインスタントメソッド内でself.をつかうとインスタすメソッドになるのでこれはクラスメソッドにするためのselfではない??
class<<self
endで囲われてなかったり
def User.~となっていなかったりdef self.~やメソッド内でself.class.hoge~となっていないのでクラスメソッドではないと判断したが会っているのかな?11.2.1
演習
コンソールを開き、CGIモジュールのescapeメソッド (リスト 11.15) でメールアドレスの文字列をエスケープできることを確認してみましょう。このメソッドで"Don’t panic!"をエスケープすると、どんな結果になりますか?>> CGI.escape('Don’t panic!') => "Don%E2%80%99t+panic%21"11.2.2
演習
Railsのプレビュー機能を使って、ブラウザから先ほどのメールを表示してみてください。「Date」の欄にはどんな内容が表示されているでしょうか?Mon, 22 Apr 2019 01:44:05 +0000
GMT時間でアクセスした日時表示11.2.3
演習
この時点で、テストスイートが greenになっていることを確認してみましょう。
リスト 11.20で使ったCGI.escapeの部分を削除すると、テストが redに変わることを確認してみましょう。
1
確認
2n----==_mimepart_5cbd2c00cb0fa_140cbc99a4665c3\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nHi Michael Example,\r\n\r\nWelcome to the Sample App! Click on the link below to activate your account:\r\n\r\nhttp://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/account_activations/cmK5mEK4rCiqcyWEM67KyA/edit?email=michael%40example.com\r\n\r\n----==_mimepart_5cbd2c00cb0fa_140cbc99a4665c3\r\nContent-Type: text/html;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\n<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n <style>\r\n /* Email styles need to be inline */\r\n </style>\r\n </head>\r\n\r\n <body>\r\n <h1>Sample App</h1>\r\n\r\n<p>Hi Michael Example,</p>\r\n\r\n<p>\r\nWelcome to the Sample App! Click on the link below to activate your account:\r\n</p>\r\n\r\n<a href=\"http://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/account_activations/cmK5mEK4rCiqcyWEM67KyA/edit?email=michael%40example.com\">Activate</a>\r\n </body>\r\n</html>\r\n\r\n----==_mimepart_5cbd2c00cb0fa_140cbc99a4665c3--\r\n". test/mailers/user_mailer_test.rb:14:in `block in <class:UserMailerTest>' 41/41: [=========] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.55150s 41 tests, 171 assertions, 1 failures, 0 errors, 0 skips11.2.4
演習
新しいユーザーを登録したとき、リダイレクト先が適切なURLに変わったことを確認してみましょう。その後、Railsサーバーのログから送信メールの内容を確認してみてください。有効化トークンの値はどうなっていますか?
コンソールを開き、データベース上にユーザーが作成されたことを確認してみましょう。また、このユーザーはデータベース上にはいますが、有効化のステータスがfalseのままになっていることを確認してください。1
Redirected to https://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/
ルートディレクトリになっている2
>> user = User.find(101) User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 101], ["LIMIT", 1]] => #<User id: 101, name: "test01", email: "test01@example.org", created_at: "2019-04-22 05:18:28", updated_at: "2019-04-22 05:18:28", password_digest: "$2a$10$rPJDdcoRU/M9dkyv/X5hKehWF24P25FKLFq4mIPWA4T...", remember_digest: nil, admin: false, activation_digest: "$2a$10$IyKVaC6xJh2zfGTywkFUhuaY8JnePJer1j4jKPFYsJw...", activated: false, activated_at: nil> >> user.activated? => false11.3.1
演習
コンソール内で新しいユーザーを作成してみてください。新しいユーザーの記憶トークンと有効化トークンはどのような値になっているでしょうか? また、各トークンに対応するダイジェストの値はどうなっているでしょうか?
リスト 11.26で抽象化したauthenticated?メソッドを使って、先ほどの各トークン/ダイジェストの組み合わせで認証が成功することを確認してみましょう。>> user = User.create(name: "test1131",email: "test@mail.org", password: "123456") (0.1ms) begin transaction User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "test@mail.org"], ["LIMIT", 1]] SQL (2.8ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest", "activation_digest") VALUES (?, ?, ?, ?, ?, ?) [["name", "test1131"], ["email", "test@mail.org"], ["created_at", "2019-04-22 09:15:50.439813"], ["updated_at", "2019-04-22 09:15:50.439813"], ["password_digest", "$2a$10$Vz24S0K53ErWqPOMThXBbOLJ5c.IqzV4LhT2Am07tNANWysk9vrUq"], ["activation_digest", "$2a$10$glOElS8hLRrrL7S6oU7HbeGa2YNAK8SY6eNmz4Y3ebu6LI3bw5ky2"]] (7.2ms) commit transaction => #<User id: 102, name: "test1131", email: "test@mail.org", created_at: "2019-04-22 09:15:50", updated_at: "2019-04-22 09:15:50", password_digest: "$2a$10$Vz24S0K53ErWqPOMThXBbOLJ5c.IqzV4LhT2Am07tNA...", remember_digest: nil, admin: false, activation_digest: "$2a$10$glOElS8hLRrrL7S6oU7HbeGa2YNAK8SY6eNmz4Y3ebu...", activated: false, activated_at: nil> >> user.remember_token => nil >> user.activation_token => "cCxeiFM8zoy1ziofmOnrcA" >> user.remember_digest => nil >> user.activation_digest => "$2a$10$glOElS8hLRrrL7S6oU7HbeGa2YNAK8SY6eNmz4Y3ebu6LI3bw5ky2" >>2
>> user.remember_token = User.new_token => "ixM6QvBSTvD-UIFn16IpvA" >> user.update_attribute(:remember_digest, User.digest(user.remember_token)) (0.1ms) begin transaction SQL (2.6ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2019-04-23 11:33:13.345380"], ["remember_digest", "$2a$10$Blp7ueOFh/3xEb9iErYT1uwR9I6T4qo0SrFuqBH0i9Y1ddAApScyC"], ["id", 102]] (5.7ms) commit transaction => true11.3.3
演習
リスト 11.35にあるactivateメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 11.39に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。そこで、リスト 11.40のテンプレートを使って、この動作を変更してみましょう9。なお、ここで使っているActive Recordのwhereメソッドについては、13.3.3でもう少し詳しく説明します。
ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。
1
update_columns(activated: true, activated_at: Time.zone.now)2
def index
@users = User.where(activated: ture).paginate(page: params[:page])
enddef show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end3
- 投稿日:2019-05-21T09:26:36+09:00
Rails6 のちょい足しな新機能を試す20(ActiveStorage::FileNotFoundError編)
はじめに
Rails 6 に追加されそうな新機能を試す第20段。 今回のちょい足し機能は、
ActiveStorage::FileNotFoundError
編です。
Rails 6.0 では、ActiveStorage::Blob#open
などで対応するファイルが見つからなかったときにActiveStorage::FileNotFoundError
が発生するようになりました。記載時点では、Rails は 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1わざと動作しないコードを書く
今回は、
ActiveStorage::FileNotFoundError
が発生するようにわざと動作しないコードを作成します。Rails6 のちょい足しな新機能を試す16(UploadedFile#to_path編) で使ったコードを修正して動作しないようにします。
User
モデルのavatar_file_format
メソッドを動作しないように修正します。app/models/user.rbclass User < ApplicationRecord ... private def avatar_file_format avatar.blob.open do |file| header = File.read file, 8 if header != PNG_FORMAT_HEADER errors.add(:avatar, 'is not png format file') end end end end試してみる
Rails サーバーを立ち上げて http://localhost:3000/users/new にアクセスしてユーザー登録してみます。
アップロードする画像ファイルも指定します。
Create User
ボタンを押すとActiveStorage::FileNotFoundError
が発生します。
ファイルのアップロード先に限らず、ファイルが見つからないときは、
ActiveStorage::FileNotFoundError
が発生するようになっています。 ファイルが見つからない場合のエラーをrescue
したいときには、何も考えずにActiveStorage::FileNotFoundError
を指定しておけば良いので、便利ですね。ソースコード
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try020_active_storage_missing_file参考情報