Author Archives: yoheimiyamoto

ルーティング追加

単純なルーティング

src/app/app-routing.module.ts

ファイル作成

ng generate module app-routing --flat --module=app

heroesにアクセスした際に、HeroesComponentを表示

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent }      from './heroes/heroes.component';

const routes: Routes = [
  { path: 'heroes', component: HeroesComponent }
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

src/app/app.component.html

<h1>{{title}}</h1>
<router-outlet></router-outlet> // ルーティングにより表示の出し分けがされる場所
<app-messages></app-messages>

詳細ページへのルーティング

src/app/app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent }      from './heroes/heroes.component';
import { DashboardComponent }   from './dashboard/dashboard.component';
import { HeroDetailComponent }  from './hero-detail/hero-detail.component';

const routes: Routes = [
  { path: 'dashboard', component: DashboardComponent },
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'heroes', component: HeroesComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

src/app/heroes/heroes.component.html

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <a routerLink="/detail/{{hero.id}}">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </a>
  </li>
</ul>

src/app/hero-detail/hero-detail.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../hero';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';

import { HeroService }  from '../hero.service';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
  @Input() hero: Hero;

  constructor(
    private route: ActivatedRoute,
    private heroService: HeroService,
    private location: Location,
  ) { }

  ngOnInit(): void {
    this.getHero();
  }

  getHero(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.heroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }

  goBack(): void {
    this.location.back();
  }

}

src/app/hero-detail/hero-detail.component.html

<ng-container *ngIf="hero">
  <h2>{{ hero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name">
    </label>
    <button (click)="goBack()">go back</button>
  </div>
</ng-container>

DIのプロバイダ登録時にclass名を変更する

src/app/hello.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';

@Injectable()
export class HelloService {
  str: string;
  setSrt(str: string): string {
    return this.str = str;
  }
}

src/app/global-hello.service.ts

独自のトークンを作成

import { InjectionToken } from '@angular/core';
import { HelloService } from './hello.service';
export let GlobalHelloService = new InjectionToken<HelloService>('global.hello.service');

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';

import { HelloService } from './hello.service'
import { GlobalHelloService } from './global-hello.service'

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    MessagesComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
  ],
  providers: [
    { provide: GlobalHelloService, useClass:  HelloService}, // HelloServiceをGlobalHelloServiceとして登録
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

src/app/heroes/heroes.component.ts

import { Component, OnInit, Inject } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
import { HelloService } from '../hello.service';
import { GlobalHelloService } from '../global-hello.service';
import { APP_INFO } from '../app-info';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css'],
  providers: [{ provide: HelloService, useClass: HelloService }]
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
  selectedHero: Hero;
  heroes: Hero[];

  // GlobalHelloServiceトークンを注入
  constructor( private heroService: HeroService, @Inject(GlobalHelloService) public globalHelloService: HelloService) {
    this.heroService.getHeroes()
      .subscribe(heroes => this.heroes = heroes);
    globalHelloService.setSrt("wow!");
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
    console.log(this.selectedHero.name);
  }
}

src/app/heroes/heroes.component.html

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

{{ helloService.str }}
{{ globalHelloService.str }} // globalHelloServiceを読み込めている

map keyが存在しない場合

存在しないキーの場合は、0が出力される。
int型にnilが戻ってくることはない。

package main

import (
    "fmt"
)

func main() {
    vs := make(map[string]int)
    vs["a"] = 1
    if vs["c"] == 0{
        fmt.Println("zero")
    }
    fmt.Println(vs["c"])
}

golang dropbox

curl

curl -X POST https://content.dropboxapi.com/2/files/alpha/upload \
    --header "Authorization: Bearer {token}" \
    --header "Dropbox-API-Arg: {\"path\": \"/Homework/math/あいう.txt\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}" \
    --header "Content-Type: application/octet-stream" \
    --data-binary @hello.txt // ローカルのファイル名

golang

client.go

package dropbox

import (
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "net/url"
)

// APIEndpoint constants
const (
    APIEndpointBase   = "https://content.dropboxapi.com"
    APIEndpointUpload = "/2/files/alpha/upload"
)

// Client ...
type Client struct {
    token        string
    endpointBase *url.URL     // default APIEndpointBase
    httpClient   *http.Client // default http.DefaultClient
}

// NewClient ...
func NewClient(token string, httpClient *http.Client) (*Client, error) {
    u, err := url.ParseRequestURI(APIEndpointBase)
    if err != nil {
        return nil, err
    }
    c := Client{
        token:        token,
        endpointBase: u,
    }
    if httpClient != nil {
        c.httpClient = httpClient
    } else {
        c.httpClient = http.DefaultClient
    }
    return &c, nil
}

func (c *Client) url(endpoint string, query url.Values) string {
    u := c.endpointBase
    u.Path = endpoint
    u.RawQuery = query.Encode()
    return u.String()
}

func (c *Client) do(req *http.Request) (*http.Response, error) {
    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token))
    req.Header.Set("Content-Type", "application/octet-stream")
    return c.httpClient.Do(req)
}

