at backyard

Color my life with the chaos of trouble.

Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React. という警告を直した際のメモ

react-railsでアプリを書いていると、下記のような"warning"に出会った。

Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.

調べてみると下記のissueにたどり着いた。

github.com

内容を見てみると、以前に修正されてはいるんだけど、バージョン上げたらまた出てきたよ!という内容のようだ。
なお、この時点での最新バージョンは下記のとおりとなる。
・react-rails (v2.2.0)
・webpacker gem (v2.0)

---------追記---------

そうそう、自身の環境を書き忘れていたので、下記に追記
Rails 5.0.2
・react-rails (1.11.0)

なお、webpackerは使用していない。

------追記終わり------

そして、以前あがっていたissueとは下記のものになるよう。

github.com

issue内にも書かれているとおり、下記のコードを"application.js"に追記したら、warningが出なくなった。

ReactRailsUJS.handleEvent('turbolinks:before-cache', function() {
  window.ReactRailsUJS.unmountComponents();
});


ちなみにreact-railsは今年リリースした"Sauna Map of Japan"にて使用中。
フロント側は元々jQueryで書いていたのだが、今回react-railsを使って、絶賛書き直し中。
自身の勉強を兼ねてもいる。今更な話ではあるが、react良いですね〜

react-rails

github.com


ちなみにSauna Map of Japanはまだベータ版。少しずつではあるが、使いやすいものにしていきます。

Sauna Map of Japan

Sauna Map of Japan


そういえば、久しぶりのブログ更新になりました。
あと、少し前にMediumにも近況載せました。
最近フロントエンド側でコード書いていますよ、という話。

medium.com

Sauna Map of Japan - β

先日Sauna Map of JapanをBetaにあげました。
どんな機能追加が行われたかについては当サイトのChange logを引用します。

Betaリリースに伴い、下記の機能が追加されています。
・日本地図からの検索が県単位で行えるようになりました
・ユーザがサウナ情報の登録を行えるようになりました(ログイン時のみ)
・ユーザのコメント履歴をユーザページに追加
・ユーザアカウント作成時のメールアドレス認証

Railsで作られているSauna Map of Japanでは認証周りに"sorcery"を使用しているのですが、
とても使いやすくて重宝しています。
今回実装したメールアドレス認証もsorceryのおかげ。

Sauna Map of Japan - α

5/3に"Sauna Map of Japan"というサウナの情報サイトをリリースしました。
まだα版(試作段階)なので、今後色々と更新・変更を加えていく予定です。が、ひとまず先に公開してしまいます。 (最近サウナにハマっている自分自身が、実際に使いながら改良を加えていきたいということもあり)

Sauna Map of Japan

改善点やバグなど見つかりましたら、ご連絡いただけると幸いです。

4月。春の雨。最近のことをちょろっと書く。

最近、ブログを更新する習慣がパタリとなくなってしまった。
プライベートで黙々とRailsアプリを書いているからかもしれない。
GWまでにはベータ版を公開したい。そして旅行とか行く際に、自分自身が存分に活用したい。
出先でいろいろなサウナに入ってみたいので。というわけで、サウナがキーワードのWebアプリ書いています。

ElixirConf 2017

www.elixirconf.jp

行ってきた。
下記は感想・メモなどをまとめてくれたりしている方々の記事、あとtogetter。
あとで読もうとしているので、自分へのメモとして貼っています。

in.fablic.co.jp

thinca.hatenablog.com

togetter.com

Elixirは最近触り始めたばかりだし、あまり周辺の知識もないのだが、色々と話が聞けて面白かった。
今回聞けた話を伝ってElixir周りやErlang周辺を色々と調べてみたい。
時間ができたら、自身でも取っていたメモをちゃんと整理して、ブログに書きたい <= 自分の頭の整理も兼ねて。

Phoenixでも簡単なアプリを書いてみたが、書いてみた第一印象は思っていた以上にRailsっぽい。
勿論、全然違う側面もあるのだけど、入り口というか、精神的な敷居としてはかなり低く、
想像していたよりは抵抗なく入っていける感じがとても印象良かった。

PAUL McCARTNEY THE LIFE

※下記はAmazonへのリンクとなっています。

最近知ったんだけど、"ポールが初めて認めたバイオグラフィー"という売り文句で、本が発売されたらしい。
気づいてすぐにKindleで購入して今読んでいるところ。
まだ読み始めたばかりだが、とても面白い。
ま、ポール好きなので、当然面白い。

春の雨

春の雨ってやつですね、この土日は。 春の雨といえば、大昔に作ったこの曲を思い出す。 Peruriと言います。

youtu.be

様々な言語からMinioのファイルアップロードを試す

