sitateru tech blog

sitateru tech blog

シタテルの技術やエンジニアの取り組みを紹介するテックブログです。

2018年12月3日月曜日

HDEさんのオフィスに遊びに行ってみた!

12月 03, 2018

こんにちは!
シタテル株式会社CTOの和泉です。

HDEさんのオフィスに遊びに行ってみた!

https://www.hde.co.jp/

images/image-0.png

HDEはクラウドセキュリティ、メールのSaaSサービスを展開している企業で、創業から20年以上の歴史があります。

先日参加した、SaaSカンファレンスで汾陽(かわみなみ)さんにお会いして、遊びに行かせてください!のノリで押しかけました。

丁寧すぎる道案内

渋谷駅からHDE HQ までの順路

自社ブログに道順を写真付きで乗っけるというアイディア!
(私が知らないだけで珍しくないのかも?)

images/image-1.png

GoogleMapで行こうかと悪魔がよぎりましたが、
ブログの説明と写真を頼りに渋谷駅ハチ公前から行ってみました。

これが、RPGみたいで楽しい。写真と道の形状や看板を見比べたりしながら、迷うことなくとたどり着けました!

受付からのドクターペッパー

受付のオープンスペースの写真撮り忘れた!

待ち合わせの時間に受付を訪れると、素敵な展望のオープンなスペースに汾陽さんがおられました。

そして真っ先に紹介されたのが、、、

ドクペ!ドクペ!ドクターペッパー!

images/image-2.png

の自動販売機!!

社員と 来訪者はなんとただで 飲める。驚愕!!

images/image-3.png

いただきました。

どうしてこうなった - 経緯

  • 社長がドクペが好きだった
  • 社員が6缶ぐらい入って冷やせる自動販売機を社長の誕生日にプレゼントした
  • 社員に好きに飲んでいいよと開放
  • 社員が増えてきて冷蔵庫にドクペがストックされるようになる
  • 社長が自腹で無料で提供し続ける限界が訪れる
  • 福利厚生の一部としてやろう
  • 自社ラベルのドクペ作りたかったけどライセンス許可(本国)おりなかった
  • 自販機ならいける?いける?いけた!

みたいな。

何この素敵な会社!

執務スペース

images/image-4.png

受付もそうでしたが、オープンで素敵なオフィス!

週に2回はランチが出たり、毎週のようにピザパーティーが開かれたりしているようです。

でも、集中部屋やスペースもきちんと完備されていて 個人の特性に配慮されてるなーと感じました。

images/image-5.png

真ん中に写っている着物の方が小椋社長。

そしてこの会社、外国人エンジニアがかなりいます。社内公用語も英語化しています。

グローバルインターンシッププログラムというものを作って、アジアや世界各地からのエンジニア候補を受け入れています。

その結果として優秀な方が多く採用できているとのことでした。したたか!

これは、シタテルも真似したい!真似するぞ!

20年を超える歴史のある企業で、汾陽さんも20年以上勤務されているとのことで、紆余曲折あった話などいろいろと教えていただきました。

まとめ

以上、ドクターペッパーのことしか伝わっていないのではないかという不安もありますが、本当にオープンで素敵な企業という印象を受けました。

社長がCTOでテクノロジー出身というのもあるのか、エンジニアが働きやすい環境が整備されていて。今後のシタテルのオフィスを考えていく上でもとても参考になりました。

ありがとうございました!!

2018年11月30日金曜日

AWSLambdaとServerlessを使ってみる[第2回]

11月 30, 2018

こんにちは、シタテルエンジニアの工です!

AWSLambdaとServerless第2回に入っていきます!
前回は、AWSLambdaとserverlessについてさらっと確認して
serverlessでServiceをcreateするところまでをやってきました。
AWSLambdaとServerlessを使ってみる[第1回]|sitateru tech blog

今回は実際にSansan OpenAPIを利用して名刺情報を取得してみます 🚀

SansanAPI


公式ドキュメント Sansan Open API

名刺API 名刺Set取得(期間指定)を使ってみます。
SansanAPIを利用するにはAPI Keyが必要です。
取得方法はこちらを参考にしてください。

