2020年11月25日水曜日

WindowsでDockerを利用してMySQLサーバーを立てる場合の注意点。

オフィス狛 技術部のHammarです。

最近はnode.jsを触る機会がまた多くなってきたのですが、あるプロジェクトでAPIはnode.js、DBはMysqlを使うことになったので、Dockerでそれらのローカル環境を作り開発することにしました。
そこでWindowsとDockerの絡みでハマりポイントがあったので、ご紹介したいと思います。

※今回はDockerがWindows10環境にインストールされている前提で以下進めていきます。

まずDockerの起動にはdocker-compose.ymlを利用していきます。
そのdocker-compose.ymlには今回開発につかうAPIとDBの設定を下記のように記載します。
※今回DBの記述がメインとなるので、API側の記述は割愛します。
version: '3'
services:
  api:
	build:・・・
	・・・
  db:
    image: mysql:8.0
    command: mysqld --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - './docker/dev/mysql/data:/var/lib/mysql'
      - './docker/dev/mysql/my.cnf:/etc/mysql/conf.d/my.cnf'
      - './docker/dev/mysql/sql:/docker-entrypoint-initdb.d'

上記のように書いて、あとは通常通り上記ymlを使ってDocker起動するだけです。
しかしながら、上記方法でMacでは上手くいくのですが、Windowsではなぜかうまくいきません。

これはWindowsはMacとは違い、WindowsはdockerをVirtualBox経由で起動させているためのようです。
こちら参考にさせていただきました。
https://qiita.com/waterada/items/1dbf6a977611e0e8f5c8

※ちなみにWindowsは上記が原因で、作りたい環境内容によっては他の問題も発生するようで、実際自分も別環境作成時にまた問題があったのですが、またそのあたり別の投稿で記載しようと思います。

ということでいろいろと調べた結果、mysqlの設定ファイルであるmy.cnfファイルを別途作成し、それをdocker起動時にマウントさせることによって本事象を解消できるということのようでした。

1.docker-compose.ymlを修正する

まずはdocker-compose.ymlを下記のように書き換えます。
version: '3'
services:
  api:
	build:・・・
	・・・
  db:
    image: mysql:8.0
    command: mysqld --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - './docker/dev/mysql/sql:/docker-entrypoint-initdb.d'

2.my.cnfファイルを作成する

もともとDBの文字コードの設定もymlファイルのcommand部分に記載していたのですが、このあたりをmy.cnfに別途下記のように作成します。
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4

3.Dockerfileを作成する

最後に上記のmy.cnfファイルをマウントさせるために、下記のようにDockerfileを作成し、そこでADDします。
またファイルの権限もデフォルトだと777となってしまい、mysqlは権限777のcnfファイルは読み込まないということなので、ADDしたあとに権限も変更するように記述します。
FROM mysql:8.0

ADD ./docker/dev/mysql/my.cnf /etc/mysql/conf.d/my.cnf

RUN chmod 644 /etc/mysql/conf.d/my.cnf

上記3つが整った状態で、ymlを使ってDocker起動すると、なんとかうまく起動されました。
上記手順書いてみると、なるほどなーと感じるのですが、これを全く知らないところから調べていったので、かなりハマって環境構築だけで結構時間がかかってしまいました。。。
そもそもやっぱりVirtualBox経由でDockerを利用することがいろいろな弊害を生んでいるようなので、このあたりWindowsユーザーは不利だなーと感じたのでした。

,

Illustrator2021のオブジェクトを再配色を試してみる。


こんにちは、オフィス狛 デザイン部のSatoです。

先日アップデートされましたIllustrator2021で、以前ブログでご紹介した「オブジェクトを再配色」という機能がさらに便利になったようなのでご紹介します。

↓以前アップしたオブジェクトを再配色の記事です。

実は元々の機能の場所などが少し変わっていますので、「オブジェクトを再配色」画面の表示方法から説明していきます。 


色を変えたいオブジェクト全体を選択し、Illustratorメニューバーの「編集」の「カラーの編集」から「オブジェクトを再配色」を選ぶかツールバーの「オブジェクトを再配色アイコン」をクリックします。

以前は「オブジェクトを再配色パレット」が表示……されていたのですが、今回のアップデートから新しい「オブジェクトを再配色」画面が表示されるようになりました。

今まで最初に表示されていた「オブジェクトを再配色パレット」は右下に表示される「詳細オプション」ボタンをクリックすることで表示されるようになっています。
今まで通り再配色パレットを最初に表示したい場合ははオブジェクトを再配色パレットの左下に表示される「起動時に詳細オブジェクトを再配置ダイアログを開く」にチェックを入れましょう。
(新しい「オブジェクトを再配色」のダイアログが表示しづらくなるのであまりおすすめはできません)

新しくなったオブジェクトを再配色には「カラーテーマピッカー」や「目立つカラー」パレットなどの機能が追加されたのでご紹介したいと思います。 


最初にご紹介したいのはカラーテーマピッカーツールです。
なんとこの機能、選択したオブジェクトではない別の画像やオブジェクトの全体的な配色をつかって再配色してくれる機能です。
「カラーテーマピッカー」ボタンを押すとマウスカーソルがスポイトマークに変化するので参考にしたい画像をクリックしたり、Shift キーを押しながらクリックして複数選択したり、ドラッグして選択範囲を選択します。

画像を使い、Illustratorで描いたイラストの色を変えました

選択した画像でよく使われている色に変わりましたが、なんだか配色にメリハリがない配色になってしまいました。
こういう時にもう一つの新機能 「目立つカラー」パレットが役立ちます「目立つカラー」パレットの目立たせたい明度の高い色の端を横にドラッグして広げます。

「目立つカラー」パレットを調整してイラストがメリハリのある配色になりました!


オブジェクトを再配色を駆使し色をさらに調整してイラストを完成させました。

「カラーテーマピッカー」も「目立つカラー」も、よく色の参考に使っている「カラーライブラリ機能」よりもなかなか普段使わないような配色のデザインが作れるので、普段似たような色ばかり使ってしまうなあ……と悩んだ時に使ってみたい機能だと思います。


みなさんも配色に悩んだ際に「オブジェクトを再配色」の新しい機能を使ってみてください。

2020年10月29日木曜日

Angular・【Simple Modal Module】モーダルを手軽に実装する。


こんにちは、オフィス狛 技術部のpinoです。

今回は、AngularのSimple Modal Moduleの使い方についてご紹介したいと思います。

Simple Modal Moduleは、とても簡単にモーダルが実装できるプラグインです。
bootstrapなどでモーダルを作成されていた方も、この機会にぜひ目を通していただきたい内容です。

作れるもの

Simple Modal Moduleで実装できるモーダルは、以下のデモで試せます。
https://ngx-simple-modal-demo.stackblitz.io/

準備

まずは、以下のコマンドでインストールします。
npm install ngx-simple-modal

基本となるCSSはすでに用意されていますので、angular.jsonに以下を定義します。
angular.json
"styles": [
   "styles.css",
   "../node_modules/ngx-simple-modal/styles/simple-modal.css"
],
これで、モーダル表示時のスタイルやアニメーションはバッチリです。

HTMLは、上記のCSSに定義されているスタイルを使用するような形でテンプレートがある程度決まっていますので、以下の形から派生させていくことになります。
HTML
<div class="modal-content">
    <div class="modal-header">
      <!-- モーダルのタイトル -->
    </div>
    <div class="modal-body">
      <!-- モーダルの本文 -->
    </div>
    <div class="modal-footer">
      <!--
        フッターはbutton要素を置く
        ex.: <button (click)="close()">Cancel</button>
      -->
    </div>
</div>

1. AppModuleにImport

Simple Modal Moduleをimportします。
app.module.ts
import { NgModule} from '@angular/core';
import { CommonModule } from "@angular/common";
import { BrowserModule } from '@angular/platform-browser';
import { SimpleModalModule } from 'ngx-simple-modal'; // 追加🍨
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    CommonModule,
    BrowserModule,
    SimpleModalModule // 追加🍨
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

また、冒頭のデモでは、モーダル以外をクリックしたり、escキーを押すことでモーダルを閉じたりできたかと思います。
そのようにオプションを変更したい場合は、以下のようにも定義できます。

imports: [
    ...
    SimpleModalModule.forRoot({container: 'modal-container'}, {...defaultSimpleModalOptions, ...{
      closeOnEscape: true,
      closeOnClickOutside: true,
      wrapperDefaultClasses: 'o-modal o-modal--fade',
      wrapperClass: 'o-modal--fade-in',
      animationDuration: 300,
      autoFocus: true
    }})
  ]

・項目名 ... 説明(型, デフォルト値)
・closeOnEscape ... escapeキー押下でモーダルを閉じる(boolean, false)
・closeOnClickOutside ... モーダル以外をクリックしてモーダルを閉じる(boolean, false)
・bodyClass ... モーダルが開いている間、bodyタグに付与するクラス(string, 'modal-open')
・wrapperDefaultClasses ... <simple-modal-wrapper>に付与するクラス(string, 'modal fade-anim')
・wrapperClass ... モーダルが開いている間、<simple-modal-wrapper>に付与するクラス(string, 'in')
・animationDuration ... モーダルの開閉にかける時間(number, 300)
・autoFocus ... モーダルを閉じた後に、フォーカスをモーダルが開く直前の状態に戻す(boolean, false)

* <simple-modal-wrapper>に関して
デフォルトのラッパーとして、モーダルはこのタグの配下に配置されます。

* wrapperDefaultClasses、wrapperClassに関して
前述で紹介した基本のCSSではデフォルト値が使用される前提でスタイリングされているので、通常はあまり変更する必要がないと思います。
以降で紹介するaddModal(), close()といったモーダルの仕組みだけ利用し、スタイリングは1から行いたい場合などに活用できそうです。

2. モーダルコンポーネントを作成

モーダルのコンポーネントを作成していきます。
HTML
<div class="modal-content">
    <div class="modal-header">
      <h4>{{title || 'Confirm'}}</h4>
    </div>
    <div class="modal-body">
      <p>{{message || 'Are you sure?'}}</p>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-outline-danger" (click)="close()">Cancel</button>
      <button type="button" class="btn btn-primary" (click)="confirm()">OK</button>
    </div>
