人生なんて暇つぶし

Webエンジニアの雑記ブログ。技術の話題はDApps開発、NEMアプリ開発がメイン。

移転しました。

約3秒後に自動的にリダイレクトします。

React、Vue.jsを学ぶ前に「初めてのJavaScript」

「JavaScript」と聞くとどういうイメージを思い浮かべるでしょうか?

ぼくがプログラミングを始めたのは10数年前。

当時は「HTML + CSS + JavaScriptでWebサイトを作る!」みたいな入門書が並んでいたのを覚えています。要は初心者プログラマー向けってイメージでした。

それから6年ほどブランクを経て、Web業界に転身するんですが状況がガラリと変わってました。

2010年頃にNode.jsが登場し、サーバーサイドでもJavaScriptが利用できる。

フロントエンドとバックエンドで同じ言語を使える。そして、Backbone.js、Angular、React、Vue.jsなどJSフレームワークが続々と登場し衝撃と困惑を同時に味わいました。

業務はバックエンド開発が中心でしたので、JavaScriptを触る機会があってもググった知識でごまかしていました。

最近、アプリを作ろうとAngularを学ぶも文法がわからず挫折。

「昔のイメージのままじゃダメだな」って意識をアップデートし本書を手に取りました。

www.oreilly.co.jp

本書の対象者は?

下記に1つでも当てはまったら読む価値があります。

  • これからWebサービスやアプリを開発したい
  • React、Vue.js、AngularなどのJSフレームワーク触ったけど理解できなかった
  • 書いた順番通りに動いてくれない
  • 次の文を理解できない(1から書けない)
const o = {
  name: 'Julie',
  greetBackwards: function() {
    const getReverseName = () => {
      console.log(this);
      let nameBackwards = '';
      for (let i = this.name.length - 1; i >= 0; i--) {
        nameBackwards += this.name[i];
      }
      return nameBackwards;
    };
    return `${getReverseName()} si eman ym ,olleH`;
  }
};

console.log(o.greetBackwards()); 

JavaやPHP、Pythonを主に使用してますが、JavaScriptで苦しんだのが関数の文法非同期処理

上記の関数はES2015で導入されたアロー関数という表記法を利用しています。わかってしまえばなんてことないんですけどね。

また、思うように動かない理由は非同期処理を理解していないのが原因です。

プログラムは1行目から順番に処理されるというある種思い込みがありましたが、処理が完了するのを待たずに次の処理に移るので、それらを制御するための知識が必要となります。

ググった情報はあくまでも断片的な知識なので、一度包括的に学習するのをオススメします。

本書の評価は?

基礎的な知識は十分に網羅されており、本書を学習したことでVue.jsやAngularの学習スピードが向上しました。

また、サンプルが数多く載っておりほとんどがNode.jsで動作確認できるので、とりあえず写経してパッと動かしてみることができるので理解も早いです。

PDF形式であればコピペもしやすく、持ち運びもしやすいのでオススメです。

PDFでの購入はこちら

※Kindleは参考書に向いてないと思ってるので、PDFで購入するようにしています。

学習後の理解度ですが、ぼくは本書を学習した総仕上げとして、Node.jsでSlackのリアクションを集計するツールを作成しました。

www.maroemon.com

これくらい作れるって目安になるかと思います。

まとめ

フロントエンドを学ぶことは必須ではないですが、技術について発信したいならJavaScriptの基礎から学んでおいて損はないでしょう。

「ぼくはこんなサービス作りました!」ってわかりやすくアピールしやすいですからね。ではまた。

try〜catchしすぎて人生が前に進まない人

f:id:maroemon58:20180807114338j:plain

先日、TwitterのTLでこんなツイートを見かけました。

ホントその通りだなって思います。

現状に満足できずどうすれば良いか聞かれますが、アドバイスされて実践し続ける人は少ないです。

たぶんこういう思考なんでしょう。

<?php

try {
    自己学習に励む();
}
catch(Exception $仕事疲れた) {
    飯食って寝る();
}
catch(Exception $気分がのらない) {
    テレビ見る();
    飯食って寝る();
}
catch(Exception $飲み会誘われた) {
    気分転換が必要と解釈();
    朝まで飲む();
}

起業するような方はこんな感じですかね。

<?php