作る

前回createしたServiceを利用して作っていきます。
現在この2ファイルが作成されている状態です。

  • serverless.yml
    • 各種設定を書いていきます
  • handler.js
    • 処理を書いていきます

axiosインストール

今回は、httpリクエストにaxiosを使用します。

$ npm install axios

インストールされました

.
├── handler.js
├── node_modules
│  ├── axios
├── package-lock.json
└── serverless.yml

serverless.yml

環境変数の設定

lambdaには環境変数を設定することができます。
SansanAPIKeyを環境変数として設定してみます。
serverless.yml

# you can define service wide environment variables here
  environment:
    SANSAN_API_KEY: xxx

環境変数を利用する場合は、このように取得できます。

process.env.SANSAN_API_KEY

handler.js

それでは処理を書いていきます。
lambdaの非同期処理とcallbackについてはこちらを参考にしてください。

名刺情報はお見せできないので、取得した件数を出力してみます。

'use strict';

const axios = require('axios')
axios.defaults.headers.common['X-Sansan-Api-Key'] = process.env.SANSAN_API_KEY
axios.defaults.headers.get['Content-Type'] = 'application/json'

module.exports.hello = async (event, context, callback) => {
  var updatedFrom = "2018-11-26T01:00:00Z"
  var updatedTo = "2018-11-27T21:00:00Z"

  await axios.get(`https://api.sansan.com/v2.0/bizCards?updatedTo=${updatedTo}&range=all&entryStatus=completed`)
      .then(function (response) {
        callback(null, response.data.data.length)
      })
      .catch(function (error) {
        callback(error)
      });
};

deploy

cliでdeployします。
serverlessを使わない場合、serviceを手動でzipにしてawsにdeployする必要があるのでとても面倒です...

$ serverless deploy -v

deployが完了したら情報が出力されます。

...
Serverless: Stack update finished...
Service Information
service: hello-sansan
stage: dev
region: us-east-1
stack: hello-sansan-dev
api keys:
  None
endpoints:
  None
functions:
  hello: hello-sansan-dev-hello

lambdaの関数のところにも表示されています。

環境変数も設定されています。

実行

こちらもcliで実行します。

$ sls invoke -f hello
16

件数が取得できました。

おわりに

今回はlambdaからSansanOpenAPIを利用して名刺情報をゲットしてみました!
lambdaはサーバーのこと気にせずに処理だけ書けば良く、serverlessはcliでごにょごにょできるし、とても便利です!
sitateruではlambdaを使って、SansanとHubSpotの同期を自動化しています。
どこかのタイミング第3回としてserverless-offlineを使用したlambdaの開発についても書こうと思います。

2018年11月29日木曜日

Vueでwatchが動いてくれない

11月 29, 2018

こんにちは、シタテルの藤本です。
主にSCS(Sitateru-Control-System)という生産管理システムのバックエンド(Rails)を担当しています。

SCSではRailsでのViewは基本hamlで書かれていますが現在Vueへと移行しようとしています。
最近私もVueを触る機会があり、watchを使用していてハマってしまったところを2点ほど書いておきます。

  1. Objectをwatchするときはdeepをつけよう
  2. = で追加してはリアクティブになりませんよ

1. Objectをwatchするときはdeepをつけよう

以下のようにdata(user_nameusers)がいて
その値が変更されたことを監視するようにしたい場合にwatchを以下のように書きます

  data () {
    return {
      user_name: '太郎',
      users: {}
    }
  }

  watch: {
    user_name: function (new_value, old_value) {
      alert(`user_name watch!!${this.user_name}`)
    },
    users: function (new_value, old_value) {
      alert(`users watch!!${this.users['name']}`)
    }
  }

  methods: {
    changeJiro () {
      this.user_name = '二郎'    
      this.$set(this.users, 'name', '二郎')    
    },
    changeSaburo () {
      this.user_name = '三郎'    
      this.$set(this.users, 'name', '三郎')
    }
  }

上記の状態でchangeJiro or changeSaburoを呼び出すと
user_nameしか監視されません。

