sou's blog

落ち着いた華やかさがあり、上品に明るく陽気なさまを表す。

なにかを成し遂げるための必須条件

なにかを成し遂げたい思ったときに成功するかどうかは、その願いの強さだと思った。

 

事業や会社の立ち上げも同じで、ビジネスモデルの素晴らしさなどは結局後から付いてくるものであって、必須条件ではないのかもしれない。どんなに綺麗なフレームワークや思考プロセスを手に入れたとしても、そこに願いの強さがないと成し遂げられない。

 

逆に、それがあれば仮にまわり道したとしてもいつか成功に近づけると思った。課題感を持ってるってレベルではダメなんだなと痛感した。どうやら、人生かけてもいいと思えるくらいの問題意識とそれに取り組む覚悟が必要そうだ。

チームにおける共通認識のジレンマ

プロジェクトを進める上で、関わる人が多くなればなるほど共通の認識を持つことが難しくなる。

 

また、大規模になれば自分の担当セクション内で関係する範囲で共通の認識を持てば良かったり、必ずしも共通の認識を持つ必要すらなくなってくる。

 

全員が全員、共通の認識が必要かどうかはプロジェクトの規模にもよると思うが今回は少人数のプロジェクトにフォーカスして考えてみる。

 

物事を前進させるために、沢山のアイデアがさまざまな形で出てくる。それは、思いつきのものもあれば、顕在化した明確な課題解決のためのアイデアかもしれない。画期的なものからちょっとしたものかもしれない。思いついたアイデアを発信する人もいればしない人もいる。今すぐ取り組むべきものもあるし、いつかやれれば良いというものもある。

 

では、それらのアイデアを汲み取り実行していくには何が必要かと言われると結局はチームの納得感だと思う。そして、それは共通の認識を持っていることが前提条件である。

 

仮に結果的に正しい事を発信していても、尖りすぎたアイデアならチームの納得感無しには進められない。即ち実現しない。

 

突拍子も無いアイデアをチームとして実現するためにはチームのみんなを納得させなければならず、そのためには共通の認識を持つ必要がある。ただし、共通の認識を持つには時間がかかる。特に、アイデアが尖っていれば尖っているほど時間がかかる。色んな根回しが必要になってくる。

 

一人きりのプロジェクトならそんな事を考えずに、すぐに行動を起こせたのに関わる人が増えるとその分共通認識を持たせるために時間がかかる。必ず正解を出す人なら何も言わずに黙って言うこと聞いておけと思うに違いない。そして成功した時にそれ見た事かと言うだろう。

 

でも果たして必ず正解を出す人はいるのだろうか。恐らくいない。いや、世の中にはいると思うが少なくとも自分の周辺にはいるはずがない。そういう人は既に成功して違う世界にいるはずだから。

 

なので、たとえ万一にも正解の主張だとしても、みんなの納得感を得られずに自分の主張だけをするのは間違っていると思うし、やりたいことがあるなら時間をかけてみんなを納得させてチームで成し遂げるか、一人で成功するかのどちらかしかないのである。

 

もっというなら、常にみんなが共通の認識を持って取り組めるようにするということに常日頃からちゃんと時間をかけるべきである。それが巡り巡ってトータルのコストを下げることにつながり実現可能性を上げるはずだから。

 

でもでも、そういうことが苦手で天才的なアイデアを発信し続けてるにも関わらず、実現に至らずに現状に甘んずる人も恐らくいるんだろうなとは思う。

 

これが、チームにおける共通認識のジレンマだと思った。

 

Amazon Athena カラム変更方法

はじめに

Amazon Athenaをデータ分析の際のクエリ発行に利用しています。

元のデータソースはS3にCSVを置いて取得してきていますが、データソースが変更されたり追加されたりしてAthenaのテーブルもそれに合わせてカラムの追加などを行う必要が出てきます。

AWSのマネジメントコンソール上には「カラム追加」「カラム削除」のような気の利いたメニューは無いので、DROP & CREATEでテーブルを作り直すのが一番早そうです。(テーブル追加や削除といったマネジメントコンソールでの操作も結局はDDLを発行しているだけなので)

