astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

マクロって言ったり、関数って言ったり、Functionだったりする何か。~Google Spread Sheet~

f:id:astamuse:20170425195524j:plain

お久しぶりでございます。scalaでバックエンドを開発しているaxtstar(@axtstart)でございます。

今回は、弊社の勉強会で話したネタを少しアレンジして記載します。

Google Spread Sheet

前々回のエントリーでExcelマクロには○○とか話をしていてナンですが、 業務では結構G Suite上のGoogle Spread Sheetなどを通して、仕様やデータを検証することも多く、 ローカルでのExcelでの作業より、G Suite上での作業の方が徐々に多くなってきています。

GoogleSpreadSheetを使った業務改善の話はこちらのエントリーでも 取り上げていますが、ここではもっとプリミティブなtips的なSpreadSheetの関数を紹介してみたいと思います。

ImportRange

こちらは他のSpreadSheetのデータを参照することができる関数です。

元のSheetを汚したくない場合などによく利用しています。 データのレンジ範囲をセル上に書けば、その範囲を全部拾って来ます。 Excelにはこのような、記載したセル以外に値を展開する関数というのはみたことがありませんでした。

例えば、ターゲットのスプレッドシートが下記のようなURLの場合、

https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxx/

f:id:astamuse:20170423112752p:plain

対象スプレッドシートのスプレッドキー(上記の場合:xxxxxxxxxxxxxxxxx)とシート・レンジを指定します。

=ImportRange("xxxxxxxxxxxxxxxxx","シート6!A1:F12")

1度だけ、アクセスの許可をする必要があります。

f:id:astamuse:20170422234819p:plain

なお、範囲上に展開する関数は、他のデータが展開先に存在する場合、エラーになります。

f:id:astamuse:20170422235031p:plain

すると、下記のように、C4:H15の範囲にシート6!A1:F12の内容が展開されます。

f:id:astamuse:20170423121306p:plain

上記を日直の持ち回り表というようにみていただくと、 追加でExcelライクな関数を設定することで次の日直を自動で算出することが可能です。 (この場合はGさんが次の日直) まぁ少し例としては微妙ですが。。。

f:id:astamuse:20170423130055p:plain

SpreadSheet API

またGoogleスプレッドシートはAPIからアクセスすることができますので、 メッセージに応じて、slack上に日直を表示するというような、botを作ることもできます。

f:id:astamuse:20170423235020p:plain

こちらにslack botの scala で実装したサンプルを上げておきました。

スクリプトエディタ

また、ほんのちょっとした処理をスクリプトエディタで記載するのもいい手です。

例えば、文字列をURLエンコードする関数は、存在しないのですが、 スクリプトエディタだと↓簡単に記述できます。

下記のようにスクリプトエディタを開き、

f:id:astamuse:20170425185221p:plain

こちらに

f:id:astamuse:20170425185442p:plain

下記を張り付ければOK。

function encode(value) {
  return encodeURIComponent(value);
}

function decode(value) {
  return decodeURIComponent(value);
}

スクリプトエディタ*1は、JavaScript1.6をBaseにつくられており、 Array、Date、RegExp、MathといったObjectが利用可能です。 ただし、Googleのサーバで動作するため、Window APIは利用不可。

ImportXML

URLを指定することでXMLをパースして取得することができます。 実際にはHTMLでもかなり取得してくれます。 ただ、認証など入力することはできないため、ログインが必要なデータは取得できないようです。

例えば下記で、本ブログのキーワードタグを取得できます。

=ImportXML("http://lab.astamuse.co.jp/","//ul[@class='hatena-urllist']")

f:id:astamuse:20170423181907p:plain

こちらを先ほど作成したencode関数と下記関数で、キーワードタグに対して

 
関数用途
Transpose 転置
=Transpose(ImportXML("http://lab.astamuse.co.jp/","//ul[@class='hatena-urllist']"))
REGEXEXTRACT 正規表現によるデータの取得
=REGEXEXTRACT(A2,"( \([1-9]+\))")
Substitute 文字列置換
=Substitute(A2,B2,"")
encode URLエンコード。スクリプトエディタで作成した関数
=encode(C3)

URLを↓下記のように生成できます。