</div>
TS
import { Component } from '@angular/core';
import { SimpleModalComponent } from "ngx-simple-modal";
export interface ConfirmModel {
  title:string;
  message:string;
}

@Component({
    selector: 'confirm',
    template: './confirm.component.html'
})
export class ConfirmComponent extends SimpleModalComponent<ConfirmModel, boolean> implements ConfirmModel {
  title: string;
  message: string;

  constructor() {
    super();
  }

  confirm() {
    this.result = true; //🍀
    this.close();
  }
}

ポイントは🍀マークのthis.result = true;です。
理由は後述します。

また、close()でモーダルを閉じることができます。こちらはモーダルコンポーネント自らで呼び出すことができるため、TS上だけでなくHTML上でclickイベントに直接割り当てるのもOKです。

3. 作成したコンポーネントをモジュールに登録

App.module.tsに作成したコンポーネントをimportします。

import { NgModule } from '@angular/core';
import { CommonModule } from "@angular/common";
import { BrowserModule } from '@angular/platform-browser';
import { SimpleModalModule } from 'ngx-simple-modal';
import { ConfirmComponent } from './confirm.component'; // 追加🍨
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent,
    ConfirmComponent  // 追加🍨
  ],
  imports: [
    CommonModule,
    BrowserModule,
    SimpleModalModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

* 公式ではentryComponentsに定義するようになっていますが、ver.9から非推奨となっているようです。
弊社ではdeclarationsに定義するだけで問題なく使用しています。
参考: https://dev.to/angular-jp/entrycomponents-53mo

4. 実際に使ってみる

モーダルを表示させたいコンポーネントに組み込みます。
例なのでapp.component.tsに組み込んでみます。

import { Component } from '@angular/core';
import { ConfirmComponent } from './confirm.component';
import { SimpleModalService } from "ngx-simple-modal";

@Component({
  selector: 'app',
  template: './app.component.ts'
})
export class AppComponent {
    constructor(private simpleModalService:SimpleModalService) {}

    showConfirm() {
      this.simpleModalService.addModal(ConfirmComponent, { // 🍀①
        title: 'Confirm title',
        message: 'Confirm message'
      })
      .subscribe(isConfirmed =>{ // 🍀②
        if(isConfirmed) {
          alert('accepted');
        } else {
          alert('declined');
        }
      });
    }
}

🍀①のaddModal()でモーダルを表示させることができます。
モーダルを使用する側でモーダルを閉じたい場合は、removeModal()removeAll()が使用できます。

また、ポイントは🍀②部分です。
「2. モーダルコンポーネントを作成」 でresultにセットした値はここで取得することができます。
resultにはbool値以外も使用することができますので、
Submitされたか」「"はい"が押されたか」「処理に成功したか」など
様々なフラグをカスタマイズするのもいいですね。

注意点

これは実際に私がハマったことなのですが、いくらsubscribeしているからといってresultの変更を常々検知できるわけではありません。
ngOnDestroy時にObserverbleとしてresultが返される仕組みだそうなので、resultを取りたい場合はモーダルを閉じる必要があります。

今回記事を書くにあたり公式を確認したところ、その旨記載がありました。
ハマったとき、公式をちゃんと確認する癖をつけなければ...!


補足

公式にはBootstrapと一緒に使う方法も載っていますので、「Bootstrapと完全に縁を切るのは難しいけど、モーダルをコンポーネントとして管理したいんだよなぁ...。」といった場合にもおすすめだと思いました。
https://www.npmjs.com/package/ngx-simple-modal#what-if-i-want-to-use-bootstrap-3-or-4


今回は公式の内容を参考に、Simple Modal Moduleについて紹介しました。
参考になればうれしいです。

Node.jsを使用して、S3上のPDFをマージする方法。


オフィス狛 技術部のmmm(むー)です。

先日、業務でPDFのマージ作業が必要でしたので、その備忘録を残します。
マージ作業には、pdf-libというライブラリを使用しました。

pdf-libとは
pdf-libとは、PDFを作成・マージするためのnpmパッケージです。
今回はNode.jsで作業を行いましたが、ブラウザ側のJavaScriptでも使用することができます。また、PDFのマージだけではなく、実際にコードベースでPDFの作成もできるようです。
参考:https://www.npmjs.com/package/pdf-lib (公式)

【前提条件】
Nodeとnpmがインストールされていること。

1. pdf-libのインストール

使用したいプロジェクト配下に移動し、下記を実行します。
$ cd ~/<プロジェクトパス>
$ npm install --save pdf-lib

package.json に下記が追加されました。
// package.json

"dependencies": {
    "pdf-lib": "^1.11.1",
  }

2. S3からファイルを読み込む

< ... > 内は可変ですので、ご自身の環境の値を設定してください。
   const aws = require('aws-sdk');

// S3にアクセスするための情報
const s3Client = new aws.S3({
  accessKeyId: <accessKeyId>,
  secretAccessKey: <secretAccessKey>,
  region: <region>,
});

// バケットの情報 
// (バケット内にディレクトリを作成していない場合は、Keyにファイル名のみ指定してください)
const params = {
      Bucket: <bucketName>,
      Key: <s3DirectoryName>/<file.pdf>,
    };

// S3にある対象ファイル情報取得
const dataInS3 = await s3Client.getObject(params).promise();

// bufferデータを取得
const bufferData = dataInS3.Body;

// 💡 S3ではなく実行環境上にファイルがある場合は、下記のみでファイルの情報を取得できます
const fs = require('fs');
const file1 = await PDFDocument.load(fs.readFileSync('./file1.pdf'));


3. 読み込んだPDFをマージする

本来であれば複数のPDFをS3から読み込むと思いますが、今回は同じPDFファイルでマージします。
const fs = require('fs');
const { PDFDocument } = require('pdf-lib');

// マージ用の空PDF空を作成
const mergedPdf = await PDFDocument.create();

// マージ対象のファイル情報取得
const targetPdf = await PDFDocument.load(bufferData);

// マージ対象のファイルの全てのページ情報取得
const targetPdfPages = await mergedPdf.copyPages(
  targetPdf,
  targetPdf.getPageIndices(),
);

// 最初に作成した空のマージ用PDFに、マージ対象の全てのページを追加
for (const page of targetPdfPages) {
  mergedPdf.addPage(page);
}

// 💡 1ページだけ取得したい場合はこちら (下記例は、最初の1ページのみ取得)
const [targetPdfOnePage] = await mergedPdf.copyPages(
  targetPdf,
  [0],
);

// マージ用PDFに、1ページ更に追加
mergedPdf.addPage(targetPdfOnePage);

4. マージしたファイルを書き出す

ディレクトリを指定してファイルを書き出す場合、ディレクトリが存在しないとエラーになるので注意してください。
// PDFファイル書き出し
fs.writeFileSync('./merge.pdf', await mergedPdf.save());

5. マージしたファイルをレスポンスする

下記コードは、Expressを使用しています。
const pdfBytes = await mergedPdf.save();
const pdfbuffer = Buffer.from(pdfBytes);

res
    .attachment('merge.pdf')
    .set('Content-Type', 'application/pdf')
    .set('isBase64Encoded', true)
    .send(pdfBuffer)
    .toString('base64');

以上となります。
PDFをマージする必要がある場合、参考にしてみてください🍭

, ,

2020年10月27日火曜日

AWS Lambdaの関数コードでzipファイルをアップロードする。(DeprecationWarningの対応)

オフィス狛 技術部のJoeです。

弊社のあるプロジェクトで、AWSのLambda関数でPOSTリクエストする機能があるのですが、
ある日、ログを確認すると以下のようなメッセージが出力されていました。

DeprecationWarning: You are using the post() function from 'botocore.vendored.requests'.  This dependency was removed from Botocore and will be removed from Lambda after 2021/01/30. https://aws.amazon.com/blogs/developer/removing-the-vendored-version-of-requests-from-botocore/. Install the requests package, 'import requests' directly, and use the requests.post() function instead.

【直訳】
非推奨警告:「botocore.vendored.requests」のpost()関数を使用しています。 この依存関係はBotocoreから削除され、2021/01/30以降にLambdaから削除されます。 https://aws.amazon.com/blogs/developer/removing-the-vendored-version-of-requests-from-botocore/。 リクエストパッケージをインストールし、「リクエストをインポート」して、代わりにrequests.post()関数を使用します。

このメッセージの対応として、Lambda関数のコードでzipファイルをアップロードして、
「botocore.vendored.requests」からPythonのRequestsモジュールに変更した手順をご紹介します。
(Windowsでの手順になります)


1.Pythonをインストール

公式サイトから、Pythonをダウンロードして、インストールしてください。
※今回はVersion 3.9.0 を使用しました

環境変数(Path)の追加をお忘れなく。
[インストールフォルダ]\Python39
[インストールフォルダ]\Python39\Scripts

2.pipでRequestsを指定のフォルダにインストール

適当な場所に作業用のフォルダを作成し、
コマンドプロンプトを起動し、作成したフォルダに移動してください。

以下のコマンドで、インストールします。
pip install requests -t .

成功すると、作業用フォルダの中に、以下のフォルダが作成されます。
bin
certifi
certifi-2020.6.20.dist-info
chardet
chardet-3.0.4.dist-info
idna
idna-2.10.dist-info
requests
requests-2.24.0.dist-info
urllib3
urllib3-1.25.11.dist-info

3.zipファイルの作成

Lambda関数にアップロードするzipファイルを作成します。
上記2のフォルダをzipファイルにするだけなのですが、注意点があります。

①「.dist-info」のフォルダは不要なので削除します。

②今回のように、zipファイルをアップロードするLambda関数に、既に関数コードを記述している場合、
アップロードしたタイミングで、消えてしまいます。

既存のコードを使用する場合、「2」で作成したの作業フォルダの中に、
新規で「lambda_function.py」ファイルを作成して、
既存の関数コードをコピーしてください。

上記①、②の対応をした場合、zipファイルの中身は以下のようになります。
bin
certifi
chardet
idna
requests
urllib3
lambda_function.py

4.zipファイルのアップロード

Lambdaの関数コードのアクションで、「.zip ファイルをアップロード」を選択して、
「3」で作成したzipファイルをアップロードします。

Environmentにzipしたファイルとフォルダが表示されていると思いますので、
これでPythonのRequestsモジュールが使用できます。

最後にimportを修正して、「Deploy」すれば完了です。
修正前:from botocore.vendored import requests
修正後:import requests



上記の対応で、ログから警告メッセージが消えて、問題なく動作しました。


,

2020年10月23日金曜日

Angular・リアクティブフォームにおけるdisabled属性の扱いについて。


はじめまして、オフィス狛 技術部のpinoと申します。

今回は、Angularのリアクティブフォームにおけるdisabled属性の扱いについて書いてみたいと思います。

先日、Angularの実装中に以下のWarningに出会いました。
It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true
when you set up this control in your component class, the disabled attribute will actually be set in the DOM for
you. We recommend using this approach to avoid 'changed after checked' errors.

Example:
form = new FormGroup({
     first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
     last: new FormControl('Drew', Validators.required)
});

(かなり砕いた感じに)意訳すると、リアクティブフォームを使っているのに、disabled属性をHTMLに直に設定していませんか?例の書き方ならdisabled属性をDOMで設定することができます!といった具合でしょうか。

Warningなので最悪このままにしておいてもいいかもしれませんが、できることなら解消しておきたいですよね。

上記のWarningは、文言に忠実に従うことで回避できます。
以下、応用も含めて例を紹介します!

Formの定義

修正前は、以下のFormを定義していることにします。
(わかりやすく、Warningの文中にあった例を流用したいと思います。)
HTML
... // 中略

  <form [formGroup]="form">
    <input type="text" disabled formControlName="first">
    <input type="text" formControlName="last">
  </form>

...
TS
... // 中略

  form = new FormGroup({
    first: new FormControl('Nancy', Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

...
FormBuilderを使用する場合
... // 中略

  form = this.fb.group({
    first: ['Nancy', Validators.required],
    last: ['Drew', Validators.required]
  });

  constructor(private fb: FormBuilder) { }

...
* FormBuilderを使用する場合は、DIが必要です。 以降は、記述を省略します。

Warningの出る書き方

HTMLで以下のようにdisabled属性を設定していると、上記のWarningが出ます。
例)
  // ①HTMLの書き方そのまま
  <input type="text" disabled>

  // ②バインディング構文使用、設定値は固定
  <input type="text" [disabled]="true">

  // ③バインディング構文使用、設定値は可変
  <input type="text" [disabled]="form.invalid">

③もダメなんですね...。


解消方法

1. 基本に従う
Warningの文言にある通りに書き換えます。
HTML
... // 中略

  <form [formGroup]="form">
    <input type="text" formControlName="first">
    <input type="text" formControlName="last">
  </form>

...
TS
... // 中略

  form = new FormGroup({
    first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

...

HTMLからdisabledの記述を無くし、
コンポーネント側に {value: 'Nancy', disabled: true} という記述を増やしました。
ちなみにdisabled: trueで非アクティブ、disabled: falseでアクティブとなります。

FormBuilderを使用する場合は、以下となります。
... // 中略

  form = this.fb.group({
    first: [{value: 'Nancy', disabled: true}, Validators.required],
    last: ['Drew', Validators.required]
  });

...
こちらも、{value: 'Nancy', disabled: true} を追記すればOKです。

2. 動的に設定する
動的に切り替えたい時の方法です。
HTML
... // 中略

  <input type="text" formControlName="first">
  <input type="text" formControlName="last">
  <input type="checkbox" (click)="switchDisabled()">チェックするとfirstが非アクティブになります

...
TS
... // 中略

  isDisabled = true;

  form = this.fb.group({
    first: [{value: 'Nancy', disabled: this.isDisabled}, Validators.required],
    last: ['Drew', Validators.required]
  });

...

  switchDisabled() {
    if (this.isDisabled) {
      this.form.controls.address.enable();
    } else {
      this.form.controls.address.disable();
    }
    this.isDisabled = !this.isDisabled;
  }

...

切り替え用のチェックボックスを置いてみました。
ポイントは、コンポーネント側のenable()disable()です。
これにより、アクティブ・非アクティブを切り替えることができます。

今回はチェックボックスを使用しましたが切り替えるためのトリガーはなんでもOKですし、
切り替えが必要ない場合は、ngOnInit()に処理を置いて初期状態の判断に使用するのも良さそうです!

補足

ここまで説明してきましたが、実はdisabledをこのように指定しなくてもWarningが出ない要素もあります。
今のところそのように把握しているのは、button要素とoption要素です。

さらに、前述したWarningの出る書き方の例でいうと

button要素 → ①, ②, ③ どの書き方をしても怒られない
option要素 → ①は怒られる。②と③なら怒られない

という結果になりました。

この辺りは、余裕ができたらもう少し詳しく調べてみたいですね。


今回はここまで。
簡単な内容だったと思いますが、参考になれば嬉しいです。
,

2020年10月21日水曜日

Angular・HttpParams を動的に追加したい。


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。


Angularで、バックエンドAPIとやり取りする時は、
HttpClient」と「HttpParams」を使うと思います。

「HttpParams」はGETやDELETEの時のクエリパラメータを設定する時に使いますね。

  getHogeInfo(req: HogeInfoRequestModel): Observable<HogeInfoModel> {
    const options = {
      params: new HttpParams()
        .set('hogeId', req.hogeId)
        .set('testCode', req.testCode)
    };

    return this.http.get('xxxxx/yyyyy/hoge/info', options).pipe(tap(console.log));
  }

この時、クエリパラメータ「koma」が配列型式で追加になったとします。

よっしゃ、追加したろ、という感じで、
  getHogeInfo(req: HogeInfoRequestModel): Observable<HogeInfoModel> {
    const options = {
      params: new HttpParams()
        .set('hogeId', req.hogeId)
        .set('testCode', req.testCode)
    };

    body.komaArray.forEach((item, index) => {
      options.params.append(`koma[${index}]`, item);
    });

    return this.http.get('xxxxx/yyyyy/hoge/info', options).pipe(tap(console.log));
  }
とやると、うまくいきません・・・・。

ちょっとハマったのですが、

HttpParamsのappend()は、appendを実施したHttpParams自身には影響を及ぼさず、単純に追加したオブジェクトを返却するのみ。

と言う事です。つまり、
    body.komaArray.forEach((item, index) => {
      options.params = options.params.append(`koma[${index}]`, item);
    });

こんな感じで、「options.params」に値を再設定しないといけません。

結構単純なところにハマってしまうものですね。

Amazon WorkSpaces で気になったことを調べてみました。


オフィス狛 技術部のJoeです。

先日、担当したプロジェクトの開発で、AWS仮想デスクトップサービスの WorkSpaces を利用しました。

WorkSpacesの概要は、Amazon WorkSpacesのページを見ると判るのですが、
利用するにあたり、事前に気になって調べた内容を共有しようと思います。
※こちらは、2020年7月時点の情報になります


1.どのOSが選択できるのか?

以下から選択可能です。
・Windows 10(バンドルでoffice2010、2013、2016を選択できました)
・Linux2

今回はWindows10を選択して使用したのですが、
実際にWorkSpacesに接続して、OSのシステムを確認すると、
なぜか「Windows Server 2016 Datacenter」になっていました。

2.ボリュームサイズは拡張できるのか?

WorkSpacesを起動(作成)する時に、ユーザーボリュームサイズを10~100GBを選択できます。
WorkSpacesを起動(作成)後も、最大2,000GBまで拡張できます。
注意点としましては、サイズを縮小することはできませんでした。

3.接続方法は?

以下で接続可能です。
ただし、WorkSpacesを起動(作成)するディレクトリの「アクセス制御のオプション」で、有効化する必要があります。
・クライアント
 ※WorkSpacesのページからダウンロードできます。(Windows版、Mac版などあります)
・Webブラウザ

4.1つのWorkSpacesに複数ユーザーで同時接続できるか?

できませんでした。1セッションのみ有効です。

5.固定IP(Elastic IP)を利用できるか?

利用できます。
ただし、注意点としまして、WorkSpacesを起動(作成)するディレクトリの「インターネットへのアクセス」が有効になっていると、
WorkSpacesを起動(作成)時に、パブリック IPが自動で割り当てられてしまうため、
Elastic IPを関連付けすると、エラー「既にパブリックIPが関連付けられています」になってしまいました。

いろいろ試してみましたが、新たにElastic IPを関連付けできず、結局、下記のように新たにWorkSpacesを起動して対応しました。
①WorkSpacesを起動(作成)するディレクトリの設定で「インターネットへのアクセス」を無効化する
②WorkSpacesを起動(作成)する
②WorkSpacesにElastic IPを関連付けする

6.WorkSpacesに接続するIPアドレスを制限できるか?

制限できます。
まず、WorkSpacesサービスのIPアクセスコントロールで、接続するIPの「IPグループ」を作成して、
WorkSpacesを起動(作成)するディレクトリの「IPアクセスコントロールグループ」で、
作成した「IPグループ」を選択することで制御できます。

7.クライアントからWorkSpacesにファイルをコピーできるのか?

直接ファイルをコピーすることはできません。
Amazon WorkDocsを利用することで、ファイルの共有が可能になります。


WorkSpacesをご利用される際に、お役に立てれば幸いです。


,

2020年9月30日水曜日

Photoshopで出会った謎の「インデックス」レイヤーを編集できるようにする!

こんにちは、オフィス狛 デザイン部のSatoです。


先日pngファイルをPhotoshopで開いて編集しようと思った際に編集できず、困ったことがありました。

レイヤーロックを解除しようとレイヤーの上でダブルクリックしても解除できないのです。

よく見てみるとロックされているレイヤー名が「インデックス」になっています。

「インデックス」レイヤーは普段Photoshopでよく見る背景レイヤーと違い、レイヤーパネルの操作だけではロックが解除できずどうしたら編集できるのか分かりづらかったので、ロック解除のしかたを記載します。


まずレイヤーパレットからはインデックレイヤーを普通のレイヤーにすることはできません。レイヤーのカラーモードが関係しているので、レイヤーのカラーモードを通常のカラーモード(RGBやCMYKなど)に変更する必要があります。

Photoshopメニューバーの「イメージ」内の「モード」にマウスカーソルを合わせます。

インデックスカラーにチェックがついているので「RGBカラー」や「CMYKカラー」など使用する用途に合わせた項目をクリックします。

この操作で、今までインデックスレイヤーとして変更ができなかったレイヤーが通常レイヤーに変わり、ロックも外れて編集ができるようになります。


インデックスカラーの事を今回初めて知ったので、Adobeのカラーモードの説明を読んでみた所、

インデックスカラーモードは使用できる色を制限する事で目に見える画質を維持しながらファイルサイズを小さく保存できるというなかなか魅力的なカラーモード……。のようですがレイヤーが編集できないので何も知らないまま開くと少しびっくりしますね。

2020年6月30日火曜日

angularでの複数チェックボックスの取り扱い。


オフィス狛 技術部のHammarです。

最近久々にAngularプロジェクトを開発する機会があり、そこでまたいろいろと勉強になったことがあったので、ご存知の方には基本的なお話しなのかとは思いますが、今回は「angularで複数のチェックボックスを使った記述方法」の一例をご紹介できればと思います。

■作りたい画面

今回作る画面のイメージとしては、下記のような登録画面で、基本情報の入力と共にチェックボックスが複数設置され、その内容をフォーム送信して登録というようなシンプルな画面構造を想定します。

画面イメージ



■コンポーネント

まずコンポーネント側の記述ですが、今回フォーム送信を行うので、Angularのリアクティブフォームから、FormControl、FormGroup、FormBuilder、FormArrayクラスをそれぞれ利用します。 各クラスの細かな説明はAngularの公式ドキュメント内のリアクティブフォームの説明を見ていただいたほうがわかりやすいかもですが、めちゃくちゃざっくり書くとこんな感じかなと思います。
FormControl:単一入力欄の制御
FormGroup:FormControlのグループ化
FormBuilder:FormControlの作成に使う(FormControlをたくさん扱う場合にインスタンス不要)
FormArray:FormGroup内の配列要素コントロールの制御
さらに、今回は入力の必須チェックも行いたいので、Validatorsクラスもインポートします。

では、実際に上記をインポートし、コンポーネント側を記述します。
export class AppComponent implements OnInit {
  favoriteFruitsList: {key: string, value: string}[] = [
    {key: '1', value: 'りんご'},
    {key: '2', value: 'バナナ'},
    {key: '3', value: 'みかん'},
    {key: '4', value: 'ぶどう'},
  ];

  checkBoxFormArray: FormArray;
  groups: FormGroup[] = [];

  form: FormGroup = this.formBuilder.group(
    {
      name: ['', [Validators.required]],
      age: ['', [Validators.required]],
      tel: ['', [Validators.required]],
      formFavoriteFruitsList: this.formBuilder.array(this.groups, [Validators.required, this.checkBoxValidator]),
    }
  );

  constructor(
    private formBuilder: FormBuilder,
    public v: UserValidator,
  ) {}

  ngOnInit() {
    this.v.formGroup = this.form;
  }

  checkBoxValidator(control: FormArray) {
    for (let i = 0, loop_cnt = control.length; i < loop_cnt; i++) {
      if (control.at(i).value) {
        return null;
      }
    }
    return { unChecked: true };
  }

  onChange(value, isChecked: boolean) {
    this.checkBoxFormArray = this.form.get('formFavoriteFruitsList') as FormArray;
    if (isChecked) {
      this.checkBoxFormArray.push(new FormControl(value));
    } else {
      const index = this.checkBoxFormArray.controls.findIndex(x => x.value === value);
      this.checkBoxFormArray.removeAt(index);
    }
  }

  onSubmit() {
   // 登録処理
  }

}

form: FormGroup = this.formBuilder.group(...
の記述で、まずはフォームの初期設定をおこないます。
ここでチェックボックスとなるformFavoriteFruitsListはarray()メソッドでフォーム配列とし、さらにその中でチェックボックスをグループ化しています。これはcheckBoxValidatorでチェックボックスの必須チェックの為にこのような記述にしておきます。
ちなみにここで作成したcheckBoxValidatorは、チェックボックスのいずれかにチェックが入っているかのバリデーションで、Validatorsクラスには無いオリジナルのバリデーションになります。
onChangeについてはhtml側の記載で説明します。

■html

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <h2>名前</h2><input type="text" formControlName="name">
  <h2>年齢</h2><input type="text" formControlName="age">
  <h2>電話</h2><input type="text" formControlName="tel">
  <h2>好きな果物</h2>
  <ng-container formArrayName="formFavoriteFruitsList">
    <ng-container *ngFor="let item of favoriteFruitsList;">
      <label>
        <input type="checkbox" [value]="item.key" (change)="onChange(item.key, $event.target.checked)"><span>{{ item.value }}</span>
      </label>
    </ng-container>
  </ng-container>
  <button type="submit">登録</button>
</form>

ここでformArrayNameにコンポーネント側で設定したformFavoriteFruitsListを記載します。
これによりこのチェックボックスは配列要素のフォームとして扱います。
またここで、コンポーネント側に作成した、チェックボックスをチェックした時に発火するonChangeを使います。
チェックをON、OFFする際に、checkBoxFormArrayの配列内で、値を追加、削除する関数になります。
これにより、submitした時に、実際にチェックした値だけフォーム送信されます。


今回記載した内容はあくまで一例で、結構他の記載方法もあるようで、もしかしたらもう少し簡単に書ける部分もあるかもしれません。
また画面的には非常にシンプルなのですが、まだAngularのリアクティブフォームの使い方をうまく把握できていないと最初は難しく、結構苦戦しました(苦笑)。

自分のようなAngular初心者の方で、もしご参考になればと思います。

2020年6月4日木曜日

nodemonを使用して、コード変更後に自動でNode.jsのアプリを再起動する方法。


オフィス狛 技術部のmmm(むー)です。

Node.jsで動いているアプリケーションのコードの修正をした後、サーバーの再起動が必要になリます。
コードを修正した後、毎回手動で再起動するのは面倒ですし、再起動し忘れることもあります。
nodemonを使用すれば、その煩わしい作業を自動化できます😶

nodemonとは
nodemonとはNode.jsベースのアプリケーションのコードの変更を監視し、変更があった際に自動でサーバーの再起動をしてくれるツールです。
参考:https://nodemon.io/

【前提条件】
Nodeとnpmがインストールされていること

1. nodemonのインストール

使用したいプロジェクト配下に移動し、下記を実行します。
devDependencies オプションは開発環境でのみ使用するため、今回つけています。
$ cd ~/<プロジェクトパス>
$ npm install nodemon --save-dev

--save-dev オプションを使用してインストールすると、 package.json に下記のように追記されます。
devDependencies 配下のパッケージは本番環境で npm install --production コマンドを実行すると除外されます。
// package.json

"devDependencies": {
    "nodemon": "^2.0.3"
  }

2. nodemonでアプリケーションを実行する

package.jsonに nodeコマンドの代わりにnodemonで起動するscriptを追加します。
script名(start-watch)は自分可変です。
   // package.json

"scripts": {
     "start": "node src/index.js",
     "start-watch": "nodemon src/index.js", // 🌟追加
     "test": "echo \"Error: no test specified\" && exit 1"
   },

nodemonを使用して、アプリを起動します。
$ npm run start-watch

下記のように起動ログが出力されれば、起動成功です。
 [nodemon] watching path(s): *.*
 [nodemon] watching extensions: js,mjs,json
 [nodemon] starting `node src/index.js`

コードに何か変更があった場合、下記のようにログが出力されます。
 [nodemon] restarting due to changes...
 [nodemon] starting `node src/index.js`

また手動で再起動したい場合は、 rs コマンドを打ってください
$ rs
 
 [nodemon] starting `node src/index.js`

以上となります。
インストールして少し設定するだけで、開発が楽になるのでまだ使用していない方はぜひお試しください🍭

2020年5月28日木曜日

SUZURIでオリジナルグッズを作りました。


こんにちは、オフィス狛 デザイン部のSatoです。

弊社のWEBサイトなどで使用しているイラストのグッズをSUZURIというサービスで作成し、社員が注文した現物が届きましたので紹介します。


SUZURIはpngまたはjpg画像を1枚アップロードするだけでオリジナルプリントのグッズが販売できるようになるサービスです。
Tシャツなどのポピュラーなアイテム以外にも赤ちゃんのよだれかけなどの様々なアイテムに、注文がある度に印刷して発送してくれます。
在庫の管理をしなくてよいので売れるかどうかを気せずに、気軽にオリジナルの絵柄のグッズをオンライン販売できるのが有難いですね。


試しにいろいろなグッズを作ってしまいました。
グラスなど調整が必要なグッズもありましたが基本的にWEBサイトなどに使用した画像のカラーモードを変えただけの画像をほぼそのままアップロードするだけでオリジナルプリントのアイテムのモックアップが自動的に生成されました。
(ちなみに調整が一番大変だったのはスマートフォンケースでした。自動的にいろいろな端末のケースが作られてしまうのでシンプルな絵柄のものでないと調整が難しく感じました)

また、利益分も個人的に100円単位で設定できるのが有難いと思います。(弊社のグッズは利益分は初期設定または初期設定以下に設定にしてあります。)


今回、サイトで作ったTシャツとステッカーを社員が購入したので実物の写真を撮影してみました。
(Tシャツの上に置いて撮影したステッカー自体の色が暗いので雑なコラージュのようになってしまいました…)

綺麗に印刷されていますね。写真では分かりにくいかもしれませんが、Tシャツ自体は厚手で非常にしっかりした作りです。
あまりにも作業が簡単なのでもっと安っぽい印刷かと思いましたがほとんど普通にお店で売っているプリントTシャツと同じようなプリントなのではないでしょうか…?

カラーモードをRGB(画面で表示する際のカラーモード)→CMYK(印刷する際のカラーモード)に変更したので少々くすんでいるかと思いましたが非常に色も鮮やかに見えます……!
個人的にはTシャツは大満足の出来です。

ステッカーの方はモックアップをPC画面で見た時よりも少し暗めの色になってしまっていますね。ステッカー自体の色が暗い色だからでしょうか…?
でも印刷自体は色ムラもなくかなり綺麗だと思います。


現在オフィス狛のSUZURIのページの方でオリジナルグッズをオンライン販売中ですので気になる方はぜひアクセスしてみてはいかがでしょうか。
私も緊急事態宣言がおさまり次第、トートバックを買おうかと思っています。

2020年5月19日火曜日

MacOS Catalinaはデフォルトのログインシェルがbashではないらしい。



オフィス狛のyuckieee(ゆっきー)です。
最近Angularの環境構築をする機会があったのですが、その際以下の事象に悩まされました。

「ターミナルを起動するたびに"source ~/.bash_profile"を実行しないとパスが通らない!!!」

最初は毎回コマンド叩いてたのですが、煩わしくなって調べたら簡単な話でした...。
そこで本事象の原因や対処方法について軽くまとめておきます。

事象の原因

MacOS Cataliaからデフォルトログインシェルが「bash」から「zsh」に変更された事で、設定対象と読み込み対象のシェルが食い違っていることで本事象が発生していました。

もう少し細かく説明すると、環境構築方法を説明した多くの記事はbash(.bash_profile等)にパス設定する手順が書かれています。 ですが、MacOS Catalinaからはデフォルトのログインシェルが変更されており、ログイン時に読み込まれるのはzshの設定ファイルです。 いくらbashに設定を追加しても読み込まれることはなく、毎回強制読み込みコマンド("source ~/.bash_profile")を実行する必要があるのでした。
そりゃそうだ...。

現状の設定確認

それでは、実際にデフォルトのログインシェルの設定状況を確認してみましょう。
ターミナルを起動し、以下のコマンドを実行してください。

$ echo $SHELL
/bin/zsh

実行結果が"/bin/bash"以外であれば、パスを書き込むべきは".bash_profile"ではありません。

なぜデフォルトログインシェルが切り替わったのか?

公式での見解は見つからなかったのですが、幾つか記事を参照しているとライセンス問題と推測する声が多くありました。 そもそもmacOS Mojave以下のデフォルトログインシェルはbashですが、バージョンは最新(2020/05/08時点でver5.0)ではないようです。
セキュリティ上、最新化すべきと考えるのが自然ですが、ライセンス問題から旧バージョンのままとなっていたと思われます。
そして、この解決策として、ライセンスに課題を抱えるbash最新化ではなく、別のシェル(zsh)への乗り換えを選択したのでしょう。多分。

対応方法

対応方法としては、大きく2パターンが考えられます。

 パターン① zsh用の設定を行う
 パターン② デフォルトのログインシェルをbashに切り替える

切り替わった理由を推し量ると、推奨される対応方法はパターン①だと思います。
また、もしbashに切り替えるなら最新化もセットで行う必要がありそうです。

とはいえ、今回はパターン①、②双方の切り替え方法をご紹介しておきます。

パターン①zsh用の設定を行う

パスを通すだけであれば、基本的に設定ファイル名称を変更すれば済みます。
bash設定ファイルとの対比は以下のとおり。
bash zsh 補足
.bash_profile .zprofile ログイン時 (SSH ログインも含む) に実行
.bashrc .zshrc 新しいターミナルセッションごとに実行

指定例は以下のとおり。
今まで".bash_profile"を指定していた箇所をzsh用の".zprofile"に置き換えるのみです。
$ echo export PATH='/usr/local/bin:$PATH' >> ~/.zprofile
$ vim .zprofile

既に".bash_profile"にパスを設定している場合は、ファイルコピーで対応が可能です。
$ cp .bash_profile .zprofile

パターン②デフォルトのログインシェルをbashに切り替える

推奨ではないと思われますが、以下の手順でbashへの切り替えも可能です。
まず、切り替え対象のシェルが利用可能か確認するため、以下のコマンドを実行します。
bashに切り替える場合、本リスト内に"/bin/bash"があればOKです。
$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.
/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh

前項の確認結果を基にシェルの切り替えコマンドを実行します。
実行時にパスワードを求められるので入力してください。
$ chsh -s /bin/bash
Changing shell for ユーザー名.
Password for ユーザー名: 🗝

再度デフォルトログインシェルを確認し、切り替わっていればOKです。
$ echo $SHELL
/bin/bash

パスを直ぐに通したい場合は、以下のコマンドでパスを読み込ませます。
$ source ~/.bash_profile

Appleのサポートページでは、この他にもシステム環境設定画面からシェルを切り替える方法なども紹介されています。
Apple公式サポート:zshをMacのデフォルトシェルとして使う

【 おまけ 】

bash切り替え後にターミナルを立ち上げると以下のようなメッセージが表示されます。
「デフォルトシェルはzshに変わったから切り替えて!」と言うようなメッセージです。
 The default interactive shell is now zsh.
 To update your account to use zsh, please run `chsh -s /bin/zsh`.
 For more details, please visit https://support.apple.com/kb/HT208050.

本メッセージは、以下のコマンドを実行することで非表示にできます。
 $ export BASH_SILENCE_DEPRECATION_WARNING=1

事象が解決しない場合に確認すべきこと

ログインシェルの設定はソフトウェア単位にも設定可能な場合がありますので、そちらが有効となっていないか確認してみてください。

例としてターミナルでの設定確認方法をご紹介します。
ターミナルを起動し、[ターミナル] > [環境設定]を開き、環境設定画面[一般]タブの[開くシェル]設定が[コマンド(完全パス)]になっている場合、こちらの設定が優先となっています。





まとめ

セキュリティ上の懸念やAppleの対応(zshに切り替え)を考えると、個人的には利用者側もzshに切り替えるのが最善だと思います。
私もbashに切り替えるのではなく、zshの使用に切り替えるよう対応しました。
また、改めて環境構築周りの知識を強化しなければと反省。

2020年5月8日金曜日

AWS EC2でプロキシサーバー(Linux)を構築する。


オフィス狛 技術部のJoeです。

最近担当したプロジェクトで、開発用に用意した複数のAmazon WorkSpaces(Windows)から、外部の開発用VPNへ接続する必要があったのですが、 VPNに接続可能なIPアドレスが1つという制限がありましたので、EC2でプロキシサーバーを構築することにしました。

AWSであれば、NAT(インスタンス、ゲートウェイ)でも実現可能ですが、 今回の開発環境では、一部ネットワークの接続制限がありましたので、プロキシサーバーを選択しました。

最低限の設定にはなりますが、手順をご紹介します。
※VPC、サブネット等は、事前に作成されている前提です

1.EC2インスタンス作成(Linux)

プロキシサーバー用のEC2インスタンス(Linux)を作成します。
細かい手順は省略させていただきますが、注意点としては以下のようになります。
・インスタンスタイプは最小を選択
・「インスタンスの詳細の設定」で、「自動割り当てパブリックIP」は「無効化」に設定
 ※後で「Elastic IPアドレス」を関連付けます

2.Elastic IPアドレスの割り当てとEC2への関連付け

EC2サービスの「Elastic IP」から、プロキシサーバー用の固定IPアドレスを割り当てます。
・スコープ:VPC
・パブリックIPv4 アドレスプール:Amazon の IPv4 アドレスプール

続けて、割り当てたIPアドレスを選択して、Actionの「Elastic IP アドレスの関連付け」から、1.で作成したEC2に関連付けます。

3.Squidの設定

プロキシサーバー用のEC2インスタンス(Linux)に、プロキシサーバーソフトの「Squid」を、以下の手順で設定します。

①Squidインストール
sudo yum install -y squid

②Squid有効化
sudo systemctl enable squid

③有効化の確認(squid.serviceがenabledであること)
sudo systemctl list-unit-files -t service | grep squid

④Squid設定ファイル修正
設定ファイルに、プロキシサーバーに接続するIPアドレスを設定します。

※注意
必ず、「http_access deny all」の記述より「上」に設定してください。
(上記は「そのほかのアクセスはすべて拒否する」という意味になりますので、下に設定してしまうと有効になりません)


sudo vim /etc/squid/squid.conf

【追記内容】
# 接続したいIPアドレスを指定(複数指定可能)
acl myip src [IPアドレス1]/32
acl myip src [IPアドレス2]/32

# 接続したいIPを許可
http_access allow myip

⑤Squidリロード
設定ファイルを反映する場合、再読み込みを行います。
sudo systemctl reload squid

⑥squidアクセスログ確認
ログでアクセス状況を確認することもできます。
sudo cat /var/log/squid/access.log

4.WorkSpaces(Windows)側の設定

最後に接続側の設定です。
インターネットオプションから、「接続」タブの「LANの設定」ボタン押下します。

続けて「プロキシサーバーを使用する」をチェックして、
・アドレス:2.で割り当てたElastic IPアドレス
・ポート:Squid設定ファイルにの「http_port」に定義されているポート番号(デフォルトは3128)

※プロキシサーバーを使用したくない接続先がある場合は、「詳細設定」ボタンから、例外を指定してください。


以上が、必要最低限の設定になります。

接続側のグローバルIPを確認(確認くん など)すると 「2.Elastic IPアドレス」のIPアドレスが、表示されると思います。


,

2020年4月14日火曜日

JSON ServerとFaker.jsを使って、大量のダミーデータがあるREST APIサーバーの作り方


オフィス狛 技術部のmmm(むー)です。

フロント担当になったけど、APIがまだ作成されていない... Databaseもない...
そんな時のために大量のダミーデータがあるREST APIサーバーの作り方をご説明します😶

【前提条件】
Nodeとnpmがインストールされていること

1. JSON Serveのインストール

JSON Serverを使用すると、REST APIのモックサーバーを簡単に作成できます。

使用したいプロジェクト配下に移動し、下記を実行します。
$ cd ~/<プロジェクトパス>
$ npm install --save json-server 

# もしグローバルインストールしたい場合はこちら
$ npm install -g json-server

2. Faker.jsのインストール

Faker.jsを使用すると、大量のダミーデータを簡単に作成することができます。

Faker.jsのデータを保存する用に新たなファイルを作成します。
$ vi database.json

中身は下記のようにしてください。
{
     "info": []
 }

Faker.jsをインストールします。
$ npm install faker --save

Faker.jsでデータを作成する用のファイルを作成します。
$ vi generate.js

中身は下記のようにしてください。
(これは一例なので、名前とメールアドレスがセットのデータを10個作成していますが、必要なデータに合わせて変更してください)
var faker = require('faker');
 
 var database = { info: []};
 
 for (var i = 1; i<= 10; i++) {
   database.products.push({
     id: i,
     name: faker.name.findName(),
     email: faker.internet.email(),
   });
 }
 
 console.log(JSON.stringify(database));


以下のURLのNameSpacesに 、Faker.jsの用意されているダミーデータが記載されていますので必要なものを使用してください。
http://marak.github.io/faker.js/index.html

generate.jsのコードを実行して、出力結果をdatabase.jsonに書き込みます。
$ node generate.js > database.json

database.json中身が下記のようになりました。
$ cat database.json

 {"products":[
  {"id":1,"name":"Newton Kris","email":"Precious40@hotmail.com"},
  {"id":2,"name":"Dee Hackett","email":"Kasandra.Trantow91@gmail.com"},
  ~ 省略 ~
  {"id":10,"name":"Alvera Russel","email":"Felicia57@hotmail.com"}
 ]}

database.json のデータを返す、jsonServerを起動します。
$ json-server --watch database.json
 
  #  下記のように表示されたら、起動成功です
   \{^_^}/ hi!
 
   Loading database.json
   Done
 
   Resources
   http://localhost:3000/info
 
   Home
   http://localhost:3000

ブラウザにて、http://localhost:3000/info にアクセスしてみましょう。
作成したデータが表示されているはずです。

3. コマンドラインから確認する場合

curlコマンドを使用して確認します。
 # GET:全てのデータ取得
 $ curl -X GET "http://localhost:3000/info"
 
 # GET:「idが1」のデータだけ取得
 $ curl -X GET "http://localhost:3000/info/1/"
 
 # POST:「idが11」「nameがhoge」「emailがfuga@gmail.com」のデータ作成
 $ curl -X POST -d "id=11&name="hoge"&email=fuga@gmail.com" "http://localhost:3000/info"
 
 # PUT:idが1のデータの「nameをtest」「emailをupdate@gmail.com」に更新
 $ curl -X PUT -d "name=test&email=update@gmail.com" "http://localhost:3000/info/1"
 
 # DELETE:「idが11」のデータを削除
 $ curl -X DELETE "http://localhost:3000/info/11"

🍭 以下おまけ

既存プロジェクトのpackage.jsonのscript箇所に、データを作成するコマンドとjsonServerを起動するコマンドを入れると 便利です。

   // 一例として、angularのプロジェクトのpackage.json
   "scripts": {
     "ng": "ng",
     "start": "ng serve",
     "build": "ng build",
     "test": "ng test",
     "lint": "ng lint",
     "e2e": "ng e2e",
     "generate": "node generate.js > database.json", // 追加
     "server": "json-server --watch database.json" // 追加
   },

下記のようにスクリプトを実行することができます。
# ダミーデータの作成
 $ npm run generate
 
 # jsonServerの起動
 $ npm run server


以上となります。
簡単に使用できるので、REST APIサーバーが至急必要な場合はぜひ試してみてください。

2020年3月31日火曜日

iOSでViewの一部分を角丸にしたい時の拡張クラスの作り方。


こんにちは、オフィス狛 モバイル開発担当Aika-yuy です。
今回の投稿は、viewなどの一部分を角丸にしたい場合にインスペクタから設定できるようにする拡張クラスをご紹介します。
iOS11以降は、バージョンアップして以前より簡単に部分的に角丸にできるようになりました。


〜iOS10の設定方法

let path = UIBezierPath(roundedRect:layer.bounds,
                                byRoundingCorners:[//角丸にしたい場所を指定
                                                                    .topRight,
                                                               .topLeft,
                                                                    .bottomRight,
                                                                    .bottomLeft
                                                                   ],
                                cornerRadii: CGSize(width: XX, height: XX))
        let maskLayer = CAShapeLayer()
        maskLayer.path = path.cgPath
        layer.mask = maskLayer


iOS11〜の設定方法

layer.maskedCorners = [//角丸にしたい場所を指定
                                       .layerMaxXMaxYCorner,//右上
                                       .layerMinXMaxYCorner,//左上
                                          .layerMaxXMinYCorner,//右下
                                       .layerMinXMinYCorner//左下
                                         ]


CustomViewを作成しました

角丸の位置の指定は、ビット値で設定しているため、cornersではそれぞれの角丸に設定された値の合計を出しています。
class CustomView : UIView {
    @IBInspectable var cornerRadius: CGFloat = 0.0 { didSet { self.setNeedsLayout() } }
    @IBInspectable var cornerTopRight: Bool = false { didSet { self.setNeedsLayout() } }
    @IBInspectable var cornerTopLeft: Bool = false { didSet { self.setNeedsLayout() } }
    @IBInspectable var cornerBottomRight: Bool = false { didSet { self.setNeedsLayout() } }
    @IBInspectable var cornerBottomLeft: Bool = false { didSet { self.setNeedsLayout() } }
    
    private var corners: UInt {
        return UInt((cornerTopLeft ? 2 : 0) + (cornerTopLeft ? 1 : 0) + (cornerBottomRight ? 8 : 0) + (cornerBottomLeft ? 4 : 0))
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.setcornerRadius()
    }
    
    private func setcornerRadius() {
        layer.cornerRadius = self.cornerRadius
        if #available(iOS 11.0, *) {
            layer.maskedCorners = CACornerMask(rawValue: corners)
        } else {
            let path = UIBezierPath(roundedRect: layer.bounds,
                                    byRoundingCorners: UIRectCorner(rawValue: self.corners),
                                    cornerRadii: CGSize(width: self.cornerRadius, height: self.cornerRadius))
            let maskLayer = CAShapeLayer()
            maskLayer.path = path.cgPath
            layer.mask = maskLayer
        }
        layer.masksToBounds = self.cornerRadius > 0
    }
}




CustomViewを使用すると、インスペクタからon/offで部分的に角丸を設定できるようになります。 細かい角丸の設定が必要な際は使ってみてください!

2020年2月28日金曜日

knex.jsで発生した問題と対処方法(Invalid columnエラー、returningで取得できない)


オフィス狛 技術部のJoeです。

弊社ではknex.jsを一部のプロジェクトで使用しているのですが、
これまで開発中にハマってしまった事象と対処方法を2点ご紹介したいと思います。

【環境】
・node.js:12.14.0
・knex.js:0.15.2
・SQL Server 2017

1.「Invalid column name」エラー

内部結合でand条件を追加するため、マニュアルを参考に下記のように記述したのですが、エラーが発生しました。
  const number = '1234';
  return await knex
    .select('id')
    .from('tbl_1')
    .innerJoin('tbl_2', function cond() {
      this.on('tbl_2.id', 'tbl_1.id');
      this.andOn('tbl_2.number', number);
    });

エラー内容)
Invalid column name '1234'.

function内で変数を使用すると、カラム名として扱われてしまいまいた。
対処として、プレースホルダを使用すると、値として正しく扱うことができました。

対処方法:プレースホルダを使用する)
  const number = '1234';
  return await knex
    .select('id')
    .from('tbl_1')
    .innerJoin('tbl_2', function cond() {
      this.on('tbl_2.id', 'tbl_1.id');
      this.andOn(knex.raw('tbl_2.number = ?', [number]),
      );
    });

2.insertした値を「returning」で取得できない

「returning」を使うとinsertした行の、指定したカラムの値を取得することができます。
(マニュアルを見ると、PostgreSQL、MSSQL、およびOracleで使用できるようです)

通常は以下のように記述します。この場合、insertしたデータのidの値「2」を取得できます。
(insertする値を取得するというちょっと意味のないことをしていますが、本来はオートインクリメントの値などを取得したい時などに使用します)

  return await knex
    .insert({ id: 2, memo: 'メモ' })
    .into('memo_tbl')
    .returning('id');

次に、insert句にknex.rawを使用して記述してみると、値が取得できませんでした。

NG:値が取得できない)
  return await knex
    .insert(knex.raw("(id, memo) VALUES (2, 'メモ')"))
    .into('memo_tbl')
    .returning('id');

対処として、returningは使用せず、SQL Serverで同じく操作した行の値を取得するOUTPUT句をそのまま記述することで値を取得することができました。
(OUTPUT句はSQL Serverで使用可能です。他のデータベースは異なります)

対処方法:OUTPUT句をそのまま記述する)
  return await knex
    .insert(knex.raw("(id, memo) OUTPUT INSERTED.id VALUES (2, 'メモ')"))
    .into('memo_tbl');


今回ご紹介した事象のように、一見すると正しい記述のようですが、
思わぬところで期待通りに動作しないことがありますので、ご注意ください。


,

Angularのバージョンアップ(7から8)後に「An unhandled exception occurred: Job name "..getProjectMetadata" does not exist.」が発生した場合の対処。


こんにちは。
最近は、CI/CD周りの環境構築にどっぷり浸かっていましたKoma(Twitterアカウントの中の人&CEO)です。
CI/CD周りはいずれ投稿するとして、今日はAngularのアップデート時に発生したエラーの解消方法です。

結論から言うと、バージョン「9」がリリースされた今だけ限定の事象ではあるのですが、
日本語で解決策を載せているサイトが無かったので、勢いで書きました。

(1)エラーが出る前にやっていたこと

今回やっていたことは以下の通りです。
・既存のAngularプロジェクトのバージョンをアップデート(8.2へ)
・既存のバージョンは「7.2」

バージョンアップ自体は、いつもの通り、Angular Update Guideを参考に行なっています。

(2)エラーの内容

アップデート自体は、うまく行ったのですが、その後の起動(ng serve)で下記のエラーが発生しました。
An unhandled exception occurred: Job name "..getProjectMetadata" does not exist.

(3)対応した内容

実は、本家のissueにも登録されている内容なのですが、
アップデート後に、「npm audit fix」をやってしまった事が原因です。
(というか、普通やりますけど)

「npm audit fix」によって、関連のプラグインがアップデートされますが、
この時、「@angular-devkit/build-angular」のバージョンが上がってしまうことに問題があります。
「npm audit fix」後の package.jsonを見ると・・・・
"@angular-devkit/build-angular": "^0.900.4",
となっています。
これが、Angular 9用のバージョンなのですが、今回アップデートしようとしているのはバージョン8なので、エラーになってしまうのですね。

と言うことで、
"@angular-devkit/build-angular": "~0.803.24",
に戻したところ、エラーはなくなり、無事に起動(ng serve)出来ました。

気持ち的には、「Angular 9」にアップデートしたいところなのですが、使用しているプラグインの対応を待たないといけないので、もどかしいところですね。
(時間さえあれば、プラグインの更新に貢献したいところなのですが・・・)

さて、これから残り3サイトのアップデートを行おうと思います。😭
しんどいけど、アップデートされず放置されているシステムが世の中に溢れていることを考えると、
アップデート出来るだけ、恵まれてますね😄

では、より良いAngularライフを❗️


2020年2月27日木曜日

nodenvでプロジェクトごとにnode.jsのバージョンを切り替える方法。


オフィス狛 技術部のmmm(むー)です。

nodenvを使用してローカル環境のプロジェクトごとにnode.jsのバージョンを切り替える方法を説明します。
もちろん全てのプロジェクトで最新のバージョンを使用するべきですが、難しい時もありますよね...¯\_(ツ)_/¯

【環境】
MacOS Mojava
バージョン 10.14.6

1. anyenvのインストール

anyenvをインストールします。
(anyenvとは、ローカル環境で言語ごとに複数のバージョンを使用するためのツールです。node.jsだけでなく、pythonやPHPにも使用できます🍭)
➜ 😶git clone https://github.com/riywo/anyenv ~/.anyenv

パスを通す
➜ 😶echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile
➜ 😶echo 'eval "$(anyenv init -)"' >> ~/.bash_profile

shelの再起動
➜ 😶exec $SHELL -l

anyenvの初期化
➜ 😶anyenv install --init

2.nodenvとnode-buildのインストール

nodenvをインストールします。
➜ 😶anyenv install nodenv

node-buildをインストールします。
➜ 😶brew install node-build

default-packagesファイル作成する。
➜ 😶echo yarn >> $NODENV_ROOT/default-packages
  // 実行しないと 後続作業でnodeをインストールするときに下記エラーがでる
  // nodenv: default-packages file not found

3.node.jsのインストール

インストールできるnode.jsのバージョン一覧を確認する。
➜ 😶nodenv install --list

使用したいバージョンをインストールする。
➜ 😶nodenv install <バージョン>

4.プロジェクトで使用するnode.jsのバージョンの設定

使用したいプロジェクト配下へ移動する。
➜ 😶cd <プロジェクトパス>

そのプロジェクトで使用するバージョン指定する。
➜ 😶nodenv local <バージョン>

// プロジェクト配下ディレクトリで使用するNode.jsのバージョンが「.node-version」に記載される
➜ 😶cat .node-version
 10.12.0

設定したバージョンと同じものが表示されていれば成功。
➜ 😶node -v
10.12.0 

補足1)インストールしたNode.jsのバージョン一覧を表示
➜ 😶ls ~/.anyenv/envs/nodenv/versions

