20200707のRubyに関する記事は15件です。

unless をリライトするために

発端

現在見ている他人様のコードに、↓なコードがあるわけだ。

tgt
unless hash.has_key?("1") || hash.has_key?("2") || hash.has_key?("3") || arr1.size == 0 || arr2.size == 0
  ....
end

実際には否定とか混ってたりする。テスト実行するにも、膨大なコードのど真ん中。
他にも複数条件の unless 文がけっこう、、、
俺のスカスカな脳味噌では、挙動が想像出来ない。お手上げ。

ググってみてもド・モルガンの説明なんて、精々 2つの条件で説明してる程度

仕方ないので、確認用の雛形スクリプトを作る。

コンセプト

まずは、2つの条件で。
条件式の代りに 0, 1 の値を呼ぶ。

2.pl
my @d = ( [0,0],[1,0],[0,1],[1,1] ) ;
for my $r ( @d ){
    print join "", @{$r} unless $r->[0] || $r->[1] ;
}
$ perl -l 2.pl
00

unless A || B の成立する条件は、一つ目の条件式が偽(0)、二つ目の条件式も偽(0)だと分る。つまり if ! A and ! B

本題

じゃ、tgt の場合は?

高々5ヶなのでごり押しのコード

$ ruby -e 'p [0,1].product([0,1],[0,1],[0,1],[0,1])'
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 1], [0, 1, 0, 0, 0], [0, 1, 0, 0, 1],
 [0, 1, 1, 0, 0], [0, 1, 1, 0, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 1], [1, 0, 1, 0, 0], [1, 0, 1, 0, 1], 
[1, 1, 0, 0, 0], [1, 1, 0, 0, 1], [1, 1, 1, 0, 0], [1, 1, 1, 0, 1]]

これをソースコードに埋め込んで

5.pl
#!/usr/bin/env perl
my @d = (
    [0, 0, 0, 0, 0], [0, 0, 0, 0, 1],
    [0, 0, 0, 1, 0], [0, 0, 0, 1, 1],
    [0, 0, 1, 0, 0], [0, 0, 1, 0, 1],
    [0, 0, 1, 1, 0], [0, 0, 1, 1, 1],
    [0, 1, 0, 0, 0], [0, 1, 0, 0, 1],
    [0, 1, 0, 1, 0], [0, 1, 0, 1, 1],
    [0, 1, 1, 0, 0], [0, 1, 1, 0, 1],
    [0, 1, 1, 1, 0], [0, 1, 1, 1, 1],
    [1, 0, 0, 0, 0], [1, 0, 0, 0, 1],
    [1, 0, 0, 1, 0], [1, 0, 0, 1, 1],
    [1, 0, 1, 0, 0], [1, 0, 1, 0, 1],
    [1, 0, 1, 1, 0], [1, 0, 1, 1, 1],
    [1, 1, 0, 0, 0], [1, 1, 0, 0, 1],
    [1, 1, 0, 1, 0], [1, 1, 0, 1, 1],
    [1, 1, 1, 0, 0], [1, 1, 1, 0, 1],
    [1, 1, 1, 1, 0], [1, 1, 1, 1, 1]
) ;

for my $r ( @d ){
    print join "", @{$r} unless $r->[0] || $r->[1] || $r->[2] || $r->[3] || $r->[4] ;
}

実行する。

$ perl -l 5.pl
00000

改良

ついでに数も変えられる様に、@d 生成を perl で記載1

5.2.pl
#!/usr/bin/env perl
sub comb{
    my $in = shift ;
    # 0 から、2進数の組合せ -1 までの十進数を、0x回数埋めの 2進数に変換した後、
    # 1文字ずつ分割。配列リファレンスにする。で、戻り値は、Array of Arrays
    map{ [ unpack '(A)*', sprintf "%0${in}b", $_ ] } 0 .. 2 ** $in - 1 ;
}

my @d = comb $ARGV[0] ;
for my $r ( @d ){
    # $ARGV[0] - 1 回も || を書くのは面倒臭いので、
    # || で連結した文字列を、eval してそれを unless の引数にしてる。
    # 実際には、検証対象に合せたコードに変更
    print join "", @{$r} unless eval join '||', map{ $r->[$_] } 0 .. $ARGV[0] - 1 ;
}
$ perl -l 5.2.pl 12
000000000000

あとは、||&& にするとか、! を付けるとか、テストしたい条件文に合せて、その場その場で対応する。

ああ、これで、ストレス少なく、unlessif に変えられる。

蛇足

2進数を利用しない時。

sub comb {
    my $in = shift ;
    # ([0,0,0..])な配列を作る
    my @d = ( [(0)x$in] ) ;
    # キャッシュ用ハッシュ
    my %seen ;
    # @d を舐めるのだが、@d はループ内で push されて数が増えるので
    # 抜ける条件は別に付ける
    for my $r ( @d ){
        # $r = [ x,x,x,x,x ... ]の要素を順番に 1 に変えて、
        # 未知の組合せだったら、@d に追加
        push @d, grep{ ! $seen{ join "", @{$_} } ++ }
                 map { my @k = @{$r} ;  $k[$_] = 1 ; [@k] }
                 0 .. $in - 1  ;
        # [全部 1 ] が最後の組合せ
        last if +(join '', @{$d[-1]}) eq '1' x $in ;
    }
    return @d ;
}

