P学習帳

書いておぼえるブログ

【Ruby】配列または文字列どちらも受け取れる関数を作りたい

状況  

成り行きで関数に文字列と配列の両方が入ってくるコードができあがった。配列が来たらeachで回して要素を処理すればOK、と思っていたら引数が文字列の場合にエラーが出た。両方対応できるようにしたい。

解決

instance_of?(Array)を使って配列を判定する。引数が配列のときにtrueを返す。  

コード  

引数を受けて配列なら要素べつに処理、文字列ならそいつ自身を処理して配列を返す関数にできる。

def do_stuff(chunk)
  ret = [] 
  if chunk.instance_of?(Array) 
    chunk.each do |element|
      # do something
      ret << hogehoge(element)
    end
  else
    ret = fugafuga(chunk)
  end
  return ret.present? ? ret.flatten.compact.uniq : []
end

雑感  

こういう処理は、有名なモジュールのソースコードをみればうまい書き方が学べる気がする。「こういうときはこう」というベストプラクティス的な書き方は知っておくと役に立つ。

あと、配列変数を宣言したあとでarry << ['hoge', 'fuga']とすると、

[['hoge', 'fuga']]

みたいに配列がネストするのでフラットにしてから返している。なんだけど、もっとうまいやり方ありそう。

【Ruby】配列をずっと循環させる

状況  

バッチ処理などで指定時間のあいだずっと配列をぐるぐるループしつづけていたい。  

コード

cycleをつかう。

['apple', 'banana', 'orange', 'peach', 'grape', 'pineapple'].cycle do |fruit|
  p "I like #{fruit}"
end

cycle(3)のように回転数を指定することもできる。このとき3回転する。  

参考  

わかりやすく、詳しい。

[Ruby] cycleメソッド(Array)の使い方 | qs Developers

雑感  

バッチ処理でたくさんあるデータをぐるぐる回していたい。指定の時間が来たらプロセスはいったん終了される。そしてまた再度プロセスが実行される。その繰り返しがつづく。具体的にはループさせたかったのはテーブルのカラム名だった。カラムごとにデータを処理するために、その名前を配列にして循環させたかった。わざわざ循環させたかった理由は、小刻みにでもすべてのカラムのデータを処理していきたかったからだった。

【デバッグあるある】渡しているはずの引数が存在しないことになっている

状況   

関数とかに確実に渡しているはずの引数の値が空(undefined または nil とか)だと怒られる。 呼び出し元では変数に値が入っている。原因がわからず途方にくれる。    

原因

変数に値が入っていると確認した呼び出し元のコードの後続の行のどっかで同じ変数名を空の値で上書きしている。

雑感

該当の変数に値が入っているかどうかだけに注意をうばわれて、他の可能性が目に入らなくなるという現象。

Mac OSをアップデートするとMySQLがクラッシュすることがあるらしい?

状況  

Mac OSのアップデートを実行、再起動後、MySQLが起動しなくなった。  

現象  

pidが消えた。エラーがたくさん出る。ググりまくって試行錯誤するものの解決できず。結局、MySQLのバージョンをあげたら再インストールできた。しかしバージョンをあげたことによって違う問題がでてきたりと、大変。  

雑感  

DBがポシャると開発できなくなるので大事。Dockerイメージつくっておいてクシャッたらいちから立て直すみたいなことができるようにしておきたいと思った。解決しないトラブルシューティングに何時間も費やすと心身の消耗が激しい。

入力フォームの値が以前のデータと同じだったら送信をキャンセルさせたい

状況  

何らかのデータを更新するためのフォームがあるとして、入力された値が以前と同じ状態で送信されたらリクエストをキャンセルさせたい。  

コード  

$('input[type="test"]').on('submit', function(e) {
  var prev_val = $('.prev_val');
  var new_val = $('.new_val');
   if ( prev_val == new_val ) {
     e.preventDefault();
     alert('同じ内容です。');  
  }
})

DEMO

codepen.io

雑感  

jQueryでテキストを取得する場合、pタグはtext()、inputタグはval()と使い分ける。間違えやすい。

【Ruby on Rails】capistranoによる自動デプロイ環境でタスクを定期実行させたい

状況  

何らかのバッチ処理を本番サーバーで定期実行させたい。capistranoによる自動デプロイのついでにバッチ処理の設定もしてしまいたい。  

前提  

方法  

capistrano  

capistranoの設定ファイルにwheneverの設定を追記する。   

deploy.rbなどに以下を加える。

set :whenever_command, "bundle exec whenever"
require "whenever/capistrano"

環境を指定するには  

set :environments "production"

とすることでRAILS_ENV=production ~相当のcronジョブが生成される。stagingで実行したいときは置き換えること。   複数の環境を設定する方法があるとおもうが、わからなかった。

whenever

そしてschedule.rbに定期実行したいジョブを加える。

every 1.day, :at => '00:00 am' do
   runner "SomeStuff.new.delay.awesome_procedure"
   rake "jobs:work"
 end

runner "SomeStuff.new.delay.awesome_procedure"は、delayed_jobsにキューを入れる。 次のrake "jobs:work"でキュー入りしたジョブを取り出す。  

キューするときに実行タイミングを引数で指定できる。    

runner "SomeStuff.new.delay(run_at: 1.hour.from_now).awewome_procedure"

デキューされた1時間後にawesome_procedureが実行されることになる。  

雑感

わかってしまえばシンプルに記述できるが、わかるまでが大変。

【RoR】モデルの属性を更新するフォームをつくりたい

状況  

ユーザーのフォーム入力に従ってモデルの値を更新したい。  

やり方  

  • ビューでform_forを使う。  

  • コントローラーでstrong parameterを使ってパラメーターをフィルターする。  

  • モデルの更新  

前提

更新対象のモデル:article title属性をもつ。これを更新したい。

コード  

ビュー  

= form_for @article, url: "/article/update_article_title" do |f|
  =f.text_field :title, value: @article.title

p

コントローラー  

def update_arcicle_title
   return 404 is !update_params[:title]
   article = Article.find(update_params[:id])
   if article.present?
     article.update_attributes(title: update_params[:title])
   end
   redirect_to index_path
end

private
def update_params
   params.require(:article).permit(:title)
 end

ビューから渡ってくるパラメーターについて  

ビューの

= form_for @article, url: ...

に入力&送信されたパラメーターは次のようなハッシュとなる。

article { :title => "great article" }

これをストロングパラメーターでフィルターしたい。 requirepermitで二段階にフィルターする。はじめはモデル名、次に属性名で絞り込む。

params.require(:article).permit(:title)
# => "great article"