f:id:astamuse:20170423190011p:plain

さらに生成したURLにImportXmlを適用し、タイトルを取得してみました↓

f:id:astamuse:20170423193443p:plain

ただあまり処理数が多いと以下のようにエラーになります。

f:id:astamuse:20170423231554p:plain

今回はこの辺で。

最後に

いかがでしたか?ローカルで動作するExcelとは違った機能を紹介してみました。

またここでは紹介はしなかったですが、Gmail等のGoogleのサービスへの連携も比較的簡単にできるため重宝しています。

アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ 採用サイト からご応募ください。

参考にしたサイトなど

Google スプレッドシートの関数リスト

Google Apps Script ドキュメント

Google API Client Libraries JAVA編

*1:正確にはGoogle Apps Script

複数サイトを運営する上でのフロントエンドのちょっとしたノウハウ

こんにちは。 デザイン部でフロントエンドエンジニアをしているkitoです。
弊社は、転職ナビというドメインの異なるサイトを300サイト以上運営しています。レアケースであるとは思いますが、運営上のノウハウの一部をご紹介したいと思います。
今回はスタイルシートを量産するフローを書かせて頂きます。

jsonファイルから複数のcssを作成する

転職ナビは数百サイトありますが、ひとつのアプリケーションでほぼ同じテンプレートが使われ、静的なアセットは原則として共有されています。
しかし、それぞれのサイトに固有のスタイルシートを適用しなければならない場面がどうしてもあります。 弊社では、サイト固有のスタイルシートをjsonファイルのデータから量産することでその問題に対処していますが、そのjsonの元ファイルはコミュニケーション上使い勝手のよいcsvファイルでデータが管理されています。 従って、まずcsvファイルをjsonに変換します。

下記のようなcsvファイル、prefecture.csvがあったとします。これをprefecture.jsonに変換します。
domainは文字通りサイトドメインで、R,G,BはそれぞれサイトカラーのRGBの値です。

domain R G B name
hokkaido 0 11 111 北海道
aomori 32 66 66 青森
iwate 4 15 43 岩手
miyagi 55 180 8 宮城
akita 10 20 30 秋田
yamagata 90 0 0 山形
fukushima 10 10 10 福島
ibaraki 40 30 12 茨城
tochigi 68 11 54 栃木
gunma 250 100 40 群馬

csvtojsonというモジュールを使いたいので下記コマンドでインストールしてください。

npm install --save csvtojson

以下作成していくディレクトリは以下のようになります。

├── package.json
├── converter.js
├── template.js
├── prefecture.json
├── Gruntfile.js
├── dev
│   └── unique
│        ├── akita
│        │   └── styles
│        │        ├── sp_unique.scss
│        │        └── unique.scss
│        ├── aomori
│        │   └── styles
│        │        ├── sp_unique.scss
│        │        └── unique.scss
│        以下略
├── tasks
│   └── prefecture_styles.js
├── unique
│   ├── akita
│   │   └── styles
│   │        ├── sp_unique.css
│   │        └── unique.css
│   ├── aomori
│   │   └── styles
│   │        ├── sp_unique.css
│   │        └── unique.css
│   以下略
└── node_modules

converter.jsを作成して下記のコードを実行してください。csvファイルをjsonに変換します。

var fs = require('fs');
var csv = require("csvtojson");
var converter = csv({});
var csvFile = './prefecture.csv';
var jsonFile = './prefecture.json';

fs.createReadStream(csvFile).pipe(converter);
converter.on("end_parsed", function (jsonArray) { 
  fs.writeFile( jsonFile, JSON.stringify( jsonArray, null, '    ' ),'utf8');
  console.log('csv to json is complete !');
});

下記のようなprefecture.jsonが作成されたと思います。

[
    {
        "domain": "hokkaido",
        "R": 0,
        "G": 11,
        "B": 111,
        "name": "北海道"
    },
    {
        "domain": "aomori",
        "R": 32,
        "G": 66,
        "B": 66,
        "name": "青森"
    },
    以下略
]

弊社はsassを導入しているのでprefecture.jsonからgruntを通じてsassを作成し、それを元にcssを作成するフローになります。
二度手間のように感じるかもしれませんが、他のsassをimportしたり、ユニークなカラーを変数に設定できたりと、使い勝手がよいからです。

