- 投稿日:2019-05-07T22:57:42+09:00
railsアプリ開発時にやったSEO対策メモ
概要
railsでQAサイトを作った際のSEOメモ
パンくず設置
gretelを導入。
httpsリダイレクト
config/enviroments/production.rbconfig.force_ssl = true上記でhttpsにリダイレクトされるようになる。httpとhttpsどちらにもアクセスが有るとgoogleの評価が分かれるのでリダイレクトさせる。
tag
metatagを導入して下記の対策を行う。導入方法は下記記事に記載。
https://qiita.com/you8/items/fa546663a514e7552791
devise周りのnoindex化
<% set_meta_tags noindex: true %>deviseを導入するとuser登録周りのページが自動で作られるがこれらのページはindexされてもしょうがないのでmetatagでnoindexに。
titleのユニーク化
SEO的にはtitleはユニークなものにしたほうがいい。違うページだと認識しにくいので。これはmetatagで下記のようにviewごとに書いて出し分けるのが一番ラクそう。
<% set_meta_tags title: "タイトル1" %>
- 投稿日:2019-05-07T22:55:58+09:00
Ruby minitest キーワード引数を叩くMockの定義方法
@koshi_life です。
キーワード付き引数を叩くMockの定義方法でハマったので備忘です。前提
- Ruby 2.6.0
- minitest 5.11.3
検証対象モジュール
比較用に検証対象モジュール内で通常引数、キーワード引数各Mock化する2パターンでテストを書きました。
検証対象モジュールは以下とコード参考。
Hoge#countup_normal_args():
通常引数の@api_client.get,@api_client.updateを叩くHoge#countup_keyword_args():
キーワード引数の@api_client.get(item_id:),@api_client.update(item_id:, value:)を叩くhoge.rbclass Hoge def initialize(api_client) @api_client = api_client end # countupメソッド 通常引数のモジュールを利用する def countup_normal_args(item_id) item = @api_client.get(item_id) new_value = item[:val] + 1 @api_client.update(item_id, new_value) end # countupメソッド キーワード引数のモジュールを利用する def countup_keyword_args(item_id) item = @api_client.get(item_id: item_id) new_value = item[:val] + 1 @api_client.update(item_id: item_id, value: new_value) end endテスト
3つのケースを定義しました。詳細はコード参照。
- 通常引数のメソッドを叩くパターン
- キーワード引数のメソッドを叩くパターン1
- キーワード引数のメソッドを叩くパターン2 Proc 利用
hoge_test.rbrequire 'test_helper' class HogeTest < ActiveSupport::TestCase test '#Mock 通常引数のメソッドを叩くパターン' do item_id = 'id-1' retval_get = { val: 3 } args_get = [item_id] retval_update = true args_update = [item_id, 4] api_mock = MiniTest::Mock.new api_mock.expect(:get, retval_get, args_get) api_mock.expect(:update, retval_update, args_update) # 検証したいモジュールを実行します hoge = Hoge.new(api_mock) result = hoge.countup_normal_args(item_id) assert_equal(true, result) # Mockに送信されたデータを検証します。 api_mock.verify end test 'キーワード引数のメソッドを叩くパターン1' do item_id = 'id-2' retval_get = { val: 3 } args_get = [{ item_id: item_id }] # 配列の中にハッシュで定義 retval_update = true args_update = [{ item_id: item_id, value: 4 }] # 配列の中にハッシュで定義 api_mock = MiniTest::Mock.new api_mock.expect(:get, retval_get, args_get) api_mock.expect(:update, retval_update, args_update) # 検証したいモジュールを実行します hoge = Hoge.new(api_mock) result = hoge.countup_keyword_args(item_id) assert_equal(true, result) # Mockに送信されたデータを検証します。 api_mock.verify end test 'キーワード引数のメソッドを叩くパターン2 Proc利用' do item_id = 'id-3' retval_get = { val: 3 } retval_update = true api_mock = MiniTest::Mock.new api_mock.expect(:get, retval_get) do |item_id:| puts ":get() called args item_id:#{item_id}" item_id == 'id-3' end api_mock.expect(:update, retval_update) do |item_id:, value:| puts ":update() called args (item_id:#{item_id}, value:#{value})" item_id == 'id-3' && value == 4 end # 検証したいモジュールを実行します hoge = Hoge.new(api_mock) result = hoge.countup_keyword_args(item_id) assert_equal(true, result) # Mockに送信されたデータを検証します。 api_mock.verify end end参考
- 投稿日:2019-05-07T22:43:07+09:00
多対多 DBに値入らない時
real_shopテーブル----real_shop_expert_collectionテーブル(中間)----expert_collectionテーブル
それぞれテーブルを作る
real_shop.rbhas_many :real_shop_expert_collections has_many :expert_collections, through: :real_shop_expert_collectionsexpert_collection.rbhas_many :real_shop_expert_collections has_many :real_shops, through: :real_shop_expert_collectionsreal_shop_expert_collections.rbbelongs_to :real_shop belongs_to :expert_collectionエキスパートコレクション内でリアルショップに登録してある内容をチェックボックスで全表示させたい!
expert_collections/_form.html.erb<table class="form_area " id="is_shop_area_1"> <tr> <th scope="row">対象店舗</th> <td> <% for shop in current_site.real_shops %> <%= check_box_tag "shop_ids[]", shop.id, @expert_collection.real_shops.ids.include?(shop.id), :id => "shop_id_#{shop.id}" , :class => 'shop'%> <%= label_tag "shop_id_#{shop.id}", shop.name %> <% end %> <% if current_site.real_shops.present? %> <br /> <input type="button" value="全て選択" id="shop-true" class="set_all_button" /> <input type="button" value="全て解除" id="shop-false" class="set_all_button" /> <% end %> </td> </tr> </table>check_box_tagのリファレンス
check_box_tag(要素名 [, 値, checked = false, オプション])realshopの店舗から選択したエキスパートコレクションがshop_ids[]に格納される
なので
多対多の要素をDBに登録したい
expeert_collections_controller.rbdef new @expert_collection = ExpertCollection.new end def create @expert_collection = ExpertCollection.new(params[:expert_collection]) @expert_collection.site_id = current_site.id @expert_collection.real_shops = RealShop.find(params[:shop_ids]) if params[:shop_ids].present? if @expert_collection.save redirect_to ar_admin_expert_collections_path, :notice => "エキスパートコレクションを作成しました。" else render :new end endcreateの3行目。
params[:shop_ids]があったら@expert_collection.real_shopsに格納
してからsaveすれば中間テーブルに値が入ります。
- 投稿日:2019-05-07T22:39:15+09:00
binding.pryで条件付きでデバッグする
what
Railsで
binding.pryを使って、条件付きで処理を止める方法を知りたい。Android Studioだとこれがデフォルトでできました。
例えば、何回もアクセスされるエンドポイントで、特定の条件のときにだけエラーが起きている場合、関係ないアクセスのときにも処理が止まってしまうとデバッグするのが面倒です。
(ある料理レシピサイトがあったとして、リストの3ページ目にアクセスしたときだけエラーが起きる場合。1ページめを表示したときにも処理が止まると面倒ですよね。)how
まずは、 gemをインストール。
gem install pry-byebugデバッグしたいファイルに以下を追加
require 'pry'処理を止めたい箇所に以下を記述
binding.pryさらに、処理を止めたい条件を以下のように記述
binding.pry if page == 3これで
page == 3のときにだけ処理が止まるようになりました。おまけ
処理を止めた時に使えるコマンド
- next
- 次の行を実行(メソッドは実行される)
- step
- 次の行を実行、メソッドなら中に入る
- continue
- プログラムを実行し、pryを終了
- break
- break
数字=> 数字の行にbreak pointを貼るaliasを貼る
以下を
~/.pryrcに貼り付けるif defined?(PryByebug) Pry.commands.alias_command 'c', 'continue' Pry.commands.alias_command 's', 'step' Pry.commands.alias_command 'n', 'next' Pry.commands.alias_command 'f', 'finish' end直前のコマンドを
Enterキーで繰り返す以下を
~/.pryrcに貼り付けるPry::Commands.command /^$/, "repeat last command" do _pry_.run_command Pry.history.to_a.last end
- 投稿日:2019-05-07T21:08:02+09:00
質問箱をOSS(オープンソースにしてみた)
peing-質問箱-のクローン(OSS)
ソースコードはこちら
https://github.com/seiyatakahashi/peing-questionbox-cloneクローン質問箱の現状はこんな感じです。
質問箱とは
みなさんpeing-質問箱-をご存知ですか?twitterで2017年ごろから流行っている匿名で質問ができるwebサービスのことです。このサービスは個人開発をしているせせり氏が開発したサービスで公開1ヶ月で1億アクセスに達したそうです。
匿名で質問できるという機能はとても面白いと思いました。なのでこれをオープンソース化して、もっといろんな機能をつけていきたいと考えています。
デプロイ deploy
誰でもカンタンにデプロイできるようにしましたので是非みなさんもお使いください。
まずはgitからファイルをダウンロードしてください。
git clone git@github.com:seiyatakahashi/peing-questionbox-clone.gitダウンロードができたらダウンロードしてきたフォルダに移動する
cd peing-questionbox-cloneherokuにログインをする
heroku loginheorku でプロジェクトを作成する
heroku create 作りたいアプリ名heorku にアップロード
git add .
git commit -m "all"
git push heroku masterアップロードしてら、データベースにマイグレード
heroku run rails db:migrate環境変数の設定
heroku config:set APP_NAME="アプリの名前"
heroku config:set APP_NAME_EN="アプリの英語の表記名"
heroku config:set API_KEY="ツイッターのAPI KEY"
heroku config:set API_SECRET="ツイッターのAPI シークレット"
heroku config:set TOKEN="ツイッターのAccess token"
heroku config:set SECRET="ツイッターのaccess token secret"
heroku config:set CURRENT="自分ののツイッターのID"
heroku config:set DESCRIPTION="サイトの情報"
heroku config:set KEYWORDS="サイトのキーワード"
heroku config:set GOOGLE_ANALYTICS="Google アナリティクスのID"
今後つけたい機能
決済機能
質問したい人がお金を払って質問された人がお金をもらえる機能。
複数のSNSログイン
現在twitterでのログインしかできなのでfacebookやlineなどを加えたいです。
ネイティブアプリ化
railsをapi化させて、iosやandroidのようなネイティブなアプリケーションを作りたいです。
- 投稿日:2019-05-07T21:05:48+09:00
Rails+Docker+HerokuでCI/CD
CI/CD初心者が、CI/CDを構築した時のメモ。
初めて触ったので、誤っているところがあるかもしれません。
ご指摘いただければ、幸いです。
以下、参考にした記事等です。
https://qiita.com/kei_f_1996/items/934296e23b0d8d877ff1
https://qiita.com/Kesin11/items/47079bc7f659e71b694c
https://docs.docker.com/compose/rails/環境
OS:mac
Ruby:2.5.0
Rails:5.2.2
CicleCI
Heroku手順
Dockerfileの作成
docker-compose.ymlの作成
database.yml
CicleCI
HerokuDockerfileの作成
基本的にはdocker docsのQuickstartを参考に書きました。
DockerfileFROM ruby:2.5.0 RUN apt-get update -qq && apt-get install -y postgresql-client sudo RUN curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash - RUN sudo apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp CMD ["rails", "server", "-b", "0.0.0.0"]3行目で、nodejsのversionを指定しています。
server.pidファイルが存在する場合に、railsのサーバが再起動しなくなったので、Quickstartに書かれている、
# Add a script to be executed every time the container starts.
の部分を書いておくべきでした。docker-compose.ymlの作成
こちらもQuickstartを基に書いてます。
docker-compose.ymlversion: '3' services: db: image: postgres:9.6 ports: - '5432:5432' volumes: - postgresql-data:/var/lib/postgresql/data web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/myapp ports: - "3000:3000" depends_on: - db - chrome chrome: image: selenium/standalone-chrome:3.141.59-dubnium ports: - 4444:4444 volumes: postgresql-data: driver: local・自動テスト時に、chromeでのテストがうまくできていなかったみたいなので、chrome部分を追加しました。
(対処として最適かどうかはわかりません)
・コンテナを潰してもDBのデータが消えないようにするために、volumesの部分を追加。database.yml
database.ymlに記述を追加。
これもQuickstartを参考にしました。database.ymldefault: &default adapter: postgresql encoding: unicode #追加 host: db username: postgres password: pool: 5あとはbuildしたりupしたりします。
$ docker-compose build $ docker-compose up $ docker-compose run web bin/rails db:createCicleCI
アプリケーションのルートディレクトリに、.cicleciフォルダを作ります。
.cicleci直下に以下のconfig.ymlを記述します。config.ymlversion: 2 jobs: build: machine: image: circleci/classic:edge steps: - checkout - run: name: docker-compose build command: docker-compose build - run: name: docker-compose up command: docker-compose up -d - run: name: sleep for waiting launch db command: sleep 1 - run: name: "before_test: setup db" command: docker-compose run web rails db:create db:migrate - run: name: test command: docker-compose run web bundle exec rspec - run: name: docker-compose down command: docker-compose down deploy: machine: image: circleci/classic:edge steps: - checkout - run: name: "build docker image" command: docker build --rm=false -t registry.heroku.com/${HEROKU_APP_NAME}/web . - run: name: setup heroku command command: bash .circleci/setup_heroku.sh - run: name: heroku maintenance on command: heroku maintenance:on --app ${HEROKU_APP_NAME} - run: # HEROKU_AUTH_TOKEN is generated by `heroku auth:token` name: "push container to registry.heroku.com" command: | docker login --username=_ --password=$HEROKU_AUTH_TOKEN registry.heroku.com docker push registry.heroku.com/${HEROKU_APP_NAME}/web bash .circleci/heroku-container-release.sh - run: name: heroku db migrate command: heroku run rails db:migrate --app ${HEROKU_APP_NAME} - run: name: heroku maintenance off command: heroku maintenance:off --app ${HEROKU_APP_NAME} workflows: version: 2 build_and_deploy: jobs: - build - deploy: requires: - build filters: branches: only: masterhttps://circleci.com/docs/2.0/configuration-reference/
machine: の部分でdocker-composeをそのまま使えるように設定しています。
CicleCIのEnvironmentValiablesに、
HEROKU_APP_NAME,HEROKU_AUTH_TOKEN,HEROKU_LOGIN
,HEROKU_API_KEYを設定しておきます。デプロイ時に実行するshellは以下の二つです。
setup_heroku.shwget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz sudo mkdir -p /usr/local/lib /usr/local/bin sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku cat > ~/.netrc << EOF machine api.heroku.com login $HEROKU_LOGIN password $HEROKU_API_KEY EOF # machine git.heroku.com # login $HEROKU_LOGIN # password $HEROKU_API_KEY # Add heroku.com to the list of known hosts ssh-keyscan -H heroku.com >> ~/.ssh/known_hostheroku-container-release.sh#!/bin/bash imageId=$(docker inspect registry.heroku.com/${HEROKU_APP_NAME}/web:latest --format={{.Id}}) payload='{"updates":[{"type":"web","docker_image":"'"$imageId"'"}]}' curl -n -X PATCH https://api.heroku.com/apps/${HEROKU_APP_NAME}/formation \ -d "$payload" \ -H "Content-Type: application/json" \ -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \ -H "Authorization: Bearer ${HEROKU_API_KEY}"Heroku
postgresのアドオンを入れ忘れないように気をつけて、herokuの設定も完了すれば、CICDできます。たぶん
- 投稿日:2019-05-07T21:03:36+09:00
Ruby2.6.x WSLでbrewを使用し、bundle installのmysqlでコケる際の対処。
tl;dr
WSLのbrewで管理しているのであれば
$ bundle config --local build.mysql2 "--with-ldflags=-L/home/linuxbrew/.linuxbrew/opt/openssl/lib"上記コマンド後に
bundle installで多分解決します。状態
Using kaminari-core 1.1.1 Using kaminari-actionview 1.1.1 Using kaminari-activerecord 1.1.1 Using kaminari 1.1.1 Using launchy 2.4.3 Using ruby_dep 1.5.0 Using listen 3.1.5 Fetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /home/iruk/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/mysql2-0.5.2/ext/mysql2 /home/iruk/.rbenv/versions/2.6.0/bin/ruby -I /home/iruk/.rbenv/versions/2.6.0/lib/ruby/2.6.0 -r ./siteconf20190507-7094-1spvp3a.rb extconf.rb --with-ldflags\=-L/usr/local/opt/openssl/lib checking for rb_absint_size()... yes checking for rb_absint_singlebit_p()... yes checking for rb_wait_for_single_fd()... yes ----- Using mysql_config at /home/linuxbrew/.linuxbrew/bin/mysql_config ----- checking for mysql.h... yes checking for errmsg.h... yes checking for SSL_MODE_DISABLED in mysql.h... yes checking for SSL_MODE_PREFERRED in mysql.h... yes checking for SSL_MODE_REQUIRED in mysql.h... yes checking for SSL_MODE_VERIFY_CA in mysql.h... yes checking for SSL_MODE_VERIFY_IDENTITY in mysql.h... yes checking for MYSQL.net.vio in mysql.h... yes checking for MYSQL.net.pvio in mysql.h... no checking for MYSQL_ENABLE_CLEARTEXT_PLUGIN in mysql.h... yes checking for SERVER_QUERY_NO_GOOD_INDEX_USED in mysql.h... yes checking for SERVER_QUERY_NO_INDEX_USED in mysql.h... yes checking for SERVER_QUERY_WAS_SLOW in mysql.h... yes checking for MYSQL_OPTION_MULTI_STATEMENTS_ON in mysql.h... yes checking for MYSQL_OPTION_MULTI_STATEMENTS_OFF in mysql.h... yes checking for my_bool in mysql.h... no ----- Setting libpath to /home/linuxbrew/.linuxbrew/Cellar/mysql/8.0.16/lib ----- creating Makefile current directory: /home/iruk/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/mysql2-0.5.2/ext/mysql2 make "DESTDIR=" clean current directory: /home/iruk/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/mysql2-0.5.2/ext/mysql2 make "DESTDIR=" compiling client.c In file included from ./mysql2_ext.h:39:0, from client.c:1: client.c: In function ‘rb_set_ssl_mode_option’: ./client.h:22:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] mysql_client_wrapper *wrapper; \ ^ client.c:127:3: note: in expansion of macro ‘GET_CLIENT’ GET_CLIENT(self); ^~~~~~~~~~ client.c:128:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] int val = NUM2INT( setting ); ^~~ client.c:133:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val ); ^~~ client.c: At top level: cc1: warning: unrecognized command line option ‘-Wno-self-assign’ cc1: warning: unrecognized command line option ‘-Wno-parentheses-equality’ cc1: warning: unrecognized command line option ‘-Wno-constant-logical-operand’ cc1: warning: unrecognized command line option ‘-Wno-cast-function-type’ compiling infile.c compiling mysql2_ext.c compiling result.c compiling statement.c statement.c: In function ‘rb_raise_mysql2_stmt_error’: statement.c:47:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt)); ^~~~~ statement.c:53:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] rb_encoding *default_internal_enc = rb_default_internal_encoding(); ^~~~~~~~~~~ In file included from ./mysql2_ext.h:39:0, from statement.c:1: statement.c: In function ‘rb_mysql_stmt_execute’: ./client.h:22:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] mysql_client_wrapper *wrapper; \ ^ statement.c:261:3: note: in expansion of macro ‘GET_CLIENT’ GET_CLIENT(stmt_wrapper->client); ^~~~~~~~~~ statement.c:389:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0); ^~~~~ In file included from ./mysql2_ext.h:39:0, from statement.c:1: statement.c: In function ‘rb_mysql_stmt_fields’: ./client.h:22:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] mysql_client_wrapper *wrapper; \ ^ statement.c:491:3: note: in expansion of macro ‘GET_CLIENT’ GET_CLIENT(stmt_wrapper->client); ^~~~~~~~~~ statement.c: At top level: cc1: warning: unrecognized command line option ‘-Wno-self-assign’ cc1: warning: unrecognized command line option ‘-Wno-parentheses-equality’ cc1: warning: unrecognized command line option ‘-Wno-constant-logical-operand’ cc1: warning: unrecognized command line option ‘-Wno-cast-function-type’ linking shared-object mysql2/mysql2.so /usr/bin/ld: cannot find -lssl /usr/bin/ld: cannot find -lcrypto collect2: error: ld returned 1 exit status Makefile:259: recipe for target 'mysql2.so' failed make: *** [mysql2.so] Error 1 make failed, exit code 2 Gem files will remain installed in /home/iruk/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/mysql2-0.5.2 for inspection. Results logged to /home/iruk/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/extensions/x86_64-linux/2.6.0-static/mysql2-0.5.2/gem_make.out An error occurred while installing mysql2 (0.5.2), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: mysql2MacOS
参考 : mysql2 gemインストール時のトラブルシュート
/usr/bin/ld: cannot find -lsslこの部分が問題となっており、MacOSにおける解決の為のコマンドとしては下記コマンドになります。
$ gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/' -- --with-cppflags=-I/usr/local/opt/openssl/include --with-ldflags=-L/usr/local/opt/openssl/libしかし、WSLのbrewではopensllの存在するディレクトリが違うため上記コマンドでは問題の解決には至りません。
WSL
最初のエラー文を眺めると、
----- Setting libpath to /home/linuxbrew/.linuxbrew/Cellar/mysql/8.0.16/lib -----とあります。
WSLのbrewでは/home/linuxbrew/.linuxbrew/Cellar以下で管理をしているようですね。
では上記のbundle configを上記ディレクトリに合うように書き換えると、最初のコマンドになります。$ bundle config --local build.mysql2 "--with-ldflags=-L/home/linuxbrew/.linuxbrew/opt/openssl/lib"これでopensllを読み取れるようになって
bundle installを正常にパスできるはず。
- 投稿日:2019-05-07T20:59:15+09:00
Railsのproduction環境でeager_autoload!でハマった
正確に言うとgemで使っているクラス名をModelで使ってしまい、gemが正常に読み込まれなくなってしまっただけです。
Rails5のプロジェクトをproductionで実行すると以下のエラーが発生しました。
$ bundle exec rails s -e production vendor/bundle/ruby/2.4.0/gems/actionmailer-5.2.3/lib/action_mailer.rb:61:in `eager_load!': undefined method `eager_autoload!' for Mail:Class (NoMethodError)まず、eager_load!って何ですか?
config/enviroments/production.rb# Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. # Rake tasks automatically ignore this option for performance. config.eager_load = trueなんかパフォーマンスが良くなるらしい。
次に、RailsのActionMailerにおいてMail:Classにeager_autoload!メソッドがないと言われてしまっています。ActionMailerはMailというgemを使っているのでそれを確認しました。mail-2.7.1/lib/mail.rb# This runs through the autoload list and explictly requires them for you. # Useful when running mail in a threaded process. # # Usage: # # require 'mail' # Mail.eager_autoload! def self.eager_autoload! @@autoloads.each { |_,path| require(path) } endありました。。。
そこでActionMailerでMailクラスを確認すると、、、pry(ActionMailer)> Mail.superclass => ActiveResource::BaseなぜかActiveResourceクラスになっていました。
このプロジェクトではDB無しでActiveResourceを使っているのですが、
app/modelsを確認すると「mail.rb」がありました。。。。gemなどで定義されているクラスに気をつけよう
言い訳をすると、
・config/enviroments/development.rbだとconfig.eager_loadがfalse
・かつ今回の開発ではメール機能は特に使っていなかった
・複数人で開発しておりクラス名などの設計の部分までガバナンスを効かせていなかった
といったことからデプロイ作業の時に初めてこの問題が顕在化しました。
- 投稿日:2019-05-07T17:49:36+09:00
Railsのdeviseで新規登録するとき、belongs_toなものも一緒に作る方法
環境
- Rails 5.2.2
- Ruby 2.5.3
前提
- User belongs_to Organization
- Organization has_many Users
- Userを新規登録するとき、Organizationも一緒に作りたい(というか作れないとエラーで登録できない)
コード
class ApplicationController < ActionController::Base ... before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [organization_attributes: [:name]]) end end# app/views/devise/registrations/new.html.erb ... <% resource.organization ||= Organization.new %> <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= f.error_notification %> <div class="form-inputs"> <%= f.input :email, required: true, autofocus: true , input_html: { autocomplete: "email" }%> <%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length), input_html: { autocomplete: "new-password" } %> <%= f.input :password_confirmation, required: true, input_html: { autocomplete: "new-password" } %> <%= f.fields_for :organization do |organization_form| %> <%= organization_form.input :name %> <% end %> </div> <div class="form-actions"> <%= f.button :submit, t("devise.sign_up") %> </div> <% end %> ...参考
https://github.com/plataformatec/devise#strong-parameters
https://stackoverflow.com/a/7987480/7824640
- 投稿日:2019-05-07T17:49:36+09:00
Railsのdeviseで新規登録するとき、親のモデル(belongs_toなもの)も一緒に作る方法
環境
- Rails 5.2.2
- Ruby 2.5.3
前提
- User belongs_to Organization
- Organization has_many Users
- Userを新規登録するとき、Organizationも一緒に作りたい(というか作れないとエラーで登録できない)
コード
class ApplicationController < ActionController::Base ... before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [organization_attributes: [:name]]) end end# app/views/devise/registrations/new.html.erb ... <% resource.organization ||= Organization.new %> <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= f.error_notification %> <div class="form-inputs"> <%= f.input :email, required: true, autofocus: true , input_html: { autocomplete: "email" }%> <%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length), input_html: { autocomplete: "new-password" } %> <%= f.input :password_confirmation, required: true, input_html: { autocomplete: "new-password" } %> <%= f.fields_for :organization do |organization_form| %> <%= organization_form.input :name %> <% end %> </div> <div class="form-actions"> <%= f.button :submit, t("devise.sign_up") %> </div> <% end %> ...参考
https://github.com/plataformatec/devise#strong-parameters
https://stackoverflow.com/a/7987480/7824640
- 投稿日:2019-05-07T16:52:03+09:00
railsのエラーをまとめてみた
railsのエラーを初心者が勉強の為にまとめてみた。
初心者が陥るエラーなんかエラー画面見たら、だいたいわかるはず。
エラー発生の理由を自分なりに言語化する為。書き方
.表示されるエラー名
.最初に考えられる事
.エラーの理由
.エラーがでた経緯を言語化エラーその1(NoMethodError in Tweets#index)
考えられる事
undefined method `nickname' for nil:NilClassをみる。
①空のクラスにnicknameをなんて定義できないよ!って怒られる。
②cuurent.userって構文から、devise関係であり、deviseのメソッドを使っていることからコントローラー云々の記述ではない。
③current_user(ログイン状態)。ログインしている状態が空なのに、呼び出しの実行までコードが走っているのがおかしい。原因と対策
<% if true %> <%user_signed_in?%>と変更する。気をつける事
deviseの知識がないと解けない問題なので、不安に感じたらdeviseとは何かを調べる必要がある。
エラーその2(ActiveRecord::RecordNotFound in UsersController#show)
考えられる事
コントローラでshowアクションが見つけられないよ!
①そもそもdeviseでログイン機能を実装しているのに、見つけられないはおかしい。
②ログアウト押した時の HTTPパラメータの渡し方を疑う。
③自分でアクション定義していないので、このようなロジックで判断。原因と対策
<% link_to "ログアウト", destroy_user_session_path> の記述を変更する。 <% link_to "ログアウト", destroy_user_session_path, method: :delete %>気をつける事
こう言われる。Couldn't find User with 'id'=sign_outを聞きたい!(メンター確認
エラーその3(Routing Error)
途中飛ばして
考えられる事
ツイート投稿した時にルートが見つからない!
①ツイート新規投稿画面のフォームの飛ばし方を疑う。きちんとしたURLで飛んでない?
②rake routesでパスを確認する。
③対応するcreateアクションがない。おかしいと判断する。
④ルートを確認する。原因と対策
tweetsにcreateも許可する。
Rails.application.routes.draw do devise_for :users root to: "tweets#index" resources :tweets, only: [:index, :show, :new, :destroy, :edit, :update, :create] do resources :comments, only: [:create] end resources :users, only: [:show] end気をつける事
ルーティングエラーはルートを見に行くのが早い??
エラーその4(ActionView::MissingTemplate in Tweets#index)
考えられる事
ツイート投稿した時にルートが見つからない!
①ミッシングテンプレートとなると、部分テンプレートを怪しむ
②部分テンプレートのファイル表記確認する。
③部分テンプレートの表記方法を確認する。原因と対策
部分テンプレート表示させていたファイル名の先頭に_を加える。気をつける事
他に気をつける事は何かを考える、
エラーその5(SyntaxError)
考えられる事
1つtweetコントローラの46行目にエンドがないよ。
原因と対策
classのエンドが抜けていた。エンドを追加
記載するまでもなし気をつける事
SyntaxErrorは構文エラーなので、原因は把握しやすい。慌てずに。
エラーその6(NameError)
考えられる事
メソッドの定義の名前あっているか確認が入っている。
原因と対策
?が抜けている
redirect_to :action => "index" unless user_signed_in?気をつける事
SyntaxErrorは構文エラーとなりそうだが、ならない。なぜなら、構文自体は間違っていないから。
ヘルパーメソッドの定義が違うので、作動しないだけ。注意必要。エラーその7(処理がループしている)
考えられる事
ループ処理が永遠重なっている。
原因と対策
before_action でユーザーログインしていなければ、indexアクションへredirectするように指定している。
before_action :redirect_to_index except: [index] def index hoge end private def redirect_to_index redirect_to :action => "index" unless user_signed_in? end気をつける事
SyntaxErrorは構文エラーとなりそうだが、ならない。なぜなら、構文自体は間違っていないから。
ヘルパーメソッドの定義が違うので、作動しないだけ。注意必要。エラーその8(Routing Error)
考えられること
ルートエラーだからroutesをみる。
原因と対策
createのルート構築ができていない。
Rails.application.routes.draw do devise_for :users root "tweets#index" resources :tweets, only: [:index, :show, :new, :destroy, :edit, :update, :create] #追加 resources :comments, only: [:create] do end resources :users, only: [:show] end気をつける事
routesエラーはその名の通りルーティング周りのを探す。
エラーその9()
考えられること
原因と対策
エラーその10()
考えられること
原因と対策
エラーその11()
考えられること
原因と対策
エラーその12()
考えられること
原因と対策
エラーその13()
考えられること
原因と対策
- 投稿日:2019-05-07T13:52:57+09:00
form_forをもう一度理解する
はじめに
railsの勉強をしていて、避けては通れない
form_forというヘルパーメソッド。今回はこのなんども使うことになるform_forの使い方をもう一度おさらいしていこうと思い、この記事を着手した。form_forとは
form_forは先述した通りrailsのヘルパーメソッドであり、モデルの編集や追加などに使われるメソッドである。つまり、特定のテーブルに対して、レコードの追加やレコードの編集などを行いたい時に、
form_forを用いると簡単にその記述が書けてしまう優れものであるということ。使い方
基本的な使い方は以下の通り。
<%= form_for(モデルクラスのインスタンス) do |f| %> … <% end %>使いたいモデルのインスタンスを引数として渡すことがポイントとなる。
この時に、渡したインスタンスが情報を持っていなければ
createアクションを、インスタンスが情報を持っていたらupdateアクションを行うようになっている。この点が非常に便利となっている一つである。
メソッドの使い方はf.htmlタグ名 :カラム名と指定することで対応するformの作成をすることができる。詳しくはrailsの公式ドキュメントを参照。form_tagとの違い
そして勉強しているときによく似たものとして出てくるのが、
form_tagという存在。この違いを明確にすることで、要所でform_forを扱うことができるようになるだろう。
この二つの大きな違いは、モデルを介する操作かどうかというところによる。
例えば、入力データがモデルを持っていれば、モデルにレコードの追加編集ということでform_forを扱い、入力データがモデルを持っていなければ、単にデータを特定の要件で扱うだけということでform_tagを用いる。form_tagはモデルにレコードとして追加しないため、検索窓、などを作成する時に用いると効果的に扱うことができる。
まとめ
今回は
form_forおよびにform_tagについてまとめてみた。railsを勉強していたら必ずと言っていいほど扱うことになるため、きちんと用途を抑えて扱えるようにしておきたいところだ。
- 投稿日:2019-05-07T12:16:04+09:00
view_contextにしてやられた話
なんだかよく分からないレシーバとの邂逅
不具合がとあるサイトで起きてその改修作業をしていたところ、問題のコードに妙なレシーバを発見した。
view_context
これはなんなんだ?よく分からない。ファイルに対してこの文字列にgrep検索かけても定義された箇所が全く出てこない。おいおい、オメーさんよぉ、一体なんなんだよぉ〜ええ?(ミスタ風)
こいつ、独自にヘルパーメソッド作ったやつ呼ぶのに使われるってよ
独自にヘルパにーメソッドを作るには、
アプリケーション全体で使う場合は、app/helpers/application_helper.rb に書くことができる
app/views/foo/ で使う場合は、app/helpers/foo_helper.rb に書くことができる
ということで、このディレクトリパスに調べに行ったら問題のメソッドを発見することができた。
- 投稿日:2019-05-07T08:35:11+09:00
Rails6 のちょい足しな新機能を試す11(has_secure_password 編)
はじめに
Rails 6 に追加されそうな新機能を試す第11段。 今回のちょい足し機能は、
has_secure_password編です。
has_secure_password にオプションとしてカラムを指定できるようになりました。記載時点では、Rails は 6.0.0.rc1 です。
gem install rails --prereleaseでインストールできます。$ rails --version Rails 6.0.0.rc1プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1 $ cd sandbox6_0_0rc1Gemfile を編集して bcrypt gem を追加する
has_secure_password を使うために bcrypt gem を追加します。
Gemfile の bcrypt の行を有効にします。
Gemfile# Use Active Model has_secure_password gem 'bcrypt', '~> 3.1.7'
bundleを実行します$ bundleUser モデルを作成する
User モデルを作成します。
このとき、password の他に token 用の digest カラムを追加します。$ bin/rails g model User name password_digest token_digest $ bin/rails db:create db:migrateUser モデルを編集する
User model を編集し、
has_secure_passwordを追加します。
:tokenオプションを追加したものとオプションを省略したもの2行を追加します。
オプションを省略した場合は従来と同じですね。app/models/user.rbclass User < ApplicationRecord has_secure_password # これは今までと同じ has_secure_password :token, validations: true # こちらは新機能 end
has_secure_passwordの機能を確認するrails コンソールから確認してみます。
$ bin/rails c Running via Spring preloader in process 393 Loading development environment (Rails 6.0.0.rc1)オプションなしの方は今までと変わりません。
password,password_confirmationなどのメソッドが使えます。irb(main):001:0> u = User.new => #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil> irb(main):002:0> u.password => nil irb(main):003:0> u.password_confirmation => nilオプションを指定した
:tokenに合わせて、tokentoken_confirmationなどのメソッドが追加されます。irb(main):004:0> u.token => nil irb(main):005:0> u.token_confirmation => nilvalidation を試してみると
passwordだけではなくtokenについてもチェックしていることがわかります。irb(main):006:0> u.valid? => false irb(main):007:0> u.errors.full_messages => ["Password can't be blank", "Token can't be blank"]
tokenとtoken_confirmationが一致しているかどうかのチェックもしてくれます。irb(main):008:0> u.password = 'password' => "password" irb(main):009:0> u.password_confirmation = 'pass' => "pass" irb(main):010:0> u.token = 'token' => "token" irb(main):011:0> u.token_confirmation = 'tok' => "tok" irb(main):012:0> u.valid? => false irb(main):013:0> u.errors.full_messages => ["Password confirmation doesn't match Password", "Token confirmation doesn't match Token"]
password_confirmation,token_confirmationを設定してデータベースに保存してみましょう。irb(main):014:0> u.password_confirmation = 'password' => "password" irb(main):015:0> u.token_confirmation = 'token' => "token" irb(main):016:0> u.name = 'user1' => "user1" irb(main):017:0> u.save (0.3ms) BEGIN User Create (0.5ms) INSERT INTO "users" ("name", "password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "user1"], ["password_digest", "$2a$10$uvvJuUHV8fNQ/0GRRtyewuboiAEUpD4Q1.0/coyWWhVxOivHnIMia"], ["token_digest", "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe27/EE56UpC"], ["created_at", "2019-05-03 23:04:03.971339"], ["updated_at", "2019-05-03 23:04:03.971339"]] (7.3ms) COMMIT => true今保存したデータを読み直します。
irb(main):022:0> u = User.last User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]] => #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">password は今まで通り
authenticateで認証できます。irb(main):023:0> u.authenticate('pass') => false irb(main):024:0> u.authenticate('password') => #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">
tokenはauthenticate_tokenで認証できます。irb(main):025:0> u.authenticate_token('password') => false irb(main):026:0> u.authenticate_token('token') => #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">
validations: falseにしたときは?
validations: falseのときは、validation のチェックをしません。 authenticate_token (authenticate_xxx) は使えます。app/models/user.rbclass User < ApplicationRecord has_secure_password has_secure_password :token, validations: false endirb(main):007:0> u = User.new => #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil> irb(main):008:0> u.valid? => false irb(main):009:0> u.errors.full_messages => ["Password can't be blank"] irb(main):010:0> u.password = u.password_confirmation = 'password' => "password" irb(main):011:0> u.token = 'token' => "token" irb(main):012:0> u.save (0.3ms) BEGIN User Create (0.7ms) INSERT INTO "users" ("password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["password_digest", "$2a$10$NC1IH2S8G/RKX7myjNfeeepMknnQUuKIqD60S70iom3ZOQl92j/Cq"], ["token_digest", "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNjr2dibMU/MK"], ["created_at", "2019-05-03 23:42:47.628292"], ["updated_at", "2019-05-03 23:42:47.628292"]] (7.2ms) COMMIT => true irb(main):013:0> u = User.last User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]] => #<User id: 3, name: nil, password_digest: [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47"> irb(main):014:0> u.authenticate_token('pass') => false irb(main):015:0> u.authenticate_token('token') => #<User id: 3, name: nil, password_digest [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYrQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47">"'"]
:validationsオプションを省略したときは?
validations: trueを指定したときと同じ動作になります。おまけ
irb の出力で
password_digestが[FILTERED]となっているのにtoken_digestが[FILTERED]にならないのは、
config/initializers/filter_parameter_logging.rbで設定されていないからです。
tokenも追加してあげれば[FILTERED]になります。config/initializers/filter_parameter_logging.rb# Configure sensitive parameters which will be filtered from the log file. Rails.application.config.filter_parameters += [:password, :token]参考情報
- 投稿日:2019-05-07T07:42:28+09:00
プロジェクトで使っている94つのGemを調べてみた
プログラミングスクールのときは31つのGemを使っていましたが、3ヶ月前に働き出した会社のプロジェクトでは94つもありました。未だに分からないGemが多いため1つ1つ調べていくことにしました。
五月雨式で書いていくので、見づらいかと思いますがご了承ください。
94つのGem
- rails
Ruby on Rails自体がGem
- sass-rails
railsでsassが使えるようになる
【参考記事】Sass + Railsの基礎
- uglifier
UglifyJS2というJavaScriptのコード軽量化ライブラリを、Rubyで簡単に使えるようになる
- bootsnap
railsの起動時間を短縮してくれる
【参考記事】bootsnapについて調べてみた
- puma
スピードと並列性を追求したRubyのWebサーバー
【参考記事】RackサーバーのPumaについて調べてみる
- browserify-rails
Node.jsのモジュールシステムをブラウザでも利用できるようになる
- jquery-ui-rails
railsにjQuery-uiを使えるようになる
- mysql2
RubyからMySQLへ接続できるようにする
- byebug
プログラムを途中で止めることができる。そのままコンソールを使える。
【参考記事】Ruby on Rails初心者必見。Railsでのデバッグの味方byebug(バイバグ)。
- rails-controller-testing
コントローラーにテストを書くことができる
- rubocop
コードスタイルを統一できる
【参考記事】cookpadのスタイルガイド
- slim_lint
「Slim template」のLintツール
【参考記事】Slim-Lint を使ってみる
- timecop
日付、時間のテストができるようになる
【参考記事】RailsでTimecopを使って日付/時間のテストをする
- guard
テストを自動化する
【参考記事】Guardによるテストの自動化
- minitest-ar-assertions/minitest-power_assert/minitest-stub_any_instance
minitestをより使いやすくするためのGem
【参考記事】minitestのオススメ基本設定調べてみた
- better_errors
エラー画面を見やすくしてくれる。変数の中身も表示してくれる
【参考記事】Railsのデバッグが捗るエラー画面Better Errors使ってみた!
- binding_of_caller
メソッド呼び出し元のbindingでevalすることができる。
(よく分からなかったので、今度しっかり調べる)【参考記事】【黒魔術】binding_of_caller gem でメソッド呼び出し元の binding を手に入れる
- bullet
繰り返しのある記述(N+1問題)を検出してくれる
- listen
ファイルの変更を検知してそれをフックに何か処理ができる
- rack-dev-mark
開発環境と本番環境でのご操作を防ぐ対策になる
【参考記事】Rack_dev_markで開発環境と本番環境を視覚で判断しよう
- rack-mini-profiler
パフォーマンスをブラウザに表示する
【参考記事】ページのロードにかかった時間が表示されます。
- spring
アプリケーションの動作を早くする
- spring-watcher-listen
springのファイルシステムの変更検知方法をpollingからlistenに変更してくれる
- devise-i18n-views
【参考記事】i18n:deviseを日本語表記にする。
- easy_translate
google翻訳が使える
- i18n-debug
i18n で参照しているパスを調べてくれる
【参考記事】i18n-debug の基本的な使い方
- i18n-tasks
ロケールファイルで不足している翻訳や未使用の翻訳を検出してくれる
【参考記事】i18n-tasks | Rails のロケールファイルを整理整頓する
- i18n_generators
日本語化を助ける
【参考記事】i18n_generators で Rails アプリを日本語化
- brakeman
開発中の静的セキュリティチェック
- bundler-audit
gemのセキュリティ情報のチェック
- html2slim
erbファイルをSlimに一括変換する
- license_finder
パッケージのライセンスを出力する
【参考記事】プロジェクトで使用しているパッケージのライセンスを出力する
- rails_best_practices
railsアプリのコードの質をチェックするメトリクスツール
【参考記事】Ruby on Railsのコードをよりモダンにするために、Rails Best Practices入門・おすすめ設定
- stackprof
プログラムが実行された際、どの部分のコードの実行にどれだけの時間がかかったのかを計測してくれる
【参考記事】StackProfを使ってrubyプログラムのプロファイリングをする方法
- swagger_ui_engine
Swagger-UIをMountable Engineとして使える。
(よく分からなかったので、今度しっかり調べる)【参考記事】Swagger+JSON SchemaでAPIの型をテストして開発サイクルをスピードアップさせた話
- daemons
rubyスクリプトをデーモン化し、外部からコントロール(start, stop等)できるようにする。
【参考記事】BestGems Pickup! 第6回
- delayed_job/delayed_job_active_record
バックグラウンドで非同期処理をしてくれる
【参考記事】delayed_jobの機能についてもうちょっと
【参考記事】RailsでAcitiveJobとDelayedJobを使ってバックグランド処理を行う
- delayed_job_web
遅延ジョブを監視する
- letter_opener_web
送信メールがWebインタフェースで確認できる
【参考記事】【Rails】letter_opener_webを利用してメールを確認する
- rails_db
GUIでDB内容が見れる
- activerecord-session_store
データベースにセッションを保管する
- devise
ログイン機能の実装を簡単にしてくれる
【参考記事】【Rails】deviseを導入してみる
- devise-encryptable
(よく分からなかったので、今度しっかり調べる)
【参考記事】Rails4: 古いdeviseのパスワードを新しいdeviseで使う方法
- pundit
認可まわりの機能を作成する
【参考記事】Punditを使って権限を管理する
- grape/grape-entity/grape-swagger/grape-swagger-entity
Restful な WEB-API が簡単に作成できる
【参考記事】Rails5でGrapeを使って簡単にAPIを実装する
- aasm
状態遷移の機能を簡単に付けることができる機能
【参考記事】aasm を使って,Ruby のクラスに状態遷移の機能を付与する
- activerecord-import
まとめてINSERTできる。
【参考記事】(初心者向け)activerecord-importの使い方
- activerecord-userstamp
ユーザがモデルに対して作成、更新、削除した情報を持たせることができる
- acts_as_list
モデルの並び順の維持や、並びの変更を行う
- by_star
日付分析関連の便利scope群
- draper
第4のレイヤを提供するGem
【参考記事】Draperで驚くほどRailsコードがわかりや少なったよ!
- enum_help
enumをI18n化する
【参考記事】【Rails】enumをI18n対応させるenum_helpが便利すぎた
- paper_trail/paper_trail-association_tracking
監査、またはバージョン管理のためにモデルの変更を追跡するツール
- paranoia
論理削除を実装できる
- validates_timeliness
動く日付時刻用のvalidates
- cells/cells-rails/cells-slim
ビューの一部を切り離し、Rubyの通常のクラスとしてコンポーネント化する
【参考記事】Railsコードを改善する7つの素敵なGem
- cocoon
1対多で関連するデータを1つのフォーム画面に記述できる
【参考記事】nested_form はもう古い!? Cocoon で作る1対多のフォーム
- data-confirm-modal
削除確認ダイアログを出してくれる
Data-Confirm Modalを使って Rails の削除確認ダイアログをいい感じにする
- font-awesome-rails
FontAwesomeのアイコンを使えるようになる
【参考記事】Font Awesome Lcons
- icalendar
icalendarがrailsで使えるようになる
- kaminari
ページネーション機能が使える
railsチュートリアルで使われているgem「will_paginate」と同じような用途
【参考記事】railsチュートリアル 9章
- ransack
検索機能
【参考記事】Ransackのススメ
- redcarpet
マークダウンを実装できる
【参考記事】Railsのプロジェクトにgem 'redcarpet' を用いてマークダウンエディタを導入してみた
- simple_form
formの記述を簡単な形に、少なくできる
【参考記事】Railsのform_for内のコードをすっきりさせるsimple_formの使い方
- slim/slim-rails
HTMLのViewをSlim形式で書くことができる。コード量が少ない
【参考記事】Slim入門をして書き方を練習をしました
- config
開発環境ごとに定数を YAML ファイルで管理できる
【参考記事】Railsで定数を環境ごとに管理するrails_config(現 config)
- dotenv-rails
カレントディレクトリに .env という名前で環境変数を書き込んでおくと、自動的にENVの中にその値を追加してくれる
【参考記事】環境によって変わる設定値はdotenvを使うと便利
- seedbank
seeds.rbを分割し、ファイルの肥大化を防いでくれる
- business_time
Dateに対していろいろなmethodが使えるようになる。
【参考記事】[ruby] 祝日判断や休日判断をrubyでやるなら、これでいいんじゃないかな - メモ
- geoip
特定のホストまたはIPアドレスを検索し、そのIPアドレスが割り当てられている国などの情報を返してくれる
- gon
controller内でセットした変数をJavascript内で使う事ができる。
- hashie
Hashをより良くしてくれる
【参考記事】Hash をよりパワフルにする hashie gem を試す #ruby
- responders
respond_withとrespond_toが使える
【参考記事】respond_withとクラスレベルのrespond_toの扱いについて
- whenever
cronのバッチを作成して、定期的に回すことができる。
【参考記事】Railsで定期的にバッチ回す「Whenever」
- aws-sdk-rails
s3に画像ファイルをフォームからアップロードできる
【参考記事】railsのaws-sdk gemを使ってs3に画像ファイルをフォームからアップロードする
- ridgepole
スキーマ管理用のコマンドラインツール
【参考記事】Rails マイグレーションの廃止とRidgepoleの導入・利用方法
- aws-sdk
S3を使えるようになる
- rack-attack
同一IPからの過剰なアクセスを制限できる
- sentry-raven
エラーログ収集に使うツール
【参考記事】RailsアプリケーションにSentryを導入する
- whiny_validation
Validation Errorをログ出力
- derailed_benchmarks
RailsアプリやRubyアプリのさまざまなベンチマークを取ることができる。
【参考記事】Rails: 多機能ベンチマークgem「derailed_benchmarks」README(翻訳)
- memory_profiler
Railsアプリで文字列ががっつり重複を取り除いてくれる
【参考記事】Rails: memory_profilerでRuby文字列の重複を削減する(翻訳)
最後に一言
何か間違っている点があれば、教えて頂けたら幸いです。
よろしくお願い致します。
- 投稿日:2019-05-07T05:04:26+09:00
【10日間でポートフォリオ作成に挑戦】10日目:AWSでのデプロイ
概要
今回は、2019年のGW期間(10日間)を全て費やして取り組む
ポートフォリオの製作過程を取りまとめた内容を投稿させて頂きます。(投稿は毎日行う予定)全体通した取り組みの詳細については、前回までの記事をご参照ください。
【10日間でポートフォリオ作成に挑戦】1日目:要件定義〜記事投稿のCRUD
【10日間でポートフォリオ作成に挑戦】2日目:アクセス制限〜コメントのCRUD機能
【10日間でポートフォリオ作成に挑戦】3日目:ページネーション~CKEditorの導入
【10日間でポートフォリオ作成に挑戦】4日目:テーブル分割〜CKEditorのフォームへの反映
【10日間でポートフォリオ作成に挑戦】5日目:CKEditorへ画像アップロード機能を追加
【10日間でポートフォリオ作成に挑戦】6日目:テストコードの実装
【10日間でポートフォリオ作成に挑戦】7日目:検索機能〜いいね機能の実装
【10日間でポートフォリオ作成に挑戦】8日目:記事ストック機能〜ユーザーフォロー機能の実装
【10日間でポートフォリオ作成に挑戦】9日目:フロントエンドの実装〜各種機能の修正今日一日の作業内容
ここからは、今日1日で取り組んだ作業内容をご説明します。
本番環境へのデプロイ
一通りの機能の実装が完了したので、本番環境へのデプロイを行います。
デプロイにあたっては、AWSを利用しています。下記の様な構成です
- 仮想サーバー:EC2(AWS)
- ストレージ:S3(AWS)
- アプリケーションサーバー:Unicorn
- WEBサーバー:Nginx
- データベース:MySQL
- 自動デプロイ:Capistrano
これでデプロイ出来たものが、下記のURLです。
http://3.112.115.114/今日の失敗
ここから、今日の失敗をまとめます。
画像アップロードが本番環境で動作しない
開発環境では正常に動作していた画像アップロードですが、本番環境ではエラーが発生してしまい、正常に動作しませんでした。
アクセスキーの設定ミス
エラーログを確認して見ると、下記の様な内容でした。
rake stderr: rake aborted! NoMethodError: undefined method `match' for nil:NilClass色々調べて見ると、どうやらAWSのアクセスキーの読み込みが上手く行っていなかった様でした。
これまで、
secrets.ymlで読み込ませる方法しか実践した事が無かったのですが、今回Rails5.2を利用する事にした為、secrets.ymlがcredentials.ymlに置き換わっていた事を失念していました。なので、
credentials.ymlからアクセスキーを読み込ませる様に設定を変更しました。
なお、credentials.ymlは直接エディタで編集を行えない為、ターミナルから下記コマンドを実行して、編集を行う必要があります。$ EDITOR="vi" bin/rails credentials:editまた、復号化に必要な
master.keyは、デフォルトで.gitignoreに追加されているため、Capistranoの自動デプロイに関する設定ファイルに読み込ませる為の記述を追加する必要があります。config/deploy.rbset :linked_files, %w{ config/master.key } (中略) desc 'upload master.key' task :upload do on roles(:app) do |host| if test "[ ! -d #{shared_path}/config ]" execute "mkdir -p #{shared_path}/config" end upload!('config/master.key', "#{shared_path}/config/master.key") end end before :starting, 'deploy:upload' after :finishing, 'deploy:cleanup' end私は、前に実装したコードをそのまま引用した為に、
master.keyと指定すべき箇所を、secrets.ymlと指定してエラーになってしまいました。Shrineの設定
上記のアクセスキーの設定を行った後も、別のエラーが発生しました。
Shrine::Error (storage :cache isn't registered on ImageUploader):公式のリファレンスも確認して、コードを書き換えたりしたのですが、これが一向に解消されません。
uploaders/image_uploader.rbrequire 'image_processing/mini_magick' class ImageUploader < Shrine plugin :remove_attachment plugin :pretty_location plugin :processing plugin :versions plugin :delete_raw plugin :store_dimensions, analyzer: :mini_magick process(:store) do |io, _| versions = { original: io } io.download do |original| pipeline = ImageProcessing::MiniMagick.source(original) versions[:standard] = pipeline.resize_to_limit!(400, 400) end versions end endconfig/initializers/shrine.rbrequire 'shrine/storage/s3' if Rails.env.production? s3_options = { access_key_id: Rails.application.credentials.dig(:aws, :access_key_id), secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key), region: 'ap-northeast-1', bucket: 'gooderorrs', } Shrine.storages = { cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), store: Shrine::Storage::S3.new(**s3_options), } else require 'shrine/storage/file_system' Shrine.storages = { cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'), store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads') } end Shrine.plugin :activerecord Shrine.plugin :backgrounding Shrine.plugin :logging Shrine.plugin :determine_mime_type Shrine.plugin :cached_attachment_data Shrine.plugin :restore_cached_dataこれについては、今日中の解決が困難だった為、一旦持ち越しにしています。
今後の予定
結局、当初予定していた機能を期間内で全て実装する事が出来ませんでした・・・
その辺りの改善点などは、また時間が取れた時にまとめて投稿しようと思います。また、これで終了ではなく、機能拡張は続けて行くので、それもある程度まとまった内容が書ける段階で記事に出来ればと考えています。
おまけ
最後になりますが、現在、私は下記の目標を立てて学習に取り組んでいます。
- 3年間で「10,000時間」をプログラミングに費やす
- その間、毎日ブログの投稿を行う
Twitterでは、その過程で学んだ事などを発信しています。
もし宜しければフォローしてみてください。













