プログラミングしながらできる、健康への近道講座:2回目 - Re.Ra.Ku アドベントカレンダー day 9

こんにちは!
Re.Ra.Kuの泉原です。

1回目を読んで下さった皆様、ありがとうございます!
ブックマークや星、facebookシェアもしていただけて、 モデルのタミー共々、大変嬉しく感じています。

今回は、プログラミングしながらできる、肩甲骨のストレッチ方法をお伝えします。

肩甲骨の仕組み

弊社のボディケアでも注目している肩甲骨。簡単に仕組みをお伝えしてからストレッチの話に入っていきます。

1. 17種類の筋肉が付いている

肩甲骨には、17種類の筋肉がついています。その筋肉は、首や背中、骨盤と繋がっています。
そのため、肩甲骨の動きが硬くなると、首や腰を引っ張ってしまい、体の前後左右のバランスが崩れ、姿勢が悪くなってしまいます。
逆に言うと、肩甲骨がほぐれると、首や腰も一緒にほぐれるため、早く効率的に体を整えたいという方には、肩甲骨のケアをお勧めしています。
また、肩甲骨の前には肺があるため、肩甲骨の位置が整うと肺が広がり、呼吸もより大きくなりやすくなります。

2. 褐色脂肪細胞が密集している

肩甲骨の間には、褐色脂肪細胞という細胞が密集しています。この細胞は、体脂肪を燃焼し、熱を生み出す細胞と言われています。
人体には肩甲骨以外に、首、わき下、心臓・腎臓部分にありますが、中でも肩甲骨の周囲は熱量の出し方がとても多く、筋肉の数十倍もあると言われています。
肩を大きく動かすことで、痩せやすい体づくりも期待できます。

肩甲骨のストレッチ

ストレッチは、座りながらできるストレッチを紹介します。
ストレッチを行う時は、綺麗な姿勢を作ってから行うように意識してみてください。
姿勢の保ち方は、前回の講座でお伝えしています。ご確認下さい。
プログラミングしながらできる、健康への近道講座:1回目 - Re.Ra.Ku アドベントカレンダー day 3 - Re.Ra.Ku tech blog

綺麗な姿勢を作ったら、息を吸って、左手で椅子をつかんで、ひじの裏を表に向けて、自然な呼吸で右側に体を倒します。 f:id:nozomiii:20161208145714j:plain

気持ち良く伸びたな、と実感できたら息を吸いながらゆっくり体を戻します。
戻した後は反対側も行います。
側面を伸ばすことで、普段縮こまっている腕の疲れをとります。

次に、
背もたれの後ろで手を組んで、肘を背もたれに引っ掛けて、おへそから体を倒します。
この時、首は下げないように注意してください。
f:id:nozomiii:20161208145730j:plain

1つ目のストレッチと同様、自然な呼吸で伸ばしていき、
気持ち良く伸びたなと実感できたところで息を吸いながゆっくり体を戻します。
前のめりになっている肩甲骨を正しい位置に近づけるストレッチです。

最後は、
両手を机につき、臀部で椅子を行けるところもまで後ろに下げます。
f:id:nozomiii:20161208145748j:plain

肩甲骨を伸ばすとともに、背中もしっかり伸ばします。

どのストレッチも、どこまで出来れば良い悪い、というものではなく、ご自身が一番気持ちが良いと感じる強度が一番良いストレッチです。
ご自身のペースで行ってみてください。

モデルについて

お伝えしそびれていましたが、今回の講座でモデルをしてくれているのは、弊社デザイナーのタミーです。
ドイツ人ですが、日本語も英語もペラペラなトライリンガルです。
弊社の中では、テキストデザインはもちろんのこと、店舗デザインや動画制作等、デザイン全般担当してくれています。
店舗の販促ツールや店舗デザインルール等も、タミーが管理しています。
店舗の近くに寄る機会があれば、ぜひデザイン面も気にかけて見ていただけると嬉しいです。
reraku.jp

次回は、プログラミングの合間にできる簡単なストレッチ方法をお伝えします。
今後とも宜しくお願いいたします!

リラクのサーバサイド事情 with Scala / ドメイン編 - Re.Ra.Ku アドベントカレンダー day 8

Re.Ra.Ku アドベントカレンダー 8日目です。

こんにちは。ヘルステックチームの近藤です。

2日目のリラクのサーバサイド事情 with Scalaにて宣言した通り、今回はサーバサイド事情の中でも中核となるドメインについての話です。

「ドメイン」とは何か

「ドメイン (domain)」はそもそも「領域」という意味ですが、様々な箇所で使われ、文脈によって指すものが異なりやすい言葉だと思います。

今私が掲題している「ドメイン」とは、レイヤードアーキテクチャで言うところのドメイン層で、ビジネス要件の塊です。含まれるものは:

  • ユーザ名は必須かつ20文字以内
  • ログインできるユーザはステータス: Activatedのみに限り、ステータス: Registeredおよびステータス: Suspendedのユーザはログインできない
  • チャットルームはユーザが自由に作成することができ、作成したユーザがオーナーとなる
  • チャットルームのオーナーは任意のユーザーをチャットルームに参加させられる。既に参加済みのユーザーを参加させようとすると、UserAlreadyJoinedというエラーが起きる

などなどで、あくまで「方針」です。

反対に含まれない(含みたくない)ものは:

  • ユーザなどの情報はPostgreSQLに保存する
  • ユーザのアイコンはAmazon S3に保存する
  • 各種操作のリクエストはHTTPにて受け付ける

などの、何によって実現するかという「詳細」です。

ですが、何故詳細を含みたくないのか?それは方針が詳細に引き摺られてしまうからです。

