20201028のRailsに関する記事は24件です。

Rails+herokuでLINEbotを作ってみた

作ったもの

毎朝ゴミ出しの通知をしてくれるBOTがあったら便利だなーと思い、
勉強がてら作ってみました。

スクリーンショット 2020-10-28 23.01.21.png

使用言語

Ruby
Ruby On Rails6
Heroku
LINE Messager API

STEP 1 メッセージをオウム返しするLINEBOTの作成

こちらについては他の方が詳しく投稿されているので割愛します。

参考記事

https://qiita.com/y428_b/items/d2b1a376f5900aea30dc

https://qiita.com/natsukingdom-yamaguchi/items/e84dffdd90d7f5ef8224

STEP 2 PUSHメッセージ

タスクを追加し、決まった時間になったらメッセージを送信します。

タスクの作成

$rails g task reminder_task

次にコントローラーを生成

$rails g controller webhook trash

descriptionとtaskを記述

lib/tasks/reminder_task.rake
namespace :reminder_task do
  desc "ここにタスクのお題"
  task :trash => :environment do
    webhook = WebhookController.new
    puts webhook.trash
  end
end

次にWebhookcontrollerのアクションを定義

app/controllers/webhook_controller.rb
  def trash
    puts "メッセージ"
  end

ここでrakeタスクに追加されているか確認

$ rake -T
rake reminder_task:trash  タスクのお題  

追加されているか確認が出来たら

$ rake reminder_task:trash

【メッセージ】が送信されたらOK

環境変数の設定

PUSHメッセージを利用する場合は環境変数を設定しなければなりません。

環境変数については以下を参照しました。
https://qiita.com/yuichir43705457/items/7cfcae6546876086b849
https://qiita.com/noraworld/items/bfa80811c9e30b4474af

設定方法

dotenvをgemに追加します

gem 'dotenv-rails'

.envファイルをアプリケーションディレクトリのルートディレクトリに作成
(appやgemfileがある場所に作成)

LINEチャネルの諸々を設定

/.env
LINE_CHANNEL_SECRET='xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
LINE_CHANNEL_TOKEN='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
LINE_USER_ID='zzzzzzzzzzzzzzzzzzzzzz'

Webhookcontrollerのtrashアクションを書き換える

app/controller/webhook_controller.rb
class WebhookController < ApplicationController
  require 'line/bot'

def trash
  message = {
            "type": "text",
            "text": WebhookController.contents
           }
            client = Line::Bot::Client.new { |config|
              config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
              config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
           }
            response = client.push_message(ENV["LINE_USER_ID"], message)
  end

  def self.contents

      date = Date.today

      case date.strftime('%a')
        when "Mon"
            "今日は月曜日、燃えるゴミの日!"
        when "Tue"
            "無し!"
        when "Wed"
            "今日は水曜日、燃えないゴミ・段ボールの日"
        when "Thu"
            "今日は木曜日、普通ゴミの日だわ!"
        when "Fri"
            "今日は金曜日、缶・瓶・ペットボトルの日!"
        when "Sat"
            "一週間お疲れ!今日は無し!"
        else
            ""
        end
    end
  end

HerokuにPushしスケジューラーを設定すれば、メッセージが届くはずです。

最後に

rubyを勉強して初めて作ったサービス?になりますので、間違ってる点などが多々あるかと思われます。
その際は、ご指摘いただけると嬉しいです!

(参考記事の方々、ありがとうございました。)

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

【Ruby On Rails】FactoryBotを用いてRSpecでモデル単体テストを行うと外部キーが入力されていないことでエラーになる

初投稿
備忘録です。

エラー背景

RSpecでテストを実行する際に、FactoryBot内に記述すべき外部キーについて未入力であった。

FactoryBot.define do
  factory :order_form do
    zipcode       {'123-4567'}
    prefecture_id {1}
    city          {'亜嗚呼市あああ区'}
    address       {'亜嗚呼1-1-1'}
    phone_number  {'09012345678'}
  end
end

エラー内容

OrderForm
  商品購入
    商品購入がうまくいく時
      全項目が存在すれば購入できる (FAILED - 1)

Failures:

  1) OrderForm 商品購入 商品購入がうまくいく時 全項目が存在すれば購入できる
     Failure/Error: expect(@order_form).to be_valid
       expected #<OrderForm:0x00007fe44961c1e8 @zipcode="123-4567", @prefecture_id=1, @city="亜嗚呼市あああ区", @address="亜嗚呼1-1-1", @phone_number="09012345678", @validation_context=nil, @errors=#<ActiveModel::Errors:0x00007fe448aeeaa0 @base=#<OrderForm:0x00007fe44961c1e8 ...>, @messages={:user_id=>["can't be blank"], :item_id=>["can't be blank"]}, @details={:user_id=>[{:error=>:blank}], :item_id=>[{:error=>:blank}]}>> to be valid, but got errors: User can't be blank, Item can't be blank
     # ./spec/models/order_form_spec.rb:10:in `block (4 levels) in <top (required)>'

