2017年10月31日火曜日

SpriteKit で iPhoneX 対応する方法

概要

過去に iPad 対応しました
今度発売される iPhoneX も対応しなければいけなかったのでその対応方法を紹介します

環境

  • macOS X 10.12.6
  • Xcode 9.0.1 (9A1004)

対応コード

Xcode で Game プロジェクト作成した場合を例に紹介します
デフォルトだと UIViewController を継承した GameViewController があります
そこの viewDidLoad を以下のように変更します

override func viewDidLoad() {
    super.viewDidLoad()
    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.sks'
        if let scene = SKScene(fileNamed: "GameScene") {
            // Set the scale mode to scale to fit the window
            if (UIDevice.current.model.range(of: "iPad") != nil) {
                // iPad の場合
                print("iPad")
                scene.scaleMode = .fill
            } else {
                if (UIScreen.main.nativeBounds.height == 2436.0) {
                    // iPhoneX の場合
                    print("iPhoneX")
                    scene.scaleMode = .aspectFit
                } else {
                    // iPhone の場合
                    print("iPhone")
                    scene.scaleMode = .aspectFill
                }
            }
            view.presentScene(scene)
        }
        view.ignoresSiblingOrder = true
        view.showsFPS = true
        view.showsNodeCount = true
    }
}

ポイントは UIDevice.current.model.range と UIScreen.main.nativeBounds.height を使う点です

まず UIDevice.current.model.range を使って iPad かどうかを判断します
というのも iPhoneX の場合「iPhone」として認識されるのでまずは iPad かどうか判断する必要があります

次に UIScreen.main.nativeBounds.height で iPhoneX かそうでないかを判断します
高さが 2436.0 が iPhoneX なのでその高さの場合に aspectFit を設定することで画面からはみ出ることなく表示されます

そして最後に iPhoneX 以外のデバイスの場合に aspectFill とします

最後に

SpriteKit で iPhoneX に対応する方法を紹介しました
デバイスのタイプでは判断できないのでサイズを取得して判断する必要があります

UIKit の場合 constraints を使っていれば特に何もしなくても iPhoneX に対応できると思います
SpriteKit には constraints がないのでこのような対応をしなければなりません

執筆時点で iTunesConnect にはすでに iPhoneX 対応版のバイナリをアップロードできるので対応したらすぐにアップロードすると良いと思います
スクリーンショットや AppPreviews も iPhoneX 版をアップロードできます

参考サイト

2017年10月29日日曜日

jenkins_api_client で Pipeline Job を作成するときのコツ

概要

前回 Ruby + jenkins_api_client を使って Jenkins を操作してみました
API からジョブを作成することはできますがジョブの作成には XML が必要です
今回はパイプラインジョブでその XML の情報を生成するコツを紹介します

環境

  • macOS X 10.12.6
  • Ruby 2.4.1p111
  • jenkins_api_client 1.5.3

とりあえずパイプラインジョブを作成するコード

こんな感じでパイプラインジョブを作成することができます

require 'jenkins_api_client'

client = JenkinsApi::Client.new(
  :server_ip => 'localhost',
  :username => 'root',
  :password => 'pass'
)

def create_job_xml
  builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') { |xml|
    xml.send("flow-definition", :plugin => "workflow-job@2.12.2") {
      xml.description
      xml.keepDependencies "false"
      xml.properties {
        xml.send("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty") {
          xml.triggers
        }
      }
      xml.definition(:class => "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition", :plugin => "workflow-cps@2.41") {
        xml.script "node {\n   echo 'Hello World'\n}"
        xml.sandbox "true"
      }
      xml.triggers
      xml.disabled "false"
    }
  }
  builder.to_xml
end

# puts create_job_xml
puts client.job.create('job_name', create_job_xml)

このコード内で生成している XML の情報は以下の通りです
puts のコメントアウトすれば XML の情報を確認することができます


<?xml version="1.0" encoding="UTF-8"?>
<flow-definition plugin="workflow-job@2.12.2">
   <description />
   <keepDependencies>false</keepDependencies>
   <properties>
      <org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
         <triggers />
      </org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
   </properties>
   <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.41">
      <script>node {
   echo &amp;apos;Hello World&amp;apos;
}</script>
      <sandbox>true</sandbox>
   </definition>
   <triggers />
   <disabled>false</disabled>
