mozyのかきおき

mozyの読書感想文や思考置き場

【Docker上】 bundle install 時に `require': libmariadbclient.so.18: cannot open shared object file: No such file or directory が出た時

経緯

Railsコンテナで db関連の操作をしようとしたところ

`require': libmariadbclient.so.18: cannot open shared object file: No such file or directory - /usr/local/bundle/gems/mysql2-0.5.2/lib/mysql2/mysql2.so (LoadError)

とかいって毎回こけていた。

解決方法

mysql2 をインストールし直すと良いらしい。
docker-compose run --rm hoge bin/bundle exec gem uninstall mysql2 して
docker-compose run --rm web bin/bundle install --no-cache したら解決した

参考

ruby on rails - mysql2 gem not working after updating to Ubuntu 16.04 - libmysqlclient.so.18 - Stack Overflow

gem install scrypt でエラー

経緯

Mac Mojave 10.14.4 にて
gem install scrypt をしようとしたら以下のエラーに出会った。

An error occurred while installing scrypt (3.0.5), and Bundler cannot
continue.
Make sure that `gem install scrypt -v '3.0.5' --source 'https://rubygems.org/'`
succeeds before bundling.

In Gemfile:
  authlogic was resolved to 4.3.0, which depends on
    scrypt

解決策

command line tools for 10.13 をインストールして再度試したら解決した。

https://download.developer.apple.com/Developer_Tools/Command_Line_Tools_macOS_10.13_for_Xcode_10.1/Command_Line_Tools_macOS_10.13_for_Xcode_10.1.dmg

参考

stackoverflow.com

monappy事件についての雑記【後日編】

monappy事件ですが、犯人が見つかって書類送検されたようですね。

www.jiji.com

www.itmedia.co.jp

私たちは、当時リアルタイムで追跡を行っていたのでした

mozy-ok.hatenablog.com

mozy-ok.hatenablog.com

感想

結局わかったことは、犯人は必ずどこかに痕跡を残す。
それをわかりやすく残す人は捕まる。残さない人は捕まえることができない。ということなのだと思います。
また、追い始めるのは早ければ早いほど良い。人は多ければ多いほどいい。

そして、高い技術を持った彼を思うと、私はなんだか釈然としない気持ちになりました。
その時の気持ちを想像すると犯罪を犯してしまうのもわかる気もするからです。目の前に面白いものがあったら解きたくなってしまう気持ちが。
そんな時に、自分の立ち位置を気づきさえしていれば、真っ当に社会を変えられたでしょうし、それができるほどの技術を持っていたと思います。
彼は今、後悔しているかもしれません。犯罪者になってしまった自分を。
今後、反省をして戻ってきたその時に、
あなたの持っている素晴らしい技術を、どうか社会のために、世界のために使ってください。君にはそれができるのだから。
追跡を行っていた、とある無名のエンジニアからのお願いです。

最後に僕が尊敬しているプログラマであり、数学ガールの作者であり、クリスチャンである結城先生のツイートを引用します。

最後に

普段、我々エンジニアが生きている一風変わった業界には、若い年齢の方を含めて高い技術力を持った素晴らしいエンジニア達がいます。
そして、その素晴らしい技術を持ったエンジニアには、その技術を自分の為に使うのか、それとも社会の為に使うのか。そんな難しい判断を常に求められています。
承認欲求や、自己顕示欲に駆られる時もあるでしょう。そんなエンジニアがあなたの隣にいるかもしれません。
そんな業界で、あなたはどの様に振舞って、誰とどのようにこの先生きのこっていくのか。それがあなたにも求められています。

Swift で動画再生機能を最速で作る【約5分】

経緯

最速で実装チャレンジとしてやったことを書いていく。

目次

まず、先に手順を書いておく。

  1. Xcodeでプロジェクトを作成する
  2. mp4の動画をプロジェクトにコピーする
  3. storyboardで画面レイアウトを作る
  4. storyboardとViewControllerをつなぐ
  5. コードを書いていく
  6. 完成

やっていく

1. Xcodeでプロジェクトを作成する

言語はSwiftを選ぶのを忘れずに。