補足2)アンインストールする場合
➜ 😶nodenv uninstall <バージョン>


以上となります。
最新のバージョンを使用するべきですが、プロジェクトごとに違うバージョンのnode.jsを使用しないといけない際はぜひ参考にしてください。

2020年2月26日水曜日

package.json内にscriptsの記述を設定する(npm-scripts)


オフィス狛 技術部のHammarです。

node.jsを使った開発を行う時に、JavaScriptライブラリのパッケージマネージャーであるnpmを使いますが、このnpmにはプロジェクトの情報管理ファイルであるpackege.jsonがあります。
基本的なpackege.jsonの記載方法については割愛しますが、このpackege.json内にscriptsという記述を設定することによって、独自のコマンドを設定することができます。
これによって、例えばローカルサーバーの起動や停止等、良く使うコマンドを設定しておけば、いろいろコマンドを打たずに済むといった感じになります。

例えばAngularの開発では、package.jsonには下記のようにscriptsの記載したりします。

  "scripts": {
    "start": "ng serve",
    "test": "ng test",
    "build": "ng build",
    "copy": "cpx ./dist/* ../server/",
    "precommit": "lint-staged",
    "ng": "ng",
  },

とscripts内にいくつかのコマンドを設定しておきます。そして、

$ npm run [スクリプト名]