Objectを監視したい場合には以下のようにwatchに対してdeepオプションを付与する必要があります。

    users: {
      handler: function (new_value, old_value) {
        alert(`users watch!!${this.users['name']}`)
      },
      deep: true
    }

公式の以下に記載があります。
watchについて

2. = で追加してはリアクティブになりませんよ

もう1点ハマったところでObjectの値をセットする際の処理の仕方です。

  1. とほとんど同じ内容ですがmethodsでのusersへのセットをuser_nameと同様に=に変更しています。

    data () {
     return {
       user_name: '太郎',
       users: {}
     }
    }
    
    watch: {
     user_name: function (new_value, old_value) {
       alert(`user_name watch!!${this.user_name}`)
     },
     users: {
       handler: function (new_value, old_value) {
         alert(`users watch!!${this.users['name']}`)
       },
       deep: true
     }
    }
    
    methods: {
     changeJiro () {
       alert(`check!! ${this.users['name']}`)
       this.user_name = '二郎'
       this.users['name'] = '二郎' //←ここが=でのセットに変わっている
     },
     changeSaburo () {
       alert(`check!! ${this.users['name']}`)
       this.user_name = '三郎'
       this.users['name'] = '三郎' //←ここが=でのセットに変わっている
     }
    }

こちらも同様にchangeJiro or changeSaburoを呼び出すと
user_nameしか監視されません。
ただしusers['name']を確認してみると値はセットされています。

原因としてはVue.jsはプロパティの追加または削除を検出できず、リアクティブになっていないため監視できないとのことです。
対応方法としてはわかっているもので2つあります。

  1. 最初から定義しておく
  2. setにて追加を行う

1. 最初から定義しておく

Objectのプロパティを最初から定義しておくことで監視することが出来るようになります。
dataに定義しているusersを以下のように修正します。

users: { name: '太郎' }

2. setにて追加を行う

こちらは1. に書いてある通りで、methodを以下のように修正します。

changeJiro () {
  this.user_name = '二郎'    
  this.$set(this.users, 'name', '二郎')    
},
changeSaburo () {
  this.user_name = '三郎'    
  this.$set(this.users, 'name', '三郎')
}

もっと詳細については以下をご参照ください。
リアクティブの探求

また配列に関しても同様の注意が必要で以下に詳しく書かれております。
配列の変化を検出

終わりに

1人でわりといい時間考え込んでましたが周りに聞いたら即解決しました。
悩みすぎるのはよくないと反省すると共に心強い味方がいることに大変感謝した次第です。
またVueの公式は大変日本語でも充実しているのでしっかり読もうと思いました。

以上です、これからVueをご利用される方の助けになれば幸いです。

2018年11月28日水曜日

SPECデザインの舞台裏 [公開と非公開編 vol.1]

11月 28, 2018

こんにちは、デザイナーの藤村です。
SPECという受注生産のためのサービス開発に今年の2月頃から関わっています。
→ SPECの説明はこちらで

SPECではサービスの一環として、受注販売用Webサイトを作成するためのCMSを提供しています。私は仕様検討と共にCMSの画面デザインをしています。今回は最近のなかで一番リテイクの多かった、サイトの公開/非公開機能のUIができるまでをご紹介します。

実装が決まるまで

当初SPECのCMSには「サイトを非公開にする」という機能がありませんでした。いずれ必要になるけれど、優先すべき開発が他にあるという判断で実装待ちになっていました。クライアントさんには、要望があればシタテルに直接ご連絡をいただくようお願いしていました。

が、そうは言っても間違えて公開してしまう、ということは十分に考えられます。開発チームでも議論を重ね、いよいよ実装することが決定しました。

▼当初のCMS画面

まだ公開していないサイト。公開ボタンがあります。

公開済みのサイト。ステータス表示として「公開中」はあるけれど、非公開にできるUIはない。

デザイン第一弾:とにかくシンプルに。

まずはこんな風に作りました。

画面の要素を最小限に、操作に迷わないように操作ポイントの要素もできるだけ少なく、という方針で作りました。「非公開」と書いてあるところをクリックすると選択肢が出てきます。どの状態に変更したいか選ぶと、ダイアログで本当に公開するのか聞いてきます。OKすると公開されます。ダイアログは「うっかり公開」を防ぐために付けました。