Finished in 0.17606 seconds (files took 1.9 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/order_form_spec.rb:9 # OrderForm 商品購入 商品購入がうまくいく時 全項目が存在すれば購入できる

注目すべき点はこちらです。

@errors=#<ActiveModel::Errors:0x00007fe448aeeaa0 @base=#<OrderForm:0x00007fe44961c1e8 ...>, @messages={:user_id=>["can't be blank"], :item_id=>["can't be blank"]}, @details={:user_id=>[{:error=>:blank}], :item_id=>[{:error=>:blank}]}>> to be valid, but got errors: User can't be blank, Item can't be blank

外部キーとしていたuser_id、item_idが空欄になっているということを教えてくれました。

エラー修正

エラー内容より、FactoryBot内の記述を修正すれば良いことがわかりました。
あとは指示された通りuser_idとitem_idをFactoryBotに追加してあげれば大丈夫です。

    user_id { FactoryBot.create(:user).id }
    item_id { FactoryBot.create(:item).id }

修正後のターミナル

OrderForm
  商品購入
    商品購入がうまくいく時
      全項目が存在すれば購入できる

Finished in 0.3807 seconds (files took 3.22 seconds to load)
1 example, 0 failures

成功です。

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

rails + postgres sqlでポートフォリオ作成

記事の概要

私が作成したポートフォリオの解説です。
作った背景、機能、苦労した点、工夫したところ、今後の課題をまとめました。

実際に作成したサイトやソースコードは下記のリンクからご覧いただけます。
https://github.com/rotosiri-zu/pcparts
https://pcparts-staging.herokuapp.com/

作った背景

趣味で自作PCを作成したりしているのですが自分が知らないPCパーツのスペックをしりたい他のユーザーはどんなPCパーツを使っているのか気になり、自作PCを作成している他のユーザーからの紹介で使用しているPCパーツや購入したもので作成するときのパーツ選びに参考になればと思いWebアプリを作りました。

また、現職を退職し、Web系開発企業のバックエンドエンジニアになろうと考えていたので、
このアプリを転職活動のポートフォリオとすることに決めました。

スペック

言語

Ruby 2.6.6

フレームワーク

ruby on rails 5.2.4.3

cssフレームワーク

bootstrap4

データベース

postgres sql

バージョン管理

github

本番環境

heroku + Amazon S3

主な機能

ログイン・ログアウト機能

登録したユーザーやゲストログインでログインできます。ホーム画面からもゲストログインでログインすることができます。

circleanimationmuvie

投稿機能

会員登録することで自由に投稿することができます。
入力に誤りがあれば、投稿はされずエラ-メッセージが表示されます。

2020-10-2619 42 59

マイページ機能

ユーザーが投稿したアイテム一覧を確認することができます。

224a16330e9b45b78577fffe23533062

商品名検索機能

投稿時に記入したタイトル名で検索できます。

d32d3f013b5898f6588425a0f95a3f51

カテゴリー検索機能

投稿時に選択肢したカテゴリーで検索できます。

1b3fedbfed2ba3dca6fd34f10a117d3f

口コミ投稿機能

PCパーツの感想を投稿できます。
入力に誤りがあれば、投稿はされずエラ-メッセージが表示されます。

2020-10-2720 33 12

アイテム更新・削除機能

投稿したアイテムを更新・削除ができます。

2020-10-2620 18 54

口コミ更新・削除機能

投稿した口コミを更新・削除ができます。

2020-10-2620 24 43

苦労した点

カテゴリー機能の機能追加でIDエラーの解決に時間がかかったところです。
解決した方法は検証と仮説を立てその後に検索して調べ見つけた記事の通りやってみたところ解決しました。

工夫した点

ホーム画面からゲストログインできるようにしホーム画面に機能の説明を記載したり説明とアイテム投稿一覧の境目に黒線を追加、詳細画面の口コミ投稿のフォームに黒線を追加してわかりやすくしました。

なぜruby on railsの言語を選んだのか

ruby on railsを選んだ理由は初心者が入りやすく参考にする記事やコミニティが多いからruby on railsを選択しました。

今後の課題

今後の課題はテストコードがかけていないのでテストの実装やパンくずリストの実装を追加しようと考えております。

参考文献

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

railsでapp名を変更する方法

環境

Rails 6.0.3.4

流れ

1.gemを入れる
2.dbを削除(不要なら)
3.app名を変更
4.db作成

手順

1.gemを入れる

Gemfileに以下を追記

gem 'rename'

bundleする

$ bundle

2.dbを削除(不要なら)

特になければdbを削除

$ rake db:drop

3.app名を変更

$ rails g rename:into {{新しいapp名}}

作成したapp名のフォルダへ移動

$ cd ../{{新しいapp名}}

4.db作成

dbを作成して

$ rake db:create

マイグレーションする

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

【忘備録】Rubyをやってみて何度も調べたコマンド一覧

はじめに

Rubyを半年程さわってみてなんとなく調べる頻度が多かったコマンドをここでまとめてます。初学者兼初投稿なのでコメントやLGTMなどでフィードバック頂けると励みになります。よろしくお願いします。

Capistrano関連

デプロイ実行
$ bundle exec cap production deploy
1つ前の状態にロールバック
$ bundle exec cap production deploy:rollback

unicorn関連

起動状態を確認する
$ ps auxwww | grep unicorn
プロセスを終了する
$ kill -9 ○○○○○○
すべてのログを確認する ($ cd /var/www/app/log にて)
$ less unicorn.staderr.log
直近10件のログを確認する ($ cd /var/www/app/log にて)
$ tail unicorn.staderr.log

nginx関連

再起動する
$ sudo service nginx restart
すべてのログを確認する (本番環境のホームディレクトリにて)
$ sudo less /var/log/nginx/error.log

MySQL関連

起動状況を確認する
$ sudo service mysqld status
起動する
$ sudo service mysqld start

本番環境まわりのコマンドばかりになってしまいましたが、過不足があれば随時更新していきます。

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

HerokuでSequel ProからClearDBに接続する方法

1. HerokuのCLEARDB_DATABASE_URLを確認する



コマンドを実行

$ heroku config --app アプリ名

:point_down:CLEARDB_DATABASE_URLの情報が表示される

CLEARDB_DATABASE_URL: mysql://ユーザー名:パスワード@ホスト/データベース?reconnect=true


2.表示されたCLEARDB_DATABASE_URLの情報をバラして入力する

標準接続モードを選択
スクリーンショット 2020-10-28 20.44.10.png
以上

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

【Rails】ActiveHash(データベースに保存しないデータ)

ActiveHash

Active_Hashとは、都道府県名などの変更されないデータをモデルファイル内に直接記述することで、データベースへ保存せずにデータを取り扱うことができるGem。

実装手順

gemの追加

gem 'active_hash'

モデル作成

% rails g model genre --skip-migration

--skip-migration」とは、モデルファイルを作成するときに、マイグレーションファイルの生成を行わないためのオプションです。データベースに保存しないため、マイグレーションファイルを作成する必要はありません。

モデル設定

ActiveHash::Baseは、ActiveRecordと同様のメソッドが使用できるようになります。
ActiveHash::Baseを継承することで、Genreモデルに定義したオブジェクトに対してActiveRecordのメソッドが使用できるようになります。

genre.rb
class Genre < ActiveHash::Base #ApplicationRecordから自分で変更する。
 self.data = [
   { id: 1, name: '--' },
   { id: 2, name: '経済' },
   { id: 3, name: '政治' },
   { id: 4, name: '地域' },
   { id: 5, name: '国際' },
   { id: 6, name: 'IT' },
   { id: 7, name: 'エンタメ' },
   { id: 8, name: 'スポーツ' },
   { id: 9, name: 'グルメ' },
   { id: 10, name: 'その他' }
 ]
 end

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

カラムをgenre_idにする理由は、ジャンルのidをarticlesテーブルに保存するため。

class CreateArticles < ActiveRecord::Migration[6.0]
 def change
   create_table :articles do |t|
     t.string     :title        , null: false
     t.text       :text         , null: false
     t.integer    :genre_id     , null: false
     t.timestamps
   end
 end
end

アソシエーションを設定

ActiveHash::Associations::ActiveRecordExtensionsについては、詳細の説明は省きますが、この記述をArticleクラスに記述する事で、belongs_to_active_hashメソッドを使用できます。

article.rb
class Article < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions #自分で追記する。
  belongs_to_active_hash :genre
end

バリデーションを設定

genre_idに関するバリデーションです。
このバリデーションは、genre_idのid:1以外のときに保存できるという意味です。

article.rb
class Article < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :genre

  #空の投稿を保存できないようにする
  validates :title, :text, :genre, presence: true

  #ジャンルの選択が「--」の時は保存できないようにする
  validates :genre_id, numericality: { other_than: 1 } 
end

コントローラーの設定

articles_controller.rb
class ArticlesController < ApplicationController

  def index
    @articles = Article.order("created_at DESC")
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)
    if @article.save
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def article_params
    params.require(:article).permit(:title,:text,:genre_id)
  end

end

フォームの設定

<%= form.collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, オプション, htmlオプション) %>

引数 値 役割
第一引数
(保存されるカラム名) :genre_id 保存先のカラム名
第二引数
(オブジェクトの配列) Genre.all 配列データを指定する
(今回はジャンル情報の配列)
第三引数
(カラムに保存される項目) id 表示する際に参照するDBのカラム名
第四引数
(選択肢に表示されるカラム名) name 実際に表示されるカラム名
第五引数
(オプション) {} 今回はオプションの指定なし
(include_blankという値のない選択肢を先頭にするオプションなどがあります。)
htmlオプション {class:"genre-select"} 今回はクラス名を付与
(cssを当てるため)

<%= form_with model: @article, url:articles_path, local: true do |f| %>
  <div class="article-box">
    記事を投稿する
    <%= f.text_field :title, class:"title", placeholder:"タイトル" %>
    <%= f.text_area :text, class:"text", placeholder:"テキスト" %>
    <%= f.collection_select(:genre_id, Genre.all, :id, :name, {}, {class:"genre-select"}) %>
    <%= f.submit "投稿する" ,class:"btn" %>
  </div>
<% end %>

ビューでの表示

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

【Ruby on Rails】try(:[ ], :key)を読み解く

はじめに

user.try(:[],:name)

初めてこのコードを見た時、
「なんで空の配列をメソッドの部分に入れているんだ、、、?」
と思いました。
Ruby(Rails)の文法をすっ飛ばしてきた弊害ですね、、、

そもそもRailsのtryとは?