詳細はそれぞれが特有のインタフェースを持っています。PostgreSQLならばSQLにてやりとりしますし、Amazon S3とはHTTP APIでやりとりするでしょう。もしその情報をドメインに持ち込んでしまうと、ドメインはSQLやHTTPという方法に依存し、それらに関するコードを含むようになります。

正直この時点で匂います。関心事が混ざり合っている匂いです。そこに追い討ちとして、「永続化先をPostgreSQLからMongoDBに変更する」という要請が来たらどうなるでしょう?処理のコアとなるドメインには変更がないのに、ドメインのコードを変更しなければなりません。おかしい話ですよね?

また、表現の自由度が低下するので、その点を私たちは気にしています。詳細にはフレームワークも含まれています。フレームワークは設計を制限し、表現を規定します。これによって開発者間のコミュニケーションがしやすくなるなどのメリットもあるのですが、ことドメインにおいてはそれが枷となりやすく、足を引っ張りがちです。よって嫌います。

私はとある漫画が好きなので、思わず「ドメインを設計する時はね、詳細に引っ張られず、自由でなんというか」と続けてしまいたくなるのですが、その通りだと思います*1

DDDの採用

ドメインの設計にはDDD (Domain-driven design)を採用しています。

詳細によって表現の自由度は下げたくないのですが、表現の指針がないのもそれはそれで無法地帯です。そういうわけでドメインの表現としてはメジャーであろうDDDを選びました。よってコード中にはEntityやRepositoryといった概念が出て来ます。

もちろんDDDの核であるユビキタス言語にも気を付けています。週1でチーム内の定例ミーティングがあるのですが、そこでは現在関わっているプロジェクト単位で使用している言葉(日本語/英語共に)に違和感はないかを議論し、常にメンバー間で擦り合わせるようにしています。そのとき「現場ではこういう言葉が使われている」や「こっちの方が実際の要件に即している」ということがあれば、即座にコードに反映していきます。

そんな感じで試行錯誤しつつも結構マジメにDDDを実践していっています。

値オブジェクト

そんなわけでDDDの概念である値オブジェクト、エンティティ、リポジトリなどがどうなっているか、です。

よくあるDDDの説明で、エンティティはいの一番に語られるものだと思っていますが、関連する属性を取りまとめた論理的な単位である値オブジェクトの方が分かりやすいと思っています。ですので、まず値オブジェクトに触れていきます。

値オブジェクトの特徴は:

  • 一度生成したらその値オブジェクトが持つ属性は不変であり
  • 属性すべてが等値であれば、値オブジェクトそのものが等値である

というものです。それ以外にも実践ドメイン駆動設計には「概念的な統一体」などを挙げています。それらは何を値オブジェクトとしてモデリングすべきなのかを示しているのですが、今回はさきほど挙げた2点に絞ってみます。

値オブジェクトの特徴は「不変」で「属性によって等値かを判断する」という点です。Scalaはこれにおあつらえ向きのものがあります。case classです。よって値オブジェクトはすべてcase classを用いて定義しています。以下が値オブジェクトのコード例です。

package domain.user

import java.util.UUID

import org.usagram.clarify._

sealed trait UserId {
  def value: UUID
}

private case class UserIdImpl(value: UUID) extends UserId

object UserId {
  def apply(value: Indefinite[UUID]): UserId = {
    // valueをバリデーションし、パスしたらUserIdImplを生成し、返す
  }
}

case classで定義していると言ったのに、いきなりtraitを用いています。シンプルに書けば:

package domain.user

import java.util.UUID

case class UserId(value: UUID) {
  // valueをバリデーション
}

となり、大分すっきりするのに、なぜ先に掲載した回りくどい書き方をしているのか。それは以下に起因します。

  • バリデーションを強制したい: case class内にバリデーションを書くというのはあくまで任意なので、バリデーション忘れなどのミスには対応できません(まあ今回の例ではjava.util.UUIDなので特にバリデーションすることもないのですが……)
  • case classのapplyを使用禁止にしたい: バリデーションを強制するためにcase classの外、例えばコンパニオンオブジェクトのapplyにてバリデーションを強制する仕組みを導入したとしましょう。しかし、その場合コンパニオンオブジェクトのapplyはcase classに既に使用されているため使えません。じゃあ別のメソッド名を使うか。そうなるとcase classのapplyを使ってバリデーションを擦り抜けることが出来てしまいます

とまあ、ようするに「バリデーションを強制するためにcase classデフォルトのapplyを使用禁止にしたい」という事情があります。ですので、回りくどい書き方をしているのです。

sealed trait UserId {
  def value: UUID
}

まずtraitで値オブジェクトの型を定義します。sealed修飾子によってこのtraitが定義されたファイル以外でUserId型となりえるクラスを定義できなくなります。

private case class UserIdImpl(value: UUID) extends UserId

UserId型の実装です。ここでcase classを使っているため、「不変」かつ「属性によって等値かを判断する」という特徴を備えます。しかし、このcase classはprivateであるため、外から直接扱えません。このままではUserId型のオブジェクトを生成できなくなります。

object UserId {
  def apply(value: Indefinite[UUID]): UserId = {
    // valueをバリデーションし、パスしたらUserIdImplを生成し、返す
  }
}

ですのでコンパニオンオブジェクトに生成のためのメソッドを定義します。ここではapplyとしています。UserIdImpl case classのコンパニオンオブジェクトにapplyを定義することは出来ません(2回定義されているよとコンパイラに怒られます)が、これはUserId traitのコンパニオンオブジェクトなので、問題なくapplyを定義することが出来ます。ここでバリデーションを強制しています。詳細は後日としますが、Indefinite[UUID]という型がそれです。

