2024/04/17

Vuetify date picker 的雷

 

我這 blog 已經 8 年多沒更新了,今天花了快 1 小時解這問題,終於被我試出一個莫名奇妙的 workaround,一定要來發一篇文章,或許可以幫到某個跟我遇到一樣蝦的問題的人。

在使用 Vuetify 的 v-date-picker 時,遇到一個非常奇粑的問題,當我們點擊 v-text-field 時,預期要出現 date picker,可以選擇日期,但是偏偏在點選旁邊一點的位置時,會發現 v-text-field 有反應,label 的動畫會跑,



以下是原本實作的 code:

<template>
<v-menu
v-model="showPicker"
:close-on-content-click="false"
>
<template v-slot:activator="{ props }">
<v-text-field
clearable
:label="label"
v-model="formattedDate"
v-bind="props"
:format="dateFormat"
variant="outlined"
hint="YYYY-MM-DD"
persistent-hint
prepend-inner-icon="mdi-calendar"
@click:clear="clearDate"
@input="onTextInput"
density="compact"
hide-details
></v-text-field>
</template>
<div>
<v-date-picker
v-model="date"
></v-date-picker>
</div>
</v-menu>
</template>

幾次嘗試之後,一直想要抓到是哪個 event 造成這樣的差異,後來發現主要是 focus 與 click 的行為的不同。



接著試著在 focus 的 event 中,來觸發 date picker 的顯示:

<v-text-field
clearable
:label="label"
v-model="formattedDate"
v-bind="props"
:format="dateFormat"
variant="outlined"
hint="YYYY-MM-DD"
persistent-hint
prepend-inner-icon="mdi-calendar"
@click:clear="clearDate"
@focus="showPicker = true"
@input="onTextInput"
density="compact"
hide-details
></v-text-field>

卻發現結果原先正常的 text input 區塊,點擊後反而閃退…



猜測原因是原先 date picker 有實作 toggle (切換),但剛好前一刻才透過 focus 設成 true,下一刻可能就被 toggle 關成 false。

先傻眼了 10 秒,最後突發奇想的,那就再設一個 click 的 event,會不會能"負負得正"?

<template v-slot:activator="{ props }">
<v-text-field
clearable
:label="label"
v-model="formattedDate"
v-bind="props"
:format="dateFormat"
variant="outlined"
hint="YYYY-MM-DD"
persistent-hint
prepend-inner-icon="mdi-calendar"
@click:clear="clearDate"
@focus="showPicker = true"
@input="onTextInput"
@click="showPicker = true"
density="compact"
hide-details
></v-text-field>
</template>

想不到居然可以!


雖然在 text input 區域之外的地方,點擊後經過 toggle 還是會變成無法再次開啟,而且原先 text input 的地方,變成無法 toggle,但至少在第一次點擊到這個日期編輯區時,不會因為 date picker 沒出現而覺得困擾了…

這做法蠻瞎的,但這問題我的前端夥伴卡了蠻久,我接手嘗試解決,也只能做到這樣,特別記錄一下!

2015/11/22

[Docker] Push Error

[Docker] Push Error

這篇同步發佈在我的BlogGist

背景

上一篇Docker Private Registory提到,在公司架了Docker Registory,基於安全考量,Port當然是沒有對外開放,而平常在家裡要連回公司,就是使用VPN連到公司來存取公司內部的Services。

由於目前草創初期,CI還沒架設起來,所以目前是使用local build & publish到Docker Repository。

問題

今天在家裡改完了一些Code,要把build上傳到公司的Docker Registry的時候,發現了下面的錯誤:

Joes-MBP:admin-backend-api joecwu$ docker push {host}:{port}/xxx/admin-backend-api:0.1.17
The push refers to a repository [{host}:{port}/xxx/admin-backend-api] (len: 1)
Sending image list
Put http://{host}:{port}/v1/repositories/xxx/admin-backend-api/: dial tcp: lookup {host} on 192.168.70.254:53: read udp 192.168.70.254:53: i/o timeout

明明已經連到公司的VPN了,IP也ping得到,192.168.70.254是gateway,也都能正常連到他,為什麼會有這樣的錯誤?

解法