手順

  1. テーブル一覧のメニューから「Generate Create Table DDL」を選択しDDLを生成 f:id:sousousore1:20180111112827p:plain
  2. DDL文を加筆し、カラムの追加や削除などを行いコピーしておく
  3. 再度テーブルメニューから「Delete Table」を選択しテーブルを削除する(自動でテーブル一覧がリロード)
  4. 2でコピーしておいたCREATEのDDLを発行しテーブルを作り直す(自動でテーブル一覧がリロード)

以上の手順でAthenaのテーブルに対してカラム変更をテーブルの作り直しという形で実現できました。

URLを構築する便利な関数

はじめに

この記事はTypeScript Advent Calendar 2017の1日目の記事です。

TypeScriptに限った話ではないですが、TypeScriptで書いていたものがあったので公開しておきます。

URL関係

urlとパラメータをくっつける処理いつも同じ関数使いまわしています。 同様にurlからパラメータを取ってくるのも同じ関数使いまわしています。

export class UrlUtils {
    public static build(url: string, params: { [key: string]: any; } = {}): string {
        const keys = Object.keys(params);
        const queryString = params
            ? keys.map((key: string) => [key, params[key]].map(encodeURIComponent).join('=')).join('&')
            : '';
        return 0 < queryString.length
            ? url + (~url.indexOf('?') ? '&' : '?') + queryString
            : url;
    }

    public static getParams<T>(): T {
        const params: T = {} as T;
        if (location.search.length) {
            const pair = location.search.substring(1).split('&');
            pair.forEach(x => {
                const kv = x.split('=');
                params[kv[0]] = kv[1];
            });
        }
        Object.keys(<T>{}).forEach(x => {
            console.log(x);
        });
        return params as T;
    }
}

個人開発で利用している言語、フレームワーク、ツールなど

はじめに

この記事は個人開発 Advent Calendar 2017の6日目の記事です。

2017年も間もなく終わりということで、仕事での環境とは別に2017年度に個人開発で良く利用していた言語やフレームワークなどを記録しておこうかと思います。 ※成果物はありません

個人開発のコンセプト

Webアプリケーション開発をターゲットとしています。 その中でのコンセプトとしては、以下です。

  • フロントエンドからサーバーサイドの開発をいかに気持ちよく書けるか
  • デプロイ、CIをスケールできる形でいかに楽できるか
  • 実用的でありながらモダンな状態
  • 出来る限り無料でお金がかからない

これらを実現するため様々ツールを利用してきましたが、最近落ち着きつつあります。

フロントエンド

VSCode

この辺は好みのところがありますが、オールインワンで開発に集中でき、手に馴染むところが好きです。

Javascript, TypeScript

規模が本当に小さければほぼvanillaで書きます。ある程度の規模になればjQueryを入れます。

  • なんだかんだjQuery最強

上記よりもっと大きい規模でクライアントにゴリゴリコードを書く必要がある場合は、TypeScriptを採用します。

  • APIとの通信とかちゃんとインタフェース越しにできると効率上がる

Webpack

クライアントのビルド環境。 ちょいちょい画面を確認する必要がある場合は、ホットリローディングの有無で効率がだいぶ変わる。

サーバーサイド

RubyMine or VSCode

Railsを書くににはRubyMineがやっぱり書きやすい。 けど、VSCodeでもある程度いける。 フロントとサーバーサイド横断して利用する場合とかはVSCodeだけとかのほうが楽だったりする。エディタ一つで完結した開発スタイルが手に馴染むような気がします。

Ruby on Rails

だいたいdeviseをつないでSNS認証から実装するケースが多いです。 そしてフロントエンドどうしようかなぁ。Turbolinks切ろうかなぁと悩むところから始まる。規模が小さければTurbolinksはONのまま開発していきます。その際のTipsとして、以下のように呼び出しをコントローラやアクションを制御するというのがわかり良いです。Assetsパイプラインでスクリプトは統合されるので、Turbolinksを採用する場合はどこから呼ばれるかというのは常に意識していないといけません。

[rails] 特定のページのみで実行するJavascript | Oh My Enter!

