LCL Engineers' Blog

夜行バス比較なび(高速バス比較)・格安移動・バスとりっぷを運営する LCLの開発者ブログ

ES5からWebpack管理下の関数を呼び出す方法

フロントエンドエンジニアの岡田です。 先日ご報告したとおり、LCLが運営する「夜行バス比較なび」では、jQueryで作られたサイトをReactへ置き換えています。 techblog.lclco.com

記事を書いた後も細々と置き換えを進めていますが、ページ単位での置き換えは、それなりにまとまった時間が取れないと進められません。 しかしSPAのように複数のjsがあるページでは、その時間が取れず、なかなか置き換えが進められませんでした。

そこで、LCLではもっと小さい単位で置き換える方法を試してみました。
手順は以下のとおりです。

  1. 既存の関数(ES5)を、ES2015へ変換してWebpack管理下のディレクトリへ移動
  2. 移動した関数をグローバル関数へ変更
  3. 既存のjsからグローバル関数を呼び出し

この方法を使うと、関数単位で置き換えが可能です。

ES2015への変換ツール

変換にはlebabを使いました。 lebab.io

具体的な方法

既存の関数(ES5)をlebabでES2015へ変換

// 変換後の関数
const hogehoge = () => {
};

移動した関数をグローバル関数へ変更

window.grobalHogehoge = hogehoge;

既存のjsからグローバル関数を呼び出し

// グローバルに露出している関数を呼び出し
window.grobalHogehoge();

グローバルに露出させるのであまりよくないかもしれませんが、あくまでも移行中の一つの手段としては使えます。この方法で関数をES2015化して、最後にきれいにReactへ置き換えたいと思います。

LINE botの返事をカルーセル形式にしてみました

フロントエンドエンジニアの岡田です。 先日作ったLINE botを改良しましたのでご紹介します。 techblog.lclco.com

前回の状態では、botはテキストメッセージを返すだけでした。 f:id:lcl-engineer:20170227051900p:plain:w382

これでは開いてみるまでどんなヨガ動画かわかりませんし、候補が1つしかないのもいまいちです。

そこで、Template messageを使って、候補の出し方を変えてみました。 Template messageには3種類あります。

  • Buttons
  • Confirm
  • Carousel

ドキュメント

今回はCarouselを使いました。 右にスライドすると、最大5つのヨガ動画が表示されます。
f:id:lcl-engineer:20170330060532g:plain

見た目が一気に豪華になりましたね。
今回はURLの他に、動画のタイトルやdescription, thumbnail等も出しています。

ソースコードは以下のとおりです。

// -----------------------------------------------------------------------------
// 定数の設定
const LINE_CHANNEL_ACCESS_TOKEN = 'あなたのChannl Access Token';
const GOOGLE_API_KEY = 'あなたのGOOGLE API KEY';
const DOMAIN = 'あなたのサーバーのドメイン: https://xxxxxxxx.herokuapp.com/';

// -----------------------------------------------------------------------------
// モジュールのインポート
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();

// -----------------------------------------------------------------------------
// ミドルウェア設定
app.use(bodyParser.json());

// -----------------------------------------------------------------------------
// Webサーバー設定
var port = (process.env.PORT || 3000);
var server = app.listen(port, function() {
  console.log('Node is running on port ' + port);
});

// -----------------------------------------------------------------------------
// ルーター設定
app.get('/', function(req, res, next) {
  res.send('Node is running on port ' + port);
});