sassを量産するprefecture_styles.jsというGruntプラグインを作成します。
tasksディレクトリに、prefecture_styles.jsを作成して下記コードを記入してください。prefecture.jsonと後で作成するテンプレートを元にsassを量産します。

module.exports = function(grunt) {
    var fs = require('fs');
    var json = grunt.config('prefecture_styles').json;
    var data = JSON.parse(fs.readFileSync(json, 'utf8'));

    grunt.registerTask('prefecture_styles', 'task', function() {

        var dir = grunt.config('prefecture_styles').dir;
        var stylesDir = grunt.config('prefecture_styles').stylesDir;
        var name = grunt.config('prefecture_styles').name;
        var template = grunt.config('prefecture_styles').template;
        var sasstmp = require(template);

        Object.keys(data).forEach(function(key, index) {
            var domain = data[key]["domain"]
            var R = data[key]["R"]
            var G = data[key]["G"]
            var B = data[key]["B"]
            var sassObj = new sasstmp(R,G,B);

            name.forEach(function(val, index) {
                grunt.file.write(dir + domain + stylesDir + name[0],sassObj.pc , function(err) {});
                grunt.file.write(dir + domain + stylesDir + name[1],sassObj.sp , function(err) {});
            });
        });
        console.log('generated sass!')
    });
};

grunt.config('prefecture_styles').〇〇で、Gruntfile.jsから設定を読み込んでいます。
jsonはスタイルシートの値が入ったjsonファイル(ここではprefecture.json)、dirはベースとなるディレクトリ、stylesDirはsassが入るディレクトリ名、nameはsassの名前、templateは量産するsassのテンプレートになります。
Object.keys(data).forEachでprefecture.jsonから値を取り出して、それぞれ変数に代入し、grunt.file.writeでpc用とsp用のsassを作成&記述しています。

元となるテンプレート、template.jsを作成します。

var sasstmp = function(R,G,B) {
  this.pc = '@charset "utf-8";\n' + '$uniqueSiteColor:rgb(' + R + ',' + G + ',' + B + ');\n' + '.siteColor{ background-color:rgb(' + R + ',' + G + ',' + B + ')};\n';
  this.sp = '@charset "utf-8";\n' + '$uniqueSiteColor:rgb(' + R + ',' + G + ',' + B + ');\n' + '.siteColor{ background-color:rgb(' + R + ',' + G + ',' + B + ')};\n';
}

module.exports = sasstmp;

次にGruntfile.jsの設定に移ります。
(gruntをインストールについては割愛します。)
下記gruntプラグインをインストールしてください。

npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-clean --save-dev

grunt-contrib-compassはsassをcssにコンパイルするプラグイン、grunt-contrib-cleanはファイルを削除をするプラグインです。

Gruntfile.jsを下記のように記述してください。 実際に業務で使うときのGruntfile.jsは、sassの変更をwatchしたりwebpackを使ってjsをバンドルしたりと他のタスクを追加することになりますが、今回は必要最小限の構成にしてあります。

var grunt = require('grunt');

module.exports = function(grunt) {

    grunt.initConfig({

        pkg: grunt.file.readJSON('package.json'),

        clean: {
            build: {
                src: [
                    '.sass-cache',
                    './dev/build/unique/'
                ]
            },
            release: {
                src: [
                    '.sass-cache',
                    './unique/**/styles/*.css'
                ]
            }
        },

        prefecture_styles: {
            dir: './dev/unique/',
            json: './prefecture.json',
            template: '../template.js',
            stylesDir: '/styles/',
            name: [
                'unique.scss',
                'sp_unique.scss'
            ]
        },

        compass: {
            prod: {
                options: {
                    sassDir: './dev/unique',
                    cssDir: './unique',
                    environment: 'production'
                }
            }
        }
    });


    //ローカルにあるprefecture_styles.jsを読み込んでいる。
    grunt.task.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-compass');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.registerTask('build', ['clean:release', ' prefecture_styles', 'compass:prod']);

};