Active Supportに搭載されているメソッド (Rubyだけでは使えない)
対象のオブジェクトがnilでない時のみ、指定したメソッドを使う
第一引数に使いたいメソッド、第二引数にその引数(必要であれば)を与えます

user = {name: "test"}

# user[:group]がnilであるため、メソッドエラーが起きる
user[:group].replace("A")
# => NoMethodError: undefined method 'replace' for nil:NilClass

# nilな対象に対してtryを使うとエラーは起きず、nilが帰ってくるだけ
user[:group].try(:replace,"A")
=> nil

空の配列がメソッドに?

それでは本題ですが、rubyのHashには[]のメソッドがあります。
引数にkey名を取ってそのvalueを返します。

user.[] :name
=> "test"
# これはuser[:name]と同じ

ちなみに、「+」などもメソッドとして使えます。

user[:name].+ 2
=> "test2"


今回の場合はtryとこのメソッドの組み合わせなので

user.try(:[],:name)
=> "test"

という結果が返ってきます。

まとめ

[]についてなかなかヒットしなかったので少し理解に苦しみました。
ruby/railsの文法を勉強し直します、、、

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

【Rails】星★の評価を実装する(入力、保存、表示)

はじめに

開発しているメモアプリにランク付けをしていきたいと思います。
今回は三段階で評価していこうと思います。
それに伴い、星★による評価の入力・保存・表示について学習していきます。

参考

jQuery Raty

Railsで星の評価機能をつける

jQuery Ratyの使い方

マイグレーションファイルにカラムを追加

notesテーブル(メモテーブル)のrateカラム(型:float)に評価値を保存する
すでに、テーブルとカラムは作成されていることを前提に進めます。

カラムは、半分の星による評価を行う場合、「0.5」や「1.5」という値を保存することになるため、float型にしておく必要があります。

もし、integerやstringの型として作成してしまっていた場合は、データベースによって変更方法は異なりますが、型の変更が必要になります。下記の記事でこの型の変更の沼にハマってしまった時の解決策を書いています。

class CreateNotes < ActiveRecord::Migration[5.2]
  def change
    create_table :notes do |t|
      t.text :title
      t.integer :user_id
      t.integer :category_id
      t.text :explanation
      t.float :rate

      t.timestamps
    end
  end
end
rails db:migrate:reset

javascriptの読み込み、★画像の読み込み

上記参考リンクを確認しながら、javascriptの読み込みと星の画像の読み込みを行います。

やり方(2種類)

①jQuery Ratyを使用するには、https://github.com/wbotelhos/raty からjquery.raty.jsをダウンロードする。

ダウンロードしたスクリプトをウェブサイトの任意の場所に配置する。

app/javascriptsのフォルダの中にダウンロードしたjquery.raty.jsファイルを配置する

②jQuery Ratyを使用するHTMLの中でJavaScriptを読み込む。RatyはjQueryのプラグインなので、jQueryのスクリプトも必要である。

    <script src="/js/jquery.min.js"></script>
    <script src="/js/jquery.raty.js"></script>

今回は①のやり方で実装していきます。

*ここでJavaScriptも定義していきます。
javascripts/application.jsファイルに追加する

//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery       #追加
//= require jquery_ujs  #追加
//= require_tree .

Gemファイル追加

gem 'jquery-rails'
budle install

モデルの記述(紐付け)

note.rb
class Note < ApplicationRecord
  # ユーザーとの紐付け
  belongs_to :user,optional: true

 # バリデーション
  validates :title, presence: true
  validates :explanation, presence: true

  # カテゴリーと紐付け
  belongs_to :category

  validates :rate, presence: true
  validates :rate, numericality: {
    # only_integer: true,
    less_than_or_equal_to: 3,
    greater_than_or_equal_to: 1,
  }

 # numericality=空を許可する
    numericalityは、デフォルトでは小数も許容してしまいます。rateカラムでは整数のみ許可したいので、 only_integerを。
 
   validates :param, :numericality => { :less_than_or_equal_to => }
   # 数字が3以下であるか
   validates :param3, :numericality => { :greater_than_or_equal_to =>  }
   # 数字が1以上であるか

end

コントローラーの記述

notes_controller.rb
class NotesController < ApplicationController
  before_action :authenticate_user!
  def new
    @note = Note.new
  end

  def create
    # @note = Note.new(note_params)
    @note = current_user.notes.build(note_params)
    @note.save
    redirect_to notes_path
  end

  def index
    # is_validがマッチするレコードを全て取得
    @categorys = Category.where(is_valid: true)
    @q = Note.all.ransack(params[:q])
    @notes = @q.result(distinct: true)
  end

  def show
    @note = Note.find(params[:id])
  end

  def edit
    @note = Note.find(params[:id])
  end

  def update
    @note = Note.find(params[:id])
    @note.update(note_params)
    redirect_to note_path
  end

  def destroy
    @note = Note.find(params[:id])
    @note.destroy
    redirect_to notes_path
  end

  def search
    @categorys = Category.where(is_valid: true)
    @category = Category.find(params[:id])
    @q = @category.notes.all.ransack(params[:q])
    @notes = @q.result(distinct: true).page(params[:page])
    @title = @category.name
    render 'notes/index'
  end

  private

  def note_params
    params.require(:note).permit(:title, :category_id, :explanation,:user_id,:rate)
  end
end

ここではnote_paramsメソッドにrateカラムを追加します。

メモ登録フォームを作る

_form.html.erb
<%= form_with model:note, local: true do |f| %>
  <div class='form-group'>
    <%= f.label :タイトル %>
    <%= f.text_field :title, class: 'form-control', id: 'note_title' %>
  </div>
  <div class='form-group'>
    <%= f.label :カテゴリー %>
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </div>
  <!-- 評価 -->
  <div class="form-group row" id="star">
    <%= f.label :rate,'重要度 ', class:'col-md-1 col-form-label' %>
    <%= f.hidden_field :rate, id: :review_star %>
  </div>
  <div class='form-group'>
    <div id='editor'>
      <%= f.label :内容 %>
      <%= f.text_area :explanation, rows: 10, class: 'form-control', id: 'note_explanation', "v-model" => "input", name: "note[explanation]" %>
    <h2><i class="fas fa-eye"></i> Preview</h2>
    <div id="preview-field" v-html='input | marked'>
    </div>
    <div ></div>
  </div>
  <%= f.submit '登録', class: 'btn btn-success' %>
  </div>
  <% end %>

  <!-- リアルタイムプレビュー -->
  <script type="text/javascript">
    window.onload = function() {
      new Vue({
      el: '#editor',
      data: {
        input: '<%== j @note.explanation %>',
      },
      filters: {
      marked: marked,
      },
      });
    };

   <!-- 評価 -->
    $('#star').raty({
      size     : 36,
      starOff:  '<%= asset_path('star-off.png') %>',
      starOn : '<%= asset_path('star-on.png') %>',
      starHalf: '<%= asset_path('star-half.png') %>',
      scoreName: 'note[rate]',
      half: true,
    });
  </script>

ポイントは下記

# ★の半分の入力ができるようにするため
  half: true,

星による評価の表示

メモ一覧にて、★を表示したい。
上記の「★の入力、保存」と異なる点は、1.入力はせず表示する 、 2.繰り返し処理をするということになります

