アトミックデザインでの失敗から辿り着いたSmartphone based な Atomic Design
はじめに
久しぶりの投稿です。
私事ですが、11月あたりから転職活動始めていて、ようやく転職先が決まってひと段落をついた今日この頃です。
今回はいつもと違って、アトミックデザインで失敗してしまった話とそこから導き出した考え方を書きたいと思います。
正直、これはデザインをきちんと学んでいないただのエンジニアの私が、デザインををきちんと把握している人と会話できる状態に持っていくためのものです。
アトミックデザイン初心者でイメージ湧かない方のファーストステップの考え方として見てもらえればと思います。
アトミックデザインって?
画面を構成する要素を、原子(Atom)分子(Molecule)有機体(Organism)テンプレート(Templates)ページ(Pages)の5つの階層に分け、UIをこれらの要素を組み合わせて設計する方法で、アメリカのブラッド・フロスト氏が考案・提唱した方法です。
すでに皆さん検索済みだと思いますが、DeNAさんのブログが分かりやすいです。ここでは割愛します。
アトミックデザインで失敗した話
何を失敗したか?
アトミックデザインは前述の通り、UIを設計する方法・考え方です。
初めてこの考え方を導入したとき、コンポーネントを整理していくことに注力してしまったため、業務ロジックをどこで実現するべきかが定めずに突っ走ってしまいました。。。
その結果、もの凄いスパゲッティコードが出来上がり、絡まりすぎて小麦粉団子になってました。
密結合なコンポーネント構成
もう、これがこの失敗の一番のつらみです。
Atom、Molecule、Organismの各所からaction、getterが呼ばれてデータを変更するように出来上がってしまったんですよ。
どこでデータが変わっているのか、なんでこの分岐があるのか、後で見直したら、もう訳が分からなくなっていました。
さながら、GOTO文が乱立していて大変だったという時代のようなコードでした。
大失敗を経て学んだこと
これはきちんとアトミックデザインの位置付けを理解して、適切な指針を定めていなかったことだなと思いました。
- アトミックデザインはあくまでもUI設計の考え方を提示してくれる
- アトミックデザインを浅く理解した状態では、業務ロジックについて別の考え方がないときちんと整理できない
私の指針:Smartphone based な Atomic Design
スマホ画面を軸に整理する
これはスマホ画面に最適化されたデザインを行うのではなく、「コンポーネントを整理するときスマホ画面を軸に考えたら悩まなかったよ」という一つの指針です。
スマホの画面構成って基本、縦長で各要素がスタックしている構成だと思っています。
パソコンやタブレットの画面で頭の中を整理すると、横の配置で表現することが出てくるため、2次元で考えると思います。
スマホの画面にすることで縦画面をどこで線を引くかにすることで、考えるべき次元を1つ減らした状態で整理できるので個人的にはスッキリしました。
ラフな画面デザインからコンポーネントの整理をしていく際、以下の順番で行ってみました。
- 画面を単体で意味をなす要素を分割して整理する(Organism)
- Organismを俯瞰してみて、機能として最小単位で部品を整理する(Atom)
- Atom、Moleculeの要素をもとに動作でグルーピングする(Molecule)
メリット
再利用可能なコンポーネントができた
はじめは、5つの階層に分けるんだと意識しすぎていたため、変な構成で分かれてしまっていました。
Moleculeは少し融通がきく分け方だと考えた方が再利用のできるコンポーネントができて良かったと思っています。
業務ロジックはpagesに紐付ける
Organismを俯瞰してAtomに分解しているので、機能としての振る舞いでの制御しかないため業務ロジックを組み込めなくなりました。
組み込もうとするととても複雑な処理になるためです。
その結果、Organismの振る舞いをデータで制御するようになるため、上位コンポーネントで業務ロジックを実装するようになりました。
アトミックデザインの役割からするとPageで必要なデータを操作をするようになりました。
デメリット
データを受け渡すため冗長なコードは残る
Vue.jsの場合だと、vuexで保持しているStateを上位コンポーネントからデータをを伝播するように実装するため、propとemitでデータをバケツリレーするようになり、似たコードがとても増えました。
こちらについては良い解決方法が私自身わかっていませんが、Organism以下のコンポーネントでデータ操作を除去した方が分かりやすくなったので、このデメリットはある程度享受する必要があるかなと思っています。
まとめ
今回の投稿は一つの考え方の整理で正しい設計の仕方ではないと思っています。
エンジニアとして実装する側とデザインを考える方と会話するための一助となればと思い、まとめました。
Vue NativeにRealmを組み込んだ時のエラーを解決してみた
はじめに
前回のブログではVue Nativeを用いてiOSアプリを作ってみました。
次はモバイルアプリ側にデータを永続化したいため、Realmを組み込んでみました。
2019/11/11のバージョンではエラーが発生しており、トライアンドエラーして解決した結果を投稿します。
realmをプロジェクトに組み込む
今回の環境や手順は2019/11/10時点で以下の動作ができた状態です。
- realmが追加されたアプリがiosシュミレーターで動作した
通常の手順で試しましたが、エラーが発生してなかなか動きませんでした。issueが上がっていたのでそちらをベースにした手順になっています。
Vue Nativeのツールなどが生成した設定ファイルをいくつか除去する必要があります。
事前準備する
ソースコードはこちらです。ソースコードはrealm追加用に少し変わっています。
手順は途中まで同じです。前回のブログの依存関係を解決するまでを実施して下さい。
realmを追加する
まずは、realmのパッケージを追加します。
yarn add realm --save
次にejectしてReact Nativeのプロジェクトに変換します。こちらの入力内容は前回のブログと同じです。
yarn eject
realmを利用できるようにするため、realmをiosモジュールに紐付けます。
react-native link realm
成功すると以下のメッセージが出力されます。
iosのネイティブモジュールの依存関係を解決するため、podコマンドを実行します。
cd ios pod install
この状態で「yarn ios」を実行するとビルドエラーが発生します。
エラーを解決する
こちらのissueを参考にしました。
対応する内容は以下の2点です。
- node_modules内のサブプロジェクトを紐付ける
- 紐づけるバイナリを変更する
node_modules内のサブプロジェクトを紐付ける
サブプロジェクトをメインプロジェクトに紐付けます。xcodeでblogapp/ios/blogapp.workspaceを開きます。
ドラッグ&ドロップで2つのプロジェクトを紐付けます。
- blogapp/node_modules/realm/src/RealmJS.xcodeproj
- blogapp/node_modules/realm/react-native/ios/RealmReact.xcodeproj
Finderからドラッグ&ドロップでxcodeに紐付けるとこのような状態になります。
紐づけるバイナリを変更する
iosモジュールがrealmのモジュールを探せるようにバイナリを紐付けます。
まず、無駄な紐付けが残ってしまっているので、削除します。
先ほど紐付けたRealmJS.xcodeprojを選択し、[TARGETS]を開きます。
RealmJSTestを選択して右クリックし、[Delete]を実行します。RealmJSTestが消えています。
blogappプロジェクトの[Build Phase]を開きます。
Link Binaryで以下の対応行います。
- libGCDWebServers.aを削除する
- RealmJSプロジェクト内で利用されているため、blogappのリンクからは外します。
- RealmJSTests.xctestを削除する
- RealmJSTestは上で削除したため、blogappのリンクから外します。
- libRealmReact.aを追加する
- RealmReactプロジェクトを紐づけるため、blogappのリンクを追加します。
この状態で「yarn ios」を実行すると問題なく起動します。
ただ、デバッグログを見れないので、xcodeから起動するときちんと動作しているかが分かると思います。
最後に
トライアンドエラーしながら、動作できることまで確認しました。
発生しているのはリンクしたモジュールが探せないだったので少し大変でした。
もう少ししたらこの辺りの課題は解決されると思いますが、暫定的な対応としては問題ないかなと思います。
Vue NativeでiOSアプリを作ってみた
はじめに
cordova + vue.jsの構成でアプリを作ったことはあるんですが、WebView周りのバージョン違いで挙動が異なったり、結構困った場面に遭遇したので、違うアーキテクチャを模索していました。
ちょうど1年前にVue Nativeのことを知ったんですが、試したことがなかったので、今回チャレンジしてみました。
Vue Nativeについての説明は他の方もブログで記載しているので割愛します。
試した環境
- Node.js : v8.12.0
- yarn : 1.17.0
環境構築
Vue Nativeのドキュメントをもとに環境構築をします。
今回の環境や手順は2019/11/7時点で以下の動作ができた状態です。
- インストールできた
- iosシュミレーターが動作した
- native版のvue-routerが動作した
1週間色々試しましたが、vue-native内で利用されているreact-nativeや関連モジュールのバージョンの組み合わせでうまく動かないことがあったので、なるべく楽にそれらを解決した手順になっています。
ソースコードはこちらです。
ドキュメントみるとわかるのですが、vue-nativeはreact-nativeをラッパーしています。 そのため開発環境としてexpoかreact-native-cliのどちらかで環境を作ることができます。
試した結果ではexpoでexpo用のプロジェクトを作り、それからreact-native用のプロジェクトに変換するのが一番手間が少なかったので、そちらの手順を説明します。
cliをインストールする
vue-nativeとexpoのcliをインストールする。
yarn global add vue-native-cli yarn global add expo-cli
vue-nativeのプロジェクトを作る
vue-native-cliのコマンドでプロジェクトを作り、動作確認をします。
途中でアプリ名を求められるので入力します。
vue-native init blogapp
Yarn v1.17.0 found. Use Yarn to install dependencies?
と聞かれるので、Yesで実行します。
プロジェクトが生成されているので、プロジェクト内に移動してからiosシュミレーターを起動することができます。
cd blogapp yarn ios
必要なパッケージをインストールする
vueでプロジェクトでvuex、vue-routerを利用するので、関連したパッケージをインストールします。
vuexはそのまま利用しますが、vue-routerはnative用のvue-native-routerをインストールします。
yarn add vuex --save yarn add vue-native-router --save
依存関係を解決する
このまま、vue-native-routerを利用するとエラーになり、動作しません。 事前に他のパッケージをインストールする必要があります。
yarn add react-native-reanimated --save yarn add react-native-gesture-handler --save yarn add react-native-paper --save yarn add react-native-vector-icons --save
上の3つについてはVue Nativeのドキュメントに記載されているのですが、「react-native-vector-icons 」だけは記載されていません。
どこかのバージョンから追加インストールは必要なくなるかもしれないので、ご注意を。
サンプルアプリを作る
ひとまず、プロジェクトディレクトリの直下に「router」、「store/modules」、「views」ディレクトリを作ります。
各プログラムの説明は次回の投稿で行いますので、説明は割愛します。
以下のコードをサンプルアプリ内に作ってください。
App.vue
<template> <app-navigator></app-navigator> </template> <script> import AppNavigator from './router' export default { components: { AppNavigator }, } </script>
router/index.js
import { createAppContainer, createStackNavigator, } from "vue-native-router"; import Login from "../views/Login.vue"; import Home from "../views/Home.vue"; const StackNavigator = createStackNavigator( { Login: Login, Home: Home, }, { initialRouteName: 'Login', } ); export default createAppContainer(StackNavigator);
store/index.js
import Vue from 'vue-native-core' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({})
views/Login.vue
<template> <view class="container"> <text class="label">USER ID</text> <text-input class="fixed" v-model="userid" /> <text class="label">PASSWORD</text> <text-input class="fixed" :secureTextEntry="true" v-model="password" /> <button :on-press="doLogin" title="ログイン" /> </view> </template> <script> export default { data: function() { return { userid: '', password: '' } }, props: { navigation: { type: Object } }, methods: { doLogin: function() { this.navigation.navigate("Home",{userid: this.userid, password: this.password}) } } } </script> <style> .container { background-color: white; justify-content: center; } .text-color-primary { color: blue; } .label { margin: 8px; align-items: flex-start; } .fixed { height: 40; width: 90%; margin: 8px; borderColor: gray; borderWidth: 1; } </style>
views/Home.vue
<template> <view class="container"> <text>{{ "USER ID:" + userid }}</text> </view> </template> <script> export default { data: function() { return { userid: '', password: '', } }, props: { navigation: { type: Object } }, created: function () { this.userid = this.navigation.state.params.userid this.password = this.navigation.state.params.password } } </script> <style> .container { background-color: white; justify-content: center; margin: 8px; } .text-color-primary { color: blue; } </style>
プロジェクトを変換する
これまでの手順で作成したプロジェクトはexpoのプロジェクトです。
React Nativeプロジェクトに変換する
このプロジェクトをReact Nativeのプロジェクトに変換します。
yarn eject
このコマンドを実行するとcliが質問をしてきます。
変換方法は最初の「Bare: I'd like a bare React Native project」を選択してください。
そのあとは、アプリの名前とworkspaceのファイル名を聞かれるので、適宜入力してください。 今回の説明ではblogappと入力しています。
xcodeで開発する準備を行う
「yarn eject」を実行した後にもメッセージが出ますが、iosの場合は以下のコマンドを実行します。
cd ios pod install
これでReact NativeでiOSアプリを作る環境が整いました。
yarn iosと実行するとexpoのdevtoolが立ち上がらず、MetroとiOSシュミレータだけがたちがあって動作確認ができます。
xcode経由であれば実機でも確認できます。
最後に
ちょっと情報が不足しているので、ソース解析したり、React Nativeのマニュアル見ながらで少し大変そうです。
もう少しサンプルアプリの構成のブラッシュアップとネイティブコードの呼び出しまではチャレンジしてみたいと思います。
Amplify DynamoDB Simulatorを使ってみた
はじめに
仕事ではサーバーサイドがメインなので、サーバレースアーキテクチャはあまり触れてないです。
ただ、少し前にserveless frameworkを触って、「dynamodbのローカルでコントロールできるじゃん。ヒャッハー」って感じで喜んでたんですが、最近はAWS Amplifyに鞍替えしてました。 AWS Amplifyが個人的に熱い感じになっていて、serverless frameworkっぽく操作できるカスタムプラグイン欲しいなと妄想していました。
そういえばserverless frameworkのようにAWS Amplifyにもそういうのないのかなって探してたら、
ありました。
DynamoDB自体のjarはAWSが公開しているので、自分でもゴニョゴニョできるかなと思いましたが、ここは先人の知恵を拝借。
環境
今回は以下の環境で試しました。ちなみにNode.jsはv8の場合はv8.12以上じゃないと動かないです。
- macOS Mojave
- Node.js:v8.12.0
- Yarn:1.17.0
インストールしてみる
ただ入れるだけです。
mkdir amplify-dynamodb cd amplify-dynamodb yarn add amplify-dynamodb-simulator
動かしてみた
よーし。動かしてみるぞーとREADMEを見てみたら、明朗すぎやしませんか?って感じ。
↓はREADME.mdのUsageの内容。
const emulator = require('amplify-dynamodb-simulator'); async function main() { // start the emulator const emu = await emulator.launch({ /* options */ }); // by default we launch the emulator on some open port in the ephemeral range in the 'inMemory' mode. // get the dynamodb client (aws-sdk) const dynamodb = emulator.getClient(emu); }
少しいじってみる。
ディレクトリの直下にmain.jsを作ってみる。
// main.js const emulator = require('amplify-dynamodb-simulator'); async function main() { await emulator.launch({}); } console.log("simulator launch") main().then(() => { console.log("simulator start OK!!"); }).catch(error =>{ console.log("simulator start NG!!"); console.log(JSON.stringify(error)); });
実行してみると
node main.js
問題なく動く。
DynamoDBのshellも叩きたいので、ソース見たらデフォルトでは62224ポート使っているみたいなので、ブラウザで「http://localhost:62224/shell」を起動するとこちらも問題なく動くぜ!
使ってみた
エラーなく動いているけど、本当に使えるかわからないので、試しにAWSのマニュアルにあるDynamoDBの使い方をベースに使ってみる。
create tableしてみた
まずはここのAWSのドキュメントを参考にshellで試してみた。
エンドポイントはsimulator側をみてもらいたいので、そこはゴニョゴニョする。
AWS.config.endpoint = new AWS.Endpoint('http://localhost:62224'); var ddb = new AWS.DynamoDB(); var params = { AttributeDefinitions: [ { AttributeName: 'CUSTOMER_ID', AttributeType: 'N' }, { AttributeName: 'CUSTOMER_NAME', AttributeType: 'S' } ], KeySchema: [ { AttributeName: 'CUSTOMER_ID', KeyType: 'HASH' }, { AttributeName: 'CUSTOMER_NAME', KeyType: 'RANGE' } ], ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 }, TableName: 'CUSTOMER_LIST', StreamSpecification: { StreamEnabled: false } }; // Call DynamoDB to create the table ddb.createTable(params, function(err, data) { if (err) { console.log("Error", err); } else { console.log("Table Created", data); } });
こんな感じでブラウザ上にコードを貼り付けるといい感じに動く。
put Itemしてみた
次はこのAWSのドキュメントを参考にshellで試してみた。
AWS.config.endpoint = new AWS.Endpoint('http://localhost:62224'); var ddb = new AWS.DynamoDB(); var params = { TableName: 'CUSTOMER_LIST', Item: { 'CUSTOMER_ID' : {N: '001'}, 'CUSTOMER_NAME' : {S: 'Richard Roe'} } }; // Call DynamoDB to add the item to the table ddb.putItem(params, function(err, data) { if (err) { console.log("Error", err); } else { console.log("Success", data); } });
成功した!
scanしてみた
最後はこのAWSのドキュメントを参考にshellで試してみた。
AWS.config.endpoint = new AWS.Endpoint('http://localhost:62224'); var ddb = new AWS.DynamoDB(); var params = { ProjectionExpression: 'CUSTOMER_ID, CUSTOMER_NAME', TableName: 'CUSTOMER_LIST' }; ddb.scan(params, function(err, data) { if (err) { console.log("Error", err); } else { console.log("Success", data.Items); } });
成功した!!
(けどAWSのマニュアルがIDが'001'だけど、型が数字だからゼロ埋め消えている。。。)
最後に
環境を作ってみて一通り動くところまで確認はできた!
次はsimulatorに内包されているaws clientを用いて操作してみよう。
挫折しなければ、カスタムプラグインでこの辺りをコントロールできるものを作ってみたい。
テーブル定義はOASのSchemaを解析できたらベターかな。。。
Vue.jsとAWS Amplifyで開発するサーバレスなWebアプリ:storage
はじめに
前回の投稿でAWS Amplifyのauthコンポーネントを用いた認証をアプリに組み込みました。
今回はAWS Amplifyのstorageを少し掘り下げます。
storageを利用できればS3を用いたファイルの保存・参照を簡単に組み込むことができます。
storageコンポーネント
authと同様、aws-amplify-vueにはいくつかのstorageコンポーネントが用意されています。
amplify-photo-picker
概要
amplify-photo-pickerは写真のアップロードに関する機能が用意されています。
このコンポーネントを呼び出すだけローカルファイルを選択し、S3のバケットにアップロードできます。
<template> <v-container fluid fill-height> <v-layout align-center justify-center> <div v-if="signedIn"> <amplify-photo-picker :photo-picker-config="photoPickerConfig" /> <amplify-s3-album path="upload/"></amplify-s3-album> <amplify-sign-out /> </div> <div v-else> <amplify-sign-out /> </div> </v-layout> </v-container> </template> <script> import { AmplifyEventBus } from 'aws-amplify-vue' import Auth from '@aws-amplify/auth' export default { props: [], data () { return { signedIn: false, photoPickerConfig: { header: 'Upload Profile Pic', accept: 'image/*', path: 'upload/' } } }, async beforeCreate () { try { await Auth.currentAuthenticatedUser() this.signedIn = true } catch (err) { this.signedIn = false console.log(err) } AmplifyEventBus.$on('fileUpload', (img) => { console.log('img:' + JSON.stringify(img)) }) } } </script>
photoPickerConfigがコンポーネントの設定情報です。
- header:コンポーネントのヘッダー名
- title:アップロードボタンの名称
- accept:送信するファイルのHTMLの許可設定。image/*の場合はimageしか送付できません。
- path:S3のバケットにアップロードする際のフォルダパス
詳しいドキュメントはこちらを参照してください。
イベント
authコンポーネントと同様、発生するイベントはAmplifyEventBus経由で受け取ります。 リスナーを登録して適宜必要な処理を実装します。
AmplifyEventBus.$emit('fileUpload', img) はファイルのアップロードに成功したときに発生します。
今回のサンプルプログラムで実装したのはbeforeCreateフック内でアップロードしたファイルの情報をコンソールに出力しています。
あまり情報は持っておらず、アップロード先のファイルパスだけ取得できるようです。
img:"upload/evi003.jpeg"
出力した結果は上の通りです。uploadフォルダにアップロードするようにしていたため、上記のパスになっています。
import { AmplifyEventBus } from 'aws-amplify-vue' import Auth from '@aws-amplify/auth' export default { props: [], data () { return { signedIn: false, // 〜 省略 〜 }, async beforeCreate () { try { await Auth.currentAuthenticatedUser() this.signedIn = true } catch (err) { this.signedIn = false console.log(err) } AmplifyEventBus.$on('fileUpload', (img) => { console.log('img:' + JSON.stringify(img)) }) } }
amplify-s3-album
概要
amplify-s3-album はパラメータで渡したpath配下のファイルを表示する機能が用意されています。
実際にはamplify-s3-image を内部で複数表示する仕組みになっています。
ここで
AWS AmplifyのAPIを呼び出してファイルパスの一覧を取得しています。
this.$Amplify.Storage.list(this.path, this.options) .then(res => { that.items = res.map(item => { return { path: item.key }; }); }) .catch(e => this.setError(e));
イベント
amplify-s3-albumのイベントはありません。
storageコンポーネントを利用する際の注意点
S3へのアクセスが時間切れで表示されなくなる
コンポーネント内部の処理では毎回、S3 Presigned URL(v4)が発行されて該当のファイルを表示しています。
これの課題はExpireする時間が定められているので、画面を操作せずに一定時間が立って画像を表示しようとすると画像が表示されないことです。
リロードされれば、再度S3 Presigned URL(v4)が発行されて画像が表示されます。
表示されるURLはS3Imageコンポーネント内で設定されており、この中でPresigned URLが発行されています。詳しくはここを参照してください。
this.$Amplify.Storage .get(this.imagePath, this.options) .then((url) => { this.url = url }) .catch(e => this.setError(e));
削除機能のコンポーネントがない
アップしたら削除できないです。AWSマネジメントコンソールでS3の画面から削除するか、AWS AmpifyのAPIを用いて削除処理を個別に実装するしかないです。
いくつか不足している機能もありますが、画像ファイルをS3にアップロードし、確認できました。
まとめ
storageのコンポーネントではあまりカスタマイズが出来ませんが、ほんの数行記載するだけでS3のバケットを利用した画像イメージを管理する仕組みが実現出来ました。
利用シーンは限定的かもしれませんが、この行数で素早くできるのは良いなと思います。
Pycon JP 2019に参加してきました。
初めてのPyconJP
Pycon自体は去年知ったのですが、去年はチケットが既に完売で購入できず、今年は早めに購入し、有給を使って PyconJPのカンファレンスに両日参加しました。
Day1
Automate the Boring Stuff with Slackbot
なるほどなぁと思い、試したくなったセッションだった。
sqliteもslackボットのアプリに入れられるなら結構やれることの幅が広がりそう。
slackのプラットフォームをこんな感じの使い方できるのはとても便利で、確かに退屈な作業はSlackbotに任せたい。
まずは年末調整で記載する数字の計算(旧保険なのか、新保険なのかとか、保険料の合計がいくら以下ならこの計算式とか)はいつも面倒なのでまずは試しに作ってみたい。
Djangoで実践ドメイン駆動設計による実装
Tensorflow、Kerasに触れて2年、そのあとDjango触りはじめて1年ちょいくらいですが、ふと「そういや、DIコンテナってPythonで聞かないな」と思っていたので、個人的にタイムリーな話題。
DDDからDIコンテナの話まで広い範囲をカバーしていたんで、発表者の苦労がにじみ出ていた。
試しにこうやってみたら?の提案があったのが良かった。 このテーマに挑んだの純粋にすごいなと思いました。DDDもDIコンテナもきちんと理解できていないので。
ちなみに、JavaのCDIさわっていた時はどちらかと言うとDIコンテナよりもインタセプターに嬉しさを感じてたので、DjangoにはMiddlewareあるし、DIはpytestのMockがあれば良いじゃんで最近落ち着いています。あとはDDD周りが整理がつけば個人的な悩みはクリアできそうなので、今回のセッションもインプットにしたい。
Pie Meets Py ― PythonでAndroidアプリをつくろう
ちょっと話がそれますが、私も発表してみようと思って普段はJavaScriptもさわっていることが多かったので、たまたま見つけたTranscriptにしようと思っていたんですよ。 そんなに本流の話じゃないし。競合もいないだろうと。
そしたら去年にTranscriptのフレーズをPyconJPのセッション一覧で見て
「ヤベェ、Pythonistaの底がしれねぇ」
と思って戦々恐々してました。今年はその方のセッション聞いて、最初に思ったこと。
私の戦略とダダ被りでした(笑)
思いのほかAndroidアプリができてしまったと言うのが発表の結論でしたが、良いところ/悪いところのコメントは勉強になりました。
発表スライド
ML Algorithm to Detect Rare Clinical Events
ざっくりとしたセッションの内容は、機械学習のクリティカルな領域への転用には誤検知がやばい。(よくわかる。)
そういえば、前にAIのイベントでガンの検知をDeepLearningで検知したいがどうしたら良い?って医療関係の人が相談してたけど、同じこと言ってた。
そのための一手法としてVAEが紹介されていた。短いセッションだったので概要だけだった。
発表スライド
Day2
KubernetesとJupyterHubで構築する機械学習eラーニングサイト
JupyterHubはpythonの学習サイトで良く使われているのでどんな感じなのかなとちょっと興味本位で聞きに言った。
幾つかのノウハウの説明があったのが良かった。
もっとeラーニングサイト作るとなるともっとごちゃごちゃした構成なのかなと思っていましたが、意外とシンプルだった。
JupyterNotebookで各自のものが提出できるのは良さげ。もう学校の学習コンテンツこれにしちゃえば感ある。
スライド
Python Website is Slow? Think Again!
どちらかと言うとアーキテクチャ的な話がメインだった。
最初はチューニングの話になるのかなと思って聞いてみたら、ちょっと違った。
まぁ言っていることは「確かに」と思ったことではある。
スライド
unittestで始めるユニットテスト入門
2日目で一番楽しみにしていたセッション!
テストコードは書いています(Pythonは)
結構充実しているなぁが触ってみての最初の感想。今回のセッションを聞いてみても改めて思った。
Mock最強!Exceptionを発生させるのマジ最高!!本当にMock最強!!!(大事なことなので2回言ってみた)
スライド
チームメイトのためにdocstringを書こう
はい。書きます!!
良く書き忘れてレビューでコメントされます。。。すみません。
reSTを使って書いていますが、他に書きかたがあったのは初めて知った。
スライド
まとめ
この辺りはtwitterで呟いたのと被る部分がありますが、素直に感じたこと
とにかく門戸が広かった
- セッションでの発表者で若い方もいて挑戦的なテーマが多かった
- 初心者向けのテーマでも普段の開発にインプットできるものが多かった
- 海外の方の参加率が想像以上だった
- カンファレンス全体通して感じたんですが、色んなところに配慮がなされていました。
参加して感じたこと
これまではJavaをずっとやっていたので、私が聞きに行ったことのあるのJavaのカンファレンスだけでした。
Javaのカンファレンスだと発表経験などでセッションのハコのサイズが決まってしまいます。(経験浅くても発表はできます)
まぁ発表経験の縛りにもメリットはあって、喋り慣れている方ほどセッションのハコが大きくても、スピーカーは緊張せずに発表するので、聞き手も安定して聞けると言うのがあると思います。
スピーカーが「いつもの」になってしまうとカンファレンス自体は「いつもの」になってしまうので、この辺りは苦慮しているんだろうなと思いました。
そういった意味で今回のPyconJPは参加してみて多様な人や発表があり、意義のあるものでした。
この辺りもキーワードでもあった「Python New Era」なのかなと思いました。
Vue.jsとAWS Amplifyで開発するサーバレスなWebアプリ:authコンポーネント
はじめに
前回の投稿でAWS Amplifyを利用したVue.jsの開発環境を構築しました。
今回はAWS Amplifyのauthを少し掘り下げます。
authを利用できれば認証を簡単に組み込むことができます。
authコンポーネント
aws-amplify-vueにはいくつかのコンポーネントが用意されています。
前回の投稿では amplify-authenticator と amplify-sign-out を利用しました。
amplify-authenticator
概要
amplify-authenticatorは認証に関する機能が用意されています。
このコンポーネントを呼び出すだけで一通りのことが実現できます。含まれているのは以下の機能です。
- サインアップ
- サインアップ確認
- サインイン確認
- サインイン
- パスワード再設定
<template> <amplify-authenticator :auth-config="authConfig" /> </template> <script> import { AmplifyEventBus } from 'aws-amplify-vue' import Auth from '@aws-amplify/auth' export default { props: [], data () { return { authConfig: { signInConfig: { header: 'サインイン' }, signUpConfig: { hideDefaults: true, signUpFields: [ { label: 'ユーザーID', key: 'username', required: true, type: 'email', displayOrder: 0 }, { label: 'パスワード', key: 'password', required: true, type: 'password', displayOrder: 1 } ] }, confirmSignUpConfig: {}, forgotPasswordConfig: {}, confirmSignInConfig: {} } } } } </script>
signUpConfig、signInConfigなどが各コンポーネントの設定情報です。
signUpConfigのsignUpFieldsにはサインアップ画面のフィールドを定義しています。
- label:フィールドのラベル
- key:フィールドの値とcognitoの属性値を紐づけています。
- required:必須か
- type:フィールドタイプ(type: 'text'とするとただのテキストフィールドになります)
- displayOrder:フィールドの表示順
詳しいドキュメントはこちらを参照してください。
イベント
各コンポーネントで発生するイベントはAmplifyEventBus経由で受け取ります。 リスナーを登録して適宜必要な処理を実装します。
AmplifyEventBus.$emit('authState', 'signedIn') はユーザーがサインインに成功したときに発生します。
前回のサンプルプログラムで実装したのは
- beforeCreateフック内でユーザー情報を取得する。
- 取得したユーザーのauthStateの値で処理を振り分ける
- signedInなら/photoへ移動する。
import { AmplifyEventBus } from 'aws-amplify-vue' import Auth from '@aws-amplify/auth' export default { props: [], data () { return { signedIn: false, // 〜 省略 〜 }, async beforeCreate () { try { await Auth.currentAuthenticatedUser() this.signedIn = true } catch (err) { console.log(err) this.signedIn = false } AmplifyEventBus.$on('authState', (info) => { if (info === 'signedIn') { this.signedIn = true this.$router.push('/photo') } else { this.signedIn = false } }) } }
amplify-sign-out
概要
amplify-sign-out はサインアウトする機能が用意されています。
<template> <amplify-sign-out /> </template> <script> import { AmplifyEventBus } from 'aws-amplify-vue' import Auth from '@aws-amplify/auth' export default { props: [], data () { // 〜 省略 〜 } } </script>
イベント
amplify-authenticatorと同様にAmplifyEventBus経由で受け取ります。
AmplifyEventBus.$emit('authState', 'signedOut') はユーザーがサインアウトに成功したときに発生します。
コンポーネントのUIカスタマイズ
英語のメッセージを日本語に変える
AWS Amplify I18nモジュールがあるため、そちらを使うことでメッセージを変更できます。
signOutButton: this.$Amplify.I18n.get('Sign Out')
各コンポーネント内で指定されたキーでメッセージを取得しているので、日本語用のメッセージを登録することでメッセージを日本語に変えられます。
amplify-sign-up
- signUpFields.label
- 説明:フィールドのラベル、プレースホルダー
- 備考:signUpFieldsのlabelをキーとして利用しています
- 'Create Account'
- 説明:アカウント作成ボタン
- 'Have an account? '
- 説明:アカウントはありますか?のラベル
- 'Sign in'
- 説明:サインインリンク
- 'Create a new account'
- 説明:サインアップ画面ヘッダー
- 備考:デフォルト値
amplify-sign-in
- 'Password'
- 説明:パスワード入力フィールドのラベル
- 'Enter your password'
- 説明:パスワード入力フィールドのプレースホルダー
- 'Forget your password? '
- 説明:パスワード再設定リンクのヒント
- 'Reset password'
- 説明:パスワード再設定リンク
- 'Sign In'
- 説明:サインインボタンのテキスト
- 'No account? '
- 説明:サインアップリンクのラベル
- 'Create account'
- 説明:サインアップリンク
- 'Sign in to your account'
- 説明:サインイン画面ヘッダー
- 備考:デフォルト値
amplify-confirm-sign-up
- 'Confirmation Code'
- 説明:確認コード入力のラベル
- 'Lost your code? '
- 説明:コード再送リンクのヒント
- 'Resend Code'
- 説明:コード再送リンクのテキスト
- 'Confirm'
- 説明:確認ボタンのテキスト
- 'Have an account? '
- 説明:アカウントはありますか?のラベル
- 'Back to Sign In'
- 説明:サインインに戻るリンクのテキスト
- 'Confirm Sign Up'
- 説明:サインアップ確認画面のヘッダー
amplify-sign-out
- 'Sign Out'
- 説明:サインアウトボタンのテキスト
- 備考:デフォルト値
これらのメッセージをmain.jsに設定する。
import Vue from 'vue' import './plugins/vuetify' import App from './App.vue' import router from './router' import store from './store' import Amplify, * as AmplifyModules from 'aws-amplify' import { AmplifyPlugin } from 'aws-amplify-vue' import awsconfig from './aws-exports' Amplify.configure(awsconfig) Vue.use(AmplifyPlugin, AmplifyModules) Vue.config.productionTip = false let messageResource = { ja:{ 'Create Account': '', 'Have an account? ': '', 'Sign in': '', 'Create a new account': '', 'Password': '', 'Enter your password': '', 'Forget your password? ': '', 'Reset password': '', 'No account? ': '', 'Create account': '', 'Sign in to your account': '', 'Sign Out': '' } } AmplifyModules.I18n.putVocabularies(messageResource) new Vue({ router, store, render: h => h(App) }).$mount('#app')
フィールドを追加する
サインアップ画面の入力フィールドにユーザ名とパスワード以外のフィールドを追加したいケースはあると思います。
signUpConfigのsignUpFieldsをカスタマイズすることで実現可能です。また、カスタム属性を追加することも可能です。
今回の例はカスタム属性を追加したケースです。
但し、issueに書いてありますが、今のamplify-cliでCognitoのカスタム属性の追加はできません。
ここだけ手作業が発生してしまいます。
amplify add auth amplify push
- pushしたのち、AWSのコンソールからCognitoの画面を開きます。
- ユーザープールの画面を開きます。
- カスタム属性としてtenantIdを追加します。
変更を保存します。カスタム属性に tenantId が追加されました。
signUpFieldにテナントIDを追加します。
signUpConfig: { signUpFields: [ { label: 'ユーザーID', key: 'username', required: true, type: 'email', displayOrder: 1 }, { label: 'パスワード', key: 'password', required: true, type: 'password', displayOrder: 2 } { label: 'テナントID', key: 'custom:tenantId', required: true, type: 'text', displayOrder: 0 } ] }
起動してサインアップ画面に移動するとテナントIDが入力フィールドとして追加されていることが確認できます。
サインアップしてユーザーを登録するときちんとカスタム属性に値が設定されています。
一部手作業が発生しますが、登録するユーザー情報にカスタム属性まで追加できることが確認できました。
まとめ
簡単なカスタマイズであれば可能ですが、全てが完璧という状態ではありませんでした。
自由度の高いカスタマイズを行うのであれば、自作のコンポーネントにAWS AmplifyのAPIを用いて制御する必要がありそうです。
ちなみにコンポーネントに対する要望や課題(UIだけに限らず)を受けてリファクタリングを検討しているようで、Issueはこちらです。2019年末にはリリースしたいようです。
次回はstorageコンポーネントについて試したいと思います。