読者です 読者をやめる 読者になる 読者になる

あのにのに

僕の雑記

Unityでスマホのカメラを使う

僕の同僚かつフレンズのid:hiragramがなんかよくわからないカメラアプリを作ってて凄い。

hiragram.hatenablog.jp

僕もそういうのやりたいけどiOS開発やったこと無い。

Unityなら出来る。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour  {
    public WebCamTexture webCameraTexture = null;
    public GameObject plane;

    // Use this for initialization
    void Start ()
    {
        webCameraTexture = new WebCamTexture ();
        plane.GetComponent<Renderer>().material.mainTexture = webCameraTexture;
        webCameraTexture.Play ();
    }

    // Update is called once per frame
    void Update ()
    {

    }
}

f:id:anoChick:20170323002050p:plain

うつった。 テクスチャに投影してるだけなので↓のようにも出来る。

だからなんだって感じではあるけど、 なんかいろいろできそう。

すてにゃんが分報チャンネル消したらしいから僕も消した。

stefafafan.hatenablog.com

といってもただ便乗しただけじゃなくて、

先週あたりから消そうかどうか悩んでたんですが、 すてにゃんが消したということで踏ん切りがついて僕も消したという感じです。

分報チャンネルの用途

前提として、僕の分報チャンネルの使い方はだいたい以下のような感じでした、

  • 業務でのゆるめな悩みつぶやき
  • 業務外の技術的なはなし
  • 雑談
  • にゃーん的感嘆詞
  • 自分用bot[栗山さん]

f:id:anoChick:20170322072240p:plain

栗山さんbotはYaya製です 他にもwebhook受け取ったり、色々やってくれます

anoninoni.hateblo.jp

やめた理由

最近Twitterで全然ツイート出来ていないのが辛くて、 なんでツイート出来ないんだろうって考えてみたら、 Twitterと分報チャンネルどっちで言おう→分報チャンネル ってケースが多くあって、結果としてツイート量が減ったんだと思いました。

あと分報チャンネル見てる人って実はそんなに居ないんですよね。 最近社内のSlack利用者がめちゃめちゃ増えてて、ノイズ感すごくなってきたので、 だいたいのチャンネルは入ってるけどミュートしているものと思ってます。(わたしはそう) join数三桁の分報で発信した気になって実は誰も観てないなんてことも普通にありえます。

発信先の分類が面倒で自分の好きにできる領域にPOSTしちゃってるんだと思うんです。

それだけです

情報発信先チャネルの最適化をもっとしていかないといけないなと思いました。

Yaya作った

github.com

こんなんつくりました。

導入

1.用意するもの

2.Herokuにデプロイ

https://github.com/anoChick/yaya

↑の[Deploy to Heroku]ボタンを押します f:id:anoChick:20170306031846p:plain

HerokuにログインしていればApp作成画面が出てくると思います。 環境変数をいれていきましょう

[SLACK_API_TOKEN] SLACKのAPIトークンをいれてください。 [ROOT_URL] アプリケーションのROOTURLを入れる所なんですが、まだ生成されていないので適当な文字列を入れておいて下さい。 f:id:anoChick:20170306032129p:plain

Deployボタンを押せばデプロイされます。

デプロイが完了したらwebページが生成されるので、そのURLをROOT_URLに設定しておいて下さい。 (Settingsの[Config Variables])でできます。

slackのbotがオンライン状態になったらデプロイ成功です。 f:id:anoChick:20170306033026p:plain

動かす

GraphQLのfieldに付くresolveをTypeによって指定する。

GraphQLでDateTimeを扱う時、フォーマットを指定する。 - あのにのに

前回の記事で 「DateTime型とかはクエリ側で出力フォーマットを引数として指定できると便利!」 って話をしました。

ついでに課題として

  • DateTimeTypeって作ったけど意味なくない..?

  • いちいちresolveにフォーマット付与を記述するの面倒じゃない?

という物があったのでこれを解決したいと思います。

やること

Typeが**ならresolveでxxをする。と言うような仕組みを作ります。

今回の場合だと 「fieldのTypeがDateTimeTypeの場合、そのfieldに引数[:format]を用意し、resolveではformatに応じで出力フォーマット通りに書こうするようにする。」 になります。

これができるようになると、DateTimeTypeが付いたfieldは全て[:format]が使えるようになります。

ちなみに使用するgemは前回に引き続き

https://github.com/rmosolgo/graphql-ruby

です。

やってく

今回はGraphQL::Define::AssignObjectFieldをモンキーパッチをあてる形で動かしてみます。 GraphQL::Define::AssignObjectField#callの程よくfieldが得られたタイミングで

if field.type.to_s == 'DateTime'
  field.arguments['format'] = GraphQL::Argument.define(name: 'format', type: -> { GraphQL::STRING_TYPE })
end

f:id:anoChick:20170201041027p:plain

ついた!

次にリゾルバを作ります。

class DateTimeTypeFormatResolver
  def initialize(attr_name)
    @attr_name = attr_name
  end
  def call(obj, args, ctx)
    return obj[@attr_name].strftime(args[:format]) if args[:format].present?

    obj[@attr_name]
  end
end

こんな感じ。あとは

if field.type.to_s == 'DateTime'
  field.arguments['format'] = GraphQL::Argument.define(name: 'format', type: -> { GraphQL::STRING_TYPE })
  field.resolve=(DateTimeTypeFormatResolver.new(name))
end

とか書けばok

f:id:anoChick:20170201041249p:plain

一応これで完成。

割りと便利な気がする。

