- 投稿日:2021-01-16T22:49:18+09:00
carrierwaveでpdfファイル投稿機能をつける
はじめに
前回書いた記事でも触れたのですが、carrierwaveを用いてRailsアプリにpdf投稿機能をつけたのでその際に行った手順を備忘録として残しておきます。
carrierwaveの導入
gemfileに、下の1行を加えてbundle installします。
gemfilegem 'carrierwave'dbにfileカラムを追加
今回、送信されたデータをdbに保存するために、元々作っていたpostsテーブルにfileカラムを追加しました。マイグレーションファイルを作ってカラム追加しただけなのでコマンドは割愛します。
carrierwaveクラスの作成
carrierwaveを使うには、専用のクラスを作る必要があります。使い方の概要は以下の記事を参考にしました。
https://pikawaka.com/rails/carrierwave以下、上記ページとほぼ同じではありますが自分が行った手順をまとめます。
bin/rails g コマンドでcarrierwaveクラスを生成
terminalbundle exec rails g uploader アップローダー名以下の場所 app/uploaders/アップローダー名_uploader.rb に作られた設定ファイルで次のように記述します。
app/uploaders/アップローダー名_uploader.rbclass アップローダークラス < CarrierWave::Uploader::Base #保存場所の設定 storage :file #保存するフォルダの設定 def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end end今回はpublic配下のuploadsフォルダ内に投稿されたpdfファイルを保存します。デプロイの際は外部のストレージサービスへの指定にしたりもできるようです。
アップローダークラスとモデルの紐付け
クラスと関連づけたいモデルで、以下の記述をします。
モデル名.rbclass モデル名 < ActiveRecord::Base mount_uploader :関連付けたいカラム名, アップローダークラス endフォームにfile_fieldを追加&dbに保存
パラメーターでfile_fieldの情報をfileカラムに保存するようにしました。(保存場所はご自分の任意の保存したいカラムで構いません)コントローラーでいつも通り記述するだけなので割愛します。
viewで表示する
viewで次の記述をします。
app/views/任意のview<object data="<%= @post.file.url %>" type="application/pdf" width="200" height="300"></object>@postはこのviewをレンダリングするアクションで定義したpostモデルのインスタンス変数で、fileというカラムを持っています。そこに、carrierwaveで生成したアップローダークラスが持つurlメソッドを用いて、ファイルまでのurlを指定しています。(自分で設定したモデル、カラム名に適宜置き換えてください。) width, height で表示するサムネイルのサイズを指定できます。
以上で表示できました!
終わりに
サムネイル生成の方法はobjectを使うもの以外もあるようなので気になる方は調べてみるとよいかもしれません。僕はサムネイルがなかなか実装できずcarrierwaveのエラーだと思っていたらform_withの指定ミスだったので、もし詰まっている方はそちらも確認してみるとよいかもです。
- 投稿日:2021-01-16T22:30:50+09:00
RubyなリポジトリのGitHub ActionsでCoverallsを使う
はじめに
Travis CIからGitHub Actionsに移行する際に、GitHub ActionsでSimpleCovの結果をCoverallsに送信する方法を調べました。simplecov-lcovを使用して、code coverageをlcov形式で出力し、それをCoverallsに送るという感じでした。
SimpleCovとsimplecov-lcovのインストール
Gemfileに以下の二行を追加して、
bundle install
しました。gem 'simplecov', '~> 0.21' gem 'simplecov-lcov', '~> 0.8'
spec/spec_helper.rb
の先頭に以下を追加します。Coverallsにcoverageを送る場合に重要になるのは、SimpleCov::Formatter::LcovFormatter
に関係する部分です。require 'simplecov' require 'simplecov-lcov' SimpleCov::Formatter::LcovFormatter.config do |config| # Coverallsはデフォルトではcoverage/lcov.infoの結果を送信する config.report_with_single_file = true config.single_report_path = 'coverage/lcov.info' end SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::HTMLFormatter, # formatterにSimpleCov::Formatter::LcovFormatterを加える SimpleCov::Formatter::LcovFormatter ]) SimpleCov.startCoveralls GitHub Actions
https://github.com/marketplace/actions/coveralls-github-action に、CoverallsのGitHub Actionsがあります。
Use latent version
ボタンを押すと表示されるモーダルの二行をコピーして、Workflowのymlに追記します。Workflowの例
code coverageをCoverallsに送信するために
.github/workflows/coverage.yml
というworkflowファイルを用意しました。重要なのは、前節でコピーした最後の- name: Coveralls GitHub Action
からの部分です。github-token
は必須のオプションになりますが${{ secrets.GITHUB_TOKEN }}
としておけばOKです。name: coverage on: [push, pull_request] jobs: coverage: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: ruby-version: '2.7' - name: Build and test with Rake run: | gem install bundler bundle install bundle exec rake - name: Coveralls GitHub Action uses: coverallsapp/github-action@v1.1.2 with: github-token: ${{ secrets.GITHUB_TOKEN }}必要かどうかちょっと謎ですが、一行だけの
.coveralls.yml
を用意しました。service_name: github-ciあとはこれらをcommitしてpushすれば、workflowが動きだして、Coverallsに結果が送られます。あらかじめ、Coveralls側でリポジトリの連携をONにしておいてください。
おわりに
CoverallsのGitHub Actionのexampleが、Node.jsのものだったので、Rubyは対応してないのかな〜と思っていたのですが、ちゃんと調べたら簡単でした。Travis CIの頃は、coveralls gemを使っていました。coveralls gemでは、SimpleCovのバージョンにしばりがあるので、今回の様なsimplecov-lcovを使った方法の方が自由で良いですね。
- 投稿日:2021-01-16T22:27:21+09:00
[ruby]ヒアドキュメントについて
*追記
”実はputsやprint意外にも文字列を出力する方法があります
それがヒアドキュメントです
しかもただ単に出力するのではなく、空白や改行も含める長い文章が出力できる様になります
rubyだと改行した瞬間文の終了とみなされますがヒアドキュメントを利用すると・・・”
とご指摘があり、間違った解釈をしておりました
ありがとうございますヒアドキュメント
長文を識別子で囲む事によって長文も保存できる様になれる
irb(main):033:0> puts = <<hia irb(main):034:0" ruby irb(main):035:0" most like irb(main):036:0" Programming language irb(main):037:0" hia => "ruby\nmost like\nProgramming language\n"解説しますとまず
1行目にputsと言う変数にhiaと言う識別子で開始ラベル
4行目で終了ラベルである識別子を入れてあげると2~3行目の内容を変数putsに格納してくれる
6行目には格納された内容が表示されるirb(main):038:0> puts puts ruby most like Programming language => nilもちろんputs 変数(puts)とすると先ほど変数に格納された文字列を出力できる
ちなみに変数に文字列や数字を前もって代入した状態でもヒアドキュメントは式展開で出力できる
irb(main):059:0> name = "タケシ" => "タケシ" irb(main):060:0> age = 12 => 12 irb(main):062:0> city = "神奈川" => "神奈川" irb(main):063:0> text = <<EOF irb(main):064:0" 私の名前は#{name}です irb(main):065:0" 年齢は#{age} irb(main):066:0" 出身は#{city} irb(main):067:0" EOF => "私の名前はタケシです\n年齢は12\n出身は神奈川\n" irb(main):069:0> puts text 私の名前はタケシです 年齢は12 出身は神奈川と今回はヒアドキュメントについて勉強しアウトプットしました
- 投稿日:2021-01-16T21:26:04+09:00
formオブジェクトでタグ付け機能を実装
実装の流れ
tagモデルと中間テーブルを作成
投稿とタグは多対多の関係になるため、中間テーブルを作成します。
投稿のモデルを作成します。
$ rails g model item name:string user:referencesタグモデルを作成します。
$ rails g model tag word:string中間テーブルを作成します。
$ rails g model item_tag_relation item:references tag:referencesマイグレーションファイルを確認します。
db/migrate/create_items.rbclass CreateItems < ActiveRecord::Migration[6.0] def change create_table :items do |t| t.string :name t.references :user, foreign_key: true t.timestamps end end enddb/migrate/create_tags.rbclass CreateTags < ActiveRecord::Migration[6.0] def change create_table :tags do |t| t.string :word t.timestamps end end enddb/migrate/create_item_tag_relations.rbclass CreateItemTagRelations < ActiveRecord::Migration[6.0] def change create_table :item_tag_relations do |t| t.references :item, foreign_key: true t.references :tag, foreign_key: true t.timestamps end end endunique: trueで同じタグ名を登録しないようにできますが、うまく実装できなかったので別の方法で一意性を持たせました。
$ rails db:migrate
モデルの関連付けとバリデーション
app/models/item.rbclass Item < ApplicationRecord has_many :item_tag_relations has_many :tags, through: :item_tag_relations, dependent: :destroy belongs_to :user end「has_many :tags, through: :item_tag_relations」の記述にによって、item_tag_relationsモデルを通してアイテムに紐づくタグを取得します。
app/models/tag.rbclass Tag < ApplicationRecord has_many :item_tag_relations, dependent: :destroy has_many :items, through: :item_tag_relations validates :word, uniqueness: true end「validates :word, uniqueness: true」の記述によって、タグの名前が重複して登録されるの防ぎます。(何故かうまく働きませんでした...)
app/models/item_tag_relation.rbclass ItemTagRelation < ApplicationRecord belongs_to :item belongs_to :tag end
ルーティングを設定
config/routes.rbRails.application.routes.draw do root to: 'items#index' resources :items end
formオブジェクトの作成
Formオブジェクトは、1つのフォーム送信で複数のモデルを更新するときに使用するツールです。自分で定義したクラスをモデルのように扱うことができます。
modelsディレクトリ配下に「app/models/item_tag.rb」ファイルを作成します。
app/models/item_tag.rbclass ItemTag include ActiveModel::Model attr_accessor :name, :user_id, :item_id, :tag_ids endActiveModel::Modelをincludeすることで、そのクラスのインスタンスはActiveRecordを継承したクラスのインスタンスと同様に form_with や render などのヘルパーメソッドの引数として扱えたり、バリデーションの機能が使えるようになります。
attr_accessorで使用したいカラム名をセットします。
続いて、フォームからパラメーターとして送られてきた情報をテーブルに保存する処理を追加します。
app/models/item_tag.rbclass ItemTag include ActiveModel::Model attr_accessor :name, :user_id, :item_id, :tag_ids def save @item = Item.create(name: name, user_id: user_id) tag_list = tag_ids.split(/[[:blank:]]+/).select(&:present?) tag_list.each do |tag_name| @tag = Tag.where(word: tag_name).first_or_initialize @tag.save unless ItemTagRelation.where(item_id: @item.id,tag_id: @tag.id).exists? ItemTagRelation.create(item_id: @item.id, tag_id: @tag.id) end end end endアイテムの情報を保存し「@item」という変数に代入しています。
tag_list = tag_ids.split(/[[:blank:]]+/).select(&:present?)は入力フォームのf.text_fieldから送られたタグをtag_idsとしてparamsで送信します。
そして、split(/[[:blank:]]+/)によってtag_ids内の文字列を空白で区切り、バラバラの単語にして配列に入れていきます。
最後に、select(&:present?)は、配列化した値をそれぞれpresent?メソッドで判定して、真であれば取り出します。
@tag = Tag.where(word: tag_name).first_or_initializeで新規タグか既存タグかの判別をします。
判別をして既存タグなら既存のidを使用。新規ならidを生成します。
unless ItemTagRelation.where(item_id: @item.id,tag_id: @tag.id).exists?は今回重複したタグを保存できないようにしたかったのですが、バリデーションやマイグレーションファイルに一意性を持たせても保存されてしまったので、苦肉の策でモデルにて対処しました。
ItemTagRelation.where(item_id: @item.id,tag_id: @tag.id).exists?で、中間テーブルであるitem_tag_relationモデルの投稿に対して、同じ名前のタグが存在していないかを.exists?で判定しています。
タグが重複した場合はtureになるのでunlessで条件式をかけています。
controllerの処理
$ rails g controller itemsapp/controllers/items_controller.rbdef new @item = ItemTag.new end def create @item = ItemTag.new(itemtags_params) if @item.valid? @item.save redirect_to items_path(@item) else render :new end end private def itemtags_params params.require(:item_tag).permit(:name, :text, :image, :tag_ids).merge(user_id: current_user.id) endformオブジェクトに対してnewメソッドを使用しています。
viewの作成
関連する所のみ記述します。
app/views/items/new.html.erb<%= form_with model: @item, url: items_path, local: true do |f| %> <%= f.text_field :tag_ids %> <%= f.submit "投稿する" %>tag_idsはitemモデルで「 has_many :tags, through: :item_tag_relations」の関連付けをすることよってアイテムオブジェクトに使用できるようになります。
ひとまずこれで、タグ付け機能を実装できます。
参考にさせていただいた記事
【Ruby on Rails】タグ検索機能を実装してみた
https://qiita.com/E6YOteYPzmFGfOD/items/177f18e706df05f9b42e初心者が手探りで Rails のタグ付機能を gem なしで実装してみる
https://qiita.com/ryutaro9595/items/042a1ec713c8c1f2c1d6rails 投稿記事にタグをつける機能を実装する。
https://shirohige3.hatenablog.com/entry/2020/11/08/013327
- 投稿日:2021-01-16T21:25:49+09:00
【CircleCI2.0】JavaScript に対応するように設定
はじめに
CircleCI で JavaScript を扱えるように設定しました。
今まで問題なく動作していたCircleCI
のテストでしたが、あることをしてから通らなくなったので対処方法を記していきます。今までの設定ファイルはこちらです。
目次
環境
問題
原因
- RSpecで
js: true
を使用- 今回 ローカル環境と CircleCI環境 をあわせるために行った変更
解決策
spec/support/capybara.rb
- 変更前
- 変更後
spec/rails_helper.rb
Dockerfile
.circleci/config.yml
.circleci/config.yml 全体
config/database.yml.ci
まとめ
おわりに
参考文献
環境
- Ruby: 2.6.6
- Bundler: 2.0.2
- MySQL: 8.0
- CircleCI: 2.0
問題
SocketError: Failed to open TCP connection to chrome:4444 (getaddrinfo: No address associated with hostname) # chrome:4444へのTCP接続のオープンに失敗SocketError: getaddrinfo: No address associated with hostname # ホスト名に関連付けられたアドレスはありません原因
RSpecでjs: trueを使用
RSpecで JavaScriptのテストを実装 したところローカル環境で通っていたテストがCircleCIの自動テストでは通りませんでした。
結論として、ローカル環境とCircleCIの環境の差異が原因かと思います。
ローカルではchromeコンテナを立ち上げてSystem Spec
を通していましたが、CircleCI の設定ファイル
でも設定が必要なのでエラーが起きていました。今回ローカル環境とCircleCI環境をあわせるために行った変更
- chromeコンテナ から Dockerfileで chrome を追加するようにしたこと
- Chrome使用時のURLを指定していたので削除したこと
CircleCI の設定ファイル
で Chrome の追加と background で使用するようにしたこと解決策
spec/support/capybara.rb
大幅に変更したように見えますが、
url
を指定していないというのがポイントです。変更前require 'capybara/rspec' require 'selenium-webdriver' module CapybaraSupport Capybara.javascript_driver = :selenium_chrome_headless Capybara.default_driver = :selenium_chrome_headless Capybara.register_driver :selenium_chrome_headless do |app| url = 'http://chrome:4444/wd/hub' caps = ::Selenium::WebDriver::Remote::Capabilities.chrome( 'goog:chromeOptions' => { 'args' => [ 'no-sandbox', 'headless', 'disable-gpu', 'window-size=1680,1050' ] } ) Capybara::Selenium::Driver.new(app, browser: :chrome, url: url, desired_capabilities: caps) end変更後require 'capybara/rspec' require 'selenium-webdriver' Capybara.register_driver :selenium_chrome_headless do |app| options = ::Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--window-size=1400,1400') driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) end Capybara.javascript_driver = :selenium_chrome_headlessspec/rails_helper.rb
RSpec.configure do |config| config.before(:each, type: :system) do driven_by :rack_test end config.before(:each, type: :system, js: true) do driven_by :selenium_chrome_headless end endDockerfile
ローカル環境のテストで Chrome を使用したいので
Dockerfile
で Chromeの追加 をしています。
この記述をRUN mkdir /myapp
の前に書きました。RUN apt-get update && apt-get install -y unzip && \ CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ unzip ~/chromedriver_linux64.zip -d ~/ && \ rm ~/chromedriver_linux64.zip && \ chown root:root ~/chromedriver && \ chmod 755 ~/chromedriver && \ mv ~/chromedriver /usr/bin/chromedriver && \ sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get update && apt-get install -y google-chrome-stable # ルート直下にmyappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ) RUN mkdir /myapp WORKDIR /myapp.circleci/config.yml
CircleCIの設定ファイル。Chromeを追加 することで ローカル環境との差異 をなくすようにしました。
steps: - run: name: Chrome Driver Install command: | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt-get update && sudo apt-get install -y unzip wget -N http://chromedriver.storage.googleapis.com/87.0.4280.88/chromedriver_linux64.zip -P ~/ unzip ~/chromedriver_linux64.zip -d ~/ rm ~/chromedriver_linux64.zip sudo chown root:root ~/chromedriver sudo chmod 755 ~/chromedriver sudo mv ~/chromedriver /usr/bin/chromedriver sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -' sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' sudo apt-get update && sudo apt-get install -y google-chrome-stable background: true.circleci/config.yml 全体
RSpecを動かすところで
no_output_timeout: 15m
と記述しています。
こちらはなくともテストには直接関係ないので削除しても大丈夫です。なにも設定しなければ標準で10分の制限時間があり、それを過ぎるとタイムアウトエラーになってしまうので念の為付けております。テストの数とかにもよると思いますが、実際にはほんの数分で終わるので不要です。
version: 2 jobs: build: docker: - image: circleci/ruby:2.6.6-node-browsers environment: - BUNDLER_VERSION: 2.0.2 - RAILS_ENV: 'test' - image: circleci/mysql:8.0 command: [--default-authentication-plugin=mysql_native_password] environment: - MYSQL_USER: root - MYSQL_DB: ci_test - MYSQL_ROOT_HOST: "127.0.0.1" working_directory: ~/myapp steps: - run: name: Chrome Driver Install command: | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt-get update && sudo apt-get install -y unzip wget -N http://chromedriver.storage.googleapis.com/87.0.4280.88/chromedriver_linux64.zip -P ~/ unzip ~/chromedriver_linux64.zip -d ~/ rm ~/chromedriver_linux64.zip sudo chown root:root ~/chromedriver sudo chmod 755 ~/chromedriver sudo mv ~/chromedriver /usr/bin/chromedriver sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -' sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' sudo apt-get update && sudo apt-get install -y google-chrome-stable background: true - checkout - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} - v1-dependencies- - run: name: install dependencies command: | gem install bundler -v 2.0.2 bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: key: v1-dependencies-{{ checksum "Gemfile.lock" }} paths: - ./vendor/bundle - run: mv config/database.yml.ci config/database.yml - run: yarn install - run: bundle exec rake db:create - run: bundle exec rake db:schema:load - run: name: run tests command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec \ --format RspecJunitFormatter \ --out /tmp/test-results/rspec.xml \ --format progress \ $TEST_FILES no_output_timeout: 15m - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-resultsconfig/database.yml.ci
test: adapter: mysql2 encoding: utf8 pool: 5 username: 'root' port: 3306 host: '127.0.0.1' database: ci_testまとめ
Dockerfile
とCircleCIの設定ファイル
で Chromeを追加- ローカルで通用してもCircleCI上で失敗することがある。
- ありがたいことに記事(情報)がたくさんあるので、その中で自分がどの問題に直面しているのかを理解することが大事。
おわりに
前回の記事に変更前の
CircleCIの設定ファイル
があります。(厳密に言えば少しリファクタなどしています)
JavaScript適用前 ですが、CircleCI
についてまとめていますのでよろしければご覧ください。参考文献
- 投稿日:2021-01-16T21:09:37+09:00
Rubyメモ
- 投稿日:2021-01-16T19:31:40+09:00
【Ruby】配列の要素を交互に取得する。
目的
配列の要素を交互に取得し、二つの配列を作成します。
色々なやり方があるとは思いますが、私はこのような方法をとりました。方法
array = ['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る', 'を'] array_A = [] array_B = [] array.each_with_index{|kana, index| index.even? ? array_A << kana : array_B << kana} p array_A # => ["い", "は", "ほ", "と", "り", "る"] p array_B # => ["ろ", "に", "へ", "ち", "ぬ", "を"]使用したメソッド
メソッド 意味 each_with_index 各要素とそれに付随する添字(インデックス)に対し処理を繰り返す even? 要素が偶数であればtrueを返す ? : 条件演算子(三項演算子) if・else文を1行で書いたもの << 配列に要素を追加する 解説
まず、空の配列を準備します。
array_A = [] array_B = []この中に元の配列
array
から交互に要素を入れていきます。
そのために使ったメソッドがeach_with_index
です。each_with_index
このメソッドは繰り返しを行う
each
メソッドに変数として各要素のインデックスを使用できるようにしたものです。['い', 'ろ', 'は'].each_with_index{|kana, index| p "#{kana} - #{index}"} # => "い - 0" # "ろ - 1" # "は - 2"配列
array
の各要素とインデックスは,
0 1 2 3 4 5 6 7 8 9 10 11 い ろ は に ほ へ と ち り ぬ る を となっているため、これを利用して、インデックスが偶数の時には
array_A
に、奇数の時にはarray_B
に要素を入れていきます。条件分岐には条件演算子を使用しました。
? :
?
の前の条件で分岐し、:
の前後で、trueの場合とfalseの場合それぞれの処理を分けています。num = 1 num == 1 ? "これは 1 です" : "これは 1 ではありません" # => "これは 1 です"これは、
num = 1 if num == 1 "これは 1 です" else "これは 1 ではありません" end # => "これは 1 です"と同じ意味になります。
そして、条件の偶数であるかどうかを確認するため、
even?
メソッドを使用しました。even?
このメソッドはレシーバ(.の左側)が偶数であれば
true
を、奇数であればfalse
を返してくれます。2.even? # => true 1.even? # => false # 0も偶数です 0.even? # => trueちなみに、演算子を使用した計算でも同じことができます。
num = 2 num % 2 == 0 # => trueある数を2で割った時、余りが0であれば偶数です。
even?
でインデックスが偶数か奇数かを確認し、? :
で条件分岐したあとは、
<<
で配列に要素を足しています。<<
<<
は配列の末端に要素を一つ追加します。num_array = [1, 2] num_array << 3 p num_array # => [1, 2, 3]最後に
さらに良い方法がございましたら、ご教示いただけますと幸です。
ご覧いただき、ありがとうございました。
- 投稿日:2021-01-16T19:07:03+09:00
Deviceの導入に関してのまとめ
※アプリケーション作成からDeviseの導入までをメモがわりに記入しています。
前提
ruby 6.0.0
rails 6.0.3.4
mysql2 0.5.3
devise 4.7.3上記の環境で作成する。
簡易的な流れ
①作りたいアプリケーションを立ち上げます。
コンソール% rails _6.0.0_ new アプリ名 -d mysql②アプリ内のdatabase.ymlの記述を書き換えます。
config/database.yml#中略 default: &default adapter: mysql2 # ↓↓元々はencoding: utf8mb4 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sock #以下略③gemfile内の最下層に追加したいgemを記入します。
Gemfile#中略 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'pry-rails' gem 'devise'④gemを読み込ませます。
コンソール% bundle installコンソール% yarn install⑤コンソール上の作成したアプリのディレクトリでデータベースを作成します。
コンソール% rails db:create⑥アプリにDeviseをインストールします。
コンソール% rails g devise:install⑦Deviseを用いたモデルの生成をします。
(マイグレーションファイルやルーティングも自動生成されます。)コンソール# モデル名は単数 % rails g devise モデル名⑧新規登録とログインのためのビューファイルを作成します。
コンソール% rails g devise:views⑨ローカルサーバーを起動します。
コンソール% rails s⑩マイグレーションファイルに追加するカラムを記述します。
※emailとpasswordについてはdeviseで生成した時に作成されています。11.テーブルをマイグレーションします。
コンソール% rails db:migrate12.各編集・設定を行います。
view/devise/registrations →新規登録画面の編集
view/devise/sessions →ログイン画面の編集
model/テーブル名.rb →バリデーションの設定13.application_controller.rbを編集します。
controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters # keysの内容はdeviseのテーブルに追加したカラム名 devise_parameter_sanitizer.permit(:sign_up, keys: [:カラム1, :カラム2, ‥]) end end以上
その他注意点など
・新規登録ページ、ログインページに至るためのルーティングは、モデル作成時に自動で生成されています。
・ユーザー管理機能に必要なコントローラーの処理は裏側に隠れているため、コントローラーを統括している「application_controller.rb」に対して記述することになります。
・「rails g devise:install」がうまくいかない時は、以下を実行してみましょう。
コンソール$ bundle exec spring stop
- 投稿日:2021-01-16T18:41:27+09:00
ruby色々なメソッド(数値編)
始めに
皆さん、こんにちは
私は某スクールに通っており現在転職活動中ですrubyのメソッドやクラス、オブジェクト指向について不足しているなあと感じ今一度復習の為勉強しております
今回は数値クラスのメソッドについてまとめました数値クラスのメソッド
※変数(num)には2.9を代入しております
- 変数.round・・・四捨五入して整数にする
irb(main):005:0> num.round => 3
- 変数.floor・・・切り下げ(て整数に変換)
irb(main):006:0> num.floor => 2
- 変数.ceil・・・切り上げ(て整数に変換)
irb(main):007:0> num.ceil => 3
- 変数.truncate・・・小数点の切り捨て
irb(main):008:0> num.truncate => 2判定
特定の判定なのでtrueかfalseで判定します
メソッドの後ろに?がつくと真偽値を返してくれるメソッドになります
- 変数.zero?・・・数値がゼロかどうかの判定
#変数(num)に0が代入されていた場合 irb(main):010:0> num = 0 => 0 irb(main):012:0> num.zero? => true #変数(num)に1が代入された場合 irb(main):013:0> num = 1 => 1 irb(main):014:0> num.zero? => false
- 変数.odd?・・・数値が奇数かどうか?
#変数(num)に1が代入された場合 irb(main):016:0> num.odd? => true
- 変数.even?・・・数値が偶数かどうか?
#変数(num)に1が代入された場合 irb(main):017:0> num.even? => false最後に
ぶっちゃけ判定以外使った事がなかった・・・?
今後使うかどうかわからないけど、頭には入れておいた方が何かの役に立つから予習、復習はかかさずにしようね次回は文字クラス(string)について書きます!
- 投稿日:2021-01-16T18:25:17+09:00
ActiveRecordでwhere in句を書こうとして詰まったこと
SQLで書くとわかるが、Railsでどのように書くか悩んだので、書いておく
知りたかったこと
SELECT * FROM Tables WHERE (name, task) not in ((yamada, "running"),(yamada, "swimming"));いろいろ悩んだが上手くまとめられなかったので、サブクエリで解決しました〜!
配列で渡すと複数個渡せますし、このようにサブクエリにすると欲しい条件が作れるのかなと思います!期待値通りのクエリが発行Table.where.not(id: (Table.select(:id).where(name: yamada).where(task: ["running", "swimming"])))当初の方針
最初の方針ではwhere句を複数繰り返すことで条件を絞れると思ってましたが、以下のように書いたら"yamada"さんの全てのtaskを弾いてしまいます。実際に発行されるSQLも下に記載しておきます。
ベン図を頭の中に描きながら書いていきました。期待値と違うクエリが発行Table.where.not(name: yamada).where.not(task: ["running", "swimming"])期待値と違うクエリSELECT `tables`.* FROM `tables` WHERE `tables`.`deleted_at` IS NULL AND (`tables`.`name` != "yamada") AND (`tables`.`task` NOT IN ("running", "swimming"))参考文献
- 投稿日:2021-01-16T17:49:15+09:00
【初学者】rails6アプリにCDNを使わずにbootstrapを導入しようとして学んだ事【アセットパイプライン】
はじめに
ポートフォリオのためにrails6でアプリケーションを作っていたが、気軽に綺麗なフロントが欲しかったためBootstrapを導入したかった。
以前は完全にCSSだけで作られていたCSSフレイムワークであるBULMAを使っていた。
CDNを使って導入するのではなく、gemを使って導入したいと思いました。事前知識
Bootstrapをgemで導入するには、大きく以下の3つの要素が必要。
- bootstrap (gem)
- popper.js (bootstrap (gem)に含まれている)
- jquery-rails (gem)
導入は以下の公式文章を元に実施しました。
bootstrapの導入方法(公式文章)結論
アセットパイプラインを学んだ。
bootstrapをrails6に導入しようとする場合Sprocketというアセットパイプラインを使う必要があるが、
rails6のデフォルトのアセットパイプラインはWebpackerであり公式文章通りに手順を進めても上手く動くはずがなかった。
bootstrapのCSSの要素のみを使う場合は動いたが、jqueryだったりpopper.jsに依存する要素は正常に作動しなかった。(プルダウン等)問題
結論でもう書きましたが、アセットパイプラインという存在を知らずにエラーが発生しました。
アセットパイプラインとは
アセットパイプラインとは、JavaScriptやCSSのアセットを最小化 (minify: スペースや改行を詰めるなど) または圧縮して連結するためのフレームワークです。(参照:railsガイド)
つまり、アプリケーションのフロントを装飾するファイル(image, CSS, JS, etc)を全て集めてきて合体させて圧縮してくれる機能を持ったものです。
集めて合体して圧縮してくれるのは、ファイルへのリクエストの回数を減らして処理をなるべく少なくするためです。Sprocketsとは
Rails 3.1 から導入された gem で、アセットファイル(JS, CSS, 画像など)を効率的に管理するための Asset Pipeline という仕組みの基盤です。
主な機能は以下のような物があります。
- アセットファイルのパス管理
- アセットファイルの結合・圧縮
- アセットファイルの依存関係の解決
- アセットファイルのコンパイル
- 最終的に出力するファイルに hash を付けてキャッシュを無効化
などなど。(参照Rails Sprocketsとのお別れの仕方 - 最初の一歩 -)
印象としては古くて一部のユーザーにとっては、ファイルの管理方法に問題があるような印象で動作が遅いという事でした。
Webpackerとは
まず前提として、webpackとwebpackerは別物です。
webpackはJSのnpmのパッケージです。JSのコミュニティの中で育ちました。npmというのは、Node.jsで使えるパッケージ管理ツールのことで、つまりはRubyでいうbundlerです。JSの開発者たちは、このnpmか、その代替のyarnをみんな使っています。
webpackerはRubyのgemです。Railsでもwebpackが楽に使えるように作られました。webpackはモジュールハンドラーです。
モジュールバンドラーとはモジュールをひとまとめに、つまりバンドルしてくれるためのものです。
(参照:全Rubyistに今すぐ伝えたいwebpackとwebpacker)参考記事
- 投稿日:2021-01-16T17:26:10+09:00
form_with関連でつまずいたことのまとめ
はじめに
Railsチュートリアル(version5.1対応)を1周して、ポートフォリオ作りをする際に、form_withでいくつか詰まったので備忘録として記事を書きます。間違っているところがあればコメントいただけるとありがたいです。
以下発生したエラーごとにまとめていきます。
flashが表示されない
以下のような記事を投稿するフォームを作成
_post_form.html.erb<%= form_with model: @post, url: yield(:form_url), method: yield(:method), local: true, multipart: true do |form| %> ・・・フォームいろいろ <% end %>初め、local: true の設定を行わずに書いていたのですが、投稿成功のflashの表示ができなくなって困っていました。どうやら、form_withはデフォルトではAjaxを用いて通信を行うようで、それによりflashが表示できないようです。(詳しい原理理解はしていません)
ファイルの送信が行えない
これは以下のようにpdfファイルを送信するフォームを作る際に起こりました。
_post_form.html.erb<%= form_with model: @post, url: yield(:form_url), method: yield(:method), local: true, multipart: true do |form| %> ・・・他のフォーム・・・ <%= form.label :file, "pdfファイル", class: "control-label" %> <%= form.file_field :file, class: "form-control-file" %> <% end %>pdfファイルの送信には、carrierwave を導入しました。carrierwaveのセッティングについては別記事にまとめます。今回は、それが終わった後の話です。
フォームが送れないのでググったところ、form_withの引数に、multipart: trueを指定していませんでした。これを指定すると、ファイルの形式も送信できるようになるようです。Rails4以降ではfile_fieldの記述があれば暗黙にmultipart: trueが指定される、と書いていたのですが、なぜか僕はされていませんでした・・・。scopeオプションについて
以下の記事を参照しました。
https://qiita.com/akilax/items/f36b13f377f7e442bc73データをパラメーターとして送る際に strong parameters として送るために指定する値、と理解しました。(正しいのか・・・?)
終わりに
まだ理解が浅いので、今後新しい発見があり次第追記していくつもりです。form_with 奥が深いな・・・。
- 投稿日:2021-01-16T17:16:21+09:00
テーブルが存在するのに、Table doesn't existと言われた話
はじめに
先日、PCの電源を閉じた後、再度dockerを起動し、
docker-compose run wen rails s
と入力すると、以下のようなエラーが出てしまいました。Mysql2::Error: Table 'share_read_development.books' doesn't exist現在でも、主原因は解明できておりません。
本記事では、僕が解決に向けて試したことを記載していきます。エラーが起きた主原因は不明でしたが、エラーの解決は済んでおり開発自体は進んでおります。
もし同じようなエラーに苦しんでいる方は、参考にしてみてください。また原因がわかる方がいらっしゃいましたら、コメントで教えていただけると幸いです。
開発環境
Ruby 2.72
Rails 6.0.2.3
MySQL 5.7
Docker/docker-compose発生したエラー
冒頭でも簡単に記載しましたが、以下のようなエラーが出ました。
booksテーブルがないと言われています。rails aborted! ActiveRecord::StatementInvalid: Mysql2::Error: Table 'share_read_development.books' doesn't exist /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `block in query' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:130:in `handle_interrupt' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:130:in `query' /usr/local/bundle/gems/activerecord-6.0.3.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:201:in `block (2 levels) in execute' /usr/local/bundle/gems/activesupport-6.0.3.4/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads' /usr/local/bundle/gems/activesupport-6.0.3.4/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares' # 中略 Caused by: Mysql2::Error: Table 'share_read_development.books' doesn't exist /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `_query' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:131:in `block in query' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:130:in `handle_interrupt' /usr/local/bundle/gems/mysql2-0.5.3/lib/mysql2/client.rb:130:in `query' # 中略 /usr/local/bundle/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:291:in `load_dependency' /usr/local/bundle/gems/activesupport-6.0.3.4/lib/active_support/dependencies.rb:324:in `require' bin/rails:4:in `<main>' Tasks: TOP => db:schema:dump (See full trace by running task with --trace)試したこと
①マイグレーションの実行状況の確認
まず、マイグレーションの実行状況を確認しました。$ docker-compose run web rails db:migrate:status Status Migration ID Migration Name -------------------------------------------------- up 20201207135139 Create users up 20201213135207 Create reviews up 20201218052640 Create books up 20201218075413 Add bookgenreid to books up 20201220063200 Add book to reviews up 20201222080803 Create relationships up 20201222131717 Create favorites up 20201222233733 Create comments up 20201224140712 Create bookcases up 20201225105949 Create notifications up 20201226102230 Add status to review up 20201230042031 Add caption to books全てのマイグレーションファイルが実行されていることがわかりました。
②データベースの確認
次にmysqlに接続し、デーベースのテーブルを確認しました。$ docker-compose run web rails db > show tables;そうすると、以下のように表示されました。
booksテーブルは存在している?+----------------------------------+ | Tables_in_share_read_development | +----------------------------------+ | ar_internal_metadata | | bookcases | | books | | comments | | favorites | | notifications | | relationships | | reviews | | schema_migrations | | users | +----------------------------------+しかし、以下のコマンドでは、booksテーブルがないと言われてしまいます。
> select * from books;他のテーブルのデータは取得できます。
> select * from bookcases; +----+------+---------------+---------+----------------------------+----------------------------+ | id | read | book_id | user_id | created_at | updated_at | +----+------+---------------+---------+----------------------------+----------------------------+ | 1 | 0 | 9784296108008 | 1 | 2020-12-28 01:18:40.540283 | 2020-12-28 01:18:40.540283 | | 3 | 0 | 9784478820094 | 3 | 2020-12-28 12:39:00.697316 | 2020-12-28 12:39:00.697316 | | 37 | 0 | 9784478820094 | 1 | 2020-12-29 08:16:25.944979 | 2020-12-29 08:16:25.944979 | | 39 | 0 | 9784804614151 | 1 | 2020-12-29 08:26:56.615557 | 2020-12-29 08:26:56.615557 | | 41 | 0 | 9784907095536 | 1 | 2020-12-29 08:27:01.301378 | 2020-12-29 08:27:01.301378 | | 42 | 0 | 9784908925658 | 1 | 2020-12-29 08:39:11.191829 | 2020-12-29 08:39:11.191829 | | 45 | 0 | 9784284204705 | 1 | 2020-12-30 04:10:08.090943 | 2020-12-30 04:10:08.090943 | +----+------+---------------+---------+----------------------------+----------------------------+公式ドキュメントを調べても、自身で試したことしか記載していませんでした。
エラー Table 'xxx' doesn't exist または Can't find file: 'xxx' (errno: 2) が発生する場合、xxx という名前のカレントデータベースにテーブルがないことを示しています。
SHOW TABLES を使用して、カレントデータベースにあるテーブルを確認できます。【URL】
http://download.nust.na/pub6/mysql/doc/refman/4.1/ja/cannot-find-table.html③データベースの作り直し
あまり気が進みませんでしたが、データベースを作り直すことにしました。$ docker-compose run web rails db:reset $ docker-compose run web rails db:create $ docker-compose run web rails db:migrateこれでエラーが解消することができました!
個人開発なので、データベースを削除するという方法で済みましたが、実際の現場ではこの方法は推奨されないと思います。
もし、今回のエラーの原因等がわかる方がいらっしゃいましたらコメントにて教えていただけると幸いです。
- 投稿日:2021-01-16T17:14:59+09:00
Refinementsで遊んでみた
はじめに
Refinementsで遊んでみた記事です。具体的な内容としてはトップレベルでRefinementsが使えないかなと試してみたとき備忘録です。
ブログからの転載です
Refinementsとは
Rubyでは、後から自由にクラスにメソッドを追加することができる。
class Integer def hoge puts :hoge end end 42.hoge # => :hogeこれはこれで便利なんだけど、グローバルに変更されるし、継承とかでも意図しない動きをする可能性があるのであまりいいとは言えない。
そこでRefinementsの出番。
module Refine refine Integer do def hoge p :hoge end end end # 42.hoge # => Error! using Refine 42.hoge # => :hogeRefinementsでは
using
した以降で再定義したものを使える。
またusing
した箇所のファイルスコープに限定されるので影響範囲が小さくて済む。やりたかったこと
以下のようにトップレベルで
refine
を使って特定のクラスにメソッドを生やしたかった(毎回、using
とか書くのが面倒だったので……)refine Integer do def hoge puts :hoge end end 42.hogeただ、このコードは
refine
というメソッドが定義されていないのでエラーになる。やってみた
で、出来そうか試してみたところ以下のコードで動作した。
module Kernel module_function def refine klass, &block Module.new do refine klass do yield end end end end refine Integer do public def hoge p :hoge end end 42.hoge # => :hogeやっていること
Refinementsをよく使われる人は以下のようなコードで無名モジュールを使っているんじゃないかと思う。
using Module.new { refine Integer do def hoge p :hoge end end } 42.hoge # => :hogeこのコードを元に
Kernel
モジュールにrefine
メソッドを作成。module Kernel module_function def refine klass, &block Module.new do refine klass do yield end end end end
refine
メソッドは第一引数に再定義したいクラスなどを受け取り、メソッドの再定義などはブロック引数で渡します。これにより以下のように
hoge
メソッドを追加できます。refine Integer do public def hoge puts :hoge end end 42.hoge # => :hogeただし……
ただ、このコードは
using
していないのに再定義した内容が使用できているんですよね……。module Kernel module_function def refine klass, &block Module.new do refine klass do yield end end end end # ここで usingを使っていないが作成したhogeメソッドが使えている…… refine Integer do public def hoge p :hoge end end 42.hoge # => :hogeこれはRefinementsの仕様通りの動きなのかがはっきりとしていないのでなぜ動いているのやら……。
それと今回のコードでは
public
を追加しないとメソッドが呼び出されなかった。module Kernel module_function def refine klass, &block Module.new do refine klass do yield end end end end refine Integer do def hoge p :hoge end end 42.hoge # => private method `hoge' called for 42:Integer (NoMethodError)エラーから
private
メソッドとして定義されているらしいことがわかったのでpublic
を付けてみたら動いたという……。
この辺も仕様通りの動きなんだろうか……?おわりに
とりあえず、やりたいことはできた。
これが仕様通りの動きなのかは判断がつかないので、Refinementsの実装とか読んでみようかと思う。参考
- 投稿日:2021-01-16T15:12:10+09:00
ActiveRecordのdefault_timezoneについて
想定バージョン
Ruby 2.5系
Ruby on Rails 5.2系説明
ActiveRecordが発行するタイムスタンプのtimezoneはRailsの場合、config/application.rbで定義します。
# config/application.rb Rails.application.configure do config.active_record.default_timezone = :local # (ActiveRecord::Base.default_timezone = :localと同義) 中略 endこの:localは何を指しているのかというとrubyプロセスタイムゾーンを指していて
rubyプロセスタイムゾーンはOSに定義されている環境変数のTZ
を見ています。
つまりこの環境変数を設定しないとActiveRecordはdefaultのUTCでタイムスタンプを発行してしまうことになります。例えば以下のように定義すると、 created_atやupdated_atが東京の時間になります。
TZ=Asia/Tokyoconfig.time_zoneとの違い
Railsで設定するタイムゾーンといえば、以下の方もあると思います。
Rails.application.configure do config.time_zone = 'Tokyo' endこちらは
Time.zone
で取得するタイムゾーンを東京にできる設定となっています。アプリケーション上で
Time.zone.now
を叩くと東京の現在の時間が取得できるようになります。参考
- 投稿日:2021-01-16T14:50:10+09:00
部分テンプレートを使用する(保守性の高いコードを書く)
前書き
ある程度ポートフォリオが完成してきたところでコードの手直しを行なっていくことにしました。
実際の業務では保守性の高いコードが求められているからです。具体的には同じコードは一つにまとめておくことによって手直しするときに複数直さなくても済むように改善していきます。
書き方を忘れないように備忘録として残しておきます。部分テンプレートの使用
ヘッダーは他のページでも繰り返し使用するので部分テンプレートとしてまとめていきます。
部分テンプレートとは、ビューファイルで繰り返し使用するコードを切り出し、再利用する仕組みのことをいいます。
部分テンプレートとして切り出すときに作成するファイルは命名規則として、アンダースコアを先頭に記述します。
今回の場合、_header.html.erbとなります。
app/views/shared/header.html.erbディレクトリに作成していきます。renderメソッド
renderメソッドは、部分テンプレートを呼び出す際に利用するメソッドのことです。
<%= render "ディレクトリの指定" %>と記述します。
例えばindex.html.erbやshow.html.erbでもヘッダーのコードを使いたい時にrenderメソッドで使い回すことができます。index.html.erb<%= render "shared/header" %> <div id="home-index" class="contents row"> <h2 class="page-title">Find your favorite!</h2> <p class="page-p">歌いたかったあの曲との出会い提供するMY KARAOKE。 </p> <p class="page-p">このページではみんなが歌いたい曲の情報が満載です。 みんなの曲を見てレパートリーを増やしましょう。 </p> 省略_header.html.erb<header class='top-page-header wrapper'> <%= link_to 'MY KARAOKE', root_path, class: "title" %> <nav> <ul class="main-nav"> <% if user_signed_in?%> <li><%= link_to current_user.nickname, new_user_session_path, class: "user-nickname" %></li> <li><%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: "logout" %></li> <% else %> <li><%= link_to'ログイン', new_user_session_path, class: "login" %></li> <li><%= link_to '新規登録', new_user_registration_path, class: "sign-up" %></li> <li><%= link_to 'ゲストログイン', users_guest_sign_in_path, method: :post %></li> <% end %> </ul> </nav> </header>一行目の<%= render "shared/header" %>よって_header.html.erbのコードを使用することができます。
部分テンプレートを作成したことによってindex以外のページでも同じように使い回すことができます。show.html.erb<%= render "shared/header" %> <div class="main"> <div class="song-show"> <table class="detail-table"> <tbody> <tr> <th class="show-detail-value">投稿者</th> <td class="show-detail-song"> <%= link_to user_path(@song.user_id) do %> <%= @song.user.nickname %> <% end %> </td> </tr> 省略今回はわかりやすいようにヘッダーの例を出しましたがもちろんそれ以外も部分テンプレートが使える場面があります。
例えばnewとeditは新規投稿する際にフォームなどはほぼ同じ記述になるので(少なくとも僕の場合は)使えたりします保守性の観点からまとめてあげるほうが後々楽になるのでやっておくほうがいいと思います。後書き
とりあえず簡単な部分テンプレートいついてまとめました。 localsオプションを使った部分テンプレートの記事を次回書いてみようかと思っています。
- 投稿日:2021-01-16T14:13:32+09:00
【Rails奮闘記/Railsチュートリアル編】Railsチュートリアル6章で学んだこと
はじめに
今回はRailsチュートリアル6章を終えて学んだことをまとめたいと思う。
5章でUserコントローラーを作成したので、6章ではUserモデルを作成していくことになる。
早速確認してみます。Userモデル作成
userモデルのデータ構造の基本。
users id integer name string string created_at datetime updated_at datetime まずはUserモデルを以下のコマンドで作成。
$ rails generate model User name:string email:stringコントローラ名には複数形を使い、モデル名には単数形を用いるという慣習を頭に入れておく必要がある。
コントローラはUsersでモデルはUserになる。
コマンドによって生成されたマイグレーションファイルについてみていく。db/migrate/[timestamp]_create_users.rbclass CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end endマイグレーションは「データベースに与える変更を定義したchangeメソッドの集まり」を指している。
モデル名は単数形だが、テーブルは複数形になる。
マイグレーションファイルの最後のt.timestampsはマジックカラムを作成するものである。
ユーザーモデルに当てはめると、あるユーザーが作成または更新されたときにその時刻を自動的に記録するものになる。マイグレーションファイルに問題がなければ、以下のコマンドでデータベースを作成する。
$ rails db:migrate上記のコマンドを実行すると、データベースにUserテーブルが作成され、schema.rbが更新される。
以上がUserモデルを作成するまでの一連の流れになる。Userモデルにバリデーションを追加
Railsチュートリアル6章では、presence・length・format・uniqueness・confirmationを実装していく。
presence
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true endlength
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 } endformat
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX } enduniqueness
app/models/user.rbclass User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: true endemailにインデックスを追加する必要があるので、以下のコマンドを実行。
$ rails generate migration add_index_to_users_emailusersテーブルのemailカラムにインデックスを追加するために、add_indexというRailsのメソッドを使用している。
unique: trueを指定することで一意性を強制することができる。db/migrate/[timestamp]_add_index_to_users_email.rbclass AddIndexToUsersEmail < ActiveRecord::Migration[6.0] def change add_index :users, :email, unique: true end endマイグレーションファイルを修正したら、マイグレート。
これが終わったら、confirmationに。confirmation
users id integer name string string created_at datetime updated_at datetime password_digest string セキュアなパスワードは、has_secure_passwordというRailsのメソッドを呼び出すことでほとんど完結できる。
usersテーブルにpassword_digestというカラムを追加する必要がある。
カラムを追加する際、以下のコマンドを叩く必要がある。$ rails generate migration add_password_digest_to_users password_digest:stringdb/migrate/[timestamp]_add_password_digest_to_users.rbclass AddPasswordDigestToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :password_digest, :string end endadd_columnメソッドを使ってusersテーブルにpassword_digestカラムを追加。
これを適用するために、データベースでマイグレーションを実行。マイグレーションが終わったら、userモデルにパスワードを以下のように記述。
今回はパスワードにpresenceとlengthを入れている。app/models/user.rbclass User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: true validates :password, presence: true, length: { minimum: 6 } endこれに合わせてテストを書いて、Railsチュートリアル6章は終わりである。
まとめ
6章から本格的にアプリ開発をしてる感があってようやく本番だなというのが正直なところ。
粘り強くやりぬきたいと思っている。
- 投稿日:2021-01-16T13:55:36+09:00
VScodeのスニペット機能を活用しよう ~ もう<%=%>はいちいち打ちたくない ~
概要
railsの開発をしていると、結構書くことになる「<%= %>」や「<% %>」。
いちいち打つのは結構めんどくさい。
なので、VScodeのスニペット機能を使って簡単にこれらのめんどくさいコードを打つ時間の短縮をしてみよう!
もちろん、他の言語でもOK!導入の仕方
VScodeの左下の歯車マークをクリック
Command paletteを開く
snippetsと打って、省略したいコードの言語の拡張子を選択
スニペットを記述しよう
自由にスニペットは作れるのですが、
と書き方は、以下のように
はじめの””の中には、当該スニペットの端的な内容
prefixの後には、スニペットを出力するためのトリガー
bodyの後には、実際出力したいコードの内容
descriptionの後には、スニペットの説明
を記述します!ruby{ // Place your snippets for erb here. Each snippet is defined under a snippet name and has a prefix, body and // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the // same ids are connected. // Example: "write <%=%> more easier": { "prefix": "pa", "body": [ "<%= $0 %>" ], "description": "Log output to console" } }また、$0をかけば、カーソルの位置を指定できます。
上の例のようにかけば、<%= %>における=と%の間にカーソルが合います。
同様に、$1や$2をかけば、改行を挿入できます。また、以下のように記述すれば複数のスニペットを作成できます。
ruby{ // Place your snippets for erb here. Each snippet is defined under a snippet name and has a prefix, body and // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the // same ids are connected. // Example: "write <%=%> more easier": { "prefix": "pa", "body": [ "<%= $0 %>" ], "description": "Log output to console" }, "write <%%>": { "prefix": "ni", "body": [ "<%%>", ], "description": "Log output to console" } }終わり
- 投稿日:2021-01-16T13:11:58+09:00
比較演算子と条件分岐
比較演算子とは
値どうしをを比較するときに使います。書き方は数学で学んだのと似てるようで微妙に違います。
比較演算子 条件式 意味 > A > B AはBより大きいか >= A >= B AはB以上か < A < B AはBより小さいか <= A >= B AはBより大きいか >= A <= B AはB以下か == A == B AとBは等しいか 真偽値
真偽値とは式が正しいか違うかとことを表すもので
値が真であれば true
値が偽であれば false
で表される。式の真偽の確認や、真偽値に対しての演算を行うことができる演算子を論理演算子と呼びます。
論理演算子 OR ( | | )
真偽値と共に使われた場合、 演算対象のどちらかが true ならば、true を返し、両方とも false の場合は、false を返します。
A || B (AまたはBみたいなイメージ)論理演算子 AND ( && )
真偽値と共に使われた場合、 演算対象の両方が true ならば、true を返し、そうでなければ、false を返します。
A && B (AかつBみたいなイメージ)論理演算子 NOT ( ! )
!(エクスクラメーションマーク)は not演算子 と呼ばれ、否定の意味で使われます。
!と=を合わせた!=は、値同士が等しくない場合にtrueを返します。A!=B(AとBは等しくない)
if文
もし〇〇なら□□するのように条件によって処理を分岐させる物です
if 条件式 #この条件式がtrueの場合に処理が実行される 処理 endelseを使うと条件式がfalseの時に実行する処理をかける
if 条件式 # 条件式が真(true)のときに実行する処理 else # 条件式が偽(false)のときに実行する処理 endelsifを使うとより複雑な条件処理を作れる
if 条件式1 # 条件式1が真(true)のときに実行する処理 elsif 条件式2 # 条件式1が偽(false)のとき、かつ # 条件式2が真(true)のときに実行する処理 else # 条件式1と条件式2がどちらとも偽(false)のときに実行する処理 end以上です。
- 投稿日:2021-01-16T12:02:25+09:00
【新人注目】データベースの主キーと外部キーのお話
主キーとは
データベースを扱う際に、必ずひとつの情報を特定できるキーとなるものが必要になります。
例えば、大学でいう学籍番号のようなものです。
この番号があれば、S12H1234という学籍番号は田中太郎くんだ
というように、特定をすることができます。データベースの表には必ずひとつひとつの行(レコード)を識別することができるように、キーとなる情報が必ず含まれるようになっています。これが主キーです。
主キーを構成する列に必要な条件
重複していないこと
空でないことこのふたつが主キーに必要な条件。
外部キーとは
アプリケーションを開発する中で、使用するのがリレーショナルデータベースというものです。これはひとつひとつの表を関係付けできることが特徴になります。表と表とを関係付けるために、
外部キー
というものを使います。学生表
学籍番号 名前 学部ID S12463545 田中太郎 1 S12346456 鈴木一郎 2 S14345678 山田二郎 3 学部表
学部ID 学部名 1 法学部 2 経済学部 3 体育学部 上の表からすると、学生表では学部表の主キーを参照し、外部キーとして学部IDの列を設けています。
これにより、田中太郎くんの学部はどこ?という問いに対して、法学部というような返答ができる
ようになります。
- 投稿日:2021-01-16T11:33:06+09:00
Railsアプリにransackを導入したけどあたふたした話。
ポートフォリオ用のアプリにransackを使って検索機能を実装しようとした際にちょっと詰まったので
備忘録を残しておきます。やりたいこと
検索文字列を含んだ投稿を検索する機能を実装したい。
ひとまずransackをインストール
Gemfilegem 'ransack'$ bundle installコントローラーについて
posts_controller.rbclass PostsController < ApplicationController before_action :set_q, only: [:index, :search] def search @results = @q.result end private def set_q @q = Post.ransack(params[:q]) end endルーティングについて
routesresources :posts do collection do get :search end endビューについて
posts/index.html.erb<%= search_form_for @q, url: search_posts_path do |f|%> <%= f.text_field :body_cont, placeholder: "検索"%> <%= f.submit "検索"%> <% end%>posts/search.html.erbお好みにカスタマイズ
結論
色々な記事などに<%= f.search_field :body_cont %>
と記されていたのですが、これだとうまく行かなかったです。
ヤケクソで<%= f.text_field :body_cont %>にすると
すんなり行きましたー!
- 投稿日:2021-01-16T03:45:46+09:00
【Ruby初心者向け】puts print pの違いとは?実は明確な使い分けの方法があるんです!
Rubyを学習していたら、誰しもがputsメソッドやprintメソッド、pメソッドをよく見かけると思いますし、使うことも多々あるでしょう。
しかし、それぞれの違いをしっかりと理解していない人も少なくないのではないでしょうか。
というのも、私自身がその一人でした。
ある参考書ではputsを使って文字列などを出力しているけど、またある学習コンテンツではpを使って出力を行っている。そう思ったらprintメソッドというものもあるというのを知りました。
当時、Rubyの学習を始めたばかりで、それぞれの違いがわからない私は、どれを使ったら良いのかわからず混乱してしまいました。
当記事では、そのような方に向けてputs print pの3つのメソッドについて解説しています!
といってもそこまで難しいことでもないので、簡単に習得してしまいましょう!
puts、print、pメソッドの明確な違いとは?
簡単にいうとputs、print、pメソッドの違いとは、対象者の違いにあります。
putsとprintは一般ユーザー、pは開発者が対象者です。これがどういうことかというと、出力したい値が誰に向けたものかということです。
例えば、自身で開発したアプリケーションがあるとします。そのアプリケーションが実行される過程で、ユーザーに向けた何かを表示したい場合、putsやprintを使用します。
しかし、ここで注意が必要なのは、printは出力したいことの内容に改行がされないということです。
開発者側からしても、ユーザー側からしても、何か長い文章などが出力される時などに改行が行われないのはとても不便だと言えます。
ですので、一般的には一般ユーザに向けた出力をする場合はputsを使用します。
一方で、pは開発者に向けたメソッドです。
pのputsやprintとの大きな違いは、引数で渡されたオブジェクトがそのまま戻り値となるということです。
例えば、このようになります。
# 以下の内容を実行します。また、わかりやすくするためにirbなどで実行するための記述は省きます。 p "ダブルクオートも含まれた状態で出力されます" "ダブルクオートも含まれた状態で出力されます" # 出力内容 =>"ダブルクオートも含まれた状態で出力されます" # putsで出力した場合 puts "ダブルクオートも含まれた状態で出力されます" ダブルクオートも含まれた状態で出力されます # 出力内容 => nilどうでしょうか?意外と3つのメソッドには大きな違いがあることが分かったのではないでしょうか。
まとめ
puts、print、pメソッドはそれぞれ同じ引数を扱っていても、違う出力結果を表示することがわかりました。
初心者目線でいうと、それぞれの対象ユーザを気にして使っていくことをお勧めします。
簡単に、一般ユーザに向けた出力内容を求める場合はputsメソッド、開発者向けの出力内容を求める場合ならpメソッドを使えば良いでしょう。
- 投稿日:2021-01-16T01:30:43+09:00
【3行で説明】プログラミングスクールの技術記事を検索結果に表示させない方法
※単純に初歩的すぎる記事を見る必要がなくなった時に使う方法です。他意はありません。
1.Chromeでこの拡張機能(uBlacklist)を入れる。
2.表示してほしくないサイトに行く、または調べる
3.拡張機能のボタン(ブラウザ右上のパズルのようなマーク)から選択して、ブロックを押す。
- 投稿日:2021-01-16T00:59:45+09:00
PHPによる型の比較について
概要
以下の記載によるとPHPは比較対象の型は異なっていてもTRUEになってしまうらしい。
https://www.php.net/manual/ja/migration80.incompatible.php
0 == "0" はTRUEになるから、型の一致まで検証するなら 0 === "0" にする必要があるようです。実践(PHP)
PHPで簡単な処理を書いてみました。
本当に0 == "0"はtrueになるみたいです。<?php if (0 == "0") { echo "true"; } else echo "false"; ?>実行結果 % php test.php true次は以下のコードで実行してみます。
変更点は「if (0 == "0")」が「if (0 === "0")」に変わっただけです。<?php // 比較対象の型も検証する if (0 === "0") { echo "true"; } else echo "false"; ?>実行結果 % php test.php false型の検証まで行うとfalseになることを確認できました。
実践(Ruby)
Rubyだと「0 == "0"」は数値と文字列は比較不能なのでfalseになりました。
これは想定通りです。if 0 == "0" then puts "true" else puts "false" end実行結果 % ruby test.rb false実践(Python)
Pythonでも「0 == "0"」は型が違うのでfalseになりました。
これも想定通りです。if 0 == "0": print("true") else: print("false")% python test.py falseまとめ
PHPはRubyやPythonと設計思想が違うようです。
PHPは比較対象の内容が合ってればいい、ということで数値の0も文字列の0もtrueになっちゃうみたいですね。
- 投稿日:2021-01-16T00:13:01+09:00
[Rails]"kaminari"の使い方について
はじめに
"kaminari"の実装をやったので忘れないように記していきます。
「kaminari」
ページネーションを実装するためのrubyのgemの一つです。
何かの一覧ページを表示した時に、たくさんある場合見やすいようにページを数字で分割してくれ、最初や最後のページに飛べるボタンが実装できたりします。
実装
インストール
まずはgemをインストールします。
日本語表記にするので一緒に「i18n」もインストールします。「i18n」は以前学習していましたのでいちお貼っておきます。
Gemfilegem 'kaminari' gem 'rails-i18n'インストール!
bundle install
コントローラ
controller.rbdef index @advises = Advise.page(params[:page]) endビューファイル
view.html.erb<div class="advise-lists"> <% @advises.each do |advise| %> <ul class="advise-text"> <li><%= link_to advise.title, advise_path(advise), class: "advise2_link" %></li> </ul> <% end %> <!--↑ 一覧表示の部分--> </div> <!--↓ ページネーション実装部分--> <%= paginate @advises %>「kaminari」の設定ファイルを作成
rails g kaminari:config
「kaminari_config」ファイルが作成されます。
お好みで変更していきます。こちらでは「default_per_page」を1ページあたり3行とわざと小さく設定しています。
config/initializers/kaminari_config.rb# frozen_string_literal: true Kaminari.configure do |config| config.default_per_page = 3 # config.max_per_page = nil # config.window = 4 # config.outer_window = 0 # config.left = 0 # config.right = 0 # config.page_method_name = :page # config.param_name = :page # config.max_pages = nil # config.params_on_first_page = false endたくさん項目がありますが
こちらでは
フリーランスLIFE!:【Rails】でページネーションをgemで実装!TECH SCORE BLOG:Railsライブラリ紹介: ページングを行う「kaminari」
以下のように説明されています。
default_per_page
デフォルトのページあたりの表示件数(デフォルトは 25)。max_per_page
ページあたりの表示件数の最大(デフォルトは nil、つまり無制限)。window
表示中のページの左右何ページ分のリンクを表示するかを指定します(デフォルトは 4)。上記画像はデフォルトの 4 で、11 ページを表示しているところです。11 の左右それぞれ 4 ページ分のリンクが生成されています。outer_window
先頭ページ、及び最終ページから何ページ分のリンクを表示するかを指定します(デフォルトは 0)。left、right が指定された場合は、そちらの値が優先されます。left
先頭ページから何ページ分のリンクを表示するかを指定します(デフォルトは 0)。上記画像は 3 を指定した場合です。right
最終ページから何ページ分のリンクを表示するかを指定します(デフォルトは 0)。上記画像は 2 を指定した場合です。page_method_name
モデルに追加されるページ番号を指定するスコープの名前(デフォルトは page)。param_name
ページ番号を渡すために使用するリクエストパラメータの名前(デフォルトは page)。引用: Railsライブラリ紹介: ページングを行う「kaminari」
ロケールの設定
ロケールファイルのロードパスを設定します。
デフォルトロケールを「日本語( ja )」にセットします。config/application.rbconfig.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{ry,yml}").to_s] config.i18n.default_locale = :javiewディレクトリを作り、その下にファイルを作ります。
locales/views/pagenate.ja.ymlja: views: pagination: first: "先頭" last: "最後" previous: "前へ" next: "次へ" truncate: "..."そうすると英語表記から日本語表記に代わって表示され、ページネーションが実装されます。
さらにカスタマイズをしたい場合は
rails g kaminari:views default
を入力すると
viewにこれらが作成されます。これらのファイルを編集すれば、ページネートのビューを変更することができます。
(_gap.html.erbはページが省略される"..."の部分、
_page.html.erbはページ番号の部分、
_paginator.html.erbは全体の構成定義)まとめ
装着するの自体はそんなに難しくはないですが、カスタマイズの仕方をもう少し触って勉強していきたいと思います。
装飾とカスタマイズの部分をまた近々やっていきたいと思います。
参考
kikawaka:【Rails】kaminariの使い方をマスターしよう!
設定項目を確認するには・・
RAILS GUIDES:Configuring Rails Applications
装飾の参考