sou's blog

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

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 });
    }
  });
}

お名前.comで取得したnaked domainをherokuのアプリケーションに割り当ててSSLも自動更新で運用する方法

現時点(2017年度9月)でおそらく最短距離で実施する方法だと思います。 昨今SSL接続必須化の流れを鑑み、ドメインを割り当てるだけでなく、SSL接続を可能にし、証明書更新の運用もHerokuに丸投げするように設定してみました。

  1. お名前.comでドメインを取得する
  2. HerokuのDynoをHoby($7/month)以上にする
  3. HerokuのSettings > Domains and certificates
    1. Configure SSLでAutomaticallyを選択しContinueを押下
    2. Add Domainで1.で取得した独自ドメインを設定
  4. HerokuのResources > Add-onsでPointDNSを追加
  5. PointDNSで1.で取得した独自ドメインを登録
  6. PointDNSで登録したドメインのレコード設定でALIAS、CNAMEに3-2で取得されたDNS Targetを設定
  7. お名前.comでネームサーバーの変更で6.で一覧表示されているTypeがNSのもの(ネームサーバ おそらく dns12.pointhq.com.など3つほど)を登録

オープンソースのmastodonからみるRails + React構成や採用されているライブラリについて

mastodonについて

mastodonという新興SNSが国内でも流行りだしています。フェーズとしてはまだイノベータからアーリーアダプターが様子を見てるって感じかなと個人的には思います。

mstdn.jpという日本ドメインインスタンスでは、現時点(2017/4/22)で全インスタンスと比べても世界一のユーザ数を誇っています。ただ、実際に投稿内容を見てみるとユーザもまだテスト投稿したりという感じのようです。(もっとコアなドメインインスタンスだとそのコンテキストの投稿がもっと盛んにされているのかもしれないですが)

このサービスの面白いところは、分散型ネットワーク+OSSというところなのかなと勝手に解釈しています。つまり、個人もしくは企業がTwitterのようなSNSサービスを(手軽に)独自運営していけてそれが本家の改善にもつながる仕組みになっているところに魅力があります。もう少し具体的に言うと本体のソースコードOSSなので大抵はForkしたものをデプロイすることになると思うのですが、そのインスタンス特有の機能を入れて本体とは少し違ったアプリケーションにしたり、場合によっては追加した機能や見つかったバグの修正を本体側にPull-Requestするなどサービス自体が分散して成長していく仕組みが取り入れられているのです。

コードを覗いてみてみる

前置きが少し長くなりましたがコードを実際に見てみるとRails+Reactで構成されていることがわかります。流行りのWebアプリケーションの構成だと思うので、参考になるところが多いです。

サーバーサイド構成

Rubyは2.4.1、Railsのバージョンは現時点で5.0.2で比較的新しい構成であることがわかります。

ruby '2.4.1'

gem 'rails', '~> 5.0.2'
APIの管理

app/controllers/api_controller.rbにApiControllerと言う基底クラスを定義しています。また、app/controllers/api/v1配下に実際のAPIをApiControllerクラスの具象クラスとして配置しています。これでいわゆるAPIのバージョニング管理をしているようです。

concernの実装

model/concerns配下では以下のような共通の振る舞いが定義されています。

cacheable.rb
paginable.rb
streamable.rb
targetable.rb

paginable.rbを見てみると内部的にarel_tableを使用してqueryを組み立てていることがわかります。

scope :paginate_by_max_id, -> (limit, max_id = nil, since_id = nil) {
      query = order(arel_table[:id].desc).limit(limit)
      query = query.where(arel_table[:id].lt(max_id)) unless max_id.blank?
      query = query.where(arel_table[:id].gt(since_id)) unless since_id.blank?
      query
}

フロントエンド構成

app/assets/javascripts/components配下にReactのコードが配置されています。JavascriptはECMAScirpt 2015記法で書かれています。Gemfileをみると以下の記述が確認できます。

gem 'react-rails'
gem 'browserify-rails'
gem 'autoprefixer-rails'
React + Redux

reactをrailsに導入するアプローチはいくつかありますが、mastodonではasset_pipline上にReactをのせるためreact-railsを採用しています。また、browserifyでbabelを使ってコンパイルするようにbrowserify-railsを採用しています。Reactでのスタイル適用はインラインでもscssでもどちらにも書かれています。

Ajax

ajax通信ライブラリとしてはaxiosが採用されています。ただし、使用するにはapi.jsxというラッパーモジュールから呼び出しています。

レンダリング対策

react-addons-pure-render-mixinが採用されています。

途中。気づいたら追記

Azure AppServiceで任意のタイムゾーンを設定する

タイムゾーンを日本に合わせたい人へ

ポータル > AppService > アプリケーション設定 でアプリ設定にWEBSITE_TIME_ZONEを「Tokyo Standard Time」で保存すればアプリが自動的に反映してくれます。

f:id:sousousore1:20160913182548p:plain