旅好きの気ままなお話

旅のこと映画、統計、会計、プログラミングなど、気の向くままに語ります

railsにvue-test-utilsを入れてみる

前提

  • webpackは使わない。
  • node.jsを入れている。
  • npmを使用出来る

※ webpackを使わない理由は対象をrails5だけに絞っていないからです。

必要なものをinstall

npm install --save-dev jest @vue/test-utils vue-jest babel-jest jest-serializer-vue

各設定ファイルを編集する

./package.json

{
  "name": "new_minkabu_pc",
  "version": "1.0.0",
  "description": "[![Build Status](https://travis-ci.com/minkabu/minkabu_pc.svg?token=A1r56DPr9tpa3qzgAWM1&branch=master)](https://travis-ci.com/minkabu/minkabu_pc) [![Maintainability](https://api.codeclimate.com/v1/badges/643434d3559be5ad03e3/maintainability)](https://codeclimate.com/repos/599d159c257148029c000746/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/643434d3559be5ad03e3/test_coverage)](https://codeclimate.com/repos/599d159c257148029c000746/test_coverage)",
  "main": "index.js",
  "directories": {
    "doc": "doc",
    "lib": "lib",
    "test": "test"
  },
  "scripts": {
    "unit": "jest --config test/unit/jest.conf.js --coverage",
    "test": "npm run unit"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/minkabu/minkabu_pc.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/minkabu/minkabu_pc/issues"
  },
  "homepage": "https://github.com/minkabu/minkabu_pc#readme",
  "devDependencies": {
    "@vue/test-utils": "^1.0.0-beta.19",
    "babel-jest": "^23.0.1",
    "jest": "^23.1.0",
    "jest-serializer-vue": "^2.0.2",
    "vue-jest": "^2.6.0"
  }
}

./test/unit/jest.conf.js

const path = require('path');

module.exports = {
  rootDir: path.resolve(__dirname, '../../'),
  moduleFileExtensions: [
    'js',
    'json',
    'vue'
  ],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  transform: {
    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
  },
  testPathIgnorePatterns: [
    '<rootDir>/test/e2e'
  ],
  snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
  setupFiles: ['<rootDir>/test/unit/setup'],
  mapCoverage: true,
  coverageDirectory: '<rootDir>/test/unit/coverage',
  collectCoverageFrom: [
    'src/**/*.{js,vue}',
    '!src/main.js',
    '!src/router/index.js',
    '!**/node_modules/**'
  ]
};

./.babelrc

{
  "presets": ["es2015"]
}

sampleのテストファイルを用意する

./app/assets/javascripts/App.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

./app/assets/javascripts/lib.js

export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

./app/assets/javascripts/tests/App.test.js

import * as lib from '../lib';
describe('Counter', () => {

  it('run test !!!!!!!!!', () => {
    expect(1).toBe(1)
  })

  it('hoge_test', () =>  {
    expect(lib.square(11)).toBe(121)
  })
})

流してみる

npm test もしくは jest で流れます。

npm testで流れる理由はpackage.jsonに記載しているからです。

vue.jsにeslintを設定した話(vscode使っています)

eslintとvue.js用のeslintプラグインをinstallする

今回はグローバルにインストールします。
npm i -g eslint eslint-plugin-vue
※上記はnpmですが、yarnでも大丈夫です。

vscodeに読み込ませるnode_moduleを設定

vscodeの"基本設定=>設定"のUser Settingsのjsonファイルを開いてください。 次に、自分の好きなパスのnode_moduleを設定してください。(今回はglobalのを読み込ませています。)

"eslint.nodePath": "/Users/[ユーザー名]/node_modules",
"eslint.enable": true,

.eslintrc.jsonを設置する

.eslintrcをプロジェクト直下に配置します。

