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

rails heroku エラー録

久しぶりにHerokuにpushしようとしたらエラーが、、

...
remote: -----> Preparing app for Rails asset pipeline
remote:        Running: rake assets:precompile
remote:        rake aborted!
remote:        ActiveSupport::MessageEncryptor::InvalidMessage: ActiveSupport::MessageEncryptor::InvalidMessage
...
remote:        
remote:        Caused by:
remote:        OpenSSL::Cipher::CipherError: 

ActiveSupport::MessageEncryptor::InvalidMessage
これはrailsのmaster.keyが原因のエラーぽい

Herokuに登録しなければいけないぽいので

heroku config:set RAILS_MASTER_KEY=`cat config/master.key`
Setting RAILS_MASTER_KEY and restarting ⬢ tunagu-natto... done, v475
RAILS_MASTER_KEY: 72ddbs9f8c905e836e4e8dbf72a8b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FactoryBotで外部キーの値はどうやって作り出すんだ〜(泣

はじめに

 先日投稿した、テストコードに関するエラーで少し進展したので、記録しておきます。

先日の投稿↓
RSpecを使ってテストコードを書いているが、うまくいきません

検証結果

仮説2はおそらく関係がないことがわかりました。
ヘルパーメソッドを
number_fieldからtext_fieldに変えても、課題の解決には繋がりませんでした。依然として、うまく保存ができないため、仮説1の外部キーに関わる部分でエラーが発生していると考えられます。
モデルで外部キーであることをバリデーションしていることで、単にIDを入力するだけでは、主キーを見つけられないことが原因だと考えられます。

最後に

 引き続きテストコードがうまくいくように検証を続けて行きます。

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

[Ruby on Rails] kaminariの使い方

kaminariとは?

rubyのgemの一つで、ページネーションを作るためのものです。

kaminariのインストール

以下を Gemfile に追加します。

gem 'kaminari'

ターミナルでインストールします。

$ bundle install

コントローラーにページネーションのコードを追加

class PostsController < ApplicationController

def index
  @posts = Post.all
  @posts = Post.page(params[:page]).per(15)
end

end

page(params[:page]).per(15) の部分でページあたりいくつ表示させるかを設定します。
ページネーションが表示されるのは、表示される数が設定した数より多い場合です。

ビューファイルにページネーションのコードを追加

ビューでページネーションを表示させる場所に追加します。

    <%= paginate @posts %>

kaminariを日本語化する

kaminariの表示はデフォルトだと英語です。
日本語化する場合は、 config/localeskaminari_ja.yml というファイルを作成すると管理しやすいです。
kaminari_ja.yml に以下を追記します。

ja:
  views:
    pagination:
      first: "&laquo; 最初"
      last: "最後 &raquo;"
      previous: "&lsaquo; 前"
      next: "次 &rsaquo;"
      truncate: "..."
  helpers:
    page_entries_info:
      one_page:
        display_entries:
          zero: ""
          one: "<strong>1-1</strong>/1件中"
          other: "<strong>1-%{count}</strong>/%{count}件中"
      more_pages:
        display_entries: "<strong>%{first}-%{last}</strong>/%{total}件中"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails の credentials.yml.enc と master.key の関係性について(undefined method `[]' for nil:NilClass (NoMethodError))

職場で運用している Rails で作成した、顧客と収益アプリの公開用 clone を作成する際に、題名のところでハマったのでメモ用として残しておきます。

経緯

  1. GitHub のプライベートリポジトリからローカルにソースコードを clone
  2. プライバシー情報を削除後、.gitファイルを削除して、アプリ名(folder 名)を_v2へと変更
  3. git initして、リモートリポジトリへプッシュ
  4. CircleCi と連携させて、master に merge されたタイミングで自動テストを組む
  5. RDS を本番環境の db にしたいので、ローカルでcredentials.ymlに、RDS の情報を打ち込む設定を行う(このとき、credentials:editコマンドを一度使用して、なぜか一度 credential ファイルを削除、credeitials:editコマンドで再作成という謎の手順を踏んでいます)
  6. ECR にコンテナデプロイして、ECS のタスクを実行しようとしたところ、ECR のコンテナが起動していない
  7. 開発用の Dockerfile を push していたため、rails sコマンドを起動しておらず追記
  8. 一度、開発環境で動作確認しようと思い、docker buildすると、サーバーが起動しないエラー発生(undefined method `[]' for nil:NilClass (NoMethodError))

ここまでが経緯です。

AWS は一度お試しアプリで、本番環境として自動デプロイを組んだことがあるという程度で、サービスに関してよく理解しないまま進めていました。

何が問題だったのか

問題は、普通に 5 番目の項目ですね。笑

少し詳しくみると、

  • credential.yml.enc は、master.key を使用して暗号化、復号される
  • git push時に、master.key はプッシュされない(.gitignore に記載されているため)

つまり、clone してきた時点で master.key は無かった。 そして、一回目のcredentials:editコマンド時に master.key が作成された。

しかも、一度 credentials.yml ファイルを削除して、再度credentials:editコマンドを行っていたので、master.key との整合性が取れていない credentials.yml が作成されていた。

そのため暗号化も復号もできておらず、一度作成していた RDS の設定も消えていた。という感じですかね?多分

どう解決したか

この場合は、

  • 一度、master.key と credentials.yml を削除
  • credentials.editコマンドで 2 つのファイルを再作成

という工程でいけました!

結構ありがちなエラーみたいで調べたらたくさん情報が出てきたけど、いい勉強になった。

同じようなケースの方は私の屍を超えていってください!!

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

【rails】railsとjsを用いて「いいね機能」を実装してみた

今回はrailsとjsでいいね機能を実装していきたいと思います

** また最後におまけでユーザーがいいねした投稿を表示できるような機能も実装していきます**

jsを読み込んだりする説明は割愛!

参考にさせていただいた記事

https://techtechmedia.com/favorite-function-rails/
https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3

完成予想図

スクリーンショット 2020-12-22 21.34.19.png

下準備

ユーザーはたくさんの投稿にいいねをして、投稿もたくさんのユーザーにいいねされるので

likesテーブルを中間テーブルにした、ユーザと投稿の多対多のテーブル構造

rails g model like 

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

class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.integer     :user_id
      t.integer     :drink_id
      t.timestamps
    end
  end
end
rails g controller likes

アソシエーション

like.rb

class Like < ApplicationRecord
  belongs_to :user
  belongs_to :drink, counter_cache: :likes_count
end

・counter_cahce: :likes_countはリレーションされているlikeの数の値をリレーション先のlikes_countというカラムの値に入れますよっていう意味です。なのでlikes_countカラムをstoriesテーブルに追加しましょう。(rails g migration AddLikes_countToStories likes_count:integerをターミナルで実行すればオッケーです。)
この文章の参照元

drink.rb

class Drink < ApplicationRecord
  has_many :likes
  has_many :liking_users, through: :likes, source: :user
end

liking_usersモデルは無いので、likesテーブルを中間テーブルにして、userモデルとアソシエーションを汲みますよーってことをrailsに伝えてます

has_manyはbelongs_toはアソシエーションを組むのが本質ではなくて、メソッドを作るメソッド。

つまり,@drink.liking_userとかやったら、その投稿にいいねしたユーザー一覧を取得できるメソッドができるし、アソシエーションも組める

user.rb

  has_many :likes
  has_many :like_drinks, through: :likes, source: :drink

これも、user.like_drinksとかやったら、そのユーザーがいいねした投稿一覧が取得できる
これは、インスタ、Twitterによくある、そのユーザーがいいねした投稿を表示する時に便利

has_manyはbelongs_toはアソシエーションを組むのが本質ではなくて、メソッドを作るメソッド。

これを覚えて帰りましょう。

いいねボタンの記述

drinks/index.html.erb

こちらは投稿一覧のページになります

<%if @drinks%>
      <% @drinks.each do |drink|%>
      <li class='list'>
        <%= link_to drink_path(drink.id) do %>
          <%= link_to user_path(drink.user.id) do%>
            <div class="user-info-timeline">
                <%=image_tag drink.user.image.variant(resize: '60x60'),class: "user-img-timeline"  if drink.user.image.attached?%> 
                <div class="username-timeline">
                  <%= drink.user.nickname %>
                </div>
            </div>
          <% end %>
        <div class='item-img-content'>
          <%= image_tag drink.image , class: "item-img" if drink.image.attached? %>

          <%# if drink.trade%>



          <%# end %>
        </div>
        <div class='item-info'>
          <h3 class='item-name'>
            <%= drink.name %>
          </h3>
          <div class='item-price'>
            <span><%= drink.price %>円<br>(税込み)</span>
            <div class='star-btn'>
              <%# image_tag "star.png", class:"star-icon" %>
              <span class='star-count'>0</span>
            </div>
          </div>
          <div class='item-explain'>
            <%= drink.explain%>
          </div>
          <div class='item-tag'>
            <% drink.tags.each do |tag| %>
              #<%=tag.tag_name%>
            <%end%>
          </div>
          <%= render "likes/like",drink: drink%>
        </div>

        <% end %>
      </li>
      <%end%>

 <%= render "likes/like",drink: drink%>

に注目して欲しいです!

まずは可読性を高めるために
画像のいいねボタンを部分テンプレートで切り出しています、

そして、,drink: drinkの部分ですが、

  <% @drinks.each do |drink|%>

のeach文内のブロック変数を、likes/likeにも適用するために変数を受け渡しています。

ブロック変数とは(分かる人は飛ばして)

ブロック変数とは、each文やらtimes文,form_withとか、そのメソッド内だけで使える変数です。
つまり、eachだったらeachから endまでの範囲無で使える変数

@drinksにはいろんな情報が、配列として入っていますが、|drink|
とすることで、配列の中の一つ一つの情報がdrinkに入っていって、@drinksにある配列の数だけ表示します

likes/_like.html.erb

パーシャル(部分テンプレート)であることを分かりやすくするために慣習的にファイル名を_likeとしてます。
ただ

<%= render "likes/like",drink: drink%>

で呼び出す時はアンダーバーはいりません

<div class="like" id="like-link-<%= drink.id %>">
  <% if current_user.likes.find_by(drink_id: drink.id) %>
    <%= link_to unlike_path(drink.id), method: :delete, remote: true do %>
        <div class = "iine__button">❤️<%= drink.likes.count %></div>
    <% end %>
  <% else %>
    <%= link_to like_path(drink.id), method: :post, remote: true do %>
        <div class = "iine__button">♡️<%= drink.likes.count %></div>
    <% end %>
  <% end %>
</div>


id="like-link-<%= drink.id %>"

がミソ。

jsで非同期で画面を切り替えたいので、idを取得できるように、投稿ごとにidを区別するために
このように記述しましょう。

<%= link_to unlike_path(drink.id), method: :delete, remote: true do %>

, remote: true

と記述することにより、

リンクを押した時にajaxが発火するので非同期で通信が行われます。

いいねボタンを押したらいいねがすでについてれば、unlike_pathそうじゃなければlike_pathに飛びます

それぞれのpathをまだ定義してないので、このままじゃルーティングエラーになってしまうので

routes.rb

  post 'like/:drink_id' ,to: 'likes#like', as: 'like'
  delete 'like/:drink_id',to: 'likes#unlike', as: 'unlike'

と記述しましょう

as: 'like' とすることにより本来ならlikes_like_path(drink.id)とパス指定をしなきゃいけないのですが、
like_path(drink.id)でlikes#likeにpostリクエストを送ることができます

これで、リンクを踏んでリクエストを送ることができたので、次はコントローラーをみていきましょう

likes_controller

class LikesController < ApplicationController
  include SessionsHelper

  before_action :set_variables

  def like  
    like = current_user.likes.new(drink_id: @drink.id)
    #redirect_to drinks_path
    # jsを用いるので画面遷移は行わない
    #binding.pry
    like.save
  end

  def unlike
    like = current_user.likes.find_by(drink_id: @drink.id).destroy
    #binding.pry
  end

  private

    def set_variables
      @drink = Drink.find(params[:drink_id])
      @id_name = "#like-link-#{@drink.id}"
    end

end

remote: trueのリンクからlike,unlikeアクションが呼び出されるので、
デフォルトの遷移先はilke.js.erb,unlike.js.erbとそれぞれなります。

「⚠︎ @id_name = "#like-link-#{@drink.id}"
とControllerにViewの処理を書くのは、MVCパターン的にあまりよろしくないと思いますね。」

とご指摘をいただいたので、あまりよく無いですが、機能的には問題無いので一旦次いきます。

likes/like.js.erb

$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink  )) %>');