様々な言語からMinioを試してみる。
※といっても、現在は3言語しかまだ書いていない

f:id:shinshin86:20170318082954p:plain

最近 Go言語で書かれた、S3互換のオブジェクトストレージサーバを触り始めたので、
各言語での試し方をこちらにメモしておく。
なお、あくまで現時点でのやり方となるので、もしこの記事が古くなっているようだったら、
公式からDocsに飛んで確認するのが望ましい。

www.minio.io



まずはNode.jsから

Node.js

セットアップ

npm install minio

実装

const Minio = require('minio');
const fs = require('fs');
const path = require('path');

const location = 'us-east-1';

const minioClient = new Minio.Client({
  endPoint: END_POINT,
  port: PORT,
  secure: false, // テストなのでひとまずfalseにしている
  accessKey: ACCESS_KEY,
  secretKey: SECRET_KEY
});

// Bucket作成
function createBucket(bucketName) {
  minioClient.makeBucket(bucketName, location, function(err) {
    if(err) return console.log(err);

    console.log('Bucket created successfully in ', location);
    console.log('Created bucket name  => ', bucketName);
  });
}

// imagesディレクトリ内の画像ファイルを作成したBucketに全てアップロードする
function fileUpload(bucketName, uploadDir) {
  let files = fs.readdirSync(uploadDir);
  files.forEach(function(file) {
    console.log('Upload file =>', file);

    // アップロードするファイル名
    let filePath = path.join(uploadDir, file);
    // fPutObjectを使用してファイルをBucket "SampleImages"にアップロードします
    minioClient.fPutObject(bucketName, file, filePath, 'image/png', function(err, etag) {
      if(err) return console.log(err);
      console.log('File upload successfully!!');
      console.log('Uploaded File Name => ', file);
    });
  });
}

// 作成するBucket名
const bucketName = 'nodetest';

// アップロード対象が格納されたディレクトリ名
let targetDir = 'images';

// アップロード対象の絶対パス
let uploadDir = path.join(__dirname, targetDir);

// 実行
createBucket(bucketName);
fileUpload(bucketName, uploadDir);

Python

セットアップ

pip install minio

実装

from minio import Minio
from minio.error import ResponseError
import os
import glob

# テストなのでひとまずsecureはFalseにしている
minioClient = Minio(END_POINT,
    access_key=ACCESS_KEY,
    secret_key=SECRET_KEY,
    secure=False)

# Bucketを作成する
def create_bucket(bucket_name):
    try:
        minioClient.make_bucket(bucket_name, location='us-east-1')
    except ResponseError as err:
        print(err)

# 対象ディレクトリなのファイルを取得し、アップロードする
def file_upload(bucket_name, upload_dir):
    for i in glob.glob(os.path.join(upload_dir, '*.png')):
        try:
            minioClient.fput_object(bucket_name, os.path.basename(i), i)
        except ResponseError as err:
            print(err)


# 作成するBucket名
bucket_name = 'pythontest'

# アップロード対象のファイルが格納されたディレクトリ名
target_dir = 'images'

# アップロード対象の絶対パス
upload_dir = os.path.join(os.getcwd(), target_dir)

print('File upload start');
create_bucket(bucket_name)
file_upload(bucket_name, upload_dir)
print('File upload successfully!')

Go

セットアップ

go get -u github.com/minio/minio-go

実装

package main

