re:dashがインストールできなくなったので調査した

7/3 22:30追記

re:dash開発者の @arikfr さんがこの記事を読んでくれたらしく。setuptoolsが修正されるまではこの記事で紹介したワークアラウンドを入れることになったようです。

github.com

github.com

なので、これからインストールする方にはこの記事は不要なのですが、記事は自分の活動記録としてそのまま残しておきます。

まえがき

Ubuntu用のセットアップスクリプトでre:dashをインストールしようとしたらインストールできなくなってたので調べました。

使用した環境はVagrantで起動したUbuntu14.04で、Vagrant Boxはubuntu/trusty64 (virtualbox, 20160627.0.0), re:dashのバージョンは0.10.1.b1834です。

検証に使用したVagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.network "forwarded_port", guest: 80, host: 9001

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end

  config.vm.provision "shell", inline: <<-SHELL
    sudo locale-gen ja_JP.UTF-8
  SHELL
end

セットアップスクリプト実行中のエラー

$ curl https://raw.githubusercontent.com/getredash/redash/master/setup/ubuntu/bootstrap.sh
$ sudo bash bootstrap.sh

VMにログインし上記の様にスクリプトを実行したところ、途中は割愛しますがre:dashが依存しているモジュールを pip でインストールするあたりでこんなエラーが出てしまいました。

Downloading/unpacking httplib2==0.9.2 (from -r requirements.txt (line 1))
  Downloading httplib2-0.9.2.zip (210kB): 210kB downloaded
  Running setup.py (path:/tmp/pip_build_root/httplib2/setup.py) egg_info for package httplib2
    Traceback (most recent call last):
      File "<string>", line 3, in <module>
      File "/usr/local/lib/python2.7/dist-packages/setuptools/__init__.py", line 14, in <module>
        from setuptools.extension import Extension
      File "/usr/local/lib/python2.7/dist-packages/setuptools/extension.py", line 11, in <module>
        from . import msvc
      File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 244, in <module>
        class PlatformInfo:
      File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 253, in PlatformInfo
        current_cpu = safe_env['processor_architecture'].lower()
      File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__
        raise KeyError(key)
    KeyError: 'processor_architecture'
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 3, in <module>

  File "/usr/local/lib/python2.7/dist-packages/setuptools/__init__.py", line 14, in <module>

    from setuptools.extension import Extension

  File "/usr/local/lib/python2.7/dist-packages/setuptools/extension.py", line 11, in <module>

    from . import msvc

  File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 244, in <module>

    class PlatformInfo:

  File "/usr/local/lib/python2.7/dist-packages/setuptools/msvc.py", line 253, in PlatformInfo

    current_cpu = safe_env['processor_architecture'].lower()

  File "/usr/lib/python2.7/UserDict.py", line 23, in __getitem__

    raise KeyError(key)

KeyError: 'processor_architecture'

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /tmp/pip_build_root/httplib2
Storing debug log for failure in /root/.pip/pip.log

re:dashのセットアップスクリプトでいうと以下の辺りです。

原因を考える

ちょっと前に使った時は問題なかったのですが、なぜエラーが発生するようになってしまったのでしょうか。

いろいろ調べて回ったのですが、同様の事例や解決策を見つけられなかったのでsetuptoolsリポジトリを見てみました。

github.com

リリースを見てみると、最近リリースがあったようです(2016/7/03 10時現在では8時間前)

先ほどのエラーメッセージに/usr/local/lib/python2.7/dist-packages/setuptools/msvc.pyとあったので、該当のファイルが追加されたPull Requestをみてみます。

github.com

たぶんこのあたりでなんかあるんだろうなと思ったので、セットアップスクリプトを以下のように変えて再度実行します。

@@ -29,7 +29,7 @@
 apt-get -y update
 apt-get -y dist-upgrade
 apt-get install -y python-pip python-dev nginx curl build-essential pwgen
-pip install -U setuptools
+pip install -U setuptools==23.1.0

 # redash user
 # TODO: check user doesn't exist yet?

再実行

再実行時は念のためVMを破棄し、再度作成しなおしたVM上でスクリプトを実行したところインストールが問題なく完了し、 http://localhost:9001/ にアクセスすることが出来ました。

まとめ

re:dashは頻繁にアップデートされるため、たまにインストールがうまくいかないことがある印象があるのですが、今回はsetuptoolsの問題でした。

おそらくsetuptoolsもそのうち修正されるんじゃないかと思いますが、re:dashを使おうとして同じような問題にぶつかった方がこの記事を参考にしていただけたらうれしいです。

iOS9のWebView内でTwitterのシェアボタンをクリックしたらiOS標準のダイアログで投稿したい

まえがき

同僚さんがタイトルに書いてあるようなことをやりたいけどできないと言っていたのでちょっと調べてみました。

やってみた

正しいやり方かどうかは別として、こんなやり方になりました。

import UIKit
import Social

class ViewController: UIViewController, UIWebViewDelegate {
    @IBOutlet weak var webView: UIWebView!
    