.
├── .git
├── .gitignore
├── .eslintrc.json # <=ここに入れる 
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
├── bin
├── config
├── config.ru
├── db
├── lib
├── log
├── node_modules
├── package.json
├── public
├── storage
├── test
├── tmp
├── vendor
└── yarn.lock

内容はとりあえず下記で大丈夫です。(カスタマイズは各々が行ってください)
.eslintrc.json

{
  "extends": [
    "eslint:recommended",
    "plugin:vue/recommended"
  ]
}

上記で設定は完了です。

補足

下記プラグインについての補足です。
https://github.com/vuejs/eslint-plugin-vue

それぞれ、vue style guidの規約でどこまで反映するかを表しています。
"plugin:vue/recommended"を設定しておけばとりあえず、全て反映してくれるので、特にこだわりがなければ"plugin:vue/recommended"でよいかと思います。

  • plugin:vue/base - Settings and rules to enable correct ESLint parsing
  • plugin:vue/essential - Above, plus rules to prevent errors or unintended behavior
  • plugin:vue/strongly-recommended - Above, plus rules to considerably improve code readability and/or dev experience
  • plugin:vue/recommended - Above, plus rules to enforce subjective community defaults to ensure consistency

wavesで自作コインを作る方法

waves platformでアカウントを作る

waves platformは各種仮想通貨の取引とウォレットです。

アカウント作成は下記urlから出来ます
https://wavesplatform.com

コイン作成費用

作成には1wavesかかるので1wavesをウォレットの中に入れておきましょう。

wavesを入れる

wavesの購入は現在の日本の取引所ではできません。 ですので、海外取引所のbinanceでwavesを購入しwavesウォレットに送金するか、日本の取引所でビットコインイーサリアムを購入し、wavesウォレットに送金し、wavesウォレット内の取引所でwavesを購入しましょう。

コインを作成する

下記画面より作成します。 f:id:tabilike:20180609164233p:plain

  • Name of your Asset
    自分の通貨の名称を入力します

  • Description
    自分の通貨の説明を入れます。これは後で変更することができません。

  • Total tokens
    自分の通貨の総発行量を決めます。

  • Reissuable
    後でトークンの追加発行が出来るかを設定します。

  • Decimals
    通貨の小数点の単位を設定出来ます。(最高は8です)

上記を全て入力するとGenerateを押下します。

押下してYour transaction is on the way!の文言が出れば成功です。

作られたコインはどうなる?

全発行量、自分のウォレットに保持されます。 ただ、保持されるだけなので、それだけでは価値は0円になります。

f:id:tabilike:20180609164350p:plain

コインを売りに出す

すぐに売りに出すことが出来ます。
発行したばかりなのと全量自分で持っているためチャートはでていません。。。 売る時の手数料はたったの0.03wavesなので手数料はほとんど気にすることなく売れます。

f:id:tabilike:20180609164303p:plain

まとめ

wavesでは気軽に10分ほどでコインを作成出来てとっても便利です。コインを作ったら人に送ってみたりしてコミュニティ内の通貨や社内通貨などにも使ってみたり出来そうです。何より発行手数料が1wavesですのでほとんどコストもかからず、利用できちゃいます。 というわけで、世の中の人がもっとwavesを使うようになったらおもしろくなるんじゃないかと思っています。

ちなみに

自分の作ったコインはjourneyという名称です。 欲しいという方は連絡をくれれば適当に配りたいと思います(笑)

vue.jsのデリミタ(delimiter)を変更する方法 sampleとしてjinja2を使用

フロントエンドのフレームワークと既存のテンプレートエンジンのデリミタ(delimiter)って被ってしまった。。。

ちなみにデリミタ(delimiter)とは、"フィールド区切り文字"のことです。 具体的には railsのerbの<% hogehoge %>とか jinja2の{{ hogehoge }}とか bladeの{{ hogehoge }}とかです。

erbだと<% %>の中にrubyのコードが書けます。 これをデリミタといいます。

