hiko1129’s blog

開発に関することを記録するためのブログ

雑なDart入門

雑なDart入門

はじめに

これは軽く個人的にまとめたもので厳密な内容にはなっていません。

詳しい内容はドキュメントを参照してください。

基本

main関数から実行される。main関数は必須。現状文末セミコロンは必須。

mainはGoやJavaなどと同じ感じ。

main() {
  var hoge = 'hoge';
  print(hoge);
}

変数

var, dynamicなどあるが基本的には後述するconstとfinalを多用することになる と思う。JSでconstを基本的に使うのと同様。

varは型が推論されるためnameにはString型の'hoge'が入る。

var name = 'hoge';

dynamicはdynamic型(動的な型)になる。

dynamic name = 'hoge';

下記のように型を明示的に書くことも可能。

String name = 'hoge';

デフォルト値

デフォルト値(ゼロ値)はすべてnullになる。

String hoge;
assert(hoge == null);

Finalとconst

constもfinalも再代入不可だがfinalはJSのconstに似ている挙動(finalは参照先が変わる可能性がある)

組み込み型

  • numbers (int, double)
  • strings
  • booleans
  • lists
  • sets
  • maps
  • runes
  • symbols

関数

Javaっぽく前置型で型を書いてJavascriptっぽく書けばDartになるってイメージ。よく聞くJavaっぽいJavaScriptまま。

String hoge() {
  return 'hoge';
}

オプションパラメータ

String echo({ String word }) {
  print(word);
  return word;
}

echo(word: 'hello');

名前付きオプションパラメータの定義時には{}をつけて記述する。

[]で囲んだ引数はオプションになる。

String echo([String word]) {
  print(word);
  return word;
}

echo('hello');
echo();

関数は第一級オブジェクト

JSと同じように関数を関数に渡せるよってだけ。

無名関数

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

() {}で無名関数になる。

まとめ

他にもいろいろあるけどだいたいちょっと変わったJavaっぽいJavaScriptという認識で軽くドキュメント見れば基本的には書けるイメージ。

個人的に気になったいくつかの書き方(今回と重複あり)はあとでまとめる。

参考文献

A Tour of the Dart Language

Next軽く使ってみたので振り返り

とりあえず Docs に軽く目を通して、 Learn Next.js で雰囲気を掴んで、examples を参考にする形でプロフィールサイトなるものを作っていった。

完成してはいないが現在の構造としては下記のような感じになっている。

.
├── components
│   ├── Home.js
│   ├── RedirectChecker.js
│   ├── common
│   │   ├── AppBase.js
│   │   └── Navbar.js
│   ├── form_helpers
│   │   └── renderField.js
│   └── forms
│       └── RedirectChecker.js
├── constants
│   └── index.js
├── containers
│   ├── RedirectChecker.js
│   └── common
│       └── Navbar.js
├── ducks
│   ├── index.js
│   ├── navBar.js
│   └── redirectChecker.js
├── env-config.js
├── next.config.js
├── package-lock.json
├── package.json
├── pages
│   ├── _app.js
│   ├── index.js
│   └── redirect_checker.js
├── store.js
├── styles
│   └── style.sass
└── validators
    ├── required.js
    └── url.js

package.jsonは現状下記のような感じ

{
  "name": "prof",
  "version": "2.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "export": "next export",
    "deploy": "npm run build && npm run export"
  },
  "dependencies": {
    "@zeit/next-sass": "^1.0.1",
    "axios": "^0.18.0",
    "bulma": "^0.7.2",
    "next": "7.0.2",
    "next-redux-wrapper": "latest",
    "node-sass": "^4.11.0",
    "react": "16.7.0",
    "react-dom": "16.7.0",
    "react-redux": "5.0.7",
    "redux": "4.0.0",
    "redux-devtools-extension": "2.13.2",
    "redux-form": "^8.1.0",
    "redux-thunk": "^2.2.0",
    "validator": "^10.10.0"
  },
  "license": "ISC",
  "devDependencies": {
    "@fortawesome/fontawesome-free": "^5.6.3",
    "babel-eslint": "^10.0.1",
    "babel-plugin-transform-define": "^1.3.1",
    "eslint": "^5.12.0",
    "eslint-config-prettier": "^3.4.0",
    "eslint-config-standard": "^12.0.0",
    "eslint-config-standard-react": "^7.0.2",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-node": "^8.0.1",
    "eslint-plugin-prettier": "^3.0.1",
    "eslint-plugin-promise": "^4.0.1",
    "eslint-plugin-react": "^7.12.3",
    "eslint-plugin-standard": "^4.0.0",
    "prettier": "^1.15.3"
  }
}