お作法本的にはタブーなコード2
こちらだと、二桁にすると厳しい。


  1. perl にも product あればいいのに。CPAN? 面倒臭いから却下。 

  2. 最初は此方を書いてて、途中で 2進数で書けばいい事に思い当った。久しぶりのコードなので残す。 

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

Rubyでgmailからメールを送信する

はじめに

参加している勉強会でプログラムを書く練習課題として、rubyでメールを送るというお題があり、実施した内容を以下に記載します。

ちなみに自分が普段使っているgmailから送るプログラムになります。

制限時間がある中での課題だったので、焦りながら実施しましたが、思った以上に参考文献、ライブラリが揃っており、助けられました。

実施環境、必要なもの

  • OS :mac
  • ruby 2.6.6
  • Googleアカウント

実施手順

Googleアプリパスワードの取得

アプリからメール送信する際の認証には、googleアカウントのパスワードをそのまま使うのではなく、googleのアカウント設定で発行するアプリパスワードを使って認証を行います。
(パスワードをそのまま使うより、セキュリティ的におすすめな方法)

Account settings

上記画面から、「セキュリティ」→「アプリパスワード」を選択後、アプリ:メール、デバイス(自分の場合はmac)を選択後に、生成ボタンを押すと、アプリパスワードが表示されます。

※なお、前提としてgoogleアカウントの二段階認証が有効になっている必要があります。

ruby-gmail gemのインストール

大変ありがたいことに、rubyにはgmailを送信するためのgemがあるので、こちらを利用します。

dcparker/ruby-gmail

gem install ruby-gmail

ruby-gmail gemには単にメール送信の他に、受信メールを読んだり、編集したりといろいろ機能があって面白そう。

環境変数の設定(gmailアドレス、アプリパスワード)

セキュリティ観点からgmailアドレスと先に取得したアプリパスワードは、プログラムにベタ書きではなく、環境変数に設定し、プログラムで取得することにします。

export GMAIL_ID="送信したいgmailアドレス"
export GMAIL_PASS="gmailのアプリパスワード"

設定した環境変数を反映させます。

source ~/.bash_profile

メール送信プログラムの作成

今回は、コマンド上で、任意の送信先アドレス、件名、本文を入力できるようにしています。

require "gmail"

USERNAME = ENV[ "GMAIL_ID" ]
PASSWORD = ENV[ "GMAIL_PASS"]

gmail = Gmail.new(USERNAME, PASSWORD)

puts "送信先のアドレスを入力してください"
address_to = gets.chomp
puts "件名を入力してください"
title = gets.chomp
puts "本文を入力してください"
text = gets

message =
  gmail.generate_message do
    to address_to
    subject title
    html_part do
      content_type "text/html; charset=UTF-8"
      body "<p>" + text + "</p>" 
    end
  end

gmail.deliver(message)
gmail.logout

参考記事

プログラムの大部分はこちらを参考にさせていただきました。大変感謝です。

【Ruby】Gmailアカウントでメール送信 - TakBoy's Programing Note

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

【Ruby】繰り返しの制御

今回は繰り返しの制御について書いていきます。

繰り返しの途中で処理を中断したり、次にスキップしたい場合がありますが、
繰り返しの制御の命令を使うことで実現できます。

break

breakは繰り返しを中断し、繰り返し処理の中から抜けます。

# サンプルプログラム
num = 0
loop do
  num += 1
  if num > 10
    break
  end
end

puts num

前回の記事で書いたものとほぼ同じですが、if式をシンプルな形にしています。
numが10より大きい、つまり11となった時点でloopメソッドから抜け出し、11が出力されます。

next

nextは次の繰り返し処理に移ります。

# サンプルプログラム
for num in 1..10
  if num.odd?
    next
  end
  puts num
end

odd?メソッドは値が奇数の場合にtrueを返します。
よって、変数numの値が奇数の場合はnextを通るので、変数numの値の出力はせずに次の繰り返し処理に移ります。
結果として2,4,6,8,10が出力されます。

redo

redoはもう一度同じ繰り返し処理をします。

# サンプルプログラム
for num in 1..10
  if num.odd?
    redo
  end
  puts num
end

nextで記述したサンプルプログラムのnextをredoに置き換えたものですが、
この場合、numの値が1のまま無限ループとなります。

redoがどういう場合に使われるか現時点では想像がつかないのですが、あまり使われないようです。

今回の繰り返しの制御については以上となります。

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

ParsingとStoringって何

Parsingって何

Parsing = Read + Convert
Libraryにあるテキストを読んでRubyで扱えるようにオブジェクトに変えること

Storingって何