try {
    $自己学習スレッド = new 自己学習に励むThread();
    $自己学習スレッド->start();

    $起業スレッド = 起業するThread();
    $起業スレッド->start();

    $セミナースレッド = セミナー開催();
    $セミナースレッド->start();

    $オンラインサロンスレッド = オンラインサロン開設Thread();
    $オンラインサロンスレッド->start();
}
// catch(Exception $仕事疲れた) {
//     飯食って寝る();
// }
// catch(Exception $気分がのらない) {
//     テレビ見る();
//     飯食って寝る();
// }
// catch(Exception $飲み会誘われた) {
//     気分転換が必要と解釈();
//     朝まで飲む();
// }
finally {
    社会に還元する();
}

かなり大雑把ですね笑

すごいと感じる人は物凄い努力して突き進んでるように思います。

良い意味で「猪突猛進」。

そして次から次へとチャレンジし、周りを巻き込み引き上げてもくれる。

まとめ

誘惑に抗わない人生は一見楽に見えますが、長い期間やりたくない仕事をすることになりかねないです。

そんな人生は苦痛でしょう。

人生40代で折り返しと言いますが、20代、30代と早いうちから知識や経験を積み重ねておくと豊かな人生になるのでは?

ではまた。

twitter.com

node.jsでSlackのリアクションを集計するツールを作ってみた

普段はWebのバックエンド開発をメインでやってます。

最近プライベートでWebサービスやDAppsの開発を始め、JavaScriptを扱う機会が多くググった知識だけでは戦えなくなって勉強し直しました。

一通り学習し終えたのでnode.jsでSlackのリアクションを集計するツールを作ってみました。

ざっくりとした仕様

以下のような仕様で開発しました。

  • プログラムは定期的(1回/日)に実行
  • 前日にリアクションされた絵文字を集計 ※publicチャンネルのみ
  • 集計結果を降順でソート
  • 結果を特定のチャンネルに送信

そもそもリアクションって何?って方はこちらへ。

get.slack.help

必要なAPIを選定する

フローを簡単にまとめると

  1. 対象チャンネルを取得
  2. 各チャンネル内の会話(リアクション)を取得
  3. リアクションを集計→送信

となります。

1と2はAPIで取得する情報になります。

良さげなやつがありました。

channels.list method | Slack

channels.history method | Slack

channels.listで対象チャンネルを取得、取得したchannelコードをchannels.historyに引き渡しリアクションを取得します。

channels.historyを実行するとき取得期間を絞るため、oldestに前日0時0分、latestに当日0時0分を設定します。

※厳密にやると0時0分が重複するからダメかもしれない。

Slackにアプリを登録する

事前準備としてSlackにアプリを登録し、Botユーザを作成します。

詳しくはこちらへ。

get.slack.help

channels.historyの実行にchannels:history readの権限が必要なので付与しておきましょう。

合わせてOAuth Access TokenとBot User OAuth Tokenが必要となるのでメモっておきます。

f:id:maroemon58:20180806181325p:plain

実装

github.com

必要なパラメータ(OAuth Access TokenとBot User OAuth Token、送信先チャンネル)は.envファイルに記載しています。

// load .env
require('dotenv').config();
const SLACK_TOKEN = process.env.SLACK_TOKEN;
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
const SEND_CHANNEL = process.env.SEND_CHANNEL;

const util = require('util');
const slack = require('slack');
const moment = require('moment-timezone');

const CronJob = require('cron').CronJob;

function getChannels(oldest, latest) {
  return new Promise(function(onFulfilled, onRejected) {
    slack.channels.list({ token: SLACK_TOKEN }).then(response => {
      onFulfilled({
        channels: response.channels.map(channel => channel.id),
        oldest: oldest,
        latest: latest
      });
    });
  });
}

function getReactions(responses) {
  return new Promise(function(onFulfilled, onRejected) {
    reactions = [];

    const channels = responses.channels;
    const oldest = responses.oldest;
    const latest = responses.latest;

    let i = 0;
    channels.map(channel => {
      slack.channels
        .history({
          token: SLACK_TOKEN,
          channel: channel,
          oldest: oldest,
          latest: latest
        })
        .then(response => {
          response.messages.map(message => {
            if ('reactions' in message) {
              reactions = reactions.concat(message.reactions);
            }
          });
          if (++i == channels.length) {
            onFulfilled(reactions);
          }
        })
        .catch(error => {
          console.log('error!!');
        });
    });
  });
}

