20200321のRubyに関する記事は26件です。

rbenvを利用してRubyのバージョンを最新安定版にする

環境

  • macOS Mojave 10.14.6
  • rbenv 1.1.2
  • Homebrew 2.2.10

方法

1. 公式サイトで最新版を確認する。

https://www.ruby-lang.org/ja/downloads/
image.png

2. rbenvのインストール可能なバージョン一覧に、1.で確認したバージョンがあるか確認する。

$ rbenv install --list

(もし確認したバージョンが無い場合)

以下のコマンドで、rbenvとruby-buildを更新する。

$ brew upgrade rbenv ruby-build

再度、rbenvのインストール可能なバージョン一覧に、1.で確認したバージョンがあるか確認する。

$ rbenv install --list

3. rbenvで1.で確認したバージョンをインストールする。

※今回はバージョン2.7.0

$ rbenv install 2.7.0

インストールされているか確認する。

$ rbenv versions

4. インストールしたバージョンは適用させる。

(環境全体に適用)

 $ rbenv global 2.7.0

適用されているか確認する。(適用されていない場合はカレントディレクトリに適用させる。)

$ rbenv versions

(カレントディレクトリに適用)

 $ rbenv local 2.7.0

適用されているか確認する。

$ rbenv versions

おわりに

『もっと簡単にできる方法あるよーーー!』

『ここわかりにくいよーーー!』

『ここ間違っているよーーー!』

等あればコメントいただけるとめちゃくちゃ嬉しいです!!!

twiiter → https://twitter.com/jiko797torayo

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

railsストロングパラメータについて

はじめに

ストロングパラメータについて調べたので備忘録として記事にしました。

対象読者

プログラミング勉強始めたての方。

題材

自分のポートフォリオのUserオブジェクト周りを題材としています。

ストロングパラメータとは

ユーザー登録フォームなどによって送られてきた情報を限定して取得し、データベースに一気に保管出来るようにした処理のことです。なぜ、情報を限定するかというとユーザー側にいじって欲しくない情報をデータベースに保存させないようにするためです。
例えば管理者情報などをいじられて勝手に管理者にならないようにしています。
また、複数カラムにデータを保存できるようにする役割もあります。
実際に設定しているものがこちらです。

app/controllers/users_controller.rb
def create
    @user = User.new(user_params)
    if @user.save
      log_in @user
      flash[:success] = 'ユーザー登録完了'
      redirect_to root_url
    else
      render 'new'
    end
  end

private

  def user_params
    params.require(:user).permit(:name, :email, :password,
                                 :password_confirmation)

基本的にはコントローラーのprivate内(カプセル化)で定義し、create等のアクション渡します。(private内に定義する理由はこのコントローラないでしか参照しない、コードがすっきりするからです。)

ストロングパラメータ内では3つのメゾットが定義されておりそれぞれに以下のような役割があります。

params:フォームなどで送られてきたメゾットを取得
require:送られてくる値が二次元ハッシュのときに使い引数に渡されたものがキーとなっているもののみ取得する。
permit:引数に渡されたもののみデータベースに保存するのを許可する。

paramsについて

送られてきたメゾットを取得します。
フォームからのpostメゾットや検索フォームなどのgetメゾットでURLのクエリに入るデータがparamsで取得できるデータです。
※検索機能についてはこちら
https://qiita.com/E6YOteYPzmFGfOD/items/dc1ab3c19d4718a0edc8
実際に新規ユーザーを登録する時にはこんな感じでフォームからコントローラへ送られます。

Parameters: {"utf8"=>"✓", "authenticity_token"=>"OSab8g1vpkYUMQI74VcZX08Ci82gTPxBCK/
YOZ766OzRmPuM/pYW5+ZX8gh/l+87UPpag69De8f+NjhCs+SqmA==", 
"user"=>{"name"=>"test", "email"=>"foo@bar.com", "password"=>"[FILTERED]", "password_confirmation"=>
"[FILTERED]"}, "commit"=>"作成"}

パラメータを取り出す際はこのように指定すれば取り出せます。

params[:user]
<ActionController::Parameters {"name"=>"test",
 "email"=>"foo@bar.com", "password"=>"123456",
 "password_confirmation"=>"123456"} permitted: false>

params[:user][:name]※二次元ハッシュ キーのnameにさらにuserがキーになっている。
"test"

このようにして値を取り出せますが、このままcreateメゾットに渡してもエラーになります。

app/controllers/users_controller.rb
def create
    @user = User.new(user_params)※エラーになる。

なぜエラーになるのかというと先ほどパラメータで取得した値の中に「permitted: false」とありますがこれがfalseになっているせいです。primittedはマスアサイメント機能を許可する部分になります。マスアサイメントとはdbに値を保存する時に複数のカラムを一括で指定できる機能になります。これをtrueにかえるのがpermitメゾットです。

permit

実際にpermitでdbに保存する値を限定することによってpermitがtrueになります。
※まさにストロングパラメータの形です。

params[:user]
<ActionController::Parameters {"name"=>"test",
 "email"=>"foo@bar.com", "password"=>"123456",
 "password_confirmation"=>"123456"} permitted: false>

params.require(:user).permit(:name,:email,:password,:password_confirmation)
<ActionController::Parameters {"name"=>"test",
 "email"=>"foo@bar.com", "password"=>"123456",
 "password_confirmation"=>"123456"} permitted: true>

これでpermitted: trueになったのでストロングパラメータで取得したものをcreateアクションに渡すことで複数カラムに情報を保存できるようになりました。

requireについて

このメゾットは値が二次元ハッシュで送られる時(post)に必要となり、引数に渡したキーの値だけを取得できるようになります。

params
<ActionController::Parameters {"utf8"=>"✓",
"authenticity_token"=>"OSab8g1vpkYUMQI74VcZX08Ci82gTPxBCK/YOZ766OzRmPuM/pYW5+ZX8gh/l+87UPpag69De8f+NjhCs+SqmA==",
 "user"=><ActionController::Parameters {"name"=>"test",
 "email"=>"foo@bar.com", "password"=>"123456",
 "password_confirmation"=>"123456"} permitted: false>,
 "commit"=>"作成", "controller"=>"users",
 "action"=>"create"} permitted: false>

params.require(:user)
<ActionController::Parameters {"name"=>"test",
 "email"=>"foo@bar.com", "password"=>"123456",
 "password_confirmation"=>"123456"} permitted: false>

最後に

ここまでお付き合いいただきありがとうございました。
また、何か気になったことがあれば記事にしたいと思います。
間違い等コメントいただけると幸いです。
それではありがとうございました。

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

LoadError, Unable to autoload constant ~

本記事投稿のいきさつ

railsでアプリ作成をしていたところLoadErrorが発生し基礎知識がなかったためにハマったため忘れないように、書き残します。
また、同じエラーで困っている初心者のためになれば幸いです。

エラー発生

itemモデルとitem_imageモデルでアソシエーションを組み,以下の記述をしたところエラーが発生

items_controller.rb
  def new
    @item = Item.new
    @item_images = Item_image.new
  end

エラー表示は以下です。

LoadError in ItemsController#new
Unable to autoload constant Item_image, expected *****/models/item_image.rb to define it

解決まで

なるほど。アソシエーションの記入を間違えたのか。
と初学者の自分は考えitem_image.rbを確認します。
item_image.rbの記述は以下です。

item_image.rb
class ItemImage < ApplicationRecord
  belongs_to :item
end

あれ?ちゃんと書けている。
と初学者の自分は思いました。自分の仮説が外れたので、Google先生に聞いてみました。

解決

Googleで調べたところどうやら

モデル名にアンダーバーを使ってもクラス名にアンダーバーはつかないとのことです。

ということでitems_controller.rbを編集。

items_controller.rb
  def new
    @item = Item.new
    @item_images = ItemImage.new
  end

無事解決することができました。
とても基本的なことですが、今までモデル名にアンダーバーを使っていなかったため、気づくこと出来なかった自分にとってはいい経験になりました。

おわり

最後まで見ていただき、ありがとうございました。

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

ダックタイピング

ダックタイピングとは

もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない

(wikipediaより)

Rubyの例
 def test(foo)
   puts foo.sound
 end

 class Duck
   def sound
     'quack'
   end
 end

 class Cat
   def sound
     'myaa'
   end
 end

test(Duck.new)
test(Cat.new)
出力結果
quack
myaa

wikipedia読んでもだからなんなのというか、なんにも特別なことをしているように感じなかったのですが、調べるとどうやらそうではないことに気づきました。

型を意識しなくて良い

Javaと比較しているサイトがあり、そのコード例によりメリットが理解できました。

ruby
def test(foo)
  puts foo.sound
end

java
void test(Duck foo) {
   foo.sound();
}

もしもそのオブジェクトが Duckオブジェクト のように Walk()メソッド を呼び出すことができ、Duckオブジェクトのように Sound()メソッド を呼び出すことができるのなら、それは Duckオブジェクト として扱える。

私はpythonを主に使うので、型に対して意識することがほぼありません。rubyも同じでしょう。

しかし、Javaは違います。上記のコードでは、testは受け取るDuckの型を予め指定しています。これにより、型がなんであれ、振る舞いが同じならそれは問題ないという処理ができないわけです。

「引数 foo がどのようなオブジェクトであれ、 sound メソッドを呼び出せれば良い」と割り切っていることが、ダックタイピングです。

なるほどと思いました。
Rustは静的型付け言語ですが、トレイトという機能を使うことによりダックタイピングに似たコーディングができます。

参考文献

https://ja.wikipedia.org/wiki/ダック・タイピング
https://blog.mmmcorp.co.jp/blog/2018/10/26/go-duck-typing/

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

Pay.jpを用いた購入機能の実装

概要

Pay.jpを用いた購入機能の実装を備忘録としてまとめます。
修正点ありましたらご指摘お願いいたします。
Transaction(取り引き)テーブルおよびProduct(商品)テーブルをもとに作っていきます。

前提

  • Pay.jpのアカウント作成済み
  • Pay.jpにてクレジットカード登録機能は実装済み
  • ビューはHamlで記載
  • deviseにてログイン済み

手順

  1. Transactionテーブルを作成(購入済みの場合SOLD OUTを表示させるため)
  2. アソシエーションの設定
  3. Transactionコントローラーを作成
  4. 環境変数の設定
  5. ルーティングの設定
  6. マークアップ:購入内容確認画面
  7. マークアップ:購入完了画面
  8. マークアップ:購入済みの場合SOLD OUTを表示
  9. 購入データの確認