// UploadFile ...
func (c *Client) UploadFile(body io.Reader, ci *CommitInfo) error {
    arg, err := json.Marshal(ci)
    if err != nil {
        return err
    }
    query := url.Values{}
    query.Set("arg", string(arg))
    req, err := http.NewRequest("POST", c.url(APIEndpointUpload, query), body)
    if err != nil {
        return err
    }
    req.Header.Set("Content-Type", "application/json; charset=UTF-8")
    res, err := c.do(req)
    if err != nil {
        return err
    }
    resBody, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return err
    }
    if res.StatusCode != 200 {
        return fmt.Errorf(string(resBody))
    }
    return nil
}

client_test.go

package dropbox

import (
    "log"
    "os"
    "testing"

    "github.com/joho/godotenv"
)

func init() {
    err := godotenv.Load("dropbox.env")
    if err != nil {
        log.Fatal("Error loading .env file")
    }
}

func TestNewAppInfo(t *testing.T) {
    c, err := NewClient(os.Getenv("DROPBOX_TOKEN"), nil)
    if err != nil {
        t.Error(err)
    }
    ci := NewCommitInfo("/あああ.txt", NewWriteMode(ModeOverwrite))
    body, err := os.Open(`hello.txt`) // ファイルを取得して、io.Readerとしてそのままpost可能
    if err != nil {
        t.Error(err)
    }
    defer body.Close()

    err = c.UploadFile(body, ci)
    if err != nil {
        t.Error(err)
    }
}

model.go

package dropbox

import (
    "time"
)

// Mode ...
const (
    ModeAdd       = "add"
    ModeUpdate    = "update"
    ModeOverwrite = "overwrite"
)

// CommitInfo ...
type CommitInfo struct {
    // Path : Path in the user's Dropbox to save the file.
    Path string `json:"path"`
    // Mode : Selects what to do if the file already exists.
    Mode *WriteMode `json:"mode"`
    // Autorename : If there's a conflict, as determined by `mode`, have the
    // Dropbox server try to autorename the file to avoid conflict.
    Autorename bool `json:"autorename"`
    // ClientModified : The value to store as the `client_modified` timestamp.
    // Dropbox automatically records the time at which the file was written to
    // the Dropbox servers. It can also record an additional timestamp, provided
    // by Dropbox desktop clients, mobile clients, and API apps of when the file
    // was actually created or modified.
    ClientModified time.Time `json:"client_modified,omitempty"`
    // Mute : Normally, users are made aware of any file modifications in their
    // Dropbox account via notifications in the client software. If true, this
    // tells the clients that this modification shouldn't result in a user
    // notification.
    Mute bool `json:"mute"`
    // PropertyGroups : List of custom properties to add to file.
    // PropertyGroups []*file_properties.PropertyGroup `json:"property_groups,omitempty"`
}

// NewCommitInfo ...
func NewCommitInfo(path string, mode *WriteMode) *CommitInfo {
    s := new(CommitInfo)
    s.Path = path
    s.Mode = mode
    s.Autorename = false
    s.Mute = false
    return s
}