sortReaction = reactions => {
  // counter
  totalReaction = [];

  reactions.map(reaction => {
    name = ':' + reaction.name + ':';
    if (name in totalReaction === false) {
      totalReaction[name] = 0;
    }
    totalReaction[name] += reaction.count;
  });

  // convert for sort
  sortReaction = [];
  for (name in totalReaction) {
    sortReaction.push({
      name: name,
      count: totalReaction[name]
    });
  }

  sortReaction = sortReaction.sort((a, b) => {
    if (a.count > b.count) return -1;
    else if (a.count < b.count) return 1;
    else return 0;
  });

  // create send message
  msg = '';
  for (reaction of sortReaction) {
    msg += reaction.name + ' : ' + reaction.count + '\n';
  }

  return msg;
};

postMessage = () => {
  // oldest ※前日0時0分
  oldest = moment()
    .subtract(1, 'days')
    .startOf('day');

  latest = moment().startOf('day');

  getChannels(oldest.unix(), latest.unix())
    .then(getReactions)
    .then(reactions => {
      if (reactions.length === 0) {
        msg =
          'Good Morning!! ' +
          oldest.format('M/D(ddd)') +
          'のリアクションは・・・なしです。';
      } else {
        msg =
          'Good Morning!! ' +
          oldest.format('M/D(ddd)') +
          'のリアクション集計しました。\n\n';
        msg += sortReaction(reactions);
      }

      console.log(msg);

      slack.chat.postMessage({
        token: SLACK_BOT_TOKEN,
        channel: SEND_CHANNEL,
        text: msg
      });
    });
};
const job = new CronJob({
  /*
  Seconds: 0-59
  Minutes: 0-59
  Hours: 0-23
  Day of Month: 1-31
  Months: 0-11
  Day of Week: 0-6
  */
  cronTime: '0 0 9 * * *', // 毎日午前9時に送信
  onTick: postMessage,
  start: false,
  timeZone: 'Asia/Tokyo'
});
job.start();

正しく動けばこんな感じでBotユーザから送信されます。

sample

GitHubにインストール手順をまとめているので、使ってみたい方はどうぞ。

まとめ

実は絶賛開発中の社内通貨プロジェクトで利用しようかなって考えてます。

www.maroemon.com

当時はSlackを利用してなかったので、別のツールを利用してイイねのやり取りを行う予定でした。

社内のチャットツールをSlackに切り替えリアクションのやり取りが予想以上に活発なので、データを収集して活用する術を頭の中で妄想してます。

たぶん特定のリアクションを貰う(贈る)と通貨がもらえるような感じになりそうです。

ではまた。

twitter.com

経験年数で足切りされたくない人へ、スキルを偏差値化するマッチングサイトFindy

IT業界で企業がエンジニアの技量を図る基準と言えば、まず第一に経験年数でしょう。

転職サイトを見ると経験年数3年以上ないと足切りされるとこも多いですね。

経験年数3年の理由は?

理由1:ミスマッチの応募者を避けたい

ミスマッチの応募者が多いと、選考や面接に手間も時間もかかる。そのため、「1年やってればこのくらいはわかるはず」「3年以上と書けば素人はこないだろう」などの思惑で境界線を設定する。

理由2:以前の中途採用者から判断するしかない

年数を決めるのは採用予定部署の責任者などだが、中途採用者の経験年数とその後の活躍度を判断材料にすることが多い。つまり、「以前に採った『経験1年』は使えなかった」などの肌感覚で数字を出す。

理由3:広告スペースと広告費に限りがある

ほしい人材を詳細に説明すると、文章量が膨大になり、広告スペースに収まらなくなってしまう。また、広告費に上限があるので、ほしいスペースを確保できない場合もある。

引用元:https://next.rikunabi.com/tech/docs/ct_s03600.jsp?p=000052

あくまでも目安ということみたいです。

基準を満たしていない場合、どう捉えらばいいでしょう?

3人のコンサルタントは口をそろえて「年数が足りなくても応募すべき」という。しかし、無条件というわけではない。

