20201124のRailsに関する記事は20件です。

【Rails】bcryptを使ってログイン機能を作ったけど失敗した話

Railsでログインページを作っていました。
bcyrptのGemを使って作ろうとしたのですが、正しいメールアドレス・パスワードを登録し、ログインしようとしても「ログインに失敗しました」と表示され、なかなかできず…

環境

Rails 5.2.4.4
DB:SQLite

問題のあったコード

・gemfileにてcryptをインストール済み
・userモデルに has_secure_password を定義
・usersテーブルに password_digest:string を追加

users_controller.rb
class UsersController < ApplicationController

  def sign_in_process
    user = User.find_by(email: params[:email])
    if user && user.authenticate(params[:password])
      user_sign_in(user)
      flash[:success] = 'ログインしました'
      redirect_to products_path
    else
      flash[:danger] = 'ログインに失敗しました'
      redirect_to sign_in_path
    end
  end

end

user_sign_in(user)の部分はhelperで、

users_helper.rb
def user_sign_in(user)
  session[:user_id] = user.id
end

と書いています。

解決方法

paramsの後に[:user]を追加しました

users_controller.rb
class UsersController < ApplicationController

  def sign_in_process
    user = User.find_by(email: params[:user][:email])
    if user && user.authenticate(params[:user][:password])
      user_sign_in(user)
      flash[:success] = 'ログインしました'
      redirect_to products_path
    else
      flash[:danger] = 'ログインに失敗しました'
      redirect_to sign_in_path
    end
  end

end

ログを見るとSQL文にメールアドレスがNULLと表示され、paramsの中にデータが入っていませんでした。
そのため、paramsの後に[:user]と書き、userモデルのemailカラム、passwordカラムに対応するようにしました。

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

Rails6 bootstrap

rails6にbootstrapをCDNで追加したところ、JSの動作不具合が多かった。下記の方法で実施すると不具合がなくなった為、備忘録として記述する。

参考URL: https://medium-company.com/rails-bootstrap/

Rails6標準の「yarn + webpacker」でBootstrapを導入

console
yarn add jquery bootstrap popper.js

Rails直下にあるpackage.jsonで確認

package.json
{
  "name": "test_pro",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.2.2",
    "bootstrap": "^4.5.0",
    "jquery": "^3.5.1",
    "popper.js": "^1.16.1",
    "turbolinks": "^5.2.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3.11.0"
  }
}

YarnでインストールしたBootstrapパッケージを利用できるようにimport
app/javascript/packs/application.js に以下のコードを追加

app/javascript/packs/application.js
import 'bootstrap'
import '../stylesheets/application'
app/javascript/stylesheets/application.scss
@import '~bootstrap/scss/bootstrap';

config/webpack/environment.js を以下のように修正し、jQueryとPopper.jsを利用できるよう

config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    Popper: ['popper.js', 'default']
  })
)
module.exports = environment

app/views/layouts/application.erb.html の「stylesheet_link_tag」を「stylesheet_pack_tag」に変更

application.erb.html
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
application.erb.html
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

画像を保存する場合はapp/javascript下にimagesを作成。
cssの記述はapp/javascript/stylesheets/application.scssへ。

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

自分で作成したRubyのアプリを指定した場所から始まるように設定するには

modelやcontrollerを作成しているとして、

例えば、app/views/tweets/indexを最初に表示させたいとして

config/routes.rbに

Rails.application.routes.draw do
 root to: 'tweets#index'
 resources :tweets
end

と記述すれば最初に表示できる。

「root to:」は最初に表示させたいview(今回の場合は、'tweets#index')を指定できる。

「resources :〜」はその後に記述するviewに向けてのルートを作成してくれる。

道路に例えると、「resources」で道を作り、「root to」でその道を選ぶという感じです。

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

Railsプロジェクトをdocker-composeで環境ごとに構築

Railsのプロジェクト(Redmine)をdocker-composeを用いて、
環境ごとに管理していきます
今回はdockerを用いた環境構築を主軸としたため、
webサーバーなどの詳しい説明は省きます

作成:2020年11月24日

環境

Ubuntu18.04
Rails 5
Ruby 2.6.5
Redmine 4.0
nginx 1.15.8
Docker 19.03.13
docker-compose 3
MySQL 5.7

必要なもの

Dockerとdocker-compsoeが動く環境(今回はUbuntu18.04で実行)
Redmineのソース
やる気

ざっくりとした方法

環境ごとにcomposeファイルを作成
環境変数COMPOSE_FILEを環境ごとに指定する

コンテナ構成

  • 開発環境
    app(rails), db(mysql)
  • 本番環境
    app(rails, puma), db(mysql), web(nginx)

フォルダ構成

docker-compose.develop.ymlが開発環境とテスト環境
docker-compose.prod.ymlが本番環境となる
開発環境のDockerファイルはDockerfile
本番環境はDockerfile.prodとなる

redmine(プロジェクトroot)
├── app
├── bin
├── etc....
├── config
│ ├── database.yml(追加)
│ ├── puma.rb(追加)
│ └── etc...
├── container(追加)
│     ├── app
│     │     ├── Dockerfile
│     │     └── Dockerfile.prod
│     ├── db
│     │ └── multibyte.cnf
│     └── web
│         ├── Dockerfile.prod
│         └── nginx.conf
├── docker-compose.develop.yml(追加)
└── docker-compose.prod.yml(追加)

初期設定

環境に応じて、環境変数を設定する

docker-composeの仕様により、
COMPOSE_FILE環境変数を設定すると、設定した環境すべてにおいて、
docker-composeコマンドに適応されるので注意。
direnvの導入をおすすめする

 開発環境
$ export COMPOSE_FILE=docker-compose.develop.yml
 本番環境
$ export COMPOSE_FILE=docker-compose.prod.yml

Gemfile修正

pumaを本番環境に適応させる

Gemfile
group :test, :production do 
  gem 'puma', '~> 3.7' # 追加
end
group :test do
  gem "rails-dom-testing"
  gem 'mocha', '>= 1.4.0'
  gem "simplecov", "~> 0.14.1", :require => false
  # For running system tests
  gem "capybara", '~> 2.13'
  gem "selenium-webdriver"
  # gem 'puma', '~> 3.7' 削除
end

puma.rb作成

config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true

database.yml作成(DB設定)

redmine公式ドキュメントに基づきdatabase.yml.exampleをもとに
database.ymlを作成

database.yml
    # Default setup is given for MySQL with ruby1.9.
    # Examples for PostgreSQL, SQLite3 and SQL Server can be found at the end.
    # Line indentation must be 2 spaces (no tabs).

    production:
      adapter: mysql2
      database: redmine
      host: db
      username: hoge
      password: "fugafuga_1"
      encoding: utf8

    development:
      adapter: mysql2
      database: redmine_development
      host: db
      username: root
      password: ""
      encoding: utf8

    # Warning: The database defined as "test" will be erased and
    # re-generated from your development database when you run "rake".
    # Do not set this db to the same as development or production.
    test:
      adapter: mysql2
      database: redmine_test
      host: db
      username: root
      password: ""
      encoding: utf8

    # 省略、、、、、

本番環境以外、パスなしrootで良ければproduction関連以外編集しなくてもよい

