kame's engineer note

技術関連のメモをブログに記録していく

sinatra+bower+gulpでWEBアプリのスケルトンを作る

概要

簡単なWEBアプリを作りたい時にsinatraをよく使っている。
素のままでも便利なのだが、開発効率をもっと向上させるために、色々とカスタマイズした状態のスケルトンを作ることにした。

具体的に言うと下記のようなことをしたい。

  • slim,sass,coffeeの自動コンパイル
  • jqueryやbootstrapなどのパッケージを管理
  • livereload的なファイルを保存したらブラウザを自動リロード

sinatraと合わせて、フロントエンド界では必須ツールでもあるbowerやgulpを使うことにする。

使用する技術と役割

sinatra

sinatraはshotgunを使ってサーバーを立ち上げる。
slimはsinatra側でコンパイルする。
データベースを使いたい場合があるので、sqlite3とactiverecordを入れておく。
foremanはshotgunの実行とgulpの実行の際に使用する。

bower

とりあずjqueryとbootstrapをbowerで管理する。

gulp

  • ビルドツール
  • http://gulpjs.com/
  • gruntも考えたけど、今はgulpの方がイケてる感??
  • tasks
    • sass&coffeeのcompile
    • gulp-bower
    • browsersync

complieしたり、bowerで管理しているパッケージを扱ったりする。
あとはファイルとブラウザの同期。

sinatraの導入

bundle init


Gemfile

source "https://rubygems.org"

gem 'sinatra'
gem 'slim'
gem "sqlite3"
gem 'activerecord'
gem 'sinatra-config-file'

group :test do
    gem 'shotgun'
    gem 'foreman'
    gem 'rspec'
    gem "rack-test"
end
bundle install --path vendor/bundle

Gemfileを生成し、sinatra等をインストールする。


次にsinatraで使用するディレクトリやファイルを作っていく。
まずは以下のような構造にする。

  • app.rb
    • 説明: ルーティングの実行(controller的な役割)
  • config.rb
    • 説明: アプリケーションの起動
  • public
    • css
    • js
      • 説明: 静的ファイルの置き場所
  • views
    • layout.slim
    • index.slim
      • 説明: viewファイル
  • models
    • init.rb
    • hoge.rb
      • 説明: modelファイル
  • spec
    • spec_helper
    • models
      • hoge_spec.rb
        • 説明: specファイル
  • config.yml
  • dev.db


上から説明していく。

app.rb

# encoding: utf-8

require 'sinatra/base'
#ymlファイルを扱う
require "sinatra/config_file"
#slimを使用
require 'slim'
#activerecordを使用
require 'active_record'

#activerecordで使用するdbを指定
ActiveRecord::Base.establish_connection(
    adapter: 'sqlite3',
    database: 'dev.db'
)

#models/init.rbの読み込み
require_relative 'models/init'

class App < Sinatra::Base
    register Sinatra::ConfigFile
    config_file 'config.yml'

    get '/' do
        slim :index
    end

end

slimはsinatraで扱う必要があるので、Gemで対応。
話は変わるが、scssとcoffeeも以下のような記述をapp.rbにすることでsinatraで使えるようになる。

get %r{^/(.*)\.css$} do
    scss :"scss/#{params[:captures].first}"
end
get %r{^/(.*)\.js$} do
    coffee :"coffee/#{params[:captures].first}"
end

だが、gulpでbowerのcss,js系のパッケージ管理をしたり、minifyしたりする想定なので、cssとjsはすべてgulpで管理することにした。


config.ru

require 'bundler'
Bundler.setup

root = ::File.dirname(__FILE__)
require ::File.join(root, 'app')

run App

sinatraの起動のために必要なファイル。


views/layout.slim

doctype html
html
  head
    meta charset='utf-8'
    title sample app
    script src="js/program.js"
    link href="css/style.css" rel="stylesheet"
  body
    .wrap.container-fluid
      .main.row
      == yield

views/index.slim

top page

slimで書く。
ファイル名をlayoutにすれば勝手にlayoutファイルと認識してくれる。
yieldで個別のviewを読み出す。


models/init.rb