さて、 vue.jsのデリミタも実はjinja2やbladeと同じく{{ }}なんです。

ですので、jinja2とvue.jsをそのまま使用するとコンフリクトが生じてしまうのです。

そこで、解決策としてデリミタを変更するという方法があります。(ここからはjinja2をsampleに書きます。)

jinja2のデリミタかvueのデリミタどちらかを変更すればよいのです。

jinja2のデリミタを変更するのはフレームワークに依存してしまうケースがあるので、完結に変更するにはvue.jsがおすすめです。

下記コードは{{ }}から[[ ]]にデリミタを変更しています。

hoge.js

new Vue({
  el: '.js-component01',
  delimiters: ['[[', ']]'], //ここにデリミタの変更を記述
  data: {
    text: 'hogehoge'
  }
});

python側ではbottleを使用しています。

run.py

from bottle import route, run
from bottle import TEMPLATE_PATH, jinja2_template as template

@route('/top')
def main():
    return template('top', hoge = "pythonの結果")

run(host='localhost', port=8080, debug=True, reloader=True)

top.html

<div class="js-component01">
  <p>[[hoge]]</p>
</div>
<div>
  {{hoge}}
</div>

テンプレートでは同じ変数hogeでもvueで設定したものとpythonで設定したもので、異なる値が表示されるようになっています。

ちなみにデリミタを変更せずに行うと、python側が優先されます。

rubyでマークルツリーを書いてみた

ブロックチェーンの勉強をしていてマークルツリーがどんなものかをrubyで書いてみました。 (まだ、勉強したてなので間違えているかもしれません、その際はご教授ください)

マークルツリーについてはわかりやすい記事があるので他のを参考にするといいです。

以下がソースです。
make_merkle_tree(data_list)のdata_listは配列です。

ソース

require "digest/md5"

def make_merkle_tree(data_list)
  loop do
    hash_list = data_list.map {|data| Digest::MD5.hexdigest(data) }
    hash_set_list = make_set(hash_list)
    data_list = integrate_hash(hash_set_list)
    break if data_list.size == 1
  end
  data_list.first
end

def make_set(hash_list)
  hash_list.each_slice(2).to_a
end

def integrate_hash(hash_set_list)
  hash_set_list.inject([]) do |integrated_data_list, hash_set|
    hash_set << hash_set.first if hash_set.size < 2
    integrated_data_list << "#{hash_set[0]}#{hash_set[1]}"
  end
end

実際に動かしてみました。
data_listのなかがビットコインでいうトランザクション(取引)データです。
取引データが
["a","b","c","d","e"] とあったら。
全ての取引は
"dfbd34a5b6634d7ccd0aa26a4dec2eccfb8a3688c2f3cc709e72181bd8074cc3"
で全て表現できているということを表しています。

もし、["a","b","c","d","e"]が ["a","b","c","f","e"]のように書換えられてしまうと、 異なるハッシュ値を返してしまいます。

これはどういうことかというと、ビットコインの生成するための計算ではタイムスタンプやナンスなどとこのトランザクションの結果をまとめてハッシュ化しており、取引データが改竄されると、計算が合わなくなり、改竄が発覚します。

なので、ブロックチェーンのデータの改竄は難しくなっているのです。 この他にも、改竄対策にはデータのコンセンサスの問題などもあるのですが、今回はマークルツリーの実装の話なので、置いておきます。

実行

[9] pry(main)> make_merkle_tree(["a"])
=> "0cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e269772661"
[10] pry(main)> make_merkle_tree(["a","b"])
=> "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f"
[11] pry(main)> make_merkle_tree(["a","b","c","d"])
=> "3bc22fb7aaebe9c8c5d7de312b876bb81ee715fcc373a83611d163d9d4ea02a3"
[12] pry(main)> make_merkle_tree(["a","b","c","d","e"])
=> "dfbd34a5b6634d7ccd0aa26a4dec2eccfb8a3688c2f3cc709e72181bd8074cc3"