這讓我想到了在公司有一台開發機,是使用無線網卡連公司的網路,有時無線網卡會不穩,網路會斷掉再重連,而這時這台開發機裡所架設的docker-machine會無法從外部連到,必需重新啟動docker-machine(也就是virtual box的VM),讓VM的網路組態重新更新之後才能正常存取。

因此簡單的把我的Mac上的docker-machine restart,之後這個問題就解決了,就能正常的上傳build嘍!

2015/11/07

[Docker] Private Registry

[Docker] Private Registry

這篇同步發佈在我的BlogGist

前言

最近開始run startup,非常忙碌,沒什麼時間寫blog,但是有些難找的東西,還是要記錄一下,看看能不能幫到別人。

之前主要專注在backend,現在自己要負責所有技術的東西,最近一直在看front-end的新技術,真的多到看不完耶,除了dev,ops的東西也要一併處理,所以就有這篇文章的出現…

不熟Docker的朋友們,請先閱讀這篇Docker – 從入門到實踐

Docker這麼紅、這麼方便,一定要用的啊!但除了用Docker Hub(公共repostiry)上了image,我自己寫開發的app,不可能放出去,所以會要架自己的Docker Registry來放,以方便內部佈置與自動化機制的建置。

背景

以下是我的環境
Docker Registry: Synology DS415+ with latest docker-registry version
Docker Client: Mac OSX 10.11.1 with Docker 1.8.3
Docker Publish Env: SBT Native Packager

Private Docker Registry

安裝Private Docker Registry很簡單,官方就有registry的image,直接抓下來用就行! 網路上也有很多文章教你怎麼用,這裡就不多說了。

但我遇到的問題就是,預設Docker Registry是”安全的”,也就是他會要求你走TLS,這樣對於沒有certificate的使用者,就無法用了。

我試著要從sbt-native-packager使用docker:publish的時候,發生了下面的錯誤,一度以為是我的Registry沒灌好…

[error] unable to ping registry endpoint https://192.168.1.2:6000/v0/
[error] v2 ping attempt failed with error: Get https://192.168.1.2:6000/v2/: EOF
[error]  v1 ping attempt failed with error: Get https://192.168.1.2:6000/v1/_ping: EOF
[trace] Stack trace suppressed: run last docker:publish for the full output.
[error] (docker:publish) Nonzero exit value: 1
[error] Total time: 9 s, completed Nov 6, 2015 7:59:57 PM

但是後來眼尖的發現,他連的是https

Insecure Registry

那我們就讓他”不安全”吧!

官方有寫這篇Insecure Registry,提到兩個方式:

  1. 直接用 DOCKER_OPTS="--insecure-registry myregistrydomain.com:5000" 告訴你的docker daemon不要去檢查registry的安全性,好處是很簡單的設定,壞處是非常不安全,而且你要設定每一個docker daemon (就是每一台要連registry的機器都要設)。
  2. 使用自己簽的certificate。這個東西有點複雜,下次再來研究。

ps: 本來嘗試要從sbt-native-package下手,但官方說法是這應該要從docker daemon下手,他們也無能為力。,

動手做

這邊我要做的是#1的solution,但是發現在Mac上好像跟官方說的方式不太一樣,後來查了這篇,才找到這個方法來解決。

修改Mac內的docker-machine

Joes-MBP:admin-backend-api joecwu$ docker-machine ssh default
                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.8.3, build master : af8b089 - Mon Oct 12 18:56:54 UTC 2015
Docker version 1.8.3, build f4bf5c7
docker@default:~$ sudo vi /var/lib/boot2docker/profile

/var/lib/boot2docker/profile 內,增加一行 --insecure-registry 192.168.1.2:6000

EXTRA_ARGS='
--label provider=virtualbox
--insecure-registry 192.168.1.2:6000
'
CACERT=/var/lib/boot2docker/ca.pem
DOCKER_HOST='-H tcp://0.0.0.0:2376'
DOCKER_STORAGE=aufs
DOCKER_TLS=auto
SERVERKEY=/var/lib/boot2docker/server-key.pem
SERVERCERT=/var/lib/boot2docker/server.pem

重新啟動docker-machine

