ゆーかん

徒然日記

jsonのキーをチェックするテストをrspecで書くときに、bodyがrenderされなくてはまった

テストが苦手な僕ですが、デグレが怖いので最低限戻り値程度のテストはしています。その時のお話。

環境

結論

  • controllerのテスト時はviewをrenderせず””を返す
  • 上記の場合、render_viewsをdescribe/context直下に記入することで解決
  • 但し、controllerから直接、render json: @sthとしている場合は呼び出される

controllerの内容

module Api
  class AbcController < ApplicationController
    def index
      @abc = Abc.all()
      render json: @abc
    end
  end
end
module Api
  class XyzController < ApplicationController
    def index
      @xyz = Xyz.all()
      render formats: :json, handlers: "jbuilder"
    end
  end
end

jbuilderの内容は省略。ちゃんとあたいは帰っています。

specの内容

RSpec.describe Api::AbcController, type: :controller do
  context '/api/v1/abcs' do

    it 'returns json' do

      get :index
     # きちんとresponse.bodyが取れる
      expect(JSON.parse(response.body).keys).to include('id', 'name')
    end
  end
end
RSpec.describe Api::XyzController, type: :controller do
  context '/api/v1/xyzs' do

    it 'returns json' do

      get :index
     # response.bodyが空
      expect(JSON.parse(response.body).keys).to include('id', 'name')
    end
  end
end

specは全く同じ。controllerの内容はrenderの部分が少し違う。これで下がエラー。結論は冒頭に

解決策

RSpec.describe Api::XyzController, type: :controller do
  context '/api/v1/xyzs' do

+    render_views

    it 'returns json' do

      get :index
     # response.bodyが空
      expect(JSON.parse(response.body).keys).to include('id', 'name')
    end
  end
end

11/11日に学んだことメモ

  • js
    • general
      • jsで千位以下を0にする方法
      • 関数のcallbackとかのタイミングは間違えなかった
    • spec
      • selectorについて深く学んだ
      • instance()でprops/fucntionとかcomponentに定義されてるものを呼べる様になることを学んだ
      • 要素の順序をテストしたい時は、forで回して、props.name === 'name' ? i : null とかで順序を取り、toBeGreater/toBeLessThan で比較
      • reduxで管理してるstoreにアクセスしないで、propsを用意したい時は、普通にcomponentに渡す
      • snapshotテスト以外はenzymeのshallowとmountで処理した方が良さそう
      • snapshotテストの時だけ、renderer.create(C)
    • 迷ったらとりあえずその場最適なコードを書いて解決した方が良さそう
  • ruby
    • general
      • corsのエラー回避をapplicaton_controllerに書けばよいことを学んだ
    • jbuilder
      • 動的にキーを入れる方法学んだ(json.set! collection.key)
      • 動的に入ったキーに、動的に値を入れる方法も学んだ(json.set! colleciton.key, collection.val)
      • オブジェクトを作りたい時は、ブロックを作成(do ~ end)にまとめる
    • modelのoriginal methodの作り方
      • scope ~, -> っていうarrow functionみたいな定義の仕方
      • 上と同じだけど、def ~ end で普通に定義する方法もある
      • joinして検索するっていう選択肢がある
    • seed_fu
      • idいれないと、データが永遠に増えていく
      • specでtest dbを作りたい時は、spec_helperのコメントアウトを外す
  • mysql
    • delete table では、idのautoincrementは初期化されない
    • truncate tableでは、idのauto incrementも初期化される

rails、ストロングパラメーターにおけるネストされた複雑なパラメータの処理

正確な内容は公式ドキュメントを参照

許可したかったparameter

{
  "nickname": "nickname",
  "info1": {
    "key1": "yes",
    "key2": "",
  },
  "info2": {
    "array1": ["aaa"],
    "array2":[]
  }
}

素直にkeyだけを列挙するのではエラーになる。 配列が存在する/ネストが生じている場合やたらとややこしくなり良く間違えるので注意が必要。

配列のpermitの仕方

配列がvalueになっているkeyのpermitは下記の様に行う。