データファイルに情報を送ること
Storing is to put information into a data file.
パソコンが壊れてもアクセスできる!(クラウド的な?)
This way you can access your data even when someone throws your laptop from a window?.

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

【Rails】複数のDB処理をまとめる transaction を実装してみた

はじめに

Rails でアプリを開発していて、複数テーブルのデータを同時に作成する、という処理を実装していた。
その処理において、どちらかの処理が失敗した場合に双方の処理をなかったことにする transaction という概念があることを知ったので備忘録兼アウトプットとして、 transaction についておよび、実装した内容について書く。

transaction とは

「transaction」という言葉の定義を調べると
【処理、取り扱い、処置、業務、取引、売買、会報、紀要、議事録】 このように出てくる。

ITやプログラミングにおいては
「複数の処理を一つにまとめた分割不可能なもの」

と定義づけることが出来そうです。

こちらの記事がトランザクションの概念について非常にわかりやすいくまとめられており、参考にさせていただきました。

「トランザクション」とは何か?を超わかりやすく語ってみた!

transaction メソッド使い方

  • 複数の処理を一つのまとまった処理として扱う
  • 一つでも処理に失敗した場合は、トランザクション全体として失敗となる
  • 失敗した場合は、全ての処理がなかったこととなる

基本的な構文としてはこうなる

モデル.transaction do
  # テーブルへのアクセス処理
  # テーブルへのアクセス処理
end
  # トランザクション処理が成功した場合の処理
rescue => e
  # トランザクション処理が失敗した場合の処理

注意点としては「処理失敗時には例外を発生させるメソッドを使用する」こと。

トランザクションは、含まれる処理のいずれか1つでも失敗すると、トランザクション内のすべての処理をなかったことにするが、そのなかったことにする条件が「例外の発生」となる。

では実際に実装してみる。

実装

今回開発していたアプリにはユーザーのグループ化機能があった。
そこで、グループを新規作成するときにグループ作成をしたユーザーは自動的にグループに所属するユーザーとなる、という仕様を想定。
そこでグループ作成をした際に、思わぬ動作が起きてしまうことを避けるためにトランザクションの利用を検討。

テーブル構造はこのようになっている。
スクリーンショット 2020-07-07 19.57.10.png

groupsテーブルインスタンスを作成すると同時に、ログインユーザーのidをuser_idカラムとする group_usersテーブル(usersテーブルとgroupsテーブルの中間テーブル)が作成されるようにしたかった。

  1. groupsテーブルインスタンスの作成
  2. group_usersテーブルインスタンスの作成

この 1, 2 のうちどちらかの処理が何らかの理由により失敗した場合、双方の処理がなかったこととなるように groups コントローラの create アクションで transaction を活用する。

groups_controller.rb
# createアクション部分を抜粋
# 変数current_userにはログインユーザーのインスタンスが格納

  def create
    @group = Group.new(group_params)

    # トランザクションを適用(グループの作成と中間テーブルを同時作成)
    # save! と create! と「!」がついている点に注意!
    @group.transaction do
      @group.save!
      current_user.group_users.create!(group_id: @group.id, permission: true)
    end
    # トランザクション成功時の処理
      flash[:success] = '新しいグループを作成しました'
      redirect_to @group
    rescue => e
    # トランザクション失敗時の処理
      flash.now[:danger] = 'グループ作成に失敗しました'
      render :new
  end

この transaction 内での処理には両方とも「!」がついており、テーブルデータ作成失敗時には例外が発生するメソッドを使っていることに注意!

仮に transaction を利用せずに以下ように実装した場合は、何らかの理由で group_usersテーブルの作成が失敗した場合に無人のグループが作成されてしまう懸念がある。

groups_controller.rb
# transactionを利用しない場合

def create
  @group = Group.new(group_params)

  if @group.save
    current_user.group_users.create(group_id: @group.id, permission: true)
    flash[:success] = '新しいグループを作成しました'
    redirect_to @group
  else
    flash.now[:danger] = 'グループ作成に失敗しました'
    render :new: 
  end
end

最後に

記事を読んでいただきありがとうございます!
今回調べながら何とか実装してみたのですが、正直トランザクションについての理解はそこまで深くないです。誤りやより良い記述方法などがあれば、気軽にコメント等いただければ幸いです。
下記の記事、参考にさせていただきました。ありがとうございました。

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

railsでrspecを使用中にハマったこと

初心者がrailsでrspecを用いてテストを記述する際にハマったことを残しておきます。

rspecでcontrollerのテストを試みたところエラーが頻発しました。どこを直してもうまくいかなかったので、一度参考サイト通りに記述し、何が問題かを調査することにしました。しかし、それでもうまくいかない。。。
そこで設定を見直したところ、controllerのテストはrails5から非推奨になっており、以下のように変更する必要がありました。

model       → model(そのまま)
controller     → request
画面遷移のテスト → system

のようにフォルダを作成する必要があり、rails_helperの設定も上記にならい記述する必要があった。
参考記事はcontrollerとrequestが混在しているため注意する必要がある。

