それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

rubyのLoggerでログを出力する方法

ruby のロガーについて調べておきました

ruby ではログを出すときに Logger が便利です。

require 'logger'

logger = Logger.new(STDOUT)

標準出力に出す場合は STDOUT($stdout) 、標準エラー出力に出す場合は STDERR($strerr) を指定する。

ログを出す

logger をインスタンス化したら、ログを書き出す。

logger.info('Hello Log')
# => I, [2017-06-07T13:53:15.178750 #20342]  INFO -- : Hello Log

ログの出し方:ログレベルに応じたログを書く

log()のような関数はなく、ログレベルに応じたメソッドを使う。

ログレベルは、 debug, info, warn, error, fatal, unknown があり、それぞれが、メソッド名になっている。

logger.debug('メッセージ')
logger.info('メッセージ')
logger.warn('メッセージ')
logger.fatal('メッセージ')
logger.unknown('メッセージ')

まとめて実行してみる。 Object.send(:method_name, args )インスタンスメソッドを呼び出せるのでそれを利用する。

[:debug,:info,:warn,:error,:fatal,:unknown].each{|e| logger.send(e,"message at level #{e}") }

実行結果

>> [:debug,:info,:warn,:error,:fatal,:unknown].each{|e| logger.send(e,"message at level #{e}") }
D, [2017-06-07T13:55:36.802479 #20342] DEBUG -- : message at level debug
I, [2017-06-07T13:55:36.802532 #20342]  INFO -- : message at level info
W, [2017-06-07T13:55:36.802549 #20342]  WARN -- : message at level warn
E, [2017-06-07T13:55:36.802562 #20342] ERROR -- : message at level error
F, [2017-06-07T13:55:36.802574 #20342] FATAL -- : message at level fatal
A, [2017-06-07T13:55:36.802585 #20342]   ANY -- : message at level unknown

ログのレベル

ログのレベルは次のようになっていて、debug が一番弱い。

  1. debug
  2. info
  3. warn
  4. error
  5. fatal
  6. unknown

一番弱いのはdebug。 弱いとは、設定でログ書き出しを無視することが出来る。

LOG_LEVELが info なら debug は書き出されない。

ログレベルに応じてフィルタをする。

ログにはレベルがあって debug, info, warn, error, fatal, unknown の順になっている。

すべてのログを出力する(デフォルト)

デフォルトではすべてのログを出力する Logger::DEBUG(=0) になっていて、ログを出すとすべてが書かれる

require 'logger'
logger = Logger.new(STDOUT)
logger.level == Logger::DEBUG #=> true

レベルを INFO にする

INFOにすると、DEBUGのメッセージが無視される。

logger.level = Logger::INFO
[:debug,:info,:warn,:error,:fatal,:unknown].each{|e| logger.send(e,"message at level #{e}") }

実行結果( debug のメッセージは捨てられる)

I, [2017-06-07T14:01:51.257465 #20342]  INFO -- : message at level info 
W, [2017-06-07T14:01:51.257579 #20342]  WARN -- : message at level warn
E, [2017-06-07T14:01:51.257608 #20342] ERROR -- : message at level error
F, [2017-06-07T14:01:51.257712 #20342] FATAL -- : message at level fatal
A, [2017-06-07T14:01:51.257743 #20342]   ANY -- : message at level unknown

長めのメッセージを出力する

ブロックで渡せる

logger.info {  "現在のログレベルは " + logger.level.to_s + "です" }
#=> I, [2017-06-07T14:05:42.599811 #20342]  INFO -- : 現在のログレベルは 1です

時刻は別にいらないんだけど、短く出来る?

できる。

開発中とか時刻は別にいらないんだけどってとき

logger.datetime_format = '%Y-%m-%d %H:%M:%S'
logger.datetime_format = ''
logger.info("サンプル")
#=>I, [#20342]  INFO -- : サンプル

番号とか記号も決めたい

proc を渡せばO.K.

logger.formatter = proc do |severity, datetime, progname, msg|
   ">>>>>> #{msg}\n"
end
logger.info("サンプル")
# =>  >>>>>> サンプル

書式に意味有るの?

ある。ログ書式を元に、複数のサーバーからエラーログを集めたり、ログに基づいた通知をしたりする。

参考資料

https://docs.ruby-lang.org/en/2.0.0/Logger.html#class-Logger-label-How+to+close+a+logger

QRコードアプリはLINEのQRリーダーが一番手軽。( ios 10 まで

LINEのQRコードリーダが一番手軽って話。

QRコードのためだけに専用アプリを入れるのもめんどくさい。どれを選んだら良いか考えるのもめんどくさくないですか?

LINEについてるQRリーダで十分

QRリーダは、別にLINEだけの規格ではないので、一般的なサイトを開くQRコードであれば大抵は読めます

http://https:// もならほとんどが大丈夫です。

起動も簡単

起動もとても簡単です 3DタッチでPeekメニューを出せばいいので、LINEアイコンを押し込むだけです。ね?簡単でしょ?

f:id:takuya_1st:20170605174026p:plain

2017-06-20 追記

iOS 11 にQRリーダ搭載されたので、あと半年の命ですかね。3Dタッチ対応してるんだろうか。

ruby で先月の月末を求める

先月の月末を求めるには

まず、月初の01日を作ります。

Date.parse(Date.today.strftime('%Y-%m-01')).to_s

今月の1日を作る方法を知ってると後はなんでも応用が効くよね。

次に−1日する

(Date.parse(Date.today.strftime('%Y-%m-01'))-1).to_s

簡単でしょ?

strftime と parseが賢いので頼れば良いんだと思います。

ツイートを常にSafariで開く(Twitterアプリに移動してしまう)またはその逆

ツイートリンクをクリックすると→ツイッターアプリが起動してしまう

この現象は、よく質問を受けます。(とくにiOS初心者の方々に)

Safariでクリックしたら Facebookアプリが開く?

常にFacebook アプリが開くのを当たり前と思ってる方々が、Safariで開いちゃうんだけど・・・という逆の質問もよく受けます。

変更方法→長押しメニューを出す。

リンクを長押しすると、メニューが出てきます。

このデフォルト動作を変更するには、長押しで開くメニューにすべての選択肢が出てきます。

この選択肢の意味は次のとおりです。

https://twitter.com 」を開く方法はどれが好き?

「どれが好きか」「選んでね」の質問と思いましょう。次回からの twitter.com のタッチ動作は「以前の選択」と同じ行動をiPhoneは繰り返します。

f:id:takuya_1st:20170605173706p:plain

ポイント:iOSがリンク選択を記憶しています。

リンクを「Safariで開いたか・Twitter.appで開いたか」このどちらの選択をしたのか。iPhoneはこれを記憶しています。

そのため、ツイッタのアプリ選択したら、そこから先はずっとアプリで開くようになります。

逆に、Safariの「新規タブで開く」なら、そこから先はずっと、サファリで開くようになります。

フェイスブックやインスタなど、 ツイッタ以外でも同じです。

InstagramFacebook 、 Pinterst 、Tumblrなどその他のサイトでも同じです。常にSafariで開くようにすることもできれば、常にアプリで開くようにする事も出来ます。

たとえば、アプリで開くと無駄な通信を行ったり、通知されちゃったり、起動するたびに更新されて最後見てた箇所が喪失しちゃうなど不便があるので個人的にはSafariで閲覧する方法をおすすめします。パケット節約にも有利です。

最後に選択した項目が重要

この「開く・Open」の項目で選択した動作が重要です。本当に重要です。

デフォルトの動作を決定しておけば、アプリに転送されることも減少します(ゼロになるわけではない)

Instagramを開けたときに、アドレスバーの上部からレコメンドされて、AppStoreに転送されることも減少します。

サファリでSNSを開く薦め

このテクを活用して、

わたしは、SNSSafariで開くことを「強くおすすめします」

通知が減少してイライラも減ります。

通知が圧倒的に少なくなります。通知の現象はイライラとストレスを減らしてくれます。

とくにTwitterでRT爆発を食らったときは重宝します。Facebookのどうでもいい通知を一掃できるのもメリットです。

通信量節約(パケット節約)と本体容量の節約と、バッテリ節約

本体容量の節約にもSafariで開く

TwitterFacebookTumblrInstagramはアプリごとに内蔵ブラウザをもっていて、その中に画像や動画を溜め込みます。

これらのアプリが溜め込んでいるキャッシュ量は油断してると数GB近くに達します。しかしキャッシュの削除はアプリをアンインストールしない限り出来ません。

その点、Safariで開くようにしておけば、キャッシュは出来る限り再利用されますし、キャッシュが爆発することもありません。またキャッシュだけを削除する手段も用意されています。またキャッシュはSafariが本体容量と合わせて上手に管理してくれます。

バッテリの節約

アプリは起動後10分ほど、ずっとバックグラウンドで起動しています。個人情報を収集したがるFacebookなどSNSは、はガンガンGPSやアレコレ通信をしてバッテリを摩耗させます。ハッキリ言って辛いです。

パケット節約

アプリを入れていると、バックグラウンドや起動時の通信でパケットを浪費してしまいます。起動時に何度も同じデータを取得したり、必要ないのにホーム画面を表示したり友達のログイン状況を通知してくれたりします。要らないですよね。数分でタイムラインの自動更新も行われたります。無駄です。

またアプリ・アップデートでパケットを浪費されたりします。

Safariで開くと、タイムラインを自動的に読み込みは動きますが、なんどもなんどもホームに飛ばされて再読込が実行されることがが減りました。

どうでしたか?

どうでしょう?これはTwitter以外にも使えるテクニックです。Facebookでも使えるテクニック。

アプリ側に制御を取られてしまうと、リンクやページや戻るでどのアプリを使用中かわからなくなって混乱することが多いので、Safariで開くとか決めておくと便利です。

コマンドから指定のSSIDに無線LANをつなぐ

コマンドから無線LANを接続するときにSSID指定

SSIDを指定して接続を切り替えたい

指定のSSIDに接続したい

networksetup -setairportnetwork en0 0001docomo
networksetup -setairportnetwork en0 mobilepoint password(pre-sharekey )

mac のコマンドからWiFiを切り替えられると便利。

マクドナルドきたら、マクドWiFiにつなぎたいとか、0001docomoが電波悪いなーとか、

ぱぱっと飛んでる公衆WiFiに片っ端から試すコマンドも作れたり。重宝しますね。

昔やったのにすっかり忘れて再検索してたわ

メモって大事ですね。

関連資料

mac osx で無線LANをオンオフと指定SSIDに接続するコマンド - それマグで!

chromeの起動オプションでページをHTML→PDFで保存する。

chrome だけを使って、HTMLをPDFに変換します。

chrome で pdf にサイトを保存することが可能です。Seleniumのscreenshot を作らなくても、単一なページであれば、PDFにすることが可能です

--print-to-pdfオプション

$ alias chrome='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
$ chrome   --print-to-pdf='/Users/takuya/Desktop/a.pdf' https://www.yahoo.co.jp

--headless オプションと併せて設定するととても便利です。

$ chrome  --headless --print-to-pdf='/Users/takuya/Desktop/a.pdf' https://www.yahoo.co.jp

コマンド引数

コマンドは次のようにするだけです。

chrome  --headless --print-to-pdf='path.to.file'  URL

とても簡単で、Chromeさえアレばどこでも使えるので、たとえばMarkdownー>HTMLー>PDFにするときなどに活躍しそうですね。

2017-06-27 追記

もし、それでも印刷できない場合はどうするのか?

解決策がある

幾つか解決策があります。

ひとつは、base タグの利用、もう一つが contentEditableの利用

base タグを使えば保存したHTMLでもドメインやPATHを固定できるのでそれを使うと殆どの場合解決する

それでも駄目なら、ContentEditableを有効にして、HTMLのほしい場所をクリップボードに送ってHTMLのスタイル付きで持ってくればいい。

chrome(webdriver)を headless はリサイズできないので、初期値で決めて解決させる

headless で動かしてる chrome でエラーが出る。

最初、なんのエラーかわからず悶絶してた。理由がわかったので書いておきます。

リサイズすると次のようなエラーに

driver.manage.window.resize_to(1200,1200)

エラー

Selenium::WebDriver::Error::UnknownError: unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
  (Session info: headless chrome=61.0.3114.0)
  (Driver info: chromedriver=2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b),platform=Mac OS X 10.11.6 x86_64)
from /Users/takuya/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'

なぜリサイズが必要だったのか?

とあるサイトが、レスポンシブ・デザイン(笑)になっていて、Widthにより、コンテンツの表示内容が変化する。そのためボタンがクリックできずに、 stale エラーやnot clickable で落ちてた。

headless chrome のデフォルトウインドウサイズ 800x600

Chromeのheadless は 次のようになっている。

>> driver.execute_script('return window.innerHeight;')
=> 600
>> driver.execute_script('return window.innerWidth;')
=> 800
>>

これでは、モバイルサイトや、タブレット配置にされちゃいますよね。スマホサイトは無駄にクリックと遷移がおおく、スクレイピングに向かないので不便。

初期値を投入してリサイズの代用にする。

chrome の起動オプションに「ウインドウサイズの指定」があるのでコレを活用することで問題はほとんど解決しますね。

  caps = Selenium::WebDriver::Remote::Capabilities.chrome(
    "chromeOptions" => {
      "args" => [
        "--headless", #headlessにすると動かない・・・
        "--window-size=1280,800", # width 入れると解決する。

起動オプション設定後のウインドウの高さと幅

縦横のサイズは次のようになったので、無事解決しました。

>> driver.execute_script('return window.innerHeight;')
=> 1200
>> driver.execute_script('return window.innerWidth;')
=> 800
>>

Chrome の headlessで落ちてた原因は殆どが、モバイルサイト落ち

PCサイトでデザインした、ClickやSubmitが動かない原因の大半が、モバイルサイト落ちになって発生していた。

ボタンが見つからない、Staleされる、not clickable などになる。頭のなかで、PCサイトを想定していると迷う。デバッガでみるとちゃんと要素はあるのに、クリックが出来ない。

クリック出来ない理由がわからず、悶絶して苦悩することになった。

レスポンシブデザインは気づかずに導入されてるので注意が必要だった。

どうでもいいけど。

レスポンシブデザインって、モバイル・PCでガッツリとデザインを変えるんじゃなくて、もともと目指してたのは要素を横長から縦長に再配置して、同じように見せてアクセシビリティを確保するんじゃなかったのか。

いつの間にか、ガッツリデザインをかえてメニュー項目を展開メニューに折りたたんだりしてるんだよね。そもそも使いにくいのでやめてほしい。そこまでやるなら、もう別サイト・別HTMLでデザインした方がいいよね。。。。

Selenium でモバイルエミュレーション

seleniumでモバイルエミュレーションを使いたい

ChromeのWebDriverでモバイル・エミュレーションを使えれば便利なのに。回線速度とか、画面の回転とか、画面のサイズとか、逐次セットするのは面倒なので、セットになってるモバイル・エミュレーション機能を使えば楽なのに。

と思って調べたら

出来る

caps = Selenium::WebDriver::Remote::Capabilities.chrome(
   "chromeOptions" => {
      "mobileEmulation" => {
         "deviceName" => "Google Nexus 5" }
       }
   )

Capabilitiesのオプションに渡せば出来る。これはRubyなので 設定にアクセスに階層にハッシュをつかうけど、JavaPythonならキーでアクセスですね。mobileEmulation.deviceNameがキーですね。

selenium で Google Chromeの開発者ツールにアクセスする

Selenium から 開発者ツールにアクセスしたい。

DevtoolsがChromeで閉じられちゃうので、なんとかならないのかなと思って調べてた。

パフォーマンスタブにアクセスできる

 driver.execute_script("return window.performance.getEntries();")

これでなんとかなるかもしれない。

ちなみに、Chrome+WebDriverで開発者ツールが閉じられちゃうのは理由がある。

Chromeの制限で、デバッグにアクセスできるのは常に1つだけ。がある。

そのため、webdriverでアクセスすると、いったん開発者ツールを閉じてWebdriverが実行される。排他処理が実行されてる

selenium で parital text/ link_text を指定すれば楽ができる!!!!

selenium でリンクの指定が楽になった。

ドキュメントを読んでたら、良いものを見つけた 今までのやり方。

driver.find_element(:xpath, "//a[contains(./text(), 'ログイン')]")
driver.find_element(:xpath, "//a[./text()='ログイン']")

今回知ったやり方:1

partial_link_text を指定すれば、文字列 を含むリンクを取れる

driver.find_element(:partial_link_text, "ログイン").click

今回知ったやり方:2

link_text を使えば、文字列に一致するリンクを取れる。

driver.find_elements(:link_text => "戻る").click

参考資料

selenium documentation

Evernote で選択中ノートを全て指定したノートに移動する

Evernote のノートをまとめて移動したい

Evernoteのノートが増えてくると、検索結果をまとめて別のノートに移動したいとか。そういう事が増えてくる

それも「キーボードショートカット」で!

キーボードショートカットを作るには?

Mac でキーボードショートカットを作る方法を知って無くてはならない。

Macでは⌘キーは、メニューの各項目(コマンド)を実行する、と考える。つまり、キーボードショートカットを「作る」には「メニュー項目を追加する」という作業になる。メニュー項目は「サービス」として追加できる。アプリ(:またはファイル)ごとに好きな「サービス」を追加することが出来る。

サービスの追加場所

サービスの追加場所はここになる。ここに好きなサービスを作って放り込む

~/Library/Services/

サービスの作成方法

サービスの作成方法はAutomator.appを使うのが楽でいいっすね。

Evernoteにサービスメニューを追加する。

  1. AutomatorでWorkflowを新規作成し
  2. 対象アプリをEvernoteにする。
  3. そしてやりたいことを記述。
  4. 最後に~/Library/Services に保存する。

キーボードショートカットを追加

キーボードショートカットをPreferenceで追加する。

Evernoteで選択したノートを移動させるスクリプト

Automatorで、AppleScript(language:JS)を実行すると、選択したノートを指定した場所に移動することができる。

わたしは、利便性を考慮、つまりテストのしやすさと扱いやすさを考えて、すべてのサービスをシェルコマンドで作成し、Automatorではシェルコマンドを実行するようにしている。

#!/usr/bin/osascript -l JavaScript -i
ObjC.import('stdlib')  
//コマンドライン引数を取る
function run( argv ){
  if( argv.length < 1 ){
    console.log("引数が必要")
    $.exit(1)
    } 
    move_selected_note_to(argv[0])
  $.exit(0)
}

//Evernote で選択中ノートを全て指定したノートに移動する
function move_selected_note_to( notebook_name ) {

  var app = Application("Evernote");
  var notebook = app.notebooks[notebook_name];
  var selected = app.selection();

  selected.forEach( function(e){
    e.move({to:notebook});
  } )

}

vim で .viminfo を保存反映するとyankバッファが共有されて嬉しい

vim の作業中情報は .viminfoに書かれている。

.viminfoのファイルに必要なデータが保存されている。

作業中のVimから別のVimへデータを引き継ぐには、 .viminfo を使うと実現できる。

vim から vim へ.viminfoを使うには

端末Aが作業中のvim で 端末Bがtmuxやsshでもう一つ起動したvim

  • 端末Aのvim:wvを実行
  • 端末Bのvim:rv! を実行

以下の通り

すると、端末A/Bでの作業は次のように反映されていく。

  1. 端末Aのvim:wv を実行して
  2. 端末A現在の選択状態やヤンクバッファを .viminfoに書き出す
  3. 端末Bのvim:rv! を実行して
  4. 現在の起動中のvim に.viminfoの内容をロードする

また、:wv はで.viminfoを保存しておけば

次のvim起動時に .viminfo を読み込むので、起動後に状態を反映したいときに便利。

またうっかり .viminfo を消してしまったとしても、すでに.viminfoを読み込んだvim がまだ起動していれば、 :wv で読み込み中の内容をダンプ出来る。

ipythonの入力履歴を見る

ipython の入力履歴をみたい

ipythonの入力のヒストリファイルはどこにあるのか調べたのでメモ

~/.ipython に ipython のファイルがある。

しらべたら次のところにファイルがあった *1

~/.ipython

最近のLinuxなら ~/.config にファイルを集める傾向があるので、将来的には ~/.config/ipython かもしれない

入力履歴はプロファイルにある

入力履歴を始め、ipythonは「実行環境」としての設定をプロファイルで保存してるのでここにあった。

~/.ipython/profile_default/

入力履歴を見る。

入力履歴は sqlite3 のデータベースファイルになっていたので、ここから、必要なデータを取り出せば、入力のヒストリを取得することができる。

sqlite3を使って履歴を取得

sqlite3 .ipython/profile_default/history.sqlite  -line ' select source_raw from history;'

出力履歴も残っている

出力履歴も合わせて見るなら、カラムごととりだせば見ることができる。

sqlite3 .ipython/profile_default/history.sqlite  -line ' select * from history;'

入力と出力の履歴を同時に保存して後で見るのは便利。 jupyter と同じ感じなのね

*1: 2017/05/30 現在

Xpathで隣り合う前後要素を取得/ 兄弟・姉妹要素を取る

xpath で隣り合う要素を取得するには

妹(弟)ノードを取りに行く。

id("gaika_k")/following-sibling::div

あるノードの弟ノードをとるには、あるノードを指定してから following-sibling::タグ名とすれば取ることができる。

姉(兄)ノード

姉を取りに行くなら

id("gaika_k")/preceding-sibling::div

precedingを代わりに使えば同じように、上のノードが取れる。

xpath は成れてくるとCSSより細かい指定や適当な指定ができて、xpath書きやすい。

xpath で要素をRange(範囲)指定する。( index で範囲指定 )

Xpathで要素を範囲指定するには

position() 関数と組み合わせて戦える。

//tr[ position() > 0 ]

これは指定番目を1つ取り出す次の書式の発展系と考えればわかりやすい

1番目のtrを取り出す。
//tr[ 1 ]
1番目より後ろの trを取り出す。
//tr[ position() > 1 ]

テーブルなどで指定行から先、指定列から先を取り出したいときによく使う。

//table//tr[poisition() > 3]//td[position() > 1]