20200710のRailsに関する記事は19件です。

Rails ✕ PostgreSQLで既定のIDをUUIDに設定する方法

動作環境

  • Ruby 2.7.0
  • Rails 6.0.3.2
  • PostgreSQL 12.3

準備

1. PostgreSQLデータベースを使用してRailsアプリケーションを作成します。

$ rails new blog -d=postgresql

2. config/database.yml でデータベース名を変更できます。

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: blog_development

test:
  <<: *default
  database: blog_test

3. 下記のrailsコマンドで config/database.yml の内容でデータベースを作成してくれます。

$ rails db:create
Created database 'blog_development'
Created database 'blog_test'

PostgreSQLでUUIDを有効にする

通常、以下の手順で、プライマリキーとしてUUIDを使うことが出来ます。

1. マイグレーションファイル作成コマンドを実行します。

$ rails g migration enable_uuid

2. マイグレーションファイルの編集

# db/migrate/xxx_enable_uuid.rb
class EnableUuid < ActiveRecord::Migration[6.0]
  def change
    enable_extension 'pgcrypto'
  end
end

既定のIDをUUIDに設定する

config/initializers/generators.rb ファイルを作成して、中身を下記のようにします。

# config/initializers/generators.rb
Rails.application.config.generators do |g|
  g.orm :active_record, primary_key_type: :uuid
end

この設定でmodel作成時にidがuuidであるというオプションがmigrationファイルに自動で付与されるようになります。

確認

1. Postモデルを作成します。

$ rails g model Post title:string

2. マイグレーションファイルを確認します。

# db/migrate/xxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts, id: :uuid do |t|
      t.string :title

      t.timestamps
    end
  end
end

3. マイグレーション

$ rails db:migrate

4. レコードの作成

$ rails console
> Post.create(title: 'new post')
   (0.2ms)  BEGIN
  Post Create (25.3ms)  INSERT INTO "posts" ("title", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["title", "new post"], ["created_at", "2020-07-10 13:05:38.915722"], ["updated_at", "2020-07-10 13:05:38.915722"]]
   (7.0ms)  COMMIT
 => #<Post id: "464b7fa5-aaae-4970-a56f-b5c0cf6c1b83", title: "new post", created_at: "2020-07-10 13:05:38", updated_at: "2020-07-10 13:05:38"> 

プライマリキーがUUIDタイプになりました!

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

商品情報編集機能を実装したい~part2~

前提

  • ruby on rails 6.0.0 を使用。
  • ユーザー機能はdeviseにより導入されているものとする。
  • viewファイルは全てhaml形式とする。
  • ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。

はじめに

前置きや手順などは part1 に記述してあるので早速実装の続きをしていこうと思います。気になったら是非読んでネ。
part2 でやることとしては手順の2番目にあたる「imageテーブルを関連づけて画像の投稿を実装」というところです。

ではさっそく、

本日もはりきってやっていきましょう。

$ rails g model image
$ rails db:migrate

まずはimageモデルを作ってマイグレートするところから。
前回と同じくマイグレーションファイルの記述等は省略しています。

次にモデル構造の説明をしていきます。

app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images
end
app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
end

上記を見ていただく通り、1つのproductモデルに対して、複数のimageモデルを持つことができる、1対多の関係になっています。(わかりやすいようバリデーションは省略しております)
imageテーブルでは1枚の画像に1つのレコードが対応していて、それぞれにproduct_idを紐づけているといった形です。

ざっと構造が把握できたところでローカル環境で画像をアップロードする準備を行っていきます。

Gemfile
~省略~
gem 'carrierwave'
gem 'mini_magick'

まずはgemの導入からです。画像をアップロードするために、今回は carrierwave の uploader を使っていきたいと思います。

$ bundle install
$ rails g uploader image

忘れずに bandle install を行ってから、uploaderファイルを生成します。これらをしっかり使うために、生成したuploaderファイルと先ほどのモデルに記述を追加していきましょう。

app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
  mount_uploader :src, ImageUploader
end

このように記述することで、imageモデルのsrcカラムにおいてImageUploaderを使用することが可能になります。

app/uploaders/image_uploader.rb
include CarrierWave::MiniMagick
process resize_to_fit: [50, 50]

こちらはMiniMagickの設定です。アップロードした画像のサイズを調整できる便利なgemですね。
上記の2行は新しく追加するというより、コメントアウトの中からこの記述を見つけてコメントアウトを外していただく形になります。(resizeの中身は自由に調節していただいて構いません)

ここまででアップロード自体はできるようになったので、実際に画像を投稿するフォームを作っていきます。

app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true
end
app/views/products/_form.html.haml
= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  = f.fields_for :images do |i|
    = i.file_field :src
  = f.submit 'SEND'

fields_forとやらが出てきました。こちらは1つのテーブルに対して、それに紐づいた複数のテーブルに同時にデータを保存することができるフォームヘルパーです。
使い方としては、まず親となるモデルに、accepts_nested_attributes_for という記述を追加します。因数にはそれに紐づく子モデルをもってきましょう。後ろに続く allow_destroy の部分は、trueにすると親レコードが削除された場合に、関連する子レコードも同時に削除してくれるといった便利な機能です。

app/controllers/products_controller.rb
#~省略~
def new
  @product = Product.new
  @product.images.new
end
#~省略~
def product_params
  params.require(:product).permit(:name, images_attributes: [:src]).merge(user_id: current_user.id)
end 

扱うデータに伴って、コントローラーにも少し修正を加えます。
まずnewアクションの部分では、productに関連する新しいimageインスタンスを生成する記述を追加しています。これがないとimageに正しく画像が保存されないので注意です。
そしてストロングパラメータですね。こちらは少し特殊な記述をしています。attributes: [: ] というのは関連づくモデルのカラムを指定するというものです。
フォームヘルパーのfields
forを使用した際にセットで使用するものと考えていただければ大丈夫だと思います。

これで画像の投稿機能は実装できたことになります。
ただこのままだと画像が1枚しか選ぶことができないので次回そこをアップグレードしていきたいと思います。

最後に

今回はimageモデルを作成して実際にフォームで画像を送り、データベースに保存するところまで実装しました。

文字だけではわかりにくいと思ったので参考画像やgifなんかも付け足したいなと思いました。今は少し忙しいので余裕ができたらやろうと思います。(多分)

次のpartではとうとうjQueryを導入していきます。表示をあれこれいじって画像を複数投稿できるフォームを作ろう!といった感じですね。頑張ります。

それではまた次のpartで。ありがとうございました。

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

画像の登録?もちろんできません(できる)~part2~

前提

  • ruby on rails 6.0.0 を使用。
  • ユーザー機能はdeviseにより導入されているものとする。
  • viewファイルは全てhaml形式とする。
  • ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。

はじめに

前置きや手順などは part1 に記述してあるので早速実装の続きをしていこうと思います。気になったら是非読んでネ。

part2 でやることとしては手順の2番目にあたる「imageテーブルを関連づけて画像の投稿を実装」というところです。

別テーブルに画像を登録するだけなのに実装するのに3万年くらいかかったので正しい手順を後世に残したいと思います。

ではさっそく、

本日もはりきってやっていきましょう。

$ rails g model image
$ rails db:migrate

まずはimageモデルを作ってマイグレートするところから。
前回と同じくマイグレーションファイルの記述等は省略しています。

次にモデル構造の説明をしていきます。

app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images
end
app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
end

