- 投稿日:2019-05-03T23:20:55+09:00
Day002 webエンジニアへの道 - Ruby on Railsの基礎を学ぶ -
こんにちは。
webエンジニアを目指すtomoです。エンジニアの勉強をしている日々の学びをQiitaに記録しています。
私自身の頭の整理と今後迷った時のリファレンスも兼ねて書き連ねていきますが、同じようにエンジニア転職を考えている方の参考にもなればと考えています。
また、「ここ間違ってる!こっちが正しい!」といったご指摘もあれば頂けると嬉しいです?
前回は仮想マシンの環境設定を行いました。
しかし、実際にRuby on railsを使い始めたところ必要な関連ソフトが足りずエラーが連発しました。
必要な関連ソフトは何かなどちゃんと把握した上で進めたいのですが、それでは時間がいくらあっても足りなさそうなので、関連ソフトの理解は後にしてまずはdot-installのレッスン通りCentOS6を用いた環境で学習を進めることにします。
また、ひとつひとつのコードを全て書き写しているとキリがないので、学習を進める中で調べたことやエラー対応したことに絞って記述していきます。
railsサーバの起動
commandlinerails server -b [ipアドレス] -d-b オプションでバインドするipアドレスを指定。
-d オプションでデーモンとしてサーバを起動。
デーモンとはメモリ上に常駐し、いつでも処理できるよう待機するプログラムのこと。[参考]
railsコマンド(rails)
デーモン (daemon)rails(5.1.4)以降ではリンク先が表示されないため、ブラウザに直接
http://[ipアドレス]:3000
と入力して動作確認する。scaffoldで作成したファイルを削除する
commandlinerails destroy scaffold [name][name]は削除したいscaffoldの名前。
rails g scaffold [name]
で要素を間違えて指定した時に有用Modelの作成
rails g model Post title:string body:textPost → Modelは個々のデータ構造を定義するので単数形で指定する。
string → 1行テキスト
text → 複数行のテキスト追加した変更が反映されない時の対応
Transmit5を利用して仮想マシンとのファイル転送を行っていたが、エディタで保存したはずの変更がブラウザに反映されなかった。
↓
原因:Transmitとの接続が途中で切れていたため。(Transmit上で「サーバから切断」を行うと、再接続しても既に開いているエディタのファイルとのリンクが切れてしまう)tips:ブラウザまたはコンソール上で見える簡単な変更(htmlの文章やplaceholderなど)を加えてみると、どこに不具合があるか特定しやすい。目で見て確認しづらい処理の不具合をチェックする場合は処理の内容を簡素化(文字列の出力など)すると特定しやすい。
★メモ
*後日調べること & 気づいたこと
- kill -9 [プロセス番号] → killコマンドで-9以外の場合どうなる?
- Convention over Configuration → どのファイルがどこのディレクトリにあるか、指定は単数形か複数形かといった細かいルールがrailsには設定されている。ということらしいので調べる。
- Active Recordとは何か?
- MVCのフローを理解するため整理して図に書き出す。
- 開発環境構築が始めの1歩だと思っていたが、「どのパッケージをどのバージョンで入れるか」というのは難易度が高いので詳しくは後で学んだ方が良い...
さいごに
2日目にして完全に自分用の学習メモと化してきました...
続けていった後で、「うわ、このとき自分はこんな簡単なことで悩んでたのかよ...」ってなれるよう精進します。
twitterもやっているので、宜しければフォローお願いします!
@tomo_tech_
- 投稿日:2019-05-03T20:29:25+09:00
docker で rspec の system spec を実行するための設定メモ
はじめに
docker 環境で rspec の system spec を実行させるための設定に手間取ったので、メモ代わりに書いておきます。
railsが動くdocker の image に chrome をインストールするのもちょっとなあと思ったので、 selenium/standalone-chrome-debug
を使う方法です。確認した Rails 環境は、5.2.2 です。
ちなみに Gemfile でバージョン指定はしていませんが、Gemfile.lock を確認したところ
capybara (3.14.0) rspec-rails (3.8.2) selenium-webdriver (3.141.0)となってました。
docker-compose.yml の編集
chrome が動作するように設定を追加します。
Rails はweb
で動作します。docker-compose.ymlversion: '3' services: web: build: . ports: - "3000:3000" volumes: - .:/app - bundle:/usr/local/bundle tty: true environment: # この環境変数を追加 - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 以下の4行を追加 selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none db: image: postgres:10.7-alpine volumes: - pgsqldb:/var/lib/postgresql/data environment: - "POSTGRES_USER=xxxx" - "POSTGRES_PASSWORD=xxxx" volumes: pgsqldb: bundle:chrome を動作させるために、selenium_chrome を追加しています。
logging
の設定をdriver: none
にしているのは、不要なログ出力を抑制するためです。rspec は web で実行しますが、 chrome は、selenium_chrome で動作させるため、web 側からアクセスできるように
環境変数SELENIUM_DRIVER_URL
を web 側に追加しています。Gemfile を編集する
gem ファイルに rspec-rails を追加します。
chromedriver-helper を削除します。webdrivers gem は追加しません。Gemfilegroup :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'rspec-rails' enddocker 環境で、bundle install を実行する
docker 環境で追加した gem をインストールします。
$ docker-compose up -d $ docker-compose exec web bash # 以下は web コンテナ内で実行します。 $ bundle installrspec の初期設定をする。
続けて、rspec の初期設定をします。
# 以下は web コンテナ内で実行します。 $ bin/rails g rspec:installsystem spec の設定をする。
system spec の設定をするために、
spec/rails_helper.rb
を編集し、spec/support/capybara.rb
を追加します。spec/rails_helper.rb# 以下の1行を有効にします。 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines.spec/support/capybara.rbrequire 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do |config| driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch("SELENIUM_DRIVER_URL"), desired_capabilities: :chrome } Capybara.server_host = 'web' Capybara.app_host='http://web' end end
spec/support/capybara.rb
内でselenium_chrome
側の headless chrome を利用するための設定をしています。
web コンテナ側から selenium_chrome コンテナ の chrome を使用するために、options
の中で、url
にSELENIUM_DRIVER_URL
環境変数の値を設定しています。
headless chrome からは、ローカル環境ではなく、web
コンテナ側の rails アプリを表示してテストする必要があるため、
Capybara.server_host
とCapybara.app_host
を設定しています。参考情報
- 投稿日:2019-05-03T20:29:25+09:00
Docker で RSpec の System Spec を実行するための設定メモ
はじめに
docker 環境で rspec の system spec を実行させるための設定に手間取ったので、メモ代わりに書いておきます。
railsが動くdocker の image に chrome をインストールするのもちょっとなあと思ったので、 selenium/standalone-chrome-debug
を使う方法です。確認した Rails 環境は、5.2.2 です。
ちなみに Gemfile でバージョン指定はしていませんが、Gemfile.lock を確認したところ
capybara (3.14.0) rspec-rails (3.8.2) selenium-webdriver (3.141.0)となってました。
docker-compose.yml の編集
chrome が動作するように設定を追加します。
Rails はweb
で動作します。docker-compose.ymlversion: '3' services: web: build: . ports: - "3000:3000" volumes: - .:/app - bundle:/usr/local/bundle tty: true environment: # この環境変数を追加 - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 以下の4行を追加 selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none db: image: postgres:10.7-alpine volumes: - pgsqldb:/var/lib/postgresql/data environment: - "POSTGRES_USER=xxxx" - "POSTGRES_PASSWORD=xxxx" volumes: pgsqldb: bundle:chrome を動作させるために、selenium_chrome を追加しています。
logging
の設定をdriver: none
にしているのは、不要なログ出力を抑制するためです。rspec は web で実行しますが、 chrome は、selenium_chrome で動作させるため、web 側からアクセスできるように
環境変数SELENIUM_DRIVER_URL
を web 側に追加しています。Gemfile を編集する
gem ファイルに rspec-rails を追加します。
chromedriver-helper を削除します。webdrivers gem は追加しません。Gemfilegroup :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'rspec-rails' enddocker 環境で、bundle install を実行する
docker 環境で追加した gem をインストールします。
$ docker-compose up -d $ docker-compose exec web bash # 以下は web コンテナ内で実行します。 $ bundle installrspec の初期設定をする。
続けて、rspec の初期設定をします。
# 以下は web コンテナ内で実行します。 $ bin/rails g rspec:installsystem spec の設定をする。
system spec の設定をするために、
spec/rails_helper.rb
を編集し、spec/support/capybara.rb
を追加します。spec/rails_helper.rb# 以下の1行を有効にします。 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines.spec/support/capybara.rbrequire 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do |config| driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch("SELENIUM_DRIVER_URL"), desired_capabilities: :chrome } Capybara.server_host = 'web' Capybara.app_host='http://web' end end
spec/support/capybara.rb
内でselenium_chrome
側の headless chrome を利用するための設定をしています。
web コンテナ側から selenium_chrome コンテナ の chrome を使用するために、options
の中で、url
にSELENIUM_DRIVER_URL
環境変数の値を設定しています。
headless chrome からは、ローカル環境ではなく、web
コンテナ側の rails アプリを表示してテストする必要があるため、
Capybara.server_host
とCapybara.app_host
を設定しています。参考情報
- 投稿日:2019-05-03T17:18:19+09:00
Railsのclass_methodsがやっていること
module ModuleA extend ActiveSupport::Concern class_methods do def method_a puts "method_a" end end end class ClassA include ModuleA end ClassA.method_a #=> "method_a"Moduleのメソッドをクラスメソッドとして使用できるようにするために、class_methodsを使う。
class_methodsで囲んだ箇所をActiveSupportのConcernがextendしてくれる。class_methodsがないと‥
module ModuleA def method_a puts "method_a" end end class ClassA include ModuleA end ClassA.method_a #=> "NoMethodError: undefined method `method_a' for ClassA:Class" instance_a = ClassA.new instance_a.method_a #=> "method_a"ModuleAのメソッドはClassAのインスタンスメソッドになる。
ちなみに
module ModuleA extend ActiveSupport::Concern class_methods do def self.method_a puts "self_method_a" end end end class ClassA include ModuleA end当たり前だが、ModuleAのself.method_aはあくまでもModuleAの特異メソッドだからClassAからもインスタンスからも呼び出すことはできない。
- 投稿日:2019-05-03T15:43:35+09:00
RailsでSimStringを使う
概要
- railsの検索で曖昧な語句を検索するためのモジュール (gem) です。
- 検索はsimstring_pure をつかっています。
ソース
https://github.com/junara/simstring_searchable
使い方
インストール
gem 'simstring_searchable', github: 'junara/simstring_searchable'bundle install使用したいモデルに
SimstringSearchable
を追加するyour_model.rbclass YourModel < ActiveRecord include SimstringSearchableこれで準備完了
検索
インデックス作成
最初にインデックスを作成する必要があります。インデックスの作成は、
create_simstring_index
methodでおこないます。
引数には、検索したい(インデックスを作成したい)カラム名を指定してください。
インデックス作成後、追加したデータでインデックスを更新したい場合も同じメソッドを実行してください。(更新のみをおこなうメソッドは用意していません)
作成された、インデックスは後述のremove_simstring_index
で破棄するまで有効です。irb> YourModel.create_simstring_index(:name)オプションとして 連続したn個の文字で分割するかを第2引数に渡して(整数)設定することができます。デフォルトは3です。
検索
検索は、
simstring_search_result
でおこないます。
第1引数に検索したいカラム。第2引数に検索文字列
を入れてください。」irb> YourModel.simstring_search_result(:name, '検索文字列')オプションとして閾値(threshold (0から1。1が最もマッチしていることを示す))とデータ数(limit。上位いくつのデータを表示するか)を設定することができます。閾値のデフォルトは0.5、データ数のデフォルトは10です。
最もマッチしたデータのみ取得する場合は、
simstring_find_by
を使ってください。イメージとしては、railsのfind_byに似たかんじのメソッドです。インデックスの破棄
作成されたインデックスは破棄するまで有効です。メモリを喰うので、使わなくなったら破棄してください。
irb> YourModel.remove_simstring_index(:name)なお、まとめてインデックスを破棄できるメソッドはこちらです。
irb> YourModel.remove_whole_simstring_indexesアドバンスド
インデックス作成の際に、ブロックを渡すことで文字列を加工する事ができます。
全角英数字を半角英数字に修正したいとうありましたらこちらを利用してください。下記では、空白(全角 or 半角)を削除してインデックスを作成する例です。
irb> YourModel.create_simstring_index(:name, gram = 3) {|str| str.gsub(/( | )/, '')} # replace space by ''その他
検索結果が空
[] or nil
の場合はthresholdの値を低く(たとえば 0.1) して検討してください。
また、 結果の順位が印象と合わない場合は、gram
の値を変更すると良くなることがありますので調整してみてください。以上。
- 投稿日:2019-05-03T15:34:32+09:00
windowsでrails + elasticsearch環境を構築する
はじめに
最近、個人的に作っているアプリで全文検索を使ってみたくなり、Elasticsearchの環境を構築しました。
しかし、家のPCのOSはWindowsで、Qiitaや開発ブログなどでは大体Mac(Linux)で書かれていて、Windowsを使っている身としては肩身が狭いです。。。そして、Elasticsearchを取り入れる時にも、エラーが結構出ました。
Windows + Railsでelasticsearchを取り入れた時に躓いた部分などを備忘録的に投稿します。elasticsearch初心者なので、初歩的なエラーが多いです。
アプリにelasticsearchをセットアップ
なお、セットアップにはこちらを参考にさせて頂きました。
gem 'elasticsearch-model', git: 'git://github.com/elasticsearch/elasticsearch-rails.git' gem 'elasticsearch-rails', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'class PoolHall < ActiveRecord::Base include Elasticsearch::Model end以上をGemfileに記載して、bundle installを実行。Elasticsearch::Modelをincludeして、ためしにPoolHall.search 'test'をしてみました。
検索は出来ているように思えましたが、検索結果を出力してみようと思い、resultsのeachやmapを呼び出すと以下のようなエラーが出ました。
Faraday::ConnectionFailed in PoolHallsController#new Failed to open TCP connection to localhost:9200 (Connection refused - connect(2) for "localhost" port 9200) Extracted source (around line #6):調べたところ、elasticsearchをlocalhost:9200で起動しないと駄目だと知りました。
elasticsearchをインストール
elasticsearchはJavaを実行できる環境が必要なので、こちらからJREをダウンロード。
Javaはインストールしていたので、実行できるかと思いきや、elasticsearchを実行するとエラーが出ました。こちらは、Javaのバージョンが古かったので、アップデートしたら実行できました。 Java SE Runtime Environment 8u211のWindows x86 Offlineをダウンロードし、インストールしました。
こちらのDownloadsのWindowsから、elasticsearchをダウンロード。バージョンは7.0.1です。
elasticsearchをダウンロードし、解凍したら、elasticsearch-7.0.1-windows-x86_64.zip\elasticsearch-7.0.1\bin\elasticsearch.batを実行すると、elasticsearchが起動できます。
インデックスを作成
以下のコマンドを実行。
rails celasticsearchを起動し、インデックスを作成し、登録。
PoolHall.__elasticsearch__.create_index! force: true PoolHall.importいちいち、コンソールを起動してやるのも面倒なので、こちらを参考にタスクを作成。
# rails g task elasticsearchで作成 namespace :elasticsearch do desc 'Elasticsearch のindex作成' task create_index: :environment do PoolHall.__elasticsearch__.create_index! force: true end desc 'Article を Elasticsearch に登録' task import_hall: :environment do PoolHall.import end endこれで以下のコマンドでインデックスを作成できる。
rake elasticsearch:create_index rake elasticsearch:import_hall検索結果をactiverecordとして扱いたい
これで、検索の準備が整い、検索をしてみました。
@hall = PoolHall.search(params[:word)viewで、検索結果を表示しようとしたところ、インスタンスメソッドを呼び出すところでメソッドが定義されていないとエラーが発生しました。
thumbnailというメソッドは、PoolHallモデルのインスタンスメソッドで、サムネイルを返すメソッドでした。
<%= @hall.each do |h| %> <%= image_tag h.thumbnail %> <% end %>調べてみると、検索結果はElasticSearchのオブジェクトとして返ってきていたので、当然の結果でした。activerecordとして扱いたい場合は以下の通り。
@hall = PoolHall.search(params[:word).recordsおわりに
elasticsearchは非常に便利ですね。
今後はkibanaなどにも手を出していきたいです。
- 投稿日:2019-05-03T14:52:55+09:00
progateの「Ruby on Rails」レッスンを自分のPCで再現したい。が、データベースの構築方法がわかりません。
現在、Progateでプログラミング学習をしている者です。
Webアプリを開発したく、Ruby on Railsのレッスンを学んでいます。レッスンで学んだSNSアプリを自分のPCにて開発しているのですが、データベースの構築の方法がわからず、同じように開発をすることができません。
開発はatomを使用しております。
お分かりの方、教えていただけますと幸いです。
PCなどの状況は下記の通りです。
OS:macOS Mojave(バージョン10.14.4)
Rails 5.0.3↑progateのコラム通りに構築をしました。
お分かりの方、教えていただけますと幸いです。
- 投稿日:2019-05-03T11:33:01+09:00
f.collection_selectについて
題記について、Railsドキュメントを見てもあまり良く理解出来なかったので、まとめてみる。
Railsドキュメントより
- form_for, form_tagにて使用可
- 取ることができる引数について、Railsドキュメントによると、
(プロパティ名, オブジェクトの配列, value属性の項目, テキストの項目 [, オプション])
と記載されており、例では、
<%= f.collection_select(:name, @categories, :id, :name) %>とコードを書くと、HTMLは、
<select id="page_name" name="page[name]"> <option value="1">Railsの基礎知識</option> <option value="2">Rubyの基礎知識</option> </select>にて生成されるらしい。
参考:Railsドキュメント
もう少し詳しく
Railsドキュメントに挙げられている例をもう少し詳しく見てみる。
状況は?
- Pageモデル(テーブル)が存在する。
- Pagesテーブルは以下のような構成になっている。
id name 1 Railsの基礎知識 2 Rubyの基礎知識 3.
@categories = Page.allとしてすべてのオブジェクトをインスタンス変数に代入しているとする。
引数と生成されるHTMLページの関係性は?
- 第一引数(プロパティ名)は、selectタグのid属性とname属性に関係する。
id="page_〜" name="page[〜]"といった具合に設定される。※今回の場合は:nameとしており、pageはモデル名となっている。- 第二引数(オブジェクトの配列)は、何となく理解できるので省略
- 第三引数(value属性の項目)は、value="〜"にて設定したい値のカラム名が入る。そのため、idカラムの値を設定したい場合は、:idとなる。
- 第四引数(テキストの項目)は、optionタグ内のテキストに設定したい値のカラム名が入る。そのため、nameカラムの値を設定したい場合は、:nameとなる。
- 投稿日:2019-05-03T09:28:45+09:00
bundle installでmysql2がエラーになる件
背景
とある教材を見ながらプロジェクトを作成していたところ、bundle install を実行したらエラーが発生したのでメモ。
「Mysqlの導入で発生したエラー解消」という内容になりますが、結論としては「」することにより解消できました。
経緯|実行したコマンド
$ rails new myapp --database=mysql --skip-bundle --skip-test $ cd myapp $ bundle installエラー内容
(省略) Gem::Ext::BuildError: ERROR: Failed to build gem native extension. (省略) 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 /var/folders/mj/93db0vd975799gg9w3nnr3_r0000gn/T/bundler20190503-14531-xlyd6cmysql2-0.5.2/gems/mysql2-0.5.2 for inspection. Results logged to /var/folders/mj/93db0vd975799gg9w3nnr3_r0000gn/T/bundler20190503-14531-xlyd6cmysql2-0.5.2/extensions/universal-darwin-18/2.3.0/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'` succeeds before bundling. In Gemfile: mysql2考えたこと
- そういえばプロジェクト作成のときにDBをmysqlで指定したのは始めてだな。
- mysql2 0.5.2 をインストールできないということかな。
gem install mysql2 -v '0.5.2'
を実行しなさいと言ってるのかしら?- Gemfile でmysql2 のバージョンは何になってる?
- ひとまずググるか。
やったこと
Gemfile の確認
gem 'mysql2', '>= 0.3.18', '< 0.6.0'Gemfile 内の記述は以下の通り。
ダメな部分があるか否かが不明のため、ひとまず確認のみ。
エラーログに記載されているコマンドを実行
$ gem install mysql2 -v '0.5.2' ERROR: While executing gem ... (Gem::FilePermissionError) You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.結果、エラーの解消にはつながらなかった。
ググる!→このコマンドでエラー解消!【解決】
エラーの原因特定、解消方法を調べてみたところ、下記方法で解決した。
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include" $ bundle installその後、下記コマンドを実行することで「You’re on Rails」画面を開くことができたので今回のエラーは解決とする。
$ rails db:create $ rails s原因・学び
今回のエラー解消に際し、色々と調べる中で学んだことをメモ。
エラーについて
どうやら、今回は下記エラーログがポイントだった模様。
ld: library not found for -lssl原因を言語化すると、opensslのパスがビルドの時に必要(ビルドのときにLDFLAGSとかCPPFLAGSとかにパスを追加する記述が必要)ということ。
ld とは?
ld
はGNUリンカーと呼ばれるコマンドとのこと。$ man ld
で内容を確認できる。
-lssl
は、mysql2 gemインストール時にld
コマンドに与えられたオプションの一つらしい。ここでは詳細の掘り下げはせずに置いておく。。
解決策について
bundle config とは?
下記ページにて詳細を確認する事ができた。
http://ruby.studio-kingdom.com/bundler/bundle_config/
コマンド
bundle config
により、Bundlerの設定システムを操作することができるとのこと。なお、 Bundlerは自身の設定を下記の優先順位で取得する。
1. ローカルのアプリケーション(
app/.bundle/config
)
2. 環境変数
3. ユーザーのホームディレクトリ(~/.bundle/config
)また、
--local
オプションをつけることで、ローカルのアプリケーションに対して設定が可能。つまりプロジェクト単位の設定となる。宿題
- bundle config で指定する「LDFLAGS」「CPPFLAGS」についてもう少し調べるべし。
- ld コマンドについてももう少し。(仮)
- 投稿日:2019-05-03T08:01:52+09:00
Rails6 のちょい足しな新機能を試す10(year_format(令和ネタ)編)
はじめに
Rails 6 に追加されそうな新機能を試す第10段。 今回のちょい足し機能は、
date_select の year_format
編です。
datetime_select, date_select で year_format オプションを指定することで、年の書式を指定できるようになりました。記載時点では、Rails は 6.0.0.rc1 です。
gem install rails --prerelease
でインストールできます。今回は、せっかくなので、「令和」に対応した Ruby 2.6.3 を使ってます。
$ rails --version Rails 6.0.0.rc1プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1
scaffold で CRUD を作成する
$ cd sandbox6_0_0rc1 $ bin/rails g scaffold User name birth_at:datetime $ bin/rails db:create db:migrate
app/views/users/_form.html.erb
を編集する
:year_format
オプションを追加して年の書式を変更してみます。app/views/users/_form.html.erb... <%= form.datetime_select :barth_at, year_format: ->year { "#{Date.new(year, 12, 31).jisx0301.split('.').first}(#{year})" } %>登録画面を表示
User の登録画面を表示してみると、年の書式が変わっていることがわかります。
参考情報
- 投稿日:2019-05-03T07:21:16+09:00
Rails6 のちょい足しな新機能を試す9(エラーメッセージのフォーマット編)
はじめに
Rails 6 に追加されそうな新機能を試す第9段。 今回のちょい足し機能は、
full message
編です。
ymlファイルで指定できるエラーフォーマットが拡張されてます。記載時点では、Rails は 6.0.0.rc1 です。
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1
model を作る
$ cd sandbox6_0_0rc1 $ bin/rails g model User name email $ bin/rails db:create db:migratemodel に validation を追加する
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true end何もymlファイルに指定がないとき
何もymlファイルに指定がないときには、今までと同じ
Name can't be blank
とEmail can't be blank
になります。$ bin/rails c Running via Spring preloader in process 653 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):002:0> user.validate => false irb(main):003:0> user.errors.full_messages => ["Name can't be blank", "Email can't be blank"] irb(mail):004:0> exityml ファイルでエラーフォーマットを指定
エラーメッセージのフォーマット全体を指定してみます。
config/locales/en.ymlen: errors: format: '%{message}'指定されたフォーマット (
can't be blank
だけ) にエラーメッセージが変わります。$ bin/rails c Running via Spring preloader in process 696 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):002:0> user.validate => false irb(main):003:0> user.errors.full_messages => ["can't be blank", "can't be blank"] irb(main):004:0> exit実はここまでは、Rails 5.2.3でも同じです。
yml ファイルで model レベルでエラーフォーマットを指定
ここからが、Rails6のちょい足しです。
エラーメッセージのフォーマットを model レベルで指定してみます。config/locales/en.ymlen: errors: format: '%{message}' activerecord: errors: models: user: format: '%{attribute} : %{message}'では、エラーメッセージを確認してみましょう。
このとき、ActiveModel::Errors.i18n_customize_full_message = true
にします。$ bin/rails c Running via Spring preloader in process 1338 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message = true => true irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["Name : can't be blank", "Email : can't be blank"] irb(main):007:0> exitなお
ActiveModel::Errors.i18n_customize_full_message
はデフォルトでは、false
です。
ActiveModel::Errors.i18n_customize_full_message = false
のときは、modelレベルの設定は無視されます。$ bin/rails c Running via Spring preloader in process 1343 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message => false irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["can't be blank", "can't be blank"] irb(main):005:0> exityml ファイルで attribute レベルでエラーフォーマットを指定
今度は、attribute レベルでエラーフォーマットを指定してみます。
config/locales/en.ymlen: errors: format: '%{message}' activerecord: errors: models: user: format: '%{attribute} : %{message}' attributes: name: format: '%{message} - %{attribute}'では、試してみましょう。
name
は、'%{message} - %{attribute}'
のフォーマットが適用され
'%{attribute} : %{message}'
のフォーマットが適用されます。$ bin/rails c Running via Spring preloader in process 1383 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message = true => true irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["can't be blank - Name", "Email : can't be blank"] irb(main):005:0>
i18n_customize_full_message
の設定今回、irb の中で動的に設定しましたが、
i18n_customize_full_message
は、 config ファイルで設定できます。config/application.rbmodule Sandbox600rc1 class Application < Rails::Application ... config.active_model.i18n_customize_full_message = true end end参考情報
- 投稿日:2019-05-03T05:34:49+09:00
【Rails5.2.1】credentials.ymlを使用してアプリをHerokuにデプロイできなかった話
- 投稿日:2019-05-03T05:34:12+09:00
【10日間でポートフォリオ作成に挑戦】6日目:テストコードの実装
概要
今回は、2019年のGW期間(10日間)を全て費やして取り組む
ポートフォリオの製作過程
を取りまとめた内容を投稿させて頂きます。(投稿は毎日行う予定)全体通した取り組みの詳細については、前回までの記事をご参照ください。
【10日間でポートフォリオ作成に挑戦】1日目:要件定義〜記事投稿のCRUD
【10日間でポートフォリオ作成に挑戦】2日目:アクセス制限〜コメントのCRUD機能
【10日間でポートフォリオ作成に挑戦】3日目:ページネーション~CKEditorの導入
【10日間でポートフォリオ作成に挑戦】4日目:テーブル分割〜CKEditorのフォームへの反映
【10日間でポートフォリオ作成に挑戦】5日目:CKEditorへ画像アップロード機能を追加今日一日の作業内容
ここからは、今日1日で取り組んだ作業内容をご説明します。
テストコードの記述
これまで実装した各機能のテストコードを記述して行きました。
- アカウント認証機能(devise)
- 記事のCURD機能(一覧、新規作成、編集、削除)
- 記事へのコメントのCRUD機能(新規作成、編集、削除)
- 記事一覧のページネーション(kaminari)
- 画像のアップロード機能(Shrine)
- CKEditorによる記事の編集機能
テストで利用したgemは下記の通りです。
group :development, :test do (中略) gem 'rspec-rails' gem 'factory_bot_rails' gem 'rails-controller-testing' end group :test do gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'chromedriver-helper' endなお、テストコードの実装にあたっては、Everyday Railsをかなり参考にさせて貰いました。
Everyday Rails - RSpecによるRailsテスト入門今日の失敗
ここからは、今日の失敗をまとめます。
テストコードを通す為に無理矢理な処理を記述する
※追記(5/3):こちらのコードは、伊藤さん(@jnchito)がコメントにて、適切な修正案を提示して頂いているので、そちらもご参考にしてください。
まずは、下記のコードをご覧ください。
spec/system/post_spec.rbrequire 'rails_helper' RSpec.describe 'post_comment', type: :system do let!(:user) { create(:user) } let(:post) { create(:post, id: 1, user_id: 1) } let(:post_description) { create(:post_description, post_id: 1) } before { login_user(user) } it 'creates post' do visit posts_path click_on(I18n.t('common.button.new')) fill_in 'post[title]', with: 'テストタイトル1' fill_in 'post[post_description_attributes][description]', with: 'テスト詳細1' click_button(I18n.t('common.button.submit')) expect(page).to have_content('テストタイトル1', 'テスト詳細1') end it 'edits post' do post create(:post_description, post_id: 1) visit edit_post_path(post) fill_in 'post[title]', with: 'テストタイトル2' fill_in 'post[post_description_attributes][description]', with: 'テスト詳細2' click_button(I18n.t('common.button.submit')) expect(page).to have_content('テストタイトル2', 'テスト詳細2') end end上記のコードは二つの欠点があります。
I18n
辞書ファイルの名称と一致するかチェックしてsubmitボタンを押す処理ですが、下記の様に
I18n
を外すと、エラーになってしまいます。click_button(t('common.button.submit'))NoMethodError: undefined method `t' for #<RSpec::ExampleGroups::Post:0x00007fb64b90cac8>インターネットで調べると、stackoverflowに下記の様な回答があったので、それに沿って、
I18n
を付与すると、エラーは解消されました。Use I18n.t instead of just t.
undefined method `t' for Admin::FaqsController:Classしかし、
I18n
無しでも本来は実装出来るはずです。
その方法が見当たらなかった為、一旦この方法で実装しています。editでのpostの定義
editアクションのテストにおいて、
post_description
を新たに作成していますが、本来であれば、let
で事前に作成しているはずなので、不要です。しかし、このコードを外すとエラーになってしまいます。
Failures: 1) post edits post Failure/Error: fill_in 'post[post_description_attributes][description]', with: 'テスト詳細2' Capybara::ElementNotFound: Unable to find field "post[post_description_attributes][description]" that is not disabledこちらも原因が特定できませんでした、テストは正常にできている様なので、一旦はこの状態で実装し、後々対策を調査しようと考えています。
流石に疲れて来た
開発を進めながら、個人ブログとQiitaを両方毎日投稿するのは結構疲れます・・・
(特にQiitaは多く目に触れるので、かなり神経を擦り減らす・・・)その影響で集中力が持続せず、作業効率がかなり落ちてしまっている様に感じます。
適宜息抜きも必要だと、改めて実感しました。明日の予定
- 記事の検索機能(ransack)
- 記事へのいいね機能(別ユーザーのみ)
- 記事のストック機能(自身の記事も可)
明日までにここを完成させないと、いよいよ後が無い・・・
デスマーチで乗り切るほかあるまい・・・おまけ
最後になりますが、現在、私は下記の目標を立てて学習に取り組んでいます。
- 3年間で「10,000時間」をプログラミングに費やす
- その間、毎日ブログの投稿を行う
Twitterでは、その過程で学んだ事などを発信しています。
もし宜しければフォローしてみてください。
- 投稿日:2019-05-03T03:59:43+09:00
railsのbxsliderのaタグが効かないときの対処法
はじめに
スライドショーのgem「bxslider」を使用していた際に、スライドショー内のリンクをクリックしても遷移しなかったので、直接コードをいじってなんとか解決しました。
そのフローを残しておきたいと思います。開発環境
- windows7
- ruby 2.4.5p335 (2018-10-18 revision 65137) [x64-mingw32]
- Rails 5.2.3
- bxslider-rails 4.2.5.1
- jquery-rails 4.3.3
事象・検証状況
- スライドショー内のリンクをクリックしても遷移しない
- aタグをマウスオンすると遷移先URLが表示される
- aタグを覆う要素は存在しない
- レンダリング時のエラーなし
- 自身のコーディングに不備はない可能性が高い
- デベロッパーツールのコンソール上でもエラーなし
- JQueryでもエラーはない
- めっちゃ調べた。
- これっぽい。
カミナリ|bxSliderのスライド内のリンクが効かなくなった際の対処方
https://kaminarimagazine.com/web/2019/03/29/bxslider%E3%81%AE%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%89%E5%86%85%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F%E9%9A%9B%E3%81%AE%E5%AF%BE/解説
原因
スマホのタッチ・スワイプに対応させるためのオプションで、デフォルトが
true
である、「touchEnabled」オプションにより、マウスオン(タッチ)時にドラッグ(スワイプ)操作判定となる。つまり、clickイベントとmouseleaveイベントが発火しない。
もう少し詳しく話すと、ページをスライドさせない(ドラッグ(スワイプ)距離がないor短い)とき、クリック処理がされないような記述になっている。参考サイトの対応策
- 「mousedown」で対策する
- bxSliderのオプションの「touchEnabled」を’false’に設定する
感想
どっちもやだ。
理由
- 操作感が変わる→気持ち悪い
- スマホ上でフリックによるスライドはさせたい
解決するにいたった手順
- gemのソースから
touchEnabled
を探すinitTouch
呼び出してた。探すonTouchStart
をbindしてた。探す- いっぱい何か書いてる...。でも、
onTouchStart
というよりはonTouchEnd
みたいな関数が関係してそう。それ、bindされてた。探すdistance
計算してるし、ここだ!- 無理矢理クリックイベントを発火させる方法調べる → 発見!(参考サイト参照) → 完成!
※
onTouchStart
あたりから逐次console.log(hoge);
で呼び出されている関数を確認していました。該当コード
app_name\vendor\bundle\ruby\2.4.0\gems\bxslider-rails-4.2.5.1\vendor\assets\javascripts\jquery.bxslider.jsvar onTouchEnd = function(e) { if (slider.settings.mode === 'fade') { # # スライドショーのスタイルがfadeの時の処理 # } else { # # 横スライド(horizontal)か縦スライド(vertical)の判定処理 # if (!slider.settings.infiniteLoop && ((slider.active.index === 0 && distance > 0)|| (slider.active.last && distance < 0))) { setPositionProperty(value, 'reset', 200); } else { if (Math.abs(distance) >= slider.settings.swipeThreshold) { # # ドラッグ距離 or スワイプ距離がスライド有効距離以上の場合のスライド処理 # } else { + document.elementFromPoint(e.clientX, e.clientY).click(); setPositionProperty(value, 'reset', 200); } } } };まとめ
初心者によるChromeのアップデートに対する不具合解消ですので、かなり付け焼刃的な解決法だったのかな、と思っています...。ちなみに、今回はfade形式のスライドショーはを使う予定がなかったので、コードを変更していませんが、
# スライドショーのスタイルがfadeの時の処理
部分の類似部分を変更すればfadeでも正しく動作するはずです。参考サイト
- エラーの概要をつかみました。
カミナリ|bxSliderのスライド内のリンクが効かなくなった際の対処方
cly7796.net|スワイプする要素の中にリンクがある場合の対応方法document.elementFromPoint(x, y).click();
で任意の座標でクリックイベントを発火させることを知りました。
Stack Overflow|How to simulate a click by using x,y coordinates in JavaScript?- 座標の取得方法はここで見つけました
gaaamiiのブログ|function(e)のeってなんだ?
- 投稿日:2019-05-03T02:24:49+09:00
RailsのActiveModel::Serializerでレスポンスのキー名をキャメルケースにしたい
フロントはTypescriptを使用している場合、レスポンスで受け取るキー名がスネークケースだと、tslintでエラーを吐いてしまいますよね。
基本的にrubyはスネークケースを使用するべきで、Typescriptはキャメルケースを使用するべきなので、両者の基本が違うのがもどかしいです。
なので、Rails(サーバ)側でレスポンスのキー名をキャメルケースにしてやりたいなぁと思ってました。
jbuilderを使っている場合が多いのかな?と思ったりもしますが、私はActiveModel::Serializerを最近好んで使っているので、Serializerでキャメルケースにする便利な設定とかないかなぁと思って探してみたら、ありました。
以下をinitializerに追記するだけでした!
ActiveModelSerializers.config.key_transform = :camel_lowerどこに記述しようか悩むところですが、とりあえず
config/application.rb
に書きました。まぁ結局パラメータチェックのところとか、変更する箇所が多すぎるので断念しましたが・・・
サーバ側から渡すときはキャメルケースで、フロントから受け取るときはスネークケースで、とか出来たら便利なんですけどね〜
(探してないだけであるかもですが)
- 投稿日:2019-05-03T01:13:11+09:00
ActiveRecordで行値式で検索する方法
以下のように複数カラムをつかってINで検索することを
行値式
や行値構成子
と呼ぶそうです。SELECT * FROM table WHERE (column1, column2) IN ( (value1, value2), (value3, value4) );これをRailsで使用する方法がなさそうだったので、実現する方法を考えました。
ActiveRecord.where
はHoge.where('id = ? OR id = ?', 1, 2) => SELECT `hoges`.* FROM `hoges` WHERE (id = 1 OR id = 2)のように文字列でWHERE文を使用しつつ、プレースホルダも使えるので、これを利用します。
pluckなどで取得した値をそのまま使えるようにしたかったので、
第1引数にカラム名の配列
第2引数に値の配列
を指定してwhereに渡す値を生成するメソッドを定義します。# @param [Array] columns 検索に使用するカラム名の配列 ex. [:id, :name] # @param [Array] values 検索に使用する値の2重配列 ex. [[1, 'hoge'], [2, 'piyo']] # @return [Array] ActiveRecord.whereで使用できるWHERE句と検索値を持った配列 def row_constructor(columns, values) # 1レコード分のプレースホルダを作成 # ex. [:id, :name] => (?,?) placeholder = "(#{(['?'] * columns.size).join(',')})" # 検索数分のプレースホルダを作成 # ex. [[1, 'hoge'], [2, 'piyo']] => (?,?),(?,?) placeholders = ([placeholder] * values.size).join(',') # 行値式のWHERE句を作成 # ex. (id,name) IN ((?,?),(?,?)) sql = "(#{columns.join(',')}) IN (#{placeholders})" # ActiveRecord.whereには可変長引数で渡すので2重配列を平坦にして、先頭にクエリ文字列を追加 values.flatten.unshift(sql) end上記のメソッドを以下のように使用します。
columns = [:id, :name] values = [[1, 'hoge'], [2, 'piyo']] Hoge.where(*row_constructor(columns, values)) => SELECT `hoges`.* FROM `hoges` WHERE ((id,name) IN ((1,'hoge'),(2,'piyo')))そもそもActiveRecordにメソッドが用意されていたり、
gemがあったら教えてください。
- 投稿日:2019-05-03T00:13:12+09:00
Rails ~会員登録までの道のり~
これまでのあらすじ
前回の記事よりActionMailerの機能でメールを飛ばせることを検証しました。
今回は、メールアドレスを認証させるために仮ユーザ登録させてからユーザ登録させるための仮ユーザ登録機能を作ってみます。
ユーザ認証に関しては、deviseなどのGemの利用がRailsでは、一般的(というか便利)らしいですが、Ruby・Railsの勉強をするために純粋にRuby・Railsで構築してきたいと思います。Top画面の作成
まずトップ画面を作成します。
top_cotroller.rb
を作成します。terminal$ rails g cotroller top
top_controller.rbclass TopController < ApplicationController def index end endindexアクションを作成し、
/views/top/index.html.erb
を作成します。
config/routes.rb
にルートを追加する。rootパスにtop#indexアクションを指定します。
routes.rbRails.application.routes.draw do root 'top#index' # top_controller get 'top', to: 'top#index' endTOP画面はこんな感じにBootstrapを駆使して作りました。
(管理者とかあるのはご愛嬌で...)
背景に動画を埋め込んで「イマドキ」っぽいおしゃれなWebサイトっぽくしました。
仮ユーザのModel作成
仮ユーザ(temp_user)のModel(DBのテーブル)を作成します。
schema.rbcreate_table "temp_users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "mail_address", null: false t.string "last_name", null: false t.string "first_name", null: false t.string "token", limit: 64, null: false t.datetime "expired_at", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["mail_address"], name: "index_temp_users_on_mail_address", unique: true t.index ["token"], name: "index_temp_users_on_token", unique: true end仮ユーザ情報として、ログインに使用するmail_addressとユーザの氏名、仮ユーザを認証するためのtoken、仮ユーザ情報の有効期限のためのexpired_atで構成されています。
仮ユーザ登録までの流れ
- TOP画面のsign onボタンを押下し、仮登録画面を表示。
- 仮登録画面で氏名・メールアドレスを入力。
- メールアドレス登録完了と同時に、本登録画面のurlを記載したメール送信。
- 仮登録が完了した旨のメッセージ表示。
1. TOP画面のsign onボタンを押下し、仮登録画面を表示。
以前の記事で紹介したモーダルウィンドウを起動し、仮登録画面を表示しました。
top_controller#sign_onで表示しています。
form_with用の@temp_userをnewしている以外は特別なことはしていません。top_controller.rbclass TopController < ApplicationController def sign_on @temp_user= TempUser.new end end2. 仮登録画面で氏名・メールアドレスを入力。
temp_user.rbにバリデーションを追加しました。
temp_user.rbclass TempUser < ApplicationRecord VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :last_name, presence: true validates :first_name, presence: true validates :mail_address, presence: true validates :mail_address, format: { with: VALID_EMAIL_REGEX } end氏名は必須。
メールアドレスは必須とメールアドレス正規表現によるフォーマットチェックをしています。3. メールアドレス登録完了と同時に、本登録画面のurlを記載したメール送信。
top#sign_onでformを表示し、formのsubmitでtop#createを読んでいます。
本処理は
- mail_addressがすでに使用されている(temp_userに登録済み)の場合は、UPDATE、未使用の場合は、INSERTをする。(UPSERT処理)
- UPDATE時、入力された姓・名・有効期限は後勝ちとする。
- tokenはランダム文字列で生成。
find_or_initialize_byで条件にformで入力されたmail_addressを指定しています。
find_or_initialize_byは取得できた場合、取得結果をModelに詰め、できない場合は、ただ単純にnewします。その後、姓・名をセットします。
tokenですが、SecureRandom.urlsafe_base64でランダム文字列を作成しました。なお、重複登録を防ぐために、token = 生成文字列でtemp_userを検索し、存在した場合、再度生成しています。
有効期限はとりあえず現在日時をセットし、saveしています。
find_or_initialize_byで生成しているため、すでに存在している場合は、UPDATE・新規の場合はINSERTをrailsで判断しています。(便利ですね!!)
top_controller.rbdef create # 入力されたメールアドレスは登録済みか @temp_user = TempUser.find_or_initialize_by(mail_address: temp_users_params[:mail_address]) # 入力された姓・名をセット @temp_user.last_name = temp_users_params[:last_name] @temp_user.first_name = temp_users_params[:first_name] # トークンを生成 @temp_user.token = create_token # 有効期限に現在日時をセットする @temp_user.expired_at = DateTime.now respond_to do |format| # UPSERT実行 if @temp_user.save format.js { @status = "success" } else format.js { @status = "fail" } end end end private def create_token token = nil loop do token = SecureRandom.urlsafe_base64 break if TempUser.find_by(token: token).nil? end return token end4. 仮登録が完了した旨のメッセージ表示。
成功した場合は、完了した旨のメッセージを表示しました。
これもModalで表示していますが、formのモーダルを消してから、完了のモーダルをshowするため
遅延実行しています。create.js.erb<% if @status == 'success' %> $('#top_modal').modal('hide'); setTimeout(function(){ $('#top_modal').html('<%= j(render 'completed_sign_on') %>'); $('#top_modal').modal('show'); }, 500); <% elsif @status == 'fail' %> $('#sign_on_errors').html('<%= j(render 'layouts/error_messages', model: @temp_user) %>'); <% end %>こんな感じ...
今後は…
これで仮登録までできました。
とりあえず、前回の記事の検証と合わせて、メールを飛ばすところまではつくりたいですね。
(こうしたほうがいいとか、ご意見あったらぜひぜひいただけると嬉しいです。)