▼こんな感じ

ちなみにSPECチームでは、デザインを作ったらすぐにチームメンバーに見てもらいます。エンジニアもプロダクトオーナーもみんな見ます。気になった人がコメントを入れます。今回は画面遷移の変更というほどではなかったので、一気にデザインを起こしzeplinにアップしました。

表示と状態の微妙な関係

エンジニアから早速ツッコミが入りました。

「表示と状態にズレができる瞬間があるため、作りが複雑になり今のスケジュールでは工数が足りない。再考してほしい」。最初は理解ができず「??」となっていました。が、よくよく考えてわかりました。

①のときは、表示上は「非公開」になりますが、まだ本当にサイトが非公開になったわけではありません。保留の状態です。ダイアログで「非公開にする!」とした時点で、実際にサイトが非公開になるのです。つまり、①〜②〜ダイアログのあいだは、公開状態と表示にズレが出ているわけです。

表示とは = 画面に何と書いているか
状態とは = 実際にサイトが公開しているのか、非公開なのか

これがなぜ複雑なのか?
プログラム上、やらなければいけない制御が増えるから、です。

「非公開にしたい!」って言ったけど、やっぱりもうちょっと公開しておこうとなった場合、表示は「非公開」に戻っていなければいけません。「やっぱり非公開するのやめた」と言ったはずなのに表示が公開のままだったら、使っている人は混乱してしまいますね。使う人は気づかない部分ですが、こういう処理をきちんとしておくことはとても大事なことです。

実装の複雑さ、ユーザーへのケア、デザインするときに考えておくべきポイントはたくさんあります。フィードバックをもらったことで気づくことができました。ということで、チームメンバーのアドバイスをもとにデザインを修正しました。

デザイン第二弾:ボタンひとつで変わる世界

意外とサクッといきました。

▼こんな感じ

「変更」というボタンを追加しただけです。

第一弾のデザインが複雑だったのは、最初の画面で表示の切り替え「だけ」を行ったことが原因でした。であれば、表示と状態を「同時に」行えるUIにすればいいのです。最初の画面ではダイアログを呼び出すだけにし、ダイアログ上で操作するように変更しました。ダイアログでの操作ひとつで、状態と表示を同時に切り替えられるようになりました。ステップ数も、最大4つあったのが3つに減りました。万歳。

さあ、実装だ!、と思っていた矢先、仕様追加の依頼が入ってきました。。

(次回につづきます)

2018年11月27日火曜日

Gulpでラクに画像を圧縮!

11月 27, 2018

こんにちは!
シタテル株式会社UI/UXデザイナーの堤です。

webサイトやメルマガで使う画像やバナーなどを作成する際に、サクッと手元で圧縮できる環境を用意しておくと便利だったのでご紹介します。
今回はgulpとパンダでお馴染みのTinyPNGやWebPに関するプラグインを使用して圧縮・生成します。

今回やりたかったこと

  • とにかく手軽に圧縮したい
  • 圧縮とともにWebPも一緒に生成したい

導入方法はNode.jsがインストールされている環境でプラグインをインストールします。

$ npm install gulp gulp-tinypng-compress gulp-webp

ディレクトリ構成

gulpfile.js
└images
 ├ src //圧縮元の画像ファイル置き場
 └ dist //圧縮後の画像ファイル置き場

gulpfile.js

const gulp     = require('gulp');
const tinyping = require('gulp-tinypng-compress');
const webp     = require('gulp-webp');

const paths = {
    srcDir : 'images/src',
    dstDir : 'images/dist'
}

gulp.task('webp', function () {
    gulp.src(paths.srcDir + "/**/*.{svg,gif,png,jpg,jpeg}")
        .pipe(webp())
        .pipe(gulp.dest(paths.dstDir));
});

gulp.task('tinypng', function () {
    gulp.src(paths.srcDir + "/**/*.{png,jpg,jpeg}")
        .pipe(tinyping({
            key: 'xxxxxxxxxx' // TinyPNGのAPI Key
        }))
        .pipe(gulp.dest(paths.dstDir));
});