def some_params
  params.permit(:name, {:array => []}
end

配列は、strong parameteが許可しているスカラー値ではないので、一旦ハッシュとして定義してあげます。

ネストが生じている場合

ネストが生じている場合は、ネストしているキーのバリューの中のキー(難しいな)に関しては、配列で定義してあげます。

def some_params
  params.permit(:name, array: [:key1, key2])
end

結論

初めにでてきたjsonを通すストロングパラメータの定義の仕方は次の様になる。

def some_params
  params.permit(:name, keys: [
    :nickname,
    {
      info1: [
        :key1, :key2
      ]
    },
    {
      info2: [
        { array1: [] }, { array2: [] },
      ]
    }
  ])
end

まとめ

配列 => ハッシュの中に格納して定義 ハッシュ => 配列に値のキーを格納して定義

ややこしいな。。

POSTMANのアップデートが便利すぎて、API作成の必須ツールになりそう

どうやら、10月10日にPOSTMANがアップデートされていたようです。MacOSで動くネイティブアプリをリリースした他にも、色々と機能が追加されてたので、調査がてらにまとめます。便利になりすぎてとてもびっくりしました。

公式ブログ

モニター機能の追加

定期的にサーバーに対してリクエストを送って、パフォーマンスチェックや、死活監視などができる機能が追加されてました。

最短5分間隔でリクエストを投げることができて、レスポンス時間や、レスポンスコードを教えてくれます。

課金体系は従量課金。月間1000件までは全ユーザー無料。それ以上使う人は追加で購入することにより可能です。

ドキュメント機能の追加

POSTMANでまとめている内容がそのままドキュメントとして公開されるようになりました。

公開/非公開の管理ができ流上に、とても見やすくて綺麗。次は是非これで描いてみよう。

課金体系は、閲覧数に対しての従量課金。

モックサーバー

ドキュメント化だけじゃなく、そのままモックサーバーとしても展開できる。

他にも環境変数が定義できたり、リクエストメソッドの種類が増えていたりと、盛りだくさんのアップデートでした。

暫定的な仕様をドキュメントに起こしながら、シームレスにモックサーバーとして公開。なおかつ、死活管理やパフォーマンスチェックまでそのままできちゃう。そして個人利用程度ならば恐らく無料。

相変わらず Googleさんのやることは恐ろしい。。。

株式市場を挟んだ献金を検出して、スポットで儲ける方法を探った

データ分析の醍醐味は、「成功すれば一攫千金」が狙えるところですね 。今回は下記の仮説のもと、分析を行って見た。

仮説:株式市場で価格を釣り上げることによる、不正献金を行っている団体があり、有意に変化が見て取れる

結論

ある銘柄を選んで仮説の通りに分析をすると、儲かることもありうる。その場合、

  • 選挙の20日前にその銘柄を購入
  • 選挙の5日後に売る

ことで利益を最大化できることがわかった。

手順

  1. ある政党の特定の人間が関連している銘柄から、選挙日程の周辺で値動きや出来高に突発的な変化がないか見る
  2. その銘柄に類似の銘柄を全銘柄から探して稼げる銘柄を複数獲得する

その1:出来高や値動きに突発的な変化のある銘柄を探す

アセットアライブ株式情報に掲載されていた、政治家関連銘柄の出来高や値動きから

  • A : 選挙前に出来高が突然上昇している
  • B : その期間で日経平均とは明らかに異なった動きをする

銘柄を探してみます。

まずは特定の政党の関係者が関係している銘柄を見て過去の選挙前後でどのような変化が起こっていたかを見ていきましょう。アセットライブ株式会社から七人の人を選び、その人たちの関係していた22銘柄について見て行きます。

調べて見たら、22の銘柄のうち、21の関連銘柄で選挙直前に出来高や株価の異常な値動きは見られませんでした。しかし、政治とカネの噂の耐えないある人に関係している、証券番号1899の銘柄だけ、株価が日経平均と比較して異常な挙動をしていました。これについて詳しく見て行きしょう。

次の写真がその銘柄の2002年〜2014年までの出来高推移です。全部で3箇所の出来高が急増してます。

new_for15.png

3つの山で起きていた出来事は左から順に、新潟中越地震と2つの民主党選挙。民進党の代表選のときなのでこちらは与野党の変更というよりは政党の方針=マニフェストの変更に影響があるのかも、、

ここで得られた銘柄を「選挙に振り回される銘柄」と定義してみます。。

選挙に振り回される銘柄をクローズアップして見ていく前に、比較相手として同じ民進党のAさん関連銘柄を見てみます。実際にほかの銘柄と比べて明らかにおかしな挙動をしていたのが分かるかと思います。

これは、1つ前の画像と同じ15年間の出来高と、株価の推移を表しています。

okada.png

緑のグラフが1枚目の画像と同じ出来高です。民主党代表選挙の期間における、日経平均株価とこの銘柄の株価の値動きの差を見てみても、異常な値動きは見られません。

次に選挙に振り回される銘柄のうち、出来高の動きが激しかった時期を中心に見て行きたいと思います。ます。緑のグラフが日経平均株価を、赤いグラフが選挙の結果に影響されやすい銘柄の推移を、紫のラインが出来高の推移を表しています。

 

2010年民主党選挙付近の2ヶ月間 b12dc6655ae69f06212aafb64d804dfb.png

2011年民主党選挙付近の2ヶ月間 afaff820afd0593fdaee5259bf322a77.png

2011年民主党選挙公示日前後の2週間

cd292bc2e750494e404425f30b4153ca.png

両期間に共通して言えるのは、入れ替えの選挙が行われる前から出来高が上がり、日経平均に対して異常な値動きをしているという点でした。儲けるならば、選挙の開始が噂になる辺りから買い始める必要がありそうです。

では次に、選挙に振り回される銘柄を利用して、同じタイミングで同様に儲けられそうな銘柄を探して見ましょう!やり方としては、同期間の全上場銘柄の株式を拾ってきて、それぞれの銘柄の関係性(相関関係)を調べていきます。

その2:「選挙に振り回される銘柄」と”似ている”銘柄を探す

相関関係とは、2つのデータの関係性の深さを表しています。期間は、「選挙に振り回される銘柄」で激しく出来高が推移していた2010年8月4日〜2010年10月4日、2011年7月1日〜2011年9月30日のものを利用しています。

銘柄情報はyahooファイナンスから当時存在していた全てのものを収集し、選挙に振り回される銘柄の変化率と、各銘柄の変化率の相関係数を求めました。両者ともに日経平均を基準として見ています。

両方の期間で共に相関係数が高かったものを下の表に挙げています。相関係数の最大値は1、最小値は-1で、高ければ高いほど両者の値動きの変化率が似通っています。

2010/8/2~2010/10/4 2011/7/1~2011/9/30
会社名 相関係数 相関係数
A社 0.659 0.811
B社 0.614 0.819
C社 0.688 0.688
D社 0.655 0.831
E社 0.611 0.831

残念ながら、A社B社の様な民主党との関連性が巷で噂されている銘柄の相関などが高く出ています。ちなみにC社民進党のポスターがたくさん貼ってあるスーパーで、D社の方も過去に疑惑をかけられたりしていました。これは何か不穏なものを感じますが、納得のいくような結果が出てきた気もします。。。笑

売りのタイミングですが、過去のデータでは多くが、投票の5日後までに株価が1.2倍ほど上昇し、その後下げることもわかりました。

react-native-fbsdkを使って、ログイン、ユーザのemailを取得する

react-nativeでfacebook認証をするためにreact-native-fbsdkを使った。公式ドキュメント、githubのreadmeはそのまま動かそうとしても、コードが古くて動かなかった。一部変えなきゃいけなかったし、なんか色々ややこしいので、メモがてらに残しておく。

react-natve 0.43.3
react-native-fbsdk: 0.5.0

react native-fbsdk以下の手順で進めレバ動くはず。

  1. セッティング
  2. ユーザにLoginをしてもらってaccesstokenを取得
  3. accesstokenを使って、graphApiにリクエストを投げる

1. セッティング

公式ドキュメントの通りに従っていけば問題なく動いた。

2. ユーザにLoginをしてもらってaccesstokenを取得

こんな感じのログインボタンコンポーネントを作ってあげる。以下のようにやると、access-tokenが取得できる。

ドキュメント

    <View style={{ flex: 2, alignItems: 'center' }}>
      <LoginButton
        publishPermissions={["publish_actions"]}
        readPermissions={["email", "public_profile"]}
        onLoginFinished={
          (error, result) => {
            if (error) {
              Alert.alert("login has error: " + result.error);
            } else if (result.isCancelled) {
              Alert.alert("login is cancelled.");
            } else {
              AccessToken.getCurrentAccessToken().then(
                (data) => {
                  Alert.alert(data.accessToken.toString());  // 確認のため、alertで内容表示
                  new GraphRequestManager().addRequest(infoRequest).start();  // 次の見出し部分の準備
                }
              );
            }
          }
        }
        onLogoutFinished={() => Alert.alert("logout.")}/>
    </View>

とりあえずここまででaccess-tokenが取得できるので、次はgraph api にリクエストを投げる部分

3. accesstokenを使って、graphApiにリクエストを投げる

// コールバック関数の用意
const responseInfoCallback = (error, result) => {
  if (error) {
    console.log(error);
  } else {
    console.log(result);
  }
}

// リクエストの時に投げるurlの用意
// こんな感じのurlが生成される
// https://graph.facebook.com/v2.9/me?fields=name%2Cemail&access_token=TOKEN
const infoRequest = new GraphRequest(
  '/me',
  {
    httpMethod: 'GET',
    version: 'v2.9',
    parameters: {
        'fields': {
            'string' : 'email,name'
        }
    }
  },
  responseInfoCallback,
);

tokenが生きているか、何を取得できるのかはグラフAPIエクスプローラを使うとわかる。fbsdkがうまく動かなかったので、初めはcurlして、リクエストを処理してたんだけど、その辺のリクエスト先urlもこれを使うとさくっと取得できる。

コード全体

import FBLogin from './fbEmailRequest';


// ... 任意のところで

<FBLogin />
import React from 'react';
import { View, Alert } from 'react-native';

const FBSDK = require('react-native-fbsdk');

const {
  LoginButton,
  AccessToken,
  GraphRequest,
  GraphRequestManager,
} = FBSDK;

const responseInfoCallback = (error, result) => {
  if (error) {
    console.log(error);
  } else {
    console.log(result);
  }
}

const infoRequest = new GraphRequest(
  '/me',
  {
    httpMethod: 'GET',
    version: 'v2.9',
    parameters: {
        'fields': {
            'string' : 'email,name'
        }
    }
  },
  responseInfoCallback,
);



export default function FBLogin() {
  return (
    <View style={{ flex: 2, alignItems: 'center' }}>
      <LoginButton
        publishPermissions={["publish_actions"]}  // 書き込み権限はこっち
        readPermissions={["email", "public_profile"]} // 読み取り権限はこっち
        onLoginFinished={
          (error, result) => {
            if (error) {
              Alert.alert("login has error: " + result.error);
            } else if (result.isCancelled) {
              Alert.alert("login is cancelled.");
            } else {
              AccessToken.getCurrentAccessToken().then(
                (data) => {
                  console.log('result', result);
                  console.log('accesstoken', data);
                  Alert.alert(data.accessToken.toString());
                  new GraphRequestManager().addRequest(infoRequest).start();
                }
              );
            }
          }
        }
        onLogoutFinished={() => Alert.alert("logout.")}/>
    </View>
  );
}

以上です。公式ドキュメントがreact-native用の公式ドキュメントでは動かなかった人のためになれば幸いっす。

react-nativeで星型とか、三角形とか、吹き出しとか出したくなった

すごく綺麗にまとめている人がいるのでこちらを参考にやっていって、疑問が出たら調べるのが一番かと。

http://browniefed.com/blog/the-shapes-of-react-native/

まぁ簡単に言うと、CSS3と同じ様に実装ができる。とはいえCSS3は初めてなので、補足知識を以下にまとめていく。

3角形の作り方

ネットにはたくさん良いコンテンツがあるんですね〜。ここに関してはここにたくさん解説がでている。

星型

この辺までくると、css3のpositionの概念の理解が必須になってくる。ここが割とまとまっている。

コード全体

react-native init StylePlaygroundして、index.ios.jsをこれに書きかえれば、楕円、三角、逆三角、矢印、星型までは見れるはず。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
} from 'react-native';