Joes-MBP:admin-backend-api joecwu$ docker-machine stop default
Joes-MBP:admin-backend-api joecwu$ docker-machine start default
Starting VM...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.

Result

Run docker:publish

> docker:publish
[info] Wrote ........./admin-backend-api_2.11-0.1.0.pom
[info] Sending build context to Docker daemon 557.1 kB
[info] Sending build context to Docker daemon 1.114 MB
[info] Step 7 : CMD
[info]  ---> Using cache
[info]  ---> 48923d750c11
[info] Successfully built 48923d750c11
[info] Built image 192.168.1.2:6000/kiri/admin-backend-api:0.1.0
[info] The push refers to a repository [192.168.1.2:6000/kiri/admin-backend-api] (len: 1)
[info] Sending image list
[info] Pushing repository 192.168.1.2:6000/kiri/admin-backend-api (1 tags)
...
[info] 48923d750c11: Pushing
[info] 48923d750c11: Image successfully pushed
[info] Pushing tag for rev [48923d750c11] on {http://192.168.1.2:6000/v1/repositories/kiri/admin-backend-api/tags/0.1.0}
[info] Published image 192.168.1.2:6000/kiri/admin-backend-api:0.1.0
Joes-MacBook-Pro:~ joecwu$ curl -i http://192.168.1.2:6000/v1/search
HTTP/1.1 200 OK
Server: gunicorn/19.1.1
Date: Sat, 07 Nov 2015 08:51:54 GMT
Connection: keep-alive
Expires: -1
Content-Type: application/json
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 99

{"num_results": 1, "query": "", "results": [{"description": "", "name": "kiri/admin-backend-api"}]}

2015/09/06

[Scala In Depth] Scala Case Classes

[Scala In Depth] Scala Case Classes

[Scala In Depth] Scala Case Classes

這篇同步發佈在我的BlogGist

這篇文章,基本上是參考這篇Scala Case Classes In Depth來寫的,閱讀吸收後以我的例子與描述方式以中文寫下。

大家對Case Class基本的認識

  • 定義一個簡單的類型(class)並在宣告的時候直接定義好有哪些參數。
case class User(region: String, id: String, name: String)
  • 可以簡單的用來建立某個類型(class)的物件,因為case class已經實作了apply,所以可省略new這個宣告方式,來建立新物件。
val joe = User("TW","123","Joe")
joe: User = User(TW,123,Joe)
  • 每個參數的宣告都被加上了val的前綴(prefix),也就是這個class基本上每個property都是immutable(不能被修改的)。
val region = joe.region
// 下面這行在compile會錯誤
joe.region = "US"
<console>:23: error: reassignment to val
  • 當然有一些基本的implementation,像是hashCode, euqalstoString…等,而在scala中,==基本上就是執行equal,所以在case class用==去比較的時候,是用整個結構會去比的(而非比reference)。
scala> val joe2 = User("TW","123","Joe")
joe2: User = User(TW,123,Joe)

scala> joe == joe2
res: Boolean = true

scala> val joe3 = User("TW","124","Joe")
joe3: User = User(TW,124,Joe)

scala> joe == joe3
res54: Boolean = false
  • 要複製的時候,可以使用copy,若有要修改的屬性,再copy當中宣告即可
scala> joe.copy(region="US")
res: User = User(US,123,Joe)
  • 有實作unapply,所以在pattern matching可以直接使用。
scala> val User(region,id,name) = joe
region: String = TW
id: String = 123
name: String = Joe

scala> joe match {
     |   case User(_,id,_) => println(s"id:[$id]")
     | }
id:[123]
  • 如果在宣告case class的時候,並沒有要給任何的參數,就像是定義ADT(Algebraic Data Type)的時候,我們有可能會宣告到一個沒有任何參數的class,這時候可以用case object來宣告。
sealed trait Life[+T]
case class Alive[T](value: T) extends Life[T]
case object Dead extends Life[Nothing]

scala> def check(life:Life[User]) = life match {
     |   case Alive(user) => println(user.name)
     |   case Dead => println("dead")
     | }
check: (life: Life[User])Unit

scala> check(Alive(joe))
Joe

scala> check(Dead)
dead

一些case class進階的認識

  • 如果你需要建立一個function,而這個function要傳入所有產生這個class的參數,最後要產生這個class的object的話,你可以使用apply
// 這裡的 _ 是因為要把apply這個function指派給變數來用,如果不加上 _ 的話,會被當成是要執行這個function而又因為後面沒有帶所需要的參數造成compile錯誤。
scala> val userCreator = User.apply _
userCreator: (String, String, String) => User = <function3>

scala> val uerica = userCreator("TW","66","Uerica")
uerica: User = User(TW,66,Uerica)
  • curried,如果你要把上面這個用法,拆成是curry的話,可以使用curried的方式,把這些參數給拆開來,方便組合使用。
scala> val curriedUser = User.curried
curriedUser: String => (String => (String => User)) = <function1>
// 上面其實就變成了一種這樣 (region: String)(id: String)(name: String) : User 的function

scala> val twUserCreator = curriedUser("TW")
twUserCreator: String => (String => User) = <function1>

scala> val samael = twUserCreator("111")("Samael")
samael: User = User(TW,111,Samael)
  • tupled,前面提到的apply是建立一個function並傳入對應class properties數量的參數,而若想要傳入的是一個tuple,就可以使用tupled
scala> User.tupled
res67: ((String, String, String)) => User = <function1>
//上面這個與之前的apply不同,多了一組刮號,因為這傳入的是tuple

scala> val tupledJoe = ("TW","123","Joe")
tupledJoe: (String, String, String) = (TW,123,Joe)

scala> User.tupled(tupledJoe)
res: User = User(TW,123,Joe)
  • unapply,當我們需要一個function,是傳入某個類型的物件,然後希望回傳的是Option[TupleN[A1, A2, ..., AN]],將N個參數分別包成tuple,然後用Option包起來(這邊用Option有可能是因為要handle這物件可能不存在的情況),這種時候就能直接使用unapply
scala> val toOptionOfTuple = User.unapply _
toOptionOfTuple: User => Option[(String, String, String)] = <function1>

scala> toOptionOfTuple(joe)
res: Option[(String, String, String)] = Some((TW,123,Joe))

當使用curried的形式來建立一個case class時…

這篇文章提到了一點,就是我們可以用curried的形式來建立一個case class,就像是下面的例子。

scala> case class Person(fingerPrint: Long)(name: String, dress: String)
defined class Person

scala> val j = Person(123123l)("Joe","Suit")

至於什麼時候要這樣用?以及該怎麼用?用了會發生什麼事?,我們分別來說明。

什麼時候要這樣用?

如果真的有一種情況,就是在你的設計需求上,希望一個物件的比對是只依照某個部份的property,例如我的例子是只要指紋是一樣的,就是同一個人,不論他的名字、裝扮如何。

scala> val j = Person(123123l)("Joe","Suit")

scala> val q = Person(123123l)("Andy","Casual")

scala> j==q
res: Boolean = true

該怎麼用?用了會發生什麼事?

curried的部份,並沒有實作前面提到case class的那些特性,也就是說,下面這幾點都會有問題:

  • 沒有實作val的前綴,所以不能直接存取這些properties
scala> j.name
<console>:24: error: value name is not a member of Person
       j.name
         ^

所以我們必須自己加上val的宣告

scala> case class Person(fingerPrint: Long)(val name: String, val dress: String)
defined class Person

scala> val j = Person(123123l)("Joe","Suit")
j: Person = Person(123123)

scala> j.name
res: String = Joe
  • 當然用copy的時候,也就知道後面curried的部份也沒有實作copy,所以你必需帶所有的參數。
scala> j.copy()(name = "David")
<console>:25: error: not enough arguments for method copy: (name: String, dress: String)Person.
Unspecified value parameter dress.
       j.copy()(name = "David")
               ^

scala> j.copy()(name = "David", dress = "Dirty")
res80: Person = Person(123123)
  • 其他的像是tupled當然也都不能用嘍!

因為case class繼承Product這個trait而所得到的能力

  • def productArity: Int,這個function可以得到這個case class的object有多少個properties。
scala> joe.productArity
res: Int = 3
// 這3個就是region, id, name
  • def productElement(n: Int): Any,取得某個指定的Element。注意:這裡的回傳type是Any哦!
scala> joe.productElement(2)
res: Any = Joe
  • def productIterator: Iterator[Any],把每個properties iterate出來。
    這裡提醒一些不熟scala的朋友,iterator這裡若用map會發現怎麼沒有被執行到,要先toList或是用foreach才會執行,這是因為iterator的特性。
scala> joe.productIterator.foreach(println)
TW
123
Joe
  • def productPrefix: String,以字串的形式取得case class object的型態。
scala> joe.productPrefix
res: String = User

2015/08/27

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 2

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 2

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 2

這篇同步發佈在我的BlogGist

前情提要

請看[ScaVa->Scala] Scalaz Stream 串流好朋友 part 1Gist版本

怎麼使用Process?

使用Monad的功能map, flatMap

首先,Process是Monad,所以Monad使用上的特性也是Process好用的特色,因此map, flatMap…等這些Moand的特性都能使用。

case class Article(id:String,title:String,body:String)
val articles : Process[Task,Article] = Process(Article("1","Hi","Scalaz Process"))
//articles: scalaz.stream.Process[scalaz.concurrent.Task,Article] = Emit(WrappedArray(Article(1,Hi,Scalaz Process)))

scala> articles.map{println(_)}
Article(1,Hi,Scalaz Process)
//scalaz.stream.Process[scalaz.concurrent.Task,Unit] = Emit(Vector(()))

scala> articles.flatMap{ a=> Process(a.id) }.runLog.run
//IndexedSeq[String] = Vector(1)

Process.scan

在Stream的處理中,蠻常有機會是會要參考到前一個值,也就是causal function,也就是以之前的值與目前的值來處理的function,Process.scan就是Process中實作的causal function,他的使用方式就像是fold一樣,我們會定義一個function,傳入兩個參數,分別是之前運算完的結果,和當下的值。

val nums : Process[Task,Int] = Process.range(0,10)
// nums.runLog.run
// IndexedSeq[Int] = Vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

val sum = nums.scan(0)( (past,present) => past+present )
// sum.runLog.run
//IndexedSeq[Int] = Vector(0, 0, 1, 3, 6, 10, 15, 21, 28, 36, 45)

先代入起始值0scan當作第一個past,然後scan會將 (0,0), (0,1), (1,2), (3,3), (6,4)…分別代入(past,present) => past+present這個function中,所以最後的值就會是第一個0與所有的0到9的總合。

將Stream結合起來的好工具 - wye

要將Stream結合起來的作法有很多,這裡介紹一個叫作wye的工具,他裡面實作了許多能將Stream結合的各種function,像是merge, either, yip(這就像是zip一樣), unboundedQueue, boundedQueue, timedQueue

scala> val p1 : Process[Task,String] = Process("1","2","3","4")
scala> val p2 : Process[Task,String] = Process("a","b","c","d")

scala> p1.wye(p2)(wye.merge).runLog.run
res: IndexedSeq[String] = Vector(1, 2, 3, 4, a, b, c, d)

scala> p1.wye(p2)(wye.either).runLog.run
res: IndexedSeq[scalaz.\/[String,String]] = Vector(\/-(a), \/-(b), \/-(c), \/-(d), -\/(1), -\/(2), -\/(3), -\/(4))

scala> p1.wye(p2)(wye.yip).runLog.run
res: IndexedSeq[(String, String)] = Vector((1,a), (2,b), (3,c), (4,d))

其中有一個很實用的是dynamic,他的interface是def dynamic[I,I2](f: I => wye.Request, g: I2 => wye.Request): Wye[I,I2,ReceiveY[I,I2]],他讓你去決定當收到 左邊 或 右邊 的stream傳入的值的時候,要怎麼去處理,並且最後決定下一個是要拿 左邊(wye.Request.L) 或是 右邊(wye.Request.R)的。

// define the process logic for wye.dynamic, here is always change to the other side.
val w = wye.dynamic( (_:Any) => wye.Request.R, (_:Any) => wye.Request.L)

scala> p1.wye(p2)(w).runLog.run
res: IndexedSeq[scalaz.stream.ReceiveY[Any,Any]] = Vector(ReceiveL(1), ReceiveR(a), ReceiveL(2), ReceiveR(b), ReceiveL(3), ReceiveR(c), ReceiveL(4), ReceiveR(
d))

p1.wye(p2)(w).runLog.run.filter( _.isR )
res: IndexedSeq[scalaz.stream.ReceiveY[Any,Any]] = Vector(ReceiveR(a), ReceiveR(b), ReceiveR(c), ReceiveR(d))

scala> p1.wye(p2)(w).runLog.run.map(_ match {
     |       case ReceiveY.ReceiveR(x) => "R:"+x
     |       case ReceiveY.ReceiveL(x) => "L:"+x
     | }).map(x=>println(s"got $x"))

got L:1
got R:a
got L:2
got R:b
got L:3
got R:c
got L:4
got R:d

至於其他的unboundedQueue, boundedQueue, timedQueue…等別的用法,就請參考wye的原始碼嘍!

Reference

2015/08/26

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 1

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 1

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 1

這篇同步發佈在我的BlogGist

何時用到Stream(串流)?

一提到Stream(串流),第一個想到的就是影音的類型,這種應用面是非常的普遍也很容易理解。

而我第一次在project中想嘗試使用Streaming的時候,一直有個困擾,我的protocol是HTTP+JSON,我就是要等到整個payload都收下來了,才能Deserialize成Json object啊,這樣stream對我的好處是什麼?

前陣子有同事提到一個例子,說Java8的Performance比Scala好很多!
仔細發現,其實他在測試時用到的是Java8的Stream,但是卻拿Scala一般的Collection來比,就好比下面這樣的結果。(最後當然是幫Scala版本加個toStream兩個的差距就差不多了。)

def measure(f:()=>Any)={
  val s=System.currentTimeMillis
  f()
  println(s"elapsed: ${System.currentTimeMillis-s}ms")
}

scala> measure{ () => (1 to 20000000).toStream.filter(_%2==0).filter(_>200).head }
elapsed: 1ms

scala> measure{ () => (1 to 20000000).filter(_%2==0).filter(_>200).head }
elapsed: 983ms

一看就知道這是一個不公平的結果!!! 一個是乖乖把所有的結果整理完,然後往後傳,通通filter過之後,只取第一個結果,另一個是streaming(串流)的方式,把資料用”流”的方式一步步的流過filter與head,當第一個match的結果出現之後就結束了。

但這個例子讓我們知道,原來stream除了從合適的protocol處理就能有幫助之外,其實就算在系統內部的資料傳遞與處理的過程中,在適當的使用下,一樣是能提升效率的。

Streaming的選擇

最近Streaming的處理方式愈來愈紅,Akka在上個月(2015.07)也正式Release Akka Streams & Http Experimental 1.0,而Twitter Util也有提供AsyncStream,結合在Finatra上使用,看起來也不錯!

不過今天主角是Scalaz Stream,主要參考這篇Scalaz Stream - a Functional Reactive Programming Tutorial

以下來簡單的介紹一下Scalaz Stream的用法

Scalaz Stream

什麼是Process?

Scalaz Stream中,最重要的核心物件就是Process,這個Process[F[_], X]主要是定義了串流中的物件為X型態,並且透過F[_]這個Monad來包裝他,而在實際的使用上,通常會使用Scalaz的Task (請參考我上一篇寫的[ScaVa->Scala] Scalaz Task 取代Scala Future來進行非同步處理的另一個選擇)。

Process提供了一些run的方式,並且透過F[_]這個effect system來包裝他,可以想像F[_]是要處理這些X型態值的driver。

trait Process[F[_], X] {
  ...
  def run(implicit m: Monad[F]): F[Unit]
  def runLast(implicit m: Monad[F]): F[X]
  def runLog(implicit m: Monad[F]): F[IndexedSeq[X]]
}

在使用上的觀念要知道Process是個Stream,所以他的值會是一直”流”進來的,當使用run的時候,他回傳的是F[Unit],所以他是忽略你的回傳值,而主要的使用是用F[_]的特性來處理這些值,例如使用Task時,就可以使用他的timed, handleWith, onFinished之類的function。

而使用runLast的時候,會執行整個串流,一直到最後一個值並只回傳最後一個值。

使用runLog會將整所有的值都包在IndexedSeq[X]裡面,讓你可以一口氣取得所有串流中的值,這也是最耗記憶體的,就是把所有的結果都收集起來。

若以Listmonad來當作F[_],就會像以下這樣的例子:

scala> val p: Process[List,Int] = Process.range(0,10)

scala> p.run
res5: List[Unit] = List(())

scala> p.runLast
res10: List[Option[Int]] = List(Some(9))

scala> p.runLog
res11: List[IndexedSeq[Int]] = List(Vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))

