20190521のRailsに関する記事は13件です。

rubyのメソッド一覧

model.rb
User.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 users
controller.rb
array.map {|item| block }
#要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します。
#collectと同じ
#map!は各要素の値をブロックの値で上書き
コマンド
文字列.split("") #文字列を配列化
配列.join #配列を文字列化
文字列.chomp #文字列の末尾にある改行を削除

グローバル変数は変数前にドルマークをつける。

controller.rb
$x = 10
def foo()
  p $x
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
Rails.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.rb
Rails.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.rb
has_many :comments
comment.rb
belongs_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みたいに名前変えたらうまくいった。

おわりに

多分もっとあったけど、思い出せる範囲でこんなもん。
以上!

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

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です。

Gemfile
gem 'bcrypt'

インストール

コマンドライン
$ bundle install

userモデルにパスワード暗号化用のおまじないとバリデーションを追記

app/models/user.rb
class User < ApplicationRecord
  validates :email, presence: true,uniqueness: true
  validates :password, presence: true
  has_secure_password
end

presence:Eメールを登録する際に空白だった場合エラーを返す

ルーティングの設定

config/routes.rb
Rails.application.routes.draw do
  resources :users
  root "users#index"
end

ユーザーコントローラーの作成

index new createコントローラーとビュー作成

コマンドライン
rails g controller Users index new create

コントローラーの修正

app/controller/users_controller.rb
class 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
end

View関連を作成

 全体の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に追記

Gemfile
gem 'bootstrap-sass'
gem 'jquery-rails'

インストール

コマンドライン
$ bundle install

custom.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-21 21.32.39.png

新規登録をクリック

スクリーンショット 2019-05-21 21.33.14.png

登録したユーザーが表示される

スクリーンショット 2019-05-21 21.34.07.png

今日はここまで次回はコチラ

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

[Rails]文字数バリデーション

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

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.rb
OmniAuth.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'
end

5.viewの設定

これで、

<%= link_to 'twitterでログイン', '/auth/twitter', :id => "twitter" %>
<%= link_to 'twitterでログイン', '/auth/another/twitter', :id => "twitter" %>

と記述すると、無事ルーティングされます。

参考記事

https://qiita.com/RyosukeKawamura/items/ff1116b0a1d861a24c62

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

You must use Bundler 2 or greater with this lockfile.

解決コマンド

$ rm Gemfile.lock
$ gem install bundler
$ bundle
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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!

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

Paperclip ~私的メモ~

動機

プロジェクトでコードの改修をするにあたりPaperclipの仕様を
理解する必要が出てきたため。

Paperclipとは

・Railsに入っているActiveRecord用のライブラリ
・ファイルアップロード用のgem

has_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" 

所感

初めての記事になりますがこれを機にアウトプットを続けて行きたいと思います。
新しい発見があった際は適宜追記予定。

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

いつも忘れるRailsでの nil? empty? blank? present? の使い分け

前置き

Railsで、StringやArrayの値のチェックをする際、nil? empty? blank? present? の使い分けをいつも調べ直しているので、共有メモとして投稿します。 (調べればたくさん記事は出てきますが...)

nil?

値がnilかどうか判定するメソッドです。
nilの場合は true
空のオブジェクト、または何かしら値が存在する場合は false となります。

nil.nil?
=> true

"".nil?
=> false

"hoge".nil?
=> false

empty?

空のオブジェクトかどうかを判定するメソッドです。
空の場合は 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? #全角スペース
=> true

present?

値が存在するかどうかを判定するメソッドです。
(blank?の反転のため !blank? と同等)
値が存在する場合は true
空のオブジェクト、またはnilのオブジェクトの場合は false となります

"hoge".present?
=> true

nil.present?
=> false 

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

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があることを確認してみましょう。また、それぞれの値はどのようになっていますか?

railstutorialchapter1201No-00.png

>> 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

railstutoriialchapter12023_No-00.jpg

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

RailsTUtorialchapter1203No-00.png

2
image.png

12.3.2

演習
12.2.1.1で得られたリンク (Railsサーバーのログから取得) をブラウザで表示し、passwordとconfirmationの文字列をわざと間違えて送信してみましょう。どんなエラーメッセージが表示されるでしょうか?
コンソールに移り、パスワード再設定を送信したユーザーオブジェクトを見つけてください。見つかったら、そのオブジェクトのpassword_digestの値を取得してみましょう。次に、パスワード再設定フォームから有効なパスワードを入力し、送信してみましょう (図 12.13)。パスワードの再設定は成功したら、再度password_digestの値を取得し、先ほど取得した値と異なっていることを確認してみましょう。ヒント: 新しい値はuser.reloadを通して取得する必要があります。

1

railsTutorialchapter1204_No-00.jpg

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 )
  end

2

test "expired token" do
~中略~
 assert_match /expired/i, response.body
end

3

password_resets_controller.rb
  def 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
  end

4

password_resets_test.rb
   assert_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 user


12.4
演習
production環境でユーザー登録を試してみましょう。ユーザー登録時に入力したメールアドレスにメールは届きましたか?
メールを受信できたら、実際にメールをクリックしてアカウントを有効化してみましょう。また、Heroku上のログを調べてみて、有効化に関するログがどうなっているのか調べてみてください。ヒント: ターミナルからheroku logsコマンドを実行してみましょう。
アカウントを有効化できたら、今度はパスワードの再設定を試してみましょう。正しくパスワードの再設定ができたでしょうか?

1スパム扱いされているらしくメールが届かない
2サーバーのログから実行

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

[環境開発]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=82

Github
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'

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

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確認

>> 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!
    end

selfをつけなくてもテストでエラーは出ないが

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に変わることを確認してみましょう。

確認

n----==_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 skips


11.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?
=> false


11.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
=> true

11.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 の両方に対する統合テストを作成してみましょう。


update_columns(activated: true, activated_at: Time.zone.now)

def index
@users = User.where(activated: ture).paginate(page: params[:page])
end

def show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end

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

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.rb
class 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 が発生します。
2019-05-18-210935_790x291_scrot.png

ファイルのアップロード先に限らず、ファイルが見つからないときは、ActiveStorage::FileNotFoundError が発生するようになっています。 ファイルが見つからない場合のエラーを rescue したいときには、何も考えずに ActiveStorage::FileNotFoundError を指定しておけば良いので、便利ですね。

ソースコード

試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try020_active_storage_missing_file

参考情報

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