Transactionテーブルを作成

今回は購入済みの場合SOLD OUTを表示させるためにTransactionテーブルを作成します。

$ rails g model Transaction
db/migrate/20200000000000_create_transactions.rb
class CreateTransactions < ActiveRecord::Migration[5.2]
  def change
    create_table :transactions do |t|
      t.references :product, null: false, foreign_key: true
      t.references :user, null: false, foreign_key: true
      t.timestamps
    end
  end
end

マイグレートを実行

$ rails db:migrate

アソシエーションの設定

transaction.rb
class Transaction < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :product, optional: true
end

Transactionコントローラーを作成

$ rails g controller transactions
transactions_controller.rb
class TransactionsController < ApplicationController
  require 'payjp'
  before_action :set_card, only: [:pay_index, :pay]
  before_action :set_product

  def pay_index
    @top_image = @product.images.first
    @card = @set_card.first
    if @card.blank?
      redirect_to controller: "cards", action: "new"
    else
      Payjp.api_key = ENV["PAYJP_PRIVATE_KEY"]
      customer = Payjp::Customer.retrieve(@card.customer_id)
      @default_card_information = customer.cards.retrieve(@card.card_id)
    end
  end

  def pay
    @card = @set_card.first
    Payjp.api_key = ENV['PAYJP_PRIVATE_KEY']
    Payjp::Charge.create(
    :amount => @product.price,
    :customer => @card.customer_id,
    :currency => 'jpy',
  )
  redirect_to action: 'done', product_id: @product
  end

  def done
    @top_image = @product.images.first
    Transaction.create(product_id: @product.id, user_id: current_user.id)
  end

  private

  def set_card
    @set_card = Card.where(user_id: current_user.id)
  end

  def set_product
    @product = Product.find(params[:product_id])
  end

end

環境変数の設定

コントローラ内のENV["PAYJP_PRIVATE_KEY"]は環境変数でテスト秘密鍵を設定し読み込む。
今回はdotenvとgonを利用する。

dotenv:Railsの環境変数管理
gon:JSにてRailsで定義した環境変数を使用
参考:https://qiita.com/3443/items/44202ff6504210592570#comments

※gonはCard登録機能にて使用したため今回は関係ありません

ルーティングの設定

routes.rb
Rails.application.routes.draw do
  devise_for :users
  root "products#index"
  resources :users, only: [:edit, :update]
  resources :products
  resources :cards, only: [:new, :show, :destroy] do
    collection do
      post 'pay_show', to: 'cards#pay_show'
      post 'pay', to: 'cards#pay'
    end
  end

  ##今回の該当箇所
  resources :transactions, only: [:index] do
    collection do
      get 'pay_index', to: 'transactions#pay_index'
      post 'pay', to: 'transactions#pay'
      get 'done', to: 'transactions#done'
    end
  end

end

マークアップ:購入内容確認画面

Image from Gyazo

pay_index.html.haml
.transaction-pay
  .transaction-pay__content
    %h2.transaction-pay__title 購入内容の確認
    .transaction-pay__item
      .transaction-pay__item-box
        = image_tag @top_image.image.url, alt:"商品画像", class: "transaction-pay__item-image"
      .transaction-pay__item-detail
        %p.transaction-pay__item-detail--name
          = @product.name
        .transaction-pay__item-detail-price
          .transaction-pay__item-detail-price--text.transaction-pay__item-detail-price--text
            = @product.price
          .transaction-pay__item-detail-price--shipping (税込)送料込み
    .transaction-pay__table
      .transaction-pay__table-inner
        .transaction-pay__table-form
          .transaction-pay__table-content
            .transaction-pay__table-pay
              %p.transaction-pay__table-pay--title 支払い金額
            .transaction-pay__table-price
              %p.transaction-pay__table-price--title%p.transaction-pay__table-price--title
                = @product.price
          .transaction-pay__table-way
            %h3 支払い方法
            .transaction-pay__table-register
              - if @default_card_information.blank?
                %i.fas.fa-plus-circle
                %span.icon-register
                = link_to "登録してください", new_card_path
              - else
                = "**** **** **** " + @default_card_information.last4
                - exp_month = @default_card_information.exp_month.to_s
                - exp_year = @default_card_information.exp_year.to_s.slice(2,3)
                = exp_month + " / " + exp_year
          .transaction-pay__table-buy
            = form_tag(action: :pay, method: :post, product_id: @product) do
              %button.transaction-pay__table-buy-button 購入する

マークアップ:購入完了画面

Image from Gyazo

done.html.haml
.transaction-done
  .transaction-done__content
    .transaction-done__text
      購入が完了しました!
    .transaction-done__image
      = image_tag @top_image.image.url, alt:"商品画像", class: "transaction-done__image--img"
    .transaction-done__title
      = @product.name
    .transaction-done__price
      .transaction-done__price--text
        = @product.price
      .transaction-done__price--info
        (送料込み)

マークアップ:購入済みの場合SOLD OUTを表示

Image from Gyazo

show.html.haml
.product-show
  .product-show__main
    .product-show__content
      .product-show__top-content
        .product-show__item-box
          .product-show__item-box--name
            = @product.name
          .product-show__item-box__body
            .product-show__item-box__body--top-img
              = image_tag @top_image.image.url, alt:"トップ画像", class: "product-show__item-top-img"
            .product-show__item-box__body--list
              - @images.each do |image|
                .product-show__item-box__body--sub-img
                  = image_tag image.image.url, alt:"サブ画像", class: "product-show__item-sub-img"
          .product-show__item-box--price
            %span 
            = "#{@product.price}円"
            .product-show__item-box--price-detail
              %span.product-show__item-box--price-detail-text
                (税込)
              %span.product-show__item-box--price-detail-text
                = @product.delivery_charge
          .product-show__item-box--item-detail
            = @product.name
          -# 商品出品者であれば表示させない
          - if user_signed_in? && (current_user.id == @product.user_id)
          - else
            .product-show__transaction
              .product-show__transaction-box
                -# 商品購入済みであればSOLD OUT
                - if @product_id.present?
                  .product-transaction-btn
                    SOLD OUT
                - else
                  = link_to "購入画面に進む", pay_index_transactions_path(product_id: @product), class: "product-transaction-btn"

購入データの確認

購入ができていれば下記のURLにて履歴が確認できます。
https://pay.jp/d/charges

以上です

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

docker-compose buildでYou must use Bundler 2 or greater with this lockfile.とエラーが出た話

経緯

「既存の作成済のrailsアプリケーションにDockerを導入してみたい!!でも、いきなり導入していろいろ変なことになったら嫌だな」ということで、Railsチュートリアルの開発環境をDockerにしてみなイカ?という記事を参考にさせていただき、まずはrailsチュートリアルをdockerにのせようと、奮闘している最中、

You must use Bundler 2 or greater with this lockfile.
ERROR: Service 'app' failed to build: The command '/bin/sh -c bundle install --jobs=4' returned a non-zero code: 20

というエラーが。。

解決した方法

You must use Bundler 2 or greater with this lockfile.

これは、Bundlerのバージョンが2以上を使わなければならなかったのに、Dockerのimageで1.X.Xを利用していたことが原因だったみたいです。

確かに、Gemfile.lockを見ると、一番最後の行に、BUNDLED WITH 2.1.4の記載が。。。

そこで、docker-compose buildした際に、bundlerをinstallすれば問題ないと知り、
Dockerfileにて

~~~略~~~
RUN  bundle install 

としていたところを

~~~略~~~
RUN gem install bundler && bundle install 

のように、bundlerをインストールすることで解決しました!!

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

#Ruby の tap で ネストされたハッシュのキーを削除しつつ返り値で変更後の値を受け取る

hash[:foo][:bar] のキーを削除したい

{foo: {bar: 1, yah: 2} }.tap { |h| h[:foo].delete(:bar) }
# => {:foo=>{:yah=>2}}

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3041

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

[Rails]いいねした商品をマイページに一覧表示する

ユーザー(user)のマイページ(show)にいいね(like)した商品一覧を表示させます。
モデルの指定はこちらに記載してます。

 ルーティング

routes.rb
  resources :users, only: [:index, :show] do
    collection do
      get :likes
    end
  end

userのshowアクションにネストさせます。

モデル

user.rb
  has_many :likes, dependent: :destroy
  has_many :like_items, through: :likes, source: :item

※今回のポイント
like_itemsでuserがどのitemにいいねしているか取得できます。

 ビュー

users/_side-bar.html.haml
.side-bar
  %section.side-bar__group
    %h2.side-bar__group__title
      マイページメニュー
    %li.side-bar__group__list
      = link_to "お知らせ", "#"
    %li.side-bar__group__list
      = link_to "いいねした商品", likes_users_path

リンク先likes_users_path指定

users/likes.html.haml
.container-show 
  = render "side-bar"
  .main
    %section.main__group
      %h2.main__group__title
        〇〇さんのマイページ
    %section.main__table
      %h2.main__table__header
        いいね!一覧
      %ul.main__table__list
        - current_user.like_items.each do |item|
          %li
            =link_to item_path(item), class: "main__table__list__item" do
              %figure 
                = image_tag asset_path(item.images[0].content), :size =>'48x64'
              .main__table__list__item__body
                .main__table__list__item__body__text
                = item.name
                %br
                = item.price %i.fas.fa-chevron-right

likeファイルを作成
- current_user.like_items.each do |item|で現在ログインしているuserがいいねしたitemを取得しています。

=link_to item_path(item)で各商品詳細ページに遷移できるようにしてます。

完成イメージ

Image from Gyazo

Image from Gyazo

ご指摘ありましたらぜひコメントよろしくおねがいします!!

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

[Rails]非同期のいいね機能実装

ユーザー(user)が出品した商品(item)にいいねできる機能を実装してます。

Likeモデル、テーブル作成

rails g model Like

like.rb
class Like < ApplicationRecord
end
XXXXXXXXXXX_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.2]
  def change
    create_table :likes do |t|
      t.integer     :user_id
      t.integer     :item_id
      t.timestamps
    end
  end
end

rails db:migrateマイグレーションファイル実行

モデル

通常はuserとitemは1対多の関係ですが、いいね機能実装時に関してはlikeテーブルが加わるので、多対多の関係になります。