// WriteMode ...
type WriteMode struct {
    Tagged
    // Update : Overwrite if the given "rev" matches the existing file's "rev".
    // The autorename strategy is to append the string "conflicted copy" to the
    // file name. For example, "document.txt" might become "document (conflicted
    // copy).txt" or "document (Panda's conflicted copy).txt".
    Update string `json:"update,omitempty"`
}

// Tagged ...
type Tagged struct {
    Tag string `json:".tag"`
}

// NewWriteMode ...
func NewWriteMode(mode string) *WriteMode {
    return &WriteMode{
        Tagged: Tagged{mode},
        Update: "",
    }
}

参考

https://www.dropbox.com/developers/documentation/http/documentation

【 ヒカ☆ラボ 】仮想通貨取引所を可能にする技術 by ビットバンク株式会社

講師プロフィール

■松本 洸 氏
Webアプリケーション開発の全般を守備範囲とするNode.jsエンジニア。
仮想通貨の将来的な価値と技術的な面白さに惹かれビットバンクに入社。
とりわけWebフロントエンド技術が好きで特にAnguler周辺技術がお気に入り。

■脇本 佑磨 氏
組み込み、スマホアプリ、Web、インフラと幅広い開発経験を持つ。
テレビに写りたいがためにビットコインミートアップに参加したのが
暗号通貨に興味を持ったきっかけ。

■ジョナサン アンダーウッド 氏
ブロックチェーン大学校株式会社 学長 兼 代表取締役を務めている
アメリカ出身のビットコイン研究者。
多数のオープンソースのビットコインウォレットプロジェクトにも参加しており、
いくつかのビットコインのスタンダードを決めるBIP(Bitcoin ImprovementProposals)の作成にも参加。
国内初のブロックチェーン専門書「ブロックチェーンの衝撃(日経BP)」2-3「ビットコインの最新技術」著者。

フロントエンジニア 松本 洸 氏「bitbankフロントエンド開発について」

  • 資料
    https://www.slideshare.net/koumatsumoto/bitbank

  • type

    • 型定義がしっかりしている
    • nullのエラーがでない
    • コンパイルエラーでエラーチェックの作業を効率化
  • Angular
    • UI実装とロジック実装の分業化
      html, css, ロジックを分割できる
    • 拡大するアプリケーション開発に耐えられる
      サードパーティー製のライブラリに依存することが少ない
  • 開発について
    • 開発体制7名
  • チームビルディング
    • 定期的な勉強会の開催
    • 誰かに聞ける文化を持つ

脇本 佑磨 氏「仮想通貨のアービトラージでお寿司を食べた話」

ジョナサン・アンダーウッド 氏「IoTとFintechを繋ぐLightning Networkの実践」

  • 資料
    https://docs.google.com/presentation/d/1Uu-ecEs9CZw8jOL7nO1Qnsy4dL3YsWnkz5J23UcPJuI/edit#slide=id.p

  • 開発言語
    noje.js が多い

  • レイテンシー
    ETH一番早い(15秒)
    BTC遅い(600秒)

  • ペイメントチャンネル

    • ブロックチェーンに送信しないで取引を行う
    • レイテンシーの問題も解決できる
    • 少額取引が完了した段階で、ブロックに登録を行う(クローズ)
    • 0.5秒で支払はできた
    • チャンネルの開閉の頻度を減らさないといけない。
    • 送金元と送金先で毎回チャンネルを開かなくてよい。
    • 既存のチャンネルを流用
    • ライトニングネットワークや、イーサリアムのライデンネットワークとして作られてる
    • デモ
      • btcd 独自のローカルのブロックチェーン
      • Alice, Bob, Carlはそれぞれチャンネルを開いている
      • チャンネルごとにバランスがある
      • lightning-dev
  • ライトニングネットワークとブロックチェーンの使い分け
    • 日々の決済はライトニングネットワーク
    • 不祥事などが起きた時に、最終的にブロックチェーン上で判別する
    • ウォレット内の仮想通貨をフィアットにする時に別にチャンネルをクローズする必要もない。
      ウォレットと取引所間のライトニングネットワークで
  • 問題
    ライトニングネットワークに必要な秘密キーを紛失した時

参考

https://atnd.org/events/93909