gorillaでAPIサーバを書く 1

gorillaを使ってAPIサーバを書く。

ディレクトリ構成としては、以下を想定。

.api
 │
 ├── handlers
 │   ├── core.go
 │   └── router.go
 └── api.go

それぞれのファイルは大まかに以下のような役割になります。

  • api.goはサーバの起動設定などを記載
  • router.goはファイル名の通りrouterなので、パスに合わせたハンドラの設定
  • core.goはハンドラーの実装

まずは必要なパッケージのインストール

go get github.com/gorilla/mux

api.goはgorilla特有の処理などは書かないので割愛。

router.go では、mux.Routerを使ったRouter関数を定義します。 エンドポイントを指定して、エンドポイントごとにHTTPメソッドに対応するHandler関数を設定します。

package handlers

import (
    "net/http"
    "github.com/gorilla/mux"
)

const (
    RegexMatchUserID = "[A-Za-z0-9]{3}"
)

func Router() *mux.Router {
    r := mux.NewRouter()

    s := r.PathPrefix("/api/").
        Subrouter().StrictSlash(false)

    usersPath := "/users/"
    subpath := usersPath + "{userId:" + RegexMatchUserID + "}"

    s.HandleFunc(usersPath, GetUsers).Methods("GET")

    s.HandleFunc(subpath, GetUserById).Methods("GET")

    s.HandleFunc(subpath, DeleteUserById).Methods("DELETE")

    s.HandleFunc(subpath, CreateUser).Methods("POST").
        Headers("Content-Type", "application/json")
    return r
}

PathPrefixでベースとなるURLを元にエンドポイントを組み立て行く方法の他に、下記のようにPathとPathPrefixを組み合わて同じように書くことが出来ます。

func Router() *mux.Router {
    r := mux.NewRouter()

    userPath := urlbase + "/users/"
    users := r.Path(userPath).Subrouter()

    users.Methods("GET").HandlerFunc(GetUsers)

    subpath := userPath + "{userId:" + RegexMatchUserID + "}"
    usr := r.PathPrefix(subpath).Subrouter()
    usr.Methods("GET").HandlerFunc(GetUserById)
    usr.Methods("DELETE").HandlerFunc(DeleteUserById)

    usr.Methods("POST").HandlerFunc(CreateUser)

    return r
}

Handlerメソッドについては、次回記載します。

SICPを読む会 1 問題1.3

読む会SICPを黙々と読む。

SICP Support for DrRacketのインストールを参考にDrRacketをインストールして実行環境を作成します。
DrRacketで言語宣言ペイン(上側のペイン)で、#lang planet neil/sicp と書いて実行しても調べたページとは違う挙動しましたが Schemeが実行出来るようなで練習問題を実施。

今日は問題1.3を実施。
今回は考え方としては3値のうち一番小さい値は何かという観点で手続きを定義します。
下記の一番最初のifの評価としては、『xはyよりも大きい、またはxはzよりも大きいのどちらもFalseになる = xは最も小さい』という周りくどい評価を実施しています。

> (define (square x) (* x x))
> (define (sum-of-squares x y)
    (+ (square x) (square y)))
> (define (f x y z)
    (if (or (>= x y) (>= x z)) 
        (if (or (>= y z) (>= y x)) 
            (sum-of-squares x y)
            (sum-of-squares z x)) 
        (sum-of-squares y z)))
> (f 3 4 5)
41
> (f 4 3 5)
41
> (f 4 5 3)
41

また、schemeではminという手続きが定義されており最小の値を求めることが出来るので、以下のように書き換えることが出来ます。

> (define (f x y z)
    (if (= (min x y z) x) (sum-of-squares y z)
        (if (= (min x y z) y) (sum-of-squares x z)
            (sum-of-squares x y))
        )
    )
> (f 3 4 5)
41
> (f 4 3 5)
41
> (f 5 4 3)
41

このような場合はifではなくcondを使ったほうが可読性が上がるので、condを使って書き換えます。

> (define (f x y z)
    (cond ((= (min x y z) x) (sum-of-squares y z))
          ((= (min x y z) y) (sum-of-squares x z))
          (else (sum-of-squares x y))))