こうすると:

  • UserId traitはsealed修飾子によって他のファイルにて継承することが出来ない。当然 new UserId { val value = ... } とその場でオブジェクトを生成することも不可能
  • UserIdImpl case classはprivate修飾子によって参照することが出来ない
  • よって必ずUserId objectのapplyメソッドを使用する必要があり、バリデーションは強制される

となり、要望を満たせます。

あとはビジネス要件に応じたバリデーションを書き、値オブジェクトを操作する必要があれば、traitの方にユビキタス言語に則ったメソッド名で定義していきます。

エンティティ

次にエンティティです。エンティティは以下の特徴を持ちます。

  • 識別子を持ち
  • 属性の変更が可能である

しかし、識別子を持つことはともかく、属性の変更が可能であっても、私たちはイミュータブルにモデリングしています。そっちの方がシンプルに考えられ、テストも容易ですしね。ですので、エンティティ自身を操作するすべてのメソッドにおいて、操作された結果の新たなエンティティを返すようになっています(もちろん識別子は同一です)。

以下がエンティティの例です。値オブジェクトのときと同様、バリデーションを強制するようになっています。

package domain.user

import org.usagram.clarify._

sealed trait User extends Entity[UserId] {
  def name: String
  
  def email: String
  
  def updateName(newName: String): User =
    User(Indefinite(id), Indefinite(newName), Indefinite(email))
}

private class UserImpl(val id: UserId, val name: String, val email: String) extends User

object User {
  def apply(id: Indefinite[UserId], name: Indefinite[String], email: Indefinite[String]): User = {
    // id, nameおよびemailをバリデーションし、パスしたらUserImplを生成し、返す
  }
}

エンティティが等値であるかどうかは、識別子が等値であるかによって判断されるべきなので、case classではなくclassとなっています。その代わり、Entity[ID] traitを利用しています。Entity[ID] traitは以下のように定義しています。

trait Entity[ID] {
  def id: ID

  override final def equals(obj: Any) = obj match {
    case that: Entity[_] => id == that.id
    case _               => false
  }

  override final def hashCode = 31 * id.##
}

Entity[ID]型は必ずid属性を持つようになります。これによってエンティティの条件である「識別子を持つ」をクリアします。あとはequalsとhashCodeをオーバライドして、識別子による等値を実現しています。実際のコードではID型パラメータに上限境界を設けたりしていますけどね。

リポジトリ

リポジトリはエンティティ(正確には集約)の永続化のための仕組みです。行うことは:

  • エンティティの取り出し
  • エンティティの格納

が主です。Userエンティティのためのリポジトリ、UserRepositoryは以下のようなコードになっています。

package domain.user

trait UserRepository[X] {
  def find(id: UserId)(implicit session: X): User
  
  def store(user: User)(implicit session: X): Unit
}

これだけです。Repositoryはたしかに永続化するための仕組みなのですが、散々言うように「何を使って永続化するか」がドメインにあってはいけません。よって提供するのは「UserIdを与え、それに対応するUserを返すfindメソッド」と「Userを与え、それを格納するstoreメソッド」というインタフェースだけなのが望ましいです。

なお上記例では定義していませんが、リポジトリにはドメインの処理を実現するための問い合わせ(例: メールアドレスの重複を防ぐために、既に使用済みでないかをリポジトリに問い合わせるなど)用メソッドがあっても構いません。

もちろん、インタフェースだけではアプリケーションとして完成しません。ですので、リラクのサーバサイド事情 with Scalaに書いたよう、インフラストラクチャ層で上記UserRepositoryを実装し、アプリケーション層にて実装されたUserRepositoryを使うように書いていきます。

サービス

あとはサービスですかね。サービスが特定の方法に依存する場合、当然ドメインにはインタフェースのみを定義し、インフラストラクチャ層で実装しています。例えばメールの送信とか。

package domain.user

trait SendActivationEmailService {
  def apply(user: User): Unit
}

サービスが複数のリポジトリやサービスをどうこうする程度のものであれば、サービスの処理自体はドメインに書き、アプリケーション層にてそのリポジトリをサービスに渡すようにしていたりはします。こんな感じ。

package domain.user

import java.util.UUID

trait RegisterUserService[X] {
  def userRepo: UserRepository[X]
  
  def sendActivationEmailService: SendActivationEmailService
  
  def apply(name: String, email: String)(implicit session: X): User = {
    val userId = UserId(Indefinite(UUID.randomUUID))
    val user = User(Indefinite(userId), Indefinite(name), Indefinite(email))
    
    if (userRepo.findByEmail(user.email).isDefined) {
      throw new UserEmailAlreadyUsed(email)
    }
    
    userRepo.store(user)
    sendActivationEmailService(user)
    user
  }
}

class UserEmailAlreadyUsed(email: String) extends Exception(s"$email is already used in other user")

これをアプリケーションサービスで:

package application.user

// 省略

object RegisterUserApplicationService {
  val registerUserService = new RegisterUserService[DBSession] {
    val userRepo = new JDBCUserRepository()
    
    val sendActivationEmailService = new SMTPSendActivationEmailService(...)
  }
  
  def apply(command: RegisterUserCommand): User =
    DB localTx { implicit session =>
      registerUserService(command.name, command.email)
    }
}

こんな感じで組み込みます。

今後やりたいこと

弊社ではドメインを値オブジェクト、エンティティ、リポジトリ、そしてサービスの4種類を用いてモデリングしています。ですので、イベントなどは用いていません。それらにはまだ手付かずですね。

ゆくゆくはイベントを導入し、エンティティ(集約)はイベントの集合(イベントソーシング)で表し、それらによるメリットを自らの手で確かめていきたいです。