grunt.initConfig({})の中に prefecture_styles.jsの設定を記述します。
compass:{}には prefecture_styles.js作成したsassをcssにコンパイルする設定を行っています。
grunt.task.loadTasks('tasks')は、コメントにあるように先程作成したprefecture_styles.jsをロードしています。
Nodeモジュールとして公開しておけば、grunt.loadNpmTasks(' prefecture_styles')で読み込めるでしょう。

下記でGruntを実行してください。

grunt build

uniqueディレクトリが作成され、それぞれのドメイン名以下に、unique.css、sp_unique.cssが作成されていれば成功です。
cssを量産するタスクを実施する際に、本稿を参考にして頂ければ幸いです。今回は以上になります。

アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

生産力を向上する非埋め込み型MVVMフレームワークAsta4jsの紹介

開発部の劉です。今回は弊社のフロントエンド開発に使われている自社フレームワークAsta4jsを紹介させていただきます。

Asta4jsとは何か?

Asta4jsは非埋め込み型によるテンプレートバインディングの仕組みを提供しています。それにより、ロジックを担当するjavascriptエンジニアとビジュアル設計とUIを担当するデザイナーは互いに作業を妨げることなく、各自のタスクを遂行できる斬新なフロントエンドMVVMフレームワークです。

github.com

Asta4jsはどうか?

分離されたテンプレートとバインディング

まず、簡単なサンプルソースでAsta4jsのバインディングを示します。

仮に、このようなHTMLファイルに対してバインドを行うとした場合を例にします。

  <span id="name-preview"></span>

下記がバインディングを行うロジックになります。

Aj.init(function($scope){  
  $scope.data = {};  
  $scope.snippet("body").bind($scope.data, {  
      name: "#name-preview" //bind the $scope.data.name to #name-preview in 1-way  
  });  
}); 

このサンプルソースのように、CSSセレクターを利用したデータバインディングの宣言をテンプレートファイルから抽出できています。そして、業務ロジックを担当するエンジニアは完全に独立してロジックを書くことができ、同時にビジュアル担当のデザイナーも自身の作業を進めることが出来ます。

実は、Asta4js自身のサンプルソースに、エンジニアとデザイナーが独立して作業することにより、生産力が素晴らしく向上出来たと称賛された例があります。 下記のリンクはAsta4jsの該当のサンプルプログラムのコードです:

http://astamuse.github.io/asta4js/examples/userList/userList.html

f:id:astamuse:20161004144242p:plain

上記の画面は弊社のデザイナーに設計してもらった画面ですが、その画面も最初は下記のように少しみすぼらしいものでした:

f:id:astamuse:20161004144249p:plain

弊社のエンジニアはUIの設計について得意ではなく、今回の目的もAsta4jsの利用方法を示すことだけなので、担当のエンジニアは必要なHTML要素を全部入れて、必要なjsソースを実装しておきました。その後、デザイナーは画面を綺麗にするために、下記のふたつをコミットしました。


SHA-1: 334859ae2785b68e8dfb7e043ffc73d777d44905

  • btn style modifired

examples\userList\userList.css examples\userList\userList.html

SHA-1: 00aa49bda5449299cd2f0b0fc0fdf40dc66fd223

  • style added

examples\userList\userList.css examples\userList\userList.html


このコミット履歴から見えるのは、デザイナーが一人で画面の設計と実装を完成したことであり、それと同時にjs側の担当エンジニアはフレームワークのバグと格闘しており、サンプルアプリの画面実装についてまったく手を入れていないという完璧な開発作業分担になっています。

Asta4jsのもっと詳しい説明は公開されているgithubのページにあります。詳細なユーザガイドもwikiの形式でgithubに載せています。ユーザガイドは6ページしかないですが、すでに「詳細」になっています。Asta4jsが簡単であるという何よりの証拠です。

web component

FacebookのReactjsとGoogleのPolymerの発表に従い、componentに基づく開発はフロントエンドに対して大きな話題になっていましたが、幸いなことにweb componentという最新なweb api specに対しても、特殊な対応をせずにすんでいます。 そして、Asta4jsがコンポーネントに基づく開発スタイルも完璧にサポートしています。