import (
	"fmt"
	"github.com/minio/minio-go" // Import Minio library.
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

func main() {
	bname := "gotest"
	tdir := "images"
	tpath, err := os.Getwd()
	ssl := false // テストなのでひとまずfalseとしている
        location := "us-east-1"

	minioClient, err := minio.New(Endpoint,
		AccessKey,
		SecretKey, ssl)

	if err != nil {
		log.Fatalln(err)
	}

	// bucketを作成
	err = minioClient.MakeBucket(bname, location)
	if err != nil {
		// 既に同名のbucketが存在していないかを確認する
		exists, err := minioClient.BucketExists(bname)
		if err == nil && exists {
			fmt.Printf("We already own %s\n", bname)
		} else {
			log.Fatalln(err)
		}
	} else {
		fmt.Printf("Successfully created %s\n", bname)
	}

	// 指定したディレクトリからファイルを取得
	filelist, err := ioutil.ReadDir(filepath.Join(tpath, tdir))

	if err != nil {
		fmt.Println(err)
		return
	}

    // 取得したファイルをすべてアップロードする
	for index, element := range filelist {
		fmt.Printf("Uploading... %s : %d\n", element.Name(), index)

		n, err := minioClient.FPutObject(bname, element.Name(), filepath.Join(tpath, tdir, element.Name()), "image/png")
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Printf("Successfully uploadefd %s of size %d\n", element.Name(), n)
	}
}

ひとまずここまで。
汚いコードをただ書き散らしているような気もする。。。精進していかねば。

あと、昨日からElixir触り始めたので、Elixirのサンプルも近々書きたい。


ちょっと試すだけでも使用コストがかかってしまうAmazon S3の代替手段として
Minioの存在価値はとてもあると思う。気軽にローカルでの開発などに使えるので、とても便利。

インストール関連の記事は検索してみると色々出て来るし、公式のスタートガイドも分かりやすい。

いいですね、Minio

Ruby on Rails 5でページ遷移時にJavaScriptが読み込まれない問題の解決法

個人度がいつも以上に高い備忘録なので、この内容がリーチしない方にとっては読む意味ありません。

表題の件で悩まれている方がこのページに訪れた際は、下に貼ったリンク先の記事が解決の手立てとなるかもしれませんので、ぜひ見てみてください。
(現に私が解決したので)


Rails 5でアプリ書いているとき、ページ遷移時にJavascriptが読み込まれなくて困っていた。
解決のきっかけになったのは、下記の記事

qiita.com


最初、下記のように書いていたのだけど、これが間違えていた。

$(document).on 'ready page:load', ->
  console.log("call function!")

上に貼った記事にもある通り、この書き方ではRails 5に対応していないようだ。
正しくは下記。

$(document).on 'turbolinks:load', ->
  console.log("call function!")

感謝!

Node.js v 0.10系で、Promiseなどを使わないで、無理矢理に同期処理を行う。

JavaScript備忘録記事。

今年に入ってからNode.js + MongoDB(with mongoose)という組み合わせで仕事をしている。
ただ、プロジェクトの諸事情により、最近までNode.jsはPromiseが使えない古いバージョンを使わざる負えない状況だった。
具体的には0.10系。
そうなると困るのが同期的な処理をしたい時。

例えば"mongoose"を使って、指定した値を取得し処理をしたい時などに困るシーンが出てくる。
具体的には下記のような泥臭い処理を実装して凌いでいた。

var userModel = dataModel.getUserModel(); // "user"コレクションのMpdel取処理とする

// userコレクションから"country"が"us"のデータを取得する
userModel.find({"country" : "us"}, function(err, users) {

  // エラー処理
  if(err) {
    errorHandling();
  }

  // 取得件数が0件の場合の処理
  if(users.length === 0) {
    notGetProcessing();
  }

  // global変数として持たせることで共通的なカウンターとして使用
  counter = users.length;
  console.log("find user count : ", counter;

  users.forEach(function(user) {
    // 取得したデータに対する処理。この場合、"Processing"に処理を移し、
	// そちらでの処理が完了した後にGlobal変数である"counter"を-1していく
    setTimeout(function(a) {
      processing(a);
    },1000, user);
  });

  // Global変数である"counter"が"0"になった時点で、
  // 取得データに対する処理が全て完了したものとし、終了関数に処理を渡す
  setInterval(function() {
    if(counter === 0) {
      // End processing
      endProcess();
    }
  }, 100);
});

相当なスパゲティコードである。茹ですぎて完全に伸びている状態といったところか。
このように泥臭い処理をいたるところでしていたので、
相当コードがスパゲティになっていて、保守コストも高く付きそうになってしまっていた。
が、ある時点でNodeのバージョンが上がることになり、今ではバージョン6系で書いている。

Promiseも使えるようになったので、おかげで下記のように処理を変更できる。
(青臭いコードかもしれない。もし、もっとこうした方がスマートに出来るなどありましたら、コメントいただけると幸いです。。。)

// "country"が"us"のuserデータをPromiseで取得
function getUsUser() {
  let userModel = dataModel.getUserModel();
  return userModel.find({"country" : "us"}).exec();
}

// メイン処理
function main() {
  let promise = getUsUser();
  promise.then(function(users) {

    // 取得件数が0件の場合の処理
    if(users.length === 0) {
      notGetProcessing();
    }

    console.log("find user count: ", users.length);

    // 取得した全データに対して処理を行う
    users.forEach(function(user) {
      processing(user);
    });

    return users.length;
  })
  .then(function(userCount) {
    console.log("Successful user count : ", userCount);

    // 終了処理
    endProcess();
  })
  .catch(function(err) {
    conosole.log(err);

    // 終了処理
    endProcess();
  });
}

// 実行関数
main();

毎日泥臭いコードを書いていたのは大変だったが、
いかにして無理やり同期的に処理させるか?と考えてコードをガリガリ書いていたのは、
それはそれでとても勉強になったと感じている。