しかし、イベントソーシングを導入するとなると、永続化される情報はイベントの集合になり、問い合わせが困難になるため、当然問い合わせ専用のモデル(RDB的な話ならばテーブル)が必要になり、それってもうCQRSじゃん?と、あれもこれもとなります。正直まだ中々踏ん切りが付いていないところですね。まあ小さく始めている途中ということで。

というわけで弊社のサーバサイドのドメイン事情でした。それでは。

*1:ちなみにこの場合の「自由」は何をしてもいいという意味ではなく、要件を満たすための前提としての自由です

SwiftでJSONのマッピングにはUnboxが便利らしい - Re.Ra.Ku アドベントカレンダー day 7

Re.Ra.Ku アドベントカレンダー 7日目です。

こんにちは、Re.Ra.Kuさんのパートナーとして開発に携わっています、神場です。 アドベントカレンダーをやるということで、せっかくならと思い手を挙げたところ参加出来ることになりましたので、書かせていただきます!

概要

今回はSwift製のJSON decoderであるUnboxをプロダクトで採用してみて、かなり使いやすいということが分かりましたので、いくつか使い方のサンプルを紹介するという内容の記事になっています。

Unboxを選択した理由

Swift3でJSONを扱うライブラリにはSwiftyJSONArgoがあると思いますが、今取り掛かっているプロダクトでは

  • マッピング時に型推論が出来る
  • 異なる型への変換が楽に出来る
  • インターフェースが直感的である
  • 実装が軽い
  • Githubのスター数がある程度(300以上ぐらい?)付いている

等の理由からUnboxを採用しました。

この記事で紹介する内容

この記事では

続きを読む

アプリ用画像アセットを書き出す Sketch Plugin を作ってみた話 - Re.Ra.Ku アドベントカレンダー day 6

Re.Ra.Ku アドベントカレンダー 6日目です。

こんにちは。ヘルステックチームの磯貝です。主に iOS アプリ開発を担当しています。

iOS 担当です……が、今回は、アプリ内で用いる画像アセットの生成に関して書いていこうと思います!

背景

弊チームでは長らく、モバイルアプリケーションのモックづくりや画像アセット作成に Adobe Illustrator を使用してきました。

しかし最近ではチームメンバーが増え、開発スピードも上がってきたため、モックやアセットに対してより多くのメンバーが簡単にアクセスできるよう、Sketch への転換を進めています。このあたりの話については、後日別の記事で書ければと思います。

転換にあたり、ワークフローの中で必要となるいくつかの機能は、Sketch デフォルトでは実現できないことがわかりました。そこで、その中の一つである、モバイルアプリケーションのプラットフォームに合わせて最適な画像アセットを書き出す機能を、プラグインとして実装してみました。

Sketch Plugin 概要

Sketch はプラグインによる機能拡張に対応しています。プラグインの仕様は詳しく公開されており、ある程度 Mac (もしくは iOS) のフレームワークや JavaScript に通じていれば、作成することができます。現にプラグインのほとんどはサードパーティから提供され、公式サイトや GitHub 等で数多く公開されています。

Sketch Plugin は、CocoaScript という、JavaScript に Objective-C 記法での Cocoa フレームワークアクセス機能を付加した言語を用いて作成します。近い将来 Swift でのプラグイン作成も可能になるとのことですが、今のところはまだ対応していません。

Sketch Plugin では CocoaScript を用いることで、Sketch の提供する API だけでなく、Cocoa フレームワークを通して macOS へアクセスすることができます。そのため、ファイルの読み書きをはじめとした多くの機能を、思ったままに実現することができます。

var helloWorld = function() {
  log("hello, world!");

  var osVersion = [[NSProcessInfo processInfo] operatingSystemVersionString];
  log(osVersion);
}

helloWorld();

// => "hello, world!"
// => "Version 10.11.6 (Build 15G1004)"

ちなみに Sketch は、以前は App Store でも公開されていましたが、今は公式サイトからのダウンロードのみとなっています。そのため、App Sandboxing に関しては、この記事では触れません。

Tips

プラグインの実装に入る前に、開発における Tips をいくつか紹介します。

コード片実行環境

Sketch.app には、1ファイルに収まる CocoaScript を Sketch 上で実行するための環境が用意されています。
メニューの、[Plugins] > [Custom Plugin]を選択してください。先程の Hello World コード片は、この画面からも実行できます。

log() メソッドを使えば、実行結果をコンソール上に出力することができます。

Console.app

プラグイン内における log() の結果は、Mac 標準のグローバルなログとして、/var/log/system.log へ出力されます。同時に、Sketch.app 自体が吐き出すログも確認することができるので、エラーメッセージもある程度は読むことができます。

このログ出力を簡単にフィルタして閲覧するために、Mac にデフォルトでインストールされている Console.app を使います。

Console.app を開いたら、メニューの [ファイル] > [新規システムログクエリー] を選択し、新たなフィルタ設定を追加しましょう。参考までに、私が使用している設定を以下に挙げます:

f:id:y1soga1:20161205020423p:plain

CocoaScript のブラケット表記に関して

CocoaScript は、Sketch や Cocoa フレームワークの API を、Objective-C のブラケット表記を用いて実行することができます。しかし、JavaScript のコード内にブラケット表記が紛れているのは、正直気持ち悪いです。JavaScript 向けのコードフォーマッティングも効きません。

実は、ブラケット表記は JavaScript 形式のメソッドに置き換えることができます。外部引数名をアンダースコア _ で繋いだものをメソッド名とし、引数はまとめて与えます。

var month;

// こんなブラケット表記は
month = [[NSCalendar currentCalendar] component:NSCalendarUnitMonth fromDate:[NSDate new]];

log(month) // => "12"