產生Process

這裡舉幾種產生Process方式的例子:

1. 要把現有的值包成Process

val p = Process(1,2,3,4,5)
//p: scalaz.stream.Process0[Int] = Emit(WrappedArray(1, 2, 3, 4, 5))

如果你直接嘗試去run這個p,會得到以下的錯誤:

scala> p.run
<console>:16: error: could not find implicit value for parameter C: scalaz.Catchable[F2]
       p.run
         ^

因為他缺少了driver,也就是F[_],所以他不知道該怎麼去處理他,這時候他的型態是Process0,只是個wrapper,所以你可以直接toList取得你要的值,或是告訴他你的driver是什麼。

val p = Process(1,2,3,4,5):Process[Task,Int]
//p: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Emit(WrappedArray(1, 2, 3, 4, 5))
p.runLog.run
//IndexedSeq[Int] = Vector(1, 2, 3, 4, 5)

2. 使用scalaz.stream.io來產生

scalaz.stream.io有將一些io的操作包裝起來,讓你方便直接使用,例如讀檔案:

val lns: Process[Task,String] = io.linesR("inputfile.txt")

3. 使用Task.async來包裝你的執行動作,再透過Process.eval來產生

這種用法,很適合將你本來要進行的處理(一些java/scala library的存取、或是對server的request)給包成Task,再透過Process來將這裡面的結果包成Stream。