user.rb
  has_many :likes, dependent: :destroy
  has_many :like_items, through: :likes, source: :item
item.rb
  has_many :likes, dependent: :destroy
  has_many :liking_users, through: :likes, source: :user

dependent: :destroyはいいねを外した時に、中間likesテーブルにある該当userとitemのレコードを一緒に削除してくれます。

:like_itemsはuserがどのitemをいいねしているのかを取得
:liking_usersはitemがどのuserによっていいねされているのか取得

through: :likesは多対多の関係で中間likeテーブルを経由するための関連付けで記述

source:オプションは関連付け元の名前を指定するため記述

like.rb
class Like < ApplicationRecord
  belongs_to :item, counter_cache: :likes_count
  belongs_to :user
end

counter_cahce: :likes_countはリレーションされているlikeの数の値をリレーション先のlikes_countというカラムの値に入れるという意味です。なのでlikes_countカラムをitemsテーブルに追加しましょう。

itemsテーブルにlikes_countカラム追加

rails g migration AddLikes_countToItems

XXXXXXXXXXX_add_likes_count_to_items.rb
class AddLikesCountToItems < ActiveRecord::Migration[5.2]
  def change
    add_column :items, :likes_count, :integer
  end
end

rails db:migrateマイグレーションファイル実行

ルーティング設定

routes.rb
resources :items
  member do
    post   '/like/:item_id' => 'likes#like',   as: 'like'
    delete '/like/:item_id' => 'likes#unlike', as: 'unlike'
  end

いいねをつける時→like 外す時→unlike
as:でルーティングに名前を付けれる。この二つはlike_path,unlike_pathとして使えるようになります。

コントローラー

rails g contoller likes

likes_controller.rb
class LikesController < ApplicationController
  before_action :set_variables

  def like
    like = current_user.likes.new(item_id: @item.id)
    like.save
  end

  def unlike
    like = current_user.likes.find_by(item_id: @item.id)
    like.destroy
  end

  private
  def set_variables
    @item = Item.find(params[:item_id])
    @id_name = "#like-link-#{@item.id}"
  end
end

@id_nameは非同期で使用します。

ビュー

items/show.html.haml
  .option
    = render partial: 'likes/like', locals: { item: @item }

renderを使用し、部分テンプレートへ誘導
likesディレクトリに部分テンプレート_like.html.hamlファイルを作成

likes/_like.html.haml
.option__like{:id => "like-link-#{@item.id}"}
  - if current_user.likes.find_by(item_id: item.id)
    = link_to unlike_item_path(@item.id, @item.id), method: :delete, remote: true, class: "option__like-on" do
      .fas.fa-star
      .option__like-on__text いいね!
      .option__like-on__count
        =item.likes.count
  - else
    = link_to  like_item_path(@item.id, @item.id), method: :post, remote: true, class: "option__like-off" do
      .fas.fa-star
      .option__like-off__text いいね!
      .option__like-off__count
        =item.likes.count

いいねボタンのビューを記述します。

{:id => "like-link-#{@item.id}"}をつけることで@itemのボタンであることを指定します。

remote: trueをつけることでリンクを押した時、ajaxを発火させます。

=item.likes.countでいいねされた数を表示します。

いいねボタンの非同期化

like.js.hamlunlike.js.hamlファイル作成

likes/like.js.haml
$("#{@id_name}").html('#{escape_javascript(render("likes/like", item: @item  ))}');

likes/unlike.js.haml
$("#{@id_name}").html('#{escape_javascript(render("likes/like", item: @item  ))}');

コントローラーで定義した@id_nameを指定し、escape_javascriptで先ほど作成した_likeファイルを埋め込んでます。

Sass

item.show.scss
          .option {
            display: flex;
            justify-content: space-between;
            &__like {
              &-on {
                text-decoration: none;
                padding: 11px 10px;
                border-radius: 40px;
                color: #3CCACE;
                border: 1px solid #ffb340;
                display: flex;
                line-height: 16px;
                .fas.fa-star {
                  padding-right: 5px;
                }
                &__text {
                  padding-right: 5px;
                }
              }
              &-off {
                text-decoration: none;
                padding: 11px 10px;
                border-radius: 40px;
                color: #333;
                border: 1px solid #f5f5f5;
                display: flex;
                line-height: 16px;
                background: #f5f5f5;
                .fas.fa-star {
                  padding-right: 5px;
                }
                &__text {
                  padding-right: 5px;
                }
              }
            }
          }

sassの説明は省略します。

完成イメージ

Image from Gyazo

ユーザーがいいねした商品一覧を表示させたい方はこちらをご覧ください

間違えている部分があったらぜひコメントよろしくお願いします!!

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

[Ruby on Rails]enumを用いたselectフォームの作成

selectを作る際に、選択肢が3つ程度のものであればoptionで記載しても良いかとは思いますが、多くなればコードが冗長になってしまいます。
enumを用いているカラムに対してであれば、スッキリ綺麗なコードで実装できます。

実装した機能

gyazoリンク
https://gyazo.com/67a692c100281014ff59426fa6eb1a53

enumとは

列挙型、列挙クラスといわれる。
本記事はenumについてではないため、ザックリとした説明になりますが、
自分の認識では、
わざわざ、tableを作成し、外部キーとして呼ばなくとも中身を持ったidとして管理できるもの

どんなものに使うのか?

enumを使う代表的なものといえば、都道府県でしょうか
optionで書くのも面倒ですし、テンプレを使ったとしても冗長で読みやすいコードとは言い難いですね。

product.rb
 enum delivery_prefecture:{
   "北海道":1,"青森県":2,"岩手県":3,"宮城県":4,"秋田県":5,"山形県":6,"福島県":7,
   "茨城県":8,"栃木県":9,"群馬県":10,"埼玉県":11,"千葉県":12,"東京都":13,"神奈川県":14,
   "新潟県":15,"富山県":16,"石川県":17,"福井県":18,"山梨県":19,"長野県":20,
   "岐阜県":21,"静岡県":22,"愛知県":23,"三重県":24,
   "滋賀県":25,"京都府":26,"大阪府":27,"兵庫県":28,"奈良県":29,"和歌山県":30,
   "鳥取県":31,"島根県":32,"岡山県":33,"広島県":34,"山口県":35,
   "徳島県":36,"香川県":37,"愛媛県":38,"高知県":39,
   "福岡県":40,"佐賀県":41,"長崎県":42,"熊本県":43,"大分県":44,"宮崎県":45,"鹿児島県":46, 
   "沖縄県":47
 }

このように記載することで、今回でいうdelivery_prefectureがidで管理されるようになります
migrationファイルはintegerにしておきましょう

selectフォームの作成

products/new.html.haml
 .form__group
   = f.label :delivery_prefecture do
     発送元の地域
     %span.form-description.form-require 必須
   = f.select :delivery_prefecture, Product.delivery_prefectures.keys,{include_blank: '選択してください'},{class: "exhibition__select"}

今回であれば

Product.delivery_prefectures.keys
モデル.カラム名(複数形).keys

この記述で選択肢に先ほどのenumの記述部分が適用されます。
注意としては、複数形にすることです。

ちなみに

{include_blank: '選択してください'}

によって何も選択されていない時に 選択してください と表示されるようにしています

おまけ

products/new.html.haml
 .form__group
   = f.label :delivery_days do
     発送までの日数
     %span.form-description.form-require 必須
   = f.select :delivery_days, Product.delivery_days.keys,{include_blank: '選択してください'},{class: "exhibition__select"}
product.rb
  enum delivery_days:{
    "1~2日で発送": 1,
    "2~3日で発送": 2,
    "3~7日で発送": 3,
  }

このようにカラム名に複数形が使われている場合(あまり望ましくないが、dayだと変なので今回は止むを得ず、、)は、そのままカラム名を記載するだけでうまく行きます。

最後に

本記事がQiita初投稿になります:innocent:
アドバイス等いただけたら幸いです!

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

Rubyの基本的なメソッドと参照について学んだこと

目次

  • Rubyのメソッド
  • 参照の概念
  • ライブラリ

Rubyのメソッド

Rubyには標準でいろんなメソッドがあります。
また、自分でメソッドを定義することも可能です。


メソッドに引数を設定する

スクリーンショット 2020-03-21 17.43.52.png

cry.rb
def cry(animal)
  if animal == 'dog'
    puts 'woof woof!'
  elsif animal =='cat'
    puts 'meow meow!'
  end
end
cry('dog') # -> woof woof!
cry('cat') # -> meow meow!
# 引数に過不足があるとエラーになる
# cry -> `cry': wrong number of arguments (given 0, expected 1) (ArgumentError)

メソッドを定義する際引数を設定できます。
今回はcryというメソッド名で引数によって動物の鳴き声を出力するメソッドを定義しました。


引数にはデフォルト値をつけることも可能

スクリーンショット 2020-03-21 17.37.23.png

cry.rb
  def cry(animal: 'cat')
    if animal == 'dog'
      puts 'woof woof!'
    elsif animal =='cat'
      puts 'meow meow!'
    end
  end

  cry(animal: 'dog') # -> woof woof!
# 引数がない場合でもエラーにならない!
  cry # -> meow meow!

通常引数は呼び出し側(実引数)とメソッド定義側(仮引数)で数が一致している必要があります。
ですが、メソッド定義側でデフォルト値を設定することが可能です。
今回はcryというメソッド名で引数によって動物の鳴き声を出力するメソッドに
引数が存在しない場合はデフォルト値をcatとして定義しました。


標準出力で使われるメソッド

puts 'Hello World'
Hello World
=> nil

print 'Hello World'
Hello World=> nil

p 'Hello World'
"Hello World"
=> "Hello World"

メソッドを定義してターミナルに出力確認をする際今回の記事ではputsメソッドを使用していますが他にもターミナルに出力できるメソッドがあります。
irbを使用して確認して見た結果

puts
引数のオブジェクトをto_sメソッドで文字列に変換し、改行を加えて出力する。
戻り値: nil

print
引数のオブジェクトをto_sメソッドで文字列に変換し、改行ををせず出力する。
戻り値: nil

p
引数のオブジェクトをinspectメソッドで文字列に変換し、改行ををせず出力する。
戻り値: 文字列


真偽値を返すメソッド

スクリーンショット 2020-03-21 18.04.40.png

boolean.rb
# ?で終わるメソッドを自作する場合
def what_name?(name)
  name == 'bob'
end
p what_name?('tom') # -> false
p what_name?('bob') # -> true