/likes/unlike.js.erb

$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink  )) %>');

likes/_like.html.erbにまた戻ります

この時にまた

drink: @drink

と書いて_like.html.erbに変数を受け渡してあげましょう

この@drink

likes_controller

  private

    def set_variables
      @drink = Drink.find(params[:drink_id])
      @id_name = "#like-link-#{@drink.id}"
    end

@drinkです。

以上で実装終了です。お疲れ様でした。

おまけ、ユーザーがいいねした投稿を表紙

users/show.html.erb

 <%= link_to "#{@user.nickname}がいいねした投稿",user_likes_path(@user.id)%>

こんな感じのリンクを作成

@userhはusers#showで@user = User.find(params[:id])
とかよくある感じで定義してます

user_like_pathはまだ定義してないので

routes.rb

  get 'user/likes/:id',  to: 'users#likes',as: 'user_likes'
  resources :users do
    member do
      get :following,:followers
      # memberメソッドを使うと
      # ユーザーidが含まれてるURlを扱うようになる
    end
  end

resources :userとかみんなやると思うので、resourcesの上に get 'user/likes/:id', to: 'users#likes',as: 'user_likes'

を書きましょう

これで、 リンクを踏んだらusers#likesにGETリクエストを飛ばすことができます

users_controller

   def likes
    @user = User.find(params[:id])
    @drinks = @user.like_drinks.paginate(page: params[:page],per_page: 10).order("created_at DESC")

   end

こんな感じで実装しましょう

.paginate(page: params[:page],per_page: 10)

はページネーション をまだ取り入れてなければ書かなくて大丈夫です。

.like_drinksメソッドは

  has_many :like_drinks, through: :likes, source: :drink

とuser.rbで書いたので、ユーザーがいいねした投稿一覧を取得できます。

デフォルトで、users/likes.html.erbにリダイレクトされるので、そのビューも用意しましょう

users/likes.html.erb


  <div class="user-profile">
    <h2 class="user-profile-name"><%= current_user.nickname %></h2>
    <h2><%= image_tag @user.image.variant(resize: '100x100'),class: 'user-img' if @user.image.attached? %></h2>
    <div class="user-like-post">
      <%= link_to "#{@user.nickname}がいいねした投稿",user_likes_path(@user.id)%>
    </div>
    <div class="user-edit">
      <% if current_user?(@user) %>
      <%= link_to "プロフィールを編集",edit_user_path(@user)%>  
      <% end %>
    </div>
      <% unless current_user?(@user) %>
        <div id="follow_form">
        <% if current_user.following?(@user) %>
          <%= render 'unfollow' %>
        <% else %>
          <%= render 'follow' %>
        <% end %>
        </div>
      <% end %>
  </div> 





<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>

<div class='main'>

  <%# 商品一覧 %>
  <div class='item-contents'>
    <h2 class='title'><%= @user.nickname%>の投稿</h2>
    <%= will_paginate @drinks%>
    <ul class='item-lists'>

      <%# 商品のインスタンス変数になにか入っている場合、中身のすべてを展開できるようにしましょう %>
      <%if @drinks%>

      <% @drinks.each do |drink|%>

      <li class='list'>

        <%= link_to drink_path(drink.id) do %>
        <div class='item-img-content'>
          <%= image_tag drink.image , class: "item-img" if drink.image.attached? %>

          <%# if drink.trade%>



          <%# end %>
        </div>
        <div class='item-info'>
          <h3 class='item-name'>
            <%= drink.name %>
          </h3>
          <div class='item-price'>
            <span><%= drink.price %>円<br>(税込み)</span>
            <div class='star-btn'>
              <%# image_tag "star.png", class:"star-icon" %>
              <span class='star-count'>0</span>
            </div>
          </div>
          <div class="item-explain">
            <%= drink.explain%>
          </div>
        </div>
        <% end %>

      </li>

      <%end%>
    </ul>
    <%= will_paginate @drinks%>
  </div>
  <%end%>
</div>

自分はこんな感じ

これで以上です。お疲れ様でした。

今までのコードのまとめ

自分のgithub

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

[メモ] ?で終わるメソッド

勉強用のメモ代わりとして記事にさせていただきます。

?で終わるメソッド

Rubyのメソッド名は?で終わらせることができる。?で終わるメソッドは慣習として真偽値を返すメソッドになっている。

# 空文字列ならtrue、そうでもなければfalse
''.empty?   #=> true
'abc'.empty?   #=> false

# 引数の文字列が含まれていればtrue、そうでもなければfalse
'movie'.include?('mo')   #=> true
'movie'.include?('at')   #=> false

# 奇数ならtrue、そうでもなければfalse
3.odd?   #=> true
4.odd?   #=> false

?で終わるメソッドは自分で定義することができる。

# 2の倍数ならtrue、それ以外はfalseを返す
def multiple_of_two?(n)
  n % 2 == 0
end
multiple_of_two(1) #=> false
multiple_of_two(2) #=> true
multiple_of_two(3) #=> false

まとめ

真偽値を返す目的のメソッドであれば、?で終わらせるようにした方が良い。

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

Railsと別で書いていたscssをRailsに入れようとしたら、SassC::SyntaxError in Users::Sessions#newが出た件について

目次

  • はじめに
  • 実行環境
  • 理由
  • 解決方法
  • まとめ

はじめに

今回は別で書いていたscssをrailsに入れようとしたら
background-color: $color-basic;
のところで
Error: Undefined variable: "$color-basic".
というエラーが出た。

実行環境

この記事は以下の動作環境で動作確認しています。
* ruby (2.7.1)
* rails (6.0.3)

理由

* application.scss(css)内のデフォルトでいる

 *= require_tree .

が、app/assets以下の全てのcssファイルを読み込んでいる
ので、
読み込む順番がずれて、
$colorを指定したmiximファイルが先に読み込まれていなかったから。
*が前に書いてあって、コメントアウトされている様に見えるけれど
ナ●シカの巨神兵の様に奴らは生きている...!!


解決方法

今回はstyle.scssに

style.scss
@import "./ホゲホゲ/mixin";
@import "./ホゲホゲ/plugin";
@import "./ホゲホゲ/reset";
(などなど続く)

の様な@importの集団を書いて、これを読み込ませたかったので、

1 application.scssに
(application.cssならscssに変更)

2 同ファイルの*= require_tree .の行を削除

3 末尾に↓の様に@importしてstyle.scss(などの@importがたくさん書いてあるファイル)を読み込む

application.scss
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *
 *= require_self //このファイルが最初に読み込むの意味。
 */
 @import "./css/style.scss";  // ここにimport(適用)したいscssを入れる。今回はstylesheet/cssフォルダ内にimportしたいscssを入れたのだけれど、ファイル構造や名前に合わせていただければ!

こんな感じで解決した。

まとめ

application.scss(css)の中身は
*の後ろも生きているので注意が必要!

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

間違ったマイグレーションファイルを削除する方法

オンラインスクールの課題に取り組んでいる時に、間違って、余計なマイグレーションを行ってしまいました。
せっかく上手く動作していたのに、そのマイグレーションをきっかけにエラーが頻発。パニック。
rollbackを行おうとしたのですが、上手くできなかったため、今回は削除することにしました。

まずはマイグレーションの状態を調べる

rails db:migrate:statusで現在のマイグレーションの状態を調べます。
その結果、このように返ってきました。

Status   Migration ID    Migration Name
--------------------------------------------------
   up     20201222113451  Devise create users
   up     20201223005506  Create items
   up     20201223093320  Add nickname to users
  down    20201223095940  Add devise to users

一番下のファイルが余計に追加してしまったデータベースです。downになってますね。

マイグレーションを削除!

rm -rf db/migrate/20201223095940_add_devise_to_users.rb

そして、rails db:migrateを行い、再びrails db:migrate:statusを実行。

きれいに削除されました

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20201222113451  Devise create users
   up     20201223005506  Create items
   up     20201223093320  Add nickname to users

きれいに最後のマイグレーションが削除され、無事に動作確認をすることができました。

まとめ

今回と問題になったパターンは違うと思いますが、rollbackを使って、マイグレーション問題を解決した方法を書きました。

今回は削除して対応してみました。
まだ上手く頭の中でマイグレーションについて理解できていませんが、少しずつ深めていきたいと思います。
先人の経験に感謝です!

参考

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

【Rails】フォームのオートフォーカスによる画面エラー

Railsで個人アプリを開発しているときに発生したエラーです。
備忘録としてエラーの発見から解消までを記します。

事象

スマートフォン(OS:iOS、ブラウザ:Google Chrome,Safari)でサインアップページにアクセスすると、ページのレイアウトが一瞬だけ表示された後、「このページは開けません。」というエラーが発生する。

image.png

ただし、該当URLを直打ちでアクセスするとページが正常に表示される。(これについては最終的に謎のまま終わりました・・・)

調査

他に同様のエラーが出るページは無いか探したところ、プロフィール編集ページでも発生しました。