_notes_index.html.erb
<div class='row'>
  <table class='table'>
    <thead>
      <tr>
        <th>タイトル</th>
        <th>カテゴリー</th>
        <th>重要度</th>
      </tr>
    </thead>
    <tbody>
      <% @notes.each do |note| %>
      <% if user_signed_in? && current_user.id == note.user_id %>
      <tr>
        <td>
          <%= link_to note_path(note) do %>
            <%= note.title %>
          <% end %>
        </td>
        <td><%= note.category.name %></td>
        <!-- 評価 -->
     <td>
         <div id="star-rate-<%= note.id %>"></div>
          <script>
            // 繰り返し処理でもidをidを一意にできるようにnote_idを入れる
          $('#star-rate-<%= note.id %>').raty({
            size: 36,
            starOff:  '<%= asset_path('star-off.png') %>',
            starOn : '<%= asset_path('star-on.png') %>',
            starHalf: '<%= asset_path('star-half.png') %>',
            half: true,
            // 読み込みだけで入力できない
            readOnly: true,
            score: <%= note.rate %>,
          });
          </script>
        </td>
      </tr>
      <% end %>
      <% end %>
    </tbody>
  </table>
</div>

ポイントは下記

# 繰り返し処理を実行してもidを一意に保てるようにreview.idを埋め込む
<div id="note-rate-<%= note.id %>"></div>

# 読み取り専用(入力できない)
readOnly: true,

# 星の入力値を読み込む
score: <%= note.rate %>,

これで完成!

最後に

説明が分かりづらかったりすると思いますがご了承ください。
また、間違っているところがあればご教授いただけると幸いです。

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

Railsで多言語化対応する!

自分が開発しているwebアプリを英語に対応しようとしたときにやったことを書いていきます。

詳しくは>

インストール

Gemfile
gem 'rails-i18n'

インストール

bundle

config/application.rbに設定

config/application.rb
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

    # 対応する言語。今回は日本語(ja)と英語(en)だけ
    config.i18n.available_locales = %i(ja en)

    # 上記の対応言語以外の言語が指定された場合、エラーとするかの設定
    config.i18n.enforce_available_locales = true

    # デフォルトの言語
    config.i18n.default_locale = :ja

controller

controller/application_controller.rb
    before_action :set_locale

    def set_locale
        I18n.locale = locale
    end

    def locale
        @locale ||= params[:locale] ||= I18n.default_locale
    end

    def default_url_options(options={})
        options.merge(locale: locale)
    end

routes.rb

routes.rb
  scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do
    root "posts#index"
    resources :posts
  end

scopeで囲んだ部分が多言語対応するページです。

多言語化

en.ymlとja.ymlを作ります。
多言語化したい文に

views
<%= t(:hello) %>

と書きます。helloのところは自由に変えてください

ja.yml
  ja:
    hello: こんにちは
      # モデルは全て activerecord を起点にする。
      # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
    activerecord:
      models:
        user: 'ユーザー情報'
      attributes:
        user:
          name: '名前'
          mail: 'メールアドレス'
          url:  'ウェブページ'
en.yml
  en:
     hello: "hello"
    # モデルは全て activerecord を起点にする。
      # これにより、User.model_name.human / User.human_attribute_name({attr_name})でアクセス可能。
    activerecord:
      models:
        user: 'User'
      attributes:
        user:
          name: 'Name'
          mail: 'Email'
          url:  'URL'

こんな感じでやっていきます。

参考
http://alfa.hatenablog.jp/entry/2013/12/03/221308

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

Railsのフォームで過去の日付を入力させない方法

はじめに

カレンダーから日付を入力してもらう際に、過去の日付を選択できないようにしたい。
保存するモデルのカラムはDate型を使用。
ビューでのフォーム作成にはform_withを使用。

開発環境

ruby 2.6.5
Rails 6.0.3.3

方法

※余計な記述は省いています
私の場合はorderモデルのstart_date(Date型)に保存するためのフォーム

index.html.erb
<%= form_with model: @order, url: item_order_path, local: true do |f|%>
   <%= f.date_field :start_date, class:"order-date-form %>
<% end %>

カレンダー表示ができて、日付を選択できるようになってます。

しかし、現状では過去の日付も選択できてしまいます。

これを解消するために記述を追加します。

index.html.erb
<%= form_with model: @order, url: item_order_path, local: true do |f|%>
   <%= f.date_field :start_date, class:"order-date-form, min: Date.current %>
<% end %>

同じようにカレンダーは表示されますが、過去の日付は選択できないようになりました。

"Date"についてはActive_supportが用意してくれているもので、デフォルトで使えるようです。
"Date.today"ではなく"Date.current"が推奨されています。

詳しくはこちら Railsガイド

さいごに

バリデーションやコントローラーで防ぐのは各々で追加してみてください。

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

【Rails】遷移前のURLのパス名を取得してリンク先を変更する

環境

rails 6.0.3

説明

遷移前のページのURL内のパス名によって、ページに表示するリンク先を分岐させる方法。

該当のviewファイル
<% if URI(request.referer).path == 'パス名' %>
  #前のページが指定したパスだった場合のリンク先
<% else %>
  #その他のリンク先
<% end %>

補足

例えば、URL
https:///www.example.com/user
だった場合は、パス名は/user になる。

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

Railsで遷移前のURLのパス名を取得してリンク先を変更する

環境

rails 6.0.3

説明

遷移前のページのURL内のパス名によって、ページに表示するリンク先を分岐させる方法。

該当のviewファイル
<% if URI(request.referer).path == 'パス名' %>
  #前のページが指定したパスだった場合のリンク先
<% else %>
  #その他のリンク先
<% end %>

補足

例えば、URL
https:///www.example.com/user
だった場合は、パス名は/user になる。

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

Railsのエラーメッセージの日本語化【devise/Formオブジェクト等】

背景

未経験からエンジニアになるために勉強中です。

簡単なアプリケーションを作成している際に、バリデーションエラーのメッセージが英語なことに引っ掛かり、
エラーメッセージを日本語化する方法を調べていたところ、
知りたい情報が1つにまとまっている記事が見つからなかったため、
自分の備忘録かつ、同じようにプログラミングを勉強中でお困りの方の役に立てばと思い記事を作成しました。
まだ至らぬ点があるとは思いますが、何かお気づきの点があればコメントにてご指摘ください。

環境

  • ruby 2.6.5
  • Rails 6.0

変更前の画面(一例)

何も設定をしていないと、バリデーションエラーのメッセージは以下の画像のように英語で出力されます。
スクリーンショット 2020-10-28 13.30.14.png

変更後の画面(一例)

変更後は以下の画像のように日本語でエラーメッセージが出力されます。
スクリーンショット 2020-10-28 13.33.32.png

日本語化の準備

・下準備①(Gemのインストール)

エラーメッセージを日本語化するためには、rails-i18nというgemを使用します。
Gemfileに以下の一文を追加して、ターミナルにてbundle installを実行してください。

gem 'rails-i18n'
# Gemfileに上記コードを記載

・下準備②(localeの言語設定変更)

次にconfig/application.rbに以下の一文を追記し、言語設定を変更します。
追記後に、サーバ再起動すると下記画像のようにエラーメッセージの一部が日本語化されます。

config/application.rb
class Application < Rails::Application
    config.load_defaults 6.0
    # この下に下記のコードを記載
    config.i18n.default_locale = :ja
    # 上記のコードを記載
  end

スクリーンショット 2020-10-28 13.55.18.png

しかし、このままではmodelの属性名は英語のままです。
ですので、次はそこを日本語に変更します。

・カラム名を日本語に変更する

modelの属性名を変更するためには、日本語での対応表を作る必要があります。
config/locales/models/に、ja.ymlというファイルを作成して、以下のようにカラム名とそれに対応する日本語を記述してください。

config/locales/models/ja.yml
ja:
  activerecord:
    models:
      item: 商品
    attributes:
      item:
        image: 商品の画像
        name: 商品名
        details: 商品の説明
        price: 価格

私はitemというモデルの属性名を変更したかったので、コード内にitemと記載しております。自身の変更したいモデルに合わせて、上記コードのitemという所を変更してください。

・上記の対応表を読み込む

上記で設定したファイルは自動では読み込まれません。
なので、config/application.rbにこのymlファイルの読み込む一文を追記します。