gulp.task('default', ['tinypng', 'webp']);
  • TinyPNGのAPI Keyは、Tiny PNGもサイトから取得します
  • 無料で月500枚まで圧縮可能です

実行方法

$ gulp

// tinypngのみなら
$ gulp tinypng

// webpのみなら
$ gulp webp

WebPについて

Googleが開発している画像フォーマットです。
少し前まではWebKitを用いたウェブブラウザだけだったのですが、最新のEdgeや私が愛用するFirefoxはver65から対応するようで今後普及していきそうな気がしています。
https://caniuse.com/#search=webp

導入方法

まだ対応していないブラウザもあるため、cssで指定している背景画像の分岐はmodernizr.jsを使用して判別します。
modernizr.jsを参照すると対応ブラウザはclass="webp"、非対応ブラウザはclass="no-webp"というclassを自動的に付与してくれます。
あとはhtmlとcssを下記のように記載しておけばそれぞれ対応している画像を読み込んでくれます。

.webp .image {
    background-image: url("/images/background-hoge.webp");
}

.no-webp .image {
    background-image: url("/images/background-hoge.jpg");
}
<picture>
    <source srcset="/images/hoge.webp" type="image/webp">
    <img src="/images/hoge.jpg" alt="">
</picture>

まとめ

圧縮方法や使うプラグインはさまざまあり、外部サービスでも簡単に圧縮できるサービスはあるので、自分に合うものをいくつか試すのが良いかなと思っています。

最近LPで使用している画像を圧縮したのですがこれぐらい圧縮できました!

  • 元の画像 3.3MB ※そもそもがデカすぎやしないかいというのはありますが、、
  • TinyPNG 909KB
  • WebP 446KB

通信環境はどんどん良くなっているものの、初回の描画までに時間がかかると離脱に繋がるため、これからも面倒くさがらずに(もっと楽な圧縮方法を追求しつつ)圧縮していきたいと思います!

2018年11月26日月曜日

Active Storage移行記:バリデーション編

11月 26, 2018

こんにちはあさのです。

今回は、Ruby on Railsのファイルアップロード機能をPaperclipからActive Storageに移行したときの話を書こうと思います。

Paperclip

Paperclipはファイルアップロード用のGemです。

https://github.com/thoughtbot/paperclip

比較的導入が簡単であり、アプリケーションにファイルアップロード機能をつけるためによく使われています。

ですが、現在は "Deprecated" とされていて、Rails 5.2から標準機能の1つとなったActive Storageへの移行が推奨されています。

Active Storage

Active StorageはRailsのバージョン5.2から標準搭載されている機能の一つで、Paperclipと同様Active Recordのオブジェクトと紐づけたファイルアップロード機能を提供しています。

https://github.com/rails/rails/tree/master/activestorage

シタテルでもRails 5.2へのアップグレードが落ち着いたところでこの移行を行いました。

そんなわけで、移行にあたっていくつか注意点となったところを振り返っていきたいと思います。

今回はモデルのバリデーションの話です。

Active Storage でのバリデーション

Papaerclipにはバリデーション機能があります。

以下はiconという名前で扱う添付ファイルのバリデートをする例で、モデル内にこう書いておけば保存時に自動でチェックされます。

見てのとおりですが、ファイル形式は[jpg, jpeg, gif, png]のどれか、ファイルサイズが10MB以下である必要があります。

  validates_attachment :icon,
                       content_type: {
                         content_type: [
                           'image/jpg',
                           'image/jpeg',
                           'image/gif',
                           'image/png'
                         ]
                       },
                       size: {
                         less_than_or_equal_to: 10.megabytes,
                         message: I18n.t('errors.messages.file_too_large')
                       }

しかしActive Storageにはこのようなバリデーション機能はありません。