サインアップページとプロフィール編集ページの共通点は、ユーザー情報を入力するフォームがあることでした。

サインアップページ

image.png

プロフィール編集ページ

image.png

原因

1画面内にautofocus: trueのフォームが複数存在すると、スマートフォンで閲覧した際にエラーが発生するようです。
そのため、autofocus: trueを一番最初の入力フォームにのみ残し、残りのフォームから消したところエラーは解消されました。
PCで閲覧する際にはエラーが出ないので見落としていました・・・
フォームをコピペで増やしていくのもいいですが、この辺りをしっかりメンテナンスしないといけないですね・・・。

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

lib/配下のファイルをリロードするたびに再読み込みする

めも

TL;DR

config/application.rb
...
config.eager_load_paths += ["#{Rails.root}/lib"]
...

参考記事: https://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request

requireは不要になるので削除。

開発環境でのみonにする

開発時にしか行わないことなので、本番で毎回読み込まないようにするには以下のように書く。

config/application.rb
...
config.eager_load_paths += ["#{Rails.root}/lib"] if Rails.env.development?
...
呼び出し側
require './lib/invoice_pdf' unless Rails.env.development?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】paramsってなんだ?

はじめに

こんにちは、23日目のカレンダーを担当する@sleepy_catです。

今現在メンターをしていますがこの質問かなり多いなと感じ、また、Railsを学び始めた頃にちょくちょく見かけて理解するのが大変だったparamsについての記事を書いていきます。

この記事を読んで欲しい方

  • モデル.find(params[:id])ってとりあえずshowアクションに書いてるけどよくわからんって方
  • formから送られてきたデータがよくわかんないけど直接DBに保存されると思ってる方

環境

$ rails -v
Rails 5.2.4.3
$ ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]

Rails5系です。現在6系が最新ですが、バージョンそこまで関係ない話だと思います。

paramsとは

クライアントからURLやフォームで送信された値(パラメータ)を取得するメソッドです。
これでわかりましたって方は多分元からこの記事を読んでないはずなので詳しく見ていきましょう。

パラメータの送り方について知る

ここでいうパラメータとはユーザ側のアクションによってページ遷移の時に一緒に送られるデータ(リクエスト情報)のことをいいます。主に送り方は3種類あります。
注)このパラメータについて知るにはHTTPメソッドやformについての知識も必要ですがそれも書くとめんどくさいかなり長くなってしまうため割愛させていただきます。

クエリパラメータ

URLパラメータとかクエリ文字列とか正しい呼び方がわからない...
遷移先がGETメソッドの時のデータの送り方。データの送り方としてはシンプルでURLの末に送りたいデータを載せて送るやり方です。
例を出すなら
https://hogehoge/hugahuga/index?like=dog
このURLの?以降がクエリパラメータです。今回はlikeという名前のデータを送っていますね(データの中身はdog)。一つしか送れないかというとそんなことはなくいくつも送れます。

よく見る場面としては検索機能ではこのクエリを用いてデータがやり取りされることが多いです。実際にこのqiitaの検索機能を使って検索結果のページに飛んだ後、URLを確認してみましょう。URLにクエリパラメータがあるのが確認できると思います。

もう一つよく使われる場面としてはWebサイトのアクセス解析をするために付けられることがあります。一番イメージしやすいのが個人ブログに貼ってあるamazonとかへのリンクだと思います(俗にいうアフィリエイト)。あのリンクはそのブログ経由で商品が購入された時にブログ主にも収益が入ると言った物ですが、じゃあどのサイトからきたのか、というのを示すためにクエリがついていることがあります。

ルートパラメータ

先ほどのクエリがURLの末にデータを載せるやり方ならこちらはURLの中にパラメータを入れ込むやり方です(URLの一部をパラメータと見なす、の方が適切かもしれませんが)。

例として以下のルーティングがあるとします。

config/routes.rb
Rails.application.routes.draw do
  resources :users
end
   Prefix  Verb    URI Pattern                Controller#Action
    users  GET     /users(.:format)           users#index
           POST    /users(.:format)           users#create
 new_user  GET     /users/new(.:format)       users#new
edit_user  GET     /users/:id/edit(.:format)  users#edit
     user  GET     /users/:id(.:format)       users#show <=この行を主に考えます
           PATCH   /users/:id(.:format)       users#update
           PUT     /users/:id(.:format)       users#update
           DELETE  /users/:id(.:format)       users#destroy

ここで一部ルーティングのURL(URI Pattern)に少しおかしな物が紛れているかと思います。"/:id"といった部分です。まさしくこの部分がパラメータとなる部分です。このようにURLの文字列内にコロンがついた部分は文字として扱われず一種の変数として扱われます。
(前提条件 Railsが動く順番として
URLが入力される => ルーティングで一致するcontroller/actionが実行)
例えば/users/1というURLのページを開こうとするとこれに一致するルーティングは/users/:id(.:format)だとRailsは判断します。"id"という名前のデータ(中身は1)が渡されページ遷移が行われるといったイメージです。

おまけ

Prefix  Verb    URI Pattern                Controller#Action
  item  GET     /qiita.com/:user_name/items/:item_id(.:format)       items#show

こんなパスがあるとして
https://qiita.com/sleepy_cat/items/c8eecfc5c486b0f7f2b8このサイトを開きたいと思います。ここで得られるパラメータは何があるでしょうか

答え
user_nameという名前のデータ、中身はsleepy_cat
item_idというデータ、中身はc8eecfc5c486b0f7f2b8

ポストデータ

遷移先がPOSTの時のデータの送り方。ログインの時などにフォームにメールアドレスやパスワードを入力して画面遷移など一番イメージしやすいと思います。通常ユーザ側が送られたデータを確認することはできないので(クエリなどはURL確認すると一発でわかってしまいますよね)セキュリティ上今までのものと比べ一番ましです。

paramsとは(再び

さて、データの送り方がいくつかあることはわかった。じゃあ受け取り方は?とここまで読んでいただいた方は思っている頃だと思います。それこそがparamsメソッドです。Railsではこの三つのデータを1つにまとめてparams[:パラメータ名]といった形で受け取れるようになっています。

ではこんなルーティングがあり

Prefix  Verb    URI Pattern               Controller#Action
  user  GET     /users/:id(.:format)       users#show

/users/4とURLが指定され、コントローラで

app/controllers/users_controller.rb
 def show
  @user = User.find(params[:id])
 end

と記述されていたら、@userにはどんなプロセスで、どんな値が入っているのか、今ならわかるはずです。
①まずURLパラメータとして、このページに"id"という名前のデータが送られます。
②送られた"id"というデータをparams[:id]が受け取ります。
③受け取ったデータの中身は"4"なので@user = User.find(4)となる。
④Userモデルからfindメソッドでidが4のデータを持ってきて、@userに入力されます。

確かめてみよう

paramsで受け取れるデータはどこで受け取ればいいのか。確認方法としてはデバッグ用gemで見たりもいいですが、ターミナルを確認してみるのもいいでしょう。

こちら一部変更してますが簡単に作ったアプリケーションでupdateアクションを行った時のターミナルの一部です。

Started POST "/users/2" for 999.999.999.99 at 2020-12-23 09:25:51 +0000
Cannot render console from 999.999.999.99! Allowed networks: 127.0.0.1, 
::1, 127.0.0.0/127.255.255.255
Processing by UsersController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"ABw1gqVMw8Jhogehogehugahuga",
 "user"=>{"name"=>"sleepy_cat", "text"=>"i love dog"}, "commit"=>"Update User",
 "id"=>"2"}
.
.
.

注目していただきたいのが"parameters:"から始まる記述の部分です。{"データの名前" => "データの中身","データの名前" => "データの中身"...}となっています。
例えばここでparams[:id]とすると得られるデータは"2"です。またparametersはハッシュのデータもあり、"user"=>{"name"=>"sleepy_cat", "text"=>"i love dog"}この部分は入れ子のようになっているため取り出す際はparams[:user][:name]とすると"sleepy_cat"が得られます。

まとめ

ちなみにparamsメソッドはcontrollerとviewで扱うことができます。
Railsの場合、formだろうとクエリだろうと①ページ遷移②parametersに情報集まる③コントローラなどで使う、モデルに振り分ける(ストロングパラメータなどがここに密接に関わってくるのでよかったら更に調べてみてください)等をするといった手順さえ頭の中に描けていればよりRailsの記述の理解が進むかもしれません。
この記事を読んでparamsと少しは仲良くなれそうだと思っていただけたら幸いです。

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

ransack使用 検索機能実装

はじめに

※すでにアプリケーションがある定で書いてます。

rails用の検索機能を実装する為のgemになります。
検索キーワードを元に紐付いたデータを取得
逆にキーワードが無い場合は全て取得する。

手順

Gemfileにransackを記述してbundle install

Gemfile
gem 'ransack'
ターミナル
bundle install

次に
コントローラーにransackを機能を利用する処理を書きます。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @q = User.ransack(params[:q])
    @users = @q.result
    #resultメソッドで結果を返します
  end
end

次に
ビューファイルに検索フォームを書きます。
ransackを使用した検索フォームはsearch_form_forというヘルパーメソッド を使います。

app/views/users/index.html.erb
<%= search_form_for @q do |f| %>
      <div class="d-flex">
        <div class="form-group flex-grow-1">
         #検索窓  
          <%= f.search_field :name_cont, placeholder: "〇〇を探す", class: "form-control" %>
        </div>
        <div> 
        #検索
        <%= f.submit "検索", class: "btn btn-primary ml-1" %>
        </div>
      </div>
    <% end %>

今回は「部分一致検索」が目的の為カラム名_contにしています。
「一致検索」の場合は,カラム名_eq とします。

検索機能完成

検索機能ができました!
ezgif-3-d2096169635d.gif

※Qiiteに動画のアップロード初めてでしたが、上手く行きました!
この記事には関係ありませんが参考になりました!:relieved:
https://qiita.com/bohebohechan/items/f8bf6d4bbe0f14497a7b

終わりに

ざっくりとした内容になりましたが、
結論画期的なgemを作って下さった技術者に感謝です!:joy:

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

購入機能の実装

 購入機能の実装

課題で唯一難しかったので記録用として保存しておきます。
FormオブジェクトとPay.jpを使った機能ですが詰まったところだけ抜粋して記述。

購入するためコントローラの記述 

Payjp::Charge.createに商品の価格やトークンを渡せば購入できます