と打てば上記で設定したコマンドが実行されます。
よく使うコマンドは独自に作っておけば結構楽な場合があります。

また、上記scriptsには予約語があり、start、stop、restart、testが予約語として用意されています。
なので、たとえばstartのスクリプトを実行するときは、npm run startと打たなくても

$ npm start

とコマンドを打つだけで実行可能です。

もっと細かくいろいろ設定したりもできます。
たとえばscriptsに記述したコマンドを複数一気に実行したい場合、以下のようにscript内に記述しておきます。

  "scripts": {
    "start": "ng serve",
    "test": "ng test",
    "all": "run-s build copy",  // 追加
    "build": "ng build",
    "copy": "cpx ./dist/* ../server/",
    "precommit": "lint-staged",
    "ng": "ng",
  },

そして、上記スクリプト名の"all"を

$ npm run all

と実行すれば、"build"と"copy"というスクリプトが実行されます。

    "all": "run-s build copy"

の記述の run-s というコマンドが、複数実行で直列に実行するnpm-run-allのショートカットの意味で、指定したスクリプトを順番に実行してくれます。
また、応用で run-p というコマンドもあるのですが、こちらも複数実行ですが、スクリプトを並列に実行するコマンドになります。

上記以外にももっといろいろ細かく設定できるようなのですが、まずは上記のような設定で、ライトに使い始めてもいいかなと思いました。