また、最近のrailsではコントローラーのテストは非推奨になり、
create,destroy,edit,updateのテストが記述してあれば十分になったらしい。

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

railsで開発中にAWSから高額請求されたが、、

はじめに以下の記事にあるように、railsでアプリを開発中に初期設定のミスが原因で高額請求に至った。
https://qiita.com/381704/items/d5216295eadd67eb9e8c

完全に注意不足であり、しっかり利用してしまったあとなので支払うしかないと思っていたが、駄目元でAWSのサポートセンターに以下のようなメールを送ってみた。(本文を載せるのは抵抗があったが、証拠&同じ状況の人の希望になればと思い残しておく)
スクリーンショット 2020-07-07 16.17.45.png

2日後にAWSサポートより返信が来た。返信内容には、現在料金が発生しているRDSのインスタンスとスナップショットの削除をすることとあり、その後に返金が可能かの相談にはいるということだ。
内容通り、RDSの削除をして再度AWSに削除完了のメールを送信した。
その2日後AWSサポートより返答があり、今回に限り返金措置を設けてくださるとのこと!!!

請求料金は合計600$以上になったが、AWS様より返金をしてくださり、最終的な請求料金は100$ほどになった。落ち度は完全に私のほうにあるにも関わらず、今回の対応をしてくださったことは本当に感謝してもしきれないほどだ。

離職中だったので金銭的に苦しい状況だったため本当に助かりました。ありがとうございました。
反省として請求アラームを2個付け、UdemyでAWSに関する教材を購入し基礎からAWSの利用法を学び直しました。
これからもサービスを利用させていただきます。

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

Rails5でECサイトを作る④ ~ヘッダーとフッター~

はじめに

架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る③の続きです。
今回は、ヘッダーとフッターを作ります。せっかくなのでレスポンシブ対応にしてみました。

ソースコード

https://github.com/Sn16799/bakeryFUMIZUKI

配色

いきなり余談ですが、サイト全体のテーマカラーを決めました。店名(架空)が文月なので、夏らしい青系統の色でまとめます。
カラーコードも一応載せておきます。ご参考までに。

濃青……#120136、rgb(18,1,54)
中青……#035aa6、rgb(3,90,166)
淡青……#40bad5、grb(64,186,213)
ワンポイント黄……#fcbf1e、rgb(252,191,30)

ヘッダー

app/views/layouts内に、ヘッダー用の部分テンプレートを作ります。ファイル名は、_header.html.erbとしました。

管理者でログインした時顧客がログインした時ログインしていない時の3パターンで表示が変わるように条件分岐します。
顧客ログイン時に「ようこそ、〇〇さん!」とメッセージを表示したいのですが、データベース上は苗字と名前を別々で保存しているため、viewのコードが煩雑になってしまいます。そのため、Modelのファイルにちょい足しして、苗字と名前を連結した文字列を出力できるようにします。

app/model/customer.rb
def full_name
  self.family_name + " " + self.first_name
end

これでviewやcontrollerでfull_nameと入力すれば、フルネームで表示できるようになりました!

app/views/layouts/_header.html.erb
<header class="container-fluid middle-blue-back">
  <nav class="navbar navbar-expand-lg">
    <!-- ロゴ画像。あとで差し替え。 -->
    <div>
      <%= link_to customer_top_path, style: 'color: #fcbf1e' do %>
      <h1>LOGO</h1>
      <% end %>
    </div>

    <!-- 画面幅が狭くなった時のみ出現するトグルボタン -->
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarFumizuki" aria-controls="navbarFumizuki" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
    </button>

    <!-- 各種リンク。管理者ログイン時と顧客ログイン時、非ログイン時で表示切替。 -->
    <div class="collapse navbar-collapse" id="navbarFumizuki">
      <!-- 管理者 -->
      <ul class="navbar-nav mr-auto w-75 nav-justified">
        <% if admin_signed_in? %>
          <div class="admin-message">管理者としてログインしています</div>
          <li class="nav-item"><%= link_to '商品一覧', admins_products_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to '会員一覧', admins_customers_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to '注文履歴一覧', admins_orders_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'ジャンル管理', admins_genres_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'ログアウト', destroy_admin_session_path, method: :delete, class: 'nav-link' %></li>
        <!-- 顧客 -->
        <% elsif customer_signed_in? %>
          <li class="nav-item"><%= link_to 'About', customer_about_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'マイページ', customer_path(current_customer.id), class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to '商品一覧', products_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'カート', cart_items_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'ログアウト', destroy_customer_session_path, method: :delete, class: 'nav-link' %></li>
        <!-- 非ログイン -->
        <% else %>
          <li class="nav-item"><%= link_to 'About', customer_about_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to '商品一覧', products_path, class: 'nav-link' %></li>
          <li class="nav-item" ><%= link_to '新規登録', new_customer_registration_path, class: 'nav-link' %></li>
          <li class="nav-item"><%= link_to 'ログイン', new_customer_session_path, class: 'nav-link' %></li>
        <% end %>
      </ul>
      <!-- メッセージ(customer) or 検索窓(admin) -->
      <% if customer_signed_in? %>
        <p>ようこそ<%= current_customer.full_name %>さん!</p>
      <% else admin_signed_in? %>
        <!-- 検索フォームは仮。あとで機能するものを作って差し替え。 -->
        <form class="form-inline my-2 my-lg-0">
        <input type="search" class="form-control mr-sm-2" placeholder="検索..." aria-label="検索...">
        <button type="submit" class="btn btn-outline-success my-2 my-sm-0">検索 
        </button>
        </form>
      <% end %>
    </div>
  </nav>