class OrdersController < ApplicationController
  before_action :authenticate_user!
  before_action :item_find

  def index
    if user_signed_in? && current_user.id == @item.user_id || @item.order.present?
      redirect_to root_path
    end
    @item_order = ItemOrder.new
  end

  def create
    @item_order = ItemOrder.new(item_order_params)
    if @item_order.valid?
      pay_item
      @item_order.save
      redirect_to root_path
    else
      render :index
    end
  end

  private
  def item_order_params
    params.require(:item_order).permit(
      :postal_code, :prefecture_id, :city, :block, :building, :telephone_number
    ).merge(user_id: current_user.id, item_id: params[:item_id], token: params[:token])
  end

  def item_find
    @item = Item.find(params[:item_id])
  end

  def pay_item
    Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
    Payjp::Charge.create(
      amount: @item.price,
      card: item_order_params[:token],
      currency: 'jpy'
    )
  end

end

購入のビューファイル

@item_orderの変数を定義して受け取りができるように記述

<%= render "shared/second-header"%>

<div class='transaction-contents'>
  <div class='transaction-main'>
    <h1 class='transaction-title-text'>
      購入内容の確認
    </h1>
    <%# 購入内容の表示 %>
    <div class='buy-item-info'>
      <%= image_tag @item.image, class: 'buy-item-img' %>
      <div class='buy-item-right-content'>
        <h2 class='buy-item-text'>
          <%= @item.name %>
        </h2>
        <div class='buy-item-price'>
          <p class='item-price-text'>¥<%= @item.price %></p>
          <p class='item-price-sub-text'><%= @item.charge.name %></p>
        </div>
      </div>
    </div>
    <%# /購入内容の表示 %>

    <%# 支払額の表示 %>
    <div class='item-payment'>
      <h1 class='item-payment-title'>
        支払金額
      </h1>
      <p class='item-payment-price'>
        ¥<%= @item.price %>
      </p>
    </div>
    <%# /支払額の表示 %>

    <%= form_with(model:@item_order, id: 'charge-form', class: 'transaction-form-wrap',local: true) do |f| %>

    <%= render 'shared/error_messages', model: f.object %>

    <%# カード情報の入力 %>
    <div class='credit-card-form'>
      <h1 class='info-input-haedline'>
        クレジットカード情報入力
      </h1>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">カード情報</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :number, class:"input-default", id:"card-number", placeholder:"カード番号(半角英数字)", maxlength:"16" %>
        <div class='available-card'>
          <%= image_tag 'card-visa.gif', class: 'card-logo'%>
          <%= image_tag 'card-mastercard.gif', class: 'card-logo'%>
          <%= image_tag 'card-jcb.gif', class: 'card-logo'%>
          <%= image_tag 'card-amex.gif', class: 'card-logo'%>
        </div>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">有効期限</label>
          <span class="indispensable">必須</span>
        </div>
        <div class='input-expiration-date-wrap'>
          <%= f.text_area :exp_month, class:"input-expiration-date", id:"card-exp-month", placeholder:"例)3" %>
          <p>月</p>
          <%= f.text_area :exp_year, class:"input-expiration-date", id:"card-exp-year", placeholder:"例)23" %>
          <p>年</p>
        </div>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">セキュリティコード</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :cvc, class:"input-default", id:"card-cvc", placeholder:"カード背面4桁もしくは3桁の番号", maxlength:"4" %>
      </div>
    </div>
    <%# /カード情報の入力 %>

    <%# 配送先の入力 %>
    <div class='shipping-address-form'>
      <h1 class='info-input-haedline'>
        配送先入力
      </h1>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">郵便番号</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :postal_code, class:"input-default", id:"postal-code", placeholder:"例)123-4567", maxlength:"8" %>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">都道府県</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.collection_select(:prefecture_id, Prefecture.all, :id, :name, {}, {class:"select-box", id:"prefecture"}) %>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">市区町村</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :city, class:"input-default", id:"city", placeholder:"例)横浜市緑区"%>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">番地</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :block, class:"input-default", id:"addresses", placeholder:"例)青山1-1-1"%>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">建物名</label>
          <span class="form-any">任意</span>
        </div>
        <%= f.text_field :building, class:"input-default", id:"building", placeholder:"例)柳ビル103"%>
      </div>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text">電話番号</label>
          <span class="indispensable">必須</span>
        </div>
        <%= f.text_field :telephone_number, class:"input-default", id:"phone-number", placeholder:"例)09012345678",maxlength:"11"%>
      </div>
    </div>
    <%# /配送先の入力 %>
    <div class='buy-btn'>
      <%= f.submit "購入" ,class:"buy-red-btn" %>
    </div>
    <% end %>
  </div>
</div>
<%= render "shared/second-footer"%>

モデルの記述

class OrderAddress
  include ActiveModel::Model
 attr_accessor :postal_code, :prefecture_id, :city, :address_num, :building_name, :phone, :item.id, :params[:token], :current_user.id

  with_options presence: true do
    validates :postal_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "is invalid. Include hyphen(-)"}
    validates :city, format: { with: /\A[ぁ-んァ-ン一-龥]/, message: "is invalid. Input full-width characters."}
    validates :address_num
    validates :phone, format: {with: /\A\d{10}\z|\A\d{11}\z/ , message: "is invalid."}
    validates :token
  end
    validates :prefecture_id, numericality: { other_than: 1 }


    def save
      Order.create(item:id)
      Address.create(postal_code: postal_code, address_num: address_num, building_name: building_name, phone: pOrder.create(user: current_user.id, item: id)
      Address.create(postal_code: postal_code,  prefecture_id: prefecture_id, city: city, address_num: address_num, building_name: building_name, phone: phone)hone)
    end
end

クレジット機能の記述

const pay = () => {
  Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
  const form = document.getElementById("charge-form");
  form.addEventListener("submit",(e) => {
    e.preventDefault();

    const formResult = document.getElementById("charge-form");
    const formData = new FormData(formResult);

    const card = {
      number: formData.get("item_order[number]"),
      exp_month: formData.get("item_order[exp_month]"),
      exp_year: `20${formData.get("item_order[exp_year]")}`,
      cvc: formData.get("item_order[cvc]"),
    };


    Payjp.createToken(card, (status, response) => {
      if (status == 200 ) {
        const token = response.id;

        const renderDom = document.getElementById("charge-form");
        const tokenObj = `<input value=${token} name='token' type="hidden">`;
        renderDom.insertAdjacentHTML("beforeend", tokenObj);
      }

      document.getElementById("card-number").removeAttribute("name");
      document.getElementById("card-exp-month").removeAttribute("name");
      document.getElementById("card-exp-year").removeAttribute("name");
      document.getElementById("card-cvc").removeAttribute("name");

      document.getElementById("charge-form").submit();
    });
  });
};

window.addEventListener("load", pay);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「OpenSSH keys only supported if ED25519 is available」のエラーの解決方法

目標

エラーを解決してデプロイを完了させる。

本番環境

・Ruby: 2.5.7
・Rails: 5.2.4
・AWS:EC2
・OS: macOS Catalina

解決方法

開発環境にてGemを追加

1.Gemを追加

Gemfile
gem 'ed25519'
gem 'bcrypt_pbkdf'

bundleインストールを行い、再度capistranoの自動デプロイコマンドを実行する

ターミナル
$ bundle install
$ bundle exec cap production deploy

エラー文にもこのgemファイルを入れろという指示があったので試してみたら治りました。

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

[Rails]本番環境のデータベースをリセットする方法(Capistrano版)

はじめに

前提

・Railsを使用してアプリケーションを開発
・Capistranoでの自動デプロイを実装している
・AWSのEC2にてサーバーを構築している
・RDSでMySQLを使用している

背景

私は開発環境では、rails db:migrate:resetにていつもデータベースを作り直していましたが、本番環境ではどのようにすればいいのかという疑問から実装しました。

本番環境のデータベースをリセットする

まずはターミナルを用いてEC2で自分のアプリケーションフォルダの階層まで進む。
Capistranoでの実装をしているのでミスをしないように基本的にはcurrentディレクトリで作業するようにする。

terminal
[ec2-user@ip-222-22-2-222 アプリ名]cd current
current
RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:drop

これでデータベースが消去することができました。

データベース再度作成する

今回はRDSにMySQLを導入している前提でお話していきます。
まず引き続きターミナルは同じディレクトリでmysqlに接続します。

mysql -u (マスタユーザ名) -p -h (エンドポイント)
「-u」はユーザ名、「-p」はパスワードの入力、「-h」は接続先の情報を表すオプションです。
なお、「エンドポイント」は、RDSメニューで確認し、入力してください。
「パスワード」は、「マスターパスワード情報」で設定したパスワードを入力します。

terminal
mysql -u root -p -h rds-mysql-server.xxx.ap-northeast-1.rds.amazonaws.com

このコマンドを打つとpasswordの入力が求められるので入力すると無事mysqlに接続が完了しました。

削除したアプリケーション名と同じ名前をつけデータベースを再度作成します。

terminal
mysql> create database アプリケーション名;

再度本番環境でmigrateを実行します。

terminal
bundle exec rails db:migrate RAILS_ENV=production

これでデータベースのリセットは終了です。
あとは本番環境でcapistranoのコマンドを叩いて終了です

terminal
bundle exec cap production deploy

お読みいただきありがとうございました

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

datetimeを使って、本日の注文件数を表示したい。

まずは「$ rails c」でdatetimeの動きを見てみる。

irb
#現在時間の取得
[1] pry(main)> dt=DateTime.now
=> Wed, 23 Dec 2020 05:34:58 +0000

#parseで日付情報だけに加工
[2] pry(main)> today=Date.parse(dt.strftime("%Y/%m/%d %H:%M:%S"))
=> Wed, 23 Dec 2020


#ある日付を定義
[3] pry(main)> dt2 = DateTime.new(2020,12,23,5,6,7)     
=> Wed, 23 Dec 2020 05:06:07 +0000

#parseで日付情報だけに加工
[4] pry(main)> the_day=Date.parse(dt2.strftime("%Y/%m/%d %H:%M:%S"))
=> Wed, 23 Dec 2020

#today == the_day
[5] pry(main)> the_day == today
=> true


本日の注文を表示させたい時

状況設定
 ECサイトの管理者top画面で本日の注文件数を表示させたい。
 注文に関するモデル:Order

orders_controller
    def top

        dt=DateTime.now  #まずは、今日の日付を取得。こんな感じ(=> Wed, 23 Dec 2020 05:34:58 +0000)
        today=Date.parse(dt.strftime("%Y/%m/%d %H:%M:%S"))  #日付だけ取り出す


        orders=Order.all  #とりあえず、注文情報全て引っ張ってくる。

        @sum=0  #合計件数用。 ※別に初期化しているわけではないことに注意
                #@sum = @sum + 1 とした時 「@sumって何?」というエラーが出る。

        orders.each do |x|   #理系ですので、xと定義させていただきます。
            dt2=x.created_at
            the_day=Date.parse(dt2.strftime("%Y/%m/%d %H:%M:%S"))

            if today == the_day
                @sum = @sum + 1
            end

        end

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

anyenvでrbenvとnodenvを管理する

はじめに