// こう書くこともできます
month = NSCalendar.currentCalendar().component_fromDate(NSCalendarUnitMonth, NSDate.new());

log(month) // => "12"

画像アセット書き出しプラグイン

さて、ここからは実際に画像を書き出すプラグインを作っていきます。

要件

今回の要件は、以下の通りです。

  • アートボード直下に存在するレイヤーを、透過 png 画像として書き出す。
  • iOS 向けの書き出しは、Asset Catalog 形式として書き出す。その際、アートボードごとに名前空間を切る。
  • Android 向けの書き出しは、resources ディレクトリ以下に各解像度の画像を書き出す。
  • 書き出しの設定には、アートボード名やレイヤー名を用いる。
    • アートボード名は、export:format_a:format_b:base_name とする。
      • アートボードに含まれるルートレイヤーが、それぞれ画像アセットとして書き出される。
      • フォーマットは複数指定できる。対応するのは、iphone, ipad, ios (universal), android (/resources), android-mipmap (/mipmap)
      • base_name は、Asset Catalog においてはサブディレクトリ名となり、名前空間が切られる。Android resources においては、ファイル名の prefix となる。
    • レイヤー名の頭に noexport: が含まれる場合、そのレイヤーは書き出さない。

あとは要件に合わせて粛々と書いていくだけなのですが、数点、ポイントとなりそうな部分をピックアップして解説します。

最終的に出来上がったものは、GitHub で公開しています: sketch-mobile-assets-generator

ファイル構造

Sketch Plugin のファイル構造は、以下のようになっています。

GenerateMobileAssets.sketchplugin    // プラグイン Bundle の拡張子は .sketchplugin
  Contents/                       // 必須
    Sketch/                       // 必須
      manifest.json               // 必須
      generate.cocoascript        // manifest.json から、コマンドに応じて実行するスクリプトを指定する
      foo_script.js
      bar_script.any_extension    // 拡張子はなんでも ok
      SomeOptionalDirectory/      // ディレクトリも追加可能
        shared_contents.js

manifest.json

プラグインのメタデータです。

{
  "name": "Generate mobile assets",
  "description": "Plugins to generate assets for iOS and Android.",
  "author": "yisogai",
  "version": 0.1,
  "identifier": "jp.co.reraku.GenerateMobileAssets",
  "commands": [                          // プラグインとして実行できる操作を記述する
    {
      "name": "Generate All",            // メニューに表示されるタイトル
      "identifier": "all",               
      "shortcut": "ctrl shift e",
      "script": "generate.cocoascript"   // コマンド選択時に実行されるスクリプトファイル
                                         // デフォルトでは 'onRun()' メソッドが実行される
    },
    {
      "name": "Generate Android",
      "identifier": "android",
      "script": "generate.cocoascript",
      "handler": "android"               // 'onRun()' 以外のメソッドを実行したい場合に指定
    },
    {
      "name": "Generate iOS",
      "identifier": "ios",
      "script": "generate.cocoascript",
      "handler": "ios"
    }
  ],
  "menu": {                              // 実際にプラグインメニューに表示される内容
    "title": "Generate Mobile Assets",   // ルートディレクトリ名
    "items": [
      "all",                             // コマンドの 'identifier' を指定する
      "-",                               // '-' は区切り線となる
      {                                  // ネストすることもできる
        "title": "Platforms",
        "items": [
          "android",
          "ios"
        ]
      }
    ]
  }
}

詳しくはこちら

generate.cocoascript

スクリプトの拡張子は特に決まっていませんが、ここでは公式でも用いられている .cocoascript を使用します。

@import "lib/document_parser.js"
@import "lib/config_loader.js"
@import "lib/exporter.js"

// manifest.json で指定したエントリーポイントとなるメソッドには、context オブジェクトが渡される。
// そこからプラグイン実行対象のファイルへアクセスできる。
var onRun = function(context) {
  _generate(context, ["xcode_asset_catalog", "android_res"])
}

var android = function(context) {
  _generate(context, ["android_res"])
}

var ios = function(context) {
  _generate(context, ["xcode_asset_catalog"])
}

var _generate = function(context, configTypes) {
  var document = context.document
  var baseDir = document.fileURL().path().split(document.displayName())[0] + "build"
  var exporter = new Exporter(document, baseDir)

  var layerGroups = DocumentParser.parse(document)
  var configs = ConfigLoader.load(configTypes)

  exporter.export(layerGroups, configs)
}