val f: Task[A] = Task.async { ... } //Do something to create an object
val evalF: Process[Task,A] = Process.eval(f)

這類型的有eval,repeat,repeatEval,suspend等…

4. 使用scalaz.stream.async.Queue

scalaz.stream實作了Queue來讓你從一邊把串流的值給不斷的塞進去,而另一邊可以直接以Process來接收這些串流的值。

import scalaz.stream._

val q = async.unboundedQueue[String]

while (true) {
  val inputString = readFromTheNetwork()
  q.enqueueOne( inputString ).run
}

//...elsewhere...

val stringsFromTheNetwork: Process[Task,String] = q.dequeue

5. 另外一種方式 - signal

這裡的signalqueue不同的地方是,這個的implementation是針對signal,所以他保證你能得到最後的值,但中間過程中若有許多次的變動,不一定每次都會拿得到,若你是希望每一個過程都要拿得到的話,要使用queue

val signal = async.signal[Boolean]
val signalChanges: Process[Task,Boolean] = signal.discrete

//Thread 1
signal.set(true).run // Time = 1
signal.set(true).run // Time = 2
signal.set(false).run //Time = 3
...
//Thread 2
signalChanges.map(x => {
  println("" + x + " -> " + System.currentTimeMillis)
  }).run.run
// Will print:
// true -> 1
// false -> 3