kotlinでbit flyerのapiから仮想通貨の価格を取得する(android)

kotlinのデータを取得する

下記のブログを参考にさせていただきました。 ver-1-0.net

khttpを入れる

androidをほとんど触ったことがないのでライブラリをどういう風に入れるかもわかりませんでした。。。
まずはbuild.gradleにkhttpのライブラリを入れましょう。

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile "khttp:khttp:0.1.0" // 追加する
}

ソースを書いていく

自分はMainActivity.ktにkhttpをinportしました。

import khttp.get

ちなみに下記はETHを取得しています。
また、MyAsyncTaskを使っている理由は、androidはメインスレッドでネットワーク通信が出来ないからです。(おそらくですが。。。)

取得処理自体はgetEthRateで行っています

全文ソース(MainActivity.kt)

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.os.AsyncTask
import khttp.get

open class MyAsyncTask : AsyncTask<Void, Void, String>() {

    override fun doInBackground(vararg params: Void): String? {
        return null
    }

    override fun onPostExecute(text: String) {}
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        object : MyAsyncTask() {
            override fun doInBackground(vararg params: Void): String? {
                return getEthRate()
            }
            override fun onPostExecute(text: String) {
                val ethRate = findViewById<TextView>(R.id.ethRate)
                ethRate.setText(text)
            }
        }.execute()
    }
}

fun getEthRate(): String {
    var url : String = "https://lightning.bitflyer.jp/v1/getboard"
    return get(url).jsonObject.getString("mid_price")
}

マニフェストを修正する

どうやらandroidは設定ファイルを修正しないとエミュレーターで通信出来ないようです。 ですので <uses-permission android:name="android.permission.INTERNET" />をAndroidManifest.xmlに追加しましょう。

全文ソース(AndroidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="e.yuma.cccheck">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>修正する

普段Androidを触っていないと慣れないことが多いですが、やってみたら意外と楽しいかもです。(画面の修正は難しいですね)

binanceから日足を取得するスクリプトを書きました(python)

仮想通貨のコイン間での値動きの相関を調べてみたいと思い、まずはbinanceからデータを取得するpythonスクリプトを書いてみました。

実装には下記のライブラリを使用させてもらいました。(作者の方は他のwrapperも作っていて意欲的な感じがします!)

github.com

ちなみに以下が書いてみたスクリプトです。
ソースコードは自分用に書いているので汚ないです。。。

from binance.client import Client
from datetime import datetime

class Binance(object):
    def __init__(self, api_key, api_secret):
        self.client = Client(api_key, api_secret)

    def get_bars(self, bar_duration, start, end):
        kline_list = []
        for ticker_combination in self.ticker_combinations():    
            klines = self.client.get_historical_klines(ticker_combination, bar_duration, start, end)
            klines = self.format_klines(klines)
            kline_list.append({"ticker_combination":ticker_combination, "klines":klines})
        return kline_list

    def ticker_combinations(self):        
        combinations = self.client.get_all_tickers()
        ticker_combination_list = []
        for combination in combinations:
            ticker_combination_list.append(combination['symbol'])
        return ticker_combination_list

    def format_klines(self, klines):
        klines = list(map(lambda x: [self.format_timestamp(x[0]),x[1],x[2],x[3],x[4],x[5],self.format_timestamp(x[6]),x[7],x[8],x[9],x[10],x[11]], klines))
        return klines

    def format_timestamp(self, timestamp):
        time = datetime.fromtimestamp(timestamp/1000)
        return time

利用方法

api_key, api_secretはbinanceで取得してください (binanceは香港の仮想通貨取引所です。)

start = "1 Dec, 2017"  
end = "1 Jan, 2018"  
bar_duration = "1d"  

bi = Binance(api_key, api_secret)  
bi.get_bars(bar_duration, start, end)  

分析のデータがほしい方はぜひ使ってみてください。