上記を見ていただく通り、1つのproductモデルに対して、複数のimageモデルを持つことができる、1対多の関係になっています。(わかりやすいようバリデーションは省略しております)
imageテーブルでは1枚の画像に1つのレコードが対応していて、それぞれにproduct_idを紐づけているといった形です。

ざっと構造が把握できたところでローカル環境で画像をアップロードする準備を行っていきます。

Gemfile
~省略~
gem 'carrierwave'
gem 'mini_magick'

まずはgemの導入からです。画像をアップロードするために、今回は carrierwave の uploader を使っていきたいと思います。

$ bundle install
$ rails g uploader image

忘れずに bandle install を行ってから、uploaderファイルを生成します。これらをしっかり使うために、生成したuploaderファイルと先ほどのモデルに記述を追加していきましょう。

app/models/image.rb
class Image < ApplicationRecord
  belongs_to :product
  mount_uploader :src, ImageUploader
end

このように記述することで、imageモデルのsrcカラムにおいてImageUploaderを使用することが可能になります。

app/uploaders/image_uploader.rb
include CarrierWave::MiniMagick
process resize_to_fit: [50, 50]

こちらはMiniMagickの設定です。アップロードした画像のサイズを調整できる便利なgemですね。
上記の2行は新しく追加するというより、コメントアウトの中からこの記述を見つけてコメントアウトを外していただく形になります。(resizeの中身は自由に調節していただいて構いません)

ここまででアップロード自体はできるようになったので、実際に画像を投稿するフォームを作っていきます。

app/models/product.rb
class Product < ApplicationRecord
  belongs_to :user
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true
end
app/views/products/_form.html.haml
= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  = f.fields_for :images do |i|
    = i.file_field :src
  = f.submit 'SEND'

fields_forとやらが出てきました。こちらは1つのテーブルに対して、それに紐づいた複数のテーブルに同時にデータを保存することができるフォームヘルパーです。
使い方としては、まず親となるモデルに、accepts_nested_attributes_for という記述を追加します。因数にはそれに紐づく子モデルをもってきましょう。後ろに続く allow_destroy の部分は、trueにすると親レコードが削除された場合に、関連する子レコードも同時に削除してくれるといった便利な機能です。

app/controllers/products_controller.rb
#~省略~
def new
  @product = Product.new
  @product.images.new
end
#~省略~
def product_params
  params.require(:product).permit(:name, images_attributes: [:src]).merge(user_id: current_user.id)
end 

扱うデータに伴って、コントローラーにも少し修正を加えます。
まずnewアクションの部分では、productに関連する新しいimageインスタンスを生成する記述を追加しています。これがないとimageに正しく画像が保存されないので注意です。
そしてストロングパラメータですね。こちらは少し特殊な記述をしています。attributes: [: ] というのは関連づくモデルのカラムを指定するというものです。
フォームヘルパーのfields
forを使用した際にセットで使用するものと考えていただければ大丈夫だと思います。

これで画像の投稿機能は実装できたことになります。
ただこのままだと画像が1枚しか選ぶことができないので次回そこをアップグレードしていきたいと思います。

最後に

今回はimageモデルを作成して実際にフォームで画像を送り、データベースに保存するところまで実装しました。

文字だけではわかりにくいと思ったので参考画像やgifなんかも付け足したいなと思いました。今は少し忙しいので余裕ができたらやろうと思います。(多分)

次のpartではとうとうjQueryを導入していきます。表示をあれこれいじって画像を複数投稿できるフォームを作ろう!といった感じですね。頑張ります。

それではまた次のpartで。ありがとうございました。

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

【Rails】多対多のアソシエーションを用いてコミュニティ加入の申請/承認機能の実装を行う

実施したいこと

申請・承認の仕組みを実装する

具体的には?

「ユーザ(小西)が、とあるコミュニティに加入したいと思いますが、
加入にはコミュニティの管理者(大西)の承認が必要になる」といったケースでの実装例です。

ざっくりイメージ

キャプチャ.PNG

処理フロー概要

  1. 該当コミュニティ画面にて、ユーザ(小西)が「申請」ボタンを押す。
  2. 該当コミュニティの申請待ちリストにユーザ(小西)が追加される。管理者が、該当コミュニティの申請待ち一覧画面を見て、申請者を承認するかどうか判断。
  3. 申請を承認する場合は、承認ボタンを押す。申請ユーザがコミュニティに加入し、申請待ちリストからは消去される。
  4. 申請を却下する場合は、否認ボタンを押す。申請待ちリストから消去される。

環境

Ruby 2.6.5
Rails 5.2.4.2
mysql 5.7

現在のアプリケーション機能の構成

ユーザが色々なコミュニティに所属できるアプリケーションを作成しています。
- userモデル:アプリの利用ユーザ情報を保持
- communityモデル:コミュニティ情報を保持
- belongingモデル:ユーザが所属するコミュニティ情報を保持。ユーザは複数のコミュニティに所属できます。
※userとcommunityはbelongingモデルを介して多対多の関係となっています。

以下ソースは関連部分のみ抜粋

user.rb
class User < ApplicationRecord

  has_many :belongings, dependent: :destroy
  has_many :applies, dependent: :destroy
  has_many :communities, through: :belongings # ユーザが所属しているコミュニティ

end

community.rb
class Community < ApplicationRecord
    has_many :belongings, dependent: :destroy
    has_many :applies, dependent: :destroy
    has_many :users, through: :belongings # コミュニティに所属しているユーザ

    # ユーザがコミュニティに所属していればtrueを返す
    def user_belonging?(user)
      users.include?(user)
    end

end

belonging.rb
class Belonging < ApplicationRecord
    belongs_to :user
    belongs_to :community
    validates :user_id, presence: true
    validates :community_id, presence: true

    validates  :user_id, uniqueness: { scope: :community_id }
    validates  :community_id, uniqueness: { scope: :user_id }

end

Applyモデルを追加し、申請状況を保持させる。

Applyモデルのルーティングやコントローラ、ビューファイルを追加します。

ルーティング(抜粋)

routes.rb
Rails.application.routes.draw do
  root 'home#index'

  resources :communities do
    resources :applies, only: %i[index create destroy]
    resources :belongings, only: %i[index create destroy]
  end

  resources :users, only: [:index, :show]

end

1.該当コミュニティ画面にて、ユーザ(小西)が「申請」ボタンを押す。

  • 申請ボタン(コミュニティ詳細画面に配置) (views/communities/show.html.erb)
show.html.erb
<!-- ログインユーザが当該コミュニティに所属している場合 -->
<% if @community.user_belonging?(current_user) %>
    <%= link_to '退会する', community_belonging_path(@community, @belonging), method: :delete, data:{ confirm: "コミュニティ「#{@community.name}」を退会します。よろしいですか?" } ,class:"mini-red-link-btn font-bold text-line-none" %>
<!-- 当該コミュニティには所属していないが、ログインはしている場合 -->
<% elsif current_user %>
    <% if @apply %>
        <%= link_to '申請取消', community_apply_path(@community, @apply), method: :delete, class: "mini-red-link-btn font-bold text-line-none" %>
    <% else %>
        <%= link_to '加入申請', community_applies_path(@community), method: :post, class: "mini-green-link-btn font-bold text-line-none" %>
    <% end %>

申請ボタンは、以下の条件で表示させます。
1. コミュニティにすでに加入している場合:退会するボタンが表示
2. コミュニティにまだ加入していない場合:申請ボタンが表示
3. すでに加入申請済の場合:申請取消ボタンを表示

  • 申請ボタン押下後(createアクションが発生)