各ファイル詳細

開発、本番環境共用

実際の本番環境ではDBサーバーを別途用意するとは思いますが、、

cotainers/db/multibyte.cnf
# 文字コードを設定しないとエラーとなるため
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci

開発環境

appコンテナ

containers/app/Dockerfile
FROM ruby:2.6.5
RUN apt-get update -qq && apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs
RUN mkdir /redmine
WORKDIR /redmine
COPY . /redmine
RUN gem install bundler && bundle install

compose

docker-compose.develop.yml
# DBを永続化したい場合、コメントを外す
version: '3'
services:
  app:
    build: 
      context: ./
      dockerfile: containers/app/Dockerfile
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    volumes:
      - .:/redmine
    ports:
      - "3000:3000"
    depends_on:
      - db  

  db:
    image: mysql:5.7
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    ports:
      - 3306:3306
    volumes:
      - ./containers/db/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      #- db-store:/var/lib/mysql


#volumes:
  #db-store:

本番環境

appコンテナ

containers/app/Dockerfile.prod
FROM ruby:2.6.5
RUN apt-get update -qq && apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs
RUN mkdir /redmine
WORKDIR /redmine
COPY . /redmine
RUN gem install bundler && bundle install --without development test

webコンテナ

Dockerfile.prod
FROM nginx:1.15.8
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/myapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/myapp.conf
containers/db/nginx.conf
user  root;
worker_processes  1;

events{
    worker_connections  512;
}

# ソケット接続
http {
  upstream redmine{
    server unix:///redmine/tmp/sockets/puma.sock;
  }
  server { # simple load balancing
    listen 80;
    server_name localhost;

    #ログを記録しようとするとエラーが生じます
    #root /redmine/public;
    #access_log logs/access.log;
    #error_log logs/error.log;

    location / {
      proxy_pass http://redmine;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
    }
  }
}

compose

docker-compose.prod.yml
# DBコンテナはdatabase.ymlをもとに設定
# DBを永続化したい場合、コメントを外す
version: '3'
services:
  app:
    build:
      context: ./
      dockerfile: containers/app/Dockerfile.prod
    command: bash -c "rm -f /redmine/tmp/pids/server.pid && bundle exec puma -e production -C config/puma.rb"
    volumes:
      - .:/redmine
      - public-data:/redmine/public
      - tmp-data:/redmine/tmp
    depends_on:
      - db

  web:
    build: 
      context: containers/web/
      dockerfile: Dockerfile.prod
    ports:
      - 80:80
    volumes:
      - public-data:/redmine/public
      - tmp-data:/redmine/tmp
    depends_on:
      - app

  db:
    image: mysql:5.7
    environment:
      MYSQL_USER: hoge
      MYSQL_PASSWORD: fugafuga_1
      MYSQL_ROOT_PASSWORD: rootdayo
      MYSQL_DATABASE: redmine
    ports:
      - 3306
    volumes:
      - ./containers/db/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      #- db-store:/var/lib/mysql

volumes:
  #db-store:
  public-data:
  tmp-data:

実行手順

開発環境

$ docker-compose build
# DBを永続化した場合は最初だけ----------
# db作成(appはコンテナ名)
$ docker-compose run app rake db:create
# テーブル設定
$ docker-compose run app rake db:migrate
# ---------------------------------------
# 起動
$ docker-compose up -d
# http://localhost:3000

本番環境

$ docker-compose build

# Redmine公式ドキュメントに基づき、シークレットトークン作成
# 実行は最初の一回だけでよい
$ docker-compose run -e RAILS_ENV=production app bundle exec rake generate_secret_token
# DBを永続化した場合は最初だけけ---------
# テーブル設定
$ docker-compose run -e RAILS_ENV=production app bundle exec rake db:migrate
# ---------------------------------------
# $ 起動
$ docker-compose up -d

参考

Redmine GitHub
https://github.com/redmine/redmine
Redmineインストール

http://guide.redmine.jp/RedmineInstall/
docker-compose.ymlが環境別に複数ある場合はCOMPOSE_FILEを定義しておくと幸せになれる

https://suin.io/535

GitHub

ソースをgithubにあげているので良かったら参考にしてください
https://github.com/kinako555/redmine_managed_docker.git

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

ポートフォリオ作成記録 環境準備編

目的

ポートフォリオ作成の記録として、Qiitaの投稿にすることに致しました。
また、自分が詰まったエラーなどの内容を記録して、
今後に役立つと思い始めました。
今回は題名の通り、環境構築の過程のメモを投稿しようと思います。

使用環境

  • MacBook Air (13-inch, 2020 )
  • macOS Big Sur 11.0.1
  • VSCode 1.51.1

つまづいたこと

いざ、railsを私のMacにインストールといきたいですが、
私の環境が対応してないのでそこを対処が必要になりました。
原因は、エラーメッセージに

「Your CLT does not support macOS 11.0.
It is either outdated or was modified.
Please update your CLT or delete it if no updates are available.」

どうやら、XcodeのCLTを単体のみダウンロードしてインストールが必要でした。
その後、検索した記事内容のrbenvのインストールができました。

感想

あとは、問題なく準備ができました。
初投稿で、雑ですが数をこなして改善と挑戦を続けて記事を投稿し続けたいと思います。

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

Rails 掃除提案アプリのアルゴリズム

はじめに

オリジナルアプリとして掃除提案アプリを開発しました。その際、苦労したところを忘れずに書き記します。

目次

1.掃除提案機能の説明
2.必要なカラムとその型
3.Rubyのコード

1.掃除提案機能の説明

予め掃除箇所の情報を登録する。登録情報に応じて、毎日どこを掃除すれば良いか提案する機能である。なお登録情報は下記3つ。
・掃除箇所名
・掃除頻度(○日に一回掃除する)
・最後に掃除した日付

提案のアルゴリズムとしては下記の通り。
①最後に掃除した日付と今日の日付から経過日数を算出する
②経過日数と掃除頻度を比較する
③結果に応じてtrueもしくはfalesを返す。
④falseとなった場合、提案(画面に表示)する。

2.必要なカラムとその型

必要なカラムと型を下記に示す。状態は上記のtrueもしくはfalseが入るboolean型のカラムである。

登録情報 カラム名
掃除箇所 place string
清掃期間 period_cleaning integer
最後に清掃した日付 last_cleaned_date date
状態 status boolean

マイグレーションファイルの記述も下記に示す。

2020***********_create_suggestions.rb
class CreateSuggestions < ActiveRecord::Migration[6.0]
  def change
    create_table :suggestions do |t|
      t.string :place, null: false
      t.integer :period_cleaning, null: false
      t.date :last_cleaned_date, null: false
      t.boolean :status, null: false
      t.timestamps
    end
  end
end

3.Rubyのコード

改めて提案のアルゴリズムを下記に示す。
①最後に掃除した日付と今日の日付から経過日数を算出する
②経過日数と掃除頻度を比較する
③結果に応じてtrueもしくはfalesを返す。
④falseとなった場合、提案(画面に表示)する。