> (f 3 4 5)
41
> (f 4 5 3)
41
> (f 5 3 4)
41

goのお勉強1

スコープ

変数や関数名で1文字目が小文字だとパッケージ内からのみ見える。大文字だとパッケージ外からのみ見える。

ポインタ

ポインタは、C言語と同様に利用できる。

package main

import "fmt"

func main() {
  a := 5
  var pa *int = &a // *intはint型のpointer
  fmt.Println(pa) // アドレスが表示される
  fmt.Println(*pa) // 値(5)が表示される

  *pa = 10
  fmt.Println(a) // 10が表示される
}

ただし、C言語と違いポインタは演算は出来ない。 c言語だと配列とかはポインタで表現され、以下のようにできるがgoでは出来ないことを確認する。 C言語では以下のように記述できる。

#include <stdio.h>

int main() {
    int  array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int* p = array;

    /* 配列の全要素の値を表示する */
    for (int i = 0; i < 10; ++i) {
        printf("*p = %d\n", *p);
        p = p + 1;  /* ポインタの値を 1 増やす */
    }

    return 0;
}

goで同じような処理をしようとしてみるとinvalid operationとコンパイルエラーになる。
スライスにしても同様にコンパイルエラーになる。

package main

import "fmt"

func main() {
  arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  var p *[10]int = &arr
  for i := 0; i < 10; i++ {
    fmt.Println(*p) // [1 2 3 4 5 6 7 8 9 10]が出力される
    // fmt.Println(p[i]) とすると配列のi番目の値を出力できる
    p = p + 1    // invalid operation: p + 1 (mismatched types *[10]int and int) となりコンパイルエラーになる。
  }
}

Omnibus InstallerでインストールしたChefのRubyでserverspecを実行する

chefのバージョンを確認する。

# chef-client -v
Chef: 12.0.3

Rubyのバージョンを確認する。

/opt/chef/embedded/bin/ruby -v
ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-linux]

rubyなどはchef/embeddedに入っているので、serverspecのインストールを行う。

/opt/chef/embedded/bin/gem install serverspec

serverspec-initを実行する

# /opt/chef/embedded/bin/serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 2

 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

serverspecを実行する

# cd spec/localhost
# /opt/chef/embedded/bin/rake

パーフェクトRuby写経 特異メソッドの作り方

特異メソッドとは、クラスに定義されたメソッドの他に、そのオブジェクト固有のメソッド持つことができ、それを特異メソッドといいます。

特異メソッドを定義するには、メソッドを定義したいオブジェクトをメソッド名の前に指定してメソッドを記述します。 また、特異メソッドの中でsuperを呼び出すと、クラスに定義されている同名のメソッドが呼び出されます。

class Foo
    def override_me
        p 'in Foo class'
    end

end

foo = Foo.new
bar = Foo.new

def bar.singleton_method
    p 'Called!'
end

def bar.override_me
    p 'in Foo class'
end

bar.singleton_method
bar.override_me

モジュールに定義されたメソッドを、オブジェクトの特異メソッドとして取り込む。 そのさいには、extendを用います。

module Sweet
    def self.lot
        %w(brownie apple-pie bavarois pudding).sample
    end

    def greet_to(name)
        p "Hello, #{name}. My name is #{self.class}"
    end
end

o = Object.new
o.extend Sweet

o.greet_to 'world'
#o.lot

p Sweet.lot

また、self.lotのようにモジュールのメソッド名の前にselfをつけて定義すると、モジュールの特異メソッドとなります。

また、モジュール関数は「privateなインスタンスメソッドであると同時に、モジュールの特異メソッドでもある」メソッドとの事なので、これも特異メソッドの一種のようです。

module MyFunctions
    def my_module_func
        p 'Called!'
    end
    module_function :my_module_func
end

一つのメソッドの場合は上記のように記述し、複数のメソッドを定義さする場合には下記のように記述するようです。

module MyFunctions
    module_function 

    def my_first_func
        p 'first!'
    end

    def my_second_func
        p 'second!'
    end

end