app.post('/webhook', function(req, res, next) {
  res.status(200).end();
  for (var event of req.body.events) {
    if (event.type == 'message') {
      var requestHeaders = {
        'Content-Type': 'application/json',
        'referer': DOMAIN
      }
      //オプションを定義
      var options = {
        url: 'https://www.googleapis.com/youtube/v3/search?key=' + GOOGLE_API_KEY + '&part=snippet&channelId=UCd0pUnH7i5CM-Y8xRe7cZVg&q=' + encodeURI(event.message.text),
        method: 'GET',
        headers: requestHeaders,
        json: true
      }

      //リクエスト送信
      request(options, function(error, response, responseBody) {
        //コールバックで色々な処理
        var columns = [];
        for (var item of responseBody.items) {
          columns.push({
            "thumbnailImageUrl": item.snippet.thumbnails.medium.url,
            "title": item.snippet.title,
            "text": item.snippet.description ? item.snippet.description.substr(0, 60) : ' ', // title指定時は60文字以内,
            "actions": [{
              "type": "uri",
              "label": "動画を再生",
              "uri": 'https://www.youtube.com/watch?v=' + item.id.videoId
            }]
          });
          // carouselは最大5つのため、6つ以上の候補はカット
          if (columns.length === 5) {
            break;
          }
        }

        var body = {
          replyToken: event.replyToken,
          messages: [{
            "type": "template",
            "altText": "this is a carousel template",
            "template": {
              "type": "carousel",
              "columns": columns
            }
          }]
        }
        var url = 'https://api.line.me/v2/bot/message/reply';
        var headers = {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
        }
        request({
          url: url,
          method: 'POST',
          headers: headers,
          body: body,
          json: true
        });
      })
    }
  }
});

以上です。 更なるパワーアップを目指して、改良していこうと思います。

RailsでPaperclipを利用して、画像に透かし(ウォーターマーク)を付与する

弊社で運営している「バスとりっぷ」というメディアでは、Paperclipを利用して画像のアップロードを行っています。Paperclipでは、サムネイルの作成やリサイズ・背景色の変更などを行っていましたが、透かし(ウォーターマーク)の付与にも対応しました。

www.bushikaku.net

ウォーターマークの付与イメージ

画像に透かし(ウォーターマーク)を付与するには、透かし画像を用意し元画像に重ね合わせます。

f:id:lcl-engineer:20170326200650p:plain

実装方法

paperclipを拡張してもできますが、paperclip-watermarkというgemを利用しました。

GitHub - vikewoods/paperclip-watermark: module PaperclipWatermark

gem 'paperclip-watermark'

paperclipの設定に、以下のようにwatermarkの設定を追加するだけです。これでアップロードしたファイルに、ウォーターマークが付与されます。

class SampleImage < ActiveRecord::Base
  has_attached_file : attachment,
                    :processors => [:watermark],
                    :styles => { 
                            :original => { :watermark_path => "#{Rails.root}/watermark.png",:position => "southwest"}
                     }

paperclipの利用方法については、下記のページを参照ください。

Thumbnail Generation · thoughtbot/paperclip Wiki · GitHub

ウォーターマーク付与位置の変更

ウォーターマークを付与する位置は、positionを指定することで変更できます。

:original => { :watermark_path => "#{Rails.root}/watermark.png",:position => "northwest"}

以下の9種類の位置が指定できるようになってます。

  • northwest 左上
  • north 中央上
  • northeast 右上
  • west 中央左
  • center 中央
  • east 中央右
  • southwest 左下
  • south 中央下
  • southeast 右下

さらに「geometry」を指定すれば、上記の基準位置からピクセル単位で調整できるようです。(未検証)

ウォーターマークを外す

ウォーターマークを付与したくない画像に対して付与してしまった場合など、ウォーターマークを外したい場合もあります。その時は、以下の手順で復元します。

  • ウォーターマーク付与時に、元画像を別名で保存する
  • ウォーターマークを外す場合は、元画像を付与済みの画像に上書きする。

別名保存は、paperclipの指定で簡単にできます。

:styles => { 
                   :original_file_backup => {},
                   :original => {
                         :processors => [:watermark],
                   ・・・
                 }
}

別名で保存した画像が、インターネット上で誰にでもアクセスされてしまうと、結局ウォーターマークが付与されていない画像を取得することが可能になってします。paperclipでは、S3の権限も簡単に制御可能なので、以下のようにアクセス制限を行っています。

:s3_permissions => {:original_file_backup => :private},

既にアップロード済み画像に付与する

既にアップロード済みの画像に対しても、paperclipで再処理することが可能です。処理対象のmodelに対して、reprocessを実行します。

sample_image = SampleImage.find(123)
sample_image.reprocess!

まとめ

実際には、ウォーターマークを付与する・しないを画像によって振り分けているため、細かい判定をロジックを行っています。 その辺は結構手間がかかってますが、ウォーターマークの付与は、paperclip-watermarkを使って簡単に実現できました。