</flow-definition>

XML をコードに落とすときのコツ

自分のおすすめとしては

  1. まず手動でパイプラインジョブを作成する
  2. jenkins_home にある jobs/job_name/config.xml で XML の内容を確認する
  3. XML をコードに落とし込む

という流れが一番簡単で良いと思います

サンプルコードの場合 XML のマーシャライザは nokogiri を使っています
なので少し nokogiri の使い方も知っておく必要があります
まずタグ情報の定義は xml.hoge という感じで定義します
XML 内に値がある場合はスペースを 1 つ開けて文字列で値を定義します

ex)

xml.disabled "false"

XML のタグ情報にハイフンやドットが含まれる場合は xml.send メソッドを使います
第一引数で要素名を指定して第二引数で要素の属性情報を定義します

xml.send("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty")

また XML が入れ子になっている場合はブロックにすれば OK です

xml.definition(:class => "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition", :plugin => "workflow-cps@2.41") {
  xml.script "node {\n   echo 'Hello World'\n}"
  xml.sandbox "true"
}

改行は \n で置換してあげましょう
それ以外もメタ文字に関してはちゃんと置換してあげてください

たぶん気をつけるべきはそれくらいだと思います
実際にリクエストを送信する前に XML の情報を出力してみて、手動で作成したジョブの XML と合っているか確認すると良いと思います

最後に

jenkins_api_client でジョブを作成するときのコツを紹介しました
基本は素の XML を頑張って Ruby のコードに落とし込む作業をやる感じになります
なれれば簡単にコードに落とし込むことができます
今回はジョブの設定がほとんどないジョブをコード化したので短いですがジョブの設定が多くなればそれだけコード側も多くなります

jenkins_api_client を使う場合はおそらくこれが一番簡単だと思いますが、結局 XML の文字列情報を client.job.create に渡せれば良いだけなのでファイルから文字列情報を生成しても良いかと思います

2017年10月28日土曜日

docker-compose でデータコンテナを作成して別コンテナでマウントする方法

概要

docker-compose を使ってデータコンテナを作成し同時に別のコンテナからデータコンテナの領域をマウントする yml ファイルを作成してみました

環境

  • macOS X 10.12.6
  • docker 17.09.0-ce
  • docker-compose 1.16.1

docker-compose.yml

全体は以下の通りです

version: '2'
services:
  main:
    image: nginx
    ports:
      - 80:80
    volumes_from:
      - vol
    links:
      - vol
    container_name: main
  vol:
    build: .
    image: vol_image
    volumes:
      - /share
    container_name: vol

コンテナを永続化させるために nginx のイメージを使っています
動作確認のために使ってるだけなので他のイメージからでも OK です

vol がデータコンテナ側の定義になります
確認のためファイルを 1 つコンテナに追加してそれが共有されているか確認するためイメージはビルドします
ビルド後のイメージ名は vol_image とします
データコンテナが共有する領域を定義するのは volumes で行います
今回の場合 /share というパスが共有されるので、別のコンテナがデータコンテナをマウントすると /share が共有されます

main 側がマウントする側のコンテナになります
ポイントは volumes_from になります
ここにはデータコンテナのコンテナ名を指定します
今回であれば vol になるので、それを指定します
links はなくても OK です

Dockerfile

ファイルを 1 つ追加しているだけです
nginx コンテナ内にある既存のファイルを使っても問題ないですがわかりやすいように独自でファイルを作成して追加しています

FROM nginx:latest

RUN mkdir -p /share
ADD ./hoge /share

動作確認

  • docker-compose up -d

でコンテナのビルド、起動をしましょう

Creating network "docker_default" with the default driver
Building vol
Step 1/3 : FROM nginx:latest
latest: Pulling from library/nginx
bc95e04b23c0: Pull complete
110767c6efff: Pull complete
f081e0c4df75: Pull complete
Digest: sha256:004ac1d5e791e705f12a17c80d7bb1e8f7f01aa7dca7deee6e65a03465392072
Status: Downloaded newer image for nginx:latest
 ---> 1e5ab59102ce
