- 投稿日:2019-10-24T22:55:24+09:00
Railsのテストについて
テストの種類
全体的なテスト
・システムテスト:ブラウザを通してアプリケーションの挙動を外部的に確認できるテスト
・結合テスト:いろいろな機能の連続を確認するテスト
・機能テスト:コントローラ単位のテスト個々の部品のテスト(モデル・ルーティング・ビュー・ヘルパー・メーラー・ジョブ)
テストの頻度
モデルのテスト(高頻度)
結合テスト(高頻度)
ルーティング・メーラー・ジョブのテスト(モデルよりは頻度が高くない)
テストを行うために必要なもの
・データベース(自分で準備する)
・テストデータ(自動的に作られる)RSpecとCapybaraを使ってテストを行う
Unitテスト(ユニットテスト・単体テスト)
class 〇〇Test < ActiveSupport::TestCase test "test name" do #実行するコード assertion #ここにassertionメソッドを #結果の確認 #testブロックの中に最低一つは必要 end endtestは
test name do
実行するコード
確認用のassertionメソッド
endで行う
Integrationテスト(統合テスト)
複数のコントローラーに跨って、ユーザーの実際の操作を追跡するような用途で利用する。
→多段階のプロセスの追跡が可能作り方(Integrationテストは自分で作る必要がある)
rails generate integration_test testnameテストの準備と後始末(テストスクリプトでの予約メソッド)
setup (使用するリソースの初期化)
teardown(使用したリソースの後始末)
これらは基底クラスで定義されているので、個別にオーバーライドして使用する
- 投稿日:2019-10-24T21:52:43+09:00
【Rails】mysqlオプションを付けた新たなアプリケーション作成時のエラー対処(Mac)
//////////////////////////////////////////
MacOS Mojave ver.10.14.6
Ruby 2.5.3p105
Rails 6.0.0//////////////////////////////////////////
はじめに
railsプロジェクト作成の際に、
railsインストールしてrails newをしたところエラーが発生しました。
苦戦したため、備忘のため記録残します。
※悪戦苦闘しながら急ぎ作成したため少し伝わりにくいところもあるかと思います。
時間のあるときに修正を加えていきたいと思います。対処方法(要約)
今回の経験とネット情報をまとめますと、以下の対処が良いかと思います。
試してみてください。・権限付加を問われたら
$ sudo chown -R [ユーザ名]:staff /Users/[ユーザ名]/.rbenv・ERROR: Error installing mysql2 みたいなのが出たら
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include"※それでもダメなら、再インストール
起こったこと(長いです)
「blog」という名前のファイルをmysqlオプション付きで作成するため、以下のコマンドを実行
$ rails _6.0.0_ new blog -d mysqlすると、以下のような表示がされました
create create README.md create Rakefile create .ruby-version create config.ru create .gitignore create Gemfile run git init from "." Initialized empty Git repository in /Users/xxx/projects/blog/.git/ create package.json create app create app/assets/config/manifest.js create app/assets/stylesheets/application.css create app/channels/application_cable/channel.rb create app/channels/application_cable/connection.rb create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/javascript/channels/consumer.js create app/javascript/channels/index.js create app/javascript/packs/application.js create app/jobs/application_job.rb create app/mailers/application_mailer.rb create app/models/application_record.rb create app/views/layouts/application.html.erb create app/views/layouts/mailer.html.erb create app/views/layouts/mailer.text.erb create app/assets/images/.keep create app/controllers/concerns/.keep create app/models/concerns/.keep create bin create bin/rails create bin/rake create bin/setup create bin/yarn create config create config/routes.rb create config/application.rb create config/environment.rb create config/cable.yml create config/puma.rb create config/spring.rb create config/storage.yml create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb create config/initializers create config/initializers/application_controller_renderer.rb create config/initializers/assets.rb create config/initializers/backtrace_silencers.rb create config/initializers/content_security_policy.rb create config/initializers/cookies_serializer.rb create config/initializers/cors.rb create config/initializers/filter_parameter_logging.rb create config/initializers/inflections.rb create config/initializers/mime_types.rb create config/initializers/new_framework_defaults_6_0.rb create config/initializers/wrap_parameters.rb create config/locales create config/locales/en.yml create config/master.key append .gitignore create config/boot.rb create config/database.yml create db create db/seeds.rb create lib create lib/tasks create lib/tasks/.keep create lib/assets create lib/assets/.keep create log create log/.keep create public create public/404.html create public/422.html create public/500.html create public/apple-touch-icon-precomposed.png create public/apple-touch-icon.png create public/favicon.ico create public/robots.txt create tmp create tmp/.keep create tmp/cache create tmp/cache/assets create vendor create vendor/.keep create test/fixtures create test/fixtures/.keep create test/fixtures/files create test/fixtures/files/.keep create test/controllers create test/controllers/.keep create test/mailers create test/mailers/.keep create test/models create test/models/.keep create test/helpers create test/helpers/.keep create test/integration create test/integration/.keep create test/channels/application_cable/connection_test.rb create test/test_helper.rb create test/system create test/system/.keep create test/application_system_test_case.rb create storage create storage/.keep create tmp/storage create tmp/storage/.keep remove config/initializers/cors.rb remove config/initializers/new_framework_defaults_6_0.rb run bundle install The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 13.0.0 Using concurrent-ruby 1.1.5 Using i18n 1.7.0 Using minitest 5.12.2 Using thread_safe 0.3.6 Using tzinfo 1.2.5 Using zeitwerk 2.2.0 Using activesupport 6.0.0 Using builder 3.2.3 Using erubi 1.9.0 Using mini_portile2 2.4.0 Using nokogiri 1.10.4 Using rails-dom-testing 2.0.3 Using crass 1.0.5 Using loofah 2.3.1 Using rails-html-sanitizer 1.3.0 Using actionview 6.0.0 Using rack 2.0.7 Using rack-test 1.1.0 Using actionpack 6.0.0 Using nio4r 2.5.2 Using websocket-extensions 0.1.4 Using websocket-driver 0.7.1 Using actioncable 6.0.0 Using globalid 0.4.2 Using activejob 6.0.0 Using activemodel 6.0.0 Using activerecord 6.0.0 Using mimemagic 0.3.3 Using marcel 0.3.3 Using activestorage 6.0.0 Using mini_mime 1.0.2 Using mail 2.7.1 Using actionmailbox 6.0.0 Using actionmailer 6.0.0 Using actiontext 6.0.0 Using public_suffix 4.0.1 Using addressable 2.7.0 Fetching bindex 0.8.1 Installing bindex 0.8.1 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/bindex-0.8.1 An error occurred while installing bindex (0.8.1), and Bundler cannot continue. Make sure that `gem install bindex -v '0.8.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: web-console was resolved to 4.0.1, which depends on bindex run bundle binstubs bundler Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile. run bundle exec spring binstub --all bundler: command not found: spring Install missing gem executables with `bundle install` rails webpacker:install Could not find gem 'mysql2 (>= 0.4.4)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.エラー部分から和訳すると
ネイティブ拡張を使用したbindex 0.8.1のインストール Errno :: EACCES:許可が拒否されました bindex(0.8.1)およびBundlerのインストール中にエラーが発生したため続行できません。 bundle installする前に`gem install bindex -v '0.8.1' --source 'https://rubygems.org/'`がうまくいくか確認してください。 Gemfile: web-consoleは4.0.1に解決されました。 bindex bundlerで一連のGemのBinstubのインストールを実行します。 Gemfileにリストされているgemソースのいずれにもgem 'mysql2(> = 0.4.4)'が見つかりませんでした。 bundle exec spring binstub --allを実行します Bundler:springのコマンドが見つかりません `bundle install`で見つからないgem実行可能ファイルをインストールします rails webpacker:install Gemfileにリストされているgemソースのいずれにもgem 'mysql2(> = 0.4.4)'が見つかりませんでした。 `bundle install`を実行して、欠落しているgemをインストールします。と言った感じです。
※間違ってたらご指摘ください。まずは、最初の指示通り
$ gem install bindex -v '0.8.1'を実施。
すると、Building native extensions. This could take a while... ERROR: While executing gem ... (Errno::EACCES) Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/bindex-0.8.1まさかのエラー。。。
和訳するとネイティブ拡張の構築。 これにはしばらく時間がかかる可能性があります... エラー:gemの実行中...(Errno :: EACCES) 許可が拒否されました@ dir_s_mkdir-/Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/bindex-0.8.1vなぜ、許可されない。。。
権限の問題か?
ということで、root権限でコマンドを実行することに$ sudo gem install bindex -v '0.8.1'すると
「Password:」とパスワードを要求されるので」、
自分のパソコンのパスワードを入力すると
Building native extensions. This could take a while... Successfully installed bindex-0.8.1 Parsing documentation for bindex-0.8.1 Installing ri documentation for bindex-0.8.1 Done installing documentation for bindex after 0 seconds 1 gem installedが表示!!
お、お?! 激しく動揺手が震えてタイピングが、、、
Google先生、和訳をお願いします!!command + C
command + Vネイティブ拡張の構築。 これにはしばらく時間がかかる可能性があります... bindex-0.8.1が正常にインストールされました bindex-0.8.1の解析ドキュメント bindex-0.8.1のRIドキュメントのインストール 0秒後にbindexのドキュメントのインストールを完了しました 1つのgemがインストールされましたインストール成功!!
さっそく、確認!!
$ cd blog (blogはファイル名です) $ bundle installどうだ!
Fetching msgpack 1.3.1 Installing msgpack 1.3.1 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/msgpack-1.3.1 An error occurred while installing msgpack (1.3.1), and Bundler cannot continue. Make sure that `gem install msgpack -v '1.3.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: bootsnap was resolved to 1.4.5, which depends on msgpackなんと、別のエラー。。。
が、しかし
指示通り実行するだけだ!$ sudo gem install msgpack -v '1.3.1'※またしても許可されなかったため、root権限で実行してます
Building native extensions. This could take a while... Successfully installed msgpack-1.3.1 Parsing documentation for msgpack-1.3.1 Installing ri documentation for msgpack-1.3.1 Done installing documentation for msgpack after 0 seconds 1 gem installedOK!
さっそく、確認!!$ bundle installFetching bootsnap 1.4.5 Installing bootsnap 1.4.5 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/bootsnap-1.4.5 An error occurred while installing bootsnap (1.4.5), and Bundler cannot continue. Make sure that `gem install bootsnap -v '1.4.5' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: bootsnapはい次!
$ sudo gem install bootsnap -v '1.4.5'Building native extensions. This could take a while... Successfully installed bootsnap-1.4.5 Parsing documentation for bootsnap-1.4.5 Installing ri documentation for bootsnap-1.4.5 Done installing documentation for bootsnap after 0 seconds 1 gem installedはい確認!!
$ bundle installFetching byebug 11.0.1 Installing byebug 11.0.1 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/byebug-11.0.1 An error occurred while installing byebug (11.0.1), and Bundler cannot continue. Make sure that `gem install byebug -v '11.0.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: byebugはい次!
$ sudo gem install byebug -v '11.0.1'Building native extensions. This could take a while... Successfully installed byebug-11.0.1 Parsing documentation for byebug-11.0.1 Installing ri documentation for byebug-11.0.1 Done installing documentation for byebug after 12 seconds 1 gem installedはい確認!!
$ bundle installFetching ffi 1.11.1 Installing ffi 1.11.1 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/ffi-1.11.1 An error occurred while installing ffi (1.11.1), and Bundler cannot continue. Make sure that `gem install ffi -v '1.11.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: spring-watcher-listen was resolved to 2.0.1, which depends on listen was resolved to 3.1.5, which depends on rb-inotify was resolved to 0.10.0, which depends on ffiはい次!
$ sudo gem install ffi -v '1.11.1'Building native extensions. This could take a while... Successfully installed ffi-1.11.1 Parsing documentation for ffi-1.11.1 Installing ri documentation for ffi-1.11.1 Done installing documentation for ffi after 19 seconds 1 gem installedはい確認!!
$ bundle installFetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/mysql2-0.5.2 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: mysql2はい次!
$ sudo gem install mysql2 -v '0.5.2'Building native extensions. This could take a while... ERROR: Error installing mysql2: ERROR: Failed to build gem native extension. current directory: /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/ext/mysql2 /Users/xxx/.rbenv/versions/2.5.3/bin/ruby -I /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/site_ruby/2.5.0 -r ./siteconf20191024-7974-ialg44.rb extconf.rb 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 /usr/local/opt/mysql@5.6/bin/mysql_config ----- checking for mysql.h... yes checking for errmsg.h... yes checking for SSL_MODE_DISABLED in mysql.h... no checking for MYSQL_OPT_SSL_ENFORCE in mysql.h... no 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... yes ----- Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load ----- ----- Setting libpath to /usr/local/opt/mysql@5.6/lib ----- creating Makefile current directory: /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/ext/mysql2 make "DESTDIR=" clean current directory: /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/ext/mysql2 make "DESTDIR=" compiling client.c compiling infile.c compiling mysql2_ext.c compiling result.c compiling statement.c linking shared-object mysql2/mysql2.bundle ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [mysql2.bundle] Error 1 make failed, exit code 2 Gem files will remain installed in /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2 for inspection. Results logged to /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/mysql2-0.5.2/gem_make.outまたエラー;
ひとまず順に和訳する。ネイティブ拡張の構築。 これにはしばらく時間がかかる可能性があります... エラー:mysql2のインストールエラー: エラー:gemネイティブ拡張のビルドに失敗しました。 ~ ----- /usr/local/opt/mysql@5.6/bin/mysql_configでmysql_configを使用する ----- ~ ----- MySQLライブラリがパスされずmysql2がロードされませんない場合、システムにrpathを設定する方法がわからない ----- ----- libpathを/usr/local/opt/mysql@5.6/libに設定する ----- Makefileの作成 現在のディレクトリ:/Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/ext/mysql2 「DESTDIR =」をクリーンにする ~ clang:エラー:リンカコマンドが終了コード1で失敗しました(呼び出しを確認するには-vを使用してください) make:*** [mysql2.bundle]エラー1 失敗、終了コード2 Gemファイルは、検査のために /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2 にインストールされたままになります。 結果のログ /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/mysql2-0.5.2/gem_make.outとりあえずわかるのは
mysql2で何かしらのエラーこれ、StackOverFlowにて対策が投稿されていたので、参照してみる
https://stackoverflow.com/questions/30834421/error-when-trying-to-install-app-with-mysql2-gemFor anybody still experiencing the issue: When you install openssl via brew, you should get the following message: Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries Generally there are no consequences of this for you. If you build your own software and it requires this formula, you'll need to add to your build variables: LDFLAGS: -L/usr/local/opt/openssl/lib CPPFLAGS: -I/usr/local/opt/openssl/include PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig You can set these build flags (for the local application) by running the following: bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include"和訳すると
まだ問題が発生している場合: brew経由でopensslをインストールすると、次のメッセージが表示されます。 Appleは、独自のTLSおよび暗号ライブラリを支持して、OpenSSLの使用を廃止しました 通常、これによる影響はありません。 独自のソフトウェアをビルドし、この式が必要な場合は、ビルド変数に追加する必要があります。 LDFLAGS:-L / usr / local / opt / openssl / lib CPPFLAGS:-I / usr / local / opt / openssl / include PKG_CONFIG_PATH:/ usr / local / opt / openssl / lib / pkgconfig 以下を実行して、これらのビルドフラグを設定できます(ローカルアプリケーション用)。 bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include"ということで、以下を実行!!
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include"You are replacing the current local value of build.mysql2, which is currently nil和訳する!
現在nilであるbuild.mysql2の現在のローカル値を置き換えています何か実行はされたみたい!
いけたのか?さっそく確認!!
$ bundle installFetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/mysql2-0.5.2 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: mysql2同じエラー!!
変わってないやん!!本来はこの辺りでうまくいくらしいが、どうやら最新のVerはそんな生ぬるくないらしい、、、
もうこうなったら
アンインストールして、再インストールしかない!!
※手順はこちらを参照させていただきました
https://qiita.com/akiko-pusu/items/aef52b723da2cb5dc596そして再インストールを実行!
Fetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Errno::EACCES: Permission denied @ rb_sysopen - /Users/maedamasaki/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/CHANGELOG.md 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: mysql2変わらず!!
でも、ここで指摘されているのは結局権限!!こちらのサイト (http://infinity108.com/114/) を参照させていただくと
どうやら$ sudo gem installに問題があるみたい。
そこで、今度は以下の方法で権限を付加します。$ sudo chown -R [ユーザ名]:staff /Users/[ユーザ名]/.rbenv↓↓↓
$ sudo chown -R xxx ~/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2 $ sudo chown -R xxx ~/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/mysql2-0.5.2そして確認!!
$ bundle installFetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Errno::EACCES: Permission denied @ dir_s_mkdir - /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/puma-3.12.1 An error occurred while installing puma (3.12.1), and Bundler cannot continue. Make sure that `gem install puma -v '3.12.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: pumaおー、違うエラーが!!
$ sudo chown -R xxx ~/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/puma-3.12.1chown: /Users/xxx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-static/puma-3.12.1: No such file or directoryと出たので、以下でチャレンジ!
$ sudo chown -R xxx ~/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/extensions/x86_64-darwin-18/2.5.0-staticよし!
さっそく確認!$ bundle installThe dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 13.0.0 Using concurrent-ruby 1.1.5 Using i18n 1.7.0 Using minitest 5.12.2 Using thread_safe 0.3.6 Using tzinfo 1.2.5 Using zeitwerk 2.2.0 Using activesupport 6.0.0 Using builder 3.2.3 Using erubi 1.9.0 Using mini_portile2 2.4.0 Using nokogiri 1.10.4 Using rails-dom-testing 2.0.3 Using crass 1.0.5 Using loofah 2.3.1 Using rails-html-sanitizer 1.3.0 Using actionview 6.0.0 Using rack 2.0.7 Using rack-test 1.1.0 Using actionpack 6.0.0 Using nio4r 2.5.2 Using websocket-extensions 0.1.4 Using websocket-driver 0.7.1 Using actioncable 6.0.0 Using globalid 0.4.2 Using activejob 6.0.0 Using activemodel 6.0.0 Using activerecord 6.0.0 Using mimemagic 0.3.3 Using marcel 0.3.3 Using activestorage 6.0.0 Using mini_mime 1.0.2 Using mail 2.7.1 Using actionmailbox 6.0.0 Using actionmailer 6.0.0 Using actiontext 6.0.0 Using public_suffix 4.0.1 Using addressable 2.7.0 Using bindex 0.8.1 Using msgpack 1.3.1 Using bootsnap 1.4.5 Using bundler 2.0.2 Using byebug 11.0.1 Using regexp_parser 1.6.0 Using xpath 3.2.0 Using capybara 3.29.0 Using childprocess 3.0.0 Using ffi 1.11.1 Using jbuilder 2.9.1 Using rb-fsevent 0.10.3 Using rb-inotify 0.10.0 Using ruby_dep 1.5.0 Using listen 3.1.5 Using method_source 0.9.2 Using mysql2 0.5.2 Fetching puma 3.12.1 Installing puma 3.12.1 with native extensions Fetching rack-proxy 0.6.5 Installing rack-proxy 0.6.5 Using thor 0.20.3 Using railties 6.0.0 Using sprockets 3.7.2 Using sprockets-rails 3.2.1 Using rails 6.0.0 Fetching rubyzip 2.0.0 Installing rubyzip 2.0.0 Fetching sass-listen 4.0.0 Installing sass-listen 4.0.0 Fetching sass 3.7.4 Installing sass 3.7.4 Fetching tilt 2.0.10 Installing tilt 2.0.10 Fetching sass-rails 5.1.0 Installing sass-rails 5.1.0 Fetching selenium-webdriver 3.142.6 Installing selenium-webdriver 3.142.6 Fetching spring 2.1.0ついに完了!!!!
長かった!!!
メモ
※with native extensions
gemの中には、ruby以外の言語(ネイティブ:C、C++)に依存しているものもあり、そのような依存関係のあるものをインストールする場合に表示される。
https://kossy-web-engineer.hatenablog.com/entry/2019/01/23/202225※bundler, bundle install
https://www.sejuku.net/blog/19426#bundler※root権限(sudo)
https://www.atmarkit.co.jp/ait/articles/1611/28/news036.html※その他、参考になりそうなリンクも念の為貼っておきます。勉強します。
・gem installでpermission deniedされました
https://qiita.com/tokimari/items/feda1ed61f2d8b5b317c
- 投稿日:2019-10-24T21:34:52+09:00
Rails チュートリアル 6章 独学学習メモ
こちらは『Railsチュートリアル』の独学学習のメモになります。
環境について
- Macを使用
- cloud9を利用第6章 ユーザーのモデルを作成する
6.1 Userモデル
Railsでは、データを永遠に保存する方法として、データベースを使用します。
RailsライブラリのActive Recordを使うことによって簡単に操作することが可能になります。また、Rubyでデータに関することが定義ができるようになります。6.1.1 データベースの移行
Railsは、データ保存をするときに、リレーショナルデータベースを使います。各行は、それぞれデータ属性のカラムを持ちます。
まず、ユーザーコントローラを作っていきます。
rails g controller Users new
その後、Userモデルを作っていきます。
nameや emailの属性を使ったUserモデルを使っていきます。rails generate model User name:string email:string※コントローラ名は、複数形。モデル名は、単数形。モデルのテーブル名は、複数形です。
name:stringやemail:stringオプションのパラメータを指定することで、属性を指定することができます。
generateコマンドでマイグレーションファイルが作成されます。
このファイルは、Rubyでデータベースについての変更の命令を定義し、その後、その命令はActiveRecordによって翻訳されて、SQLとなり実行されます。そして、ファイル名前に作成された時間のタイムスタンプがつきます。それは、同じファイルが作られないようにするためです。つまり、同じファイル名の競合を防ぐためです。
そして、マイグレーションを実行します。
rails db:migrate実行するとこのようなファイルが作成されます。
db/development.sqlite3
このファイルをそのまま開こうとすると、全く読めない文字列が出てきますが、DB Browser for SQLiteを使うと簡単にデータベースの構造を確認できます。マイグレーションは、元に戻すことも可能です。
rails db:rollback
6.1.2 modelファイル
生成された、UserモデルのファイルはApplicationRecordを継承しています。
6.1.3 ユーザーオブジェクトを作成する
railsコンソールを起動します。
rails console --sandbox
--sandbox
とは、コンソール終了時に、このコンソール起動時のデータベース変更を取り消してくれます。①
User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>ユーザークラスを作成することなく、ユーザオブジェクトを作成することができます。しかし、このままでは、中身がnilになってしまっています。
引数を渡して、ユーザークラスを作成するとこのようになります。
user = User.new(name: "aaa", email: "bbb@example.com")ユーザークラスが有効か確かめることができます。
>> user.valid? trueユーザーオブジェクトを保存します。
user.save .. .. .. =>tureユーザーオブジェクトを呼び出してみます。
user =>#<User id: 1,name: "aaa", email: "bbb@example.com", .....>ユーザークラスにアクセスします。
>> user.name => "aaa" >> user.email => "bbb"② ①の方法だと2段階に分けなければなりません。しかし、このようにすることで1段階にすることが可能です。
User.create(name: "zzz", email: "yyy@example.org") gold = User.create(name: "gold", email: "gold@example.com")
- ユーザーオブジェクトを削除する方法
gold.destroy gold #メモリ上には、まだ保存されています。 =><User id: 3, name: "gold", email: "gold@example.com", ...... >6.1.4 ユーザーオブジェクトを検索する
- id3のユーザーが削除されているか確かめます。
User.find(3) ActiveRecord::RecordNotFound: Couldn't find User with ID=3データベースには、id3のユーザーは、削除されているので、見つかりません。
- find_by
User.find_by(email: "bbb@example.com")このemailに該当するユーザーが検索されます。
- first
User.first最初のユーザーが表示されます。
- all
User.all
全てのユーザーが表示されます。
6.1.5 ユーザーオブジェクトを更新する
- ユーザーオブジェクトを更新する方法。
>> user.email = "silver@example.net" => "silver@example.net" >> user.save => true user.reload.email => "gold@example.net" #saveしないでリロードするとsave前に戻ってしまいます。
- update_attributesメソッド
更新と保存を一度にする方法。
また、検証を回避する方法。
特定の事項を変更したいときに例外的に使用します。>> user.update_attributes(name: "silver", email: "silver@example.org") => true6.2 ユーザーを検証する
Userモデルに、nameとemail属性が与えられましたが、これらの属性に制限をかけるために検証を学習していきます。
6.2.1 有効性を検証する
有効なモデルの検証方法は、まず、有効にモデルオブジェクトを作成し、その属性のうちの1つを有効出ない属性に意図的に変更し、それをちゃんと失敗するかを確かめます。
Userモデルを作成されたこのtest/models/user_test.rbファイルを編集していきます。
- setupメソッド
このメソッドは、このメソッド内に書かれた処理は、各テストが作動する前に、実行されます。
また、setupメソッド内でインスタンス変数の@Userを宣言することで全てのテスト内で、@Userが使えるようになります。
def setup @user = User.new(name: "Example User", email: "user@example.com") end
- validメソッド
@Userにvalidメソッドを使うことによって有効性を確かめることができます。
test "should be valid" do assert @user.valid? end6.2.2 存在性を検証する
- presentメソッド
属性が存在するかを検証するメソッドです。
test/models/user_test.rbtest "name should be present" do @user.name = " " assert_not @user.valid? endassert_notメソッドを使い一旦、Userオブジェクトが有効でない状態にします。
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true endさらにvalidatesメソッドにpresence:trueという引数を与えてます。
コンソールで確かめてみます。
>> u = User.new(name: "", email: "gold@example.com") >> u.valid? => false >> u.errors.full_messages #どこがエラーしたのか確認が可能 => ["Name can't be blank"]ちなみに、全てのバリデーションに通った時に、tureを返します。
emailも同様の方法で確かめることができます。
6.2.3 長さを検証する
Userの名前やemailの長さに制限をかけます。
名前は、50文字。emailは、255文字が通例となっています。test/models/user_test.rbtest "name should not be too long" do @user.name = "a" * 51 assert_not @user.valid? end #"a" * 51とは、aが51個という意味。aを51個書かなくてよくするために。 test "email should not be too long" do @user.email = "a" * 244 + "@example.com" assert_not @user.valid? endnameとemailに引数を渡す。
app/models/user.rbvalidates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }6.2.4 フォーマットを検証する
今の所、emailの制限は、最大文字制限でした。
さらに、emailに正しい制限をかけていきます。
例えば、無効なemailアドレスが書かれているかということです。test/models/user_test.rb# ①有効なアドレスを検証する test "email validation should accept valid addresses" do valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] valid_addresses.each do |valid_address| @user.email = valid_address assert @user.valid?, "#{valid_address.inspect} should be valid" end end # ②無効なアドレスを検証する test "email validation should reject invalid addresses" do invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. foo@bar_baz.com foo@bar+baz.com] invalid_addresses.each do |invalid_address| @user.email = invalid_address assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" end end
"#{valid_address.inspect} should be valid"
この引数は、どのメールアドレスが無効だったかを教えてくれます。そして、正規表現の定数を作り、formatオプションの引数に正規表現の引数を渡すことで有効なアドレスかを確かめることができます。
app/models/user.rbvalidates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }6.2.5 一意性を検証する
ユーザーの一意性を確保するために、validatesメソッドの:uniqueオプションを使っていきます。
まず、そのためには、重複させたメールアドレスが必要になります。
その後、@user.saveした後では、同じアドレスがあるため、無効となります。test/models/user_test.rbdef setup @user = User.new(name: "Example User", email: "user@example.com") end . . . test "email addresses should be unique" do duplicate_user = @user.dup duplicate_user.email = @user.email.upcase @user.save assert_not duplicate_user.valid? end上記のテストを通過させるために、uniqueness: trueというオプションを追加します。しかし、そのオプションでは、大文字と小文字の区別がされないため、uniqueness: { case_sensitive: false }を追加する必要があります。
app/models/user.rbvalidates :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: { case_sensitive: false } endこの状態では、同時刻に、複数の同じメールアドレスが登録されてしまうと片方だけでなく両方のアドレスが登録されてしまいます。
解決方法は、データベース側で一意性を強制することでできます。
データベース上のemailカラムにインデックスを追加します。
rails generate migration add_index_to_users_emailRailsのメソッドのadd_indexでemailカラムを追加しています。
そこにunique: trueを指定することで一意性を強制することができるようになります。db/migrate/[timestamp]_add_index_to_users_email.rbdef change add_index :users, :email, unique: true endテストDBのサンプルデータが含まれています。fixtures
test/fixtures/users.yml
このファイル内で一意性が保たれてないため、テストがエラーになってしまいます。
中身を全て削除します。その後、マイグレート(rails db:migrate)をおそらく忘れたため、エラーが発生しました。
$ rails t Migrations are pending. To resolve this issue, run: bin/rails db:migrate RAILS_ENV=test $ bin/rails db:migrate RAILS_ENV=test == 20191023112923 AddIndexToUsersEmail: migrating ============================= -- add_index(:users, :email, {:unique=>true}) -> 0.0019s== 20191023112923 AddIndexToUsersEmail: migrated (0.0026s) ==================== $ rails t Running via Spring preloader in process 7137 Started with run options --seed 36960 14/14: [============================] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.63485s 14 tests, 30 assertions, 0 failures, 0 errors, 0 skipsさらに、メールアドレスの一意性を確保するために、データベースに保存する前に全て、小文字にしてしまいます。このようにすることによって、一意性がさらに確保されます。
app/models/user.rbbefore_save { self.email = email.downcase } #self.email = self.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: { case_sensitive: false }emailを小文字に対応するかテストを書いていきます。
test/models/user_test.rbtest "email addresses should be saved as lower-case" do mixed_case_email = "Foo@ExAMPle.CoM" @user.email = mixed_case_email @user.save assert_equal mixed_case_email.downcase, @user.reload.email end6.3 セキュアなパスワードを追加する
セキュアなパスワードとは、各ユーザーにパスワードの入力の確認をさせ、そのパスワードをハッシュ化し、データベースに保存することを言います。
ハッシュ化とは、ハッシュ関数を使って、入力されたパスワードを元に戻せない文字列に変更してしまうことです。
6.3.1 ハッシュ化されたパスワード
モデルの中に、password_digestという属性が含まれていれば、セキュアなパスワードを生成するhas_secure_passwordメソッドを使うをことができます。
$ rails generate migration add_password_digest_to_users password_digest:stringadd_password_digest_to_usersのto_usersをすることで、usersテーブルにカラムを追加するマイグレーションがrailsによって、自動で生成されます。
さらに、password_digest:stringを付け加えることで、マイグレーションにパスワードを作っていくということをrailsに伝えられます。db/migrate/[timestamp]_add_password_digest_to_users.rbdef change add_column :users, :password_digest, :string endusersテーブルpassword_digestカラムを追加していきます。
その後
$ rails db:migrate
を実行します。
そして、has_secure_passwordを使うには、最先端のハッシュ関数であるbcryptが必要になります。そのために、Gemfileを編集し、
bundle install
を実行します。6.3.2 ユーザーがセキュアなパスワードを持っている
app/models/user.rb. . has_secure_password endhas_secure_passwordを追加します。
そして、テストを実行するも失敗します。
理由は、has_secure_passwordによって、password属性とpassword_confirmation属性に対して、自動でバリデーションが追加されているためです。バリデーションがあるのにも関わらず、
test/models/user_test.rb
のファイルの@userには、passwordとpassword_confirmationの値が書いてないからテストがエラーになってしまいます。test/models/user_test.rbdef setup @user = User.new(name: "Example User", email: "user@example.com", password: "aaa", password_confirmation: "aaa") end6.3.3 パスワードの最小文字数
パスワードが6文字以上や空のパスワードが設定できないように、バリデーションをかけていきます。
test/models/user_test.rbtest "password should be present (nonblank)" do @user.password = @user.password_confirmation = " " * 6 assert_not @user.valid? end test "password should have a minimum length" do @user.password = @user.password_confirmation = "a" * 5 assert_not @user.valid? endパスワードの更新に備えて、バリデーションを作成していきます。
app/models/user.rbclass User < ApplicationRecord before_save { self.email = email.downcase } . . . has_secure_password validates :password, presence: true, length: { minimum: 6 } end6.3.4 ユーザーの作成と認証
railsコンソールで実際にユーザーを作成して、試していきます。
$ rails console >> User.create(name: "nihontaro", email: "nihon@example.com", ?> password: "nihon", password_confirmation: "nihon")DB Browser for SQLiteを使って、usersテーブルの中身を確認して、ユーザーが正しく保存できているか確認してみます。パスワードの中身がハッシュ化されています。
has_secure_passwordをUserモデルに追加したため、authenticateメソッドが使えるようになっています。
user.authenticate("japan") =>false user.authenticate("nihon") => #<User id: 1, name: "nihontaro", email: "nihon@example.com", created_at: ..., updated_at: ....., password_digest: ... > !!user.authenticate("nihon") => true6.4 最後に
マージまでし、herokuにプッシュまでします。
本番環境でも、Userモデルを使うためには、heroku runをする必要があります。
$ heroku run rails db:migrate
- 投稿日:2019-10-24T20:50:26+09:00
rails WebSocket と ActionCableを用いたリアルタイム通信
なぜやろうと思ったか
railsで非同期通信を行った時に、自分のwebブラウザしか結果が反映されない問題を解消したかった。
前まで、setIntervalで10秒ごとに読み込みさせて認識させていたんですが、変化がない時にも動くので何か他の方法がないかなぁ〜と模索していました。完成コード
tableはmessage,column名はbodyでdbを作成
channels/chat_message_channel.rbclass ChatMessageChannel < ApplicationCable::Channel def subscribed;stream_from "chat_message_channel";end def unsubscribed # Any cleanup needed when channel is unsubscribed end def speak(data) @message = Message.create(body: data["message"]) data['id'] = @message.id ActionCable.server.broadcast 'chat_message_channel',message: data end endmessage.jsApp.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", { connected: function() { // Called when the subscription is ready for use on the server }, disconnected: function() { // Called when the subscription has been terminated by the server }, received: function(data) { let message_id = data['message']['id'] let message_text = data['message']['message'] let html =` <tr> <td><a href="/messages/${message_id}">Show</td> <td><a href="/messages/${message_id}/edit">Edit</td> <td><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/messages/${message_id}">Destroy</td> <td>${message_text}</td> </tr>` $("#message").append(html) // Called when there's incoming data on the websocket for this channel }, speak: function(message) { return this.perform('speak',{message: message}); } }, $(document).on('keypress', '[data-behavior~=speak_chat_messages]', function(event) { if (event.keyCode === 13){ App.chatmessage.speak(event.target.value) event.target.value = '' event.preventDefault() } }));rails g scaffoldで作成したindex.html.erbにidなど少し組み込んだ
index.html.erb<p id="notice"><%= notice %></p> <h1>Messages</h1> <table> <thead> <tr> <th colspan="3"></th> </tr> </thead> <tbody id="message"> <% @messages.each do |message| %> <tr> <td><%= link_to 'Show', message %></td> <td><%= link_to 'Edit', edit_message_path(message) %></td> <td><%= link_to 'Destroy', message, method: :delete, data: { confirm: 'Are you sure?' } %></td> <td><%= message.body %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Message', new_message_path%> </div> <br/> <div class="aaa"><%= @message.body %></div> <form> <label> websocketsで送信: <input type="text" data-behavior="speak_chat_messages"> </label> </form>javascripts/cable.js// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. // //= require action_cable //= require_self //= require_tree ./channels (function() { this.App || (this.App = {}); App.cable = ActionCable.createConsumer(); }).call(this);主にこの4つのfileをいじってもらいことになりますね。
cable.js
まず最初に読まれるfileです。
クライアントからサーバーに対してWebSocket接続しているコードで、
App.cable = ActionCable.createConsumer();でWebSocket通信を確立している。
他のサイトにはcoffee fileを呼ぶものがありますが、今回はmessage.js
に記述を書いていきます。message.js
App.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", {
でcable.jsからmessage.jsに接続している。
成功すれば、connectedに飛び、失敗したもしくは拒否した場合はdisconnectedに飛ぶ。その後、inputのkeypress functionを動かす。
enterキーを押すと,if (event.keyCode === 13)の条件がtrueになる。
keycodeとは?
http://shanabrian.com/web/javascript/keycode.php
キーボードを押すと、キーコードが発行される。enterは13番。
用いればどのキーボードを押したかがわかる。
preventDefaultでrailsのhttp通信を中断し、その代わりにjqueryのApp.chatmessage.speak function
を実行する。引数にevent.target.value(
keypressしたinputの中身)を定義。次に、speak: function(message) が呼ばれます。
return this.performでサーバーのChat_message_Channel#speak
を呼び出す。
仮に、perform('appear',message: message)なら Chat_message_Channel#appearを呼び出す。
第二引数には渡したい値をハッシュで渡してあげます。
{performはrailsのメソッドだと思われ、サブスクリプションでperformメソッドを使って、RPC(リモートプロシージャコール)として利用できるみたい}
よくわかりません。。。。(・・;)
https://railsguides.jp/action_cable_overview.htmlChat_message_channel.rb
speak actionが呼ばれました。ここでは、railsのcontrollerの役割を担う部分になります。
情報をdbにsaveしたりできます。
speak(data)
におけるdataの中身はハッシュで受け取ってます。params的な感じで。
data=> {"message"=>"111111", "action"=>"speak"}
今回は、saveした@message.idをviewで使用するので、dataのハッシュにidを入れ込みます。
この時点でのdataはこんな感じ。
data=> {"message"=>"111111", "action"=>"speak", "id"=>125}
speakメソッドの
ActionCable.server.broadcast
では第1引数にチャネル名、第2引数に発言メッセージを指定することで、サーバー側からChatMessageChannelにWebSocketで接続している全クライアントに対して発言メッセージを配信することが可能になります。
これで、全クライアントに対して処理を行う準備ができました。
メッセージを配信したので、受け取りを行わなければなりませんね。message.js
message.jsに戻ってきました。
配信されたdataをここのreceived functionで受け取ります。
message: {message: "1111", action: "speak", id: 128}
__proto__: Object
dataはこんな感じなので取り出し方は、data['message']['id']
みたいな感じ。
その後、作成したhtmlを追加する処理を書いて終了となります。。最後に
参考にした記事にはcoffee fileを用いて使う手法もありましたが、利便性が悪いのでjqueryを使用してやってみました。
これで、リアルタイムのchatが作れるよ〜〜〜。
参照した記事
https://railsguides.jp/action_cable_overview.html
https://codezine.jp/article/detail/10153?p=1
https://qiita.com/fujisawaryohei/items/3a567a64691276da0d45
- 投稿日:2019-10-24T20:25:05+09:00
bundle installでnokogiriがインストールされない時
bundle install --path vendor/bundle したら怒られた
Installing nokogiri 1.10.1 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /Users/t.riku/rikuproject/vendor/bundle/ruby/2.4.0/gems/nokogiri-1.10.1/ext/nokogiri /Users/t.riku/.rbenv/versions/2.4.7/bin/ruby -r ./siteconf20191024-82200-mdp5kr.rb extconf.rb --use-system-libraries checking if the C compiler accepts -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libxml2... *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. --------------------省略-------------------- To see why this extension failed to compile, please check the mkmf.log which can be found here: /Users/t.riku/rikuprojects/vendor/bundle/ruby/2.4.0/extensions/x86_64-darwin-18/2.4.0-static/nokogiri-1.10.1/mkmf.log extconf failed, exit code 1 Gem files will remain installed in /Users/t.riku/rikuproject/vendor/bundle/ruby/2.4.0/gems/nokogiri-1.10.1 for inspection. Results logged to /Users/t.riku/rikuprojects/vendor/bundle/ruby/2.4.0/extensions/x86_64-darwin-18/2.4.0-static/nokogiri-1.10.1/gem_make.out An error occurred while installing nokogiri (1.10.1), and Bundler cannot continue. Make sure that `gem install nokogiri -v '1.10.1' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: awesome_nested_fields was resolved to 0.6.4, which depends on rails was resolved to 4.2.11.1, which depends on actionmailer was resolved to 4.2.11.1, which depends on actionpack was resolved to 4.2.11.1, which depends on actionview was resolved to 4.2.11.1, which depends on rails-dom-testing was resolved to 1.0.9, which depends on nokogiri
解決策
/Users/t.riku/rikuprojects/vendor/bundle/ruby/2.4.0/extensions/x86_64-darwin-18/2.4.0-static/nokogiri-1.10.1/mkmf.log
のなかに入っているログを見てみる
mkmf.logclang -o conftest -I/Users/t.riku/.rbenv/versions/2.4.7/include/ruby-2.4.0/x86_64-darwin18 -I/Users/t.riku/.rbenv/versions/2.4.7/include/ruby-2.4.0/ruby/backward -I/Users/t.riku/.rbenv/versions/2.4.7/include/ruby-2.4.0 -I. -I/Users/t.riku/.rbenv/versions/2.4.7/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -Wno-error=shorten-64-to-32 -pipe -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libxml2 conftest.c -L. -L/Users/t.riku/.rbenv/versions/2.4.7/lib -L. -L/Users/t.riku/.rbenv/versions/2.4.7/lib -fstack-protector -L/usr/local/lib -lruby-static -framework Foundation -lpthread -ldl -lobjc " ld: malformed file /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libpthread.tbd:4:18: error: unknown enumerated scalar platform: file '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libpthread.tbd' clang: error: linker command failed with exit code 1 (use -v to see invocation) checked program was: * begin */ 1: #include "ruby.h" 2: 3: int main(int argc, char **argv) 4: { 5: return 0; 6: } /* end */うーん、なんか分からんけどxcodeのバージョンに問題があるのかな。
https://www.famlog.jp/article/1180
↑これ見る感じ、Mojava使ってる人は'/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /'のパッケージをインストールすれば行けるらしい。あんま、わかってないけどとりあえずやってみる。
sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /
いざ
$ bundle install --path vendor/bundle --------------------省略-------------------- Bundle complete! 62 Gemfile dependencies, 195 gems now installed. Bundled gems are installed into `./vendor/bundle`通ったー。
全然理解してないので、わかったら詳細追記します。
参考にした記事
https://github.com/esnme/ultrajson/issues/332
https://www.famlog.jp/article/1180
- 投稿日:2019-10-24T20:17:29+09:00
Rails 基礎入門と基本コマンド
はじめに
備忘録として書いています。
おかしなところはバンバン教えていただけたらと思います
$ rails new sample_app
訳:「sample_appと言う名前で新しくファイル一式作ってやるぜ!」
$ rails s
訳:「railsを起動するぜ!sはserverのsだぜ!」
$ rails g controller home top
訳:
・app/views/
の中にhome
というフォルダを、その中にtop
ってファイル作るぜ!
・app/controllers/
の中にhome_controller.rb
というコントローラー作るぜ!
・app/config/
の中にroutes.rb
っていうルーティングファイル作るぜ!
・app/assets/stylesheets
の中にhome.scss
ってファイル作るぜ!
後注意なんだけど$ rails g controller home ~っていうコマンドもう使えないからね、だってもうhomeは存在してるわけだから。だからこれからは自分でファイル作成してコントローラーとルーティングファイルに追記してね。」app/controllers/home_controller.rbclass HomeController < ApplicationController def top #topのことをアクション名と言う end end #homeの中のtopと言う名前のファイルを呼び出しているapp/config/routes.rbRails.application.routes.draw do get "home/top" => "home#top" end #get "URL" => "コントローラー名#アクション名(つまりファイル名)"つまり
app/views/home/
の中のファイル名=app/controllers/home_controller.rb
のアクション名基本viewで表示する変数はコントローラーの中に書く
app/controllers/home_controller.rbclass HomeController < ApplicationController def top @hoge1 = "ほげほげ" end endデータベースとの連携
前提
$ rails g posts index
を実行したとする
$ rails g model Post content:text
訳:
db/migrate/
の中にpostsテーブルを作るぜ!
content
ってのはカラム名で
text
ってのはデータの型だ!
app/models/
の中にpost.rb
ってファイル作るぜ!
$ rails db:migrate
訳:
データーベースの変更内容を保存するぜ!
変更加えたときは必ず実行してくれよな!
$ rails console
訳:
データベースの中にばんばん情報入れてくだろ?
このコマンドを使うんだ!$ rails console > post1 = Post.new(content: "ほげほげほげー!") #訳: post1と言う変数名で"Post"というDBの中にnewメソッドを使ってデータを作成するぜ! contentというカラムに”ほげほげほげー!”という内容を入れるぜ! > post.save #訳: セーブするぜ! > posts = Post.first #訳: dbの一番上の情報だけを取得するぜ!・全部のときは
posts = Post.all
・インデックス番号0番目のcontentというカラムの中身だけ欲しいときはPost.all[0].content
posts_controller.rbdef index @post1 = Post.all end訳: postsテーブルのデータを全て取得してくるぜ!
app/views/posts/index.html.erb<% @post.each do |post| %> <div class="aaaa"> <%= post1.content%> </div> <% end %>訳: 取得したデータを1つずつ繰り返し表示していくぜ!
> post = Post.find_by(id:2)
routes.rbget "posts/:id" => "posts#show"とすることでURLは格納されたidをそのまま使える
posts#showは投稿の詳細を表示する。あと忘れちゃいけないのがコントローラーファイル
get "posts/:id" => "posts#show"
に対応するのが以下の内容post_controller.rbdef show @id = params[:id] endこのparamsとはパラメータのことで
サービスの利用者(ユーザー)がサーバー対して送ってきた情報を呼び出す
- 投稿日:2019-10-24T18:58:13+09:00
Railsチュートリアル 第7章 ユーザー登録 - ユーザー登録成功
ユーザー登録に成功した画面のモックアップ
ユーザー登録に失敗した場合の挙動は、ここまでの学習で実装が完了しました。
ユーザー登録に成功した画面のモックアップ - ユーザー登録に成功したら、当該リンク先のような画面を出力するように実装していきます。
登録フォームの完成
現状、有効な情報で登録操作を行うと何が起こるか
現状では、ユーザー登録フォームに有効な情報で登録操作を行っても、元の画面に戻されてしまいます。
このときの
rails server
のログ情報は以下のようになります。Started POST "/users" for 172.17.0.1 at 2019-10-22 09:28:55 +0000 ...略 No template found for UsersController#create, rendering head :no_content Completed 204 No Content in 1605ms (ActiveRecord: 50.4ms)「No template found for UsersController#create」というのがポイントですね。実際に発生しているのは以下の事態です。
- Railsはデフォルトのアクションに対応するビューを表示しようとする
create
アクションに対応するビューのテンプレートは現状存在しない- 元の画面に戻されてしまう
登録内容はRDBにきちんと反映される
なお、現状においても、有効な情報で登録操作を行えば、登録内容はRDBにきちんと反映されます。
Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"YZ6q9xae3E23E8J5R3JNqhrVZ5r+jL9UV+QIZy7LiF/sdbfCNkYFsMUr+8tqltfCwiHus2I/viw+wT92e7nluQ==", "user"=>{"name"=>"Foobar Foobar", "email"=>"foobar@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"} (0.1ms) begin transaction User Exists (6.7ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "foobar@example.com"], ["LIMIT", 1]] SQL (25.7ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Foobar Foobar"], ["email", "foobar@example.com"], ["created_at", "2019-10-22 09:28:55.485707"], ["updated_at", "2019-10-22 09:28:55.485707"], ["password_digest", "$2a$10$qgI/adIo1AfEBIYOxU636uh/k2kEWw9IM2F/E5kdqMAdkTgeNtRV."]] (18.0ms) commit transaction# rails console Running via Spring preloader in process 505 Loading development environment (Rails 5.1.6) >> User.count (3.0ms) SELECT COUNT(*) FROM "users" => 2 >> User.find(2) User Load (4.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] => #<User id: 2, name: "Foobar Foobar", email: "foobar@example.com", created_at: "2019-10-22 09:28:55", updated_at: "2019-10-22 09:28:55", password_digest: "$2a$10$qgI/adIo1AfEBIYOxU636uh/k2kEWw9IM2F/E5kdqMA...">別のページへのリダイレクトを実装する
Railsの一般的な慣習では、「ユーザー登録に成功した場合、ページを描画せずに別のページにリダイレクトする」ことになっています。今回は、「ユーザー登録に成功した場合、新しく作成されたユーザーのプロフィールページにリダイレクトする」という動作にしましょう。
変更するファイルは
app/controllers/users_controller.rb
です。app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) if @user.save - # TODO: 保存の成功をここに実装する + redirect_to @user else render 'new' end end ...略 end
redirect_to @user
というコードについてredirect_to @user上記のコードと下記のコードは等価の挙動となります。
redirect_to user_url(@user)上述2つのコードを等価とみなす動作も、Railsの機能の一つです。
演習 - 登録フォームの完成
1. 有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
有効な情報を送信した際、
rails server
のログは以下のようになります。Started POST "/users" for 172.17.0.1 at 2019-10-22 09:46:59 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"YZ6q9xae3E23E8J5R3JNqhrVZ5r+jL9UV+QIZy7LiF/sdbfCNkYFsMUr+8tqltfCwiHus2I/viw+wT92e7nluQ==", "user"=>{"name"=>"Foo Bar Baz", "email"=>"foobar.foobar@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"} (0.2ms) begin transaction User Exists (5.8ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "foobar.foobar@example.com"], ["LIMIT", 1]] SQL (12.4ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Foo Bar Baz"], ["email", "foobar.foobar@example.com"], ["created_at", "2019-10-22 09:46:59.624267"], ["updated_at", "2019-10-22 09:46:59.624267"], ["password_digest", "$2a$10$aVxeV/ey4bi5F9/DnuXRd.M0/41r/X3Sj7rl1MdKnpubgKUni9tlu"]] (18.4ms) commit transaction Redirected to http://localhost:8080/users/3 Completed 302 Found in 237ms (ActiveRecord: 48.6ms)RDBに対する
INSERT
文が発行された後、確かにリダイレクト処理が行われていますね。POST
リクエストの処理結果は、最終的に「302 found」となっています。Started GET "/users/3" for 172.17.0.1 at 2019-10-22 09:46:59 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#show as HTML Parameters: {"id"=>"3"} User Load (3.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]] Rendering users/show.html.erb within layouts/application Rendered users/show.html.erb within layouts/application (3.0ms) Rendered layouts/_rails_default.erb (276.7ms) Rendered layouts/_shim.html.erb (0.4ms) Rendered layouts/_header.html.erb (0.8ms) Rendered layouts/_footer.html.erb (3.6ms) Completed 200 OK in 509ms (Views: 444.4ms | ActiveRecord: 3.4ms)最終的には、以下のような画面が表示されます。
先ほど作成したユーザー情報の
"foobar.foobar@example.com"
であることを踏まえて、rails console
で、実際にRDBにユーザー情報が保存されていることを確認してみましょう。# rails console >> User.find_by(email: "foobar.foobar@example.com") User Load (11.1ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "foobar.foobar@example.com"], ["LIMIT", 1]] => #<User id: 3, name: "Foo Bar Baz", email: "foobar.foobar@example.com", created_at: "2019-10-22 09:46:59", updated_at: "2019-10-22 09:46:59", password_digest: "$2a$10$aVxeV/ey4bi5F9/DnuXRd.M0/41r/X3Sj7rl1MdKnpu...">確かに、実際にRDBにユーザー情報が保存されています。
2. リスト 7.28を更新し、
redirect_to user_url(@user)
とredirect_to @user
が同じ結果になることを確認してみましょう。
app/controllers/users_controller.rb
を変更します。app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) if @user.save - redirect_to @user + redirect_to user_url(@user) else render 'new' end end ...略 end以上の変更を行った上で、有効な情報を送信すると、
rails server
のログは以下のようになります。Started POST "/users" for 172.17.0.1 at 2019-10-22 10:47:07 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"YZ6q9xae3E23E8J5R3JNqhrVZ5r+jL9UV+QIZy7LiF/sdbfCNkYFsMUr+8tqltfCwiHus2I/viw+wT92e7nluQ==", "user"=>{"name"=>"Foo Bar Foobar", "email"=>"foobar.baz@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"} (0.1ms) begin transaction User Exists (6.0ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "foobar.baz@example.com"], ["LIMIT", 1]] SQL (20.5ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Foo Bar Foobar"], ["email", "foobar.baz@example.com"], ["created_at", "2019-10-22 10:47:07.789313"], ["updated_at", "2019-10-22 10:47:07.789313"], ["password_digest", "$2a$10$frfU4tFYiTifJHRfHLQ7nO.3c1ju83yJsQbklZU7pGJa2LNdXSENG"]] (16.6ms) commit transaction Redirected to http://localhost:8080/users/4 Completed 302 Found in 258ms (ActiveRecord: 55.5ms) Started GET "/users/4" for 172.17.0.1 at 2019-10-22 10:47:07 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#show as HTML Parameters: {"id"=>"4"} User Load (5.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]] Rendering users/show.html.erb within layouts/application Rendered users/show.html.erb within layouts/application (0.9ms) Rendered layouts/_rails_default.erb (262.1ms) Rendered layouts/_shim.html.erb (0.3ms) Rendered layouts/_header.html.erb (0.7ms) Rendered layouts/_footer.html.erb (0.5ms) Completed 200 OK in 516ms (Views: 472.0ms | ActiveRecord: 5.6ms)flash
flashとその実装
世界中に多数存在するWebアプリケーションの多くには、「新規ユーザー登録が完了した後に表示されるページにメッセージを表示し、2度目以降のログインではそのページにメッセージを表示しない」という機能が実装されています。
Railsでそのような情報を表示するには、flashという特殊な変数を使用します。その用法はRubyのハッシュと類似しています。ここでは、Railsの一般的な慣習に従い、
:success
というキーには成功時のメッセージを代入するように実装します。flashを実装する箇所は、
app/controllers/users_controller.rb
内です。app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) if @user.save + flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end end ...略 end
flash内に存在するキーがあるかを調べ、もしあればキーに対応する値(メッセージ)を全て表示する
今回はこの項見出しに示した機能を実装していきます。そういえば、第4章中のサンプルプログラムで、「ブロックを使い、特定のハッシュのkeyとvalueの組を順次表示していく」というものがありました。
>> flash = { success: "It worked!", danger: "It failed." } >> flash.each do |key, value| ?> puts "#{key}" >> puts "#{value}" >> end success It worked! danger It failed. => {:success=>"It worked!", :danger=>"It failed."}ここで
flash
という変数名を使ったのは、今回の実装に向けての伏線だったのですね。というわけで、flash変数の内容をWebサイト全体にわたって表示できるようにするための(仮)コードは以下のようになります。
<% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %>(仮)とあるのは、このコードは「HTMLとERbが雑に混ざっており、リファクタリングの余地がある」ためです。Railsチュートリアル本文には、「これをキレイに整形する作業は演習として残しておきましょう」とあります。
alert-<%= message_type %>
なるクラスの意味上述「flash変数の内容をWebサイト全体にわたって表示できるようにするための(仮)コード」の中には、以下のようなコードがあります。
alert-<%= message_type %>これは、「埋め込みRubyによって、適用するCSSのクラスをメッセージの種類により変更する」というコードです。例えば、
:success
キーのメッセージが表示される場合であれば、適用されるCSSクラスは以下のようになります。alert-success「
:success
はシンボルであるが、テンプレートに反映させる際には、埋め込みRubyが自動的に"success"
という文字列に変換している」という点に注意が必要です。このような実装により、以下の挙動を実現することができます。
- キーの内容により、異なったCSSクラスを適用させる
- 結果として、メッセージの種類によってスタイルを動的に変更させることができる
- 例えば、「ログインに失敗した旨を示すメッセージを
flash[:danger]
で表示し、その際に自動的にalert-danger
というクラスを適用させる」などBootstrap CSSにおける、
alert-
から始まる4つのクラスBootstrapのCSSでは、
alert-
から始まるクラスが以下4つ定義されています。
alert-success
alert-info
alert-warning
alert-danger
全体としてどういう挙動になるか
flash[:success] = "Welcome to the Sample App!"<% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %>上述のコードを踏まえると、当該部分に対して最終的に出力されるHTMLは以下のようになります。
<div class="alert alert-success">Welcome to the Sample App!</div>
flash
変数の内容をWebサイトのレイアウトに追加するapp/views/layouts/application.html.erb<!DOCTYPE html> <html> ...略 <body> <%= render 'layouts/header' %> <div class="container"> + <% flash.each do |message_type, message| %> + <div class="alert alert-<%= message_type %>"><%= message %></div> + <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> </body> </html>
演習 - flash
1. コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば
"#{:success}"
といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。# rails console --sandbox >> "#{:success}" => "success" >> "#{:danger}" => "danger"2. 先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
前提として、以下のようなコードを考えます。
<% flash = { success: "It worked!", danger: "It failed." } %> <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %>最終的に出力されるHTMLは以下のようになるはずです。
<div class="alert alert-success">It worked!</div> <div class="alert alert-danger">It failed.</div>実際のユーザー登録
データベースの内容をリセットする
RailsのActive Recordのマイグレーション機能により、データベースの内容をリセットすることが可能です。データベースの内容をリセットするためのコマンドは、
rails db:migrate:reset
です。実行すると、以下のようなログと共に、データベースの内容がリセットされます。# rails db:migrate:reset Dropped database 'db/development.sqlite3' Dropped database 'db/test.sqlite3' Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' == 20190928080951 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0119s == 20190928080951 CreateUsers: migrated (0.0122s) ============================= == 20191010034159 AddIndexToUsersEmail: migrating ============================= -- add_index(:users, :email, {:unique=>true}) -> 0.0133s == 20191010034159 AddIndexToUsersEmail: migrated (0.0136s) ==================== == 20191013040411 AddPasswordDigestToUsers: migrating ========================= -- add_column(:users, :password_digest, :string) -> 0.0134s == 20191013040411 AddPasswordDigestToUsers: migrated (0.0137s) ================ログを見るに、以下のような動作が行われているようですね。
- 既存テーブルの削除
- 新規テーブルの作成
- 既存のマイグレーションに基づき、列情報も自動生成される
- インデックスの定義
- パスワードダイジェスト列の生成
Railsチュートリアルにおける今回の事例は、「Usersコントローラの
user.save
コマンドが実行された回数が学習者ごとに異なる可能性があり、RDBの内容の違いを吸収するためにデータベースの内容をリセットする」という趣旨のものです。有効なユーザー情報を登録する
早速、ユーザー登録フォームに有効なユーザー情報を入力し、最初のユーザーを作成してみましょう。Railsチュートリアル本文にならい、以下の内容でユーザーを作成してみます。
- Name: Rails Tutorial
- Email: example@railstutorial.org
- PasswordおよびConfirmationは任意
上記はユーザー情報作成完了時の画面です。ユーザー登録の成功を示すフラッシュメッセージもきちんと表示されていますね。なお、Bootstrap CSSにおいて、
success
クラスの背景色・文字色は、上記スクリーンショットのように爽やかな緑色です。ユーザー情報ページを再読み込みすると、今後はフラッシュメッセージは表示されなくなります。上記スクリーンショットは、再読み込み時のものです。
演習 - 実際のユーザー登録
1. Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
# rails console >> User.count (3.4ms) SELECT COUNT(*) FROM "users" => 1 >> User.find_by(email: "example@railstutorial.org") User Load (3.9ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example@railstutorial.org"], ["LIMIT", 1]] => #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2019-10-22 22:46:59", updated_at: "2019-10-22 22:46:59", password_digest: "$2a$10$j21OfGX82PY0/BqDcapJmeeo/xaVKgSQ9pEZD8hAp4B...">RDBにも新しく作成されたユーザーの情報が正しく反映されているようです。
2. 自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
まず、ユーザー登録フォームの[Create my account]ボタンをクリックしたところからの
rails server
のログを示します。Started POST "/signup" for 172.17.0.1 at 2019-10-22 22:55:21 +0000 Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"iAAnpc1ZKq9VuKfKkDNV0dVc4M8reSqR57fJU+5zPp8o8FsU4a2WCdXm++5dmoqwagJOnOJWHnu3mt+IIQCS1Q==", "user"=>{"name"=>"Hoge Hoge", "email"=>"[Gravatar登録済みの自分のメールアドレス]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"} (0.1ms) begin transaction User Exists (7.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "[Gravatar登録済みの自分のメールアドレス]"], ["LIMIT", 1]] SQL (20.8ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Hoge Hoge"], ["email", "[Gravatar登録済みの自分のメールアドレス]"], ["created_at", "2019-10-22 22:55:21.433059"], ["updated_at", "2019-10-22 22:55:21.433059"], ["password_digest", "$2a$10$GnOotTswgiIehevLmmlDV.XvhMgBXN67XAZp2KvTxip9WtDVsyo0."]] (23.3ms) commit transaction Redirected to http://localhost:8080/users/2 Completed 302 Found in 143ms (ActiveRecord: 51.7ms)最終的にはどうなるでしょうか。
上記スクリーンショットは、ユーザー登録が正常終了した直後のものです。Gravatarに登録したプロフィール画像がきちんと表示されています。
成功時のテスト
今度は「ユーザー登録フォームにおいて、有効なユーザー情報が送信された場合」に対するテストを書いていきます。今回の主題は「データベースの内容が正しいかを検証することにより、有効なユーザー情報に対して正しくユーザーが作成されたことを確認する」というものです。
assert_difference
メソッドユーザー登録失敗時のテストに登場した
assert_no_difference
と対になるメソッドです。メソッドの使い方はassert_difference
と同様です。
- 第1引数に文字列をとる
- 今回の事例では、
'User.count'
を第1引数とする- 第2引数には、比較した結果の差異をとる
- 今回は1とする
- ブロックをとり、ブロックの実行前と実行後で第1引数の文字列が指す要素の値を比較する
assert_difference 'User.count', 1 do post users_path, ... end成功時のテストのコード
test/integration/users_signup_test.rbclass UsersSignupTest < ActionDispatch::IntegrationTest ...略 + + test "valid signup information" do + get signup_path + assert_difference 'User.count', 1 do + post users_path, params: { user: { name: "Example User", + email: "user@example.com", + password: "password", + password_confirmation: "password"} } + end + follow_redirect! + assert_template 'users/show' + end end
この時点でテストは通ります。
# rails test integrate Running via Spring preloader in process 137 Started with run options --seed 37813 21/21: [=================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.48632s 21 tests, 49 assertions, 0 failures, 0 errors, 0 skips
follow_redirect!
についてfollow_redirect! assert_template 'users/show'
assert_difference
ブロックの後に、follow_redirect!
というコードがあります。
follow_redirect!
は、「リダイレクトを伴うテストにおいて、リダイレクト先の処理を明示的に実行する」というメソッドです。今回は、「POST
リクエストの実行結果がリダイレクトになる1ので、users/show
テンプレートが表示されている」目的でfollow_redirect!
メソッドを用いています。
!
がつくだけに、follow_redirect
というメソッドも存在すると考えられます。Google検索等してみたのですが、なぜ!
がつくかについての情報は見つけられませんでした。ただ、「follow_redirect
というメソッドは、Rails 1.1.6から2.1.0まで存在したものの、以降廃止されて現在に至っている」のだそうです。
users/show
テンプレートが正しく表示されることの確認assert_template 'users/show'
users/show
テンプレートが正しく表示されるには、以下の条件が全て満足される事が必要になります。
- Userのルーティングが正しく実装されている
- Userの
show
アクションが正しく動いているshow.html.erb
ビューが正しく実装されているテストの全体像まとめ
今回実装したテストは、以下の事柄について確認しています。
- 正しいユーザー登録データに対し、
post
メソッドが正しく動作する- ユーザー登録の内容がRDBに反映される
- ユーザー登録後、登録前に比べてRDBのレコード数が1つ増えているかどうか
POST
メソッド完了時のリダイレクトが正しく実装されているusers/show
テンプレートが正しく表示される「ユーザー登録成功」というフローで必要とされる機能がカバーされていますね。こういうときに統合テストは便利なのです。
演習 - 成功時のテスト
1. 7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。
ちなみに、テキストに対するテストは壊れやすいです。文量の少ない
flash
のキーであっても、それは同じです。筆者の場合、flash
が空でないかをテストするだけの場合が多いです。Railsチュートリアル本文にならい、
flash
が空でないかをテストするだけの実装とします。test/integration/users_signup_test.rbrequire 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest ...略 test "valid signup information" do get signup_path assert_difference 'User.count', 1 do post users_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password"} } end follow_redirect! assert_template 'users/show' + assert_not flash.empty? end end
現時点の状態から
app/controllers/users_controller.rb
に手を加えなければ、テストは通ります。# rails test integrate Running via Spring preloader in process 189 Started with run options --seed 27734 21/21: [=================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.08653s 21 tests, 50 assertions, 0 failures, 0 errors, 0 skips一方、
app/controllers/users_controller.rb
でflash[:success]
以下の行をコメントアウトすると、テストが通らなくなります。app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) if @user.save - flash[:success] = "Welcome to the Sample App!" + #flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end end ...略 end# rails test integrate Running via Spring preloader in process 202 Started with run options --seed 46053 FAIL["test_valid_signup_information", UsersSignupTest, 1.8803565000016533] test_valid_signup_information#UsersSignupTest (1.88s) Expected true to be nil or false test/integration/users_signup_test.rb:33:in `block in <class:UsersSignupTest>' 21/21: [=================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.38272s 21 tests, 50 assertions, 1 failures, 0 errors, 0 skips2.1. 本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。
なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
app/views/layouts/application.html.erb<!DOCTYPE html> <html> ...略 <body> ...略 <div class="container"> <% flash.each do |message_type, message| %> - <div class="alert alert-<%= message_type %>"><%= message %></div> + <% content_tag(:div, message, class: "alert alert-#{message_type}") %> <% end %> ...略 </div> </body> </html>「読みにくい」というか、「RubyのコードとHTMLのコードが混在するのはよろしくない」という事情もあります。
Railsの
content_tag
ヘルパーにより、RubyのコードのみでHTMLを構成できるようになりました。2.2. 変更が終わったらテストスイートを実行し、正常に動作することを確認してください。
# rails test integrate Running via Spring preloader in process 215 Started with run options --seed 26929 21/21: [=================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.10806s 21 tests, 50 assertions, 0 failures, 0 errors, 0 skips問題なくテストが通っていますね。
3. リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) if @user.save flash[:success] = "Welcome to the Sample App!" - redirect_to @user + #redirect_to @user else render 'new' end end ...略 endこのコードに対するテストの結果は以下のようになります。
# rails test integrate Running via Spring preloader in process 228 Started with run options --seed 63745 ERROR["test_valid_signup_information", UsersSignupTest, 3.581053300000349] test_valid_signup_information#UsersSignupTest (3.58s) RuntimeError: RuntimeError: not a redirect! 204 No Content test/integration/users_signup_test.rb:31:in `block in <class:UsersSignupTest>' 21/21: [=================================] 100% Time: 00:00:03, Time: 00:00:03 Finished in 3.66224s 21 tests, 48 assertions, 0 failures, 1 errors, 0 skipsテストが通らなくなりました。「RuntimeError: not a redirect! 204 No Content」というエラーメッセージがポイントですね。
4. リスト 7.28で、
@user.save
の部分をfalse
に置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_difference
のテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。assert_difference 'User.count', 1 do #...略 end
@user.save
が実行されない場合、RDBにレコードは追加されません。結果、User.count
の値は変わらなくなります。「@user.save
の実行後は、実行前と比較してUser.count
の値が1増えていなければならない」という条件に反するので、この時点でテストが失敗となります。実際にやってみるとどうなるでしょうか。
app/controllers/users_controller.rbclass UsersController < ApplicationController ...略 def create @user = User.new(user_params) - if @user.save + if false flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end end ...略 endテスト結果はこのようになります。
# rails test integrate Running via Spring preloader in process 241 Started with run options --seed 8272 FAIL["test_invalid_signup_information", UsersSignupTest, 1.5347172000001592] test_invalid_signup_information#UsersSignupTest (1.54s) Expected at least 1 element matching "div#error_explanation", found 0.. Expected 0 to be >= 1. test/integration/users_signup_test.rb:15:in `block in <class:UsersSignupTest>' FAIL["test_valid_signup_information", UsersSignupTest, 1.5782804999998916] test_valid_signup_information#UsersSignupTest (1.58s) "User.count" didn't change by 1. Expected: 1 Actual: 0 test/integration/users_signup_test.rb:25:in `block in <class:UsersSignupTest>' 21/21: [=================================] 100% Time: 00:00:02, Time: 00:00:02 Finished in 2.10267s 21 tests, 44 assertions, 2 failures, 0 errors, 0 skipsあっ、「
Users.errors
に何も代入されないため、new
がレンダリングされたときにapp/views/shared/_error_messages.html.erb
の内容がレンダリングされない。結果、IDがerror_explanation
となるdiv
要素が存在しないため、その点でテストが失敗する」というのもありました。
HTTPのステータスコードは「302 Found」が返ってきます。HTTPの仕様としては「303 See Other」が正しい応答なのだそうですが、後方互換性等の観点から、このような場合には302を返すのがデファクトスタンダードになっているそうです。 ↩
- 投稿日:2019-10-24T18:27:23+09:00
テスト環境で<meta name="csrf-token">が消える
meta要素からCSRFトークンを取り出すJavaScriptがあると、テスト環境でページが動かなくなることがあります。テスト環境ではallow_forgery_protectionのデフォルトがfalseで、falseだとmetaが埋め込まれないからです。
config/environments/test.rbconfig.action_controller.allow_forgery_protection = falsemetaがないせいでテストが落ちるときは、一時的に設定を変更します。
before do ActionController::Base.allow_forgery_protection = true end after do ActionController::Base.allow_forgery_protection = false end
- 投稿日:2019-10-24T18:15:33+09:00
Capybara+ChromeでJavaScriptのエラーを出力
headless Chromeを使ったテストで、JavaScriptのエラーのせいで先に進まないときは、次のコードを埋め込んでコンソールの内容を調べられます。
puts page.driver.browser.manage.logs.get(:browser).collect(&:message)
get(:browser)
が返すのは、Selenium::WebDriver::LogEntryオブジェクトの配列です。JavaScriptの
console.error
、console.warn
の内容も出力できます。console.log
、console.info
、console.debug
の内容は出せません。
- 投稿日:2019-10-24T17:22:58+09:00
[Rails] NameError in MessagesController#index uninitialized constant Message::ImageUploader エラー
目的:
CarrierWave・Imagemagickというgemを使い、画像送信できる機能をつけたい!
しかし標題エラーが出たので解決したい。結論:rails sでサーバー再立ち上げしたところ解決。
他解決案:
・ターミナル:brew install imagemagickでimagemagickを再インストール→bundle update
・ターミナル:spring stop、 spring startでspring再起動
参考URL: https://qiita.com/yokoyan/items/0f82fc3eede75cd6db55★下記コード入力後、
```
class Message < ApplicationRecord
belongs_to :group
belongs_to :user
validates :content, presence: true, unless: :image?mount_uploader :image, ImageUploader
end
```localhost立ち上げたら、こちらのエラーが出ました。
NameError in MessagesController#index
uninitialized constant Message::ImageUploader
エラー要因:
コードの記述自体はあっていたが、imagemagickがうまくインストールされてなかった。
そこで、再度 brew install imagemagickでインストールし直したが、serverを再度立ち上げてなかったのでその内容が読み込まれてなかった。学んだこと:
configやgemなどはインストールし直したりすると、その変更内容がサーバー上に反映されてなかったりするので、rails sでサーバー再度立ち上げるのが大事。
- 投稿日:2019-10-24T15:43:03+09:00
Rails6で追加されたinsert_allとimport(とその他)のパフォーマンス検証
前回書いた記事で、activerecord-importとRails6で追加されたinsert_allのパフォーマンスを比べたところ、importの方が速そうだったのでもう少しちゃんと検証してみました。
検証方法
速度検証
1,000件のユーザーを様々な方法でバルクインサートします。
1回のバルクインサートだと数msで終わってしまうので、上記を100回実行したときの合計時間で比較します。
計測はbenchmark
を使用します。メモリ使用量検証
こちらも同様に1,000件のユーザーを様々な方法でバルクインサートします。
計測はmemory_profiler
を使用します。環境
Ruby: 2.6.5
Rails: 6.0.0
rspec-rails: 3.9
factory_bot_rails: 5.1.1対象
activerecord-import
Railsでバルクインサートできるようにするもっとも有名なgemではないでしょうか。
生成したモデルの配列を渡すだけで簡単にバルクインサートできます。users = (1..100).map { User.new(name: 'name') } User.import usersgithub
https://github.com/zdennis/activerecord-importinsert_all
Rails6.0.0で追加されたバルクインサート用のメソッド。
ハッシュを渡すことで簡単にバルクインサートできます。
importと異なり、created_atとupdated_atを明示的に指定する必要があるので注意が必要です。users = (1..100).map { { name: 'name', created_at: Time.current, updated_at: Time.current } } User.insert_all users素のクエリー
Railsではクエリーをそのまま実行できるのでクエリーを文字列で生成して実行します。
sql = "INSERT INTO users (name, created_at, updated_at) VALUES ('name', NOW(), NOW()),('name', NOW(), NOW())...;" ActiveRecord::Base.connection.execute sql検証
実装
benchmarkで検証する
benchmark_bulk_insert
とMemoryProfilerで検証するprofiler_bulk_insert
を作りました。
純粋にバルクインサートの処理を検証したかったので、データ作成等はBenchmark外で生成しています。app/models/user.rbclass User < ApplicationRecord class << self def benchmark_bulk_insert # create data import_data = [] 1_000.times { import_data << new(name: 'name', created_at: Time.current, updated_at: Time.current) } insert_data = [] 1_000.times { insert_data << { name: 'name', created_at: Time.current, updated_at: Time.current } } values = [] 1_000.times { values << "('name', '#{Time.current.to_s(:db)}', '#{Time.current.to_s(:db)}')" } sql = "INSERT INTO users (name, created_at, updated_at) VALUES #{values.join(',')}" require 'benchmark' Benchmark.bm 15 do |r| transaction do r.report 'sql' do 100.times { bulk_insert_using_sql(sql) } end raise ActiveRecord::Rollback end transaction do r.report 'insert_all' do 100.times { bulk_insert_using_insert_all(insert_data) } end raise ActiveRecord::Rollback end transaction do r.report 'import' do 100.times { bulk_insert_using_import(import_data) } end raise ActiveRecord::Rollback end end end def profiler_bulk_insert # create data import_data = [] 1_000.times { import_data << new(name: 'name', created_at: Time.current, updated_at: Time.current) } insert_data = [] 1_000.times { insert_data << { name: 'name', created_at: Time.current, updated_at: Time.current } } values = [] 1_000.times { values << "('name', '#{Time.current.to_s(:db)}', '#{Time.current.to_s(:db)}')" } sql = "INSERT INTO users (name, created_at, updated_at) VALUES #{values.join(',')}" p '################# sql ########################' transaction do report = MemoryProfiler.report do bulk_insert_using_sql(sql) end report.pretty_print(retained_strings: 0, allocated_strings: 100, normalize_paths: true) raise ActiveRecord::Rollback end p '################# insert_all ########################' transaction do report = MemoryProfiler.report do bulk_insert_using_insert_all(insert_data) end report.pretty_print(retained_strings: 0, allocated_strings: 100, normalize_paths: true) raise ActiveRecord::Rollback end p '################# import ########################' transaction do report = MemoryProfiler.report do bulk_insert_using_import(import_data) end report.pretty_print(retained_strings: 0, allocated_strings: 100, normalize_paths: true) raise ActiveRecord::Rollback end end def bulk_insert_using_import(users) import users end def bulk_insert_using_insert_all(users) insert_all users end def bulk_insert_using_sql(sql) connection.execute sql end end end結果
rails consoleで実行しました。
irb(main):080:0> User.benchmark_bulk_insert;nil user system total real sql 0.000000 0.010000 0.010000 ( 0.601744) insert_all 8.900000 0.030000 8.930000 ( 9.951685) import 10.870000 0.210000 11.080000 ( 12.255004) irb(main):080:0> User.profiler_bulk_insert "################# sql ########################" Total allocated: 4152 bytes (23 objects) Total retained: 928 bytes (1 objects) "################# insert_all ########################" Total allocated: 4493518 bytes (67974 objects) Total retained: 145088 bytes (2005 objects) "################# import ########################" Total allocated: 7187421 bytes (91961 objects) Total retained: 1824536 bytes (13654 objects)まず処理時間を見てimportが速いというのは勘違いだったとわかりました。。。ちゃんと調査しないとダメですね。
メモリー使用量を見ると、処理時間と比例して増えています。importでは処理の過程で様々なオブジェクトを生成しているので他の処理より遅いと思われます。
前回の記事でinsert_allの方が遅かったのはbuild_listで生成したオブジェクトを変換するところも計測に入っていたからですね。
そしてimportやinsert_allよりも素のsqlがパフォーマンス最強ということがわかりました。
オブジェクトを生成すればするほど遅くなるので文字列をDBに投げるだけだとかなりパフォーマンスに差がでますね。
素のsqlで実装すると可読性や生産性が落ちるので乱用はしたくないですが、ここぞというときに使うと良さそうです。
- 投稿日:2019-10-24T14:24:49+09:00
【Rails】Ransackを使わずにソート機能を実装する
目次
- 1. 環境
- 2. 要件
- 3. カスタムヘルパー実装
- 3-1. StudentHelper
- 3-2. ApplicationHelper
- 4. コントローラー実装
- 5. ビュー実装
- 6. 結果(生徒コードでソート)
1. 環境
- rails 5.2.0
- ruby 2.6.2
- MacOS Version 10.14.6
2. 要件
- 学生テーブル
students
の学生コード (student_code)
、学生名 (name)
、学年 (grade_id)
でソートしたい。- 一覧画面に実装するが、検索フォームがあるのでその結果を保持する。
- 学生テーブル以外にも今後適用するので、共通処理は纏めておく。
3. カスタムヘルパー実装
3-1. StudentHelper
モデル別にカスタムヘルパーをモジュールを切って実装する。
module StudentHelper def student_sort_link(column) search_params = search_students_params.empty? ? {} : { search_students_form: search_students_params } sort_link(Student.human_attribute_name(column), column, search_params) end end
- カラム名を引数に取る。
- 検索パラメータが空の場合、空のハッシュを返す。さもなくばキー値のハッシュを返す
- 共通処理の
sort_link
に値を渡す
- 第一引数: ソート用のリンク文字列。辞書ファイルで日本語に翻訳
- 第二引数: クエリ文字列
column=[column]
を生成する- 第三引数: 検索パラメーターのキー値のハッシュ
3-2. ApplicationHelper
共通の処理を記述。
module ApplicationHelper def sort_link(column_name, column, **additional_params) if params[:column] != column.to_s || params[:direction].nil? link_to column_name, { column: column, direction: :asc, **additional_params } elsif params[:column] == column.to_s && params[:direction] == 'asc' link_to "#{column_name} ▲", { column: column, direction: :desc, **additional_params } elsif params[:column] == column.to_s && params[:direction] == 'desc' link_to "#{column_name} ▼", { column: column, direction: :asc, **additional_params } end end end
- 第三引数の検索結果のパラメーターは可変長で受け取る
- 条件分岐は以下の3パターン
- 現在ソートがかかっているカラムと指定されたカラムが一致しない、もしくは現在ソートがされていない
- => 指定したカラムで昇順でソートする
- 現在ソートがかかっているカラムとソート対象のカラムが一致し、なおかつ現在昇順でソートされている
- => 指定したカラムで降順でソートし、「▲」マークを付ける
- 現在ソートがかかっているカラムとソート対象のカラムが一致し、なおかつ現在降順でソートされている
- => 指定したカラムで昇順でソートし、「▼」マークを付ける
4. コントローラー実装
受け取ったクエリ文字列に従ってSQLを走らせる。
def index @students = Student.search(@search_form).order(sort_column => sort_direction).preload(:grade) end ... private def sort_column params[:column].in?(%w(student_code name grade_id)) ? params[:column] : :id end def sort_direction params[:direction].in?(%w(asc desc)) ? params[:direction] : :asc end
order
は キー => 値 =ソート対象カラム => ソート順
のハッシュを引数にとるsort_column
: 指定したカラムがホワイトリストにあればそれをorder
のキーに指定し、なければid
を指定する。sort_direction
: 指定したソート順がホワイトリストにあればそれをorder
の値に指定し、なければ昇順を指定する。5. ビュー実装
呼び出し元では以下のように引数にシンボルを指定する。
<th class="col-sm-2"><%= student_sort_link(:user_code) %></th> <th class="col-sm-3"><%= student_sort_link(:name) %></th> <th class="col-sm-2"><%= student_sort_link(:grade_id) %></th>6. 結果(生徒コードでソート)
- デフォルト
クエリ文字列: なし
- 昇順
クエリ文字列:
?column=user_code&direction=asc
- 降順
クエリ文字列:
?column=user_code&direction=desc
- 投稿日:2019-10-24T12:22:23+09:00
Rails6 のちょい足しな新機能を試す99(association extension編)
はじめに
Rails 6 に追加された新機能を試す第99段。 今回は、
association extension
編です。
Rails 6 では、define_extensions
で定義される module が model 内の module として定義されるようになっています。
実際の例を見た方がわかりやすいです。Ruby 2.6.5, Rails 6.0.0 で確認しました。
$ rails --version Rails 6.0.0今回は、 Author モデル と Book モデルを定義して、rails console で確認します。
Author は Book を複数持ちます(has_many
)。Rails プロジェクトを作る
$ rails new rails_sandbox cd rails_sandboxAuthor モデルを作る
name
を持つAuthor
モデルを作ります。$ bin/rails g model Author name
Book モデルを作る
title
,published
,author_id
を持つBook
モデルを作ります。$ bin/rails g model Book title published:boolean author:references
Author モデルを修正する
Author モデルを修正します。
2つのhas_many
アソシエーションを定義します。
1つ目のhas_many
では、 ブロックでordered
メソッドを定義します。
2つ目の scopepublished_books
はextending
を使ってordered
メソッドを利用できるようにします。
extending の引数がBooksAssociationExtension
になっていることに注意してください。app/models/author.rbclass Author < ApplicationRecord has_many :books do def ordered order(:title) end end has_many :published_books, -> { where(published: true).extending(BooksAssociationExtension) }, class_name: 'Book' endseed データを作成する
Author と Book の seed データを作成します。
db/seeds.rbauthor = Author.create( { name: 'Dave Thomas' } ) Book.create( [ { title: 'Programming Ruby', author: author, published: true }, { title: 'Pragmatic Programmer', author: author, published: true }, { title: 'Agile Web Development with Rails 6', author: author, published: false } ] )データベースを作成し、 seed データを登録する
$ bin/rails db:create db:migrate db:seed
rails console で確認する
rails console で確認します。
まずは、1つ目の
has_many
を確認します。irb(main):001:0> Author.first.books.ordered Author Load (0.3ms) SELECT "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT $1 [["LIMIT", 1]] Book Load (0.3ms) SELECT "books".* FROM "books" WHERE "books"."author_id" = $1 ORDER BY "books"."title" ASC LIMIT $2 [["author_id", 1], ["LIMIT", 11]] => #<ActiveRecord::AssociationRelation [#<Book id: 3, title: "Agile Web Development with Rails 6", published: false, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 2, title: "Pragmatic Programmer", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 1, title: "Programming Ruby", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">]>2つ目の
has_many
を確認します。irb(main):002:0> Author.first.published_books.ordered Author Load (0.8ms) SELECT "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT $1 [["LIMIT", 1]] Book Load (0.8ms) SELECT "books".* FROM "books" WHERE "books"."author_id" = $1 AND "books"."published" = $2 ORDER BY "books"."title" ASC LIMIT $3 [["author_id", 1], ["published", true], ["LIMIT", 11]] => #<ActiveRecord::AssociationRelation [#<Book id: 2, title: "Pragmatic Programmer", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">, #<Book id: 1, title: "Programming Ruby", published: true, author_id: 1, created_at: "2019-10-12 09:09:58", updated_at: "2019-10-12 09:09:58">]>
Author::BooksAssociationExtension
が定義されていることを確認します。irb(main):003:0> Author::BooksAssociationExtension => Author::BooksAssociationExtensionRails 5 では
Rails 5.2.3 では、
Author::BooksAssociationExtension
ではなく、AuthorBooksAssociationExtension
が定義されます。
以下のようにextending
の引数をAuthorBooksAssociationExtension
と書く必要があります。app/models/author.rbclass Author < ApplicationRecord ... has_many :published_books, -> { where(published: true).extending(AuthorBooksAssociationExtension) }, class_name: 'Book' endRails 5.2.3 ではグローバルなモジュールとして定義されるのに対し、Rails 6 では、 Author モデルの中のモジュールとして定義されるので、 Author モデルで利用するときは、Author をつけずに、
BooksAssociationExtension
と簡潔に書くことができるようになっています。試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try099_association_extension参考情報
- 投稿日:2019-10-24T11:15:43+09:00
【開発日記】薬価検索アプリno.3 〜設計・画面の設計〜
前回の内容はこちら。
【開発日記】薬価検索アプリno.2 〜設計・機能の洗い出し〜開発背景などはこちら
【開発日記】薬価検索アプリno.1 〜開発概要→企画〜現在の進捗状況
1.必要な機能の洗い出し
2.必要な画面設計 ⬅︎今ココ
3.データベース設計
4.Railsでアプリケーションの雛形作成
5.大きな機能から順に実装
6.テストコードを書いて動作を担保する
7.リファクタリングして整理する
8.デプロイ画面設計
今日は画面設計をしてみます。
開発するアプリケーションで必要な画面の設計をします。画面がどれだけ必要か、どのような画面にするかをこの段階で決めます。
一応、元ネタというか、参照サイトはこちらです
薬価サーチ大体は同じように実装してみようかと思います。
ワイヤーフレーム
今回のアプリは画面設計から自分なので、せっかくなのでワイヤーフレームなるものもやってみようかと思います。
ワイヤーフレームを作る際に使えるツールはこちらによくまとまっていますのでどうぞ
Webディレクターにオススメしたい!さくさくワイヤーフレームが作れちゃう便利ツールまとめ今回は、prottというサービスを使ってみます。
30日間は無料です。とりあえず作ってみました。
こういうの、考え始めると難しいですね。。。
まずはこの程度はMVPとして作りたい、という目標の意味も込めて。
この辺は少しずつ改善していきます。
prott
無料期間は30日間しかないですが、使い方は本当に簡単で楽なので、本格的にワイヤーフレームを作る必要が出てきたらぜひ使いたいです。
左サイドにはフォームボタンや、パンくず、ページネーションなんかもすでに用意してあって、そのままドラッグすればOK。
Webに合わせたものや、iOSに合わせたものも色々用意してあるので、なんとなく並べるだけでも十分それっぽくなる勢い。
画面右にはプロパティが表示され、文字の大きさ、背景色の変更などが行えます。
まとめ・所感
こういうデザインとかって、サイトの目的や、誰が使うか、何を伝えたいのかなどがはっきりしていないと全く決まらない。
今の開発では既存サイトの改善版を作ろうとしているだけなので、自分で欲しいイメージがある程度はあるけど、全くのゼロからサイトを作ろうとしたら、本当に大変な仕事だと実感。
デザイナーさんありがとうございます。(一緒に働いたことないけど)最後までおつきあいいただきありがとうございました。
- 投稿日:2019-10-24T03:13:43+09:00
【エラー】bundle実行時にmysql2がインストールされない
環境
- Mac OS
- Ruby 2.6.3
- Rails 5.2.3
- MySQL 8.0
エラー内容
Dockerを使ってRails+MySQLでの環境構築をした後に
bundle installを実行すると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.といったような
mysql2がインストールできないというエラーが発生しました。対処方法
$ bundle config --local build.mysql2 "--with-cppflags=-I/usr/local/opt/openssl/include" $ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"上記二つのコマンドを実行し、
bundlerの設定をするとエラーが解決できて
無事mysql2がインストールできました!感じたこと
エラー発生時なかなか解決できず困りましたが
とにかく色々と調べた結果上記の対処方法で解決できました!(汗)Dockerを使って新しく環境構築するとこのエラーが発生するので
環境構築をし終えたら毎度このコマンドを実行してます!
- 投稿日:2019-10-24T03:12:40+09:00
Railsのslim導入方法
slimについて
slimdoctype html html head title | Sample = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' body = yield上記のような記述方法のRuby制の軽量なテンプレートエンジン。
【1】gemの追加
gem 'slim-rails' gem 'html2slim'Gemfileに上記のコードを追加する。
その後、bundle installを実行する。
$ bundle【2】erbをslimに変換する
$ bundle exec erb2slim app/views/layouts/ --delete上記のコマンドを打つと
app/views/layouts内のerb形式のファイルがslimに変換される。
以上でslimの導入が完了です。感じたこと
erbよりも記述量が減り
- HTMLタグを省略でき効率よく書ける
- 軽量で動作が速い
- 全体がすっきりまとまっていてメンテナンスがしやすくなる
など様々なメリットを感じたので積極的に導入してます!!
- 投稿日:2019-10-24T01:31:18+09:00
MVCの理解
自分なりに腑に落ちた言い回しと言いますか、そういう物を書いてみます。
※初学者の参考になれば幸いです。モデル
rails g model cotent:text
などと叩いてテーブルを作るとする。
そのテーブルを扱いたい。どうするのか?
A.テーブルを操作するためのモデルと呼ばれる特殊なクラスを用いて、データベースを使う
※クラスは設計図のようなものビュー
モデルのデータを取り出してユーザが見るのに適した形で表示する要素である。すなわち、UIへの出力を担当する。例えば、ウェブアプリケーションではHTML文書を生成して動的にデータを表示するためのコードなどにあたる。
参照URL
Model View Controller(Wikipedia)コントローラー
ユーザからの入力(通常イベントとして通知される)をモデルへのメッセージへと変換してモデルに伝える要素である。すなわち、UIからの入力を担当する。モデルに変更を引き起こす場合もあるが、直接に描画を行ったり、モデルの内部データを直接操作したりはしない。
参照URL
Model View Controller(Wikipedia)余談
コントローラーとビューは早い段階で理解できていましたが、モデルというのが曖昧だったので書いてみました。(というかモデルの事だけを書き留めておこうと思ったら中途半端だったのでVとCについても引用してきただけなのは内緒)
MVCって何かわかる?っていう質問に対しての回答になっていればいいのですが・・・。
- 投稿日:2019-10-24T00:07:27+09:00
rake routesによるエラー解決(devise)
rake routesを使ってエラーを解決した事例
タイトル通り、
rake routes
を使って、エラーを解決した事例を紹介します。
rake routes
とは、ターミナルコマンドの一つで、HTTPリクエストの一覧を表示させます。
ざっくり言うと、ビューのリンクがどのコントローラを動かすか、つまりどのビューを表示するかの一覧を表示させることができます。
このコマンドを使うとサクサクエラーが解決することが多々あります。例えば、devise gemを使ってログイン周りを実装しようとしている時に、サインアップしようとすると下記のようなエラーが出るようになりました。
ActiveRecord::StatementInvalid in Devise::RegistrationsController#create Mysql2::Error: Field 'name' doesn't have a default value: INSERT INTO `users` (`email`, `encrypted_password`, `created_at`, `updated_at`) VALUES (〜)若干読みにくいエラーですが、要するに「nameをテーブルに保存したいのに、nameが入力されてないよ」というエラーですね。
確かにこの時表示されていたサインアップ画面には'name'を入力する箇所がありませんでした(画面無し)
しかし、'name'も入力するようのサインアップ用ビューファイルは用意してあります。
何らかの原因で、このビューファイルが表示されていないようでした。原因を色々探りましたが、やはりサインアップ画面に移動するためのリンクが上手くできてないのではないかと思い、確認してみました。
※該当箇所
= link_to "Sign up", new_registration_path(resource_name), class: 'btn'この
new_registration_path
で表示するビュー(動かすコントローラとアクション)を決めています。
それでは、このリンクを実行すると、どのビューファイルを読み込むのかをrake routes
で調べてみました。
行数が短くないので、該当箇所だけ載せます。(この記事では、用語などの細かい説明はしません。というか、まだできません)Prefix Verb URI Pattern Controller#Action new_user_registration GET /users/sign_up(.:format) devise/registrations#new一番右の部分を読めば、どのビューファイルを呼び出そうするのかがわかります。
この場合はdevise/registrations#new
によって表示されるビュー、
つまり/Users/****/projects/chat-space/app/views/devise/registrations/new.html.haml
を表示させようとしているけれども、それが上手くいってないことが予想されます。
そこで実際に/Users/****/projects/chat-space/app/views/devise/registrations/new.html.haml
にあるファイルを確認してみました。すると…そこにビューファイルがありませんでした!
わかってしまえば情けない話なのですが、ビューファイルを違う場所に置いていたのがエラーの原因になったようです。
「該当のビューがないからデフォルトのビューを表示する→そのビューではnameが入力できない(テーブルに送信できない)から上記のようなエラーが出る」というのが、今回のエラーの原因だったようです。その後、適切なビューファイル(
new.html.haml
)を適切な場所(/Users/****/projects/chat-space/app/views/devise/registrations/
)に配置したことにより、エラーは解決しました。終わりに
railsでエラーとなると、真っ先にコードの間違いが思い浮かぶと思いますが、今回のようにファイルのディレクトリによるエラーも少なくない回数で起きます。他にも、例えばファイル名を
now.html.haml
としていても今回のエラーは起きていたはずなので、ファイル名を間違えないことも重要です。
ファイル名やディレクトリについてのエラーは、原因が自力じゃ気づきにくいので、是非ともrake routes
をエラー解決の選択肢としてみてください。