config/application.rb
class Application < Rails::Application
    config.load_defaults 6.0
    config.i18n.default_locale = :ja
# この下に下記のコードを記載
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]
# 上記のコードを記載
  end

ここまで設定ができていれば、下記のように全て日本語表記に変わっているはずです。

スクリーンショット 2020-10-28 13.33.32.png

・わかりやすいエラーメッセージへ変更

rails-i18nというGemを使用すれば大体のエラーメッセージを日本語に変換してくれますが、
自分でバリデーションを設定した際などに下記のように、
一見しただけではわからないような出力をされる場合があります。
そのような場合は自身でエラーメッセージを設定してあげましょう。
以下は一例になります。

【例】

ActiveHashを用いて都道府県名をプルダウン形式で選択できるようにし、それに対して自身でバリデーションをかけた場合
スクリーンショット 2020-10-28 15.14.20.png

«変更前»

スクリーンショット 2020-10-28 15.09.18.png

バリデーションをかけたモデル内のコード
   # 省略
with_options presence: true do
   # 省略
    validates :prefecture_id, numericality: { other_than: 1 }
   # 省略
  end

«変更後»

スクリーンショット 2020-10-28 15.14.02.png

バリデーションをかけたモデル内のコード
   # 省略
with_options presence: true do
   # 省略
    validates :prefecture_id, numericality: { other_than: 1 ,message: 'を選択してください'}
   # 省略
  end

上記のようにmessage: 'メッセージ内容'という風にオプションを設定してあげるとよりわかりやすい日本語に変更できます。

・さらにカラム名を追加したい場合

カラム名をさらに追加したい場合は、下記のように自身で追加してください。

config/locales/models/ja.yml
ja:
  activerecord:
    models:
      item: 商品
    attributes:
      item:
        image: 商品の画像
        name: 商品名
        details: 商品の説明
        price: 価格
        # ここから下に下記のように自身でカラム名と日本語の対応表を記載
        category_id: カテゴリー
        status_id: 商品の状態
        prefecture_id: 発送元の地域

独自に定義したFormオブジェクトの日本語化する場合

・下準備

上記までの設定ができていれば特に必要ありません。
できていなければ、上記手順に従って下準備(Gemのインストール等)と対応表(ja.yml)の作成、
対応表の読み込み(config/application.rbに追記)まで行いましょう。

・config/locales/models/ja.ymlを編集

config/locales/models/ja.ymlを下記のように追記します。

config/locales/models/ja.yml
ja:
  activerecord:
    models:
      item: 商品
    attributes:
      item:
        image: 商品の画像
        name: 商品名
        price: 価格
        details: 商品の説明
# ここから下に下記のコードを追記
  activemodel:
    attributes:
      user_purchase:
        city: 市区町村
        postal_code: 郵便番号
        phone_number: 電話番号
        prefecture_id: 都道府県
        house_number: 番地
        building: 建物名
# 上記のコードを追記(カラム名等は自身で変更してください)

自身で定義したFormオブジェクトを日本語化するには、activemodel:と記載しその下に設定してください。
きちんと設定をしないと日本語に変更をされません。

補足

ActiveModel::Modelと設定しているため、activerecord:下のattributes:にコードを記載しても反応しません。

Formオブジェクトを設定したモデル内のコード
class UserPurchase
  include ActiveModel::Model # 自身でActiveModel::Modelを設定している
  # 省略
end
config/locales/models/ja.yml
ja:
  activerecord:
    models:
      item: 商品
    attributes:
      item:
        image: 商品の画像
        name: 商品名
        price: 価格
        details: 商品の説明
# 設定がActiveRecordではないので、ここから下にコードを書いても反応しません

deviseの日本語化

簡単にユーザー登録機能等が実装できるdeviseもデフォルトの設定は英語です。
こちらも日本語に変更できます。

変更前の画面(一例)

スクリーンショット 2020-10-28 16.08.08.png

変更後の画面(一例)

スクリーンショット 2020-10-28 16.08.16.png

・Gemのインストール

deviseのエラーメッセージを日本語化するためには、devise-i18nとdevise-i18n-viewsというgemを使用します。
Gemfileに以下の一文を追加して、ターミナルにてbundle installを実行してください。

gem 'devise-i18n'
gem 'devise-i18n-views'
# Gemfileに上記コードを記載

・日本語対応表の作成

コマンドにて日本語翻訳ファイルを生成します。
ターミナルにて下記のコマンドを実行してください。

rails g devise:views:locale ja
# 下記のように実行結果が表示されれば成功です。
  create  config/locales/devise.views.ja.yml

きちんとファイルが生成されているか確認しましょう。

config/locales/devise.views.ja.yml
ja:
  activerecord:
    errors:
      models:
        user:
          attributes:
            email:
              taken: "は既に使用されています。"
              # 以下長すぎるため省略
    unlocks:
      new:
        resend_unlock_instructions: "アカウントの凍結解除方法を再送する"
              # 上記が最後の行です

・localeの言語設定変更

すでに設定をしてある場合はこの作業は不要です。
してない場合は下記のコードを追記しましょう

config/application.rb
class Application < Rails::Application
    config.load_defaults 6.0
    # この下に下記のコードを記載
    config.i18n.default_locale = :ja
    # 上記のコードを記載
  end

ここまで作業が完了したら、サーバーを再起動したら日本語に変更されているはずです。

・さらにカラム名を追加したい場合

カラム名をさらに追加したい場合は、下記のように自身で追加してください。

config/locales/models/ja.yml
ja:
  activerecord:
    errors:
      # 以下長いので省略
              confirmation: "が内容とあっていません。"
    attributes:
      user:
        current_password: "現在のパスワード"
        name: 名前
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
        remember_me: "次回から自動的にログイン"
     # 下記に必要があれば自分でカラム名とそれに対応する日本語を追加
        nickname: "ニックネーム"
        first_name: "名前"
        last_name: "名字"
        birthday: "生年月日"
     # 上記のように記載
    models:
       user: "ユーザ"
     # 以下長いので省略

まとめ

以上が私が行ったエラーメッセージの日本語化でした。
必要があれば、修正・追記をしていきます。
ここまでご覧いただき誠にありがとうございました。
何かお気づきの点などございましたら、コメントにてご連絡ください。

参考文献

Railsのバリデーションエラーのメッセージの日本語化
RailsのFormオブジェクトの日本語化
ActiveRecordのvalidatesで表示されるエラーメッセージのフォーマットを変更する
【Rails5】Devise-i18nで日本語化する
上記の記事を参考にさせていただきました。こちらの記事の方が詳しく書いていることもありますので、
そちらも是非参考にしてみてください。

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

Expected exactly 2 elements matching "a[href="/"]", found 0.. のエラーメッセージ

Expected exactly 2 elements matching "a[href="/"]", found 0..のエラーメッセージ