①〜④までのコードを下記のようにした。はじめにフォームから送られた掃除箇所の情報をインスタンス変数に代入する。次に変数this_dayに今日の日付を代入する。ここから①の経過日数を算出する(3行目:this_day - @suggestion.last_cleaned_date)。最後に掃除頻度(@suggestion.period_cleaning)to
経過日数(num_days)を比較し、trueもしくはfalseを状態(status)に代入する。

def create
  @suggestion = Suggestion.new(suggestion_params) #suggestion_paramsはストロングパラメータ
  this_day = Date.today
  num_days = (this_day - @suggestion.last_cleaned_date).to_i
  @suggestion.status = !(@suggestion.period_cleaning <= num_days)
end

以上

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

requestspecでlet!を使用する

requestspecを使ってコントローラーテストを行っていたのですが、let!の使い方に関して不明点があったため、記録として残しておきます。
letを始めRSpecに関する基礎的な文法についてはこちらの記事を参考にさせていただきました。
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

開発環境

rails (6.0.3.3)
rspec-rails (4.0.1)

疑問内容

なぜletではなくlet!なのか。

destroy以外では:laundryに対してletで通ったのですがdestroyアクションの時だけテストが通らずlet!にするとテストが通ります。
挙動としては以下を想定しています。

  • 管理者(admin: true) → 削除を実行
  • 管理者以外(admin: false) → 削除は行われずトップページにリダイレクトする

コードは以下のとおりです。(該当箇所のみ抜粋しています。)

requestspec
describe '#destroy' do
    let(:admin_user) { FactoryBot.create(:user, admin: true) }
    let(:user) { FactoryBot.create(:user, admin: false) }
    #この部分がなぜlet!になるのか
    let!(:laundry) { FactoryBot.create(:laundry) }

    context '管理者の場合' do
      it '店舗情報を削除できること' do
        sign_in admin_user
        expect {
          delete laundry_path(laundry)
      }.to change { Laundry.count }.by(-1)
      end
    end

    context '認可されていないユーザーの場合' do
      it '店舗情報を削除できないこと' do
        sign_in user
        expect {
          delete laundry_path(laundry)
        }.to_not change { Laundry.count }
      end
    end
  end

結論

deleteアクションを実行する前にデータをセットしておくため。

処理実行の流れとしては以下のとおりです。
1.Laundry.countが実行される(処理実行前のデータの数を確認)
2.expect { ... }が実行される(削除の実行)
3.Laundry.countが実行される(削除実行後のデータの数を確認)

先ほどのコードと処理実行の流れを照らし合わせながらみていきます。

  • let(遅延評価)にした場合
   let(:admin_user) { FactoryBot.create(:user, admin: true) }
    let(:user) { FactoryBot.create(:user, admin: false) }
    let(:laundry) { FactoryBot.create(:laundry) }

   context '管理者の場合' do
      it '店舗情報を削除できること' do
        sign_in admin_user
        expect {
          delete laundry_path(laundry)
      }.to change { Laundry.count }.by(-1)
     end
    end

1.Laundry.countが実行される(処理実行前のデータの数を確認)
 → FactoryBot.create(:laundry)は実行されていないためLaundry.countは0
2.expect { ... }が実行される(削除の実行)
 → delete laundry_path(laundry)でデータは作成されるが削除されるためこちらもLaundry.countは0
3.Laundry.countが実行される(削除実行後のデータの数を確認)
 → 予想は1つデータが減っているはず(to change { Laundry.count }.by(-1))だが、上記1.2からデータ数に変化はないためエラーが発生する。

エラー内容

 expected `Laundry.count` to have changed by -1, but was changed by 0

削除できないことを確認したい場合も同様の流れでエラーが発生します。

it '店舗情報を削除できないこと' do
   sign_in user
   expect {
     delete laundry_path(laundry)
   }.to_not change { Laundry.count }
end

1.データは作成されていないためLaundry.countは0
2.expext{}で削除を行うが管理者ではないため削除は実行されない(Laundry.countは1)
3.to_not change { Laundry.count }でデータ数は変わっていないことを確認したいが0から1に変更しているためエラーが発生する。

expected `Laundry.count` not to have changed, but did change from 0 to 1

以上からletで遅延評価にすることによりエラーが発生してしまいます。
これをlet!にして事前評価(各itが実行される直前にデータを作成)にすることでLaundry.countが1からスタートし、テストがパスします。

まとめ

createアクションの場合はデータが増えることを確認したいため前もってデータを用意する必要はないですが、destroyアクションの場合はデータが減ることを確認する必要があるためlet!を使う必要があります。

以上です。ありがとうございました!

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

Rails on Dockerのgemの永続化について

背景

Dockerを利用したアプリ開発を行っていて、ログイン機能を搭載したくbcryptをGemfileに追加したが以下のエラーが出続け解決不能に。

22: from /usr/local/bundle/gems/spring-2.1.1/bin/spring:49:in `<main>'
        21: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/client.rb:30:in `run'
        20: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/client/command.rb:7:in `call'
        19: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/client/server.rb:9:in `call'
        18: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        17: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        16: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/server.rb:9:in `<top (required)>'
        15: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        14: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        13: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/commands.rb:4:in `<top (required)>'
        12: from /usr/local/bundle/gems/spring-2.1.1/lib/spring/commands.rb:33:in `<module:Spring>'
        11: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        10: from /usr/local/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
         9: from /usr/local/lib/ruby/2.6.0/bundler/setup.rb:20:in `<top (required)>'
         8: from /usr/local/lib/ruby/2.6.0/bundler.rb:107:in `setup'
         7: from /usr/local/lib/ruby/2.6.0/bundler/runtime.rb:20:in `setup'
         6: from /usr/local/lib/ruby/2.6.0/bundler/runtime.rb:108:in `block in definition_method'
         5: from /usr/local/lib/ruby/2.6.0/bundler/definition.rb:226:in `requested_specs'
         4: from /usr/local/lib/ruby/2.6.0/bundler/definition.rb:237:in `specs_for'
         3: from /usr/local/lib/ruby/2.6.0/bundler/definition.rb:170:in `specs'
         2: from /usr/local/lib/ruby/2.6.0/bundler/spec_set.rb:85:in `materialize'
         1: from /usr/local/lib/ruby/2.6.0/bundler/spec_set.rb:85:in `map!'