applies_controller.rb
class AppliesController < ApplicationController

  def create
    current_user.applies.create(community_id: apply_params[:community_id])
    redirect_to community_url(apply_params[:community_id]), notice: "加入申請しました"
  end

  private

    def apply_params
      params.permit(:community_id)
    end

end

Ajaxは使わず、同画面へのリダイレクトが発生するようにします。
(Ajaxを使ってもよさそう)

これで、applyモデルに申請情報が追加されました。

2.該当コミュニティの申請待ちリストにユーザ(小西)が追加される。管理者が、該当コミュニティの申請待ち一覧画面を見て、申請者を承認するかどうか判断。

次に、申請待ちリストの確認です。
管理者(大西)が該当コミュニティ画面から「申請待ち一覧画面」を開きます。

  • 申請待ち画面へのリンク(コミュニティ詳細画面) (views/communities/show.html.erb)
show.html.erb
<% if user_admin_flg(current_user,@community) == 1 %>
    <%= link_to "承認待ち一覧", community_applies_path(@community), class:"btn btn-primary" %>
<% end %>

リンクをクリックすると、申請待ち一覧画面が開きます。

  • 申請待ち一覧画面 (views/applies/index.html.erb)
index.html.erb
<div class="container applicant-wrapper">
    <h3>承認待ちユーザ一覧</h3>
    <div class="row">
        <% @applies.each do |app| %>
        <div class="col-6">
            <% if app.user.image.attached? %>
                <%= link_to app.user.image, user_path(app.user), class:"user-icon" %>
            <% else %>
                <%= link_to user_path(app.user) do %>
                    <%= image_tag ("no_image.png"), class:"user-icon" %>
                <% end %>
            <% end %>
            &nbsp<%= app.user.username %><br>
            <%= link_to "承認", community_belongings_path(app.community, user_id: app.user.id, apply_id: app.id), method: :post, class:"mini-green-link-btn font-bold text-line-none" %>
            <%= link_to "却下", community_apply_path(app.community, app), method: :delete, class:"mini-red-link-btn font-bold text-line-none" %>
            <br>
        </div>
        <% end %>
    </div>
</div>

applyモデルから、該当コミュニティへの申請情報を一覧表示させています。
同画面において、承認ボタン、却下ボタンも配置しています。

3.申請を承認する場合は、承認ボタンを押す。申請ユーザがコミュニティに加入し、申請待ちリストからは消去される。

承認ボタンを押すと、以下の処理が実行されます。
1. belongingsコントローラのcreateアクションが実行される。
2. 申請待ち一覧から削除するため、Applyモデルの該当の申請情報が削除される。
3. 申請待ち一覧画面へリダイレクト

  • belongingsコントローラ
belongings_controller.rb
class BelongingsController < ApplicationController

    def create
        @belonging = Belonging.create(community_id: belonging_params[:community_id], user_id: belonging_params[:user_id])
        Apply.find(belonging_params[:apply_id]).destroy!
        redirect_to community_applies_url(@belonging.community), notice:"「#{@belonging.user.username}」が、コミュニティ:#{@belonging.community.name}へ加入しました。"
    end

    private

        def belonging_params
            params.permit(:community_id, :user_id, :apply_id)
        end

end

4.申請を却下する場合は、否認ボタンを押す。申請待ちリストから消去される。

却下ボタンを押すと、以下の処理が実行されます。
1. 申請待ち一覧から削除するため、appliesコントローラのdestroyアクションが実行され、Applyモデルの該当の申請情報が削除される。
2. 申請待ち一覧画面へリダイレクト

  • appliesコントローラ
applies_controller.rb
class AppliesController < ApplicationController

  def destroy
    @apply = Apply.find(params[:id])
    @apply.destroy!
    @comminity = Community.find(params[:community_id])
    redirect_to community_url(@comminity), notice: "加入申請を取り消しました"
  end

end

以上になります。

文中でappliesコントローラとbelongingsコントローラはアクション毎に分けて紹介していますが、最後に全部まとめて載せておきます。

applies_controller.rb
class AppliesController < ApplicationController

  def create
    current_user.applies.create(community_id: apply_params[:community_id])
    redirect_to community_url(apply_params[:community_id]), notice: "加入申請しました"
  end

  def destroy
    @apply = Apply.find(params[:id])
    @apply.destroy!
    @comminity = Community.find(params[:community_id])
    redirect_to community_url(@comminity), notice: "加入申請を取り消しました"
  end

  def index
    @applies = Apply.where(community_id: params[:community_id])
  end

  private

    def apply_params
      params.permit(:community_id)
    end

end
belongings_controller.rb
class BelongingsController < ApplicationController

    def create
        @belonging = Belonging.create(community_id: belonging_params[:community_id], user_id: belonging_params[:user_id])
        Apply.find(belonging_params[:apply_id]).destroy!
        redirect_to community_applies_url(@belonging.community), notice:"「#{@belonging.user.username}」が、コミュニティ:#{@belonging.community.name}へ加入しました。"
    end

    def destroy
        @belonging = Belonging.find(params[:id])
        @belonging.destroy!
        @comminity = Community.find(params[:community_id])
        redirect_to community_url(@comminity), notice: "コミュニティ「#{@comminity.name}」を退会しました。"    
    end

    private

        def belonging_params
            params.permit(:community_id, :user_id, :apply_id)
        end

end

初心者が自分の頭でロジック・コードを考えて実装したので、
間違いや、もっと適した実装方法があるかもしれません。
ぜひご指摘いただけますと幸いです。

この実装で、多対多の関係のことがより理解できたなと思います。

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

[Rails]パラメータに配列として情報を保存したい時

パラメーターに情報が入らない

controller.rb
#省略
  def new
    @goal = Goal.new
  end

  def create
    Goal.create(goal_params)
    redirect_to root_path
  end

  private
  def goal_params
    params.require(:goal).permit(:name, :time, :days)
  end 

end 

この状態でcreateアクションを実行すると値が入らないので
binding.pryで確認。

ターミナル内でgoal_paramsを実行▼