6. Process.awakeEvery定時產生Process的通知

import scala.concurrent.duration._
val clock = Process.awakeEvery(1 seconds)

Nest

[ScaVa->Scala] Scalaz Stream 串流好朋友 part 2 BlogGist

Reference

2015/08/24

情人節 台中三日遊

這次情人節,特別請了兩天假(星期四和五)避開人潮,與小柔到台中放鬆一下,玩個三天兩夜。
本來有考慮玩到四天,不過因為有颱風要來(雖然最後好像只是擦身而過),所以最後只有三天兩夜。
這次的行程都是小柔幫忙找的,其實找了很多個點,也建了Google MyMap,但因為太隨興了,很多點沒有跑到,辛苦小柔了。

Day 1 紙箱王 -> 碧根行館 -> 逢甲夜市

第一天我們大約中午從台北出發,先買了簡單的早餐在車上吃,接下來就直接出發到我們的第一個景點-紙箱王(其實也跑錯了,跑到了他的餐廳,但還是有蠻多很特別的紙箱製的作品)


再來旁邊剛好有大魯閣,跑去上個廁所又順便玩了一下遊樂設施!


算算時間差不多可以check-in了,我們就直接到碧根行館,其實本來想換到Hotel 7,但是是透過Booking.com,而免費取消的時間過了,只好住這間了。我們是Booking.com的特惠價格專案,一晚1999元,所以飯店跟我們說沒有附車位,只好花200元停在他們的特約停車場,大約走路7~8分鐘。(就在Hotel 7旁邊)