f:id:mozy_ok:20190309231339p:plain
今回はSingle View Appを選択

2. mp4の動画をプロジェクトにコピーする

iPhone でとったMOVなどは、ffmpegなどを使って変換してから追加すると良いかも。こんな感じに
ffmpeg -i 画面収録\ 2019-03-08\ 20.22.04.mov -pix_fmt yuv420p video.mp4

f:id:mozy_ok:20190309231528p:plain
ここのリソースに追加している

3. storyboardで画面レイアウトを作る

constractionsあたりの設定で中央に表示されるようにレイアウトも固定しておく。

f:id:mozy_ok:20190309231915p:plain
シンプルにボタンのみ

4. storyboardとViewControllerをつなぐ

右上にある丸が二つ重なっているアイコンを押してstoryboardとViewControllerを表示する。
んでそののちに、storyboardでボタンを選択して、controlキーを押しながらViewControllerに引っ張っていき適当な名前を付ける。 ここでconnectionAction にするのをお忘れなく。

5. コードを書いていく

ボタンアクションをガリガリかいていく。
コードの意味はシンプルなので読み解けると思う。

まずは import AVKit する。
あとはガリガリ

@IBAction func buttonAction(_ sender: Any)
    {
        if let path = Bundle.main.path(forResource: "video", ofType: "mp4")
        {
            let video = AVPlayer(url: URL(fileURLWithPath: path))
            let videoPlayer = AVPlayerViewController()
            videoPlayer.player = video
            
            present(videoPlayer, animated: true, completion:
            {
                video.play()
            })
        }
    }

6. 完成

ちなみに7分かかった笑
f:id:mozy_ok:20190309233221p:plain

シンプルな画面
f:id:mozy_ok:20190309233202p:plain
再生画面(標準で色々ついてる)

MySQL5.7 以降なら ElasticSearch 使わなくても いい感じに日本語全文検索できるよって話

経緯

とあるシステムでフォームに入力された文字を元に駅名テーブルから駅名を推薦したいという需要があった。そこで日本語の全文一致検索について調べた時のメモ

概要

もともと、MySQLには、LIKE検索というものがあって、文字を比較して検索をかけることができた。しかし、精度が低いしなんといっても遅い。
そこで、色々調べていたところ、MySQL5.7 以降ならinnodbの日本語全文検索がいい感じに使えるらしい。
そのため試しに使ってみた結果、速いし精度高いし、手軽だし幸せになれた。

やっていく

具体的には FULLTEXT KEY(title) WITH PARSER NGRAM みたいな感じにして検索するだけ。

以下の参考サイトの解説が最強なので読むべし。

原理の説明

MySQLのフルテキストインデックスを利用した全文検索は、以下のような原理で動きます。

INSERT/UPDATE時は対象カラムの内容をトークナイズしフルテキストインデックスに格納 全文検索時はフルテキストインデックス内から検索文字列をトークナイズし完全一致で検索 だが、MySQL5.6以前では日本語のトークナイズが空白文字やカンマなどでの区分けのみの状態でした。 つまり、日本語の場合は文字列ほぼそのまんまでインデックスに格納されてしまっていたのです。 そのインデックスに対して完全一致でしか検索しないので、文章の一部を含む文字列で全文検索をかけても、完全一致しないからヒットしないという状況でした。

MySQL5.7ではここが変わり、ngram(n文字で区分け)やmecab(辞書を利用する)によるトークナイズが利用可能になり、使い物になるレベルでの日本語全文検索が利用可能になったのです!

参考サイトより引用

参考サイト

LIKE検索より50倍速い!?MySQLでラクラク高速な日本語全文検索 - bitA Tech Blog

React Native + expo + FirebaseでFBログインを実装してみる

経緯

最近React Native + expo を触っていて、手習いとしてFirebase + FBログインを実装したのでメモ
本当はAuth0を使おうと思っていたけど時間がなかったのでFirebaseで。

やっていく

基本的には、以下の通りに進めていく。今回はiOSを試してみる。
Facebook - Expo Documentation

