お前の会話にはコミュ力が足りない
コミュ力ってなに
ここでは相手と疎通できる共通認識がある状態の事を指す。
業務上で意見聴取出来るし、困った事はない。
程度にもよるけど、よそのエンジニアと話す際に浮き彫りになった事例を下記に記す。(*お前=筆者を指す)
自分「イケてると思っていたAndroidアプリに設計のダメ出しをされた」
某氏「お前はどこがイケてると思ったん」
自分「ViewModelとかLiveDataとか使ってるし、画像のキャッシュも考慮して…」
某氏「それって当たり前やん?」
自分「!?!?」
某氏「設計はどうしたら良かったと思う?」
自分「モジュールに切り分けて…APIを分割して…」
自分「アッあのライブラリ使えばよかったな…」
某氏「なんで?」
自分「MVVMっぽくない」
某氏「MVCでよくね?」
某氏「君はなぜにMVCとMVVMという設計手法があるんだと思う?」
自分「っえーー……」(カス)
自分「MVVMはガベージコレクションを考慮されたなんたらでえ…」
某氏「Railsはファットモデルが良しとされてるよね?
これはドメインに合わせてスケールが出来るようにという意図があるみたいだけど」
自分「あーそうみたいですね。自分はあまりいいとは思えないです」
某氏「え!?!?!?」
↑↑↑
自分:基底クラスを継承しているにも関わらず、改修が冗長的になった事例を思い浮かべている(悔しい記憶)
某氏:プロダクトに必要なものを具体化出来るDDD手法について思い浮かべている
某氏「ていうか今そういう観点で話してへんやん?」
自分「…」
(この後某氏による認識合わせの時間が入る)
今更Vue + github pagesに触れてみる
デプロイの度に真っさらなページが出ていたので、まずはアセットを読み込んで表示するところまでやってみた。
冬に比べてだいぶ記事が出回っているので既出感あるが…。
自分の場合、ルートパスにvue.config.jsを置いておくのを忘れていて、こちらをgithub pagesのドメインと合わせるのを忘れていました。後、ビルド後のディレクトリをdocsにしたものの、プロジェクトの設定でそれを有効にしてなかった。
デプロイ自体は5分足らずで終わった印象。
ぼよんぼよ〜ん
こうやって無駄に動きをつけるのが好きです。
display: flex大好き人間なんですが、デフォルトのタグでv-flexなどが用意してあるのが良かった(小並感)
なんかコピペでそれっぽいのを作れてしまうので恐怖感はある。
デプロイ先はこちらです。
引用元:
Kotshi+MVVMをカジュアルに採用してみた。
GWも過ぎて、ハイテンションで迎えた令和もすでに鬱々とした気分で過ごす日々です。
今回はKotlinでアウトプットする機会があったので、便利ライブラリを簡単に紹介します。
ネイティブアプリケーション開発においては、バックエンド側のWeb-APIをRailsなどで用意することが多く、Kotlinで開発するクライアント側はリクエストを送信し、レスポンスに乗っているJsonを整形することで表示するデータを取得します。
しかし、null許容を搭載しているKotlinにおいてはKotlinJsonAdapterFactoryなるものを追加しない場合に空のJsonをパースする際にぬるぽが発生したり、
Moshiなるアダプターを使用するもののリフレクションを搭載している為に解釈が遅いといった問題があります。
Jsonを解析する時の問題
まだこれらの利点を払拭するような実感は得ていませんが、今回はKotshiというライブラリを使用することにしました。
よくある画像とコメントをリスト形式でレスポンスするAPIを例に、下記のようなクラスを実装しています。
Response
{
params: {
total: 100, # 総レコード数
offset: 50, # オフセット
limit: 5 # リクエスト時指定の取得数
},
album_records: [
{
"image_url": "https://album-records.herokuapp.com/images/hoge.jpg",
"comment": "Hoge",
"tag": "programming",
"recorded_at": "2019-05-10 00:04:00"
},
{
"image_url": "https://album-records.herokuapp.com/images/fuga.jpg",
"comment": "Fuga",
"tag": "programming",
"recorded_at": "2019-05-10 10:12:50"
},
...
Client
// 実際にJsonを解析するアダプタークラス
@KotshiJsonAdapterFactory
data class Album(
@field:Json(name = "params") var params: GetDataParams,
@field:Json(name = "album_records") var albumRecords: List
) {companion object {
val INSTANCE: AlbumRecord.Companion = AlbumRecord
@ToJson fun toJson(writer: JsonWriter, value: AlbumRecord, stringAdapter: JsonAdapter) {
stringAdapter.toJson(writer, value)
}
}
}// 取得したいインナークラス(album_recordsの中身)
data class AlbumRecord(
val image_url: String?,
val comment: String,
@Json(name = "tag") val tag: String?,
val recorded_at: String?
)// リクエスト時のパラメータ(今回は使わない部分)
@JsonSerializable
data class GetDataParams(
val total: String,
val offet: String,
val limit: String
)// レスポンスを格納するViewModel
class AlbumRecordViewModel: ViewModel() {
val albumRecoLiveData = MediatorLiveData>()
}
@field:Json(name = "params") のように指定することで、Jsonを解析してくれる名前を与える事が出来、後置の名前でクラスのメンバとして振る舞います。
Kotlinのdata classはtoStringなどが備わっているので、パースで入り用の際にはデータを処理するメソッドを用意する手間を無くし、憂鬱な気持ちも軽減してくれます。
今回はリスト形式のオブジェクトが含まれていた為に、クラスを切り分けつつうまく解釈してくれるような構造にしました。不必要な部分については無視してくれる便利なアノテーションも存在しますが、下記のエラーに対応していて実装はしませんでした。
Exception in thread "main" java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 16 path $.response
https://stackoverflow.com/questions/41649731/gson-expected-a-string-but-was-begin-object
つまりはStringとして期待されたメンバはオブジェクトでやってきちゃったよ!
どうしろ言うんじゃい!!!
と言うKotlin(もといJava)様の叫びです。
これは上のようにparamsとalbum_recordを切り分けずにひとつのクラスで解析しようとしていたところ頻発して、メンバ宣言の位置変えをしてみてもうまくいきませんでした。
で、実際に解析をしたいメンバを連ねるだけではダメで。
companionに書かれている専用解析変数&メソッドが鍵となります。
解析したいクラスの情報を渡して、そこではどんな感じにパースすんのと言うメソッドもimplementします。(書かなければ@FromJsonか@ToJson実装しないとこれ使えへんでと言われる)
リクエストをどうするか問題
今回リクエストに使うライブラリはOkHttp3にしてみました。触った事なかったから。
こいつはGETする時にはパラメータを変えたいなと思った時に、インスタンスがイミュータブルなもので、もうひとつ新たなものを用意しないと調整ができないと言うものでした。
Web触っていた時にはerbさんが非同期でリクエスト取ってきてくれていたものだけど、今回扱ってるものは小さなパソコン、Androidなので極力パフォーマンスも考慮して実装していきたい。
そこで画面回転があろうとインスタンスを死守してくれるViewModelさんの力を借りました。
プライベートアプリなのであらかじめ全てのデータを取得して、適当にページングして少しずつ見せる感じに。画像の表示はPicassoを使ったのでスマートにキャッシュを持ってくれて、表示もサクサクです。
諸事情でデモやコードは上げてないですが、許可が取れたら上げてみます。
今回の反省点はと言うと、なんだかんだと困ったらすぐに検索してしまい、ライブラリのソースコードをきちんと読むことを疎かにしていたって事です。ライブラリもコードなの、ああそういえば昔参加してる勉強会でひたすらコードリーディングしてる人いたけど、あれが正なのかなあ…
(イケてる開発者のトレンドはまだまだ理解できないよ…)
基本情報技術者を受けてきた
お疲れさまです。今年の本命枠とも言える資格試験を受けてきましたが、惨敗の結果に終わりました。。
理由は明白で、午後試験の対策をぬかっていたところだと思います。
基本情報技術者(以下基本情報)は午前と午後科目に分かれており、両方の試験で6割以上を期待され、それを超えることによって初めて合格となります。
本年の講評的に、万遍なく計算問題などは出題されていたものの、ハードウェアやトリッキーな問題は出ず(回路もすごく無難なものが出た)、他年度に比べて平易だったと言います。。
自己採点では午前は6割を超える事が出来ましたが、午後は50%程度しか得点できず、悔しい結果となりました。
午後試験問題の反省点
ソフトウェア設計(WebAPI)、アルゴリズムでの大コケ
一番痛かったのは、認めたくはないですがWebAPIでの大コケです。
DB, WebAPIの仕様が羅列されており、これを要約した内容に解答したり、
機能実装の例に解答するような問題です。
絵を描いて関連を把握したのは悪くなかったとは思うのですが、実際に動作するところまでを冷静に想像する必要があったと思います。ただ前提の内容を真に受けるようではダメ…
1番最初の問題はとっても基本的な問いです。
認証関連WebAPIと、登録ツールからアクセスできる。よって、申し込みDBに格納されている情報は_________である。
ここは申込者IDと解答しましたが、正解は申込者情報と検査農産物情報なのでした。(5)検査管理システムに登録された検査結果を確認して、申込者に検査結果を報告する とあるので、検査農産物テーブルは中間テーブルの役割をしていたと考えられます。
申込者←→検査農産物←→検査結果情報
こんな感じで参照することができた。
アルゴリズムについては正直テンパりながら問題を読んでいたので、お絵かきと表が全然合わなくてさらにテンパっていた。
後半に進むにつれて時間が全然足りなくて、焦りが次第に諦念に変わった感じだったな…
解答の順番はこんな感じでした。
(プログラミング)Java→情報セキュリティ→DB→ネットワーク→ソフトウェア→プロジェクトマネジメント→アルゴリズム
午後問題のよかった点
データベースとマネジメントはしっかりと得点できていた
といっても上記以外の基本的な問題の得点はしっかり得られていたと思います。
業務でも触った事があったLIKEや前提に解答が書いてあるものは簡単に感じました。(ギリギリで=でワイルドカードが使えるのかを迷ってしまったのは内緒。)
主キー=UNIQUEやろ!!みたいな思い込みが失点の原因となってしまいました。
マネジメントは計算もピッタリと合っていて、利用部門から仕様変更の依頼などは業務内でもよくある光景なので
「あーうちもこんな感じでふるいがいればいいのにな…」と感想が出るくらいには問題と向き合う余裕がありました。
Nuxtの環境構築をDockerでサクッと実現する
時間がない人の為のDockerfile
FROM node:10-alpine RUN apk add git emacs RUN npm install -g vue-cli WORKDIR /app ENV HOST 0.0.0.0 EXPOSE 3000
version: '3' services: app: build: . tty: true volumes: - "./../app:/app" ports: - "3000:3000"
足りないものは自分で継ぎ足してね(・ω・`)
要らないもの然り。
brewなど入れてあるのでgitでpushやら最低限のことはできるはず
今回はベンチマーク取ってないけど、
体感的にAlphineを使ったほうが適当なイメージをpullしたり、拾ったDockerfileを使うよりもファンが昂ぶらなくて良いと思った。
OSって色々詰まってるんだね。
npm触り出すとすぐ依存性とか詰まるから、今の所は助かってる。。
docker-compose build
docker-compose up
docker ps
(idチェック)
docker exec -it (container_id) sh
brew入れ方はLinux Brewのページを参照する
uname -a
(動いているサーバのお名前頂戴)
Linux e8aa47ead274 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux
appプロジェクト配下で
npm i --save
npm run dev
うわあああああん!!(別案件)
ご参考 https://github.com/Linuxbrew/brew/wiki/Alpine-Linux https://techblog.zozo.com/entry/docker_image_slim_in_alpinelinux https://qiita.com/tukiyo3/items/247f853c81bf00e82c11
MotionLayoutでリッチなUXをxmlのみで表現してみました
長いこと更新をサボってました。。 最近はもっぱらKotlinでのAndroid開発に従事していますが、ビューの原則はMaterialDesignを取り入れ、バラツキのあるデバイスの端末性能を吸収する優秀なプラグインが用意してあったりと、奥深く、ネイティブは楽しい分野だと感じています!
(デザイナーさんから思いもよらぬ仕様を叩きつけられることもありますが…)
勉強がてらに新しい部品に触る事も楽しいです!
そんな感じでいただいたデザインと納期の間で不安に苛まれていたとある日、何某Kotlinイベントに参加してみました。 Droid会議直後の温まった会場でMotionLayoutについて教えていただく機会があり、実装してみたので軽くレビューしていきます🐕
1. xmlファイルでリッチな動きが実現するMotionLayout
実装例・導入方法など詳しくはこちらをご覧ください💁♀️
https://github.com/googlesamples/android-ConstraintLayoutExamples
Google I/O 2018で紹介されているようですね♩ youtu.be
< 実装例gif >(でかくて申し訳ない…)
gfycat.comRecyclerViewは別途Adapterクラスなどの実装が必要なので、実装にはこちらを参照してください。
2. 導入 : build.gradle
build.gradle
dependencies { implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha3' }
3. ベースのxmlを記述する
base.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".HogeActivity"> <android.support.constraint.motion.MotionLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/design_default_color_primary" app:layoutDescription="@xml/motion" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> <android.support.v7.widget.RecyclerView android:id="@+id/top_recycler_view" android:layout_width="wrap_content" android:layout_height="500dp" android:paddingTop="30dp" android:background="@drawable/recycler_view_bkground" android:scrollbars="vertical"> </android.support.v7.widget.RecyclerView> </android.support.constraint.motion.MotionLayout> </android.support.constraint.ConstraintLayout>
4. motionタグの追加
motion.xml
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <Transition motion:constraintSetStart="@id/base_state" motion:constraintSetEnd="@id/open_state" motion:duration="3000"> <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@id/top_recycler_view" motion:touchAnchorSide="top"> </OnSwipe> </Transition> <ConstraintSet android:id="@+id/base_state"> <Constraint android:id="@id/top_recycler_view"> <Layout motion:layout_width="400dp" motion:layout_height="100dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintVertical_bias="1.0"> </Layout> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/open_state"> <Constraint android:id="@id/top_recycler_view"> <Layout motion:layout_width="400dp" motion:layout_height="500dp" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintBottom_toBottomOf="parent"> </Layout> </Constraint> </ConstraintSet> </MotionScene>
非常にさっくりと説明すると、app:layoutDescription="@xml/motion_activity_first_visit"のように設定したmotion.xmlで該当の部品のアニメーションを指定します。 今回のアニメーションはベースの状態(@id/base_state)とアニメーション終了時の状態(@id/open_state)です。 motionタグで囲う範囲については、アニメーションの可動域となるので、くれぐれも他の要素が消え去らないように注意してください。(プレビューすれば一目瞭然ですが…)
今回はmotion.xmlで、部品上部において上にスワイプする操作で、ビューがプルアップ+ダウンする挙動を制御しています。 フリック(軽く指で弾くような動作、スライドショーをめくるような動き)では反応しないので、適宜使用するタグを付け替えてみてください。
公式のリポジトリの例が豊富なので非常におすすめです。
https://github.com/googlesamples/android-ConstraintLayoutExamples
報告者を指定日時で通知するスクリプト<Slackユーザー向け / コード付き>
N番煎じ?
月末に部門定例会があるので、公平にみんなの中から報告者を決めることにしました💁♀️
GoogleAppScript
var postUrl = 'https://hooks.slack.com/services/HOGE/HOGEHOGE/HOGEHOGEHOGE'; …①
var icon = ':hatching_chick:';
var index = 0;
var checked_engeneer = '';
var icon_url = 'https://hogehoge-team.slack.com/team/HOGEHOGE'
var engeneer_ids = ['<@hoge1_slackId>', '<@hoge2_slackId>', '<@hoge3_slackId>', '<@hoge4_slackId>', '<@hoge5_slackId>'] …②
function shuffleReporter() {
index = Math.floor(Math.random()*6)var id = engeneer_ids[index]
var jsonData =
{
"username" : '悪意のないchick',
"channel" : "#development",
"icon_emoji" : icon,
"text" : '今月の開発部門報告者は' + id + 'さんだよー\n開発のみんなの1ヶ月のタスクと、最近自分ががんばったことを話してね!',
"icon_url" : icon_url
};var payload = JSON.stringify(jsonData);
var options =
{
"method" : "post",
"contentType" : "application/json",
"payload" : payload,
};UrlFetchApp.fetch(postUrl, options);
}
①… SlackのwebhookAppをオンにして、出力URLを取得しましょう。
②… メンション用の個人タグです。付けないとメンション飛びません。個人の詳細ページからmemberIdとして取得できます。
悪意のないひよこが教えてくれます。(これはKotlin因み…🐤)
Cronもあるらしいけど、今回は手動で時間設定。
時間主導型の設定フローは下記画像を参考にしてください。
カレンダーのイベント抜いて設定したら、また更新します。