Railsチュートリアルをやっている時にエラーが起きたので、解決した方法を書きます。やっていたのは5章の「5.3.4 リンクのテスト」の所です。
エラーメッセージは

 FAIL["test_layout_links", SiteLayoutTest, 2.2938910419998138]
 test_layout_links#SiteLayoutTest (2.29s)
        Expected exactly 2 elements matching "a[href="/"]", found 1..
        Expected: 2
          Actual: 1
        test/integration/site_layout_test.rb:10:in `block in <class:SiteLayoutTest>'

  1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.29667s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips

}



私の場合はこのファイルに異常がありました。
app/views/layouts/_header.html.erb

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home",   'root_path' %></li>
        <li><%= link_to "Help",   'help_path' %></li>
        <li><%= link_to "Log in", '#' %></li>
      </ul>
    </nav>
  </div>
</header>



この中でも今回は、上記のroot_pathとhelp_pathに' 'がついていたことが原因みたいでした。このエラーが出た原因は、もともとあった'#'にroot_pathなどを入れたことで外し忘れていたみたいでした。
正しくはこちらになります。

        <li><%= link_to "Home",   root_path %></li>
        <li><%= link_to "Help",   help_path %></li>



因みに、app/views/layouts/_fotter.html.erbにも同じようなミスがあったので、上記のエラーが起こった方は確認してみると良いかもしれません。

参照元

https://teratail.com/questions/189008

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

HerokuにデプロイしたMysqlのロールバック方法をメモ

はじめに

RubyOnRailsでアプリを作成しデプロイ。
Heroku上でマイグレーションファイルを実行後のベータデースのリセットをメモしておく。


● エラーメッセージから環境変数を確認

コマンドを実行

$ heroku run rake db:migrate:reset!

エラー画面
エラー画面


● 確認した環境変数 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 を付与して、データベースをdropする

コマンドを実行

$ RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:drop

 実行後
スクリーンショット 2020-10-28 16.09.06.png


● 再生成

コマンドを実行

 データベースの生成
$ heroku run rails db:create

 マイグレーションを実行
$ heroku run rake db:migrate

参考

[Rails5] heroku db:resetからmigrateまで
Rails5のproductionでrake db:dropはできない、普通には

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

herokuにデプロイしたMysqlのロールバック方法をメモ

はじめに

RubyOnRailsでアプリを作成しデプロイ。
heroku上でマイグレーションファイルを実行後のベータデースのリセットをメモしておく。


● エラーメッセージから環境変数を確認

コマンドを実行

$ heroku run rake db:migrate:reset!

エラー画面
エラー画面


● 確認した環境変数 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 を付与して、データベースをdropする

コマンドを実行

$ RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:drop

 実行後
スクリーンショット 2020-10-28 16.09.06.png


● 再生成

コマンドを実行

 データベースの生成
$ heroku run rails db:create

 マイグレーションを実行
$ heroku run rake db:migrate

参考

[Rails5] heroku db:resetからmigrateまで
Rails5のproductionでrake db:dropはできない、普通には

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

Railsを勉強する際の基本の用語

ハッシュ化

ある特定の文字列や数字の羅列を一定のルール(ハッシュ関数)に基づいた計算手順によって
別の値(ハッシュ値)に置き換えること

暗号化との違いは元に戻せるか否か
暗号化は戻せるが、ハッシュ値はハッシュ化作業を行った人も含めて誰も元に戻せない。

メソッド

メソッドとは関数のことメソッドの定義は def と end の間に実行したい処理を挟む

def メソッド名
   やりたい処理
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Tutorial 第4章学習編

Rails Tutorial 第4章で(。´・ω・)ん?って思ったところ

前提知識と復習でアウトプットしているからだと思いますが、
この章は特に(。´・ω・)ん?って思ったところはないです。
動画もテキストも非常に分かりやすかったです。

Rails 第4章 簡単な内容のおさらい

第四章はRails風味のRubyです。こんなに分かりやすいオブジェクト指向は初めてです。
ProgateのPython、TECH::CAMPのRubyでも学習しましたが。。。
(゜-゜)う~ん オブジェクト指向それって美味しいの?って感じでした。

よく説明に・・・全てのモノはオブジェクトです。
人も一つのオブジェクト、車も一つのオブジェクト
ジョンという人がいる。ジョンは人に属している。
人は職業・性別・年齢などの要素を持っている。。。
こんな内容に沿ってコードを書かされて(。´・ω・)ん?って感じになります。

私の言葉でのオブジェクト指向を説明すると・・・

"文字列"があります。
オブジェクト指向で記述されていると、
"12345".length #文字をカウントするメソッドが使える。
"12345".to_i   #文字列を整数に変換するメソッドが使える。

.lengthメソッド・.to_iメソッドを定義していないのに何で使えるの?
 ⇒ 答えはオブジェクト指向で書かれているからです。

まだ、(。´・ω・)?って感じですよね

実は"文字列"と書けば文字列と認識されていますが、
String.new("文字列")というコードの省略形です。
文字列(String)クラスのオブジェクト"文字列"を作りなさいという意味なんです。

予め文字列(String)クラスに様々なメソッドが用意されています。
String.new("")で生成したオブジェクトは文字列オブジェクトなので定義済みメソッドが使えるのです。

文字列クラスで.to_iメソッドが用意されているので、
新しく生成した文字列も.to_iなどの様々なメソッドが使えるという事です。

オブジェクト指向の最大の利点は、
文字列は必ず文字列オブジェクトとして生成する事で.to_iなどメソッドを共有出来きる事です。('ω')
文字列クラスに新しいメソッドを追加すれば、全ての文字列オブジェクトで使用できるって事です。
Progateで学んだ時は(。´・ω・)?でしたが、今になって便利さが分かりました(´-ω-`)

クラスと継承

"文字列"Stringクラスに属しています。
何処に属しているか確認する為に.classメソッドが使えます。

s = "文字列"

s.class
 String

実はStringクラスもObjectクラスに属しています。
>> s.class.superclass             # superclass : 親クラスを調べる
=> Object

Objectクラスも更に上のクラスに属しています。
>> s.class.superclass.superclass 
=> BasicObject

BasicObjectは更に上のクラスに属している? 答えはNoでした。nilは存在しないという意味です。
>> s.class.superclass.superclass.superclass
=> nil

なんでこんな事をしているのかというと・・・機能を引き継ぐ為です。('ω')
Objectクラスの下には、String(文字列)Integer(整数)Array(配列)クラスなどがあります。
配列も、整数も、文字列も、.lengthメソッドが使えますよね。
3つそれぞれ追加していくのは大変ですよね。
なのでObjectに追加して下位クラスで利用出来るようにすれば手間が省けます。

この様に上位のクラスで定義されたメソッドを下位メソッドに引継ぐ事を『継承』と言います('ω')

#class <クラス名>で新しいクラスを定義できます。
>> class Word < String             # < StringでStringクラスを継承出来ます('ω')ノ
>>   # 文字列が回文であればtrueを返す
>>   def palindrome?        # defで新しいメソッドを定義できます。
>>     self == self.reverse        # selfは文字列自身を表します
>>   end
>> end
=> :palindrome?

Ruby 基礎

$ rails console

#整数の足し算
>> 17 + 42
=> 59

#文字列の結合
>> "foo" + "bar"    # 文字列の結合
=> "foobar"

#変数の代入
>> first_name = "Michael"
=> "Michael"

#文字列の式展開 これ便利('ω')ノ Railsでも使うよ。
>> "#{first_name} Hartl"
=> "Michael Hartl"

#式展開('ω')ノ
>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # 苗字と名前の間に空白を入れた結合
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # 式展開を使って結合 (上と全く同じ)
=> "Michael Hartl"

#文字列の出力 (puts)
>> puts "foo"     # 文字列を出力する
foo
=> nil #戻り値はnilは「何にもない」

#文字列の出力 (print)
>> print "foo"    # 文字列の画面出力 (putsと同じだが改行しない)
foo=> nil

>> print "foo\n"  # \nは改行。これでputsと同じ出力になる。
foo
=> nil

#''シングルクォーテーションと""ダブルクォーテーション
>> '#{first_name} #{last_name}'    # ''は式展開できない。
=> "/#{first_name} #{last_name}"

>> "#{first_name} #{last_name}"    # ""は式展開できる。
=> "Michael Hartl"

# empty?メソッド
>> "foobar".empty?
=> false
>> "".empty?
=> true

#条件分岐 (if) ・.include?("文字列") ・.nil? メソッド
>> if s.nil?
>>   "The variable is nil"
>> elsif s.empty?
>>   "The string is empty"
>> elsif s.include?("foo")
>>   "The string includes 'foo'"
>> end
=> "The string includes 'foo'"