まずはFacebook Developer ページからアプリケーションを作成して、Facebookログイン プロダクトを追加する。 その後、プラットフォームを追加 > iOS を選択して、「バンドルID」にhost.exp.Exponent を指定する。
こんな感じ

f:id:mozy_ok:20190223184713p:plain
設定画面

そして、そのうち使うので、アプリIDと、シークレットキーをメモっておこう。

f:id:mozy_ok:20190223185251p:plain
FBの設定

次にFirebase Authentication を設定する。
Firebaseに登録をして、FBログインを有効にする。

f:id:mozy_ok:20190223185119p:plain
スクショとったときにはもう有効になってしまっていた 笑

そして、ここに先ほどメモったIDとシークレットキーを入れる。

f:id:mozy_ok:20190223185200p:plain
設定内容

最後に、ガンガンReact Nativeのコードをかいていく。
まずは expo init して雛形を作成して、App.jsを以下のように変えていく。
npm install firebase なども忘れずに。
apiKey などの config部分は、Firebaseのプロジェクト概要部分から、Webを選んで中身を置き換えてくださいな。

f:id:mozy_ok:20190223185555p:plain
プロジェクト設定部分

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

import * as firebase from 'firebase';

var config = {
  apiKey: "APIKEY",
  authDomain: "HOGE.firebaseapp.com",
  databaseURL: "HUGA.firebaseio.com",
  projectId: "IDID",
  storageBucket: "PIYO.appspot.com",
  messagingSenderId: "NUMBER"
};
firebase.initializeApp(config);

import { Container, Content, Header, Form, Input, Item, Button, Label } from 'native-base'; 

export default class App extends React.Component {
  constructor(props){
    super(props)

    this.state = ({
      email: '',
      password: ''
    })
  }

  signUpUser = ( email, password ) => {

    try {
      if(this.state.password.length<6)
      {
        alert('パスワードが短いよー 6文字以上にしてくれー')
        return;
      }

      firebase.auth().createUserAndRetrieveDataWithEmailAndPassword(email, password)

    } catch (error) {
      console.log(error.toString());
    }

  }

  loginUser = ( email, password ) => {

    try {
      firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
        console.log(user); 
      })
    } catch (error) {
       console.log(error.toString());
    }

  }

  async loginWithFacebook() {

    const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync('632193713899286', { permissions: ['public_profile'] })

    if (type == 'success') {
      const credential = firebase.auth.FacebookAuthProvider.credential(token)

      firebase.auth().signInWithCredential(credential).catch((error) => {
        console.log(error)
      })
    }
  }

  render() {
    return (
      <Container style={styles.container}>
        <Form>
          <Item>
            <Label>Email</Label>
            <Input
              autoCorrect={false}
              autoCapitalize="none"
              onChangeText={(email) => this.setState({ email })} 
            />
          </Item>

          <Item>
            <Label>Password</Label>
            <Input
              secureTextEntry={true}
              autoCorrect={false}
              autoCapitalize="none"
              onChangeText={(password) => this.setState({password})}
            />
          </Item>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            success
            onPress={() => this.loginUser(this.state.email, this.state.password)}
          >
            <Text style={{ color: 'white' }}>Login</Text>
          </Button>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            primary
            onPress={() => this.signUpUser(this.state.email, this.state.password)}
          >
            <Text style={{ color: 'white' }}>Sign Up</Text>
          </Button>

          <Button style={{ marginTop: 10 }}
            full
            rounded
            primary
            onPress={() => this.loginWithFacebook()}
          >
            <Text style={{ color: 'white' }}>Login with Facebook</Text>
          </Button>

        </Form>
      </Container>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    padding: 10
  },
});

結果

動いたぜ。いえーい

f:id:mozy_ok:20190223190026p:plain
動いた

GitHub でWIPな意味でのPRが作れるようになったよ

マジで最高です。

詳細はこちら

Introducing draft pull requests - The GitHub Blog

重要な部分を抜き出すと

Draft pull requests cannot be merged, and code owners are not automatically requested to review draft pull requests

要約すると

Draft PRはマージされないし、コードのオーナーに自動でレビューお願いしまっすーって通知も飛ばないよー ってことかな。