Step 2/3 : RUN mkdir -p /share
 ---> Running in e6d30579c4f9
 ---> 3db5418c7f21
Removing intermediate container e6d30579c4f9
Step 3/3 : ADD ./hoge /share
 ---> 2dd5e9403fa6
Successfully built 2dd5e9403fa6
Successfully tagged vol_image:latest
WARNING: Image for service vol was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating vol ... 
Creating vol ... done
Creating main ... 
Creating main ... done

コンテナが起動したら領域がちゃんと main 側にマウントされているか確認してみます

  • docker exec -it main ls -ltr /share
total 4
-rw-r--r-- 1 root root 5 Oct 25 07:59 hoge

こんな感じでマウントされてファイルが存在していれば OK です

最後に

docker-compose でデータコンテナを作成して別コンテナでその領域をマウントする方法を紹介しました
docker コマンドの -v と --volumes-from を使っているだけです

ちなみにデータコンテナ側が停止すると共有されるのは停止するまでの状態になります
またデータコンテナ側には新規でファイルが追加されなくなります (当然かもしれませんが)

2017年10月27日金曜日

Sinatra で haml を使ってみた (partial も試してみる)

概要

Sinatra のテンプレートエンジンはデフォルトで erb が使えます
haml を使うこともできるので使ってみました
また、helper メソッドを定義することで別のテンプレートを参照する方法も紹介します

環境

  • CentOS 7.3
  • Ruby 2.3.3p222
  • sinatra 2.0.0
  • haml 5.0.4

各種ライブラリのインストール

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "haml"
  • bundle install --path vendor

app.rb の作成

まずはアプリ本体を作成します

  • vim app.rb
require 'sinatra'
require 'haml'

set :bind, '0.0.0.0'

get '/' do
  @msg = "hello"
  haml :index
end

erb 同様インスタンス変数を定義することで haml テンプレート内でその変数を参照することができます

haml テンプレートの作成

とりあえず簡単な HTML を出力する haml テンプレートを作成します

  • vim views/index.haml
!!!
%html
%head
  %title haml test
%body
%h1 #{@msg}
%p ほげ

haml ではタグ情報を % を付与するだけで表現できます
見やすいように好きなようにインデントして OK です
文字列も特にダブルクォートで囲まなくて OK です、逆に囲んでしまうと表示されてしまうので注意です
先頭のビックリマーク 3 は「haml front matter」と呼ばれる特殊な命令でメタデータなどを設定することができます
#{@msg} とすることで app.rb 内で定義した変数を参照することができます

とりあえず動作確認

これでまずは動作確認してみましょう

  • bundle exec ruby app.rb

localhost:4567 にアクセスすると簡単な HTML が表示されると思います

css を追加してみる

class 属性を追加して css で装飾してみましょう

  • vim view/index.haml
!!!
%html
%head
  %title haml test
  %link{href: "style.css", rel: "stylesheet", type: "text/css"}
%body
%h1 #{@msg}
%p{class: "p1"} ほげ

%p に class 属性を追加しました
他にも追加するための記述はありますが個人的にはこれが好きです
そして %head 内にスタイルシートの参照を追記しています

スタイルシートを追加する

これは既存のスタイルを追加するだけです
haml 内でスタイルシートを定義することもできますが今回は別ファイルにしました

  • vim public/style.css
.p1 {
  font-size: 48px;
}

これで動作確認してみると文字が大きくなりスタイルが適用されているのがわかると思います
余談ですが Sinatra の場合画像やスタイル、JavaScript などの公開ファイルは public 配下に配置します

別の haml ファイルを作成して参照してみる

テンプレートファイルは共通する部分を切り出して部分的に管理したくなります
rails だと render という機能がありデフォルトで使用できるのですが、Sinatra ではそのような機能はありません
なので、今回は helper として partial というメソッドを定義して haml テンプレート内から別の haml テンプレートを参照してみたいと思います

