- 投稿日:2019-07-16T23:37:43+09:00
Rails で attr_accessor と validates
Migrationcreate_table :my_model do |t| t.string :domain, null: false t.string :category, null: false t.string :name, null: falseMyModelvalidates :name, presence: true, uniqueness: {scope:[:domain, :category]} attr_accessor :nameこのとき、MyModel.Create によって
ActiveRecord::NotNullViolation: SQLite3::ConstraintException: NOT NULL constraint failed:...が起こる。
attr_accessor
attr_accessor :name
を除いてあげると、通るようになる。
理由は分からないが、 attr_accessor は メソッド定義を上書きしている模様Rails API docs for Active Record
おそらく Overwriting default accessors にあるように、super する必要があるものの、attr_accessor 単体では super しないために値が保存されないのではないかと思われる。(確信なし)
https://api.rubyonrails.org/classes/ActiveRecord/Base.html
name の挙動
同一モデル内で属性(attributes)にアクセスしたい場合は、
self を使う。MyModeldef update_name self.name = 'My Nickname' endなど。
memo: Rails 5.2.3
- 投稿日:2019-07-16T23:37:43+09:00
Rails で attr_accessor と validates の組み合わせでエラー
Migrationcreate_table :my_model do |t| t.string :domain, null: false t.string :category, null: false t.string :name, null: falseMyModelvalidates :name, presence: true, uniqueness: {scope:[:domain, :category]} attr_accessor :nameこのとき、MyModel.Create によって作成を試みると
ActiveRecord::NotNullViolation: SQLite3::ConstraintException: NOT NULL constraint failed:...が起こる。
attr_accessor
attr_accessor :name
を除いてあげると、通るようになる。
明確な理由は分からないが、 attr_accessor は メソッド定義を上書きしている模様。Rails API docs for Active Record によると
おそらく Overwriting default accessors にあるように、super する必要があるものの、attr_accessor 単体では super しないために値が保存されないのではないかと思われる。(確信なし)
https://api.rubyonrails.org/classes/ActiveRecord/Base.html
どうすればいいか
attr_accessor でアクセスしない場合、name はローカル変数として扱われてしまい、同一モデル内では値が上書きされない。したがって作成・保存のときに Null となっている。
(ただしReadはできるのでややこしい)。同一モデル内で属性(attributes)にアクセス(更新・上書き)したい場合は、
self を使う。MyModeldef update_name self.name = 'My Nickname' endなど。
memo: Rails 5.2.3
- 投稿日:2019-07-16T22:51:52+09:00
Sprokets::FileNotFound couldn't find 'spree/backend/all.js'
初めて
docker-compose up —build
を行い、ページに飛んだ際にこのエラーに出くわしました。all.jsがないと言われているから、検索していると、
rails g spree:install
をすると解決するといった記事を見つけたので、以下の方法1のように実行しました。(先に結論から言うと、自分の場合は、この方法でエラーを解決することはできませんでした。)方法1(失敗した方法)
$docker-compose up -d
で、コンテナを起動し、
$docker-compose exec potepanec bash
で、コンテナ内に入り(potepanecの部分は、各自のものに変えてください)、
$bundle exec rake db:migrate:reset
$bundle exec rails g spree:install
を実行しましたが、エラーは解決する事ができませんでした。
方法2(成功した方法)
$docker rm -f $(docker ps -aq) && docker rmi -f $(docker images -q) && docker volume rm -f $(docker volume ls -q)
を行い、一度全部削除しました。この時気をつけなければいけないのは、これだとdockerに入っている他のプロジェクトも削除することになります。自分の場合は、扱っているプロジェクトがエラーを出しているものだけだったので、このコードで、一度全部削除しました。次に、
rm -f app/tmp/pids/server.pid
で、pidを削除しました。このpidを削除しないと、同じエラーをはき出すので、dockerを削除した後、pidも削除することをお勧めします。docker,pidを削除した後、
docker-compose up --build
を行ったところ、無事立ち上がりました!
- 投稿日:2019-07-16T21:55:58+09:00
SQLite3
- 投稿日:2019-07-16T20:37:50+09:00
cannot load such file -- bundler/setup (LoadError)と出てきたときの対処法
最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです!どういうエラーなのか
railsのリポジトリをクローンしてきてちゃんと動くか確かめようとした時に発生しました!
やったことはbundle installをしたら最後に以下のような感じのものが出てきました//省略 `require': cannot load such file -- bundler/setup (LoadError)どうやらbundlerのGemが欠けてみたいだったので
sudo gem install bundler以下のようにコマンドを入力したら問題なくいけました!
- 投稿日:2019-07-16T19:13:26+09:00
ActionMailerでhelperメソッドを使用する
メール機能であるActionMailerのviewテンプレートで、先ほど作成したhelperメソッド(regular_format)
を使おうとしたらエラーとなってしまった。解決方法
ActionMailerのView内でhelperメソッドを使いたい場合は、
add_template_helper(ApplicationHelper)
を追加すれば良い。application_mailer.rbclass ApplicationMailer < ActionMailer::Base add_template_helper(ApplicationHelper) ・ ・ ・これで無事helperメソッド
regular_format
が使えるようになった。
- 投稿日:2019-07-16T18:30:17+09:00
[Rails]DBのReadを、既存コード変更なしでレプリカへ向けるGemをMakaraにした話
Makaraを選んだ経緯
DBアクセスのRead/Writeを分けるGemとしてswitch_pointが有名です。しかし、既存コードを変更しなければならず、導入コストの観点から、私たちのプロジェクトでは使えませんでした。
他に有名なものとてOctopusがあります。Partial Replicationの設定をすることでデフォルトの接続先をマスターに向けることができ、既存コード変更なしで導入できます。使い方もModelに
using
を付けるだけと快適です。ただ、2点問題がありました。
rake db:setup
でコケる。どうやらdb:create
とdb:schema:load
を同時に行うと上手く動作しないrake db:migrate
とrake db:rollback
でレプリカにar_internal_metadata
とschema_migrations
のテーブルが作成される2点目の、レプリカなのに書き込まれてしまう点が致命的で、私たちのプロジェクトでは使えませんでした。
困っていた折、Scaling Readsという記事に出会い救われました。Makaraの使い方を共有します。
Makaraとは
開発元ブログより
MakaraはRead/Writeを分けるActive Recordのアダプタだ
こんな機能があります。
- 複数DBへのRead/Write分離
- スレーブ障害時のフェイルオーバー
- スレーブ接続断時の自動再接続
- マスターないしスレーブへの接続を分離しないオプション(sticky)
- Mysqlやpostgresに対応
- Middlewareを提供 for releasing stuck connections
- スレーブDBへの荷重接続(Weighted connection pooling)
以前はOctopus使ってた。エラーハンドリングやログ出力がイケてないから改善したぜ。
また、マスターの変更がすぐにレプリカに反映されないとき'RecordNotFound'エラーが出てしまう。マスターに書き込んだら読み込みもマスターからの方が安全なんだ。だから俺たちは'sticky'という概念を導入したぜ。使い方
distribute_reads
ブロックで囲むとレプリカへ接続されます。囲わないとマスターへ接続されます。distribute_reads { User.last } # replica User.first # primaryMakara公式のReadmeを読むと、本来の使い方は自分でProxyを作るようです。しかし、参考にしたブログではProxyクラスを
prepend
で拡張しています。
distribute_reads
実行時に:distribute_reads
フラグをON。これがONならレプリカへ接続。OFFならマスターへ接続。ということをやっています。注意すべきは、トランザクションを貼ると強制的にマスター接続になる点です。
/makara/blob/master/lib/makara/proxy.rbdef _appropriate_pool(method_name, args) ... elsif in_transaction? @master_pool # yay! use a slave else @slave_pool end end導入方法
GemfileにMakaraを追加
gem 'makara', '~> 0.4'
config/database.yml
をMakara設定に変更。マスターとレプリカの設定を記載。production: adapter: 'mysql2_makara' database: 'MyAppProduction' makara: blacklist_duration: 5 master_ttl: 5 master_strategy: round_robin sticky: true connections: - role: master name: primary host: master.sql.host - role: slave name: replica host: slave.sql.host
config/initializers/makara.rb
を新規作成。Makara::Context.generate
はv0.4以降廃止されたので、v0.3.10を参考にseed
を手動作成(context.rbのgenerate参照)Makara::Cache.store = :noop module DefaultToPrimary def _appropriate_pool(*args) return @master_pool unless Thread.current[:distribute_reads] super end end Makara::Proxy.send :prepend, DefaultToPrimary module DistributeReads def distribute_reads previous_value = Thread.current[:distribute_reads] begin Thread.current[:distribute_reads] = true seed = "#{Time.zone.now.to_i}#{Thread.current.object_id}#{rand(99999)}" Makara::Context.set_current(seed) yield ensure Thread.current[:distribute_reads] = previous_value end end end Object.send :include, DistributeReads
- 投稿日:2019-07-16T16:28:24+09:00
CircleCIのdb:migrateで、mysqldump: Couldn't execute 'SHOW PACKAGE STATUS WHERE Db
2019/07/15ぐらいに、CircleCIのdockerイメージが、debian9 -> debian10に上がって都合で、
mysql-client
がインストール出来なかった人がいると思います。https://yourmystar-engineer.hatenablog.jp/entry/2019/07/15/140644
私の場合、上記以外にもハマったので、備忘録として残しておきます。
事象
db:migrate
で、以下のエラーが発生しました。mysqldump: Couldn't execute 'SHOW PACKAGE STATUS WHERE Db = 'testdb'': You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PACKAGE STATUS WHERE Db = 'testdb'' at line 1 (1064) rake aborted! failed to execute: `mysqldump` Please check the output above for any errors and make sure that `mysqldump` is installed in your PATH and has proper permissions. /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/tasks/mysql_database_tasks.rb:105:in `run_cmd' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/tasks/mysql_database_tasks.rb:57:in `structure_dump' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/tasks/database_tasks.rb:228:in `structure_dump' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/railties/databases.rake:287:in `block (3 levels) in <top (required)>' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/railties/databases.rake:69:in `block (2 levels) in <top (required)>' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.0/lib/active_record/railties/databases.rake:61:in `block (2 levels) in <top (required)>' /home/circleci/fuga/vendor/bundle/ruby/2.6.0/gems/rake-12.3.2/exe/rake:27:in `<top (required)>' /usr/local/bin/bundle:23:in `load' /usr/local/bin/bundle:23:in `<main>' Tasks: TOP => db:structure:dump (See full trace by running task with --trace) Exited with code 1原因
dockerイメージでmysqlを使っていたのが原因でした。
docker: - image: circleci/ruby:2.6.3-node-browsers - image: circleci/mysql:5.7 - image: redis:4.0.9-alpine
default-mysql-client
を入れていたのですが、これは、mariadbのクライアントなので、DB側がmysqlだと、前述のエラーが出るようです。何とか、debパッケージ入れて、mysqlクライアント入れたかったのですが、ダメでした。
とりあえず、このdockerイメージをmariadbを入れて回避
- image: circleci/mariadb:latestdebian10でMySQLを入れてくれるツワモノが現れるのを待ちます。
- 投稿日:2019-07-16T16:14:39+09:00
Scenic を使って、Railsで RDB の VIEWを使う
こちらの記事を参考にRDBのVIEWを導入しました。
https://techracho.bpsinc.jp/morimorihoge/2019_06_21/76521VIEWを使う意図
複数の似ているテーブルをまとめて検索・参照したい
という意図で調査・検討を始めました。
そもそものDB設計として、 "似て非なるものは分ける” という思想で設計を始めるとまとめて表示・検索したい時にどうするか?というところに行き当たります。
Rails ですと、STIかとなるのですが、あまりしっくり気ませんでした。サブクラスを参照すると必ず typeで絞り込むことや、アプリケーションよりDBのデータの方が長く使われるということを考えると、やはりサブクラスはテーブルを分けておきたいと考えました。
そこで RDBのUNIONを使って参照だけする方式を検討していました。同じ時期に冒頭の Techracho さんの記事を目にして、うまく当てはまりそうだったので、 Scenic でUNION を使った VIEWを作り、 ActiveRecordのモデルで参照する方法で実装をはじめました。この記事では、上記実装に向けてまず簡単に Scenicで VIEWを作るところのみの説明をします。
Scenicの導入
gem の README を参考に作業して、すんなりできました。
以下要点だけまとめておきます。
- Gemfileにgem追加
rails g scenic:model 参照用モデル
db/views/参照用モデル_v01.sql
に SELECT文を記述rake db:migrate
db/views/参照用モデル_v01.sql
の例:SELECT users.id, users.email FROM usersここまでで、参照用モデルを使ってDBレコードを参照することができるようになりました。VIEWのバージョン管理もできています。
一応実行例を載せておきます。あまり意味が無い気もしますが、参考までに。
rails console
:参照用モデル.first終わり
とても簡単な導入記事になってしまいましたが、Qiitaで Scenicを扱う記事がなかったので少しで参考になれば幸いです。
次回は、具体的にアプリケーションのモデルを作成して複数のテーブルを参照できるようにします。
- 投稿日:2019-07-16T15:01:57+09:00
Transactionの中で意図的にロールバックさせたい
ActiveRecord::Base.transaction
はとっても便利です
save!メソッドなどの例外を拾ってロールバックを自動でかけてくれます
トランザクションの中でロールバックを意図的にかけたいときがあると思いますダメな例
begin ActiveRecord::Base.transaction do if xxx_method # trueかfalseを返すメソッド xxx else raise ActiveRecord::Rollback # ロールバックは実行される end end rescue e => # ここは実行されない Rails.logger.error(e) end良い例
ActiveRecord::RecordNotSaved
でもロールバックが走るため、こちらを例外として投げてあげると、
rescueでキャッチすることができるbegin ActiveRecord::Base.transaction do if xxx_method # trueかfalseを返すメソッド xxx else raise ActiveRecord::RecordNotSaved end end rescue e => # 実行される Rails.logger.error(e) end
- 投稿日:2019-07-16T14:05:51+09:00
Railsでsimple_formatとrails_autolinkを掛け合わせて使うと便利
文字列の中からリンクを自動で生成してくれるgemの
rails_autolink
と改行で表示してくれるsimple_format
を掛け合わせてみた。rails_autolinkをインストール
Gemfile.gem 'rails_autolink'これで
bundle install
を実行する。掛け合わせて使う
show.html.erb<%= auto_link(simple_format(@user.description), html: {target: '_blank'}) %>helperとしてmethod作成
method名はお好みでネーミングすれば良いのですが、
application_helper.rbdef regular_format(arg) auto_link(simple_format(arg), html: {target: '_blank'}) end
regular_format
という名前で定義してあげれば、先ほどのshow
はshow.html.erb<%= regular_format(@user.description), html: {target: '_blank'}) %>こうしてあげると色々便利です。
- 投稿日:2019-07-16T14:05:51+09:00
simple_formatとrails_autolinkを掛け合わせて使うと便利
文字列の中からリンクを自動で生成してくれるgemの
rails_autolink
と改行で表示してくれるsimple_format
を掛け合わせてみた。rails_autolinkをインストール
Gemfile.gem 'rails_autolink'これで
bundle install
を実行する。掛け合わせて使う
show.html.erb<%= auto_link(simple_format(@user.description), html: {target: '_blank'}) %>helperとしてmethod作成
method名はお好みでネーミングすれば良いのですが、
application_helper.rbdef regular_format(arg) auto_link(simple_format(arg), html: {target: '_blank'}) end
regular_format
という名前で定義してあげれば、先ほどのshow
はshow.html.erb<%= regular_format(@user.description), html: {target: '_blank'}) %>こうしてあげると色々便利です。
- 投稿日:2019-07-16T12:11:51+09:00
Rails6 のちょい足しな新機能を試す53(tiff 編)
はじめに
Rails 6 に追加されそうな新機能を試す第53段。 今回は、
tiff
編です。
Rails 6 では、variant
メソッドがtiff
フォーマットの画像ファイルをサポートするようになりました。Ruby 2.6.3, Rails 6.0.0.rc1, Rails 5.2.3 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1準備
今回は、 Rails6 のちょい足しな新機能を試す15(Active Storage編) を元にして作業を進めます。
Gemfile に MiniMagick を追加する
画像を変換するために MiniMagick を使用します。
Gemfile... gem 'mini_magick' ...show ページを修正する
show ページで、
variant
メソッドを呼び出して画像ファイルをリサイズしてみます。
元の画像ファイルと、 50x50 に変換した画像を表示します。app/views/users/show.html.erb... <% if @user.avatar.attached? %> <h2>original size</h2> <%= image_tag @user.avatar %> <h2>50x50 size</h2> <%= image_tag @user.avatar.variant(resize: "50x50") %> <% end %> ...ユーザー登録して確認する
ブラウザからユーザーを登録します。
このとき画像ファイルとして tiff ファイルを選択します。
詳細画面を表示するとサイズを変換した画像が表示されます。なお、 tiff ファイルは、 IE や Safari であれば表示できますが、 Firefox や Chrome では表示できないようです。
ですが、サイズ変換した画像は、 PNG フォーマットになるので、変換した画像は、 Chrome でも表示できました。
下は、Chrome のスクリーンショットです。Rails 5 では
ActiveStorage::InvariableError
が発生します。試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try053_accept_tiff_by_variant参考情報
- 投稿日:2019-07-16T09:04:05+09:00
TECH DAY 3
勉強した項目: Ruby、Rails
時間: 11:00
内容:*学習アウトプットです。なにぶん間違いなんかもあります。気をつけますがその際はご指摘いただけますと幸いです*
【attr_accessor】
ゲッター、セッターを簡単に定義できるメソッドclass Dog attr_accessor :name, :type, :age end【リファクタリング】
コードの仕様を変えずに中身を改善することRailsの環境構築 Homebrewについて
•Homebrewは、Mac OS上でのソフトウェア管理を行うソフトウェアのこと
•インターネット上から便利なアプリケーションを簡単にインストールしたり、そのバージョン管理をすることができるrbenvとruby-buildについて
•rbenvとruby-buildは、Rubyのバージョンを管理する際に組み合わせて使う
•benvを使用することでrubyのバージョンを切り替えることできる
•バージョンを切り替える必要は、開発環境によってはRubyの特定のバージョンでなければエラーが出てしまうことがあるためパスを通すとは
•本来アプリケーションを利用するためにはそのアプリケーションの実行ファイルが存在するディレクトリまで移動するか、実行ファイルの在処を絶対パスで指定する必要がある
•しかし、設定ファイルにコードを書き設定することで、どのディレクトリからもアプリケーションを呼び出せるようになる、これを「パスを通す」というMySQL
•データを保存するデーターベースサーバーの一種
gem
•Rubyを便利に扱うためのアプリケーションの総称
【rails new コマンド】
Railsで新規アプリケーションを作成する際に使用ターミナル
$ rails new アプリケーション名 # アプリケーションを新規作成 $ rails new アプリケーション名 -オプション名 # オプションを付けてアプリケーションを作成【リクエストとレスポンス】
PCやスマートフォンを使ってウェブサイトにアクセスした時、PCからはリクエストというものが送信され、
それに対して、リクエストを受け取ったサーバーはリクエストに対して適切なレスポンスを返すことRailsでの処理はルーティング→コントローラ→ビューの流れで行われている
【ルーティング】
ルーティングには、送られてきたリクエストに対してどのコントローラのどのアクションを動かすのかを設定する【コントローラ】
コントローラはルーティングとビューをつなぐ役割
リクエストが送られてきた際にルーティングが読まれると、それに対応するコントローラが動く
コントローラでの処理が終わると、そのコントローラから指定したビューが読みこまれる*コントローラはルーティングからビューへと処理を受け渡す役割
【モデル•ルーティング•コントローラー•ビュー】
•モデルは、アプリケーションとデータベースを繋げる
•ビューはブラウザに返すファイル
•ルーティングはリクエストに応じて呼び出すアクションを指定する【rails s コマン】
rails s コマンドはアプリケーションを動かすためのテストサーバーを起動するためのもの
rails sのsとはserverを省略したもの【 control c コマンド】
rails s コマンドで立ち上げたサーバーは
「control + c」(controlボタンとcを同時押し)で終了できる【テーブル】
表形式の収納場所【レコードとカラム】
テーブルの•横1行のことをレコード•縦1列のことをカラム【モデル】
Railsの中でデータベースへのアクセスをはじめとする情報のやりとりに関する処理を担当【モデルの命名規則】
種類 概要 名前例 モデルクラス名 先頭は大文字、単数形 Tweet モデルクラスのファイル名 先頭は小文字、単数形 tweet.rb テーブル名 先頭は小文字、複数形 tweets 【マイグレーションファイル】
マイグレーションファイルは、テーブルの設計図である。
マイグレーションファイルにどんなカラムを持つテーブルにするかを書き込み、実行することでテーブルが作成できる。
【ActiveRecord】
ActiveRecord(アクティブレコード)はRubyのGemの一つで、
このGemはモデルとテーブルをつなぎ合わせることで、Railsからテーブルのレコードにアクセスできるようなる【allメソッド】
allメソッドはApplicationRecordを継承したモデルと結びつくテーブルのレコードを全て取得できる
コンソール[1] pry(main)> Tweet.all【newメソッド・saveメソッド】
newメソッドはクラスのインスタンスを生成するメソッド
saveメソッドを実行するとテーブルに保存されるコンソール
[1] pry(main)> tweet = Tweet.new(name: "takamasa", text: "頑張ります") [2] pry(main)> tweet.save【ヘルパーメソッド】
Railsでは主にviewでHTMLタグを出現させたりテキストを加工するために予めメソッドが用意してある
他には、form_tag(フォームを出現させるメソッド)やlink_to(aタグを出現させるメソッド)などもあるビューファイル内で使用するメソッドである