/usr/local/lib/ruby/2.6.0/bundler/spec_set.rb:91:in `block in materialize': Could not find bcrypt-3.1.16 in any of the sources (Bundler::GemNotFound)


解決方法


こちらのサイトの方法を試したが、一向に解決せず。。。
https://qiita.com/totto357/items/1741da83bf642dab99df


問題は実はDockerのgem一覧が永続化されていなかったからということが発覚。
https://nishinatoshiharu.com/datavolume-for-gem/

gemの永続化を行なっていないとコンテナ上でbundle installを実行してもDockerイメージにはgemが保存されないためエラーが発生していたようです。

gemを永続化していないと新しいgemを入れるたびに、ビルドを実行しコンテナを起動しなければならないというわけです。

しかし、永続化を行えばコンテナ上でgemをインストールした際にイメージの再ビルドを行わなくて済むということのようです。

永続化の方法については上記のサイトがわかりやすいため参照ください。

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

[Rails]exists?メソッド、present?メソッド、presenceメソッドについて勉強してみた![初心者]

それぞれの違い

exists?メソッド
指定した条件のレコードがデータベースに存在するかどうか、真偽値で返すメソッド
present?メソッド
レコードの存在確認を行った後に、インスタンスを使って何か処理をしたい時に使用するメソッド
presenceメソッド
オブジェクトが存在すればそのオブジェクトを返し、オブジェクトが存在しなければnilを返すメソッド

exists?メソッド

基本構文
オブジェクト.exists?(条件)

exists?メソッドの引数に条件を指定すると、条件にマッチするレコードがあればtrueを返し、レコードがなければfalseを返します。

指定したidのレコードを確認する方法

pry(main)> User.exists?(1)
=> true

指定した条件のレコードを確認する方法

exists?メソッドの引数にハッシュ(カラム名: '値')を渡します。

pry(main)> User.exists?(name: '田中')
=> true

whereメソッドと併用する方法

pry(main)> User.where(name: '田中').exists?
=> true

present?メソッド

基本構文
オブジェクト.present?

present?メソッドを使用したオブジェクトが存在すればtrueを返し、存在しなければfalseを返すメソッドです。
非常にexists?メソッドと似通っていて、混乱しそうですね。
使い分けとしては、if文など条件分岐をプログラムで書くときには使われることが多いです。

使用例

app/views/users/index.html.erb
<% if  @users.present? %>
  <%  @users.each do |user| %>
    <%= user.name %>
<% else %>
    <div>ユーザーなし</div>
  <% end %>
<% end %>

presenceメソッド

基本構文
オブジェクト.presence

presenceメソッドを使用したオブジェクトが存在すればそのオブジェクト自身を返し、存在しなければnilを返すメソッドです。
presenceメソッドはRailsのメソッドなので、Rubyで使おうとすると、エラーが出ます。

使用例

app/model/user.rb
class User < ApplicationRecord
  def call
    name.presence || 'No Name'
  end
end
app/controller/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.call
  end
end
app/views/users/show.html.erb
<%= @user %>

このように記載すると、@userのnameに値があればその値が表示され、なければ “No Name” と表示されます。

おわりに

とてもややこしいですね。
自分でまとめておきながら、混乱しています。
徐々に使っていって、手で覚えるしかないですね。

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

Ruby on Railsでの頻出単語(まとめ)

Ruby on Rails似て、よく出てくる単語の意味をまとめたものです。
辞書的に使っていただけると幸いです。

[データベース関係]

rails g model テーブル名(はじめ大文字) カラム名:データ型
新しくテーブルとカラムを作るときに、ターミナルで使う

rails g migration ファイル名
テーブルに新しくカラムを追加するときに、ターミナルで使う

add_column :テーブル名, :カラム名, :データ型
マイグレーションファイルに、新しいカラム名を追加するときに使う

rails db:migrate
データベースに変更を加えるときに、ターミナルで使う

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

Refileの使い方

ImageMagickをインストール

本番サーバーとか開発環境にimage-magickがないと動かないのでインストールしておく。

インストールされてるか確認
$ convert -version #以下のようにVersion: ImageMagick バージョン名が表示されていればOK
Version: ImageMagick 7.0.10-38 Q16 x86_64 2020-11-16 https://imagemagick.org
Copyright: © 1999-2020 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI 
Delegates (built-in): jng jpeg png tiff zlib
インストール方法(Cloud9の場合)
$ sudo yum -y install libpng-devel libjpeg-devel libtiff-devel gcc
$ cd ~
$ wget http://www.imagemagick.org/download/ImageMagick.tar.gz
$ tar -vxf ImageMagick.tar.gz
$ ls
$ cd ImageMagick-x.x.x-xx
$ ./configure
$ make
$ sudo make install

Gemをインストール

Gemfile
gem "refile", require: "refile/rails", github: 'manfe/refile'
gem "refile-mini_magick"
$ bundle install

画像データを保存するカラムを追加

Userモデルにthumbnailというカラム(サムネイル画像名を保存するカラム)を追加します。

$ rails g migration AddThumbnailToUser thumbnail_id:string
class AddThumbnailToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :thumbnail_id, :string
  end
end
$ rails db:migrate

modelを修正

追加したカラム名から_idを抜いた部分をattachmentとして設定

app/models/user.rb
class User < ApplicationRecord
  attachment :thumbnail # 追加
end

ファイルアップロードのform

<%= f.attachment_field :thumbnail %>でフォームの画像アップロード部分を設置

<%= form_with model: @user, local: true do |f| %>
  <div>
    <%= f.label :name %>
    <%= f.text_field :name, class: 'form-control' %>
  </div>
  <div>
    <%= f.attachment_field :thumbnail %> <!-- 追加 -->
  </div>
  <%= f.submit class: 'btn btn-success' %>
<% end %>

strong_parametersを修正

app/views/controllers/users_controller.rb
class UsersController < ApplicationController

  private
  def user_params
    params.require(:user).permit(:name, :thumbnail) # thumbnail_idではないので注意
  end
end

画像を出力

<%= image_tag attachment_url(@user, :thumbnail, :fill, 100, 100) %>

Runtime Errorが表示された場合

エラー画面内に表示されているRefile.secret_key = 'hogehogeeeeee'config/initializers/application_controller_renderer.rbに貼り付けてサーバーを再起動。

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

このプログラムの考え方が知りたい...[Ruby]

ひな祭り

問題

あなたが持っている 10 体の人形を "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" で表します。ひな壇の各段 (計 3 段) に並べる人形の数が与えられるので、各段にならべる人形の記号を改行区切りで出力してください。人形は必ずもとの A, B, C, ... の順番で並べます。

例)

各段にならべる人形の数: 2, 3, 5

→ 人形の並べ方:

AB
CDE
FGHIJ

入力される値

入力は以下のフォーマットで与えられます。

h_1 h_2 h_3
ひな壇の 1 段目、2 段目、3 段目に並べる人形の数を表す整数 h_1, h_2, h_3 がこの順に半角スペース区切りで与えられます。入力は 1 行となり、末尾に改行が 1 つ入ります。

期待する出力

ひな壇への人形の並べ方を以下の形式で出力してください。

s_1
s_2
s_3
ひな壇の 1 段目、2 段目、3 段目に並べる人形を表す文字列 s_1, s_2, s_3 をこの順に改行区切りで出力してください。

s_i (1 ≦ i ≦ 3) は i 段目に並べる人形の記号をアルファベット順に結合した文字列です。

入力例1

2 3 5

出力例1

AB
CDE
FGHIJ

入力例2

1 1 8

出力例2

A
B
CDEFGHIJ

私の考え方

与えられる数値を

a = gets.split(" ").map(&:to_i)

で 2 3 5 というように切り離す。
次に

array = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J" }

としてsliceメソッド等で抜き出すのかなと思ったがそもそもこの時点で

Main.rb:3: syntax error, unexpected ',', expecting =>
array = {"A", "B", "C", "D", "E", "F", "G"...
            ^
Main.rb:3: syntax error, unexpected ',', expecting end-of-input
array = {"A", "B", "C", "D", "E", "F", "G", "H"...
                 ^

というエラーが出る。
繰り返し文でputsするのかなとも思ったが、「AB」ときて「CDE」というように続きから出力する方法がわからない。
この問題はいろんなパターンがあると思うが、そもそもプログラミング脳になっていないので解けなかった。悔しい。
どなたか分かる方いらっしゃいましたらアドバイスください。。。

以上!

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

【Rails】N+1問題 (Bullet で検出し includes して対策)

はじめに

未経験からWeb系エンジニアへの転職を目指し、ポートフォリオ(投稿アプリ)を作成中です。
今回は、ポートフォリオにN+1問題を対応させてみたので、まとめてみました!

前提

  • Ruby2.7.0
  • Ruby on Rails6.0.3
  • MySQL8.0

N+1問題とは

  • 必要以上にSQLの処理が行われて、システムのパフォーマンスが低下してしまうこと。
  • レスポンスが遅く、ユーザビリティの低いサービスの原因の多くは、SQL周りにあることが多いらしい。
  • SQLの発行回数を減らし、パフォーマンスを上げることが大事。

N+1問題の検出

まずは、N+1問題を検出するためBulletというgemを試してみる。

Gemfile
group :development, :test do
  gem 'bullet'
end
ターミナル
 bundle

Bulletはconfig/environments/development.rbに設定を追加しないと動作しないのでファイルを編集する。

config/environments/development.rb
Rails.application.configure do
  config.after_initialize do
    Bullet.enable = true # Bulletを有効化
    Bullet.alert = true # JavaScriptのポップアップアラートを有効化
    Bullet.bullet_logger = true # Rails.root/log/bullet.logに出力
    Bullet.console = true # ブラウザのconsole.logに出力
    Bullet.rails_logger = true # Railsのログに結果を出力
    Bullet.add_footer = true # ページの左下に結果を表示
  end
end

いざ、確認!

ローカル環境で投稿一覧画面にアクセスしてみると、N+1問題が検出され、この問題を解決するためには.includes([:user])を追加すればよいとある。

ターミナル
user: hako
GET /posts/index
USE eager loading detected
  Post => [:user]
  Add to your query: .includes([:user])
Call stack
  /Users/hako/portfolio/cafe_app/app/views/posts/index.html.erb:48:in `block in _app_views_posts_index_html_erb___2990704399900080110_90940'
  /Users/hako/portfolio/cafe_app/app/views/posts/index.html.erb:5:in `_app_views_posts_index_html_erb___2990704399900080110_90940'

N+1問題の対策

そこで、app/controllers/posts_controller.rb.includes([:user])を追加してみる。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    # @posts = Post.page(params[:page]).per(6).order(:created_at => :desc) # 編集前
    @posts = Post.includes([:user]).page(params[:page]).per(6).order(:created_at => :desc) # 編集後
  end
end

再アクセスしてみるとBulletによる警告が表示されなくなった!
今度は投稿へのコメントにアクセスしてみると、以下のメッセージが表示されたので、

ターミナル
user: hako
GET /posts/48
USE eager loading detected
  Comment => [:user]
  Add to your query: .includes([:user])
Call stack
  /Users/hako/portfolio/cafe_app/app/views/comments/_comment.html.erb:3:in `block in _app_views_comments__comment_html_erb__2537338689005093815_102800'
  /Users/hako/portfolio/cafe_app/app/views/comments/_comment.html.erb:2:in `_app_views_comments__comment_html_erb__2537338689005093815_102800'
  /Users/hako/portfolio/cafe_app/app/views/posts/show.html.erb:65:in `_app_views_posts_show_html_erb__3096142757258343957_102780'