lib/*

cocoascript ファイルは、相互にインポートすることができます。ここでは、プラグインの機能を実装しています。
ほとんどは JavaScript のコードであり、特殊な操作は行っていません。一部、Sketch API や Cocoa フレームワークを操作している箇所のみを抜粋します。

// exporter.js

...

Exporter.prototype._exportAsAndroidRes = function(baseName, layers, config) {
  var that = this
  var root = this.baseDirectory + "/" + config.id

  layers.forEach(function(layer){
    config.densities.forEach(function(density){
      // レイヤーのエクスポートには、Sketch API である MSExportRequest を使用する
      var request = MSExportRequest.exportRequestsFromExportableLayer(layer)[0]
      // request に scale を設定することにより、書き出す画像の解像度を変更できる
      request.scale = density.scale
      var filename = baseName + "_" + layer.name() + ".png"
      var file = root + "/" + density.folder + "/" + filename
      // ブラケット表記をアンダースコアによる JavaScript 風表記へ変換して記述
      that.document.saveArtboardOrSlice_toFile(request, file)
    })
  })
}

...
// exporter.js

...

Exporter.prototype._writeObjectToJsonFile = function(object, path, filename) {
  var json = JSON.stringify(object, undefined, 2)
  var string = NSString.stringWithFormat(@"%@", json)
  var file = path + "/" + filename

  // Cocoa フレームワークへのアクセス
  var manager = NSFileManager.defaultManager()
  
  // こちらも、ブラケット表記をアンダースコアによる表記へ変換している。
  // また、null と nil は同等のものとして扱われる。
  manager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, true, null, null)

  string.writeToFile_atomically_encoding_error(file, true, NSUTF8StringEncoding, null)
}

...

まとめ

いかがでしたでしょうか。かなり駆け足になってしまいましたが、自分が Sketch Plugin を作るにあたり、引っ掛かりを感じた箇所については一通り共有できたかと思います。

用途が限定される CocoaScript ですが、みてみると分かる通り、案外簡単に書くことができます。
普段づかいのエディタを秘伝のスクリプトでモリモリ拡張しているそこのあなた! エディタ以外の環境も、ぜひ使いやすいように改良してみてください。

とはいったものの、だんだん iOS が恋しくなってきたので、次回は iOS や Swift に関しての話題を書ければと思います。それでは!


今回作成したプラグイン: sketch-mobile-assets-generator

いってきたぜ #builderscon tokyo 2016 - Re.Ra.Ku アドベントカレンダー day 5

丸山です。この記事はRe.Ra.Kuアドベントカレンダー5日目の記事です。前の記事はアンドロイドアプリの設計手法についての記事でした。明日はおそらくiOSネタになると思います。お楽しみに!

さて、12/3(土)にbuilderscon tokyo 2016というカンファレンスが開催されました。

buildersconは「知らなかった、を聞く」というキャッチフレーズ(?)を掲げた、多様な方面のエンジニアリングに関わる発表が聞ける、「エンジニアのためのお祭り」です。わたしは今回は聴衆として、また、発表者として参加してまいりました。

参加まで

ビルダーズコンの構想を聞いて、「これはスタッフとしてお手伝いしたい!」と思って、最初の方のスタッフミーティングにはちょこちょこ顔を出していたのですが、業務の都合や家庭の事情でなかなかコミットすることができず、途中から存在感が消えてしまっておりました。残念。来年はお役に立ちたい!

とはいえ、絶対に発表はしたいなあと思っており、トークには応募しました。そしたら、あれよあれよと言う間にどんどん面白そうなトークが応募され、「あー、これはトーク通らないかもなあ」と思って半分くらいは発表を諦めていました。

チケット発売のタイミングでは、「チケットを購入し聴衆として参加しよう」!と思っていたのですが、蓋を開けてみればなんとチケットは3時間でソールドアウト!あらためてビルダーズコンの注目度の高さにおどろき、「ああ、もうだめだ、これでトークが採用されなかったらわたしは記念すべき第一回ビルダーズコンになんの関わりもなく終わっていくのか……ああ……」と思ってかなり苦しい気持ちになったことを覚えています。

祈るような気持ちでトーク採択を待ち続けていましたが、なんとかトークを採択していただき(「片手間JavaScripter」にも知ってほしい、Vue.jsで実現するMVVMパターン、Fluxアーキテクチャとの距離)、発表者としてなんとか参加できることになり、意気揚々と参加してきました。忙しい中発表の練習に付き合ってくれた同僚に感謝の意をここで伝えておきたいです。

当日

とにかく最高だった!とくに面白いと思ったトークは以下の通りです。

OSS は Windows で動いてこそ楽しい

ミーハーなので、「うおおおおお生mattnさんや!!」みたいな感情が湧いてきたところから始まったのですが、mattnさん自身からも発表からも「ハックする気持ち」が溢れ出てて、とにかくかっこよかった。質疑応答の最中に、「若いプログラマに勢いだけは負けたくない」とおっしゃっていましたが、家族と子供がいて普段の業務も忙しい中でそのような気持ちを持ち続けることがどれだけ難しいことか!そして、mattnさんほどの方もそのような思いに突き動かされていると言うこと。そのひとことがかなり印象に残っています。

動け!Golang 〜圧倒的IoTツール開発へようこそ〜

Akerunのkazphさん。社名はAkerunじゃなかったはずだけど、Akerunのひと。わたしはソフトウェア技術者なので、普段の仕事の中工場の話に触れることはほとんどというか一切ないのだけれど、web系に馴染みの深い技術で(というかそもそもkazphさんの出自自体がweb系技術者である)リアルの業務を自動化、改善していく内容は「うおお、これはエンジニアリングだ!うおお!」という感じで相当面白かった。

製品にファームを焼くときに、商品個体のidをどうやって書き込むのかと言うとバイナリを書き換えてるんだよ〜ってのは「まあそうか、そらそうだ」という感じだったのですが、そこはPerlやRubyで行なっていると聞いて、「おおお、LLと低レイヤーの組み合わせ!萌える!」となりました。拙作のwaveずたずた(Rubyでwavファイルをずたずたにするやつ)を思い出した。

発表のあと個人的に「golangでBLE扱うっていってましたけど、ドライバ周りとかどうなってるんです?」と質問させていただいたのですが、「そこは抽象化してくれてるライブラリがあって、それ使えば同一ソースコードでMacでもRasPiでもWinでも動くんすよ」という感じの回答で、なるほど便利という感じでした。

Open Beer Serverの理論とその実装

今回の漫談枠。かと思いきや、実装の話は普通に興味深かった。わたしは電気系かなり弱いので知らないことたくさん知れてよかった。電磁でもってバルブを開閉するソノレイドバルブというやつは頭いいなーって思った。リレーのこと知ったときにも「あー頭いいなー」と思ったが、同質の「はー頭いいなー」であった。だんだん感想が雑になってきた。

一から始めるJavaScriptユニットテスト

はてなの id:shiba_yu36 さんによる発表。JavaScriptのテスト周りはツールがいろいろあって、わたしも最初混乱したんだけど、この発表ではそれらがかなりわかりやすくまとめられていてよかった。karmaをCIに載せる話が一番参考になった。弊社はUIテスト諦めちゃってるので参考にしていきたい。

お昼をご一緒させてもらって、懇親会でもお話しさせてもらって、JSプロジェクトのアーキテクチャの話とかチーム開発の難しさとかいろいろお話しできてうれしかったです。

自分の発表について

ツイッターを追ったかぎりではそれなりに評判がよくて一安心しています。30minにかなりの内容を詰め込んでしまったため駆け足になってしまった。

今週の土曜に新潟県長岡市で行われるNDSという勉強会の50回目(!)で再演を行うので、是非みなさん足を運んでください。東京駅から1h30min程度で着きます。

内容やスライドの公開はないのか、という声がちらほら聞こえてきますが(ありがたいことです)、再演が終わったあと、またこのブログで実況中継シリーズ形式で発表内容を公開させていただきますので、今しばらくお待ちください。

最後に、ほんとに最高のカンファレンスでした。スタッフよみなさんありがとうございました!来年もトーク応募するぞ!!!

Androidアプリの設計の話 - Re.Ra.Ku アドベントカレンダー day 4

Re.Ra.Ku アドベントカレンダー 4日目です。

こんにちは、安部です。Androidアプリを担当しています。初投稿です。

今回はおそらくAndoidアプリ開発者は常に頭を悩ませる問題である、設計の話です。私もずっと試行錯誤を繰り返し、現在進行系で悩んでる問題でもあります。少し見えてきた部分があるので現在の状況を共有したいと思います。

抱えてる問題

Androidアプリ開発していくうえで、よくぶつかる壁です。

ライフサイクル

Androidのライフサイクルは複雑でViewの状態をどのように管理すればよいのか難しいです。今はActivityだけではなくFragmentもあるので、更に複雑にしていると思います。

また、画面の回転などの対応を考えたりすると更に大変です。

非同期処理

通信は非同期にしなければいけません。そのためUI側もそれを意識しなければいけなくなり、Viewの状態管理も複雑になっていきます。

テスト

ContextなどAndroid固有のものを必要とするクラスが自然と増えてしまうので、すぐにテストがしにくくなってしまいます。

最近はこのあたりはDagger2でDIすることでだいぶ改善されつつありますが、まだまだコストが高い印象です。チーム等で検証してバランスを取ってやっていくのが良いかなと。

DDD

DDDをやらなきゃと思って、どこに何を書けば良いのかわからない状況に陥りやすいです。いざやってみると頭を抱えることが多くなります。

現在の状況

現在はMVVM+DDDみたいな形でやっています。

レイヤー分離

現在のレイヤーの分け方は次のようになっています。

  • Presentation
    • Viewに対しての操作や状態管理、イベントハンドリングを行います。
  • Application
    • Domainの直接のクライントになります。ここはPresentationから呼び出され、Domainへ処理を移譲するものになります。
  • Domain
    • モデルやビジネスロジックやリポジトリとAPIアクセスのインターフェースなどがあります。
  • Infrastructure
    • 詳細な実装を記述します。DBアクセスやAPIアクセスなどの詳細な実装になります。

依存関係は Presentation → Application → Domain になります。InfrastructureについてはDIを使って依存を解決するので、他のレイヤーに直接でてくることはありません。

packageも同じような分け方になります。

Presentationレイヤー

ここではUIに関するものを処理します。

現在はDataBindingを使った、MVVMっぽい感じにしています。まだまだ改善の余地があると思っていますが。

MVPでもMVVMでもやることは変わりません。UIなどの表示とイベントに関する責務を負います。それ以外のことはしないようにします。逆に他のレイヤーにこれらの関心ごとが漏れないようにします。

ここではビジネスロジックを処理しませんが、おそらく一番コード量が多いレイヤーになるかと思います。特にViewの状態を管理したりするのはライフサイクルなども絡んできてかなり複雑になると思います。

私の場合ですが、ここから直接Domainレイヤーの処理を呼ばずに、後述のApplicationレイヤーを経由するようにしています。

このあたりのDataBindingとMVVMについては来年のDroidKaigiで話す予定です。

Applicationレイヤー

ここはDomainの直接のクライアントになります。Presentationレイヤーから呼び出されDomainレイヤーへ処理を委譲します。Domainレイヤーに受け渡すためのデータの加工を少しやったりもします。

Domainレイヤー

ここが一番どうしたら良いのかが分からなくなることが多いと思いますが、最近思うのはAndroidアプリではDomainレイヤーはビジネスロジックがそこまで多くない気がします。

アプリの性質にもよりますが、だいたいAPI経由で処理をサーバーに移譲している思うので、ほぼサーバー側にビジネスロジックが実装されていることになります。そのためDomainレイヤーにはAPIに対するインターフェースとモデルが多い感じなっています。

ただ、しっかりとモデルを設計する必要はあるので、そこまで単純ではないと思います。

ここではAPIやDBの実装の詳細は記述しません。例えば、API通信の処理についてはインターフェースのみを定義して、実装はInfrastructureレイヤーに記述するようにします。

リポジトリの場合は次のような感じのみです。

public interface UserRepository {
    User getUser();
}

APIのインターフェースは私はGateWayと命名しています。ここでは戻り値をRxJavaのObservableにすることで非同期を扱いやすくしています。

public interface UserGateWay {
    Observable<User> getUser();
}

また、ドメインモデルにも実装の詳細が入り込まないようにします。例えば、Realmを使ってる場合でRealmObjectを継承したりはしないようにします。DBテーブル情報はInfrastructureレイヤーのほうで定義してあげて、それからドメインモデルに変換してあげるようにします。

ここでは基本的にAndroidのフレームワークに依存するようなものが無い状態(import android.*がない)が理想だと思います。しかし、一部Parcelなどは例外にしています。最初はPresentationレイヤーでDTOみたいなのを作って対応していたのですが、ごちゃごちゃしてきてしまったので、Parcelは例外的にOKにしました。

Infrastructureレイヤー

Domainレイヤーに定義されたインターフェースを実際に実装します。

ここはPresentationレイヤーの次にコード量が多くなる気がします。

APIやDBから取得したレスポンスをドメインモデルへの変換もここで行います。

public class RealmUserRepository implements UserRepository {

    public User getUser() {
       // DBから取得して、ドメインモデルのUserへ変換
    }
}
public class APIUserGateWay implements UserGateWay {

    public Observable<User> getUser() {
       // APIから取得して、ドメインモデルのUserへ変換
    }
}

DI

Infrastructureレイヤーにおいたものは基本的に他のレイヤーで直接依存するようがないことが重要です。そのためDIで依存を解決してあげます。

変更に強くなったり、テストしやすくなったり、BuildVariantごとに差し替えたりできるようになります。可能であれば初めから導入しておくことをおすすめします。あとから導入すると結構大変なので。

すべては書きませんが、Dagger2の場合は次のような感じのモジュールを使う感じなります。

@Module
public class DomainModule {
    @Provides
    @Singleton
    UserRepository provideUserRepository(Context context) {
        return new RealmUserRepository(context);
    }
}

これをApplicationレイヤーでInjectしてあげます。

public class UserService {

    private UserRepository repository;

    @Inject
    UserService(UserRepository repository) {
        repository = repository;
    }

    public User getUser() {
        return repository.getUser();
    }
}

このようにすればInfrastructureレイヤーの詳細な実装が他のレイヤーに漏れることはありません。

まとめ

現在の設計の状況をざっくり書いてみました。本当はサンプルを用意できればよかったのですが…

実際に試さないとわからない部分も多くあるので、最初は多少オーバーキル的な箇所があっても良いと思います。ただあまり時間をかけすぎるのも駄目なのでバランスを取りながらやっていくと良いと思います。

設計に正解はないと思っています。今の設計もまだまだ改善すべきところはあります。このあたりは随時改善していきたいです。

プログラミングしながらできる、健康への近道講座:1回目 - Re.Ra.Ku アドベントカレンダー day 3

こんにちは!
Re.Ra.Kuの泉原です。

アドベントカレンダー3日目は、非エンジニアの泉原が担当します。

本日はbuilderscon開催日ですね。今回のbuildersconにも、弊社CTOの丸山が登壇しています。
皆様丸山の登壇内容のチェックもぜひお願いいたします。
トーク内容は近日中に当ブログでお知らせする予定なので、会場にいない皆様はブログをご確認くださいませ。

非エンジニアの私は、Re.Ra.Kuを代表してエンジニアさんに向けた健康情報をお伝えしていこうと思います。
1回目は、「プログラミング中の姿勢の保ち方」について、まとめます。

悪い姿勢とは

まず、以下の画像をご欄下さい。 f:id:nozomiii:20161202120856p:plain

恐らく誰から見ても悪い姿勢だと思いますが、 誰でも経験のある姿勢なんじゃないかな?と思います。

この姿勢が「悪い」ところは大きく分けて3つです。

  • 腰が倒れていて、体が前のめりになっている
  • 腕に引っ張られて、肩甲骨も前のめりになっている
  • 肩甲骨が上がっているので、首もすぼまっている

悪い姿勢が体に「悪い」理由

悪い姿勢になっていると、体の部位が正しい位置からずれるため、
いらない力が入って筋肉が硬くなり、コリにつながります。
また、筋肉が硬くなると、血の巡りが悪くなるため、
硬くなっている筋肉だけでなく、脳や内臓など、
全身の血の巡りも悪くなり、動きが悪くなります。

単純に見た目が悪いだけでなく、
影響している筋肉も、影響していない体の部位にも負担を大きくかけてしまうので、
姿勢が悪いことは体全体の負担になってしまいます。

綺麗な姿勢の作り方

悪い姿勢を正しい姿勢に戻すにはどうしたら良いか?
それは、骨盤を意識することです。

  • 坐骨を椅子に立てるように座る
  • 骨盤の真上に頭がくるように意識する

たった2つを意識するだけで、

f:id:nozomiii:20161202120926j:plain

こんなに綺麗な姿勢になります。

この姿勢になると、以下の箇所も正しい位置に戻って疲れのたまりにくい身体に近づきます。

  • 前のめりになっていた体重が正しくなり、身体の負担が減る
  • 肩甲骨が自然と寄るので胸が開き、呼吸も大きくなる
  • 胸が開くので腕の疲れも取れやすくなる

また、プログラミングの合間に手のひらを上に向けると さらに肩甲骨が寄って、上半身の疲れが取れていきます。 f:id:nozomiii:20161202120929j:plain

最後に

なんだそんなことか、当たり前のことか、という話だと思いますが、
この当たり前なことをちょっとした意識するかどうかでも
体は大きく変化することもあります。

より快適にプログラミングに注力できるよう 今までよりも少しだけ意識を身体に向けてもらえると良いなと思います。

なお、悪い姿勢が癖になっている身体は、筋肉のコリや体の癖も強くなっています。
そういった方はRe.Ra.Kuの店舗でコリや癖をほぐし、綺麗な姿勢を保ちやすくすることをお勧めします。

reraku.jp

次回以降は、プログラミングの合間にできる簡単なストレッチ方法をお伝えします。
今後とも宜しくお願いいたします!