其實碧根行館就在逢甲大門口,夜市裡面! 我們逛一逛還可以先回去休息一下放個東西再繼續出來逛,地點真的滿分沒話說!


這是從房間窗戶往外面的畫面,對面就是i-bike站!

晚上就是瘋逛的逛逢甲夜市了!

這棉花糖做的有夠醜!

逢甲夜市的小吃們!這個雞排真的很好吃耶!


不小心逛到一間拼圖店,1000片的拉拉熊拼圖居然只要500元,加上木框999元,當然就直接買了!(這時候飯店近的優點馬上體會到,不用走太遠就可以把東西拿回去放。)


Day2 無為草堂 -> 精明一街 -> 輕井澤 -> 沐蘭

第二天check-out的時候也差不多中午了,我們就直接出到去無為草堂吃午餐。


餐點很好吃! 五穀米也很健康的感覺!


裡面的風格真的很讚,可以花20元跟櫃台買一大杯的魚飼料,小柔餵魚餵得很開心!


精明一街沒想像中的好逛,而且我出來玩的時候,又因為有一些事在處理,很常時間用手機,還跟學長通電話講了好一陣子,委曲小柔在旁邊等我。


小柔找到一間自己手作點心的烘焙教室,我們都很有興趣,但因為時間不夠,所以沒辦法體驗,只好下次再找機會來。