anyenvを導入した理由はrubyの管理をrbenvからanyenv経由でrbenvをインストールする形式に変えたかったからです。ついでにNode.jsのバージョン管理もnodebrewからnodenvに切り替えました。こうすることで、.zshrc(bashを使う人は.bash_profile)にanyenvのパスを通すだけで済むのでファイルをきれいに保つことができます。

anyenv経由でnodenvをインストール

Node.jsをいったんアンインストールする

こちらの記事を参考にさせていただきました。また、rm -rf .npm-globalでグローバルインストールしたパッケージ等も削除しました。グローバルインストールをしない理由ということでこちらの記事も参考にさせていただきました。

anyenvを導入

brewでインストールしていきます。

$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ exec $SHELL -l
$ anyenv install nodenv
$ exec $SHELL -l

~/.zshrcの部分は使っているシェルに応じて変更してください。これだけで完了です!

プラグインの導入

anyenv-update

これはnodenvを含めた~envをまとめてアップデートできるanyenv updateとういコマンドを提供してくれます。

$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update

$anyenv rootの場所はホームディレクトリ直下です。

$ anyenv root  
/Users/shuntagami/.anyenv

nodenv-default-packages

anyenv プラグインのanyenv-update と、npmインストール時にデフォルトでいっしょにインストールしておくパッケージを指定できるnodenvプラグインです。

$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$ mkdir -p "$(nodenv root)"/plugins
$ git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages"
$ touch $(nodenv root)/default-packages

default-packagesの中身にインストールしたいパッケージを指定します

default-packages
yarn
typescript
ts-node
typesync

nodenvでNode.jsをインストール

$ nodenv install -l
$ nodenv install 15.4.0
$ nodenv global 15.4.0

最新の15.4.0をインストールしました。これでnode -vと打ってインストールしたバージョンが入っていれば成功です!

anyenv経由でrbenvをインストール

rbenvの詳細はこちらの記事でまとめました。nodenvの導入時と同様、いったんrbenvをアンインストールする必要があります。

rbenvをアンインストールする

こちらの記事を参考にさせていただきました。

再びrbenvをインストール

$ anyenv install rbenv
$ rbenv install 2.6.5
$ rbenv global 2.6.5

バージョンは適宜変えてください。以上で完了です!

補足 railsコマンドがみつからないと言われた時

私はコマンドも消してしまったので再インストールしました。

$ gem install bundler
$ gem install rails --version='6.0.0'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

anyenvに移行しました!(anyenvでrbenvとnodenvを管理する)

はじめに

anyenvを導入した理由はrubyの管理をrbenvからanyenv経由でrbenvをインストールする形式に変えたかったからです。ついでにNode.jsのバージョン管理もnodebrewからnodenvに切り替えました。こうすることで、.zshrc(bashを使う人は.bash_profile)にanyenvのパスを通すだけで済むのでファイルをきれいに保つことができます。

anyenv経由でnodenvをインストール

Node.jsをいったんアンインストールする

こちらの記事を参考にさせていただきました。また、rm -rf .npm-globalでグローバルインストールしたパッケージ等も削除しました。グローバルインストールをしない理由ということでこちらの記事も参考にさせていただきました。

anyenvを導入

brewでインストールしていきます。

$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ exec $SHELL -l
$ anyenv install nodenv
$ exec $SHELL -l

~/.zshrcの部分は使っているシェルに応じて変更してください。これだけで完了です!

プラグインの導入

anyenv-update

これはnodenvを含めた~envをまとめてアップデートできるanyenv updateというコマンドを提供してくれます。

$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update

$anyenv rootの場所はホームディレクトリ直下です。

$ anyenv root  
/Users/shuntagami/.anyenv

以下がanyenv updateコマンドを実行したときの私の例です。

$ anyenv update
Skipping 'anyenv'; not git repo
Updating 'anyenv/anyenv-update'...
Updating 'nodenv'...
Updating 'nodenv/node-build'...
Updating 'nodenv/nodenv-default-packages'...
Updating 'nodenv/nodenv-vars'...
Updating 'rbenv'...
Updating 'rbenv/ruby-build'...
Updating 'tfenv'...
Updating 'anyenv manifest directory'...

nodenv-default-packages

anyenv プラグインのanyenv-update と、npmインストール時にデフォルトでいっしょにインストールしておくパッケージを指定できるnodenvプラグインです。

$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$ mkdir -p "$(nodenv root)"/plugins
$ git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages"
$ touch $(nodenv root)/default-packages

default-packagesの中身にインストールしたいパッケージを指定します

default-packages
yarn
typescript
ts-node
typesync

nodenvでNode.jsをインストール

$ nodenv install -l
$ nodenv install 15.4.0
$ nodenv global 15.4.0

最新の15.4.0をインストールしました。これでnode -vと打ってインストールしたバージョンが入っていれば成功です!

anyenv経由でrbenvをインストール

rbenvの詳細はこちらの記事でまとめました。nodenvの導入時と同様、いったんrbenvをアンインストールする必要があります。

rbenvをアンインストールする

こちらの記事を参考にさせていただきました。

再びrbenvをインストール

$ anyenv install rbenv
$ rbenv install 2.6.5
$ rbenv global 2.6.5

バージョンは適宜変えてください。以上で完了です!

補足 railsコマンドがみつからないと言われた時

私はrailsコマンドも消してしまったので再インストールしました。

$ gem install bundler
$ gem install rails --version='6.0.0'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webの仕組みとHTTPについて

ブラウザとサーバー

ブラウザは今表示されてる画面
サーバーは色んなデータを保存してる大きなPCのようなもの
サーバーからファイルをダウンロードすることでブラウザに表示される
ファイルの中にはHTML、CSS、JSなどの情報が入ってる

図で表すとこんな感じ
      
(ブラウザ) ←←←←←← (サーバー)
         ファイル
       (HTML,CSS,JS)

HTTP

HTTPとはブラウザとサーバーのやりとりの方法
基本はリクエストとレスポンス

      リクエスト
      →→→→→→
(ブラウザ)      (サーバー)
      ←←←←←←
      レスポンス

リクエストで欲しい情報をサーバーに投げてサーバーがその情報を返してくれる
このリクエストとレスポンスのやりとりのルールを作るのがサーバーサイド言語

HTTPの基本メソッド4つ

1.GET データの取得
Webページを表示するときに使用

     このページ見たい
      →→→→→→
(ブラウザ)       (サーバー)
      ←←←←←←
      ページの情報

2.POST データの送信
フォームなどでデータを保存するに使用

      データ保存して
      →→→→→→
(ブラウザ)      (サーバー)
      ←←←←←←
      保存したよ

3.PUT データの更新
既存のデータを書き換えるときに使用

     データ変更して
      →→→→→→
(ブラウザ)      (サーバー)
      ←←←←←←
      変更したよ

4.DELETE データの削除
既存データを消すときに使用

      データ消して
      →→→→→→
(ブラウザ)      (サーバー)
      ←←←←←←
       消したよ

Twitterで例えると
ツイートを表示 GET
ツイートの新規投稿 POST
ツイートの更新 PUT (Twitterはできなかったはず)
ツイート削除 DELETE

IPアドレスとドメイン

IPアドレスとはサーバー内でデータに割り振られている住所みたいなもので数字で管理されている
ドメインとは「~~.com」の部分
IPアドレスだと分かりにくいのでDNSというシステムがIPアドレスとドメインを紐付けている

最後にWebサイト(Qiita)が表示されるまで

    qiita.comのIPアドレス教えて
      →→→→→→
(ブラウザ)      (DNS)
      ←←←←←←
     IPアドレスを返す

  DNSから取得したIPアドレスにGETリクエスト
      →→→→→→
(ブラウザ)      (サーバー)
      ←←←←←←
    HTML、CSS、JSを返す

こんな感じです

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

Rails チュートリアル 第4章 備忘録

第4章の備忘録

環境

Rails 6.0.3
Ruby 2.6.3

目次

1 配列・メソッド
2 ブロック
3 ハッシュ
4 シンボル
5 CSSの読み込みを解読

1 配列・メソッド

1.1 splitメソッド

文字列を配列に変換することが可能

# デフォルトは空白で区切る
>> "foo bar  baz".split 
=> ["foo", "bar", "baz"]

# 区切る文字を指定することが出来る
>> "fooxbarxbaz".split('x')
=> ["foo", "bar", "baz"]

1.2 インデックスに−1を使ってみる

配列のインデックスに-1を指定すると、配列の最後の要素を取得することが出来る。

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.last
=> 17
>> a[a.length - 1]
=> 17
>> a[-1]
=> 17

1.3 empty?メソッド

空かどうか調べることが出来る

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.empty?
=> false

1.4 include?メソッド

指定した値が含まれているか調べることが出来る

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.include?(42)
=> true

1.5 sortメソッド

配列を昇順(小さい順)に並べ替える

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.sort
=> [8, 17, 42]

1.6 reverseメソッド

逆順に変更する

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.reverse
=> [17, 8, 42]

1.7 shuffleメソッド

ランダムな並びに変更

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.shuffle
=> [8, 42, 17]

1.8 各メソッドに!を付ける

メソッドを使用した値自体も変更させる

>> a = [42, 8, 17]
=> [42, 8, 17]

>> a.sort
=> [8, 17, 42]
>> a
=> [42, 8, 17] # a自体は変更されていない

>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42] # a自体も変更される

1.9 push( < )メソッド

配列の最後に要素を追加する

>> a.push(6)
=> [42, 8, 17, 6]

>> a << 7
=> [8, 42, 17, 6, 7]

1.10 joinメソッド

splitメソッドの逆。配列を文字列に変換

>> a = [42, 8, 17, 6, 7, "foo", "bar"]
=> [42, 8, 17, 6, 7, "foo", "bar"]

>> a.join
=> "4281767foobar" # 連結

>> a.join(',')  
=> "42,8,17,6,7,foo,bar" # 間にカンマを挟んで連結

1.11 to_aメソッド

配列に変換する
0..9 : 9を含む

>> (0..9).to_a 
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2 ブロック

>> (1..5).each do |i|
?> puts 2*i
>> end
2
4
6
8
10
=> 10

上のコードのように do~endで囲むとブロックになる
ブロック内が複数行になる場合はこちらの方が主流らしい。

3 ハッシュ

ハッシュは配列と違って、
・ インデックスに整数値以外が使える(そのハッシュのインデックスをキーと呼び、それに対応するものが値)
・ ハッシュ内の要素の並び順が保証されない

4 シンボル

Railsでは文字列よりシンボルが使われる。
文字列との違いはクォートで囲まず、コロンが前におかれる。
ハッシュではシンボルをキーとして使うことが一般的。
また、=>はハッシュロケットと呼ばれ、下のコードのように書き換えが可能。

{ :name => "Test Name" }
{ name: "Test Name" }

