スクラムの受入条件と完了条件を理解する

要求と要件

要求

ユーザーが実現したい価値

要件

要求を実現するために提供するサービスの内容

受入条件(Acceptance Criteria)

• POが定めたストーリーを受け入れるための条件

• 受入条件は必ず言語化されていなければならない。また、抽象的な用語は避けること。

• e.g.

• 受入条件が文章化されていないと開発チームがPOの顔色を窺いながら開発を進めることになるのでベロシティが下がる。

完了条件(Definition of Done)

• チームが定めた 全ての ストーリーに対しての共通の終了条件の定義

• e.g. テストコードが作られ、CIに通過する

• 未完了(undone)のものはPBLに追加され、POが優先度を決定する

• POが優先度を決定するので、POの理解も必要

JavaScriptとRubyのSymbolの違い

シンボルとは

Ruby

Rubyの内部実装では、メソッド名や変数名、定数名、クラス名など の`名前'を整数で管理しています。これは名前を直接文字列として処理するよりも 速度面で有利だからです。そしてその整> 数をRubyのコード上で表現したものがシンボルです。

シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。

名前を管理するという役割上、シンボルと文字列は一対一に対応します。 また、文字列と違い、immutable (変更不可)であり、同値ならば必ず同一です。

class Symbol (Ruby 2.3.0)

stringとは違い、同じ名前のシンボルであれば、同一のオブジェクトになるわけですね。
サンプルを見てみましょう

まず、stringであれば、別個に定義したstr1str2はそれぞれオブジェクトが作られるので、 二つは等価にはなりません。

str1 = "foo"
str2 = "foo"

p str1.equal?(str2)
# => false

対してSymbolであれば、バラバラに定義しても同一のオブジェクトになるので、
二つは等価になります。

sym1 = :foo
sym2 = :foo

p sym1.equal?(sym2)
# => true

JavaScript

symbolは、ユニークで不変なデータ型で、オブジェクトのプロパティ識別子として使われたりします。symbolオブジェクトは、Symbolプリミティブデータ型をラップした暗黙的なオブジェクトです。

Symbol - JavaScript | MDN

JavaScriptのシンボルは毎回新しいSymbolオブジェクトを生成します。 そのため、以下はfalseになります。

const sym1 = Symbol("foo");
const sym2 = Symbol("foo");

console.log(sym1 === sym2)
// -> false

一方、 文字列比較は単純に同一の値か否かなので以下は 等しくなります。

const str1 = "foo"
const str2 = "foo"

console.log(str1 === str2)
// -> true

つまり、Rubyとは等価性の判定が逆になります。

JavaScriptRubyのようにSymbolを扱うには

Symbol.for(key)を使うことでRubyのように同一のオブジェクトを参照できます。

与えられたkeyで存在するシンボルを検索し、見つかればそれを返します。もしくは、グローバルsymbolレジストリにこのkeyで新しいsymboleを生成します。

const sym3 = Symbol.for("foo")
const sym4 = Symbol.for("foo")

console.log(sym3 === sym4)
// -> true

なお、Symbol.for(key)Symbol(key)で作ったオブジェクトとは等価にならないのでご注意を

const sym5 = Symbol("foo")
const sym6 = Symbol.for("foo")

console.log(sym5 === sym6)
// -> false

Reactでformat付きの数値入力コンポーネントを作る

こういう機能がついた入力フォーム、どのようなサービスでも役立つと思う。 ぐぐってもあんまり情報が見つからなかったので自分でまとめる

  • フォーカスインしているときは普通の入力エリア
  • フォーカスアウトしたら、三桁区切りでフォーマット
  • 指定した範囲内の値しか入力不可
  • onChangeイベントで数値データを渡す

この実装で注意しないといけないのが、 -.
-16.0であれば数値としても妥当であるが、Reactでは(というかJavaScriptでは) -.が入力された瞬間を考慮しなければならない

で、作ったのが↓こんなの(うろ覚え)


class InputNumber extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isOnFocus: false,
      value: '',
    };
  }

  get value() {
    if (this.state.isOnFocus) {
      return this.state.value || ''
    }
    if (this.props.value) {
      return this.props.format(+value);
    }
    return '0'
  }

  includes = value => this.props.min <= value && value <= this.props.max;

  handleFocus = () => {
    this.setState({
      isOnFocus: true,
      value: `${this.props.value}`
    });
  };

  handleBlur = () => {
    this.setState({
      isOnFocus: false,
    });
  }

  handleChange = (e) => {
    const { value } = e.target;
    if (this.isIncludes(value)) {
      this.props.onChange(+value);
    }
    // ここがどうにも苦しいロジック
    if (this.isIncludes(value) || value === '-') {
      this.setState({
        value,
      })
    }
  }

  render() {
    <input
      type="text"
      className="form-input"
      placeHolder={this.props.placeHolder}
      disabled={this.props.disabled}
      value={this.value}
      onFocus={this.handleFocus}
      onBlur={this.handleBlur}
      onChange={this.handleChange}
    />
  }
}

InputNumber.propTypes = {
  value: PropTypes.number.isRequired,
  min: PropTypes.number,
  max: PropTypes.number,
  placeHolder: PropTypes.string,
  disabled: PropTypes.bool,
  onChange: PropTypes.func.isRequired,
  format: PropTypes.func
}

InputNumber.defaultProps = {
  min: -Infinity,
  max: Infinity,
  disabled: false,
  placeHolder: '',
  format: formatDefault
}

formatDefault = value => ''; // お好きな数値フォーマッタを実装しよう!

今気づいたけどモバイル端末を考えると htmlのnumber使うようにした方が良いんだろうなぁ

Webpackで絶対パスを指定する

webpackのconfigでaliasを指定することで、絶対パスを使用してimportすることができるようになる

resolve: {
    alias: {
      components: path.resolve(__dirname, 'src/components'),
    },
}
import UserList from 'components/UserList';

EsLint

Eslintを使用している場合、上記だけだと、以下のような警告がでる

Unable to resolve path to module 'components/UserList'. (import/no-unresolved)

どうせbuildするときに分かるし、無視してもいい気がするが、 一応以下の方法で対応できる

command line

npm install --save-dev eslint-import-resolver-webpack

eslintrc.json

{
"settings": {
    "import/resolver": "webpack"
  }
}

Reactのリストコンポーネントのkeyにindexを使ってはいけない

React始めて1か月たった。 調べものをしていると、サンプルコードでlistコンポーネントのkeyにindexを使っているものがたまに見かけられる。 こんなの

render() {
   const list = this.props.usernames.map((username,index) => {
     return <li key={index}>username</li>
   });
   return (
     <ul>{list}</ul>
   );
}

...不変なリストであればこれで問題ないのかもしれないが、 リストの中身を変更したときにdomを差分更新することができないらしい。

例えばリストの中に入力エリアがある場合、入力内容はリストの変更に追随しない。

import React, { Component } from 'react';

class InputArea extends Component {
  render() {
    return (
      <div className="form-group row">
        <label className="col-sm-2 col-form-label">{this.props.label}</label>
        <div class="col-sm-10">
          <input type="text" className="form-control" />
        </div>
      </div>
    )
  };
}

class Question extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questions: [
        { id: 1525018854, label: 'do you like apple?' },
        { id: 1525018844, label: 'do you like banana?' },
        { id: 1525018824, label: 'do you like orange?' },
      ]
    }
  }

  add = () => {
    const question = { id: +new Date, label: this.refQuestion.value };
    this.setState({
      questions: [question].concat(this.state.questions)
    });
  };

  render() {
    const questionList = this.state.questions.map((question, index) => {
      return <InputArea key={index} label={question.label} />
    });
    return (
      <div>
        {questionList}
        <div className="form-group">
          <textarea className="form-control" placeholder='question?' ref={(elm) => { this.refQuestion = elm; }} />
          <button className="btn btn-primary" onClick={this.add}>add</button>
        </div>
      </div>
    )
  }
}

f:id:schiughi:20180430023334g:plain

Eslint使ってなければ気付かなかったと思う。

参考

medium.com

Hive UDFを作ったのでメモ

HiveのUDFを仕事で作ったのでメモ

実装

必要なのは以下二つ

  1. org.apache.hadoop.hive.ql.exec.UDFを継承する。
  2. evaluateメソッドを定義する。

サンプルそのまま

package com.example.hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
 
public final class Lower extends UDF {
  public Text evaluate(final Text s) {
    if (s == null) { return null; }
    return new Text(s.toString().toLowerCase());
  }
}

Q: 状態は持てるのか?

A: 持てる。インスタンスが作られるのは実行時ではないっぽい。
重たい処理(正規表現コンパイル)とかを変数にキャッシュしておくとUDFの高速化が狙える。

関数の登録

  1. jar追加
hive> -- add jar ${hdfs上のjarファイル格納パス}
hive> add jar /home/hoge/hoge-udf.jar;
hive>
hive> -- 確認
hive> list jar;
/home/hoge/hoge-udf.jar;
  1. 関数作成
hive> -- create temporary function ${hiveで使用する関数名} as '${Javaのパッケージ}'
hive> create temporary function my_lower as 'com.example.hive.udf.Lower';
hive>
hive> -- 確認
hive> show functions "my_lower";

なお、上記の方法だと、Hiveとのセッションが切れると関数定義もリセットされてしまう。 (temporaryを外せばいいのかと思ったけど、jarのPATHの方が消えてしまうので実行できない)

Hive 0.13.0以降だと以下の方法で関数定義を永続化できるらしい(確認してない)

hive> CREATE FUNCTION myfunc AS 'myclass' USING JAR 'hdfs:///path/to/jar';

Hive 0.13.0以前の場合は、 hive(またはbeeline)の実行コマンドに-iオプションをつけてadd .. ~ create..を記述したscriptを流すのが簡単な解決法なのかなあ

-- initialize.hql
add jar /home/hoge/hoge-udf.jar;
create temporary function my_lower as 'com.example.hive.udf.Lower';
...
$ hive -i ./initialize.hql
hive> 

参考

HivePlugins - Apache Hive - Apache Software Foundation