[2] pry(#<GoalsController>)> goal_params
Unpermitted parameter: days
=> <ActionController::Parameters {"name"=>"ラジオ体操", "time"=>"19:51"} permitted: true>

パラメータにdaysカラムの値が入っていない...
どうやら配列で情報を保存しいたい場合は、:daysではなく、days: []でなければならないということ。

コードを編集

controller.rb
#省略
  def new
    @goal = Goal.new
  end

  def create
    Goal.create(goal_params)
    redirect_to root_path
  end

  private
  def goal_params
    params.require(:goal).permit(:name, :time, days: []) #ここを編集
  end 

end 

binding.pryでパラメータ内を確認▼

[2] pry(#<GoalsController>)> goal_params
=> <ActionController::Parameters {"name"=>"筋トレ", "time"=>"20:01", "days"=>["", "水曜日", "木曜日", "金曜日", "土曜日"]} permitted: true>

これでパラメータに情報は入りました。
参考にしてください!

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

[CentOS][Gem]Error: Your version of SQLite (3.7.17) is too old. Active Record supports SQLite >= 3.8. 解決法

起こったこと

Rails 6 project で gem 'sqlite3' を使用して bundle install すると
SQLite が古い とエラー

'./rails-proj/'
$ bundle install
...
Your version of SQLite (3.7.17) is too old. Active Record supports SQLite >= 3.8.
...
Failed

OSの sqlite3 が古いのかと思い、
こちらを参考に 自分で最新sqlite3 (3.8.11) をインストール し
https://qiita.com/8zca/items/175efb0612070530d186

option 指定で gem install

エラーは変わらない。

# 自分でインストール
$ /opt/sqlite/sqlite3/bin/sqlite3 --version
3.32.3 
$ bundle config build.sqlite3 --with-sqlite3-include=/opt/sqlite/sqlite3/include \
   --with-sqlite3-lib=/opt/sqlite/sqlite3/lib
$ bundle exec gem install sqlite3
...
Failed

原因

どうやら sqlite3 をコンパイルする際にOSの sqlite.x86_64 nativeライブラリが必要のようで
そのversion が古いよう

$ yum list | grep sqlite
...
sqlite.x86_64                            3.7.17-1.fc21                @/sqlite-3.7.17-1.fc21.x86_64
...

Yum の標準repo で登録されてるsqlite version は古いので yum install sqlite-* で入らない。

$ yum --showduplicates list sqlite
..
Available Packages
sqlite.i686                                                 3.7.17-8.el7_7.1                                                base
sqlite.x86_64                                               3.7.17-8.el7_7.1                                                base

解決策

自分でrpmからインストールする
(最新version は glibc が必要で CentOS7は未対応そうなので
とりあえず sqlite v3.8.11)

$ wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-devel-3.8.11-1.fc21.x86_64.rpm

$ wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-3.8.11-1.fc21.x86_64.rpm

$ sudo yum install sqlite-3.8.11-1.fc21.x86_64.rpm sqlite-devel-3.8.11-1.fc21.x86_64.rpm

$ yum list | grep sqlite
Installed Packages
sqlite.x86_64                                               3.8.11-1.fc21                                                   @/sqlite-3.8.11-1.fc21.x86_64

そして bundle install も通った

'./rails-proj/'
$ bundle install
...
Installed!

参考

https://qiita.com/8zca/items/175efb0612070530d186

https://www.reddit.com/r/linuxadmin/comments/c9hy5w/trying_to_upgrade_sqlite_3717_to_version_38_on/

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

商品情報編集機能を実装したい~part1~

前提

  • ruby on rails 6.0.0 を使用。
  • ユーザー機能はdeviseにより導入されているものとする。
  • viewファイルは全てhaml形式とする。
  • ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。

はじめに

プログラミング学習のため、某フリマサイトのコピーサイトを作っています。
商品情報編集機能を実装したが、画像の複数投稿になかなか苦戦したので参考になればと思い書くことにしました。
大した量じゃないのにpart分けしているのはあとで見たときに達成感を得るためです。許してください。(いっぱい書いたぞ!ってなりたい)
part1で特に詰まる部分はないと思いますが、part2以降の土台になってくるのでウォーミングアップ程度に書いていきます。

仕様書

  • 商品に登録された情報をひとつひとつ変更できる。
  • 商品情報を編集できるのは商品を登録したユーザーのみ。
  • 画像の差し替えは一枚ごとにできる。
  • 商品名や画像など、すでに登録されている情報は編集画面にあらかじめ表示される。
  • エラーハンドリングを行う。
(マークアップまで完成したら参考画像を載せようと思ってます)

手順

1, 土台となるコントローラやモデルの作成。
2, imageテーブルを関連づけて画像の投稿を実装。
3, jQueryを導入して複数画像の投稿を実装。
4, メインとなる編集機能を実装。
5, 画像のプレビューをする。

これら5つの手順をそれぞれpart分けして実装していくことにする。

それでは、

いざ実装へ

まずは基礎的な部分を固めていく。
最初にターミナルから。

$ rails g contoroller products
$ rails g model product
$ rails db:migrate

いつもの流れですね。データベースの作成やマイグレーションファイルの記述など細かい点は省略します。

それでは早速ルートファイルから記述していきましょう。

app/config/routes.rb
Rails.application.routes.draw do
  root "products#index"
  resources :products
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
end

deviseの部分はあらかじめ実装してあるユーザー機能に関する部分なので気にしなくて大丈夫です。
ここも細かい説明は不要ですね。resourcesで基本のアクションを作り、ルートパスにindexアクションを指定しています。(今回、destroyとshowは使わないのでexceptして頂いても大丈夫です)

次にcontrollerの記述です。

app/controllers/products_controller.rb
class ProductsController < ApplicationController
  before_action :ensure_current_user, only[:edit, :update]
  before_action :set_product, only[:new, :create, :edit, :update]

  def index
    @products = Product.all
  end

  def new
    @prodcut = Product.new
  end
  def create
    @product = Product.new(product_params)
    if @product.save
      redirect_to products_path
    else
      render :new
    end
  end

  def edit
  end
  def update
    if @product.update(product_params)
      redirect_to products_path
    else
      render :edit
    end
  end

  private
  def product_params
    params.require(:product).permit(:name).merge(user_id: current_user.id)
  end
  def ensure_current_user
    product = Product.find(params[:id])
    if product.user_id != current_user.id
      redirect_to action: :index
    end
  end
  def set_product
    @product = Product.find(params[:id])
  end
end

ひとまずはこんなところでしょうか。
順を追って解説していきます。

まずは基本のアクションについて。

@products = Product.all

indexは問題ないですね、productテーブルに登録された全てのレコードを取り出しています。

@prodcut = Product.new

new,createでは商品の登録を行っています。モデル.newで新しいオブジェクトを生成し、そこにフォームの値を代入します。

def product_params
 params.require(:product).permit(:name).merge(user_id: current_user.id)
end

そうして送信されたデータをproduct_paramsメソッドで受け取っています。みんな大好きストロングパラメータですね。
最低限の機能を実装するだけなのでカラムはnameだけにしています。

if @product.save
 redirect_to products_path
else
 render :new
end

続くif文ですが、これがエラーハンドリングというやつです。
処理が成功した場合はindexへ、逆に失敗した場合はnewを再表示させています。
概念としては複雑な部分もありますが、機能としてはもうお馴染みといっていいと思います。

@product = Product.find(params[:id])

次にedit,updateアクションとなりますが、やっていることは先ほどと同じです。createと違うところと言えば、すでにデータが存在するので新しくオブジェクトを作る必要がなく、findメソッドで選択したproductをもってきていることです。

def ensure_current_user
 product = Product.find(params[:id])
  if product.user_id != current_user.id
  redirect_to action: :index
 end
end

最後にensure_current_userメソッドです。
一見難しそうに見えますが処理はすごく簡単。要は選択したproductのユーザー情報とログインしているユーザー情報が違う場合は編集できませんよーってことです。

さて、コントローラーの記述が終わったのでそろそろviewファイルを記述します。

app/view/products/index.html.haml
- if user_signed_in?
  - @products.each do |product|
    - if product.user_id == current_user.id
      = link_to edit_product_path(product.id) do
        #{product.name}
  = link_to("出品", new_product_path)

  %h2 ログインしています
  = link_to 'ログアウト', destroy_user_session_path, method: :delete

- else
  %h2 ログインしていません
  = link_to '新規登録', new_user_registration_path
  = link_to 'ログイン', new_user_session_path

まずはindexからですが、着目すべきは最初の6行だけです。
ユーザーがログインしているか。
その商品はそのユーザーが登録したものか。
といった二つの条件のもと、editとnewのパスを表示させています。

app/views/products/_form.html.haml
= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  = f.submit 'SEND'

こちらも特別に説明すべきことはないと思います。
form_withメソッドを使ってproductモデルにデータを送るというものですね。あとはこいつを部分テンプレートとしてnew.html.hamlとedit.html.hamlにrender表示させるだけです。

最後に

ここまでで、商品のデータを作成し、それを編集する機能を実装することができました。(カラムは名前だけですが、、、)

仕様書でいうところの、

  • 商品に登録された情報をひとつひとつ変更できる。
  • 商品情報を編集できるのは商品を登録したユーザーのみ。
  • エラーハンドリングを行う。

これら3つをクリアすることができたことになります。
次のpartでは画像(imageテーブル)を追加できるようにしていくのでよければお付き合いください。

商品情報編集機能を実装したい~part2~

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

BonsaiのReindexで "this cluster currently has [3000]/[3000] maximum shards open" とエラーがでる

Heroku、Rails、Elasticsearch、Bonsai、Searchkickという環境で

$ bundle exec rake searchkick:reindex CLASS=Use
# もしくは
$ bundle exec rake searchkick:reindex:all

としてReindexをしようとすると以下のようなエラーがでる。

this action would add [2] total shards, but this cluster currently has [3000]/[3000] maximum shards open

Shard(シャード:直訳で破片)というのはインデックスを分割したものらしく、通常はなにかのバックアップとして、メインで使うプライマリーシャードに対してレプリカシャードを作ったりするらしい。

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

しかしShardは3000どころか2しか使ってないとbonsaiに表示されてるんだが‥。

おそらくの原因

おそらくは bonsai が提供する Elasticsearch のバージョンが 7.2.0 で、searchkickの最新版が依存するGemelasticsearchのバージョンが 7.8.0 になっていたためだったと思われる。Gemfile.lockの中を elasticsearch で検索してバージョンが 7.8.0 になってたら、これを疑った方がいい。

The latest version works with Elasticsearch 6 and 7
https://github.com/ankane/searchkick#getting-started

Searchkick - bonsai
https://docs.bonsai.io/article/99-searchkick

SearchkickのRead.meにも、bonsaiのドキュメントにもこのことは触れられていないので気づきにくい。

解決策(たまたまかも)

elasticsearch のバージョンを 7.2.0 と明示的に指定して

# Gemfile
gem 'elasticsearch', '7.2.0' # ←追加
gem 'searchkick'

The bundle currently has elasticsearch locked at 7.8.0

と怒られたのでbundle updateした。

$ bundle update

Fetching elasticsearch-api 7.2.0 (was 7.8.0)
Installing elasticsearch-api 7.2.0 (was 7.8.0)
Fetching elasticsearch-transport 7.2.0 (was 7.8.0)
Installing elasticsearch-transport 7.2.0 (was 7.8.0)
Fetching elasticsearch 7.2.0 (was 7.8.0)
Installing elasticsearch 7.2.0 (was 7.8.0)

しかし他のGemのバージョンも上げてしまったので、特定のGemだけをアップデートさせるbundle update elasticsearchとした方が良かったかもしれない。

あとはHerokuにプッシュしてReindexしたら上手くった。

$ git push heroku master
$ heroku run bundle exec rake searchkick:reindex:all

参考

shardの状態の確認コマンドはこちらが参考になった?
https://swfz.hatenablog.com/entry/2015/07/22/040354

その他

bonsaiのConsoleでインデックスを削除してReindexしたら上手くこともあった。

GET /_cat/indices
# もしくは
GET /_aliased/
# でインデックス名を確認し
DELETE /posts_production_xxxxxxxx
# で一度インデックスを削除してからReindexし直すと直る。

けどpuroductionで同様にしたらダメでしばらく検索機能が使えない状態に‥。bonsaiを削除して入れ直してもダメ、heroku restartしてもダメで、原因を特定せずに本番環境でやってしまう危険さを身にしみたので皆さんも注意してほしい。

レプリカシャード

bonsaiをHerokuの管理画面から入れただけなので、今回レプリカシャードの指定をしていない。シャードが最大上限3000に達したというのはご判定だろうが、bonsaiのドキュメントにあるようにnumber_of_replicasをModelで設定した方がいいのかもしれない。

User.rb
class User < ApplicationRecord
  searchkick settings: { number_of_replicas: 0 } 
end

もしかするとProcfile経由でリリース時にReindexを自動実行しようとしたせいでこうなったのかもしれない。force push とかもしちゃっていたので‥。

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

[未解決] BonsaiのReindexで "this cluster currently has [3000]/[3000] maximum shards open" とエラーがでる

解決したと思ったら再発するの繰り返しです。Qiitaには非公開に戻す機能が内容で、備忘録も兼ねて未解決のまま公開しておきます。詳しい人いたらコメントで教えてくれると嬉しいです。

Heroku、Rails、Elasticsearch、Bonsai、Searchkickという環境で

$ bundle exec rake searchkick:reindex CLASS=Use
# もしくは
$ bundle exec rake searchkick:reindex:all

としてReindexをしようとすると以下のようなエラーがでる。

this action would add [2] total shards, but this cluster currently has [3000]/[3000] maximum shards open

Shard(シャード:直訳で破片)というのはインデックスを分割したものらしく、通常はなにかのバックアップとして、メインで使うプライマリーシャードに対してレプリカシャードを作ったりするらしい。

しかしShardは3000どころか2しか使ってないとbonsaiに表示されてるんだが‥。

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

解決策①(別アプリで再発)

stagingとしている方で、bonsaiのConsoleでインデックスを削除してReindexしたら上手いった。

GET /_cat/indices
# もしくは
GET /_aliased/
# でインデックス名を確認し
DELETE /posts_production_xxxxxxxx
# で一度インデックスを削除してからReindexし直す

けどpuroductionで同様にしたらダメ‥。bonsaiを削除して入れ直してもダメ、heroku restartしてもダメだった‥。

解決策②(ただし別アプリで再発)

Bonsai が提供する Elasticsearch のバージョンが 7.2.0 で、Searchkickの最新版が依存するGemelasticsearchのバージョンが 7.8.0 になっているからではと考えた(Gemfile.lockの中を elasticsearch で検索してバージョンが 7.8.0 になってたので気づいた)。

The latest version works with Elasticsearch 6 and 7
https://github.com/ankane/searchkick#getting-started

Searchkick - bonsai
https://docs.bonsai.io/article/99-searchkick

SearchkickのRead.meにも、bonsaiのドキュメントにもこのことは触れられていないが関係があるのでは?

elasticsearch のバージョンを 7.2.0 と明示的に指定して

# Gemfile
gem 'elasticsearch', '7.2.0' # ←追加
gem 'searchkick'

The bundle currently has elasticsearch locked at 7.8.0

と怒られたのでbundle updateした。

$ bundle update

Fetching elasticsearch-api 7.2.0 (was 7.8.0)
Installing elasticsearch-api 7.2.0 (was 7.8.0)
Fetching elasticsearch-transport 7.2.0 (was 7.8.0)
Installing elasticsearch-transport 7.2.0 (was 7.8.0)
Fetching elasticsearch 7.2.0 (was 7.8.0)
Installing elasticsearch 7.2.0 (was 7.8.0)

他のGemのバージョンも上げてしまったので、特定のGemだけをアップデートさせるbundle update elasticsearchとした方が良かったかもしれない。

これをHerokuにプッシュしてReindexしたら上手くった。

$ git push heroku master
$ heroku run bundle exec rake searchkick:reindex:all

シャード数について

bonsaiをHerokuの管理画面から入れただけなので、今回レプリカシャードの指定をしていない。シャードが最大上限3000に達したというのは誤判定だろうが、bonsaiのドキュメントにあるようにnumber_of_replicasをModelで設定した方がいいのかもしれない。

User.rb
class User < ApplicationRecord
  searchkick settings: { number_of_replicas: 0 } 
end

その他

もしかするとProcfile経由でリリース時にReindexを自動実行しようとしたせいでこうなったのかもしれない。force push とかもしちゃっていたので‥。またstagingとproductionで解決した方法が違ったので、この2つのアプリに何か違いがあるのかも‥。

参考

shardの状態の確認コマンドはこちらが参考になった?
https://swfz.hatenablog.com/entry/2015/07/22/040354

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

ElasticsearchのReindexで "this cluster currently has [3000]/[3000] maximum shards open" とエラーがでるとき

heroku、Rails、Elasticsearch、bonsaiという環境で

$ bundle exec rake searchkick:reindex CLASS=Use
# もしくは
$ bundle exec rake searchkick:reindex:all

としてReindexをしようとすると以下のようなエラーがでる。

this action would add [2] total shards, but this cluster currently has [3000]/[3000] maximum shards open

Shard(シャード:直訳で破片)というのはインデックスを分割したものらしく、通常はなにかのバックアップとして、メインで使うプライマリーシャードに対してレプリカシャードを作ったりするらしい。

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

しかしShardは3000どころか2しか使ってないとbonsaiに表示されてるんだが‥。

結論(編集中)

bonsaiのConsoleでインデックスを削除してReindexしたら上手くこともあったけど、本番環境でもダメ、bonsaiを削除して入れ直してもダメ、heroku restartしてもダメ。Qiitaに非公開に戻す機能がないので以下をやると危険です。取り急ぎ。

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

GET /_cat/indices
# もしくは
GET /_aliased/
# でインデックス名を確認し
DELETE /posts_production_xxxxxxxx
# で一度インデックスを削除してからReindexし直すと直る。

Procfile経由でリリース時にReindexを自動実行しようとしたせいでこうなったのかもしれない。

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

ポートフォリオ

ポートフォリオ1

こちらのポートフォリオは環境構築だけで開発は行っていません。
(aws,nginx,ruby,docker,mysql)を使って環境構築を行いました。

https://github.com/tmk616window/docker-ruby2

ポートフォリオ2 

ポートフォリオ投稿サイトを作りました。

URLはこちらになります

https://github.com/tmk616window/review00
https://review0.herokuapp.com/

経緯

まずなぜこれを作ろうと思ったのか説明させていただきます。
自分がインターンの採用のためにポートフォリオを作ろうと思ったときに「何を作ればいいかわからない。どこまで作りこめばいいかわからない。」という悩みがありました。
ツイッターやyoutubeのコメント欄でもこういった悩みを持った方々をよく見かけました。
それだったら何を作ればいいのかまたどれくらい作りこめばいいのかの指標になるようなものをポートフォリオに落とし込んでみようと思いました。

コンプト

エンジニア採用が決まった方が記事を投稿し、それを未経験の方が参考にしてポートフォリオを作る指標を定めることを目標にしています。

機能

では実際に機能の説明をさせていただきます。

トップページ

2020-07-10.png

こちらがトップページになります。
採用された方が自分のポートフォリオを6項目で評価し、数値化してわかりやすくしています。

詳細ページ

2020-07-10 (7).png

興味がある投稿をクリックすると詳細ページに移動します。
詳細ページにはポートフォリオの情報がより詳しく記載されています。
いいねボタンはajaxで実装しました。
いいね数の数字もリアルタイムで変わるように実装しました。

相談

2020-07-10 (2).png

詳細ページの「相談する」のボタンを押すと詳細ページに移動します
相談ページではslack形式でリアルタイムに相談できるようなものを作りました。
こちらでは詳細ページで気になったことなどを質問できるようにするために作りました。
入力したメッセージは相手のマイページに通知が行くようになっています
actioncableを使いました。

マイページ

2020-07-10 (3).png

上のチャットメッセージのところに先ほど他のユーザーが相談ページで送ったメッセージの通知が来るようになっています。
マイページの画像はawsのs3に接続していましたが料金がかかりすぎていたので接続を切りました。
コード自体は残っているのでそちらを参照お願いします。
ほかのユーザーのマイページに入ると画像の下にフォローボタンが出てきてフォローすることができます。

イベント一覧

2020-07-10 (5).png

こちらは時間があったのでサブで機能追加したものです。
画像はawsのs3に接続していましたが料金がかかりすぎていたので接続を切りました。
コード自体は残っているのでそちらを参照お願いします。
気になるイベントをクリックすると詳細ページに飛びます。

イベント詳細ページ

2020-07-10 (6).png

詳細ページに飛びます。
下のイベント申し込みボタンをクリックすると申し込みページに飛んで申し込むことができます。
申し込みが完了すると相手のマイページのイベントメッセージのところに通知が行くようになっています。

以上でポートフォリオの説明を終わります。

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

rails db:migrate で BusyException: database is locked

以下のようなエラーがでたが、原因はデータベースのGUI閲覧ツール(DB Browser for SQLiteなど)で変更を保存前の操作が残っていたから。

$ rails db:migrate

rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::BusyException: database is locked

以下によると、SQLiteは同時に1つのプロセスからしかアクセスできないらしく、DB Browser がアクセスしてたからロックされてたということだそうだ。
https://stackoverflow.com/questions/5452662/rake-dbmigrate-is-throwing-busyexception-database-is-locked-exception

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

Searchkickでのデバッグ術

Railsコンソールでdubug: true属性?を付与して検索を実行してやると、そのときに使われた Search Option, Setting, Mapping, Results(検索スコアとか)を見れていい。

Searchkick Dubug

> Post.search "日本の絶景", fields: [:name, :description, :category_name], operator: "or", debug: true
Model Searchkick Options
{:language=>"japanese",
 :_type=>
  #<Proc:0x00007f96bd9f4858 /Users/takuya/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/searchkick-4.4.1/lib/searchkick/model.rb:17 (lambda)>,
 :class_name=>"Post"}

Search Options
{:fields=>[:name, :description, :category_name], :operator=>"or", :debug=>true}

Elasticsearch Query
curl http://localhost:9200/posts_development/_search?pretty -H 'Content-Type: application/json' -d '{"query":{"bool":{"should":[{"dis_max":{"queries":[{"match":{"name.analyzed":{"query":"日本の絶景","boost":10,"operator":"or","analyzer":"searchkick_search"}}},{"match":{"name.analyzed":{"query":"日本の絶景","boost":1,"operator":"or","analyzer":"searchkick_search","fuzziness":1,"prefix_length":0,"max_expansions":3,"fuzzy_transpositions":true}}}]}},{"dis_max":{"queries":[{"match":{"description.analyzed":{"query":"日本の絶景","boost":10,"operator":"or","analyzer":"searchkick_search"}}},{"match":{"description.analyzed":{"query":"日本の絶景","boost":1,"operator":"or","analyzer":"searchkick_search","fuzziness":1,"prefix_length":0,"max_expansions":3,"fuzzy_transpositions":true}}}]}},{"dis_max":{"queries":[{"match":{"category_name.analyzed":{"query":"日本の絶景","boost":10,"operator":"or","analyzer":"searchkick_search"}}},{"match":{"category_name.analyzed":{"query":"日本の絶景","boost":1,"operator":"or","analyzer":"searchkick_search","fuzziness":1,"prefix_length":0,"max_expansions":3,"fuzzy_transpositions":true}}}]}}]}},"timeout":"11s","_source":false,"size":10000}'

Elasticsearch Results
{
  "took": 10,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 21,
      "relation": "eq"
    },
    "max_score": 57.485226,
    "hits": [
      {
        "_index": "posts_development_20200709213129463",
        "_type": "_doc",
        "_id": "28",
        "_score": 57.485226
      },
      {
        "_index": "posts_development_20200709213129463",
        "_type": "_doc",
        "_id": "20",
        "_score": 46.743748
      }
    ]
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]初学者によるRubocopの導入

はじめに

Rubocopとは、Rubyの静的コード解析ツール。
ruby-style-guide
に従って、ソースコードの修正・改善点を指摘してくれます。

また、インデントのずれや、不必要なスペース・改行などは特定のコマンドを打つことで自動で修正してくれるので、自分にとってはもちろん、他の人が見ても読みやすいコードに整形することが出来ます。
※あくまで初学者の観点で設定等を決めています。

導入

gemfile
gem 'rubocop', require: false
ターミナル
bundle install

使ってみる

ターミナル
bundle exec rubocop

実行すると、以下のような出力が得られます。
スクリーンショット 2020-07-10 8.06.47.png

これらがRubocopが検知した修正点になります。
しかし、デフォルトのルール全てに従っていると、コードを書く際に気を配る点があまりにも増えてしまうので、自分やチーム内で許容するルールを設定することが出来ます。

設定をカスタマイズする

アプリケーションのディレクトリに
.rubocop.yml というファイルを作成し、その中に設定を記述します。

rubocop.yml
AllCops:
  # 除外するディレクトリを設定。例えばschemaやmigrationファイルなどは書き直すことが少ないため、検知対象外に
  Exclude:
    - bin/*
    - db/schema.rb
    - node_modules/**/*
    - db/migrate/*
    - vendor/**/*
  # Rails向けのチェックを行う
  Rails:
    enabled: true

# "Missing top-level class documentation comment."を無効
Style/Documentation:
  Enabled: false

# "Prefer single-quoted strings when you don't need string interpolation or special symbols."を無効
Style/StringLiterals:
  Enabled: false

# "Line is too long"を無効
Metrics/LineLength:
  Enabled: false

#'frozen_string_literal: true'を無効
Style/FrozenStringLiteralComment:
  Enabled: false

など様々な設定が可能です。
デフォルトの設定を参考にいろいろいじってみようと思います。

設定後、もう一度

ターミナル
bundle exec rubocop

スクリーンショット 2020-07-10 8.29.43.png
すると、72filesが56filesと、先ほどよりも修正点が減っていることがわかります。

修正する

ターミナル
bundle exec rubocop --auto-gen-config

.rubocop.todo.yml というファイルが自動生成されます。
これにより、一時的に修正点を全て無効とみなされます。
ここでrubocopを実行すると、修正点がない状態になります。

.rubocop.todo.yml
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/OrderedGems:
  Exclude:
    - 'Gemfile'

一例として、上記のような記述があります。
この記述を削除すると、該当部分の無効化していた修正点が復活します。
早速修正して行きますが、
Cop supports --auto-correct.
という記述がある場合、Rubocopが以下のコマンドで自動で修正してくれます。

ターミナル
bundle exec rubocop -a

今回修正したのは、Gemfileのgemの記述がアルファベット順になっていないのを正しい順番に並べ替えました。

まとめ

1.gemを導入し、.rubocop.ymlに設定をカスタマイズする
2.bundle exec rubocop --auto-gen-configを実行し、
 .rubocop.todo.ymlを生成する。
3..rubocop.todo.ymlに書かれている記述を1つ消し、修正する
4.3.を繰り返す。

実際の現場では、自動化されていてあまり自分で修正することはなさそうですが、
まだまだ初学者ですので、一つ一つ改善していって規約を確認し、
初めから読みやすいコードを書けるエンジニアになれるよう心がけます。

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

Railsでcredentials.yml.encをAtomエディタで編集したかった色々(Windows)

初めてQiita書きます書き方全然わかりません許してください。
タイトルの通り、Rails5.2以降で追加されたらしいcredentials.ymlを
Windows環境でAtomエディタにて編集したかった時の色々です。(筆者はRails6ですが)
自分にいい感じの日本語記事が見つからなかったので、もういっそ自分で忘備録作ってしまおうと。
あと同じ目的だったり、ハマった人のためになればいいなと。



大先輩達による記事を読んでからのお話

以下2記事、リンクになっています。

Rails5.2から追加された credentials.yml.enc のキホン

たった30分でわかるcredentials.yml.enc[rails5.2] - 環境変数を定義する使い方 -




さっそくエラーにぶち当たるお話

rails credentials:editしてのエラーメッセージ。上記の記事に書いてある通りのエラーメッセージなので理解はできたのですが、なにせAtomエディタで編集したいのと、Windows環境なので色々自分とは環境が違ってくるなと。

コレ

$ rails credentials:edit

No $EDITOR to open file in. Assign one like this:

EDITOR="mate --wait" bin/rails credentials:edit

For editors that fork and exit immediately, it's important to pass a wait flag,
otherwise the credentials will be saved immediately with no chance to edit.

EDITORが指定されてないよ、何使ったらいいのわかんないよ。って言われるやつですね。
上の方にある記事ではvi、vimとかの指定でしたね。

今だけはとりあえずAtom使わせてください。初心者なんです......。


Atomエディタを指定してあげる の前に

Windows環境でシステム環境変数を指定するのですが。一体どうやって環境変数:EDITORを登録するんだ?:rolling_eyes:

Windowsでシステム環境変数いじるなんて余裕ですバッチこいな人は飛ばしてください。



Windowsでシステム環境変数の設定の仕方

Windowsのシステム環境変数の設定画面への行きつき方は
過去の自分へはこちらが分かりやすかったです。
(Qiita外の記事です)
Windows 10でPath環境変数を設定/編集する:Tech TIPS - @IT


が、めんどくさい私はWindows検索窓にて検索しちゃいます。
2020-07-10_02h23_41.png



2020-07-10_02h27_27.png
開いたらこんな画面。そして環境変数(N)...
と意味深な所をクリック:point_up:


2020-07-10_02h44_24.png
こんな画面が開いたら「新規」の部分どちらかをクリック
(モザイク広くてごめんなさい:no_mouth:
私は上のユーザー環境変数で「新規」しています。


ではWindowsでシステム環境変数の設定の仕方余裕ですバッチこいっと
すっ飛ばした勢と合流します。





Atomエディタを指定してあげる

変数EDITORにAtomエディタを割り当てます。
その時の変数名と変数値がこちら。
2020-07-10_02h58_56.png

変数名を 「EDITOR」
変数値を 「"atom" --wait」
としてOKです。
(--wait を入れとかないと、コマンドプロンプトからrails credentials:editして
Atomエディタ起動する前に処理が進んじゃってエディタ開かせてくれません:sob:

一応PC再起動して環境変数を反映させます。


あなたのプロジェクトファイルにてrails credentials:editしてみてください。そこそこ時間経ってから
Atomエディタが元気よく立ち上がりましたか?

14816.credentials.yml
# aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: ai3id93jaso......

と表示され、開いているファイル名が14816.credentials.ymlとか13613.credentials.ymlとか
最初に数列が追加された名前で開きましたか?
成功です。

余談

```
ちなみに今開いているそのファイルは一時ファイルらしく
C:\Users\ユーザー名\AppData\Local\Temp......とかに一時的に格納されているようです。
Atomエディタ自身の上部を見ればどこから開いているのか分かりますね
2020-07-10_03h23_46.png

```




あとは好きなように編集して保存してエディタを閉じるだけ。
閉じたら

File encrypted and saved.

なんてコンソールに続きが出てたら大成功です。

反映されているかの確認のため、rails credentials:show してみてください。






以上です。以上なんですけど。
時々、

Function provided here: Object.<anonymous> (C:\Users\ユーザー名\AppData\Local\atom\app-1.48.0\resources\app.asar\node_modules\github\lib\worker.js:79:22
Remote event names: destroyed, crashed
File encrypted and saved.

とかとかなんか、destroyed???? crashed???やばくない??
となりますが。厳密には違うかもしれませんけど、それはエディタ自体を終了する前にタブを閉じていたりするとなります。

一応Remote event names: destroyed, crashed出ていてもきちんと暗号化されていますし、
rails credentials:showもRails.application.credentials.~~~~
でも呼び出せるので大丈夫だとは思いますが。


怖いので。


rails credentials:edit でAtomエディタを開く(開かせる)
開いたエディタで編集する
保存する
右上の✕ボタンで "Atomエディタ自体" を終了する。
コンソールに戻ってくると「File encrypted and saved.」だけが出てる。
rails credentials:showで反映されているか確認してみる。


なのが安心できますね。

エラーメッセージが出たときにgithubの何かしらが出ていたので、
git(commit複数行メッセージ)とかでもAtomエディタ使っている人はこうなるのかもしれません。

読みづらい文章でごめんなさい。Qiitaどころかこんな長文ほぼ初めて書きました。
読んでいただいた方ありがとうございました。:hugging:

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

【Rails6】CarrierWaveとfog-googleでGoogle Cloud Storage(GCS)に画像をアップロードした

Rails6で開発していますが、画像のアップロード機能どうしようかな〜と思って、active storageを最初に利用していましたが、RailsをAPIにしてNuxt.jsに返却するのが地味に面倒とか、気をつけないとN+1問題をめっちゃ発生させたりしそうで嫌だったので、CarrierWaveを利用することにしました。(別に頑張っても良かったんですが、その辺を考えるのが面倒だったので、もう少し実装が軽くなる方法を選びました。)

また、改めてCarrierWaveを利用しようと思ったきっかけも以下でした。

https://tech.dely.jp/entry/2019/12/22/145733

しかし、CarrierWaveに移行したのはいいんですが、S3の利用率が高いのかは分かりませんが、GCSの情報があまりなかったんですよね…。

ただ、画像をRails6(consoleですが)で、GCSに画像をアップロードすることが出来たため、備忘録を残したいと思います。

Gemfile

gem "carrierwave"
gem "fog-google"
gem "google-api-client"
gem "mime-types"

Uploader

アップローダー名はImageとでもしておきましょう。

$ rails g uploader Image
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base

  # テスト以外の環境はGCSにアップロードされるよう設定
  if Rails.env.test?
    storage :file
  else
    storage :fog
  end

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

CarrierWave

あらかじめ、Google Cloud Storageのサービスアカウントを作って、認証情報となるJsonを取得しておきましょう。

※ちなみに閲覧についてはallUsersが可能です。

以下が参考になるかもしれません。

https://cloud.google.com/iam/docs/creating-managing-service-account-keys?hl=ja

config/initializers配下に、carrierwave.rbのファイルを作りましょう。

dotenvとcredentialsがごっちゃになってますが気にしないでください…

carrierwave.rb
CarrierWave.configure do |config|
  config.fog_provider = "fog/google"
  config.fog_credentials = {
    provider: "Google",
    google_project: ENV["GOOGLE_CLOUD_PLATFORM_PROJECT_NAME"],
    # ここでサービスアカウントのJsonを呼び出すようにしてあげる
    google_json_key_string: Rails.application.credentials.gcs[:google_cloud_storage_credential_content]
  }
  config.fog_directory = ENV["GOOGLE_STORAGE_BUCKET_NAME"]
end
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/

credentials.yml.encの書き方とかは、以下の記事が参考になりました。

https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39

この書き方地味に公式に載ってなくて、Issue起票して解決されていた方がいました。

そのドキュメントも共有しておきます。

https://github.com/fog/fog-google/issues/385

Model

Aritlce(記事)にthumbnail(サムネイル)を設定する想定で話を進めます。

サムネイルのカラムとアップローダークラスを紐付けましょう。

article.rb
class Article < ApplicationRecord
  mount_uploader :thumbnail, ImageUploader
end

Rails Console

既にDBにデータがある前提のコマンドです。

また、画像は適当なディレクトリに入れています。

[1] pry(main)> article = Article.last
[2] pry(main)> thumbnail = Rails.root.join("app/assets/images/test.png").open
[3] pry(main)> article.update(thumbnail: thumbnail)

これで完了です。

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

(1分で決める)empty?とblank?とpresent?の使い分けについて

(備忘録)説明最小限にしてすぐ使用できるようにまとめた。(結論)blank?かprresent?をどっちか覚えとけば何とかなる。

empty?を使う時は

下記の条件を満たしたうえで中身が空であるか知りたいときに使用。
①必ず入れ物が存在していること
②中身が数字やtrue,falseでないこと

  if box.empty?

①②を満たさない場合は NoMethodErrorが生じる。

blank?を使うときは

中身が空orそもそも存在しているかを知りたいときに使用

 irb(main):001:0> box = nil
=> nil
irb(main):002:0> box.blank?
=> true

present?を使用するときは

存在していてかつ中身が空ではないことを知りたいときに使用(!blank?と同義)

irb(main):006:0> box = nil
=> nil
irb(main):007:0> box.present?
=> false

もう少し詳しく知りたい、この場合はどうすればいいの?ってときは

https://qiita.com/go_d_eye_0505/items/541110cb9821734b0623

表形式でパターンを網羅しているのでわかりやすかったです。

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

Rails6 + Bootstrap4.5でドロップダウンが機能しない

以下解決プロセス

application.html.erbの

タグ内に
以下を挿入
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>

/app/assets/javascript/application.jsがないよと言われるようになるので

/app/assets/javascript/application.js
を作成し以下を記述
(Rails 6ではデフォルトでこの場所にapplication.jsがない)

//= require jquery3
//= require popper
//= require bootstrap

これだけだとapplication.jsがassets pipelineに含まれてないとエラーがでるので

/app/assets/config/manifest.js

//= link_tree ../images
//= link_directory ../stylesheets .css
//= link application.js # ココを追記

これでドロップダウンメニューが機能するようになりました

Bootstrapではないですがこちらを参考にしました
Rails6でSemanticUIのJavaScriptを読み込む方法(ドロップダウン・アコーディオン等を使いたい!) - Qiita

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

TECH CAMP学習 個人アプリ作成②

deviseを使用して、ユーザーモデルを作成

個人アプリ作成でdeviseのインストールを行いました。
まずはGemfileに下記を入力します。
image.png

ちなみにdeviseは日本語で「工夫する、考案する、案出する、発明する」という意味だそうです。

次にターミナルにて
『bundle install』
『rails g devise:install』
『rails g devise user』

を順番に実行します。

そしてマイグレーションファイルを以下2点のように編集します。
※注意 #は省いていただいて結構です。
① null:falseは空の状態での保存を防ぐ為に使用します。
image.png
②unique:trueはテーブル内で名前の重複を禁止させる為に使用します。
image.png

次にapp/models/user.rbを下記のように編集します。
image.png
validatesは日本語で検証する、presenceは存在、uniquenessは唯一性という意味だそうです。

英語で書かれてあるコードを普通に日本語に訳すことで、意味もよく捉えられるように感じました。
あとmigrationは移動、移行という意味のようです。つまりマイグレーションファイルはデータベースのテーブルへの移動するための設計図のようなイメージでよいのではないでしょうか。

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