# バン!バン! 強制的に論理値(True/False)で出力する
>> !!nil #Rubyオブジェクトはnilだけfalse
=> false

>> !!0  #その他のあらゆるRubyのオブジェクトはtrue
=> true

これで半分程度の内容ですかね。結構ボリューム多いです。
配列やハッシュといった重要な内容がこの後に続くのですが・・・
全部書くと著作権的にどうなのかなと思うのでシンボルについて書いたら終わりッス('ω')ノ

シンボルと整数、文字列について

#これはハッシュというラベル付配列です。
user1 = { "name"=>"test_user", "email" => "test_user@example.com" }
user2 = { "name"=>"test_user", "email" => "test_user@example.com" }

user1 == user2
=> false

同じ内容なのに falseになるのはなんで?('ω')ノ 
実はobject_idというモノが違っているから、異なっているのを確かめてみてください。
user1.object_id 
user2.object_id

このobject_idってデータの格納されている場所みたいなものです。
文字列でも試してください。実行する度にobject_idが代わるんです。
"name".object_id
"name".object_id
"name".object_id

次に数字です。数字はどうでしょうか? 数字は常に同じobject_idになるんです。
1.object_id
1.object_id
1.object_id

最後に・・・シンボルです。 シンボルも数字と同じくobject_idが変化しないんです。
:name #:+文字列をシンボルって言うのですが、object_idにラベル付けたモノって思ってください。

なんでこんな事をするかというと、文字列を使うよりも処理速度が速くなるからです。('ω')
なのでハッシュのラベル名は文字列よりもシンボルの方が好まれます。

◎ user = { :name=> "test_user" , :email => "test_user@example.com" }
△ user = { "name"=>"test_user", "email" => "test_user@example.com" }

更にもっと簡単に記述もできます。
user = { :name=> "test_user" , :email => "test_user@example.com" }
user = { name: "test_user", email: "test_user@example.com" }

カスタムヘルパー

自分でメソッドを定義する場合にヘルパーというファイルに記述します('ω')ノ

app/helpers/application_helper.rb
module ApplicationHelper

  # ページごとの完全なタイトルを返します。
  def full_title(page_title = '')
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
end

感想

第四章は非常に内容が濃くて面白いです。
自分で様々なメソッドを定義する為にはRubyの知識が必要になってきますね。
いろんなメソッドが存在するけど、ほんの僅かしか覚えていないので
Railsの学習が終わったら、Rubyもきちんと学習しないと駄目ですね・・・。
はぁー、先が長い長い( ^ω^)・・・。

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

Railsで簡単な通知機能(フォローされた時のみ)

Railsでの簡単な通知機能です。(とりあえずフォローされた時のみ)

ルーティングの設定

config/routes.rb
  resources :notifications, only: [:index]

コントローラの作成

$ rails g controller notifications

モデルの作成

$ rails g model Notification visitor_id:integer visited_id:integer
$ rails db:migrate

モデルとアソシエーションの設定

app/models/user.rb
  has_many :active_notifications, class_name: "Notification", foreign_key: "visitor_id", dependent: :destroy
  has_many :passive_notifications, class_name: "Notification", foreign_key: "visited_id", dependent: :destroy

  def create_notification_follow!(current_user)
    temp = Notification.where(["visitor_id = ? and visited_id = ? ",current_user.id, id])
    if temp.blank?
      notification = current_user.active_notifications.new(visited_id: id)
      notification.save if notification.valid?
    end
  end
app/models/notification.rb
  belongs_to :visitor, class_name: 'User', foreign_key: 'visitor_id'
  belongs_to :visited, class_name: 'User', foreign_key: 'visited_id'

コントローラの設定

app/controllers/relationships_controller.rb
  def create
    @user = User.find(params[:user_id])
    current_user.follow(params[:user_id])
    @user.create_notification_follow!(current_user) #追記部分
    redirect_to request.referer
  end
app/controllers/notifications_controller.rb
  def index
    @notifications = current_user.passive_notifications.order(created_at: :DESC)
  end

ビュー(通知一覧)の設定

app/views/notifications/index.html
<h1>おしらせ</h1>
<% if @notifications.exists? %>
  <% @notifications.each do |notification| %>
    <table>
      <tr>
        <td>
          <%= attachment_image_tag notification.visitor, :image, size: "50x50" %>
        </td>
        <td>
          <%= link_to user_path(notification.visitor) do %>
            <%= notification.visitor.name %>
          <% end %>
        </td>
        <td>
          からフォローされました。
        </td>
        <td>
          <%= " (#{time_ago_in_words(notification.created_at)}まえ)" %>
        </td>
      </tr>
    </table>
  <% end %>
<% else %>
  <p>おしらせはありません</p>
<% end %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カラムのBoolean型とは

Booleanとは、一般的に真と偽のような2つの状態を表すデータ型のこと


Railsではbooleanにデフォルト値を渡してあげることによって、
true,false,nilという3つの情報を必ず2つに収束するようにできる
nilというものが存在しないようにチェックすることができる。

なので必ずデフォルト値を入れてあげるとよい。

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

Rails Tutorial 拡張機能の返信機能を作ってみた(その3):仕様の勘違いを修正

Rails Tutorialの第14章にある、返信機能を作る件の続きです。

postされたらmodelのin_reply_toにIDを入れる機能

@replyが書いてあるmicropostがpostされたら、modelのin_reply_toにIDを入れる機能を
作ります。

変更するのはpostを受け取って、modelをsaveするところです。どこなのか探します。

文字列の操作をするメソッドをネットで検索します。
https://www.sejuku.net/blog/11843
start_with?(指定の文字列で始まっているか調べる)
http://rubytips86.hatenablog.com/entry/2014/03/28/132953
を読みます。

仕様を勘違いしていることが判明

機能を深堀りしている途中で、contentが@replyで始まるのだと思っていたのが実は勘違いであることが分かりました。
@reply exampleuser hogehoge...」
だと思っていたのですが、正しくは
@exampleuser hogehoge...」
でした。

そうだとして、nameにブランクが含まれていると問題があると分かります。
例えば、nameが「Ola Lakin I 」は「@Ola Lakin I 」とpostしてもnameは「@Ola」しか探せないことに思い当たりました

対応方法としては、ユニークなidを追加する方法が考えられます。実はテキストで同じことを言っているのですが、この時点でやっとピンときました。

@以下がユニークになる方法はテキストでも具体的な方法が書いてあることから、後で考えることにします。
当面は@nameがブランクを含まずユニークになっている前提で進めることにします。この前提で機能を作った後、ユニークにする変更を加えることにします。

文字列から@で始まる単語を取り出す

ネットで調べた方法をコンソールで試します。

>> s = "@Example hoge hoge aa"
=> "@Example hoge hoge aa"
>> b = s.scan(/@\w+/)
=> ["@Example"]

文字列に@xxが含まれないときどうなるかを調べます。

>> s2 = "Example hoge hoge aa"
=> "Example hoge hoge aa"
>> b2 = s2.scan(/@\w+/)                                                                                                                            
=> []
>> b2[0]
=> nil

nilになりました。

次に先頭の@を削除する方法を試します。

>> b = s.scan(/@\w+/)
=> ["@Example"]
>> b[0].slice!(0)
=> "@"
>> b[0]
=> "Example"

これで@xxxの単語を取り出せました。

controllerの変更

画面は変更する必要がないと考え、controllerのどこを変更するかテキストを調べます。
controllers/microposts_controller.rbでcreateをしています。

controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)
        @micropost.in_reply_to = reply_id[0].slice![0]
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end

変更点のテストをどこで書くか

controllerを変更するので、先にtestを書きます。
テスト項目は、replyのmicropostをcreateすると、受信者の件数が1増えることです。
テストをどこで書くかで、integration testとのすみわけを考えます。

