- 投稿日:2020-11-24T23:56:56+09:00
【Rails】bcryptを使ってログイン機能を作ったけど失敗した話
Railsでログインページを作っていました。
bcyrptのGemを使って作ろうとしたのですが、正しいメールアドレス・パスワードを登録し、ログインしようとしても「ログインに失敗しました」と表示され、なかなかできず…環境
Rails 5.2.4.4
DB:SQLite問題のあったコード
・gemfileにてcryptをインストール済み
・userモデルに has_secure_password を定義
・usersテーブルに password_digest:string を追加users_controller.rbclass 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 enduser_sign_in(user)の部分はhelperで、
users_helper.rbdef user_sign_in(user) session[:user_id] = user.id endと書いています。
解決方法
paramsの後に[:user]を追加しました
users_controller.rbclass 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カラムに対応するようにしました。
- 投稿日:2020-11-24T23:32:27+09:00
Rails6 bootstrap
rails6にbootstrapをCDNで追加したところ、JSの動作不具合が多かった。下記の方法で実施すると不具合がなくなった為、備忘録として記述する。
参考URL: https://medium-company.com/rails-bootstrap/
Rails6標準の「yarn + webpacker」でBootstrapを導入
consoleyarn add jquery bootstrap popper.jsRails直下にある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.jsimport 'bootstrap' import '../stylesheets/application'app/javascript/stylesheets/application.scss@import '~bootstrap/scss/bootstrap';config/webpack/environment.js を以下のように修正し、jQueryとPopper.jsを利用できるよう
config/webpack/environment.jsconst { 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 = environmentapp/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へ。
- 投稿日:2020-11-24T22:49:41+09:00
自分で作成した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」でその道を選ぶという感じです。
- 投稿日:2020-11-24T22:24:30+09:00
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.ymlGemfile修正
pumaを本番環境に適応させる
Gemfilegroup :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' 削除 endpuma.rb作成
config/puma.rbthreads_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", truedatabase.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/DockerfileFROM 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 installcompose
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.prodFROM 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 testwebコンテナ
Dockerfile.prodFROM 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.confcontainers/db/nginx.confuser 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/535GitHub
ソースをgithubにあげているので良かったら参考にしてください
https://github.com/kinako555/redmine_managed_docker.git
- 投稿日:2020-11-24T22:15:18+09:00
ポートフォリオ作成記録 環境準備編
目的
ポートフォリオ作成の記録として、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のインストールができました。感想
あとは、問題なく準備ができました。
初投稿で、雑ですが数をこなして改善と挑戦を続けて記事を投稿し続けたいと思います。
- 投稿日:2020-11-24T22:04:47+09:00
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.rbclass 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 end3.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以上
- 投稿日:2020-11-24T21:19:53+09:00
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) → 削除は行われずトップページにリダイレクトする
コードは以下のとおりです。(該当箇所のみ抜粋しています。)
requestspecdescribe '#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 end1.
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 } end1.データは作成されていないため
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!を使う必要があります。
以上です。ありがとうございました!
- 投稿日:2020-11-24T21:18:26+09:00
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をインストールした際にイメージの再ビルドを行わなくて済むということのようです。
永続化の方法については上記のサイトがわかりやすいため参照ください。
- 投稿日:2020-11-24T21:03:53+09:00
[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: '田中') => truewhereメソッドと併用する方法
pry(main)> User.where(name: '田中').exists? => truepresent?メソッド
基本構文オブジェクト.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メソッド
基本構文オブジェクト.presencepresenceメソッドを使用したオブジェクトが存在すればそのオブジェクト自身を返し、存在しなければnilを返すメソッドです。
presenceメソッドはRailsのメソッドなので、Rubyで使おうとすると、エラーが出ます。使用例
app/model/user.rbclass User < ApplicationRecord def call name.presence || 'No Name' end endapp/controller/users_controller.rbclass UsersController < ApplicationController def show @user = User.call end endapp/views/users/show.html.erb<%= @user %>このように記載すると、@userのnameに値があればその値が表示され、なければ “No Name” と表示されます。
おわりに
とてもややこしいですね。
自分でまとめておきながら、混乱しています。
徐々に使っていって、手で覚えるしかないですね。
- 投稿日:2020-11-24T19:58:49+09:00
Ruby on Railsでの頻出単語(まとめ)
Ruby on Rails似て、よく出てくる単語の意味をまとめたものです。
辞書的に使っていただけると幸いです。[データベース関係]
rails g model テーブル名(はじめ大文字) カラム名:データ型
新しくテーブルとカラムを作るときに、ターミナルで使うrails g migration ファイル名
テーブルに新しくカラムを追加するときに、ターミナルで使うadd_column :テーブル名, :カラム名, :データ型
マイグレーションファイルに、新しいカラム名を追加するときに使うrails db:migrate
データベースに変更を加えるときに、ターミナルで使う
- 投稿日:2020-11-24T19:31:04+09:00
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 installGemをインストール
Gemfilegem "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:migratemodelを修正
追加したカラム名から
_id
を抜いた部分をattachmentとして設定app/models/user.rbclass 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.rbclass 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
に貼り付けてサーバーを再起動。
- 投稿日:2020-11-24T18:18:56+09:00
このプログラムの考え方が知りたい...[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」というように続きから出力する方法がわからない。
この問題はいろんなパターンがあると思うが、そもそもプログラミング脳になっていないので解けなかった。悔しい。
どなたか分かる方いらっしゃいましたらアドバイスください。。。以上!
- 投稿日:2020-11-24T16:55:12+09:00
【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を試してみる。
Gemfilegroup :development, :test do gem 'bullet' endターミナル$ bundleBulletは
config/environments/development.rb
に設定を追加しないと動作しないのでファイルを編集する。config/environments/development.rbRails.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.rbclass 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.rbclass PostsController < ApplicationController def show @user = @post.user # @comments = @post.comments # 編集前 @comments = @post.comments.includes([:user]) # 編集後 @comment = @post.comments.build end endN+1問題が検出されなくなるまで、メッセージが表示される箇所に一つずつincludesを追加。
なお、Bulletを使っている中でN+1の警告を無視したい場合は、ホワイトリスト形式でconfig/initializer/bullet.rb
に登録しておけば良いらしい。
今後いろいろ試してみたい。終わりに
以上の対策をした後、本番環境にアクセスしてみるとパフォーマンスが上がってびっくり!動作が早い早い!!
ユーザー側に立つと、パフォーマンスが遅いとそれだけでストレスだし、そのサービスを使いたいと思えないもの。
今後は、SQLの実行回数をいかに減らせるかを意識してコードを書いていこうと思う!
引き続き、SQL周りの知識も深めていきたい。初学者のため、理解不足な点が多々あると思います。気になった点やアドバイスなどありましたら、教えていただけますと幸いです!
参考サイト
・ポートフォリオのバージョンアップ2020 7/2 (N + 1 問題)
- 投稿日:2020-11-24T15:43:16+09:00
【脱初心者!!】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.rbclass 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.rbclass Reservation < ApplicationRecord scope :future_reservations, -> { where("start_time > ?", Date.today).order(start_time: :desc) }解説するとscopeでまず定義しひとまずfuture_reservationsでメソッドを定義します。
その後はcontrollerの記述をリファクタリングするだけです!!ちなみに
reservation.rbscope :future_reservations, -> do where("start_time > ?", Date.today).order(start_time: :desc) endコードが長くなる場合はこちらの方が綺麗なので覚えておきましょう!!
reservations.controller.rbdef index @reservations = Reservation.paginate(page: params[:page]) @reservations_index = @reservations.future_reservations end最後にcontrollerにメソッドを書いてすっきりさせましょう!
お疲れ様でした!!!
- 投稿日:2020-11-24T14:53:11+09:00
[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 Relationshipcreate_relationship.rbclass 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.rbclass 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: :followerhas_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.rbdef 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.rbclass 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 endviewファイルにフォローボタンを作成する
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をフォローしている初めてフォロー機能に触れた時、「する」「される」の関係がわからなくなり、アソシエーションの中身がぐちゃぐちゃになっていた。解決するのにだいぶ時間を使ってしまった。
モデルの名義やそもそものやり方は人それぞれなので、自分が理解できるやり方を見つけるのが一番だと感じた。
- 投稿日:2020-11-24T13:43:29+09:00
ゲストログイン機能でたまに、パスワードエラーが出ていたが、解決した
はじめに
先日、ゲストログイン機能を実装したが、数回に一度エラーが出てしまうことがわかり、原因を追求した。
ゲストログイン機能実装について
私が以前投稿した、「ゲストログイン機能の実装方法」はこちら
エラーの原因
パスワードについてのエラーで、バリデーションによって弾かれていたので、この部分と予測。
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'引数に指定すれば、バリデーションで字数に制限をかけていた場合でも、エラーにならない!!
- 投稿日:2020-11-24T11:55:25+09:00
背景色を透過する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; }サイドバー背景には、個人的にお気に入りのキレイな画像を使用しました。
せっかくなので、重ねた要素は透過して背景画像を生かしたいと思います。開発環境
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
プロパティはその子孫要素にも透明度が適用される点に注意が必要です…!〜文字はそのまま、背景のみ透過したい!!〜
そんな時は…
rgba()
を使用します!rgba
CSS3で追加された、RGBAカラーモデルでカラーと透明度を指定出来る値です。.sample{ color: rgba(0, 0, 0, 0.5); }ここで注意が必要なのが、rgbaそのものはプロパティではなく、あくまで値ということです。
background-color
やbox-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); }文字はそのままで、無事に背景の透過が出来ました!
終わりに
背景色透過のプロパティはアプリ制作で多用すると思い、今回記事にまとめてみました。
初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。参考記事
要素の透明度の指定(opacity)
カラーの透明度の値(rgba, transparent)
【CSS3リファレンス】opacity
【CSS3リファレンス】rgba()
カラーコードの一覧表(色を調べる/色を作る)
- 投稿日:2020-11-24T09:29:53+09:00
スクール5週目 応用力の根底
今週は、応用カリキュラムも終盤に差し掛かり、課題図書(レスポンシブWebデザイン、Ruby応用、正規表現)を主に学びました。
プログラミングの奥深さを感じ取れた週になりました。【学習内容】
・サーバーの基礎知識
・レスポンシブWebデザイン
・Ruby応用
・正規表現サーバーの基礎知識
まず、サーバーの必要性、役割を学びます。その上でIPアドレス、WANとLAN、ドメインとDNS、ポート、WEBサーバー、HTTPの基礎を学習します。
次にサーバ向けによく利用されているOSであるLinuxについて学びます。Linuxの特徴としては、以下の点が挙げられます。
①オープンソースであり、誰でも自由に無償で利用できる
②品質の高い多くのソフトウェアが利用できる
③世界中でサーバ用途として広く利用されているため信頼性が高い
④操作を自動化するための仕組みが用意されており、サーバの運用が行いやすいそして次にSSHについて学習。SSHとは、暗号や認証の技術を利用して、安全にリモートコンピュータと通信するためのプロトコルのことです。利用するメリットとしては以下の点が挙げられます。
①通信を盗聴される危険性を回避して、安全に操作ができる
②サーバ上でのファイル操作、設定ファイルの編集ができる
③データのバックアップをしたいとき、サーバ上で圧縮(ZIP)して、ファイルの一括ダウンロードできるので、アップロード・ダウンロードの大量ファイルの時間短縮ができるレスポンシブWebデザイン
PCやタブレット、スマホなど様々なデバイスの大きさに対応し、閲覧しやすいデザインのWebサイトを作成する方法を学びました。
ユーザーがWEBサイトを閲覧する際、スマホで閲覧する比率は6割〜7割を超えると言われているそうです。そのため、WEB開発においても、スマホやタブレットでの閲覧を想定した見た目づくりがとても重要になってくるそうです。実際に私もボランティア団体のHPを作った際はスマホでの見え方を気にしながら作業をしていたので、レスポンシブの考え方は非常に常用だと感じました。
Ruby応用
これまで、Rubyの条件分岐にはif文を用いました。
しかし、Rubyにはif文以外にも条件分岐を表現する文法としてcase文があります。case文とは、条件分岐を表現するための文法です。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができます。並列する条件が多数ある場合は、if文よりもcase文を使った方がコードとして読みやすくなります。つまり、可読性を高められることだと思いました。
Sample.rbcountry = "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.rbnumber = 0 while number <= 10 puts number number += 1 end正規表現
今までさまざまなアプリケーションに触れてきた中で、「パスワードが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”>振り返り・感想
やはりプログラミングは暗記していけないと痛感させられました。なぜなら、passwordの英数字混合の判断だけでも一見どんな正規表現なのかが分かりません。そのため、プログラミングは暗記するのではなく、調べて理解して応用させることが大事だと改めて思いました。
また、コメントつけて上げると、後から誰かが見返しても丁寧で分かりやすいですし、テストコードの際に役立つと思いました。
- 投稿日:2020-11-24T09:29:53+09:00
スクール5週目
今週は、応用カリキュラムも終盤に差し掛かり、課題図書(レスポンシブWebデザイン、Ruby応用、正規表現)を主に学びました。
プログラミングの奥深さを感じ取れた週になりました。【学習内容】
・サーバーの基礎知識
・レスポンシブWebデザイン
・Ruby応用
・正規表現サーバーの基礎知識
まず、サーバーの必要性、役割を学びます。その上でIPアドレス、WANとLAN、ドメインとDNS、ポート、WEBサーバー、HTTPの基礎を学習します。
次にサーバ向けによく利用されているOSであるLinuxについて学びます。Linuxの特徴としては、以下の点が挙げられます。
①オープンソースであり、誰でも自由に無償で利用できる
②品質の高い多くのソフトウェアが利用できる
③世界中でサーバ用途として広く利用されているため信頼性が高い
④操作を自動化するための仕組みが用意されており、サーバの運用が行いやすいそして次にSSHについて学習。SSHとは、暗号や認証の技術を利用して、安全にリモートコンピュータと通信するためのプロトコルのことです。利用するメリットとしては以下の点が挙げられます。
①通信を盗聴される危険性を回避して、安全に操作ができる
②サーバ上でのファイル操作、設定ファイルの編集ができる
③データのバックアップをしたいとき、サーバ上で圧縮(ZIP)して、ファイルの一括ダウンロードできるので、アップロード・ダウンロードの大量ファイルの時間短縮ができるレスポンシブWebデザイン
PCやタブレット、スマホなど様々なデバイスの大きさに対応し、閲覧しやすいデザインのWebサイトを作成する方法を学びました。
ユーザーがWEBサイトを閲覧する際、スマホで閲覧する比率は6割〜7割を超えると言われているそうです。そのため、WEB開発においても、スマホやタブレットでの閲覧を想定した見た目づくりがとても重要になってくるそうです。実際に私もボランティア団体のHPを作った際はスマホでの見え方を気にしながら作業をしていたので、レスポンシブの考え方は非常に常用だと感じました。
Ruby応用
これまで、Rubyの条件分岐にはif文を用いました。
しかし、Rubyにはif文以外にも条件分岐を表現する文法としてcase文があります。case文とは、条件分岐を表現するための文法です。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができます。並列する条件が多数ある場合は、if文よりもcase文を使った方がコードとして読みやすくなります。つまり、可読性を高められることだと思いました。
Sample.rbcountry = "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.rbnumber = 0 while number <= 10 puts number number += 1 end正規表現
今までさまざまなアプリケーションに触れてきた中で、「パスワードが6文字未満だったり、メールアドレスに@が入っていなかったりするとユーザー登録ができないのは、どのように判断されているのだろう?」 と疑問に思っていました。これらはすべて正規表現という技術を用いて実装されていたことをここで、初めて知りました。
正規表現とは、文字列の一部分を抽出・置換したり、文字列が制約を満たしているかを調べるための表現方法です。
例えば…
・電話番号からハイフンを取り除く
・パスワードに英数字8文字以上という制約
・メールアドレスからドメインの部分のみ抽出
・全角かな/カナ漢字の区別する
・passwordの英数字混合の判断他にもたくさんの正規表現があることを学びました。
最後のpasswordの英数字混合の判断する場合、以下の記述になります。Sample.rbirb(main):001:0> password = "T2o0k2y0o" irb(main):002:0> password.match(/\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i) => #<MatchData “T2o0k2y0o”>振り返り・感想
やはりプログラミングは暗記していけないと痛感させられました。なぜなら、passwordの英数字混合の判断だけでも一見どんな正規表現なのかが分かりません。そのため、プログラミングは暗記するのではなく、調べて理解して応用させることが大事だと改めて思いました。また、コメントつけて上げると、後から誰かが見返しても丁寧で分かりやすいですし、テストコードの際に役立つと思いました。
- 投稿日:2020-11-24T02:16:50+09:00
【RSpec】traitの使い方を整理してみた
最近RSpecについて勉強している者です。
他の方のポートフォリオを拝見する機会がありまして、
「テストに書いてあるtraitって何だろう?」
と疑問に思ったため、現在理解している点についてアウトプットしました。
(「everyday rails - rspecによるrailsテスト入門」
を参考に勉強している途中です)間違っている点などありましたらご指摘よろしくお願いします。
traitの使い方
FactoryBotを用いて次のようなテストデータ(ファクトリ)を作成するとします。
spec/factories/user.rbFactoryBot.define do factory :user do name = "Taro" name { name } email = "test1@example.com" email { email } password = "pass123" password { password } end endUserというモデルにnameカラム、emailカラム、passwordカラムが存在しますね。
ここで
「emailだけ『test2@example.com』になっているファクトリを新しく作りたいな」
と考えたとします。その時は「継承」という機能を使うと便利です。spec/factories/user.rbFactoryBot.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.rbFactoryBot.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.rbuser = FactoryBot.create(:user(モデル名), :another_user(ファクトリ名))このtraitを使用することでも、「記述が重複してしまうことを防ぐこと」ができます。
いかがでしたか?
traitは、複雑なオブジェクトを構築したいときや、テストデータに関する要件がもっと複雑になってきた時にこそ輝くそうなので、今後理解できたら、また内容を更新したいと思います。ここまで読んでいただきありがとうございました。