真偽値を返すメソッドとしてメソッド名の終わりに?をつけたメソッドがあります。
"".empty?"Hello World".include?(Hello)など

自作する場合でもメソッド名に?をつけたほうが戻り値がわかりやすくなります。


エイリアスメソッド

スクリーンショット 2020-03-21 18.18.57.png

alias.rb
# メソッドを定義する

def hello
  puts 'Hello World!'
end
# aliasを設定する
alias greeting hello

# 元のメソッド名でも呼び出せ、aliasで設定した別名でも同様に呼び出すことができる
hello # -> Hello World!
greeting # -> Hello World!

Rubyにはメソッドに複数の名前をついている場合、自分で任意の名前
で定義することができます。
今回はhelloメソッドの名前をgreetingと別名で定義しました。


Rubyの戻り値

expression.rb
def expression
  hello = 'Hello World'
end
# retern分を記載しなくても最後に評価された式が戻り値として取得できる
p expression # -> Hello World
expression.js
function expression() {
  let hello = "Hello World"
  // return hello
}
// return文を記載しなければ戻り値を取得できない
console.log(expression()) // -> undefined

function expression() {
  let hello = "Hello World"
  return hello
}
// return文を記載すれば戻り値を取得できる
console.log(expression()) // -> Hello World

Rubyのメソッドではreturnを記載しない場合、
最後に評価されたものが戻り値になります。
これは他の言語と少し違います。
試しにrubyでreturnを記載しないで定義したメソッドと
javascriptでreturnを記載しないで定義したメソッドを比較してみます。


擬似変数

pseudo_variable.rb
p true # -> true
p self # -> main
p __FILE__ # -> ファイル名
p __LINE__ # -> 行番号
p __ENCODING__ # -> エンコーディング

# 擬似変数に代入しようとすることはできない
# p __ENCODING__ = 'test' # -> Can't assign to __ENCODING__

Rubyには擬似変数と呼ばれる特殊な変数が存在します。
擬似変数は値を変更することはできません。


破壊的メソッド

destroy.rb
lang = 'ruby'

# upcaseだと呼び出した文字列自身は変化しない
p lang.upcase # -> RUBY
p lang #-> ruby

#upcase!だと呼び出した文字列自身が変化する
p lang.upcase! # -> RUBY
p lang # -> RUBY

Rubyのメソッドにはオブジェクトの値そのものを変更してしまうメソッドが存在します。
今回は upcase,upcase!を使用して'ruby'という文字列を'RUBY'に変換します。
以下の出力結果を確認するとupcaseの場合、変数aの値は変化していません。
しかし、upcase!の場合変数aの値が変わっています。
このように破壊的メソッドを使用すると呼び出したオブジェクトの状態を変更することができます。


参照の概念

Rubyのメソッドが色々あるなか破壊的メソッドを用いた場合、値が書き換わりました。

実際にはRubyの変数に代入すると変数に値が格納されるわけではありません。
変数にはオブジェクトの参照情報が格納されます。


メモリ領域

 
study_night_boy.png

パソコンがしてくれる作業を僕ら人間に当てはめるとこんな感じになります。

 1. 考える脳みそ -> CPU
 
 2. 作業する机 -> メモリ
 スクリーンショット 2020-03-21 19.29.54.png

変数に値を格納した時パソコンのメモリ領域(作業する机の領域)がどうなるかというとこんなイメージになります。
スクリーンショット 2020-03-21 21.57.34.png
スクリーンショット 2020-03-21 19.54.49.png

destroy.rb
lang = 'ruby'

# upcaseだと呼び出した文字列自身は変化しない
p lang.upcase # -> RUBY

# upcaseした際のオブジェクト番号を確認
p lang.upcase.object_id # -> 70178687801560

p lang #-> ruby
# 変数のオブジェクト番号を確認
p lang.object_id # -> 70178687801780

# upcase!した際のオブジェクト番号を確認
p lang.upcase!.object_id # -> 70178687801780
p lang # -> RUBY
# 変数のオブジェクト番号を確認
p lang.object_id # -> 70178687801780

ここで先ほどの破壊的メソッドについて戻ろうと思います。

upcaseupcase!でのオブジェクト番号を確認すると
upcaseではオブジェクト番号が違うため、新しくオブジェクトを生成し、そのオブジェクトの参照情報を格納しております。

upcase!ではオブジェクト番号が同一であることがわかります。
このことから変数にはオブジェクトへの参照が格納されており破壊的メソッドを使用することで、指定のオブジェクトの中身を書き換えているということがわかりました。


ガベージコレクション(GC)

スクリーンショット 2020-03-21 20.12.40.png

パソコンがいろんな処理を行う際、作業机(メモリ領域)の場所を確保していきます。
作業机(メモリ領域)が場所が減っていくとそのうちスペースがいっぱいになってしまいます。
スペースがいっぱいになると僕ら人間が効率よく作業できないのと同様にパソコンも作業スペース
(メモリ領域)が減っていくと何もできなくなり処理が重たくなったり、最悪システムが停止したりします。
このようにメモリ領域が減っていく現象をメモリリークと言います。

このような状態にならないようにRubyにはガベージコレクションという機能が備わっています。
ガベージコレクションのおかげで僕らが作業スペースの後片付け(メモリの解放)をしなくても
使用されなくなったオブジェクトを片付けてくれます。
※家事代行サービスにお願いすればプロがよしなにやってくれる感じみたいですね。


ライブラリ

スクリーンショット 2020-03-21 22.31.19.png
Rubyでは最初から便利なプログラムがたくさん用意されています。
その便利なプログラムを寄せ集めたものをライブラリと言います。


組み込みライブラリ

スクリーンショット 2020-03-21 20.34.31.png

embedded_library.rb
text = 'Hello World'
p text.class # -> String
p text.methods.include?(:length) # -> true
p text.length # -> 11

人間でいうと本人の習得しているスキルのイメージです。
英語をネイティブレベルまで習得しているなら海外旅行に行って話す際、教科書を見なくても話せると思います。
※僕は英語話せませんが、、、

組み込みライブラリは Ruby 本体に組み込まれているため、わざわざ処理を実装しなくても
同様の処理をするプログラムが組み込みライブラリに存在していればそれを使用すれば良いのです。

今回は文字列の長さを取得する処理を実装するために、Stringクラスのlengthメソッドを使用しました。


標準ライブラリ

スクリーンショット 2020-03-21 21.02.55.png

スキルはまだ身についてないけど家にある本棚から書籍を使えばアプリ開発ができるのと同じように
標準ライブラリではあるものの、組み込みライブラリでないものについてはライブラリを読み込むことで使用することができます。

今回はTimeクラスに定義されているstrptimeメソッドを使用します。

standard_library.rb
require 'Time'
p Time.strptime('2020-03-21T01:58:30+09:00', '%Y-%m-%dT%H:%M:%S%z') # -> 2020-03-21 01:58:30 +0900

gem

スクリーンショット 2020-03-21 21.20.26.png

スキルが身についていない、家の本棚にもない場合でも問題ありません。
Rubyでは偉大な先輩エンジニアの方々が外部ライブラリとして、RubyGemsにアップロードしてくれています。
僕らは使いたいgemをそこからダンロードして、自分の端末にインストールすることで使用することができます。

今回はrails 5.2.3というgemをインストールして見ました。
gem i -v 5.2.3 rails


参考

プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで

値渡しと参照渡しの違いを理解する

Rubyにおける(オブジェクトへの参照の)値渡しを理解しようとして無知を感じた

「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

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

複数画像投稿で盛大に自爆した時の確認事項[備忘録]

はじめに

某フリマアプリの模倣アプリを開発中、出品機能実装で複数画像の登録に死ぬほど手を焼いたので、備忘録として掲載します。

誤った記述などあればご指摘いただけると幸いです。

開発環境・前提

Ruby 2.5.1p57
Ruby on rails 5.2.3
jquery-rails 4.3.5
haml-rails 2.0.1
sass-rails 5.1.0
CarrierWave 2.1.0

完成コード

先に完成コードを載せておく。

image.rb
  belongs_to :item, optional: true
  validates_presence_of :item
  validates :content, presence: true
  mount_uploader :content, ImageUploader
item.rb
  belongs_to :brand, optional: true
  belongs_to :user, optional: true
  belongs_to :category,  optional: true
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true
items_controller.rb
def new
    @item = Item.new
    @brands = Brand.all
    @category_parent_array = ["指定なし"]
    Category.where(ancestry: nil).each do |parent|
      @category_parent_array << parent.name
    end
    @item.images.build
  end

  def create
    @item = Item.new(item_params)

    if @item.save!
      @image = @item.images.create
      redirect_to :root
    else
      render :new
    end
  end

private
  def item_params
    params.require(:item).permit(
      :name, :description, :condition, :price, 
      :fee, :brand_id, :area, :shipping_days, 
      images_attributes: [:content, :id, :_destroy]
      ).merge(user_id: current_user.id, category_id: params[:category_id], brand_id: params[:item][:brand_id])
  end
new.html.haml
#画像投稿フォームの記述部分
.main-items
    = form_with model: @item, local: true do |f|
      .wrapper.image-wrapper
        #image-box.image-wrapper__image-box
          = f.fields_for :images do |i|
            .image-wrapper__image-box__js.js-file_group{data:{index: "#{i.index}"}}
              = i.label :content, class: "image-wrapper__image-box__js__label" do
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag(f.image.content)
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url" 
                = i.file_field :content, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content"
              .js-remove
                %span.js-remove__text
                  削除

モデルへのmout_uploaderの記述

before

image.rb
mount_uploaders :content, ImageUploader

after

image.rb
mount_uploader :content, ImageUploader

mount_uploaderとするかmount_uploadersか。