this.onPageload = function (controller_and_actions, callback) {
    return document.addEventListener('turbolinks:load', function () {
        var conditions = regularize(controller_and_actions);
        if (!conditions) {
            console.error('[onPageLoad] Unexpected arguments!');
            return;
        }
        return conditions.forEach(function (a_controller_and_action) {
            var ref = a_controller_and_action.split('#');
            var controller = ref[0];
            var action = ref[1];
            if (isOnpage(controller, action)) {
                return callback();
            }
        });
    });
};

var regularize = function (controller_and_actions) {
    if (typeof controller_and_actions === 'string') {
        return [controller_and_actions];
    } else if (Object.prototype.toString.call(controller_and_actions).includes('Array')) {
        return controller_and_actions;
    } else {
        return null;
    }
};

var isOnpage = function (controller, action) {
    var selector = "body[data-controller='" + controller + "']";
    if (action) {
        selector += "[data-action='" + action + "']";
    }
    return document.querySelectorAll(selector).length > 0;
};

JSから画像ファイルへアクセスしたいときなどは以下のようにしてimage_path関数経由で取得できるようにしています。

image_pathをJavaScript側から使う - Qiita

<%
images = {}
Dir.chdir("#{Rails.root}/app/assets/images/") do
    images = Dir["**/*"].inject({}) do |h,f|
      h.merge!(f => image_path(f)) unless Dir.exists? f
      h
    end
end
%>

window.image_path = function (name) {
    return <%= images.to_json %>[name];
}

その他にも

  • 豊富なgem、ドキュメント
  • 設定より規約による爆速開発
  • APIモードでクライアントとの分離も可能
  • Herokuとの相性が良い

インフラ

Heroku

個人開発においてインフラメンテほど無駄な時間はない。 git pushするだけでPaaSが環境を構築してくれる。ありがとう。

  • Pipelineでステージングでの確認、そのまま本番にボタン一つで反映が可能
  • dynoというサーバーインスタンスをスライダーで上げ下げできる。急に負荷が必要になったときにもすぐに対応が可能
  • 趣味範囲なら無料で遊べる
  • Add-onでSendGrid経由のメール、スケジューラ、FastlyCDNなども利用可能
  • すぐにメンテに入れられる
  • SSL設定がデフォルト
  • オリジナルドメインへの設定も簡単

欠点としては、regionがTokyoではないので若干ネットワークが遅いところでしょうか。そのあたりもTokyo regionがないわけではないので、必要に応じてHerokuのPrivate Spacesという機能を使えるようにする必要があるかもしれません。ただしEnterpriseアカウントしか使えないようなのでどうしたものか。

www.heroku.com

SendGrid

  • メールの開封状況などを計測できる
  • SMTPサーバーの利用だけならRailsとの連携がまじで簡単
  • Herokuとの連携
  • APIがちゃんと用意されている

PostgreSQL、Postico

DBはHerokuがRailsデプロイで標準サポートしているPostgreSQLを使っています。GUIクライアントとしてPosticoを使用しています。

AWS

個人開発ではS3くらいしかつかわない。 Herokuに直接ファイルアップロードなどはできないので、PerparClipなどを利用する場合のファイルサーバーとして使用しています。

コード管理

Bitbucket

GitHubも使うけど、publicリポジトリになんかアピールするかOSSのコントリビュートくらいにしか最近は使いません。

  • 何と言ってもPrivateリポジトリが無料
  • Pipelines機能(Herokuのものとは違う)でgit pushをhookにしてrspec実行からherokuデプロイまでを自動実行してくれる
  • Trelloとの連携

こんな感じでルートディレクトリにbitbucket-pipelines.ymlを置いておくとあとはmasterブランチにpushすると自動でテスト実行してHerokuにデプロイまでしてくれます。

image: ruby:2.4.0

pipelines:
  branches:
    master:
      - step:
          caches:
            - bundler
          script: # Modify the commands below to build your repository.
            - bundle install --path vendor/bundle
            - bundle exec rspec
            - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git HEAD -f

definitions:
  caches:
    bundler: vendor/bundle

タスク管理

Trello

  • 込み入ったことをしない(できない)ので良い
  • Bitbucketと連携している
  • やることだけをカジュアルにストックしておける

