20190716のRailsに関する記事は15件です。

Rails で attr_accessor と validates

Migration
create_table :my_model do |t|
 t.string :domain, null: false
 t.string :category, null: false
 t.string :name, null: false
MyModel
 validates :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 を使う。

MyModel
def update_name
  self.name = 'My Nickname'
end

など。


memo: Rails 5.2.3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で attr_accessor と validates の組み合わせでエラー

Migration
create_table :my_model do |t|
 t.string :domain, null: false
 t.string :category, null: false
 t.string :name, null: false
MyModel
 validates :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 を使う。

MyModel
def update_name
  self.name = 'My Nickname'
end

など。


memo: Rails 5.2.3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Sprokets::FileNotFound couldn't find 'spree/backend/all.js'

スクリーンショット 2019-05-27 17.02.52.png

初めて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
を行ったところ、無事立ち上がりました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLite3

sqliteターミナルから抜け出すコマンド

.exit
または
ctrl + D

で抜け出し可能。

その他、抜け出し系コマンド 使ったことあるもの

:q! 保存せずに終了
:w 保存
exit

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

以下のようにコマンドを入力したら問題なくいけました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionMailerでhelperメソッドを使用する

メール機能であるActionMailerのviewテンプレートで、先ほど作成したhelperメソッド(regular_format)
を使おうとしたらエラーとなってしまった。

解決方法

ActionMailerのView内でhelperメソッドを使いたい場合は、
add_template_helper(ApplicationHelper)
を追加すれば良い。

application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  add_template_helper(ApplicationHelper)




これで無事helperメソッドregular_formatが使えるようになった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]DBのReadを、既存コード変更なしでレプリカへ向けるGemをMakaraにした話

Makaraを選んだ経緯

DBアクセスのRead/Writeを分けるGemとしてswitch_pointが有名です。しかし、既存コードを変更しなければならず、導入コストの観点から、私たちのプロジェクトでは使えませんでした。

他に有名なものとてOctopusがあります。Partial Replicationの設定をすることでデフォルトの接続先をマスターに向けることができ、既存コード変更なしで導入できます。使い方もModelにusingを付けるだけと快適です。ただ、2点問題がありました。

  • rake db:setupでコケる。どうやらdb:createdb:schema:loadを同時に行うと上手く動作しない
  • rake db:migraterake db:rollbackでレプリカにar_internal_metadataschema_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                       # primary

Makara公式のReadmeを読むと、本来の使い方は自分でProxyを作るようです。しかし、参考にしたブログではProxyクラスをprependで拡張しています。

distribute_reads実行時に:distribute_readsフラグをON。これがONならレプリカへ接続。OFFならマスターへ接続。ということをやっています。

注意すべきは、トランザクションを貼ると強制的にマスター接続になる点です。
/makara/blob/master/lib/makara/proxy.rb

def _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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:latest

debian10でMySQLを入れてくれるツワモノが現れるのを待ちます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Scenic を使って、Railsで RDB の VIEWを使う

こちらの記事を参考にRDBのVIEWを導入しました。
https://techracho.bpsinc.jp/morimorihoge/2019_06_21/76521

VIEWを使う意図

複数の似ているテーブルをまとめて検索・参照したい

という意図で調査・検討を始めました。

そもそものDB設計として、 "似て非なるものは分ける” という思想で設計を始めるとまとめて表示・検索したい時にどうするか?というところに行き当たります。
Rails ですと、STIかとなるのですが、あまりしっくり気ませんでした。サブクラスを参照すると必ず typeで絞り込むことや、アプリケーションよりDBのデータの方が長く使われるということを考えると、やはりサブクラスはテーブルを分けておきたいと考えました。
そこで RDBのUNIONを使って参照だけする方式を検討していました。同じ時期に冒頭の Techracho さんの記事を目にして、うまく当てはまりそうだったので、 Scenic でUNION を使った VIEWを作り、 ActiveRecordのモデルで参照する方法で実装をはじめました。

この記事では、上記実装に向けてまず簡単に Scenicで VIEWを作るところのみの説明をします。

Scenicの導入

gem の README を参考に作業して、すんなりできました。
以下要点だけまとめておきます。

  1. Gemfileにgem追加
  2. rails g scenic:model 参照用モデル
  3. db/views/参照用モデル_v01.sql に SELECT文を記述
  4. rake db:migrate

db/views/参照用モデル_v01.sqlの例:

SELECT
  users.id,
  users.email
FROM users

ここまでで、参照用モデルを使ってDBレコードを参照することができるようになりました。VIEWのバージョン管理もできています。
一応実行例を載せておきます。あまり意味が無い気もしますが、参考までに。

rails console

参照用モデル.first

終わり

とても簡単な導入記事になってしまいましたが、Qiitaで Scenicを扱う記事がなかったので少しで参考になれば幸いです。
次回は、具体的にアプリケーションのモデルを作成して複数のテーブルを参照できるようにします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
  def 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'}) %>

こうしてあげると色々便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
  def 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'}) %>

こうしてあげると色々便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 のスクリーンショットです。

variant.png

Rails 5 では

ActiveStorage::InvariableError が発生します。

試したソース

試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try053_accept_tiff_by_variant

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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タグを出現させるメソッド)などもある

ビューファイル内で使用するメソッドである

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オリジナルアプリ 構想、手順

作成の順番

全体に共通するUIを整える

ユーザープロフィールのページ

ユーザー情報編集ペーシ

ユーザーモデル

ポストモデル

ユーザーとポストのアソシエーション

一覧画面

ログイン ログアウト 

ポストコントローラー

削除

ハッシュタグ

フォローとフォロー解除

お気に入り機能

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む