ヘルパーモジュールの追加

  • vim helpers/sinatra_partials.rb
module Sinatra::Partials
  def partial(template, *args)
    template_array = template.to_s.split('/')
    template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
    options = args.last.is_a?(Hash) ? args.pop : {}
    options.merge!(:layout => false)
    locals = options[:locals] || {}
    if collection = options.delete(:collection) then
      collection.inject([]) do |buffer, member|
        buffer << haml(:"#{template}", options.merge(:layout =>
        false, :locals => {template_array[-1].to_sym => member}.merge(locals)))
      end.join("\n")
    else
      haml(:"#{template}", options)
    end
  end
end

何をしているかというと引数で与えられた template ファイルにアンダースコアを追加して haml 関数をコールしています
アンダースコアを追加しているのは非参照のファイルであることを明示的にするためです
またテンプレート内に与える引数をオプションで指定することができます

アプリでヘルパーモジュールを参照する

追加したヘルパーモジュールを app.rb から呼び出します

  • vim app.rb
require 'sinatra'
require 'haml'
require './helpers/sinatra_partials.rb'

set :bind, '0.0.0.0'
helpers Sinatra::Partials

get '/' do
  @msg = "hello"
  haml :index
end

これで haml テンプレート内で partial メソッドが使えるようになります

haml テンプレートの変更

別の haml テンプレートを呼び出してみます

  • vim views/index.haml
!!!
%html
%head
  %title haml test
  %link{href: "style.css", rel: "stylesheet", type: "text/css"}
%body
%h1 #{@msg}
%p{class: "p1"} ほげ
= partial "footer", :locals => {:key => "footer!"}

最後の 1 行を追加してます
haml 内で関数をコールする場合は = を使います
そしてヘルパーモジュール内で定義した partial メソッドをコールします
今回は _footer.haml というテンプレートを呼び出します
またオプションで :locals を追加することで _footer.haml 内で変数の値を参照できるようにします

_footer.haml の作成

参照される側の haml ファイルを作成します

  • vim views/_footer.haml
%h2 #{key}

先程も少し紹介しましたが非参照がわかるようにアンダースコアを付与するのを忘れないようにしてください
内容は渡された変数の内容を表示しているだけです

動作確認

これで動作確認すると別の haml ファイルの内容も HTML に反映されていると思います
またローカル変数の値も参照できていると思います

最後に

Sinatra + haml を使って HTML を表示してみました
erb よりも haml を使ったほうが記述量が減らせるのが嬉しい点かなと思います
また今回紹介した partial は erb でも使うことができます
haml とコールしてる部分を erb に変更するだけです

参考サイト

2017年10月26日木曜日

terraform に入門してみた (docker 編)

概要

terraform を試してみました
terraform はインフラ自動構築するためのツールでコード化することができます
terraform にはたくさんのプロバイダがあり今回は docker プロバイダを使って試してみました

環境

  • CentOS 7.3
  • terraform 0.10.7

terraform のインストール

まずはインストールです
hashicorp 製品はバイナリを一つ置いて終了です

  • wget 'https://releases.hashicorp.com/terraform/0.10.7/terraform_0.10.7_linux_amd64.zip?_ga=2.150895542.1898133342.1508819233-1686377386.1508819233' -O terraform_0.10.7_linux_amd64.zip
  • unzip terraform_0.10.7_linux_amd64.zip
  • mv terraform /usr/local/bin

とりあえず試してみる

公式の Getting Started にあるサンプルをとりあえず実行してみましょう

  • vim main.tf
provider "docker" {
  host = "tcp://127.0.0.1:2376/"
}

# Create a container
resource "docker_container" "foo" {
  image = "${docker_image.ubuntu.latest}"
  name  = "foo"
}

resource "docker_image" "ubuntu" {
  name = "ubuntu:latest"
}

dockerd の tcp 接続を有効にしている場合は上記で OK です
socket 接続の場合はコメントアウトすれば OK です
dockerd の tcp 接続の有効化に関してはこちら を参考にしてください

.tf ファイルが記載できたら初期化します

  • terraform init