「経験年数は通常8割までが許容量だと思います。経験3年以上という記載なら、2年半ですね。大切なのは、不足の2割を補うプラスアルファ。そこが強いことが条件ですが、6、7割でも可能な場合もあります」(佐藤氏)

「ただ部署にいただけの経験3年と、業務をみっちり学んだ経験2年では、スキルで後者が勝る場合もある。しかし、企業はその事実を知りませんから、まずは応募書類でアピールすべきです。 例えば、『経験は2年ですが、私が考える御社の業務展開に特化した、密度の濃い2年でした。なぜなら……』などと具体的に説明する。これは面接に進んだ場合のアピールにも使えます」(稲垣氏)

引用元:https://next.rikunabi.com/tech/docs/ct_s03600.jsp?p=000052

何かアピールポイントが必要ということです。

そこでエンジニアに嬉しいサービスがFindy(ファインディ)です。

f:id:maroemon58:20180805202114p:plain

https://findy-code.i

コードを偏差値化?

Findyはエンジニア向けのマッチングサイトです。

最大の特徴はGitHub連携でコードを偏差値化できる機能

使い方はカンタンでGitHubアカウントでログインするだけ。

ぼくも使ってみました。

なかなか手厳しいですね・・・。

フリーランス・副業の場合は偏差値50以上が案件紹介の条件となってます。

ただ、こんな注意書きも。

しかしながら、十分なスキルをお持ちの方でスキル偏差値を解析しきれない方がいる事を把握をしており、スキル偏差値以外にも新しい指標やプロフィール項目のチェックによる解析方法の追加を近いうちに実装の検討をしていきます。

会社では高い評価を得ているけどGitHubにコードを残していない人は、今後Findyでも同等の評価を得られるかも知れませんね。

欲を言えば点数の内訳を提示してもらえば改善しがいがあるのに・・・と思いました。

まとめ

これまで会社の仕事にフルコミットしてたので、世間へアピールする材料がないって焦ってる人もいるでしょう。

ぼくもその1人です。

転職するにしてもポートフォリオの提出を求められる時代ですので、エンジニアとして活躍するために普段からセルフブランディングを心がけたいですね。

ではまた。

twitter.com

FileZillaの設定を新しいMacに引き継ぐ方法

f:id:maroemon58:20180803141856p:plain

新しいMacBook Proを購入したので、せっせと必要なアプリをインストールしています。

FTPクライアントは旧Macで使用していたFileZilleを利用することにしたのですが、「もしかしてまた1からアクセス先を設定すんの?」と一瞬脳裏をよぎりました。

そんな面倒なことやってられないので、いろいろ調べてみたところ無事に引き継げたので設定方法をまとめます。

旧Macから設定ファイルをエクスポート

まずは旧MacのFileZillaを起動し、「ファイル > エクスポート」をクリックします。

f:id:maroemon58:20180803140912p:plain

「サイトマネージャーエントリをエクスポート」「設定をエクスポート」にチェックを入れ、「OK」をクリックします。

f:id:maroemon58:20180803140933p:plain

エクスポートするフォルダとファイル名を決めて、「保存」をクリックします。

f:id:maroemon58:20180803140942p:plain

エクスポートしたファイルが出力されたら、新Macの作業に移るのでファイルを転送しましょう。

新Macで設定ファイルをインポート

「ファイル > インポート」をクリックすると、読み込むファイルを聞かれるので転送したファイルを選択します。

f:id:maroemon58:20180803140956p:plain

インポート可能なデータが表示されるので、全部チェックし「OK」クリックすると完了です!

f:id:maroemon58:20180803141007p:plain

まとめ

設定自体は非常にカンタンに引き継げますが、ログイン時に必要な鍵ファイルなどは別途持ってこないといけないので、そこだけ気をつけましょう。
※ユーザ名が一緒なら同じ場所に鍵を放り投げればいけるはずです。

ではまた。

twitter.com

【随時更新】WebエンジニアがMacBook Proを新調したのでインストールしたアプリを紹介する

f:id:maroemon58:20180731161902p:plain

MacBook Air 2015 11inchからMacBook Pro 2018 15inchに買い替えました。

ものすごく快適です。

旧Macから移行を行ったのですが、不要なゴミ(アプリ等)は入れたくなったので必要なアプリや設定を1から行いました。

OS設定

3本指ドラッグを有効にする