5 CSSの読み込みを解読

<%= stylesheet_link_tag 'application', media: 'all',
              'data-turbolinks-track': 'reload' %>

このコードを理解するためには、
・ Rubyでは丸カッコを使用しなくても構わない
・ ハッシュがメソッド呼び出しの最後の引数の場合、波カッコ省略可能
を把握することが必要。よって、

<%= stylesheet_link_tag (
    'application', 
    { media: 'all', 'data-turbolinks-track': 'reload' }
) %>

となり、引数は2つであることが判明する。

最初の要素はメディアタイプを指定、
次の要素はturbolinks機能をオンに設定している。

turbolinks : ページ遷移を高速化するライブラリ。Rails4から追加

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

Rails:find,find_by,whereについて簡単にまとめた

最初に

カレンダー企画2020の23日目
プログラミングの勉強を始めて3ヵ月程経ったので学んだことのメモをアウトプットとして記事に残します。
これからプログラミングの世界に入る人の手助けになれたら嬉しい限りです。
間違っていたり、言葉が違っていたり、誤解されるような言葉があったら教えてください^^
言葉を長々と読みづらかったら申し訳ありません。少しずつなれてがんばります。

find,find_by,whereについて

個人的に訳わからず調べて自分なりに解釈した内容です。
他にも細かく載っている記事はたくさんあると思うのでここでは大枠、捉え方ぐらいに思って貰えればいいかと^^;(詳しく書いてもどっかの受け売りでコピペになるのでね^^;)

find,find_by,whereこれらはDB(データベース)から必要な情報だけを取り出す為に必要な理解です。
WEBアプリケーションを作成するなら絶対どれかは使う。

これだけ覚える

(検索というのは私なりの解釈です。ご了承ください)

メソッド 検索範囲 取得するデータ
find id 1つ
find_by 指定したカラム 最初の1つ
where 指定したカラム すべて

find

これだけ覚える

id(主キー)を指定して、見つけた1レコードを取得する。

見つけられないとActiveRecord::RecordNotFoundというエラーが出る。

# インスタンス変数 = モデル名.find(主キー)

@post = Post.find(params[:id])

こんな感じで記述をします。

idに部分が1や2や3,,,nが入ってそれに関連しているものを取得してくる。
1だったら=> post.find(1)となってpostのid:1のデータを取得する。

find_by

これだけ覚える

findの役割に+αでid以外のカラムも指定できる

findは指定できる(検索できる)カラムがid限定でした。
find_byは指定できる(検索できる)カラムがid以外もOKになる。

検索結果とは同じ見つかった1レコードを取得してきます。
文字とかも指定できるが、1番最初に見つけたものだけが適応される。

#モデル名.find_by(任意のカラム名: 格納されている値)

@post = Post.find_by(title: "Qiita")

Postのtitleカラム内で「Qiita」と格納されているものを1つ取得する。

idで見つけるならfindを使う
それ以外ならfind_byを使う

where

これだけ覚える

find_byの役割に+αで取得するレコードが複数になる。

whereはfind_by同様に指定できる(検索できる)カラムがid以外もOK

検索結果が1つではなく該当するものすべてになる。

#モデル名.where(任意のカラム名: 格納されている値)

@post = Post.where(title: "Qiita")

find_byではtitleに「Qiita」とあるもの1つでしたが、
whereではtitleに「Qiita」とあるものすべてを取得します。

最後に

解釈の仕方が間違っていたらごめんなさい。
DBからデータを取得するので個人的には図書館で検索する機械とか漫喫にある本の検索機をイメージしましたね。
そうしたら理解できた気がしたので載せました^^

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

プログラミングを始めて3ヶ月半の未経験が「エンジニアチェッカー」を作ってインフルエンサーを撲滅してしまった話

インフルエンサー見なくなったと思いませんか?

エンジニア界隈をあんなにも盛り上げてくれたインフルエンサー。
最近、ちょっと元気ないですよね。

実はこれ、「エンジニアチェッカー」というサービスの影響なんです。

多くの未経験が目を覚ましてしまった

エンジニアチェッカーの基本機能は「エンジニアの皮を被ったインフルエンサー」を検知することです。
まるで優秀なエンジニアであるかのように装って未経験向けに発信してるインフルエンサー、いますよね。
そういった人たちに「インフルエンサー」というレッテルを貼り直してあげるのがこのサービスの仕事です。

ありがたいことに、2020年7月のリリースからわずか3日で10000人以上もの方々に使っていただくことができました。

その中にはインフルエンサーをエンジニアだと勘違いしていた未経験もいたでしょう。
情報発信を頑張っているインフルエンサー見習いの未経験もいたでしょう。
もしかしたらインフルエンサーご本人もいたかもしれません。

彼らの多くはエンジニアチェッカーを使うことによって改心しました。
インフルエンサーを信じる未経験は消え、インフルエンサーを目指す未経験も消え、そして最後にはインフルエンサーも消えていきました。

使用技術

という盛大な前置き(釣り)を経て、エンジニアチェッカーで使用した技術について書かせていただこうと思います。タイトルにもある通りプログラミングを始めて数ヶ月の自分が使った技術ですし決してレベルの高いものではありませんが、外部APIを多く使用しており、それら(札束の力)に頼ることで技術力が低くても提供するサービスの内容をある程度充実させられたと言う点で、特にこれから個人サービス・ポートフォリオを作る駆け出しエンジニアの方に多少なりとも参考になることを願って書きます。

言語・FW

Ruby 2.6.4
Ruby on Rails 5.2.4.3

触ったことのある言語がこれしかなかったのでこれで書きました。モデルもDBもないアプリなのでRailsである必要は無かったです。あとCSSも生で書いてます。

外部API

Twitter API

診断対象アカウントのツイート・名前・プロフィール・フォロー・フォロワー等を取得するためにTwitterが提供しているAPIを使いました。実際にはtwitterというgemを使用してAPIを叩いています。エンドポイントごとにリクエスト制限があるため注意です。例えば「フォロワーのリストを取得する」というエンドポイントは、15分に15回までしかリクエストを送れません。
参考:Rate limits — Twitter Developers

全くの0からコンテンツを用意しなくてもいいため、Twitterの情報をAPIで持ってきてアレコレ加工するというサービス・ポートフォリオは作りやすいし使ってもらいやすいのではないかなと思います。

Amazon Rekognition

Amazon Rekognition(高精度の画像・動画分析サービス)| AWS
画像解析をしてくれるAWSのサービスです。人間の顔写真に対して推定年齢や性別の解析(顔解析)や、何の生物や物体が写っているかという解析(ラベル解析)を行ってくれます。
リリース当時、容姿端麗な女性をアイコンにして駆け出しエンジニアを装い、フォロワーを集める、スクールの勧誘をしたり情報商材を売りつける、といった手法がにわかに流行っていたのでそれを検知するために使用しました。また「猫エンジニア」や「筋肉エンジニア」の判定にも一部使用しています。

画像解析の精度としてはほとんど満足のいくものでした。顔解析について、実物の人間の顔写真に対しては概ね正しく年齢や性別を読み取ってくれますが、二次元アイコンに対してはその多くについて性別をFemale(女性)と認識してしまいます(イラストを解析することを想定していないため、当然といえば当然のことです)。またラベル解析についてはキツネを猫と認識してしまうなどがありましたが、逆にイラストで描かれた猫についても「これは猫である」と認識してくれていたことが多かったと思います。

Amazon Comprehend

Amazon Comprehend(テキストのインサイトや関係性を検出)| AWS
文章をいろいろ解析してくれるAWSのサービスです。今回は感情分析の機能のみを使いました。
Neutral・Positive・Negative・Mixedの4種類の感情のどれに近いかをそれぞれ%表示で分析してくれます。

感情分析の用途としては「インフルエンサーの誤判定を防ぐ」ためのものです。
「インフルエンサー的なツイート」「インフルエンサーに関連するツイート」の多さで診断結果が変わってきますが、Comprehendを導入する以前は「インフルエンサーを批判するツイート」に対して上記のインフルエンサー関連ツイートだと評価してしまうケースが多発し、アンチインフルエンサー的なアカウントをインフルエンサーだと診断してしまう、診断サービスとしてあるまじき事態が発生していました。

そこでComprehendを導入し、インフルエンサーに対して感情的にPositive(肯定的)なツイートのみをインフルツイートとして計上することで、インフルエンサーの誤判定が圧倒的に減り、かなり実態と近い診断を下せるようになりました。

Comprehendの使用には従量制で課金されるので、一時期は感情分析の野良gemも試してみましたが、やはり分析の精度にかなり差異があり、結果的にはComprehendを選択しました。

インフラ

Heroku(hobbyプラン)

無料プランだとサブドメイン以外で独自ドメインが設定できなかったので月7$払ってhobbyプランにしています。

終わりに

リリースが半年近く前ということで完全に賞味期限切れの話題だと思いますがRUNTEQのアドベントカレンダーに参加させてもらうということで書かせていただきました。

私が卒業したプログラミングスクールのRUNTEQ
https://runteq.jp/

インフルエンサーを撲滅してしまったので、今はコラビットという会社で不動産の闇を暴いています!
https://github.com/collabit-inc/job-offer-engineer

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

Homebrew + rbenv + ruby-build の関係性(後編)実際にrubyとRailsの導入もできます!

前回の記事ではbrewでインストールしたプログラムが/usr/local/Cellerディレクトリなどに入りシンボリンクを作成することでパスが通ること、各ディレクトリ構成について説明しました。今回は実際にrbenvruby-buildをインストールして、それぞれのコマンドで何をしているのかを説明していきます。

rbenv, ruby-buildのインストール

まずはbrewを最新の状態にしておきます。

% brew update

次にrbenvruby-buildのインストールです。ruby-buildはrbenvのプラグインの1つです。rbenv installというRubyのバージョンをインストールするコマンドを提供しています。rbenvをインストールする際に、セットでインストールします。

% brew install rbenv ruby-build

RBENV_ROOT とは

次のコマンドの説明に必要なのでこの用語の説明をします。RBENV_ROOT というのは rbenv の shims (後述) と versions (Ruby のインストール先) がある場所(パス)を指し示すための環境変数です。 デフォルトではホームディレクトリに直下になっており、以下のように確認できます。

% rbenv root
/Users/shuntagami/.rbenv

rbenv initの実体

話を戻します。rbenv のインストール手順によると以下のコマンドを実行するようにあります。

% echo 'eval "$(rbenv init -)"' >> ~/.zshrc  #(`~/.zshrc`の部分は使っているシェルに応じて変えてください。)

これは何を行うのでしょうか? 答えは書いてあるとおり、shimsautocompletion を有効化します。shimsには何が入っているのか確認してみましょう。

% cd .rbenv
% ls
shims    version  versions