これで docker プロバイダのインストールが始まります
完了すると .terraform というディレクトリが作成されています
terraform は実行する前に実行計画を確認することができます

  • terraform plan

実行すると作成されるリソースの一覧とその情報が表示されます
今回であれば pull されるイメージの情報や作成されるコンテナの情報が表示されます

では実行してみましょう

  • terraform apply

apply コマンドで実行できます
実際に .tf ファイルに記載した内容のコンテナとイメージが作成されます
確認は docker コマンドでもできますが terraform で作成したリソースだけを表示することもできます

  • terraform show

show コマンドを実行することで terraform 経由で pull したイメージやコンテナの情報だけを表示することができます

リソースを削除する

terraform 経由で作成されたリソースのみ削除したい場合は destroy コマンドを利用します

  • terraform destroy

基本的にはこれで削除したほうが良いと思います
もちろん docker rm などを使えばそれでも削除できますが面倒なので一気に削除したい場合は destroy を使うと良いと思います
デフォルトだとリソースの削除時に確認があるので確認が不要な場合は -force を付与してください
また、plan コマンドで-destroy オプションを付与すると削除時の計画を表示することも可能です

インフラ構成を変更する

terraform では一度構築した環境を変更することもできます
以下のように .tf ファイルを書き換えてみましょう

  • vim main.tf
provider "docker" {
  host = "tcp://127.0.0.1:2376/"
}

# Create a container
resource "docker_container" "foo" {
  image = "${docker_image.ubuntu.latest}"
  name  = "foo2"
}

resource "docker_image" "ubuntu" {
  name = "ubuntu:latest"
}

コンテナの名前を変更しただけです
これで再度 apply するとコンテナの名前が変わっていると思います
docker プロバイダの場合コンテナの名前が変更されたわけではなく実際は前のコンテナが削除されて別のコンテナが作成されています

プロビジョニングする

terraform ではインスタンスなどを構築時にスクリプトを実行してプロビジョニングすることも可能です
ただ、docker プロバイダではプロビジョンの機能は提供されていません (当然ですが)
AWS のリソースなどを扱える aws_instance などでは使うことができます

変数を利用する

場合によっては apply 時に毎回情報を書き換えたい場合があると思います
そんなときは変数を使うことができます
.tf ファイルを以下のように書き換えましょう

  • vim main.tf
variable "container_name" {
  default = "hoge"
}

provider "docker" {
  host = "tcp://127.0.0.1:2376/"
}

# Create a container
resource "docker_container" "foo" {
  image = "${docker_image.ubuntu.latest}"
  name  = "${var.container_name}"
}

resource "docker_image" "ubuntu" {
  name = "ubuntu:latest"
}

variable を使って変数を使用することができます
default を使えばデフォルト値を設定することもできます
参照するときは ${var.container_name} として参照することができます (なぜか var. なので注意)

  • terraform apply

で実行すると default の値が使用されます

  • terraform plan -var container_name="fuga"
  • terraform apply -var container_name="fuga"

とすると fuga というコンテナを作成することができます

また key = value 形式のファイルを作成して -var-file でファイルを参照することもできます
変数には list や map 形式で定義することもできます

特定の結果を表示する

apply で実行した結果を確認したいケースはあると思います
そんなときは output リソースを使用することで指定した結果を取得することができます

  • vim main.tf
variable "container_name" {
  default = "hoge"
}

provider "docker" {
  host = "tcp://127.0.0.1:2376/"
}

# Create a container
resource "docker_container" "foo" {
  image = "${docker_image.ubuntu.latest}"
  name  = "${var.container_name}"
}

resource "docker_image" "ubuntu" {
  name = "ubuntu:latest"
}

output "id" {
  value = "${docker_container.foo.id}"
}

最後の output リソースがそれに当たります
上記の場合は作成されたコンテナの id を取得します

  • terraform apply

で実行したあとに

  • terraform output id

とすることで id の情報を取得することができます

モジュール化する

terraform では .tf ファイルをモジュールにすることができます
モジュール化した .tf ファイルを参照することでモジュール内のリソースを使用することができるようになります

.tf ファイル内で modules の定義をして terraform get を実行するとモジュールの .tf ファイルを取得できます