controllerのテストの例について、tutorialを読み返します。
第13章、第14章のmicropostでは、modelのテストの次はintegrationテストをしていました。
13.3.1でcontrollerのテストの内容は、ログインしているかどうかのテストでした。

実際にtestのソースを見てみます。controllersフォルダの下です。いずれもログインしているかどうかや正しいページを表示しているかのテストでした。処理した結果を検証するのはintegrationテストで良いと分かりました。

integrationテストを書いてみます。

test/integration/reply_test.rb  RED
  test "reply to user " do
    log_in_as(@user)
    content = "@#{@other.name} reply test content"
    post microposts_path, params: { micropost: {content: content }}
    log_in_as(@other)
    get root_path
    assert_not @other.following?(@user)
    assert_match content, response.body
  end

fixtureのデータでエラー

テストを実行したところ、fixtureのデータでエラーが起きました。

ERROR["test_reply_to_user_", ReplyTest, 0.3831760289999693]
 test_reply_to_user_#ReplyTest (0.38s)
NoMethodError:         NoMethodError: undefined method `id' for nil:NilClass
            test/fixtures/microposts.yml:54:in `get_binding'

not foundのようです。
ネットで調べると、fixtureを呼び出す順番に依るためで、外部制約があると
面倒なことになるようです。

fixtureでテストデータを作るのではなく、modelのtest内でpostするようにしてみます。

ついでに前にfixtureを作ったときの@replyの仕様の勘違いを修正することにします。

test/models/user_test.rb
  test "feed should have the reply posts" do
    michael = users(:michael)
    malory = users(:malory)
    assert_not malory.following?(michael)
    reply_post = michael.microposts.create!(content: "@malory reply test", 
                                            in_reply_to: malory.id)
    assert michael.feed.include?(reply_post)
    assert malory.feed.include?(reply_post)
  end

これでテストは通るようになりました。

controllerを変更

controllerを変更します。

controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)[0]
        reply_id.slice!(0)
        @micropost.in_reply_to = User.find_by(name: reply_id)
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end

テストデータを修正

テストデータを直します。姓と名の間にスペースがないuserを作ります。

test/fixtures/users.yml
Bob:
  name: Bob
  email: bob@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

間違いの修正

テストがRedのままなので、putsでデバッグしたところ、間違いが分かり修正しました。idが必要でした。

修正前 @micropost.in_reply_to = User.find_by(name: reply_id)
修正後 @micropost.in_reply_to = User.find_by(name: reply_id).id

controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)[0]
        reply_id.slice!(0)
        @micropost.in_reply_to = User.find_by(name: reply_id).id
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end

これでテストがGREENになりました。

画面でテストしてみます。
スペースがないユーザーを作り、動くことを確認できました。

所要時間

10/11から10/17までの2.5時間です。

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

to_sメソッドについて。

to_sメソッドの説明

数値が持つメソッドです。数値を文字列に変換してくれます。
ちなみに、 to_sのsの意味は「string(文字列)」です。

以下の例を見てください。

【例】irb
# 数値20にto_sメソッドをつけて実行
irb(main):001:0> 20.to_s
=> "20"

to_sメソッドによって、文字列"20"が実行結果になっています。

それでは、文字列と数値を連結させて、
"i am 20 years old."を表示しましょう。

irbで以下のコードを実行しましょう

では、実際にターミナルでto_sメソッドを試してみましょう。

ターミナル
# to_sで数値を文字列に変換し連結
irb(main):001:0> "i am " + 20.to_s + " years old."

# 続けてこのように表示されれば成功
=> i am 20 years old.

「はじめから文字列"20"を用意すればよかったのでは?」と思うかも
しれません。しかし、足し算した結果を文字列として利用したい場合など、
数値を文字列に変換するto_sメソッドを利用する場面は多くあります。
この方法は必ず覚えておきましょう。

反対に文字列を数値に変換するメソッドもあります。それがto_iメソッドです。これは文字列がもつメソッドです。
to_iメソッドについては、次回説明させていただきます。

まとめ

to_sメソッドとは、数値を文字列に変換するメソッド。

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

RSpec Setup

RSpecのセットアップ

参考文献
Everyday Rails - RSpecによるRailsテスト入門
https://leanpub.com/everydayrailsrspec-jp

環境 Mac
Ruby 2.4.9
Rails 5.1.1

Rspec導入における忘備録である。

RSpecのインストール

GemFileを開き、テスト環境と開発環境で使用可能なように
下記のグループにRSpecを追記する。

GemFile.
group :development, :test do
  #追加 バージョン指定は各環境に応じて変更してください。
  gem 'rspec-rails', '~> 3.6.0'
end

下記のコマンドを入力

ターミナル.
bin/rails g rspec:install

すると下記のフォルダが作成される。

ターミナル.
Running via Spring preloader in process 53239
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

これで準備完了。

お次に、テスト時にターミナルに流れるRSpecの出力を読みやすいドキュメント形式に変更
.rspecに下記を記載する。

#デフォルトで記載されている。
--require spec_helper
#こっちを追記
--format documentation

rspec binstubの導入

binstubとはなんぞや?
アプリケーションの起動を早くすることが可能で、bundle exec ~からbin/~と呼び出しも楽になる便利屋さん。

参考URL
https://qiita.com/tanaka51/items/bc22c1c364202d3cce4e

開発環境に追記してbundle

GemFile.
group :development do
  gem 'spring-commands-rspec'
end

続いてターミナルに下記コマンドを打ち込むと、
Railsアプリケーションディレクトリ直下にあるbinディレクトリ内にRSpecが新たに加わる。

ターミナル.
bundle exec spring binstub rspec

ここまで完了したら

ターミナル.
bin/rspec

でテストが走るか確認する。
rspecディレクトリ内のテストを一括で回してくれるので、最高に楽である。
そして、2回目の実行がめちゃくちゃ早い。

VSCode民のためのテストプラグイン

Rails Run Specs

これはテストコードを開いてcmd + shift + t でその画面のテストをVSCodeが実行してくれるという便利拡張機能!

かなりオススメ

rails g で一緒にRSpecファイルも作成してもらう

ジェネレーターでアプリケーションにコードを追加する際に一緒にRSpecのテストファイルも作ってもらう。
しかし、使用しないディレクトリ及びテストファイルまで作成されては困るので、同時に、使用しないものを作らない設定も行う。

config/application.rb内に追記していく。

config/application.rb
module Projects
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.1

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.


    #ここから下を追記
    config.generators do |g|
      #ジェネレート時にRSpecのテストファイルも作成
      g.test_framework :rspec,
      #テストデータベースにレコードを作成するファイルの作成をスキップ
      fixtures: false,
      #ビュースペックを作成しないことを指定
      view_specs: false,
      #ヘルパーファイル用のスペックを作成しないことを指定
      helper_specs: false,
      #ルーディング用のスペックファイルの作成を省略
      routing_specs: false
    end
  #ここまで
  end
end

もし手動で追加したいのであれば、
テストしたいファイルのディレクトリ名とファイル名を合わせて作成する
app/helpers/projects_helper.rb をテストするのであれば、spec/helpers/projects_helper_spec.rb を作成する。

最後に、Railsがデフォルトで用意してあるテストディレクトリを削除しておきましょう!

ちなみに、RSpecのファイルのみ作成したい場合は下記のジェネレートコマンドを入力すれば、作成される。
下記の場合は、RSpecディレクトリに、modelディレクトリが作成されて、userのRSpecファイルが作成される。

spec/models/user_spec.rb

ターミナル.
bin/rails g rspec:model user

#結果
      create  spec/models/user_spec.rb

今回はここまでにします。

間違い、誤記、ご助言等ございましたらご教授くださると幸いです。
ありがとうございました。

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