まとめ

とにかくRails + Herokuの組み合わせが個人開発では一番使いやすいです。 そんなに目新しいものはありませんが巷にたくさんあるものなんでもかんでも使うのではなく、自分の開発スタイルに馴染むものをちょこちょこ採用している感じです。 普通に気持ちよくWebアプリケーションを作るためにはということで色々試行錯誤していく中で見つけたものなので人によって感じ方はまちまちかもしれません。

Bootstrap3のpaginationでtext-algn centerが効かない問題をちゃんと調べてみる

Bootstrap3のpaginationはデフォルトでは左寄せとなっています。

            <ul class="pagination">
                <li class="privious"><a href="#">前へ</a></li>
                <li><a href="#">1</a></li>
                <li><a href="#">2</a></li>
                <li><span>…</span></li>
                <li><a href="#">4</a></li>
                <li><a href="#">5</a></li>
                <li class="next"><a href="#">次へ</a></li>
            </ul>

f:id:sousousore1:20171007133912p:plain   中央寄せにするためには、paginationクラスをつけた要素のwrapperにtext-centerのクラス追加してやれば実現できます。

        <div class="text-center">
            <ul class="pagination">
                <li class="privious"><a href="#">前へ</a></li>
                <li><a href="#">1</a></li>
                <li><a href="#">2</a></li>
                <li><span>…</span></li>
                <li><a href="#">4</a></li>
                <li><a href="#">5</a></li>
                <li class="next"><a href="#">次へ</a></li>
            </ul>
        </div>

f:id:sousousore1:20171007133957p:plain

と、解決するのは簡単ですが、そもそもなぜこういったことが必要なのか、ちゃんと向き合ってみました。

通常、li要素はinline要素なので、ulにtext-align:center;をつけるとul要素の幅に合わせて中央に配置することが可能です。(liの左側についているline-styleはulの幅に合わせて左側に配置されるがその場合はline-style-positionなどをいじる)

ただ、paginationクラスを付けたul要素に単純に text-align:center; を付けてもうまく中央寄せすることはできません。これは、pagination>li>afloat:leftが付いているのでli要素には実質中身がない状態になっているためです。

と、inline要素でもfloatなどで中身がない状態になってしまうとtext-align:centerが効かないことがあるということですね。

今回のBootstrapのケースではpaginationクラスがinline-blockであることから外側からtext-centerを付けて中央寄せすると簡単に中央寄せできるというアプローチでした。

Herokuを眠らせない方法

概要

Google Apps Scriptを利用して30分おきにアクセスを発生させてHerokuを眠らせないようにしています。 複数のサイトを考慮し、またスリープ時間も加味しております。

トリガーについて

アイコンバーの時計のようなアイコンをクリックすると、「トリガーが設定されていません。今すぐ追加するにはここをクリックしてください。」 と表示されると思うのでそちらをクリック。 ”時タイマー”を”分タイマー”にし”30分ごと”を選択して保存をクリックします。

設定するスクリプト

// 起こすサイトの情報
// ※日曜が0、土曜日が6。
var sites =[{
  url       : "https://hogehoge.herokuapp.com",
  wakeupHour: 10,
  sleepHour : 19,
  activeDayOfWeek: [0,1,2,3,4,5,6]
}];

// 現在の日の指定した時間、分、秒を取得します。
function createCurrent(hours, minutes, seconds) {
  hours   = hours   || 0;
  minutes = minutes || 0;
  seconds = seconds || 0;

  var current = new Date();
  current.setHours(hours);
  current.setMinutes(minutes);
  current.setSeconds(seconds);
  return current;
}

// URLアクセスしてHerokuを起こします。
function wakeUp() {
  sites.forEach(function(site) {
    var now        = new Date();
    var wakeupTime = createCurrent(site.wakeupHour);
    var sleepTime  = createCurrent(site.sleepHour);
    var active     = wakeupTime <= now && now <= sleepTime && 0 <= site.activeDayOfWeek.indexOf(now.getDay());
    if (active) {
      UrlFetchApp.fetch(site.url, { muteHttpExceptions:true });
    }
  });
}