    let twitterIntentUrl = "https://twitter.com/intent/tweet"
    let initialUrlString = "http://ariarijp.hatenablog.com"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = NSURL(string : initialUrlString)
        let urlRequest = NSURLRequest(URL: url!)
        
        webView.delegate = self;
        
        // WebViewの初期表示
        webView.loadRequest(urlRequest)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        let absUrl = request.URL?.absoluteString
        
        // リクエストされたURLがTwitterのシェアボタンっぽいものだったら、Social.frameworkでダイアログを表示する
        if absUrl!.containsString(twitterIntentUrl) {
            // アカウントの設定が済んでいれば投稿ダイアログ、設定が済んでいなかったらアラートを表示する
            // 参考: http://www.brianjcoleman.com/tutorial-share-facebook-twitter-swift/
            if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
                // URLからシェアに使うテキスト、URLを取り出す
                let tweetUrl = getQueryStringParameter(request.URL?.absoluteString, param: "url")
                let tweetText = getQueryStringParameter(request.URL?.absoluteString, param: "text")
                
                let twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
                twitterSheet.setInitialText("\(tweetText!) \(tweetUrl!)")
                self.presentViewController(twitterSheet, animated: true, completion: nil)
            } else {
                let alert = UIAlertController(title: "アカウントエラー", message: "Twitterにログインしてください", preferredStyle: UIAlertControllerStyle.Alert)
                alert.addAction(UIAlertAction(title: "閉じる", style: UIAlertActionStyle.Default, handler: nil))
                self.presentViewController(alert, animated: true, completion: nil)
            }
            
            return false
        }
        
        return true
    }
    
    // URLから指定したパラメータを文字列として取り出す
    // 参考: https://gist.github.com/gillesdemey/509bb8a1a8c576ea215a#gistcomment-1483938
    func getQueryStringParameter(url: String?, param: String) -> String? {
        if let url = url, urlComponents = NSURLComponents(string: url), queryItems = (urlComponents.queryItems) {
            return queryItems.filter({ (item) in item.name == param }).first?.value!
        }
        return nil
    }
}

StoryboardでwebViewを配置して、このViewControllerにOutletを追加してあるのが前提です。

また、はてなブログのURLで動作確認してますが、HTTPSでは無いのでATSの設定をいじったりしてます。

動作確認

動かしてみるとこんな感じです。

Twitterにログインしていない状態でアプリ内WebViewでシェアボタンをタップ

f:id:ariarijp:20160621233518p:plain

Twitterにログインした状態でアプリ内WebViewでシェアボタンをタップ

f:id:ariarijp:20160621233543p:plain