同様に、.includes([:user])を追加。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def show
    @user = @post.user
    # @comments = @post.comments # 編集前
    @comments = @post.comments.includes([:user]) # 編集後
    @comment = @post.comments.build
  end
end

N+1問題が検出されなくなるまで、メッセージが表示される箇所に一つずつincludesを追加。
なお、Bulletを使っている中でN+1の警告を無視したい場合は、ホワイトリスト形式でconfig/initializer/bullet.rbに登録しておけば良いらしい。
今後いろいろ試してみたい。

終わりに

以上の対策をした後、本番環境にアクセスしてみるとパフォーマンスが上がってびっくり!動作が早い早い!!
ユーザー側に立つと、パフォーマンスが遅いとそれだけでストレスだし、そのサービスを使いたいと思えないもの。
今後は、SQLの実行回数をいかに減らせるかを意識してコードを書いていこうと思う!
引き続き、SQL周りの知識も深めていきたい。

初学者のため、理解不足な点が多々あると思います。気になった点やアドバイスなどありましたら、教えていただけますと幸いです!

参考サイト

ポートフォリオのバージョンアップ2020 7/2 (N + 1 問題)

Railsメモ(19) : BulletでN+1問題を検出する

N+1問題を発見しDBのクエリを改善するBullet

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

【脱初心者!!】Railsでmodelにスコープを定義する!

はじめに

最近までwhereやorderはついついcontrollerに書いてしまうことが多かったのですが、modelで定義した方がメリットが多いので今回はmodelにスコープを定義するやり方を解説していきます!

modelに定義するメリット

  • controllerをみやすくシンプルにできる(Controllerを太らせないことが脱初心者への第一歩です!)
  • modelに書くことによりテストが書きやすくなる!(RailsでいうとRspecのmodel_specでテストがかけますね!)

modelのスコープって何?

modelのスコープとは複数のクエリをまとめたメソッドのことです!!
where, order, join, limit, distinct ...etc
などでまとめることですね!

さっそくやってみる

今回は予約テーブルのreservationsコントローラーを設定しています

reservaiontsテーブル
title(string)とstart_time(datetime)のカラムがあります。

reservations.controller.rb
class ReservationsController < ApplicationController

  def index
    #ページネーションをつかっています(Reservation.allだと思ってください)
    @reservations = Reservation.paginate(page: params[:page])

    #全ての予約からstart_timeが今日より後の予約を 降順で表示する
    @reservations_index = @reservations.where("start_time > ?", Date.today).order(start_time: :desc)
  end

ご覧の通り少しコントローラーが長くなり読みづらいです。

これをmodelに定義してみます。

reservation.rb
class Reservation < ApplicationRecord
  scope :future_reservations, -> { where("start_time > ?", Date.today).order(start_time: :desc) }

解説するとscopeでまず定義しひとまずfuture_reservationsでメソッドを定義します。
その後はcontrollerの記述をリファクタリングするだけです!!

ちなみに

reservation.rb
  scope :future_reservations, -> do
    where("start_time > ?", Date.today).order(start_time: :desc)
  end

コードが長くなる場合はこちらの方が綺麗なので覚えておきましょう!!

reservations.controller.rb
  def index
    @reservations = Reservation.paginate(page: params[:page])
    @reservations_index = @reservations.future_reservations
  end

最後にcontrollerにメソッドを書いてすっきりさせましょう!
お疲れ様でした!!!

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

[Rails]フォロー機能実装について

Railsでのフォロー機能実装に手間取ったので、
自分が理解した範囲で手順を書いていく。

※プログラミング初心者のため、内容に不備や間違っている箇所があるかもしれません。
自分用のメモとして残していますが、もし不備等あればご指摘いただければと思います。