</header>

フッター

特に機能をつけなかったのであってもなくても良いのですが、ないと寂しいので作ります。

app/views/layouts/_footer.html.erb
<div class="container-fluid middle-blue-back">
  <h3 class="footer-message">Bakery FUMIZUKI</h3>
</div>

青色の背景と、中心に黄色で「Bakery FUMIZUKI」の文字というシンプルな構成です。

おおまかな枠組みを作る

application.html.erbを編集して、サイト全体のレイアウトを調整します。
先ほど作ったヘッダーとフッターもここで読み込みます。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Fumizuki</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

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

    <!-- レスポンシブ -->
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- swiper -->
    <link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css">
    <script src="https://unpkg.com/swiper/js/swiper.min.js"></script>

    <!-- font awsome -->
    <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
  </head>

  <body>
    <%= render 'layouts/header' %>
    <div class="container-fluid">
      <div class="row">
        <%= yield %>
      </div>
    </div>
    <%= render 'layouts/footer' %>
  </body>
</html>

真ん中あたりにあるswiperというのは、写真をスライド表示するためのライブラリです。今回の記事では使いませんが、便利なのでいずれお世話になるだろうと思って入れました。
その下のfont-awsomeは、リンクをコピーするだけで数多くのアイコンを無料で使える便利なサイトです。こちらもそのうち使うと思います。

scssで装飾

HTML側で記述したクラス名に、各種の色を設定していきます。

app/assets/stylesheets/application.scss
@import 'bootstrap';

.middle-blue-back {
  background-color: #035aa6
}

.light_blue_letter {
 color: #40bad5
}

.footer-message {
  line-height: 100px;
  text-align: center;
  color: #fcbf1e
}

// header
.navbar-toggler {
  border-color: #40bad5;
}
.navbar-toggler .navbar-toggler-icon {
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgb(64,186,213)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}

.nav-link {
  color: #40bad5;
}

トップページも細工する

テーマカラーに決めた色見本がブラウザでどのように見えるのか確認したかったので、トップページで試してみました。後で正式なトップページを書くときに消します。

app/views/homes/top.html.erb
<div class="col">
  <h1>Homes#top</h1>
  <div class="row">
    <div class="col-3" style="height: 100px; background-color: #120136"></div>
    <div class="col-3" style="height: 100px; background-color: #035aa6"></div>
    <div class="col-3" style="height: 100px; background-color: #40bad5"></div>
    <div class="col-3" style="height: 100px; background-color: #fcbf1e"></div>
  </div>
</div>

localhost:3000にアクセスしてみます。
fumizuki_top.jpg

ヘッダー、フッターとも問題なく表示されています。

toggle.jpg

開発者ツールで画面幅を狭くすると、きちんとハンバーガーアイコンになりました!

後記

Bootstrapを使うとレスポンシブ対応も楽々できて、本当に便利な機能だなと思います。CSSも多少書いていますが、ほぼ色の指定をしているだけです。そのカラーコードも4色だけなので、アプリ全部を完成させても、CSSのコード量はほとんど増えないのではないでしょうか。

あと、ここまでお読みくださった方は薄々あることを思っておられるかも知れません。私もちょっと気になっています。
サイトのテーマカラー、どこかで見たことあるような。

はい。完全にI●EAですね。
不覚にもサイトで表示するまで気付かなかったため、コードを書いてからビックリする事態となってしまいました。私はIK●A好きだから良いですけど、向こうは別に私のこと好きでも何でもないと思うので、このまま実装を続けたものか迷います。
一旦IKE●に見えてしまったら、サイトを見る度に「絶対この店、パン売ってないよな」と思ってしまうのも難点です。

果たして私は●KEAから怒られずにサイトを完成させられるのか? 次回へ続く!

参考

Bootstrap4移行ガイド
Bootstrap4日本語リファレンス
とほほのBootstrap 4入門
Bootstrap4のハンバーガーメニューの色を変える方法

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

URLエンコードについて調べてみた

WEB上のPDFファイルが見れない

なぜかアップロードしたPDFファイルがリンク切れになっていたので
その調査内容を記録します。

ユーザ環境

  • MacBook
  • Chrome

アプリケーション環境

  • AWS EC2
  • AWS S3
  • ruby 2.6.5
  • Rails 5.0.7.2
    • refile (0.6.2)
    • refile-s3 (0.2.0)