error.message
NoMethodError (undefined method `map' for #<ActionDispatch::Http::UploadedFile......> #省略
Did you mean?  tap):

app/controllers/items_controller.rb:68:in `create'

mount_uploadersにすると、デフォルトでmapメソッドが使われてしまう。

つまり、1つのfile_fieldに複数の画像データが入っている配列である必要があるのだ。

そういう時は、file_fieldにmultiple: trueを記載する必要がある。

multiple: true

multiple: true を記述すると、一つのfile_fieldに複数画像をアップロードしようとする。

修正前の記述

new.html.haml
= form_with model: @item, local: true do |f|
      .wrapper.image-wrapper
        #image-box.image-wrapper__image-box
          = f.fields_for :images do |i|
            .image-wrapper__image-box__js.js-file_group{data:{index: "#{i.index}"}}
              = i.label :content, class: "image-wrapper__image-box__js__label" do
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag(f.image.content)
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url" 
                = i.file_field :content, multiple: true, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content", required: "required" 
              .js-remove
                %span.js-remove__text
                  削除

inputタグのtype[file]部分のHTML(検証)

multiple有り
<input multiple="multiple" class="image-wrapper__image-box__js__label__file js-file" 
id="item_images_attributes_0_content" type="file" 
name="item[images_attributes][0][content][]">

multiple無し
<input class="image-wrapper__image-box__js__label__file js-file"
 id="item_images_attributes_0_content" type="file"
 name="item[images_attributes][0][content]">

デフォルトで設定されるname属性が変わる

私の場合は、各file_fieldに一つずつ保存させるようなコードを書いていたのにもかかわらず、multipleの記述をしてしまっていて、エラーが起きた。

labelタグのfor属性

labelタグのfor属性は、連動させたい子要素のidの値を記述する必要がある。

修正前のlabel部分の記述
html.haml
            %label.image-wrapper__image-box__js__label
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag(f.image.content)
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url" 
                = i.file_field :content, multiple: true, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content", required: "required" 
              .js-remove
                %span.js-remove__text
                  削除
修正後
html.haml
            = i.label :content, class: "image-wrapper__image-box__js__label" do
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag(f.image.content)
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url" 
                = i.file_field :content, multiple: true, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content", required: "required" 
              .js-remove
                %span.js-remove__text
                  削除

labelタグの性質をよく理解せずに使っていた。
修正前の記述だと、検証でみてみるとわかるが、labelタグにfor属性が付与されておらず、inputタグのid属性(ここでは、item_images_attributes_0_content)に対応しておらず、不具合がおきた。

後からわかったことだが、、hidden_fieldの記述を消してやれば、%labelのままでも支障はないことがわかった。

hidden_fieldの記述

修正前の記述

new.html.haml
= form_with model: @item, local: true do |f|
      .wrapper.image-wrapper
        #image-box.image-wrapper__image-box
          = f.fields_for :images do |i|
            .image-wrapper__image-box__js.js-file_group{data:{index: "#{i.index}"}}
              = i.label :content, class: "image-wrapper__image-box__js__label" do
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag(f.image.content)
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url" 
                = i.file_field :content, multiple: true, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content", required: "required"
                = i.hidden_field :item_id, value: @item.id
              .js-remove
                %span.js-remove__text
                  削除

imagesのitem_idをparamsに送るための記述をしていたが、idや外部キーはデフォルトで送られるようになっているため、必要なかった。逆に、これがあることによって、:contentが入っていない空のfile_fieldがhidden_fieldと共にparamsに送られてしまうため、validationに引っかかってしまう。

最後に

チーム開発で商品出品機能を担当したことにより、HTML&CSS, jQuery, Rubyについての知識がかなり深まった。欲張りな性格なので、いろんな記事のいいとこ取りをしようとした結果、こんなにもの何重もの罠を自分で仕掛けて自分でハマるということになってしまった。次からは是非とも一つ一つの用法や性質を理解した上で、実装していきたい。

でも、何かしらの初学者ってこういう風に泥臭く成長していくのかなぁとも思った。

諦めたら、そこで試合終了だよ。

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

Railsチュートリアルメモ - 第13章

メモの目次記事はこちら

公式Railsチュートリアル第13章へのリンク

サマリ

  • マイクロソフトの表示、投稿、削除
    • モデルの関連付け(has_manyおよびbelongs_to
    • モデルを使ったレコードの取得(取得条件、並び順、取得件数の指定)
    • ラムダ式 (Stabby lambda) を使ったProcオブジェクトの生成
  • Homeページの動的な出し分け
  • CarrierWaveを使った画像ファイルのアップロード
  • ImageMagick(+MiniMagick)を使った画像ファイルのリサイズ
  • 本番環境でのfogを使ったS3への画像アップロード

ポイント

  • rails generate modelの際、referencesを指定するとFKの指定を行ってくれる

e.g. rails generate model Micropost content:text user:referencesを実行した場合

class Micropost < ApplicationRecord
   belongs_to :user
end
  • モデルの関連付けを行うと、1対Nの1側からN側のオブジェクトを生成することができ、N側のオブジェクトには1側のオブジェクトのidが設定される。

    • e.g.
      • user.microposts.create
      • user.microposts.create! => !をつけると生成失敗時に例外を発生させる
      • user.microposts.build
        • buildメソッドはオブジェクトを返すがデータベースに反映はしない
  • モデル内でdefault_scopeを指定することで、並び順を指定することができる

default_scope -> { order(created_at: :desc) }
  • ->というラムダ式は、ブロックを引数に取り、Procオブジェクトを返す
  • Procオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価する
  • モデルの関連付けをした1対多の1側にdependent: :destroyを付与しておくと、1側のオブジェクトが削除された際に、多側のオブジェクトも一緒に削除される
  • orderメソッド、takeメソッドを使って取得するレコードの並び順と件数を指定できる
User.order(:created_at).take(6)
  • whereメソッドでレコードの取得条件を指定できる。?句を使うことで、変数をエスケープし、SQLインジェクションを防ぐことができる。
  def feed
    Micropost.where("user_id = ?", id)
  end
  • フォーム内でバリデーションエラーがあった場合、form_forのブロック変数のobject属性にエラーが入る
  • errorパーシャルなどにrenderから変数を引き渡す場合、第二引数にハッシュを指定する e.g.
  <%= form_for(@user) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <% end %>
<% if object.errors.any? %>
<div id="error_explanation">
    <ul>
        <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
        <% end %>
    </ul>
</div>
<% end %>

renderには以下の使い方がある

  1. 引数ががアクション名(new, editなど) => アクションに対応したerbの描画
  2. 引数がパーシャル => パーシャルの描画(第二引数に与えた変数をパーシャル内で使用できる)
  3. 引数がインスタンス変数 => インスタンス変数に対応したパーシャルを探して描画
    • e.g. render @feed_items@feed_itemsの中身がmicropostであれば_micropostパーシャルを探して描画
  • フォームが含まれる画面を描画する際は、form_for(@インスタンス変数)の@インスタンス変数をbuildして空のオブジェクトを用意してから描画する必要がある。
  • フォームが送信されたら改めてparamsから取得した値を使用して@インスタンス変数をbuildしsaveする。
  • erb内のlink_toメソッドにdata: { confirm: "You sure?" }を渡すとリンククリック時に確認のダイアログが表示される
  • redirect_to request.referrerと記載すると、一つ前のページにリダイレクトさせられる

  • CarrierWaveを使った画像ファイルの取り扱い手順

    • Gemfileにcarrierwaveを追加し、bundle install
    • rails generate uploader Xxxでアップローダーを作成
    • モデルにmount_uploader :picture, PictureUploaderを追加
    • ビューにimage_tagを追加
      • <%= image_tag micropost.picture.url if micropost.picture? %>
<span class="picture">
    <%= f.file_field :picture %>
</span>
  • モデルに独自定義したバリデーションを追加する場合は、validatesではなくvalidateを使用する

感想

  • 終盤のコンテンツだけあってなかなか内容が盛りだくさんだった。
  • リスト13.50で空の配列(@feed_items = [])を追加している箇所は、_feed.html.erbの方を以下のように変えた方がわかりやすい気がした。
<% if @feed_items && @feed_items.any? %>
  • ただ、これだと空の配列追加と同様、フィードの一覧が表示されないので、普通にredirect_to root_urlでHomeにリダイレクトすればよいのではと思ってやってみたが、リダイレクトするとflashメッセージが表示されずエラーが発生したことがわからない。やはりrender 'static_pages/home'は必要なよう。

  • uploader導入後のローカルテストがredになり以下のERRORが表示された。

    • NameError: uninitialized constant Micropost::PictureUploader app/models/micropost.rb:4:in <class:Micropost>' app/models/micropost.rb:1:in'
  • サーバーを再起動しても解消せず原因が分からなかったが、一度VSCodeのすべてのターミナルを閉じてから再度開き直して実行したところgreenになった。

  • herokuにpushした際、以下のエラーが発生した。

remote:  !
remote:  !     Failed to install gems via Bundler.
remote:  !
(省略)
To https://git.heroku.com/xxx.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/xxx.git'
  • herokuはpushされた際にbundle installも実行するようで、そこでこけるとpushがエラーになる模様(たしかにここまで一度もherokuでbundle installをしなかったが、勝手にやってくれていたらしい)
  • bundle installがこけた原因はheroku config:setでAWSクレデンシャルを環境変数に設定していなかったことが原因のようで設定してからpushすると正常に動作した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Leet文字列への置換問題

Leet文字列とは

Leetとはアルファベットを似た形の数字や記号などに置き換えて表記するネットスラングです:point_up_tone1:
初めて解いた問題だったので、忘れないようにメモしておきます!

問題

paiza leet問題.png
入力されたアルファベット大文字の文字列を、上記に該当するアルファベットの場合、数字に置換して出力せよ。

解法

Leet文字列を置換するためには、gsubを使用します。
gsubは、正規表現にマッチした全ての部分を置き換えてくれるメソッドです。

まず、入力される文字列を変数(ここではstring)に定義します。

string = gets

そして、複数のパターンにおいてgsubを使う今回の場合、

puts string.gsub(/A|E|G|I|O|S|Z/, "A" => "4", "E" => "3", "G" => "6", "I" => "1", "O" => "0", "S" => "5", "Z" => "2")

これで解になります:v_tone1:
e.g.)

string = "HOGEFUGA"
puts string.gsub(/A|E|G|I|O|S|Z/, "A" => "4", "E" => "3", "G" => "6", "I" => "1", "O" => "0", "S" => "5", "Z" => "2")
=> "H063FU64"


ちなみに、一つのワードだけ置換したい場合、

"hogehoge".gsub("hoge", "fuga")
=> "hugafuga"

はじめにマッチしたワードのみ置換したい場合subメソッドを使います。

"fugafuga".sub("fuga", "hoge")
=> "hogefuga"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jwtで認証を実装する時のrspecテスト

はじめに

今参加させていただいているスタートアップでapi実装をしており、認証をfirebase authenticationに頼っているのですが、rspecでテストを行うときにjwtの認証をよしなにスキップする方法でめちゃめちゃハマったのでその備忘録です

元々のコード(説明のためかなり省略しています)

applicatioion_controller.rb
class ApplicationController < ActionController::Base
  before_action :authenticate!

  private
  def authenticate!
    if request.headers['Authorization'].present?
      jwt = request.headers['Authorization']
      # jwtを渡すと検証を行いユーザーの情報を返してくれる独自モジュールを呼び出し
      @user_info = Firebase::JwtAuth.authenticate(jwt) 
    else
      render json: { type: '401', message: 'not authorized' }, status: 401
    end
  end
end
user_controller.rb
class UsersController < ApplicationController
  # ユーザー一覧を持ってくる
  def index
    users = User.all
    render json: user status: :ok
  end
end
spec/requests/user_spec.rb
describe UsersController, type: :request do
  let(:headers) { { CONTENT_TYPE: 'application/json', Authorization: 'hoge_token' } }

  describe 'GET /users' do
    it '全てのユーザーを取得する' do
      # header情報をくっつけてリクエストする
      get "/users", headers: headers

      # ステータスコード200が返ってくる、、、はずだった
      expect(response.status).to eq(200)
    end
  end
end

問題点

最初はこんな感じで実装してたのですが、、、これだと色々と問題があります

  • jwtは有効期限が決められているため、実装した瞬間は通ることはあっても一定の時間が経つと通らなくなってしまう
  • テストをするたびにfirebase側に不要なリクエストを送ってしまう

もっとあるとは思いますが、ざっとこんなところでしょうか?

対処法

これの対処法としては
テスト時にauthenticate!メソッドが呼ばれた時はあらかじめ設定しておいた@user_infoを返すようなスタブを作成するというものです

※スタブってなんぞ?って人は↓この辺を見てあとは自分で調べてくださいw
https://wa3.i-3-i.info/word14933.html

ってことなのでそのスタブを作っていきます

/support/authenticated_helper.rb
module AuthenticationHelper
  def authenticate_stub
    # 渡したいインスタンス変数を定義
    @user_info = [
      {
        'name' => 'kosuke',
        'email' => 'kosuke@example.com',
        'email_verified' => true,
      }
    ]

    # allow_any_instance_ofメソッドを使ってauthenticate!メソッドが呼ばれたら
    # ↑のインスタンス変数を返す
    allow_any_instance_of(ApplicationController).to receive(:authenticate!).and_return(@user_info)
  end
end

そして作ったスタブをテストコードから呼び出します

spec/requests/user_spec.rb
describe UsersController, type: :request do
  let(:headers) { { CONTENT_TYPE: 'application/json', Authorization: 'hoge_token' } }

  describe 'GET /users' do
    it '全てのユーザーを取得する' do
      authenticate_stub # ←追記

      # header情報をくっつけてリクエストする
      get "/users", headers: headers

      # ステータスコード200が返ってくる
      expect(response.status).to eq(200)
    end
  end
end

こうしてあげることでauthenticate!メソッドが呼ばれた時に用意しておいたインスタンス変数が帰るので
無事テストを通すことができます

以上!

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

[PostgreSQL]テーブルの項目の型の調べ方/確認の仕方

はじめに

PostgreSQLで実装中に「今のテーブルのそれぞれの項目の状態ってどうなってたっけ?」
となったときに、カラムの型の確認の仕方がまとまっていなかったので、まとめる

環境

macOS Catalina
Ruby 2.5.1
Rails 5.0.7.2
PostgreSQL 12.2

確認方法

1. psql -lで、確認したいテーブルが含まれるデータベースの名前を確認する

PostgreSQLにあるデータベースが一覧表示されるので、確認したいテーブルが含まれるデータベース名をコピーしておく

ternminal
$ psql -l #PostgreSQLのDBを一覧で表示する

##実行結果 (今回は「app-name_development」が対象)
                                 List of databases
          Name           | Owner  | Encoding | Collate | Ctype | Access privileges 
-------------------------+--------+----------+---------+-------+-------------------
 app-name_development    | user   | UTF8     | C       | C     | 
 app-name_test           | user   | UTF8     | C       | C     | 
 postgres                | user   | UTF8     | C       | C     | 
 template0               | user   | UTF8     | C       | C     | =c/user        +
                         |        |          |         |       | user=CTc/user
 template1               | user   | UTF8     | C       | C     | =c/user        +
                         |        |          |         |       | user=CTc/user
(5 rows)

2. psql -d データベース名とコマンド入力

コンソール状態のようになる

ternminal
$ psql -d app-name_development #1.でコピーしたデータベース名
psql (12.2)
Type "help" for help.

app-name_development=# 

3. \d テーブル名とコマンド入力

入力したテーブル名のカラム、インデックス、外部キーが一覧表示される

ternminal
$ psql -d app-name_development
psql (12.2)
Type "help" for help.

app-name_development=# \d users #確認したいテーブル名
                                        Table "public.users"
   Column   |            Type             | Collation | Nullable |             Default              
------------+-----------------------------+-----------+----------+----------------------------------
 id         | integer                     |           | not null | nextval('woms_id_seq'::regclass)
 name       | character varying           |           | not null | 
 group_id   | integer                     |           |          | 
 created_at | timestamp without time zone |           | not null | 
 updated_at | timestamp without time zone |           | not null | 
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "index_users_on_group_id" btree (group_id)
Foreign-key constraints:
    "fk_rails_b5bbe7a612" FOREIGN KEY (group_id) REFERENCES groups(id)

app-name_development=# \q #\qで終了

追記(2020/03/21)

これ書いた後に気付いたが、普通にdb/schema.rbを確認するだけでよかった気もする...

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

GoogleMapAPIでマーカーに数値を表示する方法

GoogleMapAPIでマーカーに数値を表示する方法

結論

google.maps.Markerのインスタンスを作成する際に、label: に値を入れてあげる。
その際には文字列でないと、デフォルトのマーカーが表示される。

  new google.maps.Marker({
    position: { lat: 12.97, lng: 77.59 },
    label: '1',
    map: map,
  });

感想

ネットで探しても探し方の問題か、あまり良い記事がなかったので公式リファレンスをみたら一発だった。
今後は公式リファレンスを見る!(Google翻訳で問題なくみれるレベルでした)
https://developers.google.com/maps/documentation/javascript/examples/marker-labels?hl=ja#maps_marker_labels-css

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

フリマアプリの出品テストが上手く書けない人へ

初投稿です!
どうか優しい目で見てあげて下さい。お願いします!

さてさて、今回はテストコードの初歩的な事について備忘録的に書き残していこうかと思います。

フリマアプリを開発上で、商品(product)の出品時の

「必須項目が全て入力してある際は登録できる」

といったテストコードを書こうと思います!


今回登録する中身はこちら↓

productsテーブル

カラム名 オプション
name string null: false
price integer null: false
description(商品説明) string null: false
status(商品の状態) string null: false
size string null: false
judgment integer
days(発送までの日にち) string null: false
cost(配送料負担者) string null: false
prefecture_id integer null: false
category_id integer null: false, foreign_key: true
brand_id integer foreign_key: true
user_id integer null: false, foreign_key: true

テーブルから
name,
price,
description,
status,
size,
days,
cost,
prefecture_id,
category_id,
user_id
のカラムの値があれば登録できそう( ^ω^ )

というわけで、今回はFactoryBotを用いて、下記の様にテストコードを書きました!!

spec/factories/product.rb
FactoryBot.define do

  factory :product do  #値は適当です
    name              {"tomato"}
    price             {111}
    description       {"aaa"}
    status            {"aaa"}
    size              {"aaa"}
    days              {"aaa"}
    cost              {"aaa"}
    prefecture_id     {1}
    category_id       {1}
    user_id           {1}
  end

end
spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end

さてこれで、【bundle exec rspec】っと( ^ω^ )

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id (FAILED - 1)

Failures:

  1) Product#create is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id
     Failure/Error: expect(product).to be_valid
       expected #<Product id: nil, name: "abe", price: 111, description: "aaa", status: "aaa", size: "aaa", judgment: nil, days: "aaa", cost: "aaa", prefecture_id: 1, category_id: 1, brand_id: nil, user_id: 1, created_at: nil, updated_at: nil> to be valid, but got errors: Userを入力してください, Categoryを入力してください
     # ./spec/models/product_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.18668 seconds (files took 3.15 seconds to load)