晚上跟以前同學碰面吃飯,超久沒聯絡的修哥,謝謝他請我們吃輕井澤,聊了蠻多,期待下次的聚會。

吃完飯後,就去沐蘭Check-in,之前旅展買的住宿休息劵+免費升等券,入住楓舞B,星期五晚上入住要再加600元,總共3千初頭,還蠻划算的。



房間很大很漂亮!


 Day 3 宮原眼科 -> 清水財伯米糕 -> 高美濕地 -> 西湖休息站 -> 回家

這次去台中車站旁的宮原眼科,運氣很好的在旁邊找到路邊停車格 (雖然也繞了三圈,但居然在星期六的中午找到路邊停車格,Lucky!)

滿滿的人要排冰淇淋,我個人是還好,小柔那個來也不能吃冰,所以就不排了!

伴手禮的店,我們買了小柔哥哥最喜歡吃的乳酪蛋糕,剛好他今天生日,帶回去給他吃。

接下來出發往清水走,先去吃個有名的米糕!不錯!醬汁很香很夠味!



本來的計劃是小柔想要去高美濕地抓蛤蠣,但太久沒來了,不知道現在已經在進行生態保護,架了個高高的步道,人不能下去…,我們還帶了網子過去… (不過看到旁邊有人拿著路邊攤在賣的小朋友挖沙組,他應該比我們還嘔!路邊攤還在賣那個實在是詐欺啊!)


不過這次看到滿滿的螃蟹跟一群一群的魚苗,這裡生態的保護真的有做有差。
結束清水高美濕地的曬太陽行程後,就要出發回台北了,路上經過西湖休息站,因為太想睡覺,就先在這裡休息一下。

西湖休息站有讓我驚訝,覺得不比清水差啊!


有人說怎麼會有奶頭…我就進來幫奶頭餅拍了幾張照…



童心未泯的小柔!

出發前買了新東陽的熊厚呷鹽酥雞,熱的時候還不錯,但冷了就蠻難吃的(有些感覺油溫夠高的冷了還是會好吃)。


簡簡單單的三天台中行,走著非常輕鬆的行程,但感覺很不滿足,覺得不久之後應該又要再來一趟了!





My World