「システム環境設定 > アクセシビリティ > マウス/トラックパッド > トラックパッドオプション」を開き、「ドラッグを有効にする」にチェックし、「3本指のドラッグ」を選択します。

便利アプリ

Google日本語入力

MacOS標準で搭載されている日本語入力エンジン「日本語IM」は使いづらいので、Google日本語入力をインストールします。

www.google.co.jp

キーボードの入力ソースも英数(Google)、ひらがな(Google)のみに変更しました。

※「システム環境設定 > キーボード > 入力ソース」から設定可能

Parallels Desktop

Windows環境を利用するための仮想環境アプリ。

www.parallels.com

Office 365

Mac標準のアプリで十分ですが、Windowsユーザとの互換性のため・・・。

www.office.com

Pocket

お気に入りするまではないけど、とりあえずWebページを保存するアプリ。あとで読み返したいときに便利です。

Pocket

Pocket

  • Read It Later, Inc
  • ニュース
  • 無料

Skitch

Mac定番のペイントアプリ。ブログ画像の注釈入れるのに重宝してます。

Be Focused

シンプルなタイマーアプリ。集中して作業したいときに重宝します。

Be Focused - 仕事および勉強用の Timer

Be Focused - 仕事および勉強用の Timer

  • Denys Yevenko
  • 仕事効率化
  • 無料

Alfred 3

定番のランチャーアプリ。

www.alfredapp.com

OmniFocus 2

GTDというタスク管理術のためのアプリ。

www.omnigroup.com

ライセンスの移行方法についてはこちら。

www.maroemon.com

BetterTouchTool

トラックパッドジェスチャーを拡張するアプリ。こちらもド定番です。

https://folivora.ai

AppCleaner

アプリを削除する際に関連ファイルもまとめて削除してくれるアプリ。アンインストール時にゴミを残したくない場合は必須です。

AppCleaner

Tunnelblick

OpenVPNクライアント。社外から社内ネットワークに接続するときに必要となるアプリです。※サーバの設定も必要。

tunnelblick.net

ブラウザ

Web系の仕事をしているので、有名所は動作確認のため全てインストールしています。

Google Chrome

www.google.com

FireFox

www.mozilla.org

SNS系アプリ

LINE

生活必須のSNSアプリ。

LINE

LINE

  • LINE Corporation
  • ソーシャルネットワーキング
  • 無料

Skype

チャットアプリ。たまに仕事で使います。

www.skype.com

TweetBot 3

Mac専用Twitterクライアント。有料ですが非常に使いやすくオススメです。

tapbots.com

Slack

一般にも勢力を伸ばしつつあるチャットツール。使うのが楽しいアプリです。仕事で使っているので必須。

slack.com

Discord

開発者コミュニティで使用しているチャットアプリ。

discordapp.com

開発用アプリ

Xcode

アプリ開発はしないが何かと必要になるので一応。

Xcode - Apple Developer

Visual Studio Code

お気に入りの開発用エディタ。最近Atomから乗り換えました。

code.visualstudio.com

iTerm2

ターミナルアプリ。細かい設定は別途紹介するかもしれないです。

www.iterm2.com

SourceTree

Gitクライアントアプリ。ターミナルやVScodeでGit操作することが多いですが一応入れておきます。

www.sourcetreeapp.com

Homebrew

macOS 用パッケージマネージャー。

brew.sh

Nano Wallet

仮想通貨NEMのウォレット。ブロックチェーン開発で使用します。

NEM – Distributed Ledger Technology (Blockchain) » Downloads

FileZilla

FTPクライアント。もっとUIが洒落てれば100点なんですが・・・。でも、ミスったら死ぬので機能優先。

filezilla-project.org

VirtualBox

仮想環境を作成するためのアプリ。プロジェクトごとに開発環境を分けたいときは必須。

Oracle VM VirtualBox

Vagrant

こちらも仮想環境用。VirtualBoxとセットで使用します。

www.vagrantup.com

Sequel Pro

MySQL用DBクライアント。

Sequel Pro

PSequel

PostgreSQL用DBクライアント。

www.psequel.com

Git

$ brew install git

Python