さらに、Asta4jsはPolymerのビルトインコンポーネントを直接利用できます。下記のリンクはAsta4jsでPolymerのpaper-slideコンポーネントを利用するオンラインサンプルです。

http://astamuse.github.io/asta4js/examples/webComponent/polymer/slider.html

Asta4jsは標準のweb apiでweb componentをインテグレーションしていますので、Polymerだけではなく、すべてのweb component準拠な外部コンポーネントをスムーズに利用できます。Mozilla Brickのcalendarコンポーネントを利用するもう一つのサンプルがあります。

http://astamuse.github.io/asta4js/examples/webComponent/brick/calendar.html

Asta4jsのユーザガイドに外部コンポーネントの利用方法とAsta4jsを利用して自分のコンポーネントを実装する方法を詳しく説明しています。

https://github.com/astamuse/asta4js/wiki/Web-Component-Developing

なぜAsta4jsを作るのか

AngluarJS, Vue, KnockoutのようなフロントエンドMVVMフレームワークはたくさんあります。基本的にview-modelの双方向バイディングをサポートしてフロントエンドのエンジニアを煩雑なDOM操作から抜け出して、ロジックにより集中できるようになっていますが、われわれはなぜまた新しいフレームワークを作るのでしょうか?

簡単に言えば、われわれは現在のテンプレートエンジンがフロントエンドエンジニアの生産力を大変束縛しているという観点を持っています。フロントのテンプレートファイルに埋められたテンプレート制御コードはどうしてもデザイナーの作業を妨害してしまっており、フロントのページリファクタリングなども大変不便になっています。言い換えれば、デザイナーがページを直してからページがうまく表示できなくなることが開発者の日常生活になっているような状況です。たくさんの会社はデザイナーとエンジニアのコミュニケーションを強化するために、席を密接させるかなどいろいろ対策を採用しています。もしくは、デザイナーに対して最低限なプログラミング能力を求めて、すくなくともテンプレートファイルに埋められているバインディングなどのロジックを担当できるように要求しています。さらに、一部の会社はフルスタックエンジニアを求めて生産力を向上できるのではないかと模索しています。

しかしながら、そのいろいろ努力はそこまで生産力の向上に寄与していないという事実もあります。デザイナーと開発エンジニアは緊密に繋がられてよりパワフルな生産力を発揮できていません。

数年前、われわれはScalaのフレームワークLiftの感銘を受けて、JavaでのサーバサイドレンダリングフレームワークAsta4D(JavaフレームワークAsta4Dの話)を作りました。Liftから提出したロジックとテンプレート完全分離という仕組みを活用して、開発にすごくいい効果を得ることが出来ました。デザイナーはエンジニアに依存しなくても自身の作業のみでフロントページをバージョンアップすることが可能となり、開発エンジニアもデザイナーとのコミュニケーションをせずともにほとんどの開発タスクを独自に進められるようになっています。そのような柔軟な開発フローの恩恵で会社の利益は増大していっています。

近年、新しいサービスの開発にクライアントレンダリングのMVVMフレームワークが必要になり、いろいろフレームワークを評価してみましたが、既存のフレームワーク中ではAngularJSの評価が一番でした。しかし、AngluarJSも埋め込み型のテンプレートエンジンであり、HTMLのテンプレートファイルに様々なバイディングと制御を埋めなければならず、非埋め込み型フレームワークと比較すれば生産力の大幅な低下になると感じました。また、AngluarJSで複雑すぎると批判されているdiretiveで特殊なカスタマイズ作業のコストがすごく高くなってしまったこともありました。

いろいろな不満点を持ちながらもAngularJSを利用してサービスの開発をしていましたが、今回新しいサービスの開発をするにあたり、われわれが持っている非埋め込み型レンダリング経験を導入しようというきっかけで、非埋め込み型MVVMフレームワークAsta4jsの開発を着手しました。

現在、Asta4jsはまだ初期開発の状態にありますので、バグ報告やpull requestを歓迎しています。また、github上にissueチケットを作ってディスカッションを起こしていただけることも期待しております。

終わりに

今回はまた隣の席のnkgwから日本語の添削をしていただきました。こちらにも感謝しています。

Copyright © astamuse company, ltd. all rights reserved.