% cd shims
% ls
bundle bundler erb gem irb rake rdoc ri ruby testrb,,,

bundleやらrakeやらrailsを使ったことのある人ならおなじみのコマンドが入っています。つまり、shimsを有効化することにより、$RBENV_ROOT/shimsPATH に入れ,これらのコマンドがどこからでも使えるようになるということです。最後にシェルの設定ファイルの変更を以下のコマンドで読み込みましょう。

source ~/.zshrc  #(`~/.zshrc`の部分は使っているシェルに応じて変えてください。)

rbenvでrubyをインストールする

ここまででrbenvのインストールが完了したのでrubyをインストールしていきます。インストール可能なバージョンは以下のように確認しましょう。

% rbenv install --list

特に理由がなければ最新のバージョンをインストールしましょう。

% rbenv install 2.7.2

インストールしたバージョンを使うために以下のコマンドを実行します。

% rbenv global 2.7.2

最後にrbenvを読み込み、変更を反映させます。新しいバージョンの Ruby を入れたときや、実行ファイルを提供する gem を入れたあとには実行するようにしましょう。

% rbenv rehash

Railsを用意する

rubyの導入が終わり、せっかくなのでRailsの導入手順も説明していきます。

bundlerのインストール

bundlerとは、gemのバージョンやgemの依存関係を管理してくれるgemです。bundlerを使うことで、複数人での開発やgemのバージョンが上がってもエラーを起こさずに開発できます。

% gem install bundler

bundler自体もgemなのでインストールが完了すると1 gem installedと表示されるはずです。

Railsのインストール

% gem install rails --version='6.0.0'

バージョンを指定してインストールします。完了したら先ほど説明したrbenv rehashコマンドを実行しましょう。

参考

rbenv + ruby-build はどうやって動いているのか
https://takatoshiono.hatenablog.com/entry/2015/01/09/012040

rbenv公式
https://github.com/rbenv/rbenv#basic-github-checkout

まとめ

rubyとRailsのインストール手順とそれぞれのコマンドの意味を見ていきました。あとはデータベース,yarn,Node.jsをインストールすればRailsを動かすことができるようになります

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

Railsの賞味期限を延ばす方法をまとめてみる

はじめに

年末になりアドベントカレンダーが盛り上がってますね。Railsのエントリーも賑わっています。
以下のエントリーに触発されて、大規模な開発になっても少しでもうまく開発が進む方法まとめてみようと思いました。
https://okuramasafumi.hatenablog.jp/entry/2020/12/16/224401
https://blog.unasuke.com/2020/i-have-to-learn-those-things-in-the-future/

考え方

この記事では、DDDやアーキテクチャについて議論は取り扱わず、具体的なRubyのコーディング方法を中心にまとめていきます。
まず、原則的になぜRailsが大規模に向かないのか?開発が進むにつれしんどくなっていく理由について考えてみます。

大規模開発に向かない理由:
- クラスやオブジェクトの参照箇所が正確に把握できない。このため、リファクタリングはもちろん機能追加などがやり辛く、開発のスピード・品質が悪くなっていく。

こんな経験ありませんか?
- Gemのソースコードを追っていたけど、メソッドの定義がどこかわからなくなってしまう
- ネストが深すぎて処理内容がよくわからなくなる

Rubyのようなスクリプト型言語とコンパイル型言語の大きな違いの1つに動的な関数呼び出しがあり、これが規模が大きくなるにつれ開発を難しくする要因の大きない理由になっています。これを解決する手段として、DDDなどの手法を採用することも多いと思います。

参照箇所がなるべく明示的になるコーディングをする

参照箇所が明示的にわかるようにするには、大まかにいうと "メタプロを避ける" ということが言えます。
具体的には、
- 動的なメソッド定義を避ける=
- #respond_to? を使って処理を変えるコードは避ける
- #send を使わない
- 静的な処理を動的に処理しない
(例: viewでActiveRecordのattributeを.eachを使って表示を作る )

という感じです。

メタプロではありませんが、Railsの標準機能でも避けた方が良い機能もあります。

  • Concernなどのモジュールのinclude/mixinは避ける 別クラスに移譲するべき
  • ActiveRecordのbefore_*/after_*を避ける サービスパターン、コマンドパターンなどで別クラスからメソッドをコールする

コーディングルールを定める

より実践的な運用を考えると、ここまで紹介してきたようなポイントを、コーディングルールとして定めてチームで共有しておくと負債の増加が防げて望ましい状況ができます。
また、これらのことが守られているかはレビューでチェックする必要が出てきますが、レビュアーの負担が増えるため、 querly などのツールで検出することが理想的です。
これがあれば新規メンバー加入時も、チームの負担を少なく品質を守り、また新規メンバーもなるべく自力で馴染んでいくことができます。

雑感

まとめてみる ということで雑記帳のようになりましたが、そのうち統合的に再編集したい気持ちもあります。
ぜひ、よくわからないこと(避けるべき理由や対処法)や他にも気をつけた方が良いポイントなどコメントもらえたら嬉しいです

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

Payjp v2 ざっくり実装

最近Payjpのv2なるものがリリースされたらしく、Payjp側もセキュリティの向上のためv2の使用を推奨しているらしいです。

少し前に質問があり、気になったので実装してみました。忘れないようにメモとして記事作りました。
細かい説明は省きます。リファレンス見れば誰でも簡単に実装できます。

v1との違いは?

v1ではトークン化APIのみを提供しており、カード情報入力フォームは加盟店側で用意する必要がありました。

しかしより安全なクレジットカード商取引のために、最新のPCI-DSSでは、カード情報入力フォームを決済代行業者側で用意することが求められています。
で、
payjp.js v2では、これらのフォームを弊社ドメインのiframe内で用意し、かつ独自スタイルの適用・イベント監視などv1で実現できた機能を提供いたします。 加えて、カード番号入力時の自動フォーマットやレスポンシブデザインなど、ニーズの高かった機能をデフォルトで提供致します。

との事です。Payjp側で用意された入力フォームを使えるので、わざわざ作らなくて良い感じらしいですね。
ところでPCI-DSSってセキュリティの基準か何かの基準??
金融業界出身の方いたら、教えて欲しいです。

ざっくりと実装

- application.html.hamlにv2のcdnを記載
%head
  %script{src: "https://js.pay.jp/v2/pay.js", type: "text/javascript"}
  .....

payjp.jsコード

$(function(){
  if(document.location.pathname !== "/cards/new") return false;
  // カード登録ページじゃい時は処理を実行しない
  const payjp = Payjp('pk_test_7370ce03239ee60f10ca694c') //公開鍵を読み込む。
  // Payjp.setPublicKey('pk_...................')としなくて良いらしい
  const elements = payjp.elements();
  // payjpのインスタンス生成
  const cardElement = elements.create('card', {style: {base: {color: 'black'}}})
  // ここでformを生成してる。createの第一引数には、「card」「cardNumber」「cardExpiry」「cardCvc」とかのタイプを選んで作れる。
  // cardだとカード番号、有効期限、cvcの3つをまとめて横並びにしたフォームを生成する
  // フォームを分けたい人は「cardNumber」「cardExpiry」「cardCvc」を引数にして作ると良いです。
  cardElement.mount('#card-element');
  // 任意のセレクタ(#card-element)に対してiframe(入力フォームを付与する)
  const submit_btn = $("#info_submit")
  // いつものリファクタ


  submit_btn.click(function(e) {
    e.preventDefault();
    // submitしないように止める
    payjp.createToken(cardElement).then(function(response) { 
      if (response.error) {
        // Payjp側からの返ってくるオブジェクトがerrorオブジェクトを持ってた場合
     // = 通信に失敗したとき
        alert(response.error.message)
        // どの情報に対して不備があるのか教えてくれる。
     // 下記に記載しているが該当箇所のエラーを知らせてくれる
        regist_card.prop('disabled', false);
        // いつものやつ
        return ;
      }
      else {
        alert("カード登録が完了しました")
        $("#card_token").append(
            `<input type="hidden" name="payjp_token" value=${response.id}>
             <input type="hidden" name="card_token" value=${response.card.id}>`
            //  これもいつものやつ
         );
      cardElement.clear()
    // 入力情報を消す
      $('#card_form')[0].submit()
      }

    })
  });

});

ビュー

.mypage.horizontal-padding-15
  = render 'shared/side_bar'
  .mypage-main
    .block.horizontal-padding-25
      .block__menu
        支払い方法
    .block.horizontal-padding-25
      #card-element
    -# ↑これ追加しただけ。この#card-elemntがマウントされるセレクタ(名前は何でも良い)
      #card-icons
        = image_tag 'icon_visa.png'
        = image_tag 'icon_master.jpg'
        = image_tag 'icon_saison.png'
        = image_tag 'icon_jcb.png'
        = image_tag 'icon_american.svg'
        = image_tag 'icon_diners.png'
        = image_tag 'icon_discover.png'
      = form_with model: @card, id: "card_form" do |f|
        #card_token
        = f.submit "次へ進む", class:"button back-red font-white", id: "info_submit"

実際のカード情報入力ページ

スクリーンショット 2020-07-10 11.11.03.png
スクリーンショット 2020-07-10 11.14.49.png

入力に不備があると該当箇所に対してアラートもしっかりと出る(V1もできる)

スクリーンショット 2020-07-10 11.17.32.png

使ってみた所感

好きな方を使いましょう!

あんまり変わらないです!

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

Railsでバリデーション設定でハマった話

こんばんは
アロハな男、やすのりです!

今日はポートフォリオ作成中にモデルのバリデーションでハマった話をしていきたいと思います!!

もしかしたら明日は我が身かも...?

結論

Userモデルのパスワードへのバリデーションは

on: :create

を使おう!

ハマった時の状況

え、いやどういうこと...?
となってしまうと思うので、順を追って説明していきます。

まずポートフォリオではユーザーが会員登録された場合は、専用のマイページがあったり口コミ投稿ができたりする機能をRailsのGemであるdeviseで作成していました。

そんな機能の中で、マイページで行える『自身のユーザー情報変更機能ページ』で事件が起こりました...

何があったのか?

ユーザー情報の変更ページではユーザー名やアイコン画像等を変更することができるページなんですが、deviseを使用していると通常は変更の際に毎回パスワードの入力が必要になります。

ただ、『少しの変更で毎回パスワードを入力するのは面倒だなぁ...』という人のために、パスワードを入力しなくても良い様にするメソッドをdeviseは用意してくれています。

registrations_controller.rb
protected

def update_resource(resource, params)
  resource.update_without_password(params)
end

最初はこのメソッドはコメントアウトされているので、コメントアウトを外すことでユーザー情報更新の際でもパスワードを入力しなくてもよくなるはずでした。

しかし実際に変更内容を入力して更新ボタンを押してみると...