$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
$ git clone https://github.com/yyuu/pyenv-pip-rehash.git ~/.pyenv/plugins/pyenv-pip-rehash
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
$ pyenv install -l | grep anaconda3
$ pyenv install anaconda3-5.2.0
$ pyenv global anaconda3-5.2.0
$ echo 'export PATH="$PYENV_ROOT/versions/anaconda3-5.2.0/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

# 確認
$ python --version
Python 3.6.5 :: Anaconda, Inc.

※詳細はこちら https://qiita.com/maroemon58/items/53977dc802c6373c027e

node.js

$ brew install nodebrew
$ echo PATH="$HOME/.nodebrew/current/bin:$PATH" >> ~/.bashrc

$ mkdir ~/.nodebrew
$ mkdir ~/.nodebrew/src
$ nodebrew install-binary v6.10.3
$ nodebrew use v6.10.3

# 確認
$ node -v
v6.10.3
$ npm -v
3.10.10

※詳細はこちら https://qiita.com/maroemon58/items/e18ac7e80a20a3c764f8

bash_profile

Pythonのインストールで.bashrcに追記しましたが、MacOSではターミナル起動時に読み込まないので読み込むように.bash_profileに追記します。※ファイルがない場合は新規作成

$ vim ~/.bash_profile
# 下記を追加
if [ -f ~/.bashrc]; then
    . ~/.bashrc
fi

Ganache

DApps用のアプリです。Ganacheはローカル環境上にテスト用のプライベートチェーンを構築します。

truffleframework.com

Truffle

dApps開発のためのフレームワーク。

$ npm install -g truffle

twitter.com

DApps開発ギルドでデプロイしたアプリを1から作ってみる

先日、オオキマキさん(@ookimaki_JP)が主催するDApps開発ギルドの勉強会に参加してきました。

勉強会は2時間という限られた時間だったので、今回作ったアプリを復習がてら自分で1から作成してみました。

勉強会で行った内容はこちら。

givinglog.com

それでは作ってみます。

開発環境

MacBook Pro(macOS High Sierra 10.13.6)

MetaMask 4.8.0

Ganache 1.2.1

$ npm -v
6.1.0

$ node -v
v10.7.0

$ truffle version
Truffle v4.1.13 (core: 4.1.13)
Solidity v0.4.24 (solc-js)

環境構築が済んでいない方はこちらへ。

givinglog.com

プロジェクト初期化

はじめにプロジェクト用のフォルダを作成し移動しましょう。

$ cd ~/Document
$ mkdir ~/dapps
$ mkdir ~/dapps/turotial/
$ cd ~/dapps/tutorial/

今回はtutorialというフォルダで作業を行います。

プロジェクトフォルダに移動したら、truffleというフレームワークを初期化します。

$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test

tutorial直下にファイルが作成されていることを確認しましょう。

f:id:maroemon58:20180728111325p:plain

コントラクトを作成

contractsフォルダの下にSimpleStore.solを作成します。

ファイルの中身は次のコードを記載します。

pragma solidity ^0.4.23;

contract SimpleStore {
    string value;

    function set(string _value) public {
        value = _value;
    }

    function get() public view returns (string) {
        return (value);
    }
}

Migrationsを作成

先ほど書いたコントラクトをデプロイするためのmigrationを作成します。

migrationsフォルダに2_deploy_contracts.jsを作成します。

ファイルの中身は次のコードを記載します。

var SimpleStore = artifacts.require('../contracts/SimpleStore.sol');

module.exports = function(deployer) {
  deployer.deploy(SimpleStore);
};

ネットワーク設定

次にデプロイするネットワークを設定ファイルに追記します。

truffle.jsに次のコードを記載します。

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks: {
    development: {
      host: '127.0.0.1',
      port: 8545,
      network_id: '*'
    }
  }
};

host: 127.0.0.1port: 8545という接続先はプライベートチェーンGanacheを指しています。

※Ganacheのポート番号を8545に変更しています。Ganacheの設定画面から変更可能です。

デプロイ

準備が整ったのでデプロイします。コマンドを実行する前にGanacheを起動しておきましょう。

$ truffle migrate
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/SimpleStore.sol...
Writing artifacts to ./build/contracts

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x55a8c1d05804136fb1670127c8bf4f08b6832a74a44e8a0006b8108ffb355d46
  Migrations: 0x562f587d7241ca995b50476adbbdc4bf3b7fa75f
