三蔵開発メモ

Web開発やインフラ関連のメモを共有します

RubyでネストしたHashをオブジェクト化する方法

Ruby2系までは、ネストした階層のハッシュをオブジェクト化するには、JSON化してOpenStructを使うのが簡単だったが、 Ruby3.0からOpenStructが非推奨になってしまったため、Structでやる必要になって少しはまったのでメモ。

response = {
 users: [
    profile: {
      {
        birthday:20010101
      }
    }
  ]  
}

こんな感じのデータをオブジェクト化する場合は、

response_obj = JSON.parse(response, object_class: OpenStruct)

こうすればオブジェクト化できて、

p response_obj.users.first.profile.birthday
# 20010101

こんな感じでアクセスできるようになっていたが、OpenStructが使えないので、Structで作る必要がある。

birthday = Struct.new(:birthday).new(20010101)
profile = Struct.new(:profile).new(birthday)
response_obj = Struct.new(:users).new([profile])

配列の場合はnewをする時に配列の状態で入れてあげれば(今回の場合は[profile])そのまま配列のオブジェクトとしてアクセスできるようになる。

Ruby On RailsでControllerからViewへのリダイレクトの際にメッセージ等レスポンスを返す方法

登録フォームなどをRailsで書いていると、登録成功、失敗それぞれのときにView側にメッセージを返したくなる時がある。 ajaxなどを使ってもできるが、flashを利用するとRailsのみで実現できる。

  • controller側の例
def create
  # 何らかの処理

  if 成功した場合
    flash[:notice] = "成功メッセージ"
    redirect_to 成功時のリダイレクト先
  else
    flash[:alert] = "失敗メッセージ or エラーメッセージ"
    redirect_to 失敗時のリダイレクト先
  end
end
  • view側の例(slim)
- if flash[:notice]
    .notification.is-success = flash[:notice]
- if flash[:alert]
    .notification.is-danger = flash[:alert]

flashをそのまま受け取って、if文で分岐し、メッセージを表示させる。

railsdoc.com

Ruby On Rails、Slimのlink_toでget以外のメソッドを送信したいとき

管理画面の改修をしており、テーブルにある行の1つを削除したいとき、
その行に削除ボタンを設置してそのまま削除処理をおこないたくなるような場面に遭遇した。

= link_to '編集', edit_user_path(user_id)

編集画面に遷移させたいなどであればrotingやcontrollerにeditメソッドなどを用意して上記のような形で良いが、
対応するcontroller側のdestroyメソッドに飛ばしてユーザー削除処理などを行いたい場合は以下のようにやる。

= link_to '削除', user_path(user_id), method: :delete, data: { confirm: '削除してよろしいですか?' }

method: :deleteを加えてあげれば意図した動きになる。dataにconfirmもつければalertで制御もできるのでより丁寧に作れる。

存在するかわからない子データを取得する場合は「&.」が便利

超基本だがよく使うのでメモ。

- users
  - id
  - name

- user_profiles
  - id
  - user_id
  - age

上記のような1対1の関係のテーブルがある場合、Railsのモデルでuserからuser_profileのageにアクセスする場合は、

user.user_profile.age

といったようにアクセスできる。
ただ、まだuser_profileが対象のuserに存在しない場合は上記のような書き方をするとエラーになってしまう。
なのでユーザー情報入力欄などで、空の場合と存在する場合両方の可能性がある場合は、

user.user_profile&.age

とすることで存在する場合はageを取得し、そうでない場合はnilを返すようにできる。
同じようなことをするtryというメソッドもあるが、こちらの方が早い。

qiita.com

Docker ComposeでElasticsearchを動かそうとしてもうまく動かなかった時にやったメモ

elasticsearchをアップデートしたらうまく動かなくなった。 結論から言うと、 environment"bootstrap.system_call_filter=false"を追加した。

  elasticsearch:
    container_name: elasticsearch
    image: appbrewadmin/elasticsearch:7.9.3
    ports:
      - 9200:9200
      - 9300:9300
    environment:
      - "bootstrap.system_call_filter=false"
    volumes:
      - ./tmp/docker/elasticsearch:/usr/share/elasticsearch/data:delegated

上記のような必要最低限の設定事項で起動してみたが、無事動作を確認できた。

Ruby2系をRuby3系にアップグレードしたらbootsnapのエラーがでた時の対応

Ruby2.7.5から3.0.4にあげたときrails sを実行時にbootsnapがエラーを出した。 Railsは6.1.5。

.rbenv/versions/3.0.4/lib/ruby/gems/3.0.0/gems/bootsnap-1.13.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:17:in `require': cannot load such file -- rexml/document (LoadError)

調べると結構キャッシュを削除すれば良いと出てくるので、tmp/cache下を削除したがだめだった。 さらに調べたら、3系からrexmlがデフォルトで入らなくなったのが原因。 Gemfileに以下を追加してbundle installしたら解決。

gem 'rexml', '~> 3.2', '>= 3.2.5'

stackoverflow.com

Railsアップグレード等でmini_racerやlibv8-nodeなどがビルドエラーになって更新できない問題の対応

Railsの6.0から6.1にアップグレードするだけでもかなりハマってしまったのでメモ。

bundlerを最新にする

これだけでも解決することがある。

BUNDLER_VERSION=2.3.9
gem install bundler -v ${BUNDLER_VERSION}
bundle _${BUNDLER_VERSION}_ install --jobs=4 --path vendor/bundle

インストールをDocker等でコード化している場合は上記のように明記しておくと管理しやすい ※ BUNDLER_VERSIONは環境変数などにしておく

gem update --systemをする

上記とセットでした方がよりよい。ビルドをしているサーバーなどで行う。

gem update --system

プラットフォームを追加

libv8_monolith.a: No such file or directory

このようなエラーがCircle CIやLinux上のJenkinsなどでビルド中に出る場合は、 bundle lock --add-platform x86_64-linux こちらのコマンドを実行して必要なgemを追加すると解決する場合がある。

blog.code-fabrik.ch

execjsとNode.jsを使う

上記でもエラーが出る場合は、mini_racerを使わない方法が楽。 要はmini_racerはJavascriptRailsで実行するためのものなので、 こちらで紹介されているように、execjsのgemを入れれば、最適なものを選んでJavascriptを実行してくれる。 blog.dnpp.org

※ リンク先にも説明があるが、Node.js以外でももちろん大丈夫。

CircleCIで行うときの注意点

Node.jsが入っていないイメージでビルド等している場合は入っているイメージを使う。

cimg/ruby:2.7.5-node

circleci.com