今回は試さないので詳しくはこちらを試してみてください
サンプルは consul 用の module を使用しています

Remote Backends

それ以外には Remote Backends という機能があり terraform で実行した履歴や状態を保存しておくことができます
自分一人で使っている場合には不要なのですが、例えばプロダクションなどの環境でいろんなホストから実行される場合などにどこまで apply しているかを確認することができようになります

こちらも今回は試していないので興味があれば公式を見ながら試してみてださい

最後に

terraform を試してみました
公式の Getting Started は AWS 環境を使っていますが準備が大変だったのでさらっとできる docker プロバイダで試してみました

正直 docker-compose で足りるような気もしますが docker コマンドのオペレーションもこれを使えばコード化できるのが嬉しい点かなと思います
ただ docker build はできないっぽいので、それは terraform 以外の世界で事前に作成する必要があります

参考サイト

2017年10月25日水曜日

spin.js を使ってみた

概要

spin.js はローティング画面を簡単に実装できる JavaScript のライブラリです
今回は HTML と JavaScript だけを使って spin.js の動きを確認してみました

環境

  • macOS X 10.12.6
  • nginx 1.10.1
  • spin.js 2.3.2

HTML 作成

  • vim index.html
<html>
<head>
<style type="text/css">
<!--
#layer {
    width  : 100%;
    height : 100%;
    background-color : blue;
    position : absolute;
    opacity: 0.1;
}
-->
</style>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script type="text/javascript" src="http://spin.js.org/spin.min.js"></script>
</head>
<body>
<div id="layer"></div>
<div id="test_spin">spin_area</div>
<div id="stop">stop</div>
<script type="text/javascript" src="/spin/my_spin.js"></script>
</body>
</html>

dom の参照を jQuery で行いたかったので、jQuery を追加しています
あとは spin.js と独自の JavaScript ファイルを body の直前で読み込んでいます

spin.js を使った JavaScript ファイルの作成

  • vim my_spin.js
var opts = {
  lines: 13 // The number of lines to draw
, length: 28 // The length of each line
, width: 14 // The line thickness
, radius: 42 // The radius of the inner circle
, scale: 1 // Scales overall size of the spinner
, corners: 1 // Corner roundness (0..1)
, color: '#000' // #rgb or #rrggbb or array of colors
, opacity: 0.25 // Opacity of the lines
, rotate: 0 // The rotation offset
, direction: 1 // 1: clockwise, -1: counterclockwise
, speed: 1 // Rounds per second
, trail: 60 // Afterglow percentage
, fps: 20 // Frames per second when using setTimeout() as a fallback for CSS
, zIndex: 2e9 // The z-index (defaults to 2000000000)
, className: 'spinner' // The CSS class to assign to the spinner
, top: '50%' // Top position relative to parent
, left: '50%' // Left position relative to parent
, shadow: false // Whether to render a shadow
, hwaccel: false // Whether to use hardware acceleration
, position: 'absolute' // Element positioning
}
console.log("start");
var target = $("#test_spin").get(0);
var spinner = new Spinner(opts).spin(target);
setTimeout(function() {
    spinner.stop();
    $("#layer").hide();
    console.log("stop");
}, 5000);

$('#stop').click(function () {
  console.log("stop");
  spinner.stop();
});

JavaScript ファイルが読み込まれた瞬間にローティングが始まります
おまけで stop 要素をタップした場合の処理も実装しています
これはなくても動作するので参考まで

動作確認

index.html にアクセスしてみましょう
すると 5 秒後にローティング画面が非表示になりかつ薄青のレイヤー用の div も非表示になります
spin_js.gif

非表示になったあとに stop をタップすることができるようになります
レイヤ用の div を初めから削除した状態で index.html を開くといきなり stop をタップすることができます
stop をタップするとタップした瞬間ローティング画面が非表示になります

spin.js の挙動としてはローティングを表示する div タグは別にどこにあっても良くその上に強制的にローティング画面を表示します
そして、今回の設定であれば画面中央に表示されます
表示される場所やそれ以外の設定はすべて opts 変数で定義されているので適当に変更しながら挙動を確認すると良いと思います