今回新たにうまれた課題としては

  • 他でリゾルバを記述したくなったらどうするんだ
  • この書き方だとModelの属性名とTypeのfield名が一致している場合しかつかえない

次↑について考えます。

あとはその他便利拡張あればやる。

GraphQLでDateTimeを扱う時、フォーマットを指定する。

Ruby on Rails GraphQL 技術 Ruby

GraphQLにはScalarTypeというクラスが存在する。

ScalarTypeは配列やオブジェクト(いわゆるkey-valueペア)、enumではないタイプの基底クラスになる。

GraphQLではこのScalarTypeをベースとした5つの基本タイプが仕様として定義されている。

  • Int - 符号付き32ビット整数
  • Float - 倍精度少数
  • String - UTF-8文字シーケンス
  • Boolean - true or false
  • ID - GUID

これだけだとDateTime等で困るので、DateTimeのGraphQLTypeを作ってみる。

date_time_type.rb

graphql-rubyを使う

https://github.com/rmosolgo/graphql-ruby

DateTimeType = GraphQL::ScalarType.define do
  name 'DateTimeType'
  description 'ActiveRecord::Type::DateTimeに対応したType'

end

このままだとクエリに対する出力結果が

{
  "data": {
    "series": {
      "created_at": "2017-01-09T18:55:30.000Z"
    }
  }
}

になる。せっかくGraphQLなので、フォーマットもクエリとして与えられるようにしたい。

created_atフィールドをいじる

普通にcreated_atを一属性として扱うだけなら

field :title, DateTimeType

の記述だけで済むが、今回は引数formatを与えると、その通りに整形してくれるようにする。

  field :created_at do
    type DateTimeType
    argument :format, types.String
    resolve ->(obj, args, ctx) {
      return obj.created_at if args[:format].nil?

      obj.created_at.strftime(args[:format])
    }
  end

これでqueryを叩く

{
  series(id: 3) {
    created_at(format:"%Y年%m月%d日 %H:%M:%S")
  }
}

{
  "data": {
    "series": {
      "created_at": "2017年01月09日 18:55:30"
    }
  }
}

良い。 本当はフォーマットの仕組みをTypeそのものに持たせたいのだけれど、TypeのresultがStringを取ることになるので適切では無さそう。

RailsのカスタムGeneratorを自分で作る

事前に用意したテンプレートを基にファイルを生成するようなコマンドを作る。

今回はGemにしたいのでプラグイン作成の想定でやる。

Railsプラグイン作成環境を用意

bin/rails plugin new sampleplugin

gemspecファイルのTODOになってるところを書き換えて、bundle installをする。

プラグイン自体はlibディレクトリ以下に作っていき、 test/dummyrailsプロジェクトがあるのでそこで動作確認をしていく。

Generatorクラスを作る

lib/generators/sample_generator.rb にgeneratorの処理を記述する

class SampleGenerator < Rails::Generators::Base

  def initialize(args, *options)
  super

  @_args, @_options = args, options
  end

  def main
    # ここに処理を書く  
  end
end

これでbin/rails generate sampleなどと叩くとmainが実行される。

テンプレートの用意

例えば

<%= @type_name %> = GraphQL::ObjectType.define do
  name '<%= @model_name %>'

end

lib/generators/templates/types.rbにこんな感じでファイルを作っておく。

テンプレートを使う

class SampleGenerator < Rails::Generators::Base
  source_root File.expand_path('../templates', __FILE__)

  def initialize(args, *options)
  super

  @type_name ='sample_type'
  @model_name = 'Sample'
  end

  def main
    template "types.rb", "app/graphql/types/#{@type_name}.rb"
  end
end

f:id:anoChick:20170118032812p:plain

できた。

Model(ActiveRecord)からGraphQL::ObjectTypesを自動生成する仕組みを考えるメモ

最近個人的にWebアプリ作ってます。 フロントはReactJS+Redux サーバサイドはRails それぞれ独立していて、GraphQLを用いて通信しています。

GraphQLのRuby実装として一番スターの多いgraphql-rubyを使っています。

GraphQL周りはまだ発展途上なのでいろいろと不便。 Typesを手動で定義するのが面倒なのでこれについて考えてみる。

Modelに対応するObjectの構成要素について

ActiveRecord内の要素で、今回着目するつもりなのは以下の通り - ActiveRecord::Attributes - ActiveRecord::Relation - ActiveRecord::Enum

ActiveRecordは GraphQL::ObjectTypeと対応させるとして, ActiveRecord::Relationの場合は has_oneの場合:GraphQL::ObjectType has_manyの場合:GraphQL::ListType(GraphQL::ObjectType) でよさそう。 ActiveRecord::EnumはそのままGraphQL::EnumTypeで問題ないはず。

ActiveRecord::AttributesはGraphQL::ScalarTypeになるのだけれど、 GraphQLの標準として定められているScalarTypeは

  • Int
  • Float
  • String
  • Boolean
  • ID

5つだけ。

- ActiveModel::Type::BigInteger
- ActiveModel::Type::Binary
- ActiveModel::Type::Boolean
- ActiveModel::Type::Decimal
- ActiveModel::Type::DecimalWithoutScale
- ActiveModel::Type::Float
- ActiveModel::Type::Integer
- ActiveModel::Type::String
- ActiveModel::Type::Text
- ActiveModel::Type::UnsignedInteger
- ActiveModel::Type::Value

Rails側はこんな感じ

対応できないGraphQL::ScalarTypeを新たに定義するのでもいいけど ユーザ独自で定義したActiveModel::Typeにも対応させることを考えると、 ActiveModel::Typeレベルから対応関係を結んだほうがよさそう。