どなブロ

Ruby, Rails, その他気になったこととか思ったこととか

【Rails】ActiveSupportのpresence

RailsActiveSupportのpresenceがええ感じです。
文字列のnilとか空白を判別して何か固定値入れたい時とか。
object.presenceって感じに書いて、objectが空だったらnilを、そうでなければobject自身を返すってやるです。

使い方とか

ドキュメントのサンプルコードを引用するとこんな感じです。
https://api.rubyonrails.org/classes/Object.html#method-i-presence

# こう書いてたのが
state   = params[:state]   if params[:state].present?
country = params[:country] if params[:country].present?
region  = state || country || 'US'

# こう書ける
region = params[:state].presence || params[:country].presence || 'US'

自分は新しくなんかのモデルのインスタンスを作る時に、
必須のカラムに対して入れるべき物が無くて別のを入れる、みたいな時に使いました。
(言葉で書くとわけわからんな・・)

例えばUserモデルがあって、name属性が必須とします。

class User
  ~~
  validates :name, presence: true
  ~~
end

それに対して、例えばどっかのSNSアカウントから情報取ってUserを作ろうとした時に、
表示名が空でアカウントIDしか無いみたいなパターンがあったとします。

そうした時にただ表示名だけ入れようとすると作れないので

# res => SNSから取った情報

puts res.name
# => nil

user = User.new(name: res.name)
user.save!
# validation通らずsaveできない

代わりにアカウントIDを入れてあげよう、みたいな時に使えます。

puts res.name
# => nil
puts res.account_id
# => "hogehoge"

user = User.new(name: res.name.presence || res.account_id)
user.save!
# OK

presence使わずに書こうとするとこんな感じですね。

user = if res.name.present?
         User.new(name: res.name)
       else
         User.new(name: res.account_id)
       end

短くなってよいかと思います〜。

中身

中身はこんな感じでした。

https://github.com/rails/rails/blob/09a2979f75c51afb797dd60261a8930f84144af8/activesupport/lib/active_support/core_ext/object/blank.rb#L45

def presence
  self if present?
end

objectが空もしくはnilか判断してtrue/false返すpresent?使って
trueならobject自身を、falseならnilを返す、という
冒頭で説明した通りの内容ですね。

【Ruby】Hashのeach.with_indexで引数2つ渡す

2つの引数を()で囲っちゃうだけなんだけど、忘れるので。

hash = {"hoge":100, "fuga":200, "piyo":300}

hash.each.with_index do |(key, val), index|
  puts "key: #{key}, val: #{val}, index: #{index}"
end

# => 
# key: hoge, val: 100, index: 0
# key: fuga, val: 200, index: 1
# key: piyo, val: 300, index: 2

()で囲ってあげない|key, val, index|みたいに書くとkeyにハッシュのkeyとvalueが、valにindexが入っちゃいます。

参考

instance method Enumerator#with_index (Ruby 2.1.0)

以上

【Ruby】複数行ある文字列内で、特定のワードの次の行にある文字列を取る

すごくニッチな感じだと思いますが。

やりたいこと

こんな感じの文章があったとして

ほげほげほげほげ

ほげえええええええええああああああ

どうもです

提供
ほげほげ株式会社

提供の次に来るワードを拾いたい…!!
という事がありました。

each_lineで1行ずつ取る

まず文字列を改行文字で分割して1行ずつ取ってくれるeach_lineを使いましょう。
each_line (String) - Rubyリファレンス

ついでに改行文字もmap(&:chomp)で消します

hoge_lines = hogehoge.each_line.map(&:chomp)

# => ["ほげほげほげほげ", "", "ほげえええええええええああああああ", "", "どうもです", "", "提供", "ほげほげ株式会社"]

eachで回しながら目的の物を取る

eachで回しながら取得するトリガーにしたい提供が来たら、
配列の次に入っている文字列(次の行の文字列)を取ります。

hoge_lines.each.with_index do |hoge, i|
  if hoge == "提供"
    puts hoge_lines[i + 1]
  end
end

# => ほげほげ株式会社

こんな感じでした。

参考

ruby get next value on each loop - Stack Overflow

今さら知ったこと Rubyで文字列を改行で分割して改行コードも除く each_line.map(&:chomp) - osamuk's blog

【Rails】rakeタスクに引数を与える & ソース上での実行

おひさです。

rakeタスクに引数を与えたくて調べました。
あとModelでちょっとタスクを実行したくてそっちも調べました。

引数の与え方

タスク引数(?)

名前が合ってるかわからんですが[]で渡すようにするやり方

namespace :hogehoge do
  task :run, ['hoge', 'fuga'] => :environment do |task, args|
    puts args.hoge
    puts args.fuga
  end
end
# 実行
$ rails hogehoge:run["ほげ", "ふが"]
ほげ
ふが

ちなみに引数指定しないとnilが入るみたいです。