スクリーンショット_2020-12-22_22_48_09.jpg

『あれ...え、なんで!?パスワードの入力を求められたぞ...!?』
もちろん、この状況でパスワードと確認用パスワードを入力して更新ボタンを押すとユーザー情報が更新されはするんですけど...

俺が欲しかったのはこんなよくわかんない機能じゃな〜い!!

ということで、いろいろ調べてみると原因が判明しました。

原因

察しのいい方はもうお気づきかもしれませんが...
そう、今回の事象の原因はUserモデルで設定したパスワードのバリデーションのせいだったんです!!

どういうことかと言うと、今回パスワードのバリデーションとして

presence: true(カラムが空だと登録されないオプション)

を設定していたんですが、このカラムが空だと登録できないという部分が効いてしまっていたんです...!!
つまり、

Userモデル『お、ユーザー情報を更新するぞ〜』
         ↓
Userモデル『deviseのコントローラーでパスワードがいらない設定もできてるな〜』
         ↓
Userモデル『あれ、でもモデルのバリデーションでパスワードが必要だな...』
         ↓
Userモデル『はい、パスワードも入力してくださ〜い』

と言う流れになっていた様です...なんという...

解決策

原因がわかればあとは簡単です。
パスワードに適用されていたバリデーションをユーザー登録の時だけかかる様に設定しなおせば良いだけです。

そこで冒頭でお見せした、

on: :create(コントローラーのcreateアクションの時のみ動作する様にするオプション)

が必要になります!

今回の私のコードで書くと

user.rb
  with_options presence: true do
    # ユーザー新規登録時のみバリデーションを適用する。
    with_options on: :create do
      validates :password
      validates :password_confirmation
    end
  end

※いろいろ他にもバリデーションを記述はしていますが、パスワードだけを抜き出しています。

さぁ、これで無事にユーザー情報が更新される様になりました!
めでたしめでたし

最後に

ユーザー機能もパスワードのバリデーションも比較的多くのアプリで必要になってくる機能だと思いますので、もし同じ様なことが会った際は参考にしてみてください!

この件に関するご指摘・アドバイスはどんどんいただきたいと思っていますので、コメントお待ちしています!!

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

Railsのバリデーション設定でハマった話

こんばんは
アロハな男、やすのりです!

今日はポートフォリオ作成中にモデルのバリデーションでハマった話をしていきたいと思います!!

もしかしたら明日は我が身かも...?

結論

Userモデルのパスワードへのバリデーションは

on: :create

を使おう!

ハマった時の状況

え、いやどういうこと...?
となってしまうと思うので、順を追って説明していきます。

まずポートフォリオではユーザーが会員登録された場合は、専用のマイページがあったり口コミ投稿ができたりする機能をRailsのGemであるdeviseで作成していました。

そんな機能の中で、マイページで行える『自身のユーザー情報変更機能ページ』で事件が起こりました...

何があったのか?

ユーザー情報の変更ページではユーザー名やアイコン画像等を変更することができるページなんですが、deviseを使用していると通常は変更の際に毎回パスワードの入力が必要になります。

ただ、『少しの変更で毎回パスワードを入力するのは面倒だなぁ...』という人のために、パスワードを入力しなくても良い様にするメソッドをdeviseは用意してくれています。

registrations_controller.rb
protected

def update_resource(resource, params)
  resource.update_without_password(params)
end

最初はこのメソッドはコメントアウトされているので、コメントアウトを外すことでユーザー情報更新の際でもパスワードを入力しなくてもよくなるはずでした。

しかし実際に変更内容を入力して更新ボタンを押してみると...

スクリーンショット_2020-12-22_22_48_09.jpg

『あれ...え、なんで!?パスワードの入力を求められたぞ...!?』
もちろん、この状況でパスワードと確認用パスワードを入力して更新ボタンを押すとユーザー情報が更新されはするんですけど...

俺が欲しかったのはこんなよくわかんない機能じゃな〜い!!

ということで、いろいろ調べてみると原因が判明しました。

原因

察しのいい方はもうお気づきかもしれませんが...
そう、今回の事象の原因はUserモデルで設定したパスワードのバリデーションのせいだったんです!!

どういうことかと言うと、今回パスワードのバリデーションとして

presence: true(カラムが空だと登録されないオプション)

を設定していたんですが、このカラムが空だと登録できないという部分が効いてしまっていたんです...!!
つまり、

Userモデル『お、ユーザー情報を更新するぞ〜』
         ↓
Userモデル『deviseのコントローラーでパスワードがいらない設定もできてるな〜』
         ↓
Userモデル『あれ、でもモデルのバリデーションでパスワードが必要だな...』
         ↓
Userモデル『はい、パスワードも入力してくださ〜い』

と言う流れになっていた様です...なんという...

解決策

原因がわかればあとは簡単です。
パスワードに適用されていたバリデーションをユーザー登録の時だけかかる様に設定しなおせば良いだけです。

そこで冒頭でお見せした、

on: :create(コントローラーのcreateアクションの時のみ動作する様にするオプション)

が必要になります!

今回の私のコードで書くと

user.rb
  with_options presence: true do
    # ユーザー新規登録時のみバリデーションを適用する。
    with_options on: :create do
      validates :password
      validates :password_confirmation
    end
  end

※いろいろ他にもバリデーションを記述はしていますが、パスワードだけを抜き出しています。

さぁ、これで無事にユーザー情報が更新される様になりました!
めでたしめでたし

最後に

ユーザー機能もパスワードのバリデーションも比較的多くのアプリで必要になってくる機能だと思いますので、もし同じ様なことが会った際は参考にしてみてください!

この件に関するご指摘・アドバイスはどんどんいただきたいと思っていますので、コメントお待ちしています!!

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

[Ruby]作成されてから〇〇以上経ったレコードのみ取得・選択する方法

作成されてから〇〇以上経ったレコードのみ取得・選択する方法

結論

Record.where('created_at < ?', 1.minute.ago).order(created_at: :desc)

重要なのは('created_at < ?', 〇〇)

〇〇に好きな条件を入れるだけ。

1時間前なら1.hour.ago
1日前なら1.day.ago

質問

クオーテーション使わない方法やベタープラクティスってありますか?

意外と見つけづらい情報でした。。。

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

【Rails】 LIKEで複数の値で検索する方法

LIKEで複数の値で検索する方法

LIKEで複数の値で検索する方法をメモとして残します。

gem 'activerecord-like'を使えばもっと簡単にできるのですが、別の方法として実装してみました。

scope = User.all
names = ['yama', 'mae']
search_sql = []

names = names.map do |n|
    search_sql.push('name like ?')
    "%#{name}%"
end

scope = scope.where( search_sql.join(' or '), *names )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Homebrew + rbenv + ruby-build の関係性(前編)

はじめに

最近rubyの管理をrbenvからanyenv経由でrbenvをインストールする形式に変えようと思っていました。というのも、.zshrc(bashを使う人は.bash_profile)にanyenvのパスを通すだけで済むのでファイルをきれいに保てるというのが1番の理由です。その移行方法について調べている際に「rbenvをよくわかってなかったなあ」と感じたので記事を書くことにしました。また、今回の記事は「homebrewでインストールしたとき」を想定しています。

brewでインストールしたものはどこにある?

まず私はこんな基本的なこともわたしは理解していませんでした。homebrewでインストールしたものは/usr/local/Cellerに入るようになっています。「/usr/local/Cellerにパスを通したことなんてない!」と思ったのですが、homebrew の仕組みにしたがって、適切なバージョンの実行可能ファイルに /usr/local/bin からのシンボリックリンク(ショートカット)が作成されるみたいです。つまり、/usr/local/binへのパスが通っていればhomebrewでインストールしたコマンドは使えるようになります。以下のような感じです。

$ ll /usr/local/bin/{rbenv,ruby-build}
lrwxr-xr-x  1 shuntagami  admin    31B  8  3 15:26 /usr/local/bin/rbenv -> ../Cellar/rbenv/1.1.2/bin/rbenv
lrwxr-xr-x  1 shuntagami  admin    44B 12 20 08:13 /usr/local/bin/ruby-build -> ../Cellar/ruby-build/20201210/bin/ruby-build

ちなみに、PATHは環境変数$PATHに設定されており、echo $PATHで確認できます。以下は私の例です。

$ echo $PATH
/Users/shuntagami/.anyenv/envs/nodenv/shims:/Users/shuntagami/.anyenv/envs/nodenv/bin:/Users/shuntagami/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

パスは:で区切られるので見やすくしてあげます。

$ echo $PATH
/Users/shuntagami/.anyenv/envs/nodenv/shims
/Users/shuntagami/.anyenv/envs/nodenv/bin
/Users/shuntagami/bin
/usr/local/bin 
/usr/bin
/bin
/usr/sbin
/sbin

/usr/local/binへのパスが通っていますね!補足で、$PATHに同じパスが通っていたらtypeset -U path PATHで解消できます。

binがいっぱいあるけどどういう意味?

先ほどの環境変数$PATHには/usr/,/binなどがありましたがそれらの違いについて説明します。そもそもbinとはBinary codeのことで実行可能プログラム置き場ということです。 コマンドも実態はプログラムなので権限や用途に合わせてわけて保存されます。

/bin

シングルユーザモードでも利用できるコマンドを置きます。逆の言い方をすると、「/usr/bin」や「/usr/local/bin」に置かれているコマンドなどはシングルユーザモードで利用できないということになります。シングルユーザモードは、基本的にOSが壊れて正常に起動できないなど非常時に利用するものですので、「/bin」にはごく基本的かつ非常時に利用するコマンドが置かれることになります。

/sbin

ここにはシステム管理用のコマンドが保存されます。多くのシステム管理用のコマンドはrootユーザーで(管理者になってから)実行されます。

/usr/bin

/usr/binには「シングルユーザモードで利用しない」かつ「RPMやdebなどのパッケージ管理システムによって、システムに管理されるコマンドやプログラム」が置かれます。非常時に利用するものではないが、システムを構成する重要なコマンドやプログラムはここに置かれることになります。また、ここには1000以上のコマンドがあるのでls -l /usr/bin | lessコマンドを使うと確認しやすいです。

/usr/sbin

/binと/usr/binの違いと同様。

/usr/local/bin

/usr/local/binは自分でインストールしたコマンドや自作のコマンドを置くのに使います。

まとめ

brewでインストールしたプログラムが/usr/local/Cellerディレクトリなどに入りシンボリンクを作成することでパスが通ること、各ディレクトリ構成について説明しました。後編では実際にrbenvとruby-buildをインストールして詳細を解説していきます!

参考

「/bin」「/usr/bin」「/usr/local/bin」ディレクトリの使い分け
https://linuc.org/study/knowledge/544/

Linux標準教科書
https://linuc.org/textbooks/linux/

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