開発環境

Ruby 2.6.3
Rails 5.2.4

前提機能

ログイン機能(devise)

実装の流れ

① 中間テーブルを作成する

② Userモデル同士を参照するアソシエーションを記述する

③ follow、unfollowメソッドを定義する

④ フォローボタンを作成、コントローラを記述する

① 中間テーブルを作成する

「フォローするユーザ」と「フォローされるユーザ」を取得する際、
どちらも同じUserモデルを参照するため中間テーブルが必要になる。
中間テーブルについてはこちらの記事が大変わかりやすく、参考にさせていただいた。
https://qiita.com/morikuma709/items/1e389ddcdfc1102ef3f4

まず、Relationshipモデルを作成

$ rails g model Relationship
create_relationship.rb
class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|
      t.references :follower, foreign_key: true
      t.references :followed, foreign_key: true

      t.timestamps
      t.index [:follower_id, :followed_id], unique: true
    end
  end
end

カラムには「follower_id(フォローするユーザ)」と「followed_id(フォローされるユーザ)」を設定する。
どちらもUserモデルにて外部キーとして使用する(後述)のでreferencesで定義。
migrateを忘れずに。

 $ rails db:migrate 

② モデルの関連付け

relationship.rb
class Relationship < ApplicationRecord

  belongs_to :follwer, class_name: "User"
  belongs_to :follwed, class_name: "User"

end

ここの書き方は単純。
先ほど設定した「follower_id」と「followed_id」の値からUsersテーブルのレコードを参照する。
ただ、「class_name: 'モデル名'」で参照するモデルを指定しなければいけないことに注意。

続いてUserモデルを記述していく。
ここが個人的に一番混乱した。

user.rb
  # フォローするユーザ
  has_many :active_relationships, class_name: "Relationship",
                                  foreign_key: "follower_id",
                                  dependent: :destroy
  # 自分がフォローしているユーザ
  has_many :followed_users, through: :active_relationships,
                            source: :followed
  # フォローされるユーザ
  has_many :passive_relationships, class_name: "Relationship",
                                   foreign_key: "followed_id",
                                   dependent: :destroy
  # 自分をフォローしているユーザ
  has_many :following_users, through: :passive_relationships,
                             source: :follower

has_manyに続くモデル名は新たに作成したもの。もちろんモデル自体は存在しない。

・フォローするユーザとフォローされるユーザを取得
 今回はそれぞれ、「active_relationships」と「passive_relationships」とした。

  →active_relationshipsの場合
   参照するモデルはRelationshipモデル(class_name: "Relationship")
   さらに、値を参照するカラムは「follower_id」(foreign_key: "follower_id")

・自分がフォローしているユーザと自分をフォローしているユーザ
 こちらは「followed_users」と「following_users」とした。

  →followed_usersの場合
    active_relationshipsユーザーからフォローされているユーザ
    active_relationshipsを参照(through: :passive_relationships)
    取得する値は「followed_id」(source: :followed)

③ メソッドを定義する

Relationshipsコントローラのアクション内で使用するメソッドを定義する。
場所はUserモデル。

user.rb
  def follow(user_id)
    active_relationships.create(followed_id: user_id)
  end

  def unfollow(user_id)
    active_relationships.find_by(followed_id: user_id).destroy
  end

  def following?(user)
    followed_users.include?(user)
  end

④ フォローボタン作成、コントローラを記述

続いてコントローラ

relationships_controller.rb
class RelationshipsController < ApplicationController

  def create
    current_user.follow(params[:user_id])
    redirect_to request.referer
  end

  def destroy
    current_user.unfollow(params[:user_id])
    redirect_to request.referer
  end

end

viewファイルにフォローボタンを作成する

users/show.html.erb
  <% if current_user.following?(@user) %>
    <%= link_to 'フォロー中', user_relationship_path(@user), method: :delete, class:"btn" %>
  <% else %>
    <%= link_to 'フォローする', user_relationships_path(@user), method: :post, class:"btn" %>
  <% end %>  

@userにはparams[:id]が格納されている。
current_user.following?(@user)で@userがfollowed_usersに含まれているかを判断する。

以上でフォロー機能の実装は完了。
時間がある時に一覧ページ作成の流れも追記しようと思う。

自分がたどり着いた考え方まとめ

active_relationships = フォローするユーザ
followed_users = active_relationshipsにフォローされているユーザ
passive_relationships = フォローされるユーザ
following_users = active_relationshipsをフォローしている

初めてフォロー機能に触れた時、「する」「される」の関係がわからなくなり、アソシエーションの中身がぐちゃぐちゃになっていた。解決するのにだいぶ時間を使ってしまった。
モデルの名義やそもそものやり方は人それぞれなので、自分が理解できるやり方を見つけるのが一番だと感じた。

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

ゲストログイン機能でたまに、パスワードエラーが出ていたが、解決した

はじめに

 先日、ゲストログイン機能を実装したが、数回に一度エラーが出てしまうことがわかり、原因を追求した。

ゲストログイン機能実装について

 私が以前投稿した、「ゲストログイン機能の実装方法」はこちら

エラーの原因

パスワードについてのエラーで、バリデーションによって弾かれていたので、この部分と予測。

user.password = SecureRandom.urlsafe_base64

SecureRandom.urlsafe_base64について調べて見ると、
ランダムに、大文字英字、小文字英字、数字、記号の「-(ハイフン)」「_(アンダースコア)」を生成していることに気づいた。
そう、この記号はパスワードのバリデーションに含めていない!だから、時々、記号が含まれるパスワードがゲストログインの際に生成され、エラーとなっていた。

解決方法

シンプルに、

user.password = '1234abcd'

と、こちらでパスワードを指定すれば、簡単に解決はできるが…
セキュリティがガバガバになってしまう。

ランダムにパスワードを生成してくれることは諦めたくない。
さらにググると、
同じようにランダム生成のSecureRandomを使った上で、別の生成内容を見つけた。

user.password = SecureRandom.alphanumeric

これだ!!

SecureRandom.alphanumericは、英数字62種類の中から、ランダムに生成してくれる。
つまり、大文字英字(26種)、小文字英字(26種)、数字(10種)。
さらに、引数で文字列のサイズを指定できる。

user.password = SecureRandom.alphanumeric(6)
#=>'Ajge4l'

引数に指定すれば、バリデーションで字数に制限をかけていた場合でも、エラーにならない!!

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

背景色を透過するCSS (opacityとrgba)

はじめに

Railsでオリジナルアプリ制作中、サイドバー部分を実装しました。

sample.html.erb(抜粋)
<div class="side-bar-upper">
 <h2><%= current_user.nickname %></h2>
</div>
sample.css(抜粋)
.side-bar-upper {
  width: 90%;
  height: 50px;
  margin: 5px auto;
  padding: 5px;
  background-color: #fff;
}

sample

サイドバー背景には、個人的にお気に入りのキレイな画像を使用しました。
せっかくなので、重ねた要素は透過して背景画像を生かしたいと思います。

開発環境

Ruby 2.6.5
Rails 6.0.3.4
MySQL
Visual Studio Code
(GoogleChrome)

検証

まず背景の透過といえば…opacityプロパティが挙げられます。