export default class StylePlayground extends Component {

  renderOval() {
    return <View style={[styles.oval, styles.base]}/>
  }

  renderTrianglePortrait() {
    return <View style= {[styles.triangle, styles.base ]}/>
  }

  renderTriangleOddPortrait() {
    return <View style= {[styles.triangle, styles.base, { transform: [{ rotate: '180deg' }]} ]}/>
  }

  renderCurverdTailArrow() {
    return (
      <View style={styles.curvedTailArrow}>
        <View style={styles.curvedTailArrowTail}></View>
        <View style={styles.curvedTailArrowTriangle}></View>
      </View>
    );
  }

  renderStarFive() {
    return (
      <View style={styles.starfive}>
        <View style={[styles.triangle, styles.base, styles.startfiveTop]}></View>
        <View style={styles.starfiveBefore}/>
        <View style={styles.starfiveAfter}/>
      </View>
    );
  }

  render() {
    return (
      <ScrollView contentContainerStyle={styles.container}>
        { this.renderOval() }
        { this.renderTrianglePortrait() }
        { this.renderTriangleOddPortrait() }
        { this.renderCurverdTailArrow() }
        { this.renderStarFive() }
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  base: {
    margin: 10,
  },
  oval: {
    width: 100,
    height: 100,
    borderRadius: 50,
    backgroundColor: 'gray',
    transform: [
      {scaleX: 2}
    ],
  },
  triangle: {
    width: 0,
    height: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderLeftWidth: 50,
    borderRightWidth: 50,
    borderBottomWidth: 100,
    borderLeftColor: 'transparent',
    borderRightColor: 'transparent',
    borderBottomColor: 'gray',
  },
  curvedTailArrow: {
    backgroundColor: 'transparent',
    overflow: 'visible',
    width: 30,
    height: 25,
  },
  curvedTailArrowTriangle: {
    backgroundColor: 'transparent',
    width: 0,
    height: 0,
    borderTopWidth: 9,
    borderTopColor: 'transparent',
    borderRightWidth: 9,
    borderRightColor: 'red',
    borderStyle: 'solid',
    transform: [
      {rotate: '10deg'}
    ],
    position: 'absolute',
    bottom: 9,
    right: 3,
    overflow: 'visible',
  },
  curvedTailArrowTail: {
    backgroundColor: 'transparent',
    position: 'absolute',
    borderBottomColor: 'transparent',
    borderLeftColor: 'transparent',
    borderRightColor: 'transparent',
    borderBottomWidth: 0,
    borderLeftWidth: 0,
    borderRightWidth: 0,
    borderTopWidth: 3,
    borderTopColor: 'red',
    borderStyle: 'solid',
    borderTopLeftRadius: 12,
    top: 1,
    left: 0,
    width: 20,
    height: 20,
    transform: [
      {rotate: '45deg'}
    ]
  },
  startfive: {
    width: 150,
    height: 150,
  },
  startfiveTop: {
    position: 'absolute',
    top: -45,
    left: 37,
  },
  starfiveBefore: {
    backgroundColor: 'transparent',
    position: 'absolute',
    left: 0,
    top: 0,
    borderStyle: 'solid',
    borderRightWidth: 100,
    borderRightColor: 'transparent',
    borderBottomWidth: 70,
    borderBottomColor: 'red',
    borderLeftWidth: 100,
    borderLeftColor: 'transparent',
    transform: [
      { rotate: '35deg'}
    ]
  },
  starfiveAfter: {
    backgroundColor: 'transparent',
    position: 'absolute',
    top: 0,
    left: -25,
    width: 0,
    height: 0,
    borderStyle: 'solid',
    borderRightWidth: 100,
    borderRightColor: 'transparent',
    borderBottomWidth: 70,
    borderBottomColor: 'red',
    borderLeftWidth: 100,
    borderLeftColor: 'transparent',
    transform: [
      { rotate: '-35deg'}
    ],
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('StylePlayground', () => StylePlayground);

続きはまた自由研究が進んだら。