最後に

spin.js を HTML と JavaScript だけで簡単に試してみました
spin.js 自体はシンプルにローティング画面を表示/非表示するだけなので、ローティング中に他のボタンを表示させないようにレイヤなどを自分で用意する必要はありそうです
それでも簡単にローティング画面を実装できる可能です

2017年10月24日火曜日

webpack に入門してみた

概要

webpack は複数の js ファイルや css をファイルを一つの js ファイルにまとめてくれるツールです
node で動作します
今回は webpack を使って簡単な HTML を出力するところまでやってみました

環境

  • macOS X 10.12.6
  • node 8.7.0
  • webpack 3.8.1

作成するファイル一覧

今回作成するファイルの構成は以下の通りです

.
├── package-lock.json
├── package.json
├── public
│   ├── index.html
│   └── js
│       └── bundle.js
├── src
│   └── js
│       ├── app.js
│       └── modules
│           ├── footer.js
│           └── header.js
└── webpack.config.js

順を追って全部作成していきます

webpack のインストール

まずはこれがないと始まりません

  • npm install webpack -g

で webpack コマンドが使えるようになります

packer.json の作成

今回は外部のモジュールも使用するため dependencies に 2 つほど追加しています

  • npm init
  • vim packer.json
{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "jquery": ">=3.2.1",
    "date-utils": ">=1.2.21"
  }
}
  • npm install

jquery と date-utils をインストールするために作成します
やることによっては必須ではないです
今回は使うためインストールしています

webpack.config.js の作成

いわゆる設定ファイルです

  • vim webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/js/app.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'public/js')
    }
};

起点となる js ファイルと最終的に出力される bundle.js の出力パスを指定しています

起点となる app.js の作成

あとで作成するモジュールを読み込んでメインとなる処理を実施します

  • vim src/js/app.js
import $ from 'jquery';
import headerText from './modules/header';
import footerText from './modules/footer';

var htext = headerText();
var ftext = footerText();

$('header').html(htext);
$('footer').html(ftext);

今回はヘッダ情報とフッタ情報を生成するモジュールを作成しました
そしてそれらモジュールと jquery を使って HTML 情報を作成しています

モジュールファイルの作成

各機能ごとに作成します
今回は簡単な文字列情報を返却するモジュールファイルを 2 つ作成します

  • vim src/js/modules/header.js
export default function headerText() {
    return "Header";
}
  • vim src/js/modules/footer.js
export default function footerText() {
    require('date-utils');
    var date = new Date();
    var now = date.toFormat("YYYY/MM/DD HH24:MI:SS");
    return `DateTime: ${now}`;
}

せっかくなので外部のモジュールを読み込んで使っています
アクセスごとに時刻が変わるようになります

HTML ファイルの作成

  • vim public/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>webpack tutorial</title>
</head>
<body>
  <header></header>
  <footer></footer>
  <script src="js/bundle.js"></script>
</body>
</html>

ビルドして作成される bundle.js を読み込んでいます

ビルド

webpack.config.js のあるディレクトリで実行しましょう

  • webpack

すると以下のようにビルド結果が表示されます

Hash: 44c1686f210ca4db43de
Version: webpack 3.8.1
Time: 362ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  325 kB       0  [emitted]  [big]  main
   [0] ./src/js/app.js 214 bytes {0} [built]
   [2] ./src/js/modules/header.js 62 bytes {0} [built]
   [3] ./src/js/modules/footer.js 180 bytes {0} [built]
    + 2 hidden modules

これで public/js/bundle.js にすべてのファイルがまとめられたバンドルファイルが作成されます

動作確認

public/index.html をブラウザで確認してみましょう
すると以下のような感じで表示されると思います
webpack1.png

最後に

webpack を使ってみました
モジュールをわけて開発することができるのが一番の魅力かなと思います
最終的には 1 つのファイルができあげるだけなので利用する側も簡単になります
今回は Javascript のバンドルだけを行いましたが、CSS のバンドルもできるようです
できあがったサンプルが寂しいので次回は webpack を使って CSS でも当ててみたいと思います

参考サイト