2020年2月21日金曜日

Gitブランチ間の差分ステップ数を取得する。

はじめまして、オフィス狛のyuckieee(ゆっきー)です。

初投稿となる今回は、技術よりではなく開発付帯作業で使えそうなツールをご紹介しようと思います。

はじめに

みなさんが関わっているプロジェクトが、割とガッチリしたところだと「生産性の分析」のために開発ステップ数を集計されているのではないでしょうか。
例えば、母体のソースコードのステップ数、変更後の追加・修正・削除ステップ数などです。

狛ではソースコードをGitで管理しており、Git自体にもブランチ間の差分をとるdiffコマンドが存在します。ですが、このdiffコマンドがイケていない(笑)

そこで、幾つかネットで探して、最終的に「cloc」というオープンソースのツールを導入してみることにしました。
これが割と快適だったので、導入方法や出力イメージなどをご紹介します。

1.clocについて

「cloc(Count Lines of Code)」は、指定ディレクトリ又はファイルのステップ数を解析してくれる便利ツールです。Git diffコマンドとの大きな違いは、ブランチ間の差分ステップ数を「追加」「変更」「削除」で識別し、カウントしてくれる点です。
 現在、オープンソースとしてGitHubに公開されており無償で利用が可能です。

 GitHub : cloc
 ※clocは、他のソースからコピーされた部分を除きGNU General Public License v2の下でライセンスされています。