ducksflux-standard-actionwith-redux-wrapperwith-next-sasswith-universal-configuration-build-timeを参考にして実装を進めていった。

Deploy Next.js project to Netlifyを参考にしてnetlifyにホスティングした。

今後は with-typescript を参考にしてTypeScriptに移行したり、 Atomic Designに従ったディレクトリ構成の検証等もしていく予定。

とりあえずNextはルーティング周りで辛みがないので幸せだった。

よくあるリダイレクトチェックツールを作ってみた

github.com
axiosでリダイレクト一覧を取得できる機能のプルリクが出ているのでそれを利用させてもらって、serverlessで下記のような雑なコードをバックエンドとして書いた。

'use strict'

const axios = require('axios')
const ACCESS_CONTROL_ALLOW_ORIGIN = process.env.ACCESS_CONTROL_ALLOW_ORIGIN

module.exports.trace = async (event, context) => {
  const { headers, body } = event
  const parsedBody = JSON.parse(body)

  try {
    const { redirects } = await axios.get(parsedBody.url, {
      trackRedirects: true
    })

    return {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin": ACCESS_CONTROL_ALLOW_ORIGIN
      },
      body: JSON.stringify({
        redirects
      })
    }
  } catch (e) {
    if (!e.response) return {
      statusCode: 400,
      headers: {
        "Access-Control-Allow-Origin": ACCESS_CONTROL_ALLOW_ORIGIN
      },
      body: JSON.stringify({
        error: 'invalid url'
      })
    }

    return {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin": ACCESS_CONTROL_ALLOW_ORIGIN
      },
      body: JSON.stringify({
        redirects: e.response.redirects
      })
    }
  }
}

serverless.ymlは下記のような感じ

service: redirect-checker-backend

provider:
  name: aws
  runtime: nodejs8.10
  region: ap-northeast-1
  stage: ${opt:stage, self:custom.defaultStage}
  environment:
    ACCESS_CONTROL_ALLOW_ORIGIN: ${self:custom.accessControlAllowOrigin.${self:provider.stage}}

custom:
  defaultStage: dev
  accessControlAllowOrigin:
    dev: http://localhost:5000
    prod: https://example.com

plugins:
  - serverless-offline

functions:
  trace:
    handler: handler.trace
    memorySize: 128
    events:
      - http:
          path: trace
          method: post
          cors: true

フロントが別ドメインのため、cors: trueをserverless.ymlに設定して、ヘッダーにACCESS_CONTROL_ALLOW_ORIGINを付与している。

zlibエラーへの対応(macOS Mojave)

macOS Mojaveでpyenvを用いてpython 3.7.2をインストールしようとした際に下記エラーに遭遇したので共有

zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

上記エラーは下記コマンドの実行で解決する

xcode-select --install
sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /

brewでzlibを入れようとしても解決しないので注意

参考

[MacOS Mojave]pyenvでpythonのインストールがzlibエラーで失敗した時の対応
macOS mojaveでzlibが見つからない場合の対処法

最低限の努力でターミナル周りを少し良くする(Macでbash, terminalを使っている人向け)

既にfish, zsh, iTerm2等使用している場合は読む価値ないです。

  • iTerm2を入れる
  • brew install fishfishを入れる

とりあえず下のiTerm2のショートカットだけ覚えて使えるようにする 。

ショートカット 機能
cmd + d 縦分割
cmd + shift + d 横分割
cmd + [ 次のペインに移動
cmd + ] 前のペインに移動

各種問題を回避しつつfishを使うために、下記を実行して、~/.bash_profileの末尾にexec fishを追加する。これでfishの強力な補完が特に何もせずに手に入る。

echo 'exec fish' >> ~/.bash_profile

参考

ログインシェルは bash のまま fish を利用する

デフォルトシェルをfishに変えるな

iTerm2で外せないショートカットキー一覧

Serverless + Ruby で作る LINE Bot

まずLINE Developers にて Messaging API でチャネルを作成する。

LINE周りは、1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefestなどを参考にすると良いと思う。Qiitaにいろいろ記事があるので誰でも作れるはず。

流れとしては下記のような形になる。
プロバイダー作成→Messaging APIでチャネル作成→アクセストークン発行、「Webhook送信」を「利用する」に設定、「LINE@機能」全て「利用しない」に設定。