見れないファイルのパターン

チルダ「~」がファイル名に含まれているファイルはリンク切れになってしまう。

ファイルリンクを作成するコード

li
  = link_to attachment_url(file, :file), target: '_balnk'
    b-icon(icon="attachment")
    span.file__name = file.file_filename

実際に生成されるhtml

<a target="_balnk" href="/attachments/xxxxxxx/store/xxxxxx/2020.3%7E2020.4%E3%83%86%E3%82%B9%E3%83%88.pdf">
  <span class="file__name">2020.3~2020.4テスト.pdf</span>
</a>

日本語もちゃんとURLエンコードされているし、問題なさそう。

URL規約を調べてみた

URLで使用できる文字は規約で定められていて
いくつかの規約がありましたが、以下2つについてしらべてみました。

  • RFC3986
  • RFC1738

RFC1738

https://tools.ietf.org/html/rfc1738

Unsafe:
Characters can be unsafe for a number of reasons. The space
character is unsafe because significant spaces may disappear and
insignificant spaces may be introduced when URLs are transcribed or
typeset or subjected to the treatment of word-processing programs.
The characters "<" and ">" are unsafe because they are used as the
delimiters around URLs in free text; the quote mark (""") is used to
delimit URLs in some systems. The character "#" is unsafe and should
always be encoded because it is used in World Wide Web and in other
systems to delimit a URL from a fragment/anchor identifier that might
follow it. The character "%" is unsafe because it is used for
encodings of other characters. Other characters are unsafe because
gateways and other transport agents are known to sometimes modify
such characters. These characters are "{", "}", "|", "\", "^", "~",
"[", "]", and "`".
All unsafe characters must always be encoded within a URL

チルダは使用するなら必ずエンコードして使わないといけないと明記されていました。

RFC3986

https://tools.ietf.org/html/rfc3986

2.3. Unreserved Characters
Characters that are allowed in a URI but do not have a reserved
purpose are called unreserved. These include uppercase and lowercase
letters, decimal digits, hyphen, period, underscore, and tilde.
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"

チルダの扱いが「非予約文字」になっていてエンコードなしで使えそうです。

Amazon CloudFrontはどの規約に準拠しているのか?

URL規約を調べたところで
WEBアプリケーションの動作しているAWS環境を調べてみました。

ユーザからRequestをうけるAmazonCloudFrontのドキュメントを見てたら
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html

パスに ASCII 以外の文字が含まれるか、RFC 1783 に規定された安全でない文字が含まれる場合、その文字を URL エンコードします

という記述がありました。

なんで見れなかったのか?

上記、調査内容からするとチルダのエンコードで何かしら問題あったということになりますが、、
Rails link_to で作成されたhtmlリンクは正しくエンコードされている。。

<a target="_balnk" href="/attachments/xxxxxxx/store/xxxxxx/2020.3%7E2020.4%E3%83%86%E3%82%B9%E3%83%88.pdf">
  <span class="file__name">2020.3~2020.4テスト.pdf</span>
</a>

ブラウザの問題?

Chrome と Safariで試したところ、SafariからはPDFファイルが見れた!!

Chrome アドレスバーの表示

https://xxxxxx/2020.3~2020.4テスト.pdf

Safari アドレスバーの表示

https://xxxxxx/2020.3%7E2020.4テスト.pdf

エンコードされたリンクを開く際、ブラウザで日本語にデコードしてくれるようだが、
そのデコードによる問題かな。。と推測。

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

[Rails]新しいページを増やす方法

Ruby on Railsのページを増やす手順

忘れないよう、自分用のメモ書きです。

手順
1.configのrouteファイルにURLと対応するアクションを記述
2.controllerに該当するアクションを追記
3.viewフォルダにhtml.erbファイルを新規作成

※まったく0からのサイト作成ではなく
既存のプログラムに追加する想定。

routeファイルにURLとアクションを記述

〜書き方〜

HTTPプロトコル ”ページを表示させたいURL” => ”コントローラ名#アクション名”
のように記述します。

※「HTTPプロトコル」としましたが、ほとんどの場合「get」か「post」のどちらかです。

以下に具体例を示します。

route.rb
get "images/index" => "images#index"

〜/images/indexというURLへリクエストを受けた時に、
このプログラムでは、imagescontrollerの中の、indexアクションを実行しますという書き方になっています。

controllerに該当するアクションを追記

images_controller.rb
def index
end

先ほど、routeファイルに書いたとおり、URLのリクエストを受けた後、
プログラムはimagescontrollerを見に来ます。そして、indexアクションを探して実行しようとします。
今回は、その流れそのものをひとつ増やす作業を行っているので該当するアクションを追記します。

viewフォルダにhtml.rbを新規作成

最終的にブラウザ等で表示されるのはhtml.rbファイルです。
route→controller→html
※.rb省略
の順番で記述やファイルを追加していきましょう。

index.html.rb
<h1>sample</h1>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「bundle install」するとgem系のファイルについて大量にgit差分が発生してしまう

発生した背景

新しいgemを入れるときに、以下のコマンドを実行するとgitに大量に差分が発生することがあります。

コマンド
bundle install --path vender/bundle

(スクショの「Unstaged Files(1)」というところが、10000とかになったりする。ごめんなさい、再現ができなかったです)
image.png

この件について対応方法を検討したいと思います。

環境

項目 内容
OS.Catalina v10.15.4
Ruby v2.5.1
Ruby On Rails v5.2.4.3
Git 2.20.1

対応手順

【1】Gitignoreを編集する。
Gitignoreのフォーマットなるものがあります。
詳しくは参考文献「編集済みの.gitignoreを簡単に得る方法」を。
以下内容を掲載しておきます。

.gitignore
*.rbc
capybara-*.html
.rspec
/log
/tmp
/db/*.sqlite3
/db/*.sqlite3-journal
/public/system
/coverage/
/spec/tmp
**.orig
rerun.txt
pickle-email-*.html

# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb

# Only include if you have production secrets in this file, which is no longer a Rails default
# config/secrets.yml

# dotenv
# TODO Comment out this rule if environment variables can be committed
.env

## Environment normalization:
/.bundle
/vendor/bundle

# these should all be checked in to normalize the environment:
# Gemfile.lock, .ruby-version, .ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

# if using bower-rails ignore default bower_components path bower.json files
/vendor/assets/bower_components
*.bowerrc
bower.json

# Ignore pow environment settings
.powenv

# Ignore Byebug command history file.
.byebug_history

特に「/vendor/bundle」が重要ですね。
gitにあげるとパフォーマンスも落ちますし、よくないことだらけなので私は、上記を書き換えるだけでなく、既存のファイルに追記という形をとりました。

参考文献

編集済みの.gitignoreを簡単に得る方法

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

rails メソッド

railsメソッド

human_attribute_nameで多言語対応できる
const_get
https://docs.ruby-lang.org/ja/latest/method/Module/i/const_get.html

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

Ruby on Rails マイグレーションを取り消す

最新のマイグレーションを取り消す

最新のマイグレーションを取り消すコマンドは
rails db:rollback

特定のマイグレーションを取り消す

特定のマイグレーションを取り消す場合は
rails db:migrate:status
で取り消したいマイグレーションファイルのMigration IDを確認し
rails db:migrate:down VERSION = 取り消したいマイグレーションファイルのMigration ID
を実行する。

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

Ruby on Rails でアプリケーションを制作する。

Ruby on Rails でアプリケーションを新規作成する。

$ rails new アプリケーション名 -オプション名
  • データベースに MySQL を使用する場合は、オプション名に -d mysql と記述する。
    ※ デフォルトは SQLite を使用している。

データベースを作成する。

$ rails db:create

ルーティングを生成する。

config/routes.rb
Rails.application.routes.draw do
  [HTTPメソッド] '[URIパターン]', to: '[コントローラー名]#[アクション名]'

  root to: 'コントローラー名#アクション名'
    # ルートパス (get '/', to: '[コントローラー名]#[アクション名]') にリクエストする。

  resources :コントローラー名, only: [:アクション名]
    # "resources"メソッドを使用すると、指定したコントローラーの 7つのアクション (index,new,create,show,edit,update,destroy) のルーティングが設定される。
    # "only"オプションを使用すると、7つのアクション全てではなく、指定しただけのアクションのルーティングが設定される。
end
  • HTTPメソッドの種類

    • GET:ページを表示する時に使用する。
    • POST:フォームからデータを送信して、登録する時に使用する。
    • PUT:データを変更する時に使用する。
    • DELETE:データを削除する時に使用する。
  • アプリケーションのルーティングを確認する時はターミナルで $ rails routes を実行する。

  • Prefix

    • Prefix名_path と記述することで、リンク先等のパスとして扱える。

コントローラーを作成する。

$ rails g controller コントローラー名
  • ルーティングから送られたリクエストに対応する7つのアクション
    • index
    • new
    • create
    • show
    • edit
    • update
    • destroy

モデルを作成する。

$rails g model モデル名
  1. マイグレーションファイルにテーブルに必要なカラムを記述する。
マイグレーションファイル.rb
class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t. :カラム名, null: false
        # "null: false" は空のデータが登録できないようにする必須項目に付けるバリデーション (モデルファイルにも要記述)
      t.timestamps
    end
  end
end
  • 型の種類
    • integer:数値
    • string:文字列
    • text:文字列(長文)
    • boolean:選択肢
    • datetime:日付・時刻
  1. マイグレーションファイルに必要なカラムを記述したらマイグレーションをする。
$ rails db:migrate
  • もし、マイグレーションした後にマイグレーションファイルの修正をする場合には、$ rails db:rollback を実行して、データベースから差し戻す必要がある。
  • 現状 マイグレーションファイルがデータベースに適用されているか確認するには $ rails db:migrate:status を実行する。

データベースに一度 上げたテーブルの情報を追加・削除・変更するには…

  • $ rails g migration Addカラム名Toテーブル名 カラム名:型 という形で実行する。
    • Addカラム名Toテーブル名 の部分は、単語の頭文字を大文字にして、スペースを開けずに記述する。

ヘルパーメソッド

<%= link_to 'リンクに表示する文字', 'リンク先のURL' %>

ビューファイルにデータベースのテーブルに保存されている値を表示する。

  1. コントローラーでモデルからテーブルの値を取得する。
_controller.rb
class PostsController < ApplicationController
  def アクション名
    @変数名 = " ● ● ● "
      # コントローラーでインスタンス変数を定義しておくと、ビューファイルで "<%= @変数名 %>" 呼び出すことができる。

    @変数名 = モデル名.ActiveRecordクラスメソッド
      # 取得したい値があるモデル名の ActiveRecord クラスメソッドを指定して、インスタンス変数に代入する。すると、ビューファイルで変数名を指定することで値を表示できる。
  end
end
  • ActiveRecord クラスメソッドの種類
    • all:テーブルの全てのデータを取得する。
    • find(●):テーブルの中の特定のレコードを取得する。
    • new:新しくレコードを生成する。
    • save:特定のレコードに保存する。
  1. 取得した値をビューファイルに記述する。
<%= @変数名.カラム名 %>
  <!-- "find(●)" メソッドを指定して、特定のレコードを取得した場合は、代入した変数名に表示したいカラム名を記述する。 -->

<% @コントローラで代入したインスタンス変数名.each do |変数名| %>
  <%= 変数名.カラム名 %>
<% end %>
  <!-- "all" メソッドを指定して、複数のレコードを取得した場合は、Each文を利用してレコード 1つずつ取り出してから表示する。 -->

フォームを作成する。

ビューファイルにフォームを作成する。

<%= form_with model: @コントローラでモデルに新しくレコードを生成する値を代入するインスタンス変数名, url: フォームを送信するコントローラの Createアクションの Prefix_path, local: true do |form| %> 
  <%= form.フォームのヘルパーメソッドの種類 :カラムの名前 %> 
<% end %>
  • " type="" " 属性の値の種類
    • type="text":テキストフォーム
    • type="password":パスワードフォーム
    • type="checkbox":チェックボックス
    • type="radio":ラジオボタン
    • type="submit":送信ボタン

ルーティングに送信ボタンを押した後のリクエスト先を記述する。

config/routes.rb
post '[URIパターン]', to: '[コントローラー名]#create'
  # フォームからデータを送信して 登録する操作をするので、HTTPメソッドに "POST"
  # データの投稿をするリクエストには、"Create" アクション

コントローラでテーブルにレコードを追加する。

_controller.rb
def create
  モデル名.create(ストロングパラメータのメソッド名)
    # モデルにレコードを追加する為の .create メソッド
    # フォームから値を受け取る為にストロングパラメータを使用する。
end

private
def ストロングパラメータのメソッド名
  params.require(:インスタンス変数名).permit(:フォームの "name" 属性の値)
    # フォームから送信されてきた値の中で、指定されたフォームを許可する。
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ユーザー管理機能を実装する。 | Ruby on Rails

  1. Gemfile に $ gem 'devise' を追記して、$ bundle install する。

Devise の設定ファイルを作成し、 User モデルを作成する。

  1. $ rails g devise:install を実行して、Devise の設定ファイルを作成する。
  2. $ rails g devise user を実行して、Devise の機能を持つ User モデルを作成する。
    • この時に、ルーティングに devise_for :users という Devise の機能へリクエストするためのルーティングが生成される。
  3. $ rails db:migrate を実行して、Devise の機能を持つ User モデルをマイグレートする。
  4. ログイン画面等の Devise が持つビューファイルを変更したい場合は、$ rails g devise:views を実行してビューファイルを作成する。

メールアドレスとパスワード以外の値を受け取れるようにする。

  • Devise では、メールアドレスとパスワードしか値を受け取らないようにストロングパラメータが記述されているので、任意でフォームを追加した場合には、その値を受け取れるように記述する必要がある。
_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:追加したフォームの名前])
  end
end

ログインしている時としていない時で表示が変わるようにする。

  • Devise をインストールすると、ユーザーがサインインしているかどうかを判断する user_signed_in? メソッドが使用できる。
<% if user_signed_in? %>
  <!-- ユーザーがサインインしている場合に実行する処理を記述する。 -->
<% else %>
  <!-- ユーザーがサインアウトしている場合に実行する処理を記述する。 -->
<% end %>

送信したフォームの情報にサインインしているユーザーの情報を追加したい場合…

_controller.rb
class PostsController < ApplicationController
  private
  def post_params
    params.require(:post).permit(:title, :content).merge(user_id: current_user.id)
      # form_with を使用して送信される情報を取得するストロングパラメータに .merge メソッドを使用して、current_user (現在ログインしているユーザー) の ID を結合させる。
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む