今回のような実装を入れなかった場合(デフォルトの挙動

f:id:ariarijp:20160621233636p:plain

まとめ

雑な感じで「できないことはないでしょう」と言った手前ムキになってやった感がありますが、思ったようなことはできたのでよかったです。

もっと効率が良いやりかたがあれば、Twitterなんかでぜひ教えて下さい。

会社の半期評価でもらったフィードバックを読み返す

うちの会社は1年に2回評価のタイミングがあって、今日は上期の評価フィードバックがあった。

評価制度として、よくある360度評価の仕組みを取り入れられているんだけど、今回のフィードバックは今まで以上に突き刺さるものが多かった。 本当はフィードバックをそのまま転載したいぐらいだけど、自分なりに噛み砕いて、そのいくつかについて考えてみる。

話が長くなる、しゃべるのが早い

  • 話が長くてしゃべるのが早いってかなりコミュニケーションとしては致命的な感じ。これは昔からこんな感じだけど、いい加減なんとかしたい
  • 話が長いっていうのは言葉を発する前の前処理の質の問題だろうか。落とし所を考えずにしゃべりはじめるというのがある気がする
  • しゃべるのが早いっていうのは、「自分はこんなに考えてる」っていうのを無意識にアピールしようとしているような気もする
  • 特に発言者の少ない打ち合わせでは、どうしても沈黙を埋めようとしてしまう

誰かのサポートをするのはいいけど、サポートの範囲を超えて手を動かし過ぎてる

  • まったくもって仰るとおり。「自分ならできる」という快感に酔っていることがあるのだろう
  • ある程度まではサポートしつつも、最後はその人にやりきってもらうみたいな感じにできるようにしたい
  • サポート以上に「仕事を奪われている」感を与えてしまっているとつらいので、一歩引く意識をもうちょっと強く持たないとな。と思う。

要求を実現するだけでなく、もっと事業に対してこの方向に進んでいくべき。というような思考や議論ができるようになると良い

  • 意識しているつもりだけど、外から見たらあんまりできてないということかなと思う
  • これは強めにアピールしようと思えばできそうだけど、なんかそれも違う気がする

まとめ

他にもいいのがあったけど3つぐらいにしておく。

いいフィードバックをもらえてるとは思うので。改善できるところから手をつけていきたいところ。

PHPで例外が発生したらリトライするような処理をigorw/retryで簡単に書いてみる

Web APIを相手にするようなコードを書いていると、リトライ処理は必ず現れる課題です。

先日も似たようなことがあったので、ちょっとした処理を書いて対処したのですが、これはなんかライブラリがあるんじゃないかなと思って調べてみると、Packagistでよくダウンロードされていそうなライブラリがありました。

packagist.org

Githubのスターも結構付いているので、これを試してみます。

インストール

Packagistで公開されているので、もちろんcomposerでインストールしますが、開発版のようなのでdev-masterを指定してcomposer requireします。

$ composer require igorw/retry dev-master

サンプルコード

使い方は簡単で、retry関数にリトライ回数と実行したい処理を無名関数やcallableとして渡すだけです。

動作確認用に、2回目までは例外を投げて3回目に成功するメソッドを作って確認してみます。

<?php

require __DIR__.'/vendor/autoload.php';

use function igorw\retry;

class A
{
    private static $retries = 0;

    public static function hello()
    {
        if (self::$retries < 2) {
            ++self::$retries;
            echo 'retry'.PHP_EOL;
            throw new \Exception();
        }

        echo 'hello'.PHP_EOL;
    }
}

retry(2, function () {
    A::hello();
});

これを実行すると、以下のような結果になります。

$ php example.php
retry
retry
hello

2回までリトライするようになっているので、初回の実行と1回目のリトライでは例外が発生していますが、2回目のリトライ(通算3回目の実行)では処理が正常に終了していますので、想定通りの動作となっています。

また、指定された回数リトライしても例外が発生するとigorw\FailingTooHardExceptionが発生するようになっています。

ソースを読んでみる

すごくシンプルな処理なので、ソースを追うのも楽です。

retry/retry.php at master · igorw/retry · GitHub

無限ループにして指定回数を超えたらbreakするような実装を想像していましたが、まさかのgotoでした。こういう使い方もあるんですね。

このライブラリでできないこと

単純にリトライするだけなら、これを使えばとても簡単にリトライ処理が書けるような気がしますが、WebAPI相手だとExponential Backoffのようにリトライ時にスリープを入れたり、特定の例外だった場合のみリトライするようなことはできません。

リトライ時の処理をもっと柔軟に定義したい場合は、以下のライブラリもよさそうです。

github.com

この記事では紹介のみにとどめますが、このライブラリも試してみたいと思っています。

TrelloのCLIクライアントをGoで書いた

影響されやすい性格なので、Rebuildを聞いて形骸化しかけていたTODOリストの運用を再開しました。

rebuild.fm

最近までTodoistを使っていたのですが、ちょっと気分転換もしたかったので、以前使っていたTrelloに出戻りしました。

Todoistと違ってTrelloは公式のMac版クライアントがないようなので、主にブラウザから使っているのですが、ちょっとタスクのリストを確認したいときに億劫になってしまうので、ターミナル上で実行可能なCLIクライアントを書いてみました。

github.com

「trello cli」で検索すると、すでにいくつかTrelloのCLIクライアントが公開されています。

github.com

github.com

すでにあるものをなぜ作ったかという話ですが、GWに時間があったので書いてみたくなった。というのが主なモチベーションです。

Goで書いた理由は単純にGoを使いたかったというのもありますが、Githubに公開してしまえばgo getで気軽にインストールできるというのが大きな要因です。

また、すでにGoのTrello APIクライアントがあったので、それを利用できたというのもあります。

github.com

実のところ、実装の大部分はこのライブラリのサンプルコードとほとんど変わりません。

使い方

インストール

go getを使います。

$ go get github.com/ariarijp/trellocli

インストール後の設定

~/trelloclirc に必要な設定を書きます。rcという名前付けが的確だったかについては微妙に自信がありません。

app_key = "YOUR_TRELLO_APP_KEY"
token = "YOUR_TRELLO_TOKEN"
username = "YOUR_TRELLO_USERNAME"

Trelloのアプリケーションキー、アクセストークンなどは、以下のページあたりから発行できると思います。

Trello Developers

実行

設定ファイルを作ったら実行してみます

$ trellocli
# ボード名 (ボードのURL)

* リスト名
  * カードのタイトル
  * カードのタイトル
  * カードのタイトル
  * カードのタイトル
  * カードのタイトル
* リスト名
  * カードのタイトル
  * カードのタイトル

こんな感じで、微妙にMarkdownっぽい形式でタスクリストが表示されます。

また、作業途中ではありますが、ラベルの色にあわせて表示色が変わるようにもなっています。

(実際に使っているタスクリストなので、ほぼほぼモザイクになっています。見難いですがご容赦ください

f:id:ariarijp:20160510231826p:plain

作ってみた感想

Markdownっぽい表示はQiita:teamでの日報で使い回すためだとか、ラベルにあわせて色分けするとか、自分の業務を楽にするためにだけ書いたので短いコードながら楽しく書けました。

かなり雑なコードですが、go getしたいがためにGithubにpushしたので、気になるところはご指摘いただきたかったりもします。

まだ使い始めて2日程度ですが、ターミナルから出なくてもちょっとしたタスクの確認ができるのは悪くない感じです。

まとめ

そういえばRebuildで「〇〇ってツールを便利に使うために△△しました」とか言うのってなんか恥ずかしい。。みたいなことを聞いた気がする。。と思いながらこの記事を書きました。