Hashのキーをリネームしたかったけどデフォルトのメソッドはないようなので書いた
class Hash def rename_key(old:, new:) return unless has_key?(old) return if has_key?(new) self[new] = self.delete(old) self end end 使い方 h = {a: 1, b: 2} { :a => 1, :b => 2 } [92] pry(main)> h.rename_key(old: :a, new: :c) { :b => 2, :c => 1 } 変更前のキーが存在しない時と、変更後のキーが存在する時は何もしない。
実際の入れ替え処理はself[new] = self.delete(old)だけで終わってます。
参考 http://kmckelvin.com/blog/2014/06/renaming-items-in-a-ruby-hash
rake task で長時間起動するため排他処理をしておかないと複数起動してしまう物があったので多重起動を禁止にしました。
namespace :foo do task :update_all => :environment do |task| process_count = `ps -ef | grep #{task.name} | grep -v grep | wc -l`.to_i next if process_count > 1 end end でOK。呼び出し部分をメソッド化とかしたかったけど、上手くいかなかったのでとりあえずこれで。タスクのブロック引数としてtaskをちゃんと書いておかないとtask.nameが使えないので注意しましょう。
参考 [ShellScript – Rakeタスク実行時、プロセス起動数を確認して排他制御する – Qiita][1]
task :do_something do return if some_condition? do_job end というように条件に合致しない時に returnを使って処理を抜けようとするとこんなエラーになります。
LocalJumpError: unexpected return これは rake task がメソッドではなくブロックだから起きるので next を使ってやりましょう。それで抜けれます。
task :do_something do next if some_condition? do_job end ちなみにnextは気持ち悪いんのでせめてbreakを使いたいと思ったんですが、それだと
LocalJumpError: break from proc-closure になってしまいます。
参考 [ruby – How do I return early from a rake task? – Stack Overflow][1]
やり過ぎはダメだと言われても、やっぱり基底クラスの拡張は楽しい!
class Array def duplicated select{ |v| count(v) > 1}.uniq end end 使い方 a = [3, 5, 3, 4] => [3, 5, 3, 4] [15] pry(main)> a.duplicated => [3] [16] pry(main)> a = [3, 5, 3, 4, 5] => [3, 5, 3, 4, 5] [17] pry(main)> a.duplicated => [3, 5] 参考 [Ruby: How to find and return a duplicate value in array? – Stack Overflow][1]
日付の配列がある時に
pry(main)> dates = (1..10).map{|i| Date.new(2015,i) } => [Thu, 01 Jan 2015, Sun, 01 Feb 2015, Sun, 01 Mar 2015, Wed, 01 Apr 2015, Fri, 01 May 2015, Mon, 01 Jun 2015, Wed, 01 Jul 2015, Sat, 01 Aug 2015, Tue, 01 Sep 2015, Thu, 01 Oct 2015] 現在の日付よりも新しく、かつ一番近い日付を選択したい時の処理
pry(main)> Date.today => Fri, 13 Feb 2015 pry(main)> dates.find{|date| date > Date.today} => Sun, 01 Mar 2015 findメソッドはやっぱり便利!
Railsで既にindexをつけているカラムにunique制限をもうけるには、ユニーク制限だけを付加することができないので一旦インデックス自体を削除してから新規に unique な index を追加します。
def change remove_index :editabilities, [:user_id, :list_id] add_index :editabilities, [:user_id, :list_id], unique: true end 参考 [ruby on rails – How to add unique constraint to already existing index by migration – Stack Overflow][1]
Rubyの命名規則としてクラス名は通常はCamelCaseで記述しますが、HTTPなどの頭字語(Acronym)はそのまますべて大文字で記述します。
Railsで頭字語のクラス名を使うときはモデル等を生成する前に設定で以下の記述を行っておくと自動的に認識してくれます。
config/initializers/inflections.rb ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'GNU' end ただhas_manyでこのクラスを持とうとすると以下のエラーになってしまいます。
LoadError: Unable to autoload constant Gnu, expected
/Users/ironsand/dev/myproject/app/models/gnu.rb to define it
対策 クラス名を明示的に指定するとOKです。
has_many :gnus, class_name => "GNU"
Selenium-WebDriverとNokogiriで同じファイルを開いても違うxpathを書かないといけないとダメで少しハマったのでメモ。
違いの原因 同じHTMLを解析してもSelenium-WebDriverでChromeやFirefoxで開いた時は任意の要素であるtbodyが挿入されるために違いが発生します。
実際の例 例えば
<table> <tr> <td></td> </tr> </table> とある時に
Nokogiri の場合 require 'nokogiri' doc = Nokogiri::HTML(File.read("test.html")) doc.xpath("//table/tr") Selenium WebDriver の場合 require 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome driver.get("file:///C:/Users/ironsand/test.html") driver.find_element(:xpath, "//table/tbody/tr") 基本的な対策 大体の場合は//table//trとどちらにも対応できるように書いたらいいわけですが、汚いHTMLを解析するときに
//tr/td/font[contains(text(), 'Keyword')]/../../following-sibling::tr[3]/td
な感じで正確なタグの数が必要になることがあるのでそういう時に気をつけましょう。
参考 [ruby – How to get element that have same parent and n th different position by xpath – Stack Overflow][1]
例えば nokogiri でちゃんとデータの取得ができてるか確認するために
irbを使うために
$ irb してから
require "nokogiri"と
自分はいつもしてたんですが、めんどくさい。
解決策 $ irb -r nokogiriな感じでライブラリが読み込めます。ステキ!
こんなふうにファイルを読み込むと標準では改行が入ってしまいます。
File.open(filename,"r") do |f| f.readlines end 解決策 改行はいらないので消しちゃいましょう。
File.open(filename,"r") do |f| f.readlines.map &:chomp end