Dir[File.dirname(__FILE__) + '/*.rb'].each do |file|
  next if file == 'init'
  require_relative file
end

app.rbmodels/init.rbを読み込み、そこからmodels配下にあるrbファイルを全て読み込む形にした。


簡単ではあるが、sinatraの構築は以上で終了。

bowerの導入

npm install -g bower

パッケージ管理を行うbowerをインストールする。
node.jsが入ってない場合はインストールする。


bower init

bowerの初期化を行う。
色々と質問されるのでテキトウに答えていくと、bower.jsonが生成される


bower install --save jquery bootstrap

jqueryとbootstrapをインストールする。


bower.json

{
  "name": "sinatra-skelton",
  "version": "0.0.0",
  "authors": [
    "kame"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "bootstrap": "~3.2.0",
    "jquery": "~2.1.1"
  }
}

bower.jsonにbootstrapとjqueryが記述されているのと、 bower_componentsディレクトリとその中にファイルが生成されていればOK。


gulpの導入

npm init

まずは初期化コマンドでpackage.jsonを生成する。


今回、gulpで使用するプラグインは下記の通り。

  • coffee-script
    • gulpをcoffeeで書けるようにする
  • gulp
    • gulp本体
  • gulp-coffee
  • gulp-sass
  • gulp-filter
    • フィルタリング
  • gulp-plumber
    • コンパイルエラー時でも監視を中断させないようにする
  • main-bower-files
    • bowerのパッケージを扱う
  • browser-sync
    • ファイルとブラウザ同期
  • del
    • ファイルの削除
npm install --save-dev coffee-script gulp gulp-coffee gulp-sass gulp-filter gulp-plumber main-bower-files browser-sync del

installコマンドで一括インストールする。
--save-devを付けることで自動的にインストールしたプラグインをpackage.jsonに書き出してくれる。


gulpfile.coffee

g = require 'gulp'
browserSync = require 'browser-sync'
gulpFilter = require 'gulp-filter'
mainBowerFiles = require 'main-bower-files'
sass = require 'gulp-sass'
coffee = require 'gulp-coffee'
plumber = require 'gulp-plumber'
del = require 'del'

g.task 'bower-files', ->
  jsFilter = gulpFilter('**/*.js')
  cssFilter = gulpFilter('**/*.css')
  g.src mainBowerFiles()
      .pipe jsFilter
      .pipe g.dest('public/js/libs/')
      .pipe jsFilter.restore()
      .pipe cssFilter
      .pipe g.dest('public/css/libs/')

g.task 'css', ->
  g.src 'assets/sass/*.scss'
      .pipe plumber()
      .pipe sass()
      .pipe g.dest('public/css')

g.task 'js', ->
  g.src 'assets/coffee/*.coffee'
      .pipe plumber()
      .pipe coffee()
      .pipe g.dest('public/js')

g.task 'bs', ->
  browserSync.init(null, {
      proxy: 'yourdomain'
  })

g.task 'bsReload', ->
  browserSync.reload()

g.task 'clean' , ->
  del [
      "public/js/libs/*"
      "public/css/libs/*"
      "public/js/*.js"
      "public/css/*.css"
  ]

g.task 'build'  ,
    [
        'bs'
        'bower-files'
        'js'
        'css'
    ]

g.task 'default', ['clean','build'], ->
  g.watch 'assets/coffee/*.coffee' ,['js']
  g.watch 'assets/sass/*.scss' ,['css']
  g.watch 'app.rb', ['bsReload']
  g.watch 'views/*.slim', ['bsReload']

上から順に説明していく。

require

g = require 'gulp'
browserSync = require 'browser-sync'
gulpFilter = require 'gulp-filter'
mainBowerFiles = require 'main-bower-files'
sass = require 'gulp-sass'
coffee = require 'gulp-coffee'
plumber = require 'gulp-plumber'
del = require 'del'

最初に必要なプラグインを読みだしておく。


bower

g.task 'bower-files', ->
  jsFilter = gulpFilter('**/*.js')
  cssFilter = gulpFilter('**/*.css')
  g.src mainBowerFiles()
      .pipe jsFilter
      .pipe g.dest('public/js/libs/')
      .pipe jsFilter.restore()
      .pipe cssFilter
      .pipe g.dest('public/css/libs/')