opacity
要素の透明度を指定できます。

.sample{
  /* 透明度50% */
  opacity: 0.5;
}

透明度は0.0~1.0内の小数点で指定します。
(0.5であれば、50%の透過率ということです)

こちらをCSSに反映してみます。

sample.css(抜粋/修正)
.side-bar-upper {
  width: 90%;
  height: 50px;
  margin: 5px auto;
  padding: 5px;
  background-color: #fff;
  opacity: 0.5;
}

sample.opacity

要素の透過が出来ました…が
sampleの文字も薄い…一緒に透過されているのが分かります。

opacityプロパティはその子孫要素にも透明度が適用される点に注意が必要です…!

〜文字はそのまま、背景のみ透過したい!!〜

そんな時は…rgba()を使用します!

rgba
CSS3で追加された、RGBAカラーモデルでカラーと透明度を指定出来る値です。

.sample{
 color: rgba(0, 0, 0, 0.5);
}

ここで注意が必要なのが、rgbaそのものはプロパティではなく、あくまで値ということです。
background-colorbox-shadowなどのカラー指定の出来るプロパティに、値として使用ができます。
そのため、カラーはカラーコードではなく、10進数で指定する必要があります。
各値は、Red, Green, Blue(ここまでがカラー値), alpha(透明度)の順で指定します。
今回使用している#ffffffは、10進数ですと255, 255, 255になります。

それでは、こちらをCSSに反映してみます。

sample.css(抜粋/修正2)
.side-bar-upper {
  width: 90%;
  height: 50px;
  margin: 5px auto;
  padding: 5px;
  background-color: rgba(255, 255, 255, 0.5);
}

sample.rgba

文字はそのままで、無事に背景の透過が出来ました!

終わりに

背景色透過のプロパティはアプリ制作で多用すると思い、今回記事にまとめてみました。

初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。

参考記事

要素の透明度の指定(opacity)
カラーの透明度の値(rgba, transparent)
【CSS3リファレンス】opacity
【CSS3リファレンス】rgba()
カラーコードの一覧表(色を調べる/色を作る)

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

スクール5週目 応用力の根底

今週は、応用カリキュラムも終盤に差し掛かり、課題図書(レスポンシブWebデザイン、Ruby応用、正規表現)を主に学びました。
プログラミングの奥深さを感じ取れた週になりました。

【学習内容】
・サーバーの基礎知識
・レスポンシブWebデザイン
・Ruby応用
・正規表現

サーバーの基礎知識 :desktop:
まず、サーバーの必要性、役割を学びます。その上でIPアドレス、WANとLAN、ドメインとDNS、ポート、WEBサーバー、HTTPの基礎を学習します。
次にサーバ向けによく利用されているOSであるLinuxについて学びます。Linuxの特徴としては、以下の点が挙げられます。
①オープンソースであり、誰でも自由に無償で利用できる
②品質の高い多くのソフトウェアが利用できる
③世界中でサーバ用途として広く利用されているため信頼性が高い
④操作を自動化するための仕組みが用意されており、サーバの運用が行いやすい

そして次にSSHについて学習。SSHとは、暗号や認証の技術を利用して、安全にリモートコンピュータと通信するためのプロトコルのことです。利用するメリットとしては以下の点が挙げられます。
①通信を盗聴される危険性を回避して、安全に操作ができる
②サーバ上でのファイル操作、設定ファイルの編集ができる
③データのバックアップをしたいとき、サーバ上で圧縮(ZIP)して、ファイルの一括ダウンロードできるので、アップロード・ダウンロードの大量ファイルの時間短縮ができる

レスポンシブWebデザイン :iphone:
PCやタブレット、スマホなど様々なデバイスの大きさに対応し、閲覧しやすいデザインのWebサイトを作成する方法を学びました。
ユーザーがWEBサイトを閲覧する際、スマホで閲覧する比率は6割〜7割を超えると言われているそうです。

そのため、WEB開発においても、スマホやタブレットでの閲覧を想定した見た目づくりがとても重要になってくるそうです。実際に私もボランティア団体のHPを作った際はスマホでの見え方を気にしながら作業をしていたので、レスポンシブの考え方は非常に常用だと感じました。

Ruby応用 :rotating_light:
これまで、Rubyの条件分岐にはif文を用いました。
しかし、Rubyにはif文以外にも条件分岐を表現する文法としてcase文があります。

case文とは、条件分岐を表現するための文法です。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができます。並列する条件が多数ある場合は、if文よりもcase文を使った方がコードとして読みやすくなります。つまり、可読性を高められることだと思いました。

Sample.rb
country = "Japan"

if country == "Japan"
 puts "こんにちは"
elsif  country == "America"
 puts "Hello"
elsif  country == "France"
 puts "Bonjour"
elsif  country == "China"
 puts "你好"
elsif  country == "Italy"
 puts "Buon giorno"
elsif  country == "Germany"
 puts "Guten Tag"
else
 puts "..."
end

次に繰り返し処理について学びました。

繰り返し処理で思い浮かぶのはeachメソッドだけでしたが、Rubyの繰り返し処理用の1つにwhile文があるそうです。

while文は繰り返し処理を行うためのRubyの構文です。指定した条件が真である間、処理を繰り返します。

Sample.rb
number = 0

while number <= 10
 puts number
 number += 1
end

正規表現 :door:
今までさまざまなアプリケーションに触れてきた中で、「パスワードが6文字未満だったり、メールアドレスに@が入っていなかったりするとユーザー登録ができないのは、どのように判断されているのだろう?」 と疑問に思っていました。

これらはすべて正規表現という技術を用いて実装されていたことをここで、初めて知りました。

正規表現とは、文字列の一部分を抽出・置換したり、文字列が制約を満たしているかを調べるための表現方法です。

例えば…
・電話番号からハイフンを取り除く
・パスワードに英数字8文字以上という制約
・メールアドレスからドメインの部分のみ抽出
・全角かな/カナ漢字の区別する
・passwordの英数字混合の判断

他にもたくさんの正規表現があることを学びました。
最後のpasswordの英数字混合の判断する場合、以下の記述になります。

irb(main):001:0> password = "T2o0k2y0o"

irb(main):002:0> password.match(/\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i)
=> #<MatchData “T2o0k2y0o”>

振り返り・感想 :triangular_flag_on_post:

やはりプログラミングは暗記していけないと痛感させられました。なぜなら、passwordの英数字混合の判断だけでも一見どんな正規表現なのかが分かりません。そのため、プログラミングは暗記するのではなく、調べて理解して応用させることが大事だと改めて思いました。

また、コメントつけて上げると、後から誰かが見返しても丁寧で分かりやすいですし、テストコードの際に役立つと思いました。

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

スクール5週目

今週は、応用カリキュラムも終盤に差し掛かり、課題図書(レスポンシブWebデザイン、Ruby応用、正規表現)を主に学びました。
プログラミングの奥深さを感じ取れた週になりました。

【学習内容】
・サーバーの基礎知識
・レスポンシブWebデザイン
・Ruby応用
・正規表現