環境変数

コマンドライン引数で環境変数を指定してあげる感じ

namespace :hogehoge do
  task run: :environment do
    puts ENV['hoge']
  end
end
# 実行
$ rails hogehoge:run hoge="ほげえ"
ほげ

こっちも指定しないとnilになるみたい。

ソース上での実行

あんまりよくないやり方かもしれない。

require 'rake'
Rails.application.load_tasks
Rake::Task['タスク名'].execute # もしくは.invoke
Rake::Task['タスク名'].clear

最後にclearしないと同じ箇所を通った際に再度load_tasksされてexecuteで二回実行されてしまう。
.execute.invokeでは引数の扱いに違いがあるらしい。詳しくは以下参照
rakeタスク内で別のタスクを呼び出す - Qiita

参考

Ruby on Rails | Rakeタスクに引数を渡す - Tbpgr Blog
rakeタスクに引数を与える | 毒男日記
RailsでRakeタスクをcontrollerから呼び出す時の覚書 - Qiita

【Rails】migrationでのDB更新を余計な変更を加えずにschema.rbに反映させる

migrationで何かしらの変更をDBに加える時、db:migrateするとschema.rbへ更新が反映されます。
その時にDBが何かしらの理由で実際の形と異なっているとschema.rbに余計な変更が反映されてしまいます。

自分の場合で言うと、
流れとしてはローカルに立ててあるDBに向けてmigrateをして、schema.rbに変更が加わった後にmigrationファイルと一緒にコミットする感じです。
(これが普通のやり方なのかな?わからん)
それで、ローカルのDBに他の作業とかで関係ない変更が入ってたりすると、目的のmigrationと別の変更がschema.rbに入っちゃうっちゅうわけです。

今回はそういう時に安全に目的の変更だけをschemaに反映させる方法のメモです。
DBに消しちゃいけないデータがある場合はできないと思います。
あくまで自分みたいにmigration用みたいなDBがある場合。

変更前として正しいschema.rbをpullなどしてくる

db:migrateする前であればそのままでいいんですが、migrationで変更を入れる前の段階として正しいschema.rbをmasterブランチとかから持ってきます。
このschemaファイルは目的のmigrationの直前の状態のはずです。

db:resetしてDBをschema.rbと同じ状態にする

db:resetをするとDBがschema.rbと同じ状態になります。
類似のものにdb:migrate:resetがありますが、2つの違いについては簡単に言えば

db:reset => schema.rbを参照してDBを作り直す
db:migrate:reset => DBをDROPしてイチからmigrationし直す

って感じです。
詳しくはこちらで→rails db:migrate:resetできなかったのでrails db:resetした - Qiita

目的のmigrationファイルでdb:migrateする

あとはmigrationするだけです。
やってみたらgit diffとかで差分を見れば目的の変更だけdiffがある…はず。

おわり

勢いで書いたのでクソ読みづらいですね。すみませぬ。

【Python】Jupyter Notebookの差分を見やすくする -- nbdime

自分はまだペーペーなんですが、
たまにjupyter notebookで作った機械学習とか統計用のipynbファイルをレビューする機会があります。

その時にgithubで差分見てたりしたんですが、
ipynbファイルは中身としてはjsonなのでgithubでレビューしようとするとひたすらに見づらいです。

ほんで、絶対もっと楽に見れるやつあるだろ〜と思って調べたら見つけました、nbdime

jsonをパースした上で差分を見やすく出してくれるそう

ターミナルでgit diff的に見るには

nbdiff [file1] [file2]

で以下のように見れます。

f:id:andna0410:20190314162038p:plain

さらにブラウザで差分を見るには

nbdiff-web [file1] [file2]

で以下のように

f:id:andna0410:20190314162156p:plain

ん〜いいですね

file2つ用意しないといけないのちょっとアレですが、
多分git連携したらいけるのかな?そこまでは今回やってません。

installは、anacondaいれてるので
Nbdime :: Anaconda Cloud
ここを参考に

conda install -c conda-forge nbdime

で落としてきました。

レビューしやすくなって最高です。

コードの中身はまだ半分もわからないけどね!!

参考

Jupyter Notebookの差分を明瞭に確認する事ができるpackage : nbdime – Moonshot 🚀 – Medium

【Rails】複数テーブルに跨ってincludes / joinsする(ひ孫まで)

includes

Hoge.includes(fuga: :piyo)
  • ひ孫
Hoge.includes(fuga: [piyo: :bar])

joins

Hoge.joins({:fuga => :piyo})
  • ひ孫
Hoge.joins({:fuga => {:piyo => :bar}})

参考リンク

railsで親子孫ひ孫までをincludesする - niki12260714の日記

Railsでjoinsを多段ネストする方法(親から曾孫まで) - Qiita

その他

下書きに長く置きすぎて重要な事以外何書くのか忘れた…