2.導入方法

「cloc」は、MacBookやWindows、Linuxなど幅広いOSで利用が可能です。
今回は、私が開発で使っているのがMacBookなので、そちらへの導入方法をご紹介します。

インストール環境:MacOS Catalina バージョン 10.15.2

①HomeBrewをインストール

今回は、HomeBrewを使用してインストールするため、HomeBrew自体をインストールします。 MacOSへのインストール要件はコチラを参照ください。

最初に対象のMacにHomeBrewがインストールされているかどうか確認します。
% brew --version
zsh: command not found: brew

インストールされていない場合は、ターミナルから下記のコマンドを実行し、インストールしてください。 ※インストールコマンドはHomeBrewの公式ページより取得
% /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

実行完了後、正常にインストールされた事を確認します。
% brew --version
Homebrew 2.2.5

②clocをインストール

cloc本体のインストールを行います。
ターミナルを立ち上げ、以下のコマンドを実行してください。
% brew install cloc

実行完了後、正常にインストールされた事を確認します。
% cloc --version
1.84

導入作業はこれで完了です🎉

3.利用方法

主目的はブランチ間の差分ステップ数取得ですが、今回は2つ利用方法をご紹介します。

パターン① 現行のブランチに対するステップ数解析

①ターミナルを立ち上げ、該当のGitローカルリポジトリに対しclocコマンドを実行します
% cloc /Users/yuckieeee/Gitローカルリポジトリ