* 2019年1月4日時点では「Webhook送信」を「利用する」に設定しても反映されないバグがある模様。リロードすると、「利用しない」に戻っていることがある。接続確認は動いてしまうようなので注意。

WebhookURLは後ほど設定する。

hiko1129.hatenablog.com

上記リンク先の作業に続けて行う。

bundle init

上記コマンド実行後、生成されたGemfileに下記を追加する。

gem 'line-bot-api', '~> 1.3'

その後下記コマンドを実行する。

bundle install --path vendor/bundle

serverless.ymlは下記のような形に変更する。secret.ymlファイルからLINEのシークレットやトークンを読み込む形にしてある。

service: aws-ruby-example

provider:
  name: aws
  runtime: ruby2.5
  region: ap-northeast-1
  stage: ${opt:stage, self:custom.defaultStage}
  environment:
    LINE_CHANNEL_SECRET: ${self:custom.lineChannelSecret.${self:provider.stage}}
    LINE_CHANNEL_ACCESS_TOKEN: ${self:custom.lineChannelAccessToken.${self:provider.stage}}

custom:
  defaultStage: dev
  lineChannelSecret:
    dev: ${file(./secret.yml):lineChannelSecret.dev}
    prod: ${file(./secret.yml):lineChannelSecret.prod}
  lineChannelAccessToken:
    dev: ${file(./secret.yml):lineChannelAccessToken.dev}
    prod: ${file(./secret.yml):lineChannelAccessToken.prod}

functions:
  handle:
    handler: handler.handle
    memorySize: 128
    events:
      - http:
          path: handle
          method: post

handler.rbは下記のような形に変更する。Follow時、Join時に特定のメッセージを返し、メッセージが送られてきた場合にオウム返しする実装。

require 'json'
require 'line/bot'

def client
  @client ||= Line::Bot::Client.new do |config|
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_ACCESS_TOKEN"]
  end
end

def reply_text(event, text)
  client.reply_message(
    event['replyToken'],
    { type: 'text', text: text }
  )
end

def handle_message(event)
  case event.type
  when Line::Bot::Event::MessageType::Text
    reply_text(event, event.message['text'])
  else 
    reply_text(event, "I don't know.")
  end
end

def handle(event:, context:)
  body = event['body']
  signature = event['headers']['X-Line-Signature']
  return { statusCode: 400 } unless client.validate_signature(body, signature)

  events = client.parse_events_from(body)
  events.each do |_event|
    case _event
    when Line::Bot::Event::Message
      handle_message(_event)
    when Line::Bot::Event::Follow
      reply_text(_event, "Thank you for following")
    when Line::Bot::Event::Join
      reply_text(_event, "Thank you for joining")
    end
  end

  { statusCode: 200 }
end

色々したい場合には、kitchensinkを参考にすると良いと思う。 Messaging APIリファレンスにコードも載っているのでリファレンスも参考にすると良いと思う。

secret.ymlファイルを生成して、下記のsecretとtokenをLINE developersから取得したものに変更する。

lineChannelSecret:
  dev: dev_secret
  prod: prod_secret
lineChannelAccessToken:
  dev: dev_token
  prod: prod_token

下記コマンドを実行してデプロイする。

sls deploy

WebhookURLにAPI Gatewayから取得したURLを設定して完成。

完成後のものをgithubに載せた。 github.com

掃除は下記コマンド実行で行える

sls remove

lambdaがruby対応したので今更ながらlambdaでrubyを使ってみる。

serverlessを使う。

sls create --template --help

上記コマンドでaws-rubyのtemplateがあるかを確認する。

sls create --template --help
sls create --template --help

なければ下記コマンド実行で入るはず

npm i -g serverless

下記コマンドでプロジェクトを作成

sls create --template aws-ruby --path aws-ruby-example

serverless.ymlを下記のように変更

service: aws-ruby-example

provider:
  name: aws
  runtime: ruby2.5
  region: ap-northeast-1
  stage: ${opt:stage, self:custom.defaultStage}

custom:
  defaultStage: dev

functions:
  hello:
    handler: handler.hello
    memorySize: 128
    events:
      - http:
          path: hello
          method: get

下記コマンドを実行してデプロイ

sls deploy

awsAPI Gatewayにアクセスして、

API Gateway
API Gateway

URLの呼び出し/helloにアクセスして、下記が返ってくればとりあえずOK

"Go Serverless v1.0! Your function executed successfully!"