bowerで管理しているパッケージを指定のディレクトリに配置する処理だ。
基本的な流れはgulp.src()でファイルを指定し、何らかの処理をした後にgulp.dest()でファイルの書き出し先を指定する。
ここでは、gulp.src()でmainBowerFilesを指定し、そこからgulpFilterでjsとcss読み込み、gulp.dest()でpublicディレクトリ配下に書き出すといった流れだ。

gulpの基本的な使い方は以下の記事がわかりやすい。
タスクランナーgulp.js最速入門


sass,coffeeのcompile

g.task 'css', ->
  g.src 'assets/sass/*.scss'
      .pipe plumber()
      .pipe sass()
      .pipe g.dest('public/css')

g.task 'js', ->
  g.src 'assets/coffee/*.coffee'
      .pipe plumber()
      .pipe coffee()
      .pipe g.dest('public/js')

bowerの処理とほとんど一緒で、scssとcoffeeファイルを読み出し、コンパイルして、publicディレクトリ配下に書き出している。 plumber()コンパイルエラーが起きても監視(watch)を続行するためのもの。


browsersync

g.task 'bs', ->
  browserSync.init(null, {
      proxy: 'yourdomain'
  })

g.task 'bsReload', ->
  browserSync.reload()

BrowserSyncの設定タスクとファイルのwatchにつかうタスクを記述。
今回のようにプログラムを使用する場合はそれが動くサーバーを立ち上げて、設定タスクに使用するドメインをproxyで指定する必要がある。

livereloadというのもあるんだが、BrowerSyncはファイルとブラウザの同期だけではなくて、ブラウザ間同期も可能なのと、なんとスクロールやクリック等の操作も同期される。
なので、1つのブラウザを操作するだけで他のブラウザ(端末を選ばず)も同時に動作チェックすることができるので、かなり効率があがりますね。
実行時にBrowserSyncの管理画面が本ちゃん以外のポートで立ち上がって、色々カスタマイズできたり、変更履歴を簡単に確認できたりととても便利そう。

BrowserSync


clean

g.task 'clean' , ->
  del [
      "public/js/libs/*"
      "public/css/libs/*"
      "public/js/*.js"
      "public/css/*.css"
  ]

cleanタスクの定義。 cssやjsが生成される前に、以前のデータを削除しておく。


build

g.task 'build'  ,
    [
        'bs'
        'bower-files'
        'js'
        'css'
    ]

buildタスクとして、各実装をまとめる。


watch

g.task 'default', ['clean','build'], ->
  g.watch 'assets/coffee/*.coffee' ,['js']
  g.watch 'assets/sass/*.scss' ,['css']
  g.watch 'app.rb', ['bsReload']
  g.watch 'views/*.slim', ['bsReload']

cleanタスクとbuildタスクをdefaultタスク統合。
defaultタスクは、文字通りデフォルトで実行されるタスクなので、gulpコマンドだけで実行できる。
同時にcompileとBrowserSyncの実装をファイル変更のたびに行うために、watchで監視対象ファイルを指定する。


以上でgulpの設定は完了。

実行

foremanを使う

ローカルサーバーの立ち上げも一緒にやってしまいたいので、shotgunとgulpコマンドをforemanを使って一括で実行する。
その際にshotgunで生成されるIPアドレスgulpfile.coffeeのBrowserSyncのドメインに設定する。

Procfile

application: bundle exec shotgun -o localhost --port=6000
gulp: gulp


gulpfile.coffee

g.task 'bs', ->
  browserSync.init(null, {
      proxy: 'localhost:6000'
  })

shotgunでport6000でサーバーを立ち上げ、そのサーバーのproxyの設定をBrowserSyncで行う。