Saving successful migration to network...
  ... 0xa1d9bb5b7594aea5b49ea110e2648dc5859c49c18b05aad555fab6c4dc2b6658
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying SimpleStore...
  ... 0x4dd1bdb65839e6eaade2cb3a45b9d63fa403bf66f8609d014d54eb09eaf07c13
  SimpleStore: 0x15a40f91de5f6797721266c6a6daf83758d10e00
Saving successful migration to network...
  ... 0xe1b4d2a0a3aba6861b6e2c79fa5285af636d04d2ce030a0fa85db53a250ac229
Saving artifacts...

SimpleStore: 0x15a40f91de5f6797721266c6a6daf83758d10e00がコントラクトのアドレスになります。 重要なのでメモっておきましょう。※これって後で確認したいときどうするんでしょう?

無事にデプロイできたらbuildというフォルダが作成されているはずです。

f:id:maroemon58:20180728111540p:plain

フロントアプリを作成

コントラクトがデプロイできたので、これを利用するためのフロントアプリを作成します。

tutorial直下にsrcフォルダを作成し、下記3つのファイルを追加していきます。

f:id:maroemon58:20180728111642p:plain

web3.min.jsは下ページから最新バージョンをダウンロードし、dist/web3.min.jsを所定の位置に配置します。

github.com

contract_abi.jsは新規に作成し、下記コードを記載しましょう。

変数の中身はbuild/SimpleStore.jsonのabiから取ってきたものです。

var contractABI = [
  {
    // build/SimpleStore.jsonのabiをコピペ
    constant: false,
    inputs: [
      {
        name: '_value',
        type: 'string'
      }
    ],
    name: 'set',
    outputs: [],
    payable: false,
    stateMutability: 'nonpayable',
    type: 'function'
  },
  {
    constant: true,
    inputs: [],
    name: 'get',
    outputs: [
      {
        name: '',
        type: 'string'
      }
    ],
    payable: false,
    stateMutability: 'view',
    type: 'function'
  }
];

index.htmlの中身は最低限動くレベルで書いておきます。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>DApps Dev Guild Tutorial</title>
  <script language="javascript" type="text/javascript" src="./js/web3.min.js"></script>
  <script language="javascript" type="text/javascript" src="./js/contract_abi.js"></script>

  <script>
    var contract;
    var userAccount;

    function startApp() {
      var contractAddress = "0xdc30da2490373416e7bf7467bb297bae624287b4";

      contract = new web3js.eth.Contract(contractABI, contractAddress);

      var accountInterval = setInterval(function () {
        web3.eth.getAccounts((error, accounts) => {
          if (accounts[0] !== userAccount) {
            userAccount = accounts[0];
          }
        });
      }, 100);
    }

    window.addEventListener('load', function () {
      // Checking if Web3 has been injected by the browser(Mist/MetaMask)
      if (typeof web3 !== 'undefined') {
        // Use Mist/MetaMask's provider
        web3js = new Web3(web3.currentProvider);
      } else {
        // Handle the case where the user doesn't have Metamask installed
        // Probably show them a message prompting them to install Metamask
      }
      // Now you can start your app & access web3 freely;
      startApp();
    });

    function set() {
      var _val = document.getElementById("val").value;
      contract.methods.set(_val)
        .send({ from: userAccount })
        .on("transactionHash", function (txhash) {
          alert("Txhash: " + txhash);
        })
        .on("receipt", function (receipt) {
          console.log(receipt);
        })
        .on("error", function (error) {
          console.log(error);
        });
    }

    function get() {
      contract.methods.get().call().then(function (_val) {
        alert(_val);
      });
    }
  </script>
</head>

<body>
  <h1>DApps Dev Guild Tutorial</h1>

  <input type="text" name="val" id="val">
  <input type="button" value="Set" onclick="set()">
  <input type="button" value="Get" onclick="get()">
</body>

</html>

MetaMaskにethを用意する

アプリを実行するためにはMetaMaskにethがないと利用できないので、Ganacheで用意されているアドレスをインポートしましょう。

適当なアドレスのshow keysをクリックします。

f:id:maroemon58:20180728111843p:plain

プライベートキーが表示されるのでクリップボードにコピーします。

f:id:maroemon58:20180728112002p:plain