1 example, 1 failure

失敗( ´ ▽ ` )

エラー分を見てみると、
「UserとCategoryってどこのどいつ誰だよ!そんなのどこにもいねぇぞ!」
と怒られてます。

それもそのはず…
user_idとcategory_idを外部キーで指定しているのにその先が居ないからね…。( ˊ̱˂˃ˋ̱ )

そこで、
FactoryBotにUserとCategoryも追記します!!
まずここでUserとCategoryのテーブルを確認↓

Usersテーブル

カラム名 オプション
nickname string null: false
email string null: false
encrypted_password string null: false
user_image (プロフィール画像) string
introduction (自己紹介) text
family_name (姓) string null: false
first_name (名) string null: false
family_name_kana (セイ) string null: false
first_name_kana (メイ) string null: false
birth_day (生年月日) date null: false

Categoriesテーブル

カラム名 オプション
name string null: false
ancestry string

なので、FactoryBotに以下の様に追記!

spec/factories/product.rb
# ----------追記部分ここから--------------
FactoryBot.define do
  factory :user do  #値は適当です
    nickname          {"sasa"}
    email             {"kkk@gmail.com"}
    password          {"00000000"}  #登録の際に必要なので追記!
    encrypted_password{"00000000"}
    family_name       {"sasaki"}
    first_name        {"goro"}
    family_name_kana  {"sasaki"}
    first_name_kana   {"goro"}
    birth_day         {"1990-08-24"}
  end

  factory :category do  #値は適当です
    name              {"aaa"}
  end
# ----------追記部分ここまで--------------

  factory :product do  #値は適当です
    name              {"tomato"}
    price             {111}
    description       {"aaa"}
    status            {"aaa"}
    size              {"aaa"}
    days              {"aaa"}
    cost              {"aaa"}
    prefecture_id     {1}
    category_id       {1}
    user_id           {1}
  end

end

そしてuserとcategoryはこのテスト内で一旦保存されないと、
また「UserとCategoryってどこのどいつ誰だよ!そんなのどこにもいねぇぞ!」
と怒られてしまうので、
buildではなくcreateを使って行きます!!

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      user = create(:user)
      category = create(:category)
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end

これでいける!!
【bundle exec rspec】っと( ^ω^ )

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id (FAILED - 1)

Failures:

  1) Product#create is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id
     Failure/Error: expect(product).to be_valid
       expected #<Product id: nil, name: "abe", price: 111, description: "aaa", status: "aaa", size: "aaa", judgment: nil, days: "aaa", cost: "aaa", prefecture_id: 1, category_id: 1, brand_id: nil, user_id: 1, created_at: nil, updated_at: nil> to be valid, but got errors: Userを入力してください, Categoryを入力してください
     # ./spec/models/product_spec.rb:8:in `block (3 levels) in <top (required)>'

Finished in 0.17322 seconds (files took 2.77 seconds to load)
1 example, 1 failure

…えっ?_:(´ཀ`」 ∠):

同じエラーでとる…何故…??

もしかして、userもcategoryもcreateできてない?
と思い、【binding.pry】を【category = create(:category)】の直下に記入し、
確かめてみると、、、

Product
  #create

From: /Users/hoge/Desktop/GitHub/hogehoge/spec/models/product_spec.rb @ line 7 :

     2: describe Product do
     3:   describe '#create' do
     4:     it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
     5:       user = create(:user)
     6:       category = create(:category)
 =>  7:       binding.pry
     8:       product = build(:product)
     9:       expect(product).to be_valid
    10:     end
    11: 
    12:     # it "is invalid without a name" do

[1] pry(#<RSpec::ExampleGroups::Product::Create>)> user
=> #<User id: 15, nickname: "sato", email: "kkk@gmail.com", user_image: nil, introduction: nil, family_name: "sato", first_name: "kenta", family_name_kana: "sato", first_name_kana: "kenta", birth_day: "1990-08-24", created_at: "2020-03-21 05:17:39", updated_at: "2020-03-21 05:17:39">
[2] pry(#<RSpec::ExampleGroups::Product::Create>)> category
=> #<Category:0x00007fe4caab5138 id: 7, name: "aaa", ancestry: nil, created_at: Sat, 21 Mar 2020 05:17:39 UTC +00:00, updated_at: Sat, 21 Mar 2020 05:17:39 UTC +00:00>
[3] pry(#<RSpec::ExampleGroups::Product::Create>)> 

なんてことない、userとcategoryのidが違っていただけでした。( ´ ▽ ` )
そりゃ常にidが1な訳ないよね。。。

と言うことでさらにテストコードを下記の様に修正!!

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      user = create(:user)
      category = create(:category)
    #修正点!! user_id: user[:id], category_id: category[:id]を追記、これでテスト時に保存されたuserとcategoryのidが呼び出され、上書きされる
      product = FactoryBot.build(:product, user_id: user[:id], category_id: category[:id])
      expect(product).to be_valid
    end
  end
end

これでどうかな?
( ^ω^ )つ【bundle exec rspec】

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id

Finished in 0.07648 seconds (files took 2.63 seconds to load)
1 example, 0 failures

無事テスト成功!!

やったぜ!✌︎('ω'✌︎ )( ✌︎'ω')✌︎

まとめ

テスト内容は単純でも、
アプリごとにテーブルなどの制約(NotNullや、外部キーなど)も異なるので、
そこに気をつけてテストコードを書いていかないと、
私の様に、テストコードの沼にハマる事になります。

この記事が少しでも初学者の役に立ちます様に。

ここまで読んでくれて
ありがとう。
また次の記事でお会いしましょう!!♪( ´θ`)ノそれでは

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

[Rails]記事の「投稿フォーム」と「編集フォーム」をパーシャル化させた

前提

現在作成している記事投稿型のアプリで、記事の投稿フォームと編集フォームが全く同じ内容なのにパーシャル化していなかった。

悩み

submitボタンだけ、「投稿する」と「更新する」で表記を分けたかったため、
ほぼ同じ内容にも関わらず、下記のような2つのviewファイルを作成していました。

※実際には、下記コードに色々とオプションをつけていますが、分かりやすくするため簡略化しています。

app/views/articles/new.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit "投稿する" %>
  <% end %>
app/views/articles/edit.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit "更新する" %>
  <% end %>

やったこと

①ja.ymlファイルの修正

submitボタンのvalue(投稿する/更新する)を、viewファイルに直接書くのではなく、
ja.ymlファイルで管理するようにしました。

そもそも、Railsを日本語対応にしている必要がありますが、そのあたりは下記記事を参考にしました。
Railsで日本語化対応にする方法
[初学者]Railsのi18nによる日本語化対応

ja.ymlを下記のように修正しました。
「article:」以下を追記しています。ymlはインデントを間違えるとうまく機能しないので注意!

config/locales/ja.yml
#〜省略〜
    submit:
      create: 登録する
      submit: 保存する
      update: 更新する
      article:
        create: 投稿する
        update: 更新する
#〜省略〜

これで、
記事の投稿(create)の時には、submitボタンが「投稿する」
記事の編集(update)の時には、submitボタンが「更新する」
と表示されるようになりました。

②viewファイルのパーシャル化

・パーシャルファイルを作成(f.submitのvalueを削除)

app/views/articles/_article_form.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit %>
  <% end %>

・「new.html.erb」と「edit.html.erb」をrender partialに書き換える。

app/views/articles/new.html.erb
<%= render partial: 'article_form' %>
app/views/articles/edit.html.erb
<%= render partial: 'article_form' %>

以上です。
これでコードもスッキリしたし、
フォームの編集をしたい時に「_article_form.html.erb」だけを変更すれば、
投稿フォームと編集フォームのどちらにも変更が反映されるようになりました!

初めての投稿で、至らぬ点もありますがご参考にしていただければ幸いです。

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

[Rails]記事の「投稿フォーム」と「編集フォーム」をパーシャル化する

前提

現在作成している記事投稿型のアプリで、記事の投稿フォームと編集フォームが全く同じ内容なのにパーシャル化していなかった。

悩み

submitボタンだけ、「投稿する」と「更新する」で表記を分けたかったため、
ほぼ同じ内容にも関わらず、下記のような2つのviewファイルを作成していました。

※実際には、下記コードに色々とオプションをつけていますが、分かりやすくするため簡略化しています。

app/views/articles/new.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit "投稿する" %>
  <% end %>
app/views/articles/edit.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit "更新する" %>
  <% end %>

やったこと

①ja.ymlファイルの修正

submitボタンのvalue(投稿する/更新する)を、viewファイルに直接書くのではなく、
ja.ymlファイルで管理するようにしました。

そもそも、Railsを日本語対応にしている必要がありますが、そのあたりは下記記事を参考にしました。
Railsで日本語化対応にする方法
[初学者]Railsのi18nによる日本語化対応

ja.ymlを下記のように修正しました。
「article:」以下を追記しています。ymlはインデントを間違えるとうまく機能しないので注意!

config/locales/ja.yml
#〜省略〜
    submit:
      create: 登録する
      submit: 保存する
      update: 更新する
      article:
        create: 投稿する
        update: 更新する
#〜省略〜

これで、
記事の投稿(create)の時には、submitボタンが「投稿する」
記事の編集(update)の時には、submitボタンが「更新する」
と表示されるようになりました。

②viewファイルのパーシャル化

・パーシャルファイルを作成(f.submitのvalueを削除)

app/views/articles/_article_form.html.erb
  <%= form_with model: @article, local: true do |f| %>
    <%= f.label :title, "タイトル" %>
    <%= f.text_area :title %>

    <%= f.label :content, "記事内容" %>
    <%= f.text_area :content %>

    <%= f.submit %>
  <% end %>

・「new.html.erb」と「edit.html.erb」をrender partialに書き換える。

app/views/articles/new.html.erb
<%= render partial: 'article_form' %>
app/views/articles/edit.html.erb
<%= render partial: 'article_form' %>

以上です。
これでコードもスッキリしたし、
フォームの編集をしたい時に「_article_form.html.erb」だけを変更すれば、
投稿フォームと編集フォームのどちらにも変更が反映されるようになりました!

初めての投稿で、至らぬ点もありますがご参考にしていただければ幸いです。

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

【ruby】クラスの定義方法(3つ)

備忘録用に書いていきます。

class Sample
  .
  .
  .
end

Sample = Class.new do
  .
  .
  .
end


メリット…ブロックの外の変数が呼び出せる

var = 'hogehoge'

class Sample
  # classブロックの内部からは、ブロックの外で定義されたvarは呼び出せない
  puts var
end

Sample = Class.new do
  # Class.newブロックの内部からは、varを呼び出すことができる
  puts var
end

self.class.const_set :'Sample', Class.new do 
  .
  .
  .
end


メリット…クラスを動的に定義できる

class Sample
  def self.create_new_class(class_name)
    # 引数の値に応じて、クラスを作成する
    self.class.const_set :"#{class_name}", Class.new
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【解決方法】gem ffi のインストール(アップデート)に失敗する

問題

bundle updateを実行した際に、ffi のインストール(アップデート)に失敗した

$ bundle update

# ...()

Fetching ffi 1.12.2 (was 1.10.0)
Installing ffi 1.12.2 (was 1.10.0) with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

# ...()

extconf failed, exit code 1

Gem files will remain installed in /Users/xxxxx/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ffi-1.12.2 for inspection.
Results logged to /Users/xxxxx/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-18/2.6.0-static/ffi-1.12.2/gem_make.out

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

In Gemfile:
  middleman was resolved to 4.3.6, which depends on
    middleman-core was resolved to 4.3.6, which depends on
      listen was resolved to 3.0.8, which depends on
        rb-inotify was resolved to 0.10.1, which depends on
          ffi

試したこと

ffi をバージョン指定でインストール(アップデート)しようとしても失敗した

$ gem install ffi -v '1.12.2'

Building native extensions. This could take a while...
ERROR:  Error installing ffi:
        ERROR: Failed to build gem native extension.

# ...()

解決方法

Xcode Command Line Tool をインストールした上で、ffi をインストール(アップデート)したら成功した

$ xcode-select --install

xcode-select: note: install requested for command line developer tools
$ gem install ffi -v '1.12.2'

Building native extensions. This could take a while...
Successfully installed ffi-1.12.2
Parsing documentation for ffi-1.12.2
Installing ri documentation for ffi-1.12.2
Done installing documentation for ffi after 41 seconds
1 gem installed
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【解決方法】Macで gem ffi の インストール (アップデート) に 失敗する

問題

bundle updateを実行した際に、ffi のインストール(アップデート)に失敗した

$ bundle update

# ...()

Fetching ffi 1.12.2 (was 1.10.0)
Installing ffi 1.12.2 (was 1.10.0) with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

# ...()

extconf failed, exit code 1

Gem files will remain installed in /Users/xxxxx/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ffi-1.12.2 for inspection.
Results logged to /Users/xxxxx/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-18/2.6.0-static/ffi-1.12.2/gem_make.out

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

In Gemfile:
  middleman was resolved to 4.3.6, which depends on
    middleman-core was resolved to 4.3.6, which depends on
      listen was resolved to 3.0.8, which depends on
        rb-inotify was resolved to 0.10.1, which depends on
          ffi

試したこと

ffi をバージョン指定でインストール(アップデート)しようとしても失敗した

$ gem install ffi -v '1.12.2'

Building native extensions. This could take a while...
ERROR:  Error installing ffi:
        ERROR: Failed to build gem native extension.

# ...()

解決方法

Xcode Command Line Tool をインストールした上で、ffi をインストール(アップデート)したら成功した

$ xcode-select --install

xcode-select: note: install requested for command line developer tools
$ gem install ffi -v '1.12.2'

Building native extensions. This could take a while...
Successfully installed ffi-1.12.2
Parsing documentation for ffi-1.12.2
Installing ri documentation for ffi-1.12.2
Done installing documentation for ffi after 41 seconds
1 gem installed
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FizzBuzz問題のリファクタリング【Ruby】

修正ポイント

  • if文のネストをなくす
  • 条件が当てはまる確率が低い方から先に記述
  • while分をeachメソッドに変更
    • 変数の数を減らす
    • コードを減らす

リファクタリング前

def fizzBuzz(count)
  i = 1
  while i < count + 1 do
    if i % 3 == 0 then
      if i % 5 == 0 then
        puts 'FizzBuzz'
      else
        puts 'Fizz'
      end
    elsif  i % 5 == 0 then
      puts 'Buzz'
    else
      puts i
    end
    i += 1
  end
end

fizzBuzz(20)

リファクタリング後

def fizzBuzz(count)
  (1..count).each do |c|
    if    c % 15 == 0
      puts 'FizzBuzz'
    elsif c %  5 == 0
      puts 'Buzz'
    elsif c %  3 == 0
      puts 'Fizz'
    else
      puts c
    end
  end
end

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

サンプルアプリケーションを作ろう!!〜Railsチュートリアル3章〜

ほぼ性的なページの作成

あのーうち実家暮らしで家でUDEMYとかYOUTUBEとかでプログラミング動画みることあるんだけど、その時に「静的なページが作れます〜静的なページが〜」とか連呼されると若干気まずくなるからほんと辞めてほしいと思う今日この頃、、、NSぱんだまんです。アニメとか映画とかはそういうシーン来るのだいたい分かるからボリューム落としたりするんだけどまさかこんな真面目な動画でくると思わなかったからいちいち弁明するのもあれだし、すぐ自分の部屋に逃げました。

さて今回は第3章〜ほぼ静的なページの作成〜ですね。
まず静的、動的ってようわからんかったので調べました。
静的、、、何度アクセスしても同じものが表示されるWebページ
動的、、、アクセスした時の状況に応じて異なる内容が表示されるページ
ということですね。静的なページは企業紹介のサイトとか見るユーザーに同じものを見せたいときに使用される特に動きのないページ。動的ページはSNSサイトとか掲示板とかユーザー毎によって表示される内容が異なるページのことを指すんだって。

まずセットアップします〜ここは何回もやってるから少しずつ慣れていってる気がする。立ち上げのスピードも上がってきてる感じします。

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

よくわかんないけどToyアプリを作ろう!!(Railsチュートリアル2章)

前回までのあらすじ

やりたくねーなと目を背けていたRailsチュートリアル。。真剣に取り組み始めました。。
なんとか環境構築、railsの導入、git.GitHubの導入に成功しました。
*ちなみにHerokuは一旦とばしました。とりあえず一周して余裕でたらやります。
今回から第2章やります。

さあやります。

よーし頑張ってくぞー!!
内容としてはscaffoldっていうスクリプトを使ってTOYアプリっていうのを作っていくみたい。
うーん安定の意味わからんなー。。
とりあえずscaffold(スキャフォールド)っていうのは。。。

railsアプリケーションの開発をする際にはMVCを作っていき、更に必要なルーティングを作成していく必要がある。これらの作業をまとめて行って、簡単にアプリケーションの雛形を作ってくれる機能のこと。scaffoldを使用することで素早くrailsアプリケーションを作ることができる。

なるほどねー。とりあえず内容を深くっていうよりはrailsのアプリケーションを素早く作って慣れましょう、みたいな章なのかな?

アプリケーションの計画

はじめにどんなアプリにするか計画を建てると。
まずrailsインストール

$ cd ~/environment
$ rails _5.1.6_ new toy_app
$ cd toy_app/

んでbundleで扱うgemfileをテキストエディタで編集。

$ bundle install --without production
bundleインストールします。

んでgit githubの登録。ここら辺は前回の内容と同じですね。
*Herokuはとばします。

ユーザーのモデル設計

ここでユーザーのデータモデルの説明が入ります。
各ユーザーには重複のない一意のキーとなるinteger型のID番号(idと呼ぶ)を割り当て、このIDに加えて一般公開されるstring型の名前(name)そして同じくstring型のメールアドレス(email)をもたせる。メールアドレスはユーザーとしても使われる。と。
ここら辺はprogateでもなんとなくやったな。要するにユーザーというモデルにどんな概念にするのか。ここでの情報はDBのテーブルというところに保存されて必要があれば取り出すことができるんだよね。
んで、id name emailは属性っていってテーブルの中の縦の列のなるんだよね。

マイクロソフトのモデル設定

マイクロソフトはidとマイクロポストのテキスト内を格納するtext型のcontentだけで構成されている。それとマイクロポストをユーザーと関連付ける必要があるためuser_idも必要となる。

ここで気になったんだけどデータ型っていっぱいあるじゃん?
これまとめてみた。

string 文字列型(1~255文字)
text テキスト(不定長文字列)型(1~4294967296文字)
integer 整数型(整数:4バイト)
bigint(整数:8バイト)
float 浮動小数点数型(浮動小数)
decimal 固定長整数型(精度の高い小数)
datetime 日時型(日時)
time 時刻型(時間)
date 日付型(日付)
binary バイナリ文字列型(バイナリーデータ)
boolean 真偽値型(Boolean型)

とまあこんな感じ。多いね。笑 若干よくわかんないのもあるし、これが全部ってわけじゃないけど主にrailsだとここらへんが使われてるらしい。文字を扱う場合stringとtextが対応しているけどstringはtextに比べると扱える文字数が少し短め、名前とかemailとかの文字情報はstringで扱って、本文や備考などの文章はtextつ合うのが一般的みたいだよ。

Userリソース

ここではさっきのユーザー用のデータモデルを、そのモデルを表示するためのWebインターフェイスに従って実装します。
このデータモデルとWebインターフェイスは、組み合わさってUsersリソースとなり、ユーザーというものを、HTTPプロトコル経由で自由に作成/取得/更新/削除できるオブジェクトとみなすことができるようになります。「はじめに」で約束したとおり、このUsersリソースはすべてのRailsプロジェクトに標準装備されているscaffoldジェネレータで生成します。scaffoldで生成された膨大なコードを今詳細に読む必要はありません。今の段階ではおそらく混乱するだけでしょう。
(〜Railsチュートリアルより〜)

これそのままrailsチュートリアルの文章コピペしたんだけどさ。
もうこの文章が混乱するわ笑
わからない単語が多すぎる、、一回一回ポケモン図鑑開くマサラタウンの某少年のような気分だよ。

一個一個分解していこう。データモデルはさっきの情報がつまってる一つの塊だよね。
webインターフェースはWebページおよびWebブラウザを用いてソフトウェアの表示、操作を行うもののこと(IT用語辞典参照)つまり処理の方法、手段のことかな。
これが組み合わさってUserリソース(動作の実行に必要な処理システムの要素)が出来上がる。これをhttpプロトコル(インターネット上で、Webサーバーとユーザーが、相互に通信するための仕組み)経由でいろいろな編集ができるオブジェクトとみなすことができる。オブジェクトっていうのはデータと、それに対する処理(メソッド)をひとまとめにしたもののこと。
これをすべてscaffoldジェネレータで全部生成しちまおうってことなんだね。
いわゆるチート武器ってやつだ。無限ロケランみたなもんだね。
Railsのscaffoldはrails generateスクリプトにscaffoldコマンドを渡すことで生成される。scaffoldコマンドの引数には、リソース名を単数形にしたもの(この場合はUser)を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加できる。(下のやつだとまず引数にUserを入れて、中にデータ型がStringのname emailのパラメーターも入れて作ってくださいねー!)って意味になる。

$ rails generate scaffold User name:string email:string

あとこれにプラスしてDBをmigrateしなくちゃいけない。

$ rails db:migrate
$ rails server

画面見えるようになります。

本日のエラー発生

翌日CLOUD9を起動したらサーバーがつながらないってかプレビュー見れない。
oops!!みたな青白の画面出る。
原因、、、cloud9を落としたことでダウンした?
解決策、、、 $ cd 指定したいapp
からの〜 $ rails update
最後にー $ rails server
でできた!!updateとrails serverするときはcdにて指定したディレクトリになっているか確認しよう。

memo

HTTPリクエスト URL アクション 用途
GET /users index すべてのユーザーを一覧するページ
GET /users/1 show id=1のユーザーを表示するページ
GET /users/new new 新規ユーザーを作成するページ
POST /users create ユーザーを作成するアクション
GET /users/1/edit edit id=1のユーザーを編集するページ
PATCH /users/1 update id=1のユーザーを更新するアクション
DELETE /users/1 destroy id=1のユーザーを削除するアクション

あとは同じようにmicropostっていうデータベースをマイグレーションしてUSERとMICROPOSTのデータベースの情報をくっつけるみたいな感じの流れで2章は終了しました。
Progateやってたからここらへんは割とすんなり理解できました。
次回は3章目に進んでいきたいと思います。

では今日はここまで

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