bundle exec foreman start
20:14:40 application.1 | started with pid 14604
20:14:40 gulp.1        | started with pid 14605
20:14:40 gulp.1        | [20:14:40] Requiring external module coffee-script/register
20:14:41 application.1 | [2015-03-25 20:14:41] INFO  WEBrick 1.3.1
20:14:41 application.1 | [2015-03-25 20:14:41] INFO  ruby 2.1.2 (2014-05-08) [x86_64-linux]
20:14:41 application.1 | [2015-03-25 20:14:41] INFO  WEBrick::HTTPServer#start: pid=14604 port=6000
20:14:43 gulp.1        | [20:14:43] Using gulpfile ~/skelton/sinatra-skelton/gulpfile.coffee
20:14:43 gulp.1        | [20:14:43] Starting 'clean'...
20:14:43 gulp.1        | [20:14:43] Finished 'clean' after 8.16 ms
20:14:43 gulp.1        | [20:14:43] Starting 'bs'...
20:14:43 gulp.1        | [20:14:43] Finished 'bs' after 11 ms
20:14:43 gulp.1        | [20:14:43] Starting 'bower-files'...
20:14:43 gulp.1        | [20:14:43] Starting 'js'...
20:14:43 gulp.1        | [20:14:43] Starting 'css'...
20:14:43 gulp.1        | [BS] Proxying: http://localhost:6000
20:14:43 gulp.1        | [BS] Now you can access your site through the following addresses:
20:14:43 gulp.1        |
20:14:43 gulp.1        | [BS] Local (this machine):
20:14:43 gulp.1        | [BS] >>> http://localhost:3002
20:14:43 gulp.1        | [BS] External (other devices etc):
20:14:43 gulp.1        | [BS] >>> http://133.xxx.xxx.xxx:3002
20:14:43 gulp.1        |
20:14:43 gulp.1        | [20:14:43] Finished 'js' after 152 ms
20:14:43 gulp.1        | [20:14:43] Finished 'css' after 146 ms
20:14:43 gulp.1        | [20:14:43] Finished 'bower-files' after 207 ms
20:14:43 gulp.1        | [20:14:43] Starting 'build'...
20:14:43 gulp.1        | [20:14:43] Finished 'build' after 26 μs
20:14:43 gulp.1        | [20:14:43] Starting 'default'...
20:14:43 gulp.1        | [20:14:43] Finished 'default' after 24 ms

あとはforemanを実行すればshotgunとgulpが実行され、サーバーが立ち上がるはずだ。

git cloneして使う

git cloneして、いくつかコマンドを打てば、すぐに使用できるようになっている。 

npm install

package.jsonに書かれたパッケージをインストール


 npm install -g bower

bowerをインストールしていなければインストール。


bower install

bower.jsonに書かれたパッケージをインストール。


Procfile内のshotgunのドメインgulpfile.coffee内のBrowserSyncのproxyドメインを指定のドメインに変更。


bundle exec foreman start

shotgunとgulpの実行


以上でカスタマイズされたsinatraでの開発環境が整う。

sinatra-skelton

最後に

下記のQitaの記事にもあるように、railsとか使う場合でもjsとかのパッケージ管理はgemとか素でいれるんじゃなくて、bower-railsとか使ってbowerで管理したほうが良さげな感じですね。
gemだと対応遅い場合もあるし、素でいれるとしても更新作業とか面倒ですしね。
http://qiita.com/reikubonaga/items/5a6037e067b79e5f9849

gulpに関しては今回のようにちょっとしたWEBアプリや静的サイトを作る場合に適してるのかなー。
ただ下記のような記事もあった。

Gulp on Rails: Replacing the Asset Pipeline

railsの場合、assetsは時代遅れで、gulpをrailsに統合して使うべきだと。
railsとgulpを統合してて、railsの仕様通りapp/assetsを読みに行って、そこからgulpでbuildされたファイルを見に行く感じかな。

また、記事の中でフロントエンドよりの最新で便利な技術使いたいなら、railsに任せるのではなくて、そこは分けて使いましょうよと、言っていますね。

For me, there's no better tool than Gulp to glue these incredible build tools together. Unfortunately, Rails disagrees and reeeally wants us to use its tech stack to handle assets. Sure, there are a few gems out there that let you cobble together some of the technology above, but I've found that they quickly fall behind, don't expose all the same options, and some things just aren't possible.

あとはこんな記事も。
http://qiita.com/oreshinya/items/3d025dde2edc56622cc4

この辺に関しては、もう少し考えてみることにする。

参考URL