MetaMaskの人マークをクリックすると、アカウントメニューが表示されるのでImport Accountをクリックします。

f:id:maroemon58:20180728120749p:plain

先程コピーしたプライベートキーを入力し、Importをクリックするとアカウントが追加されます。

f:id:maroemon58:20180728120933p:plain

動作確認

動作確認するためにlive-server(ローカルWebサーバ)を利用します。

導入されていない方はnpm installでインストールしましょう。

$ npm install -g live-server

エディタのプラグインで提供されている場合もあるので、そっちを使っても大丈夫です。

srcフォルダに移動して実行します。

$ cd src
$ live-server
Serving "/Users/hoge/Document/dapps/tutorial/src" at http://127.0.0.1:8080
Ready for changes

Chromeでhttp://127.0.0.1:8080にアクセスするとフロントアプリの画面が表示されます。

f:id:maroemon58:20180728112358p:plain

MetaMaskを立ち上げネットワークLocalhost 8545を選択します。

f:id:maroemon58:20180728112504p:plain

実際に動かしてみます。

f:id:maroemon58:20180728112525g:plain

無事に動きました!お疲れ様でした!!

おまけ

ちょっと見た目が野暮ったいので、機能は変えずにデザインをオシャレにアレンジしてみます。

MaterializeというCSSフレームワークを利用します。

materializecss.com

ファイルをダウンロードし、materialize.min.cssmaterialize.min.jsを配置します。

f:id:maroemon58:20180728112715p:plain

index.htmlをゴリゴリ修正します。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>DApps Dev Guild Tutorial</title>
  <script language="javascript" type="text/javascript" src="./js/web3.min.js"></script>
  <script language="javascript" type="text/javascript" src="./js/contract_abi.js"></script>
  <script language="javascript" type="text/javascript" src="./js/materialize.min.js"></script>

  <!--Import Google Icon Font-->
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <!--Import materialize.css-->
  <link type="text/css" rel="stylesheet" href="./css/materialize.min.css" media="screen,projection" />

  <script>
    var contract;
    var userAccount;

    function startApp() {
      var contractAddress = "0xdc30da2490373416e7bf7467bb297bae624287b4";

      contract = new web3js.eth.Contract(contractABI, contractAddress);

      var accountInterval = setInterval(function () {
        web3.eth.getAccounts((error, accounts) => {
          if (accounts[0] !== userAccount) {
            userAccount = accounts[0];
          }
        });
      }, 100);
    }

    window.addEventListener('load', function () {
      // Checking if Web3 has been injected by the browser(Mist/MetaMask)
      if (typeof web3 !== 'undefined') {
        // Use Mist/MetaMask's provider
        web3js = new Web3(web3.currentProvider);
      } else {
        // Handle the case where the user doesn't have Metamask installed
        // Probably show them a message prompting them to install Metamask
      }
      // Now you can start your app & access web3 freely;
      startApp();
    });

    function set() {
      var _val = document.getElementById("val").value;
      contract.methods.set(_val)
        .send({ from: userAccount })
        .on("transactionHash", function (txhash) {
          alert("Txhash: " + txhash);
        })
        .on("receipt", function (receipt) {
          console.log(receipt);
        })
        .on("error", function (error) {
          console.log(error);
        });
    }

    function get() {
      contract.methods.get().call().then(function (_val) {
        alert(_val);
      });
    }
  </script>
</head>

<body>
  <nav>
    <div class="nav-wrapper blue-grey ">
      <a href="" class="brand-logo">DApps Dev Guild Tutorial</a>
    </div>
  </nav>

  <div class="container">
    <input type="text" name="val" id="val">
    <div class="center">
      <a class="waves-effect waves-light btn" onclick="set()">
        <i class="material-icons left">file_upload</i>Set</a>
      <a class="waves-effect waves-light btn" onclick="get()">
        <i class="material-icons right">file_download</i>Get</a>
    </div>
  </div>
</body>

</html>

f:id:maroemon58:20180728112818g:plain

まとめ

デプロイするまでDApps開発に敷居を高く感じていましたが、手順を教えてもらい一通りできるようになったので取っ掛かりは掴めたかなって感じです。 所々理解できていない部分もありますが、今は教えられたことを全部飲み込むフェーズにし、アプリの開発をドシドシやろうと思います。ではまた。

twitter.com