サーバーの基礎知識 :desktop:
まず、サーバーの必要性、役割を学びます。その上でIPアドレス、WANとLAN、ドメインとDNS、ポート、WEBサーバー、HTTPの基礎を学習します。
次にサーバ向けによく利用されているOSであるLinuxについて学びます。Linuxの特徴としては、以下の点が挙げられます。
①オープンソースであり、誰でも自由に無償で利用できる
②品質の高い多くのソフトウェアが利用できる
③世界中でサーバ用途として広く利用されているため信頼性が高い
④操作を自動化するための仕組みが用意されており、サーバの運用が行いやすい

そして次にSSHについて学習。SSHとは、暗号や認証の技術を利用して、安全にリモートコンピュータと通信するためのプロトコルのことです。利用するメリットとしては以下の点が挙げられます。
①通信を盗聴される危険性を回避して、安全に操作ができる
②サーバ上でのファイル操作、設定ファイルの編集ができる
③データのバックアップをしたいとき、サーバ上で圧縮(ZIP)して、ファイルの一括ダウンロードできるので、アップロード・ダウンロードの大量ファイルの時間短縮ができる

レスポンシブWebデザイン :iphone:
PCやタブレット、スマホなど様々なデバイスの大きさに対応し、閲覧しやすいデザインのWebサイトを作成する方法を学びました。
ユーザーがWEBサイトを閲覧する際、スマホで閲覧する比率は6割〜7割を超えると言われているそうです。

そのため、WEB開発においても、スマホやタブレットでの閲覧を想定した見た目づくりがとても重要になってくるそうです。実際に私もボランティア団体のHPを作った際はスマホでの見え方を気にしながら作業をしていたので、レスポンシブの考え方は非常に常用だと感じました。

Ruby応用 :rotating_light:
これまで、Rubyの条件分岐にはif文を用いました。
しかし、Rubyにはif文以外にも条件分岐を表現する文法としてcase文があります。

case文とは、条件分岐を表現するための文法です。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができます。並列する条件が多数ある場合は、if文よりもcase文を使った方がコードとして読みやすくなります。つまり、可読性を高められることだと思いました。

Sample.rb
country = "Japan"

if country == "Japan"
 puts "こんにちは"
elsif  country == "America"
 puts "Hello"
elsif  country == "France"
 puts "Bonjour"
elsif  country == "China"
 puts "你好"
elsif  country == "Italy"
 puts "Buon giorno"
elsif  country == "Germany"
 puts "Guten Tag"
else
 puts "..."
end

次に繰り返し処理について学びました。

繰り返し処理で思い浮かぶのはeachメソッドだけでしたが、Rubyの繰り返し処理用の1つにwhile文があるそうです。

while文は繰り返し処理を行うためのRubyの構文です。指定した条件が真である間、処理を繰り返します。

Sample.rb
number = 0

while number <= 10
 puts number
 number += 1
end

正規表現 :door:
今までさまざまなアプリケーションに触れてきた中で、「パスワードが6文字未満だったり、メールアドレスに@が入っていなかったりするとユーザー登録ができないのは、どのように判断されているのだろう?」 と疑問に思っていました。

これらはすべて正規表現という技術を用いて実装されていたことをここで、初めて知りました。

正規表現とは、文字列の一部分を抽出・置換したり、文字列が制約を満たしているかを調べるための表現方法です。

例えば…
・電話番号からハイフンを取り除く
・パスワードに英数字8文字以上という制約
・メールアドレスからドメインの部分のみ抽出
・全角かな/カナ漢字の区別する
・passwordの英数字混合の判断

他にもたくさんの正規表現があることを学びました。
最後のpasswordの英数字混合の判断する場合、以下の記述になります。

Sample.rb
irb(main):001:0> password = "T2o0k2y0o"

irb(main):002:0> password.match(/\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i)
=> #<MatchData “T2o0k2y0o”>

振り返り・感想 :triangular_flag_on_post:

やはりプログラミングは暗記していけないと痛感させられました。なぜなら、passwordの英数字混合の判断だけでも一見どんな正規表現なのかが分かりません。そのため、プログラミングは暗記するのではなく、調べて理解して応用させることが大事だと改めて思いました。また、コメントつけて上げると、後から誰かが見返しても丁寧で分かりやすいですし、テストコードの際に役立つと思いました。

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

【RSpec】traitの使い方を整理してみた

 最近RSpecについて勉強している者です。

 他の方のポートフォリオを拝見する機会がありまして、
「テストに書いてあるtraitって何だろう?」
と疑問に思ったため、現在理解している点についてアウトプットしました。
(「everyday rails - rspecによるrailsテスト入門」
を参考に勉強している途中です)

 間違っている点などありましたらご指摘よろしくお願いします。

traitの使い方

FactoryBotを用いて次のようなテストデータ(ファクトリ)を作成するとします。

spec/factories/user.rb
FactoryBot.define do
  factory :user do
    name = "Taro"
    name { name }
    email = "test1@example.com"
    email { email }
    password = "pass123"
    password { password }
  end
end

Userというモデルにnameカラム、emailカラム、passwordカラムが存在しますね。

ここで
「emailだけ『test2@example.com』になっているファクトリを新しく作りたいな」
と考えたとします。その時は「継承」という機能を使うと便利です。

spec/factories/user.rb
FactoryBot.define do
  factory :user do
    name = "Taro"
    name { name }
    email = "test1@example.com"
    email { email }
    password = "pass123"
    password { password }

    #新しいファクトリを定義
    factory :another_user do
      email = "test2@example.com"
      email { email }
    end
  end
end

 なぜ「継承」を利用するのかというと、「記述が重複してしまうことを防ぐため」です。
継承を利用しない場合、今回のような規模なら問題ありませんが、これがもっとファクトリ内のインスタンス数が多い中で、「新しいファクトリを定義したい」となった場合、非常に手間です。例えば、「10個のインスタンスがある内の1個だけ属性値を変えたい」という場合でも、その10個全てを再定義しないといけませんから?

 そこで、継承を使うことで、「emailだけ『test2@example.com』のファクトリを新しく作る」ということが可能になります(つまり、記述の重複がなくなる!)。

 しかし、複雑なオブジェクトを構築したいときや、テストデータに関する要件がもっと複雑になってきた時は、継承よりも「trait」の方が向いているそうです(こちらに関しては、現在勉強中です。こちらの方が、traitを使う本当の理由のようです)。

継承をtraitにしたい時は、次のように記述します。

spec/factories/user.rb
FactoryBot.define do
  factory :user do
    name = "Taro"
    name { name }
    email = "test1@example.com"
    email { email }
    password = "pass123"
    password { password }

    #新しいファクトリを定義
    trait :another_user do
      email = "test2@example.com"
      email { email }
    end
  end
end

 定義したファクトリをspecファイルで呼び出したい時は、以下のように記述します。

user_spec.rb
user = FactoryBot.create(:user(モデル名), :another_user(ファクトリ名))

 このtraitを使用することでも、「記述が重複してしまうことを防ぐこと」ができます。

 いかがでしたか?
 traitは、複雑なオブジェクトを構築したいときや、テストデータに関する要件がもっと複雑になってきた時にこそ輝くそうなので、今後理解できたら、また内容を更新したいと思います。

 ここまで読んでいただきありがとうございました。

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