じゃあどうするんだ!という話ですが、素直にActive Recordのカスタムバリデーションを使いましょう。
これで同じようなことができます。

  validate :validate_icon

  def validate_icon
    return unless icon.attached?
    if icon.blob.byte_size > 10.megabytes
      icon.purge
      errors.add(:icon, I18n.t('errors.messages.file_too_large')
    elsif !image?
      icon.purge
      errors.add(:icon, I18n.t('errors.messages.file_type_not_image'))
    end
  end

  def image?
    %w[image/jpg image/jpeg image/gif image/png].include?(icon.blob.content_type)
  end

ちなみに、

  • attached? はファイルが存在するかどうか
  • blob.byte_size はファイルサイズ
  • blob.content_type はファイルタイプ

を取得しています。


ということでActive Storageのバリデーションについて書いてみました。

Paperclipからの移行はいろいろと作業があったので他のトピックについてもまた書いていきたいと思います。

それでは。

2018年11月24日土曜日

オンライン衣服生産システム[第4回] - マイオペレーター

11月 24, 2018

縫製工場向けシステム - マイオペレーター

こんにちは!
シタテル株式会社CTOの和泉です。

シタテルのシステム紹介4回目。

第2回までにマイアトリエを使ってユーザーが衣服生産をリクエストし、SCSでコンシェルジ内容を管理することを紹介しました。

今回はついに、実際に衣服を作る縫製工場のための生産管理システム、マイオペレータについてご紹介します。

アイテムの生産管理

マイオペレーターはシタテルと取引をしていただいている縫製工場向けに無料で提供しています。一般的なウェブブラウザやスマートフォンで使えます。

下の図が、マイオペレータにログインした後のトップ画面です。

進行中の生産アイテムの一覧が表示されています。この一覧からそれぞれのアイテムの生産管理ページへと飛べます。
一件、アイテム生産ページを表示して内容を見てみましょう。

注目して欲しいのは左上のタブ、それとメッセージの表示画面です。マイアトリエやSCSと同じように、このマイオペレータでもアイテムの詳細な情報を閲覧しながら、コンシェルジュに簡単にメッセージを送信できます。
左上のタブは切り替えることで、アイテムの生産工程ごとの情報を表示されます。

この左上のタブの名前、見覚えがありませんか?
SCSでご紹介した"ボックス"について、皆さん覚えているでしょうか?

お気付きの通り、SCSのボックスとマイオペレータのタブは連動しています。

SCSで[概要〜見積もり]ボックスが作られれば、マイオペレータで[相談]タブが作成され、
SCSで[サンプル作成ボックス]が作られれば、マイオペレータでは[サンプル]タブが作成されます

このように連動して、SCSで入力された情報がマイオペレータで連携・表示されています。



スケジュールや発注仕様書、工賃など、衣服生産に必要な情報はこのタブを切り替えながら一通り確認できます。その他細かい部分はメッセージのやりとりで簡単にコンシェルジュに相談可能です。
随時この画面に情報が反映され、またサプライヤー側でも進捗を入力することで、アイテムの生産管理が行えます。

工場全体のスケジュール管理

1つずつのアイテムの生産管理画面に加えて、それらをまとめて管理するカレンダー機能も用意してあります。

カレンダーを使えば、全てのアイテムで予定されている期日についてカレンダー形式で表示して工場全体のスケジュールを見られます。

稼働状況通知機能

縫製工場には繁忙期と閑散期があります。突然依頼が入ってくることも珍しくありません。

画面から月ごとの稼働状況を選択すると、コンシェルジュにメッセージが送られます。直近の稼働状況を事前にここから連絡しておくだけで、シタテル側に繁閑を伝えることができます。簡単に受注相談を調整できます。

トーク

トーク機能ではアイテムを限定せずにスケジュールの調整やその他全体的な(雑な言い方をするなら”ざっくばらん”に、”どんなことでも”)やりとりを行うことが出来ます。システムの改善要求などをトークで教えてくださる工場もあり、とても助かっています。

まとめ

今回の記事でシタテルのシステム紹介は一旦終わりですが、シタテルではシステムの開発・改修を日々行なっています。

このシリーズ記事を書いている間にもSCSと外部ビジネスツールとの連携や工場向けのマイオペマガジンの実装がされました。これからも追加機能がどんどんリリースされます。

引き続き紹介していきますのでお楽しみに!