<実行結果(例)>
    3000 text files.
    3000 unique files.                                          
      500 files ignored.

github.com/AlDanial/cloc v 1.84  T=5.70 s (355.9 files/s, 33435.9 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Java                           350           4000              350          25000
XML                            250           2500              300         12000
 ・
 ・
 ・
-------------------------------------------------------------------------------
SUM:                          1500       10000            2000     60000
-------------------------------------------------------------------------------

パターン② ブランチ間のステップ数の差分解析

①比較対象のブランチ2つをGitからダウンロードしてください
 GitLabだとリポジトリのトップ画面右上にダウンロードアイコンがあります。

②ターミナルを立ち上げ、ダウンロード先のディレクトリに移動します

③下記のclocコマンドを実行してください
 zipやtarのままでもステップ数の解析が可能です。
% cloc --diff master.tar branch.tar
※圧縮ファイルの場合は、拡張子まで指定してください

<実行結果(例)>
     300 text files.
     300 text files.
          0 files ignored.                                         

github.com/AlDanial/cloc v 1.84  T=1.55 s (337.8 files/s, 22344.2 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Kotlin
 same                          200           1800            400          12000
 modified                      90                  0                 0               180
 added                             3             300           122              1300
 removed                         0                 0                0                100
XML
 same                          100            800             50            10000
  ・
  ・
  ・
-------------------------------------------------------------------------------
SUM:
 same                          100           200           100          10000
 modified                       20                0                3               100
 added                           20            200           150            1800
 removed                      30               10               0               100
-------------------------------------------------------------------------------

これでブランチ間の追加・修正・削除ステップ数が確認できました。 clocは、改行、コメントやコードごとに集計されるので、非常に分かり易いです。
欲を言うなら、パータン②のダウンロード作業が無くなってくれれば最高です☺️←

4.コマンドオプション

最後に、重宝しそうなオプションをいくつか紹介して終わろうと思います。

・比較対象を指定

clocはディレクトリ、ファイル、Git hashによる比較対象の指定が可能です。
Git hashでのコマンド指定を例示します。
// ローカルリポジトリに遷移
% cd /Users/yuckieeee/ローカルリポジトリ/
// 対象コミットのハッシュを確認する
% git log
// 上記で確認したハッシュを指定する
% cloc --diff <コミットハッシュ①> <コミットハッシュ②> 

・ファイルごとに解析結果を表示

何も指定しない場合は、プログラム言語ごとに集計されます。
ファイル単位に確認したい場合は、以下のように--by-fileオプションを追加すればOKです。
% cloc --diff master.tar branch.tar --by-file

・CSVファイル出力

CSVファイル出力したい場合は、以下のようにcsvタイプ指定と出力先指定をすればOKです。
% cloc --diff master.tar branch.tar --csv > result.csv

まとめ

開発の付帯作業の必要性は分かるものの、出来るだけ時間を掛けたくなですよね。。
これからも便利そうなツールや機能があれば紹介していきたいと思います。

2020年2月5日水曜日

Spring Fest 2019に参加してきました。


オフィス狛 技術部のJoeです。

去年、Spring Fest 2019(12/18 御茶ノ水ソラシティ)が開催されました。
弊社プロジェクトの一部でSpring Bootを使用しているため、情報収集を目的に今回初めて参加してみました。
年々参加者数が増えているとのことで、とてもたくさんの方が来場されていました。

と言うことで、年は明けてしまいましたが、感想をブログに残したいと思います。

【公式ページ】
https://springfest2019.springframework.jp/

基調講演

Spring Boot 2.2の概要、2.3の紹介がありました。
そのなかでも気になったのが、今後、Spring Bootはリリース周期が1年から6ヶ月と短くなるそうで、バージョンアップのタイミングにも注意が必要になりそうです。また、2.1系は2020/11にEOLとなるそうです。

Spring Boot爆速開発超絶技巧

サムライズムさん
https://speakerdeck.com/yusuke/spring-boot-and-intellij-idea-technique
これまで開発では、STSしか使用したことが無かったのですが、「IntelliJ IDEA」とはどのようなものか、タイトルにも興味があり参加してみました。
極力キーのみで操作するということで、様々なショートカットを使用したデモでは、早い、便利と大変驚きました。
機会があれば無料版を弄ってみようと思います。

徹底解剖Spring MVCアーキテクチャー -DispatcherServletの中身を覗いてきました

カサレアルさん
https://www.slideshare.net/ssuser070fa9/spring-fest-2019spring-mvc
Spring MVCのアーキテクチャーをソースコードリーディングで解説されていました。
スライドの内容が主にソースコードでしたので、なかなか説明に追いつけずに苦労しましたが、内部構造まで説明があり、他のセッションと比べて内容が深かったです。
(周りの参加者の皆さんは、ノートPCやタブレットでスライドを参照されて便利そうでした。。。次回は用意しようと思います)

システム間連携を担うSpring Integrationのエンタープライズ開発での活用

NTT DATAさん
https://www.slideshare.net/apkiban/spring-integration-207667949
異なる仕様やオンプレ×クラウドなどのシステム間連携が増える中で、システム間連携の技術として、Enterprise Integration Patternsで推奨されている「非同期メッセージング」を実現するために「Spring Integration」を紹介されていました。
「Message」、「Channel」、「Endpoint」のキーワードとフローを用いた概要と、実際の作成ステップなど説明があり、勉強になりました。

Spring Developer のための コンテナ入門

Google Cloudさん

Kubernetes、Jibと勉強不足で初めて聞く単語でしたが、Jibを使用することでJavaのアプリケーションのDockerコンテナを簡単に作成できることに驚きました。Kubernetesについては、コンテナ管理(監視や制御)を知ることができました。

JSP/JSF から Spring Web + Thymeleaf への移行

伊賀 敏樹さん
https://www.slideshare.net/ssuser2e0217/practical-migration-from-jsp-to-thymeleaf
Thymeleafの紹介と利点についてのセッションでした。Thymeleafは使用しているので復習になりました。

Springアプリケーションのテスト道具 使いどころ、使わないどころ

いろふさん
https://speakerdeck.com/irof/decide-to-use-test-tool-for-spring-application
Springのテスト道具として、JUnit 5、Mockito、Spring Test、Selenium、Dockerの紹介から始まり、
「原則として、テストしたいことの近くでテストを行う」、「道具の向き不向きを考えてテスト道具の使いどころを見極める」ということに重点を置いて説明されていました。
Springというより、テストについて改めて勉強になったセッションでした。


1日のセッションを通じて、自身はまだまだスキル不足だと実感しましたが、新しい情報を得ることができ、非常に勉強になりました。
次回も是非参加したいと思います。


Illustratorで作ったボタンをCSSで再現したい。


こんにちは、オフィス狛 デザイン部のSatoです。

今回はIllustratorでデザインを作ったオブジェクトのグラデーションなどの設定をCSSで再現したい時に使える機能をご紹介します。
この機能のを使えばグラデーションが細かいボタンをの色をCSSで数分で再現できるのでコーディングがかなり楽になります。最高の機能なのでもうAdobeに足を向けて寝れません!


Illustratorのグラデーション機能を使い作成したボタンを用意しました。
色が多くて設定も少し複雑なのでCSSで再現するのが大変そうです。


今回は「CSSプロパティ」という機能を利用してグラデーションの色味などの設定を書き出していきます。

最初にCSSプロパティパネルをワークスペースに表示させます。
Illustratorメニューバーの「ウィンドウ」の「CSSプロパティ」をクリックします。
CSSプロパティパネルがワークスペースに表示されます。

グラデーションの設定をコピーしたいオブジェクトのオブジェクト名を設定します。
オブジェクト名がclass名になるので、class名で使用できる文字以外(全角英数字や日本語など)を入れると何も設定を変更していない場合エラーでCSSを生成してくれません…(書き出しオプションで「名称未設定オブジェクト用に CSS を生成」にチェックを入れると書き出してくれるようになるようです)

グラデーションの設定をコピーしたいオブジェクトを選択するとCSSプロパティパネルにCSSが表示されます。
選択スタイルをコピーボタンをクリックすると生成されたCSSが全てコピーされます。
これをcssファイルにペーストすると色、角丸の角度などが設定できました。

ボタンの高さや幅、ボタンの中のフォントサイズなどの調整は必要ですがIllustratorで作ったボタンをほぼそのまま再現できます。
CSSのグラデーションに苦手意識があったのでいつも時間がかかっていたのですが、この機能を知ってから数分でグラデーションを再現できるようになったので助かっています。


2020年1月27日月曜日

2020年オフィス狛の年賀状の制作秘話



新年あけましておめでとうございます!オフィス狛 デザイン部のSatoです。
今年もどうぞよろしくお願いいたします。

もう一月も終わってしまいそうですが、デザイン部の年明け初めての記事ですので今年の年賀状のおはなしをしようと思います。
(今まで全然紹介してこなかったのですが、2018年の年賀状から毎年私がイラストを描いています)


2020年の年賀状はこちら!

弊社サイトやSNSなどのアイコンなどで登場している狛犬ちゃん(社内ではたまに「こまちゃん」と呼ばれています)が今年の干支であるネズミの着ぐるみを着ている……という可愛い(自画自賛)テイストなイラストがメインな年賀状です。


最初は時事ネタを毎年取り入れているので元号発表のシーンのパロディっぽくしてみたり迷走していたのですが、
「Satoさん、ジョ●ョって知ってますか?
ジョ●ョ6部の主人公がスタ●ド攻撃のせいで小さくなってネズミの毛皮を着ぐるみのように被るシーンがあるんですよ……。
あれみたいにこまちゃんがネズミの着ぐるみを着ていたらすごく可愛いと思うんです」
というアドバイスを受けて、ファンシーグッズを出しているメーカーの年賀状を色々参考にしつつシンプルにネズミ着ぐるみ可愛いですよという直球なイラストを描きました。
(ここだけの話ですが、ジョ●ョは3部までしか読んでいないので6部の主人公がネズミの毛皮を被るシーンはいまだに見ていません

年賀状用に描いたイラストに辿り着くまでこんな感じのラフを描いたりしていました↓


メインのイラスト以外にも、ネズミのイラストに合わせてブラシツールで描いた四角を散らしてみたり手書きっぽい雰囲気のフォントを使ってみたりしてみました。
Happy New Year!の部分のフォントはフリーフォントのしろくまフォントを使用しています。
手書き風なのに幼くなりすぎないオシャレなフォントなので大好きです。


今年は来年の年賀状はどうしようかな?と考えたり普段描かないのでイラストを練習したり一年を過ごそうと思います!ガンバルゾ💪(´・_・`💪)