2017년 8월 18일 금요일

VulcanJS 특징 번역

http://docs.vulcanjs.org/features.html 의 내용입니다.

VulcanJS 특징

GraphQL 스키마 생성

Vulcan은 SimpleSchema JSON 스키마를 기반으로 컬렉션의 GraphQL 스키마를 자동으로 생성합니다.
이렇게하면 두 가지 형식으로 스키마를 두 번 지정할 필요가 없습니다. 이 기능은 완전히 선택적이며 필요에 따라 수동으로 스키마를 지정할 수도 있습니다.

자동 생성 폼

Vulcan은 스키마를 사용하여 클라이언트 측 폼을 생성하고 적절한 Apollo Mutation을 통해 제출을 처리합니다.
예를 들어 하나의 동영상을 편집하기위한 양식을 표시하는 방법은 다음과 같습니다.
<VulcanForm
  collection={Movies}
  documentId={props.documentId}
  queryName="moviesListQuery"
  showRemove={true}
/>
queryName 옵션은 작업이 완료되면 자동으로 업데이트되는 쿼리를 VulcanForm에 통지하는 한편, showRemove 옵션은 "Delete Movie' 버튼을 폼에 추가합니다.
VulcanForm 클라이언트 저장소에 아직 로드되지 않은 경우 수정할 문서를 읽어오기도 합니다.

쉬운 데이터 적재

Vulcan에는 Apollo 데이터를 쉽게 로드할 수 있도록 데이터 로딩 헬퍼 세트로 withList (복수 문서용)와 withDocument (단일 문서용)를 제공합니다.
예를 들어, withList를 사용하여 MoviesList 구성 요소에 모든 동영상을 포함하는 결과를 prop에 전달하는 방법은 다음과 같습니다.
const listOptions = {
  collection: Movies,
  queryName: 'moviesListQuery',
  fragment: fragment,
};
export default withList(listOptions)(MoviesList);
Fragment를 전달하여 각 문서에 로드되는 데이터를 제어할 수 있습니다.

Schema 기반 보안과 검증

Vulcan 보안 및 검증은 모든 컬렉션의 스키마를 기반으로합니다.  스키마의 각 필드는 다음의 함수를 정의할 수 있습니다.

  • viewableBy
  • insertableBy
  • editableBy

이들은 모두 현재 사용자를 인자(선택적으로 영향을 받는 문서)로 사용하고  사용자가 주어진 작업을 수행 할 수 있는지 확인합니다.

그룹과 허가

Vulcan에는 사용자 그룹과 권한을 다루는 간단한 시스템이 있습니다. 아래의 예를 들면, 모든 사용자는 새로운 동영상을 만들고 스스로 편집/삭제할 수 있지만, 관리자만이 다른 사용자의 동영상을 편집하거나 삭제할 수 있습니다.
const defaultActions = [
  "movies.new",
  "movies.edit.own",
  "movies.remove.own",
];
Users.groups.default.can(defaultActions);
const adminActions = [
  "movies.edit.all",
  "movies.remove.all"
];
Users.groups.admins.can(adminActions);
그런 다음 mutation 검사에서 이러한 작업을 참조 할 수 있습니다.

그외 특징들

Vulcan에는 다음과 같이 즉시 사용할 수 있으며 시간 절약할 수 있는 많은 기능이 있습니다.

  • 국제화
  • 서버 측 렌더링
  • 테마를 만들거나 컴포넌트를 확장하는 도구들
  • 이메일과 이메일 템플릿 기능
  • 미리 만들어 놓은 mutation들

물론 Vulcan을 사용하면 vulcan 코어 뿐만 아니라 vulcan:posts나 vulcan:comments, vulcan:newsletter, vulcan:search 등에도 접근 가능합니다.

2017년 8월 4일 금요일

Rinkeby Test Network에 접근하는 간단한 방법.

dApp 개발 시 실제 계정으로 트랜젝션을 보내면 너무나 비싸므로
Rinkeby나 Ropsten 같은 테스트 네트워크에 연결하여 마이닝 없이 faucet을 통해 ether를 받고
그걸로 트랜젝션 테스트를 하면 편리하다.

보통 https://github.com/ethereum/wiki/wiki/Dapp-using-Meteor#create-your-%C3%90app 문서를 보고 시작하는데
geth --rpc --rpccorsdomain "http://localhost:3000"
이렇게 하면 마이닝부터 해야하니 귀찮다.
https://infura.io/#how-to 를 보고 계정을 신청하자. 이런 것도 호스팅이 되다니 좋은 세상이네.
간단한 개인 정보 몇가지를 입력하고 나면 Access Token이 나온다.

가입 후  https://infura.io/register.html 화면

Access Token이 있는 네트워크 주소로 geth를 연결한다.
geth --rpc --rpccorsdomain "https://rinkeby.infura.io/<YOUR_ACCESS_TOKEN>"
이러면 오케이.

meteor project를 만들고
meteor add ethereum:web3
추가한 다음 console에서
web3.eth.getBalance(web3.eth.coinbase, (error,result)=>console.log(
  error, result.toFormat()
));
자신의 coinbase의 잔액을 구해보자.
6eth가 최소단위인 wei로 보면 6,000,000,000,000,000,000 정도.
https://faucet.rinkeby.io/ 여기에서 받아온 (무료로/마이닝없이) ether가 잘 나온다.
여기서부터 시작하는게 좋아보인다.

2017년 7월 5일 수요일

coffeescript - export default를 coffeescript 에선 어떻게?

정답은
  return <obj>

가령, vue 같은 곳에서 (https://atmospherejs.com/akryum/vue-coffee)
<script>
  export default {
    data() {
      return {
         "say": "ho"
      }
    }
  }
</script>
이와 같이 작성한 코드는
<script lang="coffee">
  return data: ->
    "say": "ho"
</script>
이렇게 쓰면 된다.

2017년 7월 4일 화요일

spectrum에게 물어보세요. Meteor+Vue (feat HMR) + custom NPM 적용하려면?

vue를 쓰는 meteor project를 만들자.
이번엔 Blaze를 안쓸거다.
HMR(Hot module replace)도 된다고 하더라.
뭐 별로 중요하겠냐만 이런 거 좋아하는 사람도 있다.
meteor create --bare vueEx1
--bare 옵션을 주면 Blaze뿐만 아니라 어짜피 안쓸 기본 패키지도 빼준다. (jquery/autopublish/insecure 등등)

vue를 쓰기 위해 npm과 package를 하나씩 추가하자.
meteor add akryum:vue-component
meteor npm i -S vue
시작점이 중요하다.
html 먼저 작성하자.
아마 처음이자 마지막으로 작성하는 html일 것이다.

client/main.html
<head>
</head>
<body>
<app />
</body>
<app>에서 시작한다. 다른 이름이어도 상관없지만 $mount 할때 똑같이 맞추자.

client/main.js
import { Meteor } from 'meteor/meteor';
import Vue from 'vue';
import App from '/imports/ui/App.vue';
Meteor.startup(() => {
  new Vue({
    render: h => h(App),
  }).$mount('app');
});
main.js가 custom component인 <app/>을 먼저 바인딩하게 하고
imports 영역으로 최초 시작 vue component인 App.vue를 작성한다.

imports/ui/App.vue
<template>
<div class="app">
  <navhead />
  <contents />
</div>
</template>
여기까지 필수요소 되겠다.

이후로는 component를 client안에만 이름을 맞춰서 잘 넣으면 별 설정없이 잘 돌아간다.
여기선 navhead랑 contents를 썼으니 빠르게 확인해보자.

client/navhead.vue

<template>
  <div>
    <h1>header</h1>
    <input v-model="login"/>
    <div>
      <span>{{login}}</span>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        login: "guest"
      }
    }
  }
</script>
navhead 먼저 만들고

client/contents.vue
<template>
  <div>
    <ul>
      <li v-for="item in items">
        {{item}}
      </li>
    </ul>
  </div>
</template>
<script>
  export default {
    data: ()=> ({
      items: [ 2, 4, 6, 8, 10]
    })
  }
</script>
contents도 이렇게.


결과가 잘 나오는가?
전체적인 연결관계는 그려보면 아래와 같다.
파일 구조와 연결관계

약간 섬세한 초반 작업이 필요하지만 그 뒤론 vue 파일만 마구 쓰면 되니 아주 편하다.
설정은 우리 삶을 좀먹고 코드를 만들고자 하는 의욕을 떨어뜨린다.

한번 다른 vue package도 추가해보자. 
meteor npm install vue-flickity --save
그냥 npm 해도 되지만 node 조차 깔기 귀찮을 수 있다. 그냥 Meteor 만 설치했다면 이렇게 하자.
Meteor는 기본적으로 atmosphere라는 package 저장소를 사용하지만 1.3부터 meteor-node-stubs 
client/contents.vue에 <album /> 을 하나 추가하고 
<template>
  <div>
    <ul>
      <li v-for="item in items">
        {{item}}
      </li>
    </ul>
 <album />
  </div>
</template>
<script>
  export default {
    data: ()=> ({
      items: [ 2, 4, 6, 8, 10]
    })
  }
</script>
client/album.vue 를 잽싸게 만들어보자.
예제를 보고 template은 그대로 script는 export default에 맞게 조금 수정했다.
<template>
<div>
<flickity ref="flickity" :options="flickityOptions">
<div class="carousel-cell">1</div>
<div class="carousel-cell">2</div>
<div class="carousel-cell">3</div>
<div class="carousel-cell">4</div>
<div class="carousel-cell">5</div>
</flickity>
<!-- if you don't want to use the buttons Flickity provides -->
<button @click="previous()">Custom Previous Button</button>
<button @click="next()">Custom Next Button</button>
</div>
</template>
<script>
import Flickity from 'vue-flickity';
export default {
 components: {
      Flickity
 },
    data () {
      return {
        flickityOptions: {
          initialIndex: 3,
          prevNextButtons: false,
          pageDots: false,
          wrapAround: true
          // any options from Flickity can be used
        }
      }
    },
    methods: {
      next() {
        this.$refs.flickity.next();
      },
      previous() {
        this.$refs.flickity.previous();
      }
    }
}
</script>
저장하고 재적용이 잘 안되면 meteor를 다시 실행하자.
아마 css 가 적용이 안되서 잘 모르겠지만 특별히 오류도 없고 잘 작동하는 듯 하다.

솔직한 심정은 Blaze 2에서 나왔어야 할 것들이 Vue에서 대부분 구현되지 않았나 싶은데
기다리다 x된다는 말이 있는데 과연 Blaze 2의 운명은 과연 (쩜쩜쩜)

2017년 6월 30일 금요일

RxJS Common Mistakes

https://egghead.io/courses/save-time-avoiding-common-mistakes-using-rxjs

Convert RxJS Subjects to Observables

The use of RxJS Subjects is common, but not without problems. In this lesson we will see how they can be usually safely replaced with plain Observables.

Sometimes you may write code in RxJS that uses subjects. While subjects have a legitimate use cases, most of the times, they are actually unnecessary. In this case, for instance, we have a subject called click stream. Whenever a click event happens in this event listener, we will send that event into the subject using the .next method.
There are a couple reasons why this is a bad idea. First of all is that the click stream is now exposed here for any other script in the page to send events into it, so you may get confused how does this click stream work. You need to search for all of the usage of .next throughout the whole codebase. That may get confusing.
The second reason is that the event listener here is not being disposed. It's actually being attached to the document there and always staying there.
We can convert this subject to an observable and eliminate these two problems. We can do that with rx.observable.create. Here, the argument is just what we do when subscribe happens. We just write here the recipe for what should happen when subscribe happens.
For instance, we're literally just writing what happens when we subscribe. In that case, we just want to add an event listener on the [inaudible 1:26] . That event listener will send the event to the observer instead of sending it directly to the subject. We don't have a subject anymore. We just have the observer and the observable. We're going to send that event there, like this.
Obviously, we don't need this part anymore because we already have an event listener, and also the click stream now is an observable, so we don't even have this .next method available. It's only on subjects and observers.
We can remove this part and here when we subscribe to the click stream, we're going to just log out something interesting like the x-coordinate of that click. When we run this, we can click here, we can see those x-coordinates in the console.
One more thing, I also mentioned removing resources like the event listener. Here we added it to the document but we never removed it, so we can also code this logic inside the subscribe function as well.
Here, we can return unsubscribe function and this is the logic that we want to run once the user calls subscription.unsubscribe. Here we do our disposal.
For instance, when we subscribe, we get out a subscription. We can keep it like that so that later on we can clean out that subscription. For instance, if we run this code after four seconds, we can call subscription.unsubscribe. That will run the logic that is inside here.
For instance, we can do document.remove event listener, four clicks. We need to pass the reference to the same function. This function was actually anonymous. As you can see, there was no name to it. We need to have a name to function here.
That's why I'm going to extract this function and I'm going to put it over here. I'm going to call it just listener, is this anonymous function. That's what I'm registering when I add the listener. I'm going to remove that same exact function, like this.
This means that once we run this code, I can click for a while but after four seconds, I won't be able to click anymore as you can see, because the subscription was unsubscribed. This is how you can replace subjects with observables and have a lot of benefits from that.

Replace Observable.create with Observable creation helpers

In this lesson we will learn about potential problems that may arise when using Observable.create, a low-level function for creating Observables. In its place, we will use easier helper functions that create Observables in a safe manner.

With observable.create we can control what happens when we subscribe to the observable and what happens when we unsubscribe. That said, you usually don't need observable.create. Because it's very low-level, we took a lot of lines of code here just to get an observable of clicks.
Also, because it's low-level as well, here we are facing potential problems, for instance when we call observer.next. What if we would have made a typo where we have an extra V here for the name? Then this would throw an error.
But we need to catch the error and send it to the observer as well. Ideally we need to write like this. Try and then catch the error and then send that error as well to the observer. We need to do this because who knows?
Maybe some other operators down the chain will try to catch this error, or replace it, or something like that. As you can see, we need even more code to get it all correct. Instead of using observable.create, RX provides a lot of helpers to create observables of different types.
It happens to be for events on the dom such as clicks, we already have a helper called observable.fromEvent and here we can pass the node or the element and here we're going to pass the document and events of type click.
With this, we get the same thing that we had before and it already has that unsubscription logic to remove event listeners and so forth. It still works as we want it to, and etc. Of course, fromEvent doesn't work for all of the types of observables. Of course, if you want something from WebSockets, for instance, you're not going to use fromEvent. You're going to use something else.
But RX has many of these helpers to create observables and you should try to use them as much as possible instead of resorting to observable.create, because it's very low-level and you may do something wrong or forget some small detail. Usually you can get by creating all of the observables that you need from these helpers.

Use takeUntil instead of manually unsubscribing from Observables

Manually unsubscribing from subscriptions is safe, but tedious and error-prone. This lesson will teach us about the takeUntil operator and its utility to make unsubscribing automatic.

It's nice in RxJS that once you call unsubscribe, those resources such as event listeners attached will be disposed.
On the other hand, what's not nice is to keep track of these subscriptions. What if you have a dozen of these different subscriptions, and then you need to handle the calls to unsubscribe for each one of those?
It turns out that there are ways you can replace the manual unsubscribe with something smarter and more automatic. In our case, we want to dispose after four seconds. It turns out that we can represent this event of four seconds as an observable as well.
We can write here four stream as an observable of interval four seconds. That means that this will emit every four seconds, but we're going to take just one of those values.
It will emit after four seconds and then complete. We can make another observable here called clickUntil four stream. This will be basically the click stream, but we're going to use the takeUntil operator, and the argument here is four.
What it does this do is that it basically behaves the same as click stream, except it will complete when a four emits something. Just to make a quick marble diagram of what is going on there, if we have here click stream and four stream here and then clickUntil four stream will be this.
If we have our clicks happening here overtime as such, and then we have this we know that will emit a value after four seconds. Let's imagine that four seconds is somewhere over here, and then it's going to complete after that, because we just take one of those events.
ClickUntil four will be basically behaving like the click stream like this, except it will complete whenever this one emits, like that. The trick here is that whenever an observable completes, it will automatically unsubscribe from its resources. That's what allows us to be able to remove this part essentially. Instead of subscribing to the click stream, we're going to subscribe to clickUntil four.
We can try this out here in the console. I'm going to click for a while. After four seconds, it will not take my clicks anymore, as such. The trick here is really just to make observables complete somehow. TakeUntil is one of those operators that allows you to complete.
It's not the only operator that does that. We also have, for instance, here takeOperator that takes in number. Here as an argument, we're going to pass six there. Now, we can call this six clicks. We don't need this four observable anymore.
The logic here will be a bit different because we're doing something different. Instead of completing the click observable after four seconds, we're completing it after we have seen six clicks, like this. When it completes, it will also dispose the resources such as the event listener.
Let's try this out as well. One, two, three, four, five, six, and it no longer takes my clicks.
The lesson here is just, whenever you see a subscription, like for instance, if we would keep this subscription, and if you think that you need to unsubscribe it, then first ask yourself, "Can you make this one complete instead of using the unsubscribe?" Because then, you're going to have something that is more automatic and you don't need to worry about those un-subscription moments.

Convert an underlying source of data into an Observable

While there are many cases where we often believe that an RxJS Subject is necessary, there is a way of avoiding them. In this lesson we will see how to identify the underlying source of data and convert it into an Observable, essentially eliminating the use of an error-prone Subject.

There will be times where you think you need an RX subject, and still it is possible to avoid them. In this case we have a subject, just a generic one without a proper name, and we're implementing a simplified analytic system. Basically, whenever a click happens, we're sending in the event of number 1 into that subject, or whenever a request comes back from the server as response, we're also sending in a number 1 to that subject.
Then we can accumulate all of those number 1s as this X with the scan operator, and we can make this sort of count observable. Just to illustrate how the system works, whenever an event like response comes back from the server it registers, or whenever I click it will increment that. It's basically just counting all of the events that happen in my system, whether they are clicks or responses. Now the way that we can avoid the subject here is by identifying our original source of data, just asking ourselves where does the data come from?
We know that it comes from clicks. It's converted to the number 1, and then it's accumulated, so we know that it comes from the clicks. Let's just first write here our steps. We first identify the sources of data, and then we convert those sources of data to observables.
After we have observables for those sources, we can just compose them with all of the operators that we know. For instance, here we need to make an observable for our source data which is clicks, so we can make that with the from event, and click type, so that's our source of data.
We also know that data may come from the server as a response. We also need to convert this into an observable. Let's make that as response observable, and we can make it from a promise by calling from, and passing in that promise there. Now this is one of our sources of data as well. Now that we have these two sources of data, we can compose them in different ways.
We can make a stream here called oneObservable which will represent those number 1s that we were sending. It's basically going to be a merging of click stream and response stream, so we're basically saying that 1 is either events from click or either event from the response observable, and we're going to map each one of those to the number 1. We don't really care what is the contents of the click, or the contents of the response, we just want to convert that to the number 1.
We can get rid of our subject, and we can use here oneStream instead. This is going to work just like before. When we run this, it gets the number 1 to represent the response. As we click, it will register those as well.
If you want to make this slightly simpler, we can also cut out the middle man and just put in here straight merge, and then map. Now this countStream is basically either events from the clickstream or the response stream, and we're just mapping each one of those to the number 1, and then we're accumulating them over time.
The lesson here is try practicing by identifying where does your real data come from, and then asking yourself, "Can I wrap that in an observable?" When those things become observables like here, your code base will be more predictable because you can know where is the data coming from, and where it is flowing without having to hunt down all of the calls to subject.next method

Use the map operator instead of firing events on a Subject

In this lesson we will learn how to replace excessive subscribing and Subject emissions with simple uses of the map operator, simplifying our code and making it less bug-prone.

Whenever you see a next method inside a next callback when subscribing, then something is wrong. I can't imagine a case where you need this. What's going on here is that we have subject for X coordinates. Whenever a click happens, we want this subject to emit the X coordinate of that click event.
The way that we're achieving that is by listening to clickstream. We can get those vents of clicks. We feed the client X property into the subject with .next. Now, we have a situation here which data's coming from the clickstream and it's going to the X subject.
Whenever you have the situation of source of data and then destination, that's exactly the used case for an operator -- getting data from source and sending it to some destination.
In this case, instead of having this subject, we can make an observable of these X coordinates by using operator on the clickstream. That operator in this case is mapping.
Usually, when you have a next inside a next, it usually means a map, although there are different cases where you might need a slightly different operator. In this case, we get the click event and we just map that to the property clientX.
Now, I don't even need the subscribe anymore, because I don't need to get data from clicks. The only purpose of that was to send it into the X subject, but I don't need that anymore.
We can simply subscribe to the stream of coordinates. Then we can put each of these Xs in the console log. This will work as we want it to. Each time we click, it shows the X coordinate of that event. We don't need the subject here.
The tip here is if you see a next inside a next, then something is wrong. Remember that we're avoiding subject here, because it's less save by default where observables are often able to automatically dispose resources.

Use flattening operators instead of nested subscriptions

We are going to see how to avoid virtually any case of a subscribe happening inside another subscribe by replacing it with a flattening operator such as flatMap or mergeAll.

When you see a subscribe inside another subscribe call, then something is wrong as well. The reason why I say wrong is because this is starting to look like call back hell. If you remember call back hell, it happens whenever you have a callback inside another callback inside another callback, and you usually get something pyramid shaped like this code.
That's not good, because one of the main purposes of RxJS is to solve callback hell. In this example, we have a click observable and we have a response observable. The idea is that we subscribe so that whenever a click happens, we subscribe to the user data in order to make it execute and fetch that data.
Finally, when we get that data, we just put it in console. Just to demo this, I click and it will fetch from the server this data.
Notice that this is all about putting asynchronous actions in sequence, because clicks happen asynchronously and also does the user data come asynchronously. We are putting here an order that first, we want the click to happen and then we want the response.
How can we convert this to something that looks more like RxJS? First, I'm going to note that user data here is from the closure. It's basically referring to this cost that we defined up there. We can do this in different manner by first defining another observable called response when click. This will be the click stream. We're just going to map each of the click events to user data stream.
Now, I'm using this from the closure there, and then instead of subscribing to the click, I'm going to subscribe to response when click. I know that I'm not receiving anymore these events like I did before, but now I'm receiving this user data stream, which I'm going to call as response stream.
Response stream is basically this here. That's why actually this name we should rename this to stream stream, because it's basically an observable that emits observables. This is an observable. Basically, this has been always an observable.
This is not so nice, because it's an observable of observables and actually there are methods in RxJS to flatten these things. One of them is called merge all. Merge all is one of these flattening operators. In case you have an observables of observables, in our case for instance, we have clicks which were mapped to also smaller observables like these, I have the response.
Now, it's branching out. With merge all, you're able to flatten that in order to get just a normal response observable. When we do merge all, we go back to just having a normal observable, which means that this observable emits no data, it doesn't emit observables. This is now that data and we can simplify this code like this.
Let's see if that still works. Once I click here, it will fetch some data. If click again, it fetches that data again. If you want to simplify this even more, we could use instead the operator called merge map which does simply map and merge all at once. It's like a shortcut.
That is basically how we were able to remove that callback hell. It's basically, whenever you have a subscribe inside another subscribe that can be comforted to an observable of observables which in turn can be flattened with operators like merge map or merge all and others.

Use switchMap to avoid leaks when flattening

While flatMap is popular and convenient for flattening higher-order Observables, it can introduce new kinds of bugs related to subscriptions. In this lesson we will see how switchMap is a sensible default that avoids common bugs.

The merge map operator, also known as flatMap, either name works, is quite impotent and common for solving some asynchronous issues. That said, it can create some other problems if you don't use it in the correct cases.
Let's demonstrate that problem with this example. I have a click observable, then, I have another observable which depends on the clicks, and whenever a click happens, we will spawn a new inner observable which ticks every half a second. Then those numbers are put here in the console.
Let's see that working. Once I click here, it will spawn new inner observable, and if I click again, it will generate a new inner observable, and now, there are two of them happening concurrently. The more I click, the more it's going to spawn these new inner observables, and this just keeps on going forever.
Since these are infinite observables, these are never going to stop, so the more I click, it means that the more CPU and RAM we are consuming, and this doesn't stop. It doesn't have this cancelation logic into it.
Flatmap doesn't provide us that. But there are other operators, one of them is called switchMap, which provides you cancelation built in. It works in a similar fashion than flatMap, except it has slightly different semantics.
Here if we click, it will spawn a new inner observable, but once we click a second time, it cancelled, or basically unsubscribed from the previous one, and is now only subscribing to the most recent one. If I click again, you can see it cancelled the second one, and only took the third one.
That means that at any given time we only have inner observable ticking. We don't consume more and more RAM and CPU as we go. That means that switchMap is usually a better default choice, because it has this cancellation built in. Now that works even for the example with requests, here, we had our system with mergeMap to get the requests from the server.
Once we click here, it will give us that data, but we could have used switchMap here and it would have worked in basically the same way.
As you can see, once I click here, it will give me that data. The difference here is that if I click two times very quickly, basically a double click, then it wouldn't perform two requests. It would cancel the request for the first click, like this. As you can see, I just got one data back.
The conclusion is that when people use mergeMap, basically what they want to do is just flatten an observable of observables, but you need to realize that mergeMap is not the only flattening strategy, and if you don't know which strategy to choose you're probably better off with switchMap. By default, use switchMap, if you really know what you're doing, then go ahead and use mergeMap.

Replace zip with combineLatest when combining sources of data

This lesson will highlight the true purpose of the zip operator, and how uncommon its use cases are. In its place, we will learn how to use the combineLatest operator.

Sometimes in our Ext JS, we need to wait until many observables have emitted a value, to then combine those values together. This is a common use case with promises. Actually, there is a method called promise.all, which I hope is familiar to you.
Often, we need a similar functionality to promise.all, but in our Ext JS. For instance, imagine that we're calculating the volume of a room from its components like length, width, and height.
We have these observables to represent those, and then maybe you have used zip for this purpose. Now zip will actually work in this case, it takes the member observables like these three observables and then it also takes a function which tells how do we combine those three values into one single value.
In this case, we'll actually produce the correct volume there, but with a detail that if we would have updated the width and only the width to be eight meters, for instance, then we wouldn't see 5x7x2.8 and also then 5x8x2.8, we only saw 5x7x2.8.
It didn't actually consider this value, and that's because of the way that zip works, it's basically waiting for the second value from all of these other observables, so we need to add also here six, and still it's not enough because it's waiting for the second value from this observable, so we also need to add that value there.
Now, if we run this, it will calculate the volume for the first of these values, and also for the second of these values.
As you see, zip works in this synchronized manner because it's going to calculate for the first of these, and second of these, and third of these, and it's not going to mix the first with the second, and that type of stuff. Actually, this is probably not what you wanted. Zip is very specific, but a better operator to use by default is combineLatest, which has a similar signature to zip.
We don't need to change anything here, and then combineLatest allows us to update only one of these values and then as you see, it will calculate 5x7x2.8 which gives us 98, and also 5x7x2.5 which gives us 87.5.
That's why it's called combineLatest, because it only combines the latest values from each of these member observables. Basically, only the most recently emitted values from each of these members.
The lesson here is when you're combining many observables together to produce one value, then use combineLatest by default. Only use zip when you know that the observables being combined are guaranteed to have synchronized emissions which is quite rare, to be frank.

Move important side effects from do() to subscribe()

The do() operator allows us to perform side effects in the chain of operators. However, there are limited use cases for do(). We will learn what those use cases are and how to use subscribe() most of the times.

If you haven't heard of the do operator it allows us to perform a side effect in a specific point here in the chain of operators. In this example a click here will update the dot position but it will also cause request to happen to the server and get the response back.
How does do work? It's actually similar to a map operator where we take the input event and then we can simply return that same input event. Basically this done nothing. Every event that comes in will be the same that comes out, so it really does nothing. We can also include a side effect here.
Do does the same thing as this map. As you can see that allows us to add the sneaky side effect. Do is just a shortcut so that we don't need to write these lines of code. Basically do causes a sneaky side effect to happen during the data flow of this chain. This sneakiness may become a problem.
For instance, what if this part would be named as c stream for clicks and then in another file we use that c stream like this. We think that c stream is just an observable of click events, when in fact every time that we observe it, it causes this side effect. There is some sneakiness going on there.
In another sense, when we read this code it seems like data comes from this source, passes through these operators and ends in this destination. It seems like there is just one destination, but in fact there are two destinations of data where data comes from here but it also lands here as a destination in order to update the dot position.
As you can see these things are not so obvious. In fact we can refactor this code to avoid a do and we can use another subscribe. We're going to start by first noticing that this isn't anonymous observable. I didn't assign it to some const.
Also, when we call an operator this will be another observable which is anonymous. Let's give some names for these. Let's call this click stream. Then now we can subscribe to that click stream.
Notice that I didn't even change the contents here because it's basically the same for do and for subscribe. Now, we can create a response stream that will be this click stream switch mapped to this Ajax. Then we can subscribe to the response stream in order to get that data.
Notice how with this exercise where we named each of these sources of data now things are bit more explicit where we have this source of data and we subscribe to it to get that data and put it in this destination.
Then we also this source of data which depends on that source of data, and we subscribe to that one to give data to that destination. Notice also that if I replace subscribe with do this won't actually perform the side effect, because remember do is almost like a map, and as you know map will return an observable.
This will return an observable, but we're not subscribing to that one, we're just dropping it on the floor. That's why this will never happen if we have the do here. That's why we need to have a subscribe.
As you can see we got rid of our do and our code will still work in the same way. We will update the dot position and we're also going to make requests to the server. That said, there are good use cases for do, and I would recommend that you use do for debugging. We can put here whenever we get an event we can console log that event's client ex-position.
Now, when we click we also log out the ex-position here. We also do the side effects. Use do for debugging, it's very convenient, because the sneakiness of a console log is not a problem since it's just for debugging.
The lesson is, make the pipeline of sources of data and destinations of data more explicit by using subscribe more often than using do.

Implement pause and resume feature correctly through RxJS

Eventually you will feel the need for pausing the observation of an Observable and resuming it later. In this lesson we will learn about use cases where pausing is possible, and what to do when pausing is impossible.

Sometimes you will need to create a pause and resume interaction in your app. For instance, imagine that you're implementing polling where every two seconds, you want to make a request to this endpoint.
Maybe the data behind this endpoint is going to be different someday, that's why we have polling.
Right now, we're doing a request every two seconds. This is going to last forever. That's where pausing would be nice. While a pause and resume is possible in our Ext JS, there are some downsides to be aware of.
Ideally, we want to change the behavior of this observable, but that's not possible because this observable was defined to be like this, every two seconds, it emits an increasing number.
After it was defined, there is nothing that we can do to change that behavior. We could maybe apply operators but that's going to create new observables based on this observable. We can't actually change how this one behaves.
That said, we can apply a trick and that trick involves inserting a new observable which we'll call resume stream and this will emit false or true, so Booleans. We're going to make that as a subject. The subject here is not the important part. You could have done this with observables as well. It's only important that this will emit Booleans. False will mean to pause and true will mean to resume.
We're going to start by making it send false so that it's paused. We're going to set a timeout so that after a while, we are going to send true so that it starts, let's say, after 500 milliseconds. We're going to do that. Again, we're going to send false after, let's say, five seconds.
How do we use resume here? In this part of the code, we're going to write resume here, and then we're going to map, except we're going to use a switch map. We're going to take that Boolean, and we're going to check, is that Boolean true?
If it is, we're going to return this in our observable. If it is false, we're going to return our rx-observable.empty. I'm also going to put here at the end, do just for a console log for debugging purposes.
What does the switch map do here is that when we see a true from resume stream, then this stream will behave like this stream. When we see a false, then this stream will behave like the empty stream.
The empty stream won't emit any value, so that's why this part won't happen during the execution of this empty. While this one is executing, then we will get those events and that's how the Ajax is going to happen.
Let's try running this. Once we start, it starts pause. After 500 seconds, it does a request, and then it does another request. It stopped because five seconds kicked in and we have paused the resume. As you see here, we have requested zero. This zero came from the observable interval. After a while, we had requested one.
Now, I'm going to show something interesting where I'm going to resume again, let's say, at six seconds. We're going to see what happens. I'm going to pause at, let's say, nine seconds. We start paused and then after a while, it starts in two requests, and then we pause and then we start again. We pause at nine seconds.
Notice, we had requested zero, requested one, and then requested zero. This means that this stream here, the interval, although it behaves like this, it does zero, one and two, etc. We didn't resume and start getting two and three. When we resumed at this point here, we got zero.
That means that, as I told you before, we cannot pause and resume this stream, so that's why we didn't get the number two but we just restarted this stream. That's what happened. Once we subscribe again to this inner observable, we're going to start getting the number zero.
As you can see, we don't really truly have pause and resume for this observable, but we're just implementing the feature of pause and resume in a way that makes sense for us.
This is due to limitations with our Ext JS related to how observables fundamentally work, because after you call subscribe on an observable, you're not anymore in control of how data arrives. Observables are a one way communication of data from that observable to the observer.
We can't pause because that would mean that the observer wouldn't be able to send events back to the observable, and that would be a two way communication. That's not really the use case for observables. They are one way communication.
There are other features like EA6 generators that allow you to have two-way communication. Observables have different use cases.
The important thing is that we were able to implement this pause feature through different ways. It's not a problem that we cannot pause explicitly the specific observable. The lesson is, don't spend time trying to force observables to truly pause and resume.

Know when to extend the Observable class

The Observable in RxJS is a JavaScript class, and can be extended. In this lesson we will learn about those cases where extending the Observable class becomes a problem, and what cases where it makes sense to extend it.

The observable in RxJS is a class. For that reason, it is possible to extend it and make subclasses, such as my age observable here, for instance. My age observable has a constructor here that takes the initial age-- we're passing here 20 -- and then it will set that as the field here, age. When we subscribe to this custom observable, we're sending that initial age.
This is what happens here when we subscribe. We're going to see that my age is 20, like that. My custom class here has also this setter, so I can set the age, but, as you noticed when I set it 21, nothing changes here in the behavior of the observable. This is starting to look a bit complicated.
What would we need to do in order for that age to actually appear is we would need to, for instance, set the observer of the class to this observer once we subscribe. Now we have a reference to the observer, and then we could do something like this observer should receive the next age that was just set, so maybe this works now.
Also, this is not ideal because what if we do the set age before the subscribe? Then, technically, here observer is not defined yet because it's only defined once we subscribe, so this is going to break. As we see, it broke.
It started to get complicated, and the reason is that you shouldn't usually extend the observable to make your own custom observables. There are reasons why this is bad idea. First of all, it is complicated. It's hard to get this right so that we don't get errors, such as this case.
You should avoid doing this for the same reasons that you should using Observable.create, because there you may violate the observable contract, like, for instance, what if you have an error here, and then after that try to send through observer.next()? That violates the observable contract.
Another reason is that usually observables don't carry state between different subscribers, but here we're definitely carrying this common state of the current observer throughout different subscribers. If we would subscribe more times here, we would have conflicts. We would have one subscribe trying to set the observer and the other subscribe setting it again, so there's this conflict.
As you can see, it's not the case here. We shouldn't be trying to do this. To build special observables, we just compose them with the operators that we know, not by extending the observable, because we'd get into very tricky situations.
That said, there are legitimate cases for extending the observable. One of them is, for instance, logging. Here we have an example of that. To give some context, here this observable ticks every 100 milliseconds, and it looks like this over time. Once we passed that through this chain of operators, we're multiplying each of those by 10, so we get 10, 20, 30.
We filter for only numbers bigger than 15, so we get 20 and 30. Then we count the amount of those, so we're going to get two at the end. That's why it's going to show a pop-up saying two, like that. Here we can make our own custom observable by extending that. Then we can use the lift method, which is very interesting.
We also need to extend a little bit more stuff, like we need to make a log subscriber and log operator, but the idea here is that we want to inject a special behavior throughout the whole chain. Here we want to inject console.log() whenever next happens, or also when an error happens, or when complete happens.
I recreated this observable here but now as a log observable. It's an observable, so it takes the subscribe function here. We can call observer.next() every 100 milliseconds and observer.complete(). This observable is a log observable. The interesting thing is that once you call map on a log observable, the result of this will be also a log observable.
Also, once you call filter on a log observable, you're going to get out a log observable and so forth. How did we get that is through lift. Lift allows us to inject a custom behavior throughout the chain of operators. Basically, we're creating a new observable, which is also a log observable.
That's how we get those new ones, and that's what we return here in the end, but we're telling this one here that its source is the current one, so this keyword. We're wrapping the operator, such as map, in this log operator. That's how the log operator is going to create a log of subscriber and then finally inject that custom behavior in the chain.
What we get is that, once I hit run here, it's going to log each of these operations in the console log like that. We saw here 10 as the output of map. 10 didn't pass the filter because it's less than 15. Then we saw 20 as the output of map. 20 also passes the filter, so that's why we see 20 again. Then we see 30 from the map. 30 passes the filter. We see 30 again.
Then the output of map completes. That also propagates the filter, so filter completes. Now that finally this one has completed, count will kick in, so it's going to count all of the events that happened. It saw just two, so that's why count now emits two. Then finally count completes.
It's very nice that we were able to inject this logging behavior into this chain. Notice that we didn't touch any code related to this part. We only changed what was the source here, what was this observable. If we go back to this, as you see, now we don't have logging anymore.
All that was needed was just to change this source, because this log observable will propagate down this chain. That's one good use case when you should extend the observable -- when you want to inject a custom behavior throughout all of the chain of operators.
That said, it's not that common to extend the observable for this purpose, and it's also rather tricky because you need to make your own log operator that wraps other operators and also a log subscriber. You need to know some bits of the internal logic in RxJS.
Overall, I recommend that you don't extend the observable class but just stick to operators that allow you to compose and easily build observables.

Make Observables hot only where necessary

Operators like publish(), refCount(), share() make it easy to convert a cold Observable to a hot one, and are often necessary to get some feature done. In this lesson we will learn when exactly do we need to convert to hot, and when can we leave the Observable cold.

If you have used RxJS long enough, you have probably heard of the idea of cold and hot observables. It can be confusing, but we're talking about how to make a shared execution of the same observable from multiple subscribers.
The simplest example of this is when you have a clock observable that ticks every second, and we have six of those events. Now we have here two subscribers, a and b. Once we run that, each of those are going to get a different execution. If this is familiar to you, this is the usual problem.
We solve that by sharing that execution. Here, we can add .share() at the end, and now we're going to have a common execution of this clock observable shared to these two different subscribers. Now they're going to see the same events at the same time, like that. It's easy to stick to .share() without knowing what it does. I've seen cases where everything is .shared() where it doesn't need to be.
In order to avoid sharing everything, it's easy to remember that subscribe means invoke the execution of this collection so we can observe it. Without .share(), we're saying here, "Please, I want the values that come from this collection and deliver them here." Of course, if we say subscribe twice, we're invoking two different executions of that.
If you keep this in mind, it makes it much easier, because when we add .share() here, we're saying, "Instead of invoking a new execution, please share the execution of the observable if it exists."
Let's look at a bit more difficult example where it may be tempting to .share() everything. Here we also have a clock observable, and we have another observable called random numbers. We're mapping each of those to a random number. Then we have two other observables -- one of them gets random numbers smaller than 50, and the other gets random numbers larger than 50.
ToArray() simply collects all of them in an array so that we see all of them together at the end when it completes. Let's run this and see what happens. The clock is generating events over time. Then we're mapping each of those to a random number. When that completes, we collect all of those numbers into small and large collections.
Notice we have some problems here. We have four numbers in total in small and large category, but we had originally six random numbers, as you can see here. Also, these numbers don't actually occur in this original collection, and neither does 57.84 happen here. This is very strange. You may find this type of behavior in an RxJS app, so it's usually the case that we're not sharing executions here.
What's going on here? First of all, when we do subscribe, remember we're invoking, "Please, give me an execution of this collection." When we do this, we're going to look here, "Give me an execution of this collection. Give me an execution of that collection. Give me an execution of this random number," which will finally ask for an execution of the clock.
This is going to have its own execution. Each of these maps will be totally exclusive for large number subscriber and the same thing for small number and random number. We essentially have here three executions of the clock and three executions of the random number. That's why we have all of these different random numbers.
It's easy to instead put .share() here everywhere. Sometimes people do this like that in an attempt to ignore where the source is and try to share everything. To some extent, this may solve the problem. As we see here, all of these numbers correspond there, and they're the same, but this is an overkill, and we don't need to do this. We can just share what is necessary.
Let's look at what is it that we need to share exactly. It all has to do with what kind of side effects happen once we subscribe or once something happens in this operator. For instance, this operator will always give out the same output for the same input. For instance, if we give x is 20 here, it's always going to say true.
On the other hand, when we invoke this function multiple times, we get different numbers every time. This is a side effect. We probably want to share this part, for instance. Also, once we subscribe to an interval, you know that internally it will do setInterval(). That's also like a side effect because it's creating a pseudo-thread. It's creating that thing, so it's not so pure.
We could probably already conclude that this part needs to be shared, because once we call setInterval(), we want that to be shared. Then take(6) will always do the same thing, so we don't need to share that. That's not enough, because let's see what happens.
We get the numbers 61, 8, 3, 85, but those numbers are not corresponding here. The reason for that is that once we call a small number subscribe(), we're saying, "Please give me the execution of this." Then it's going to invoke the execution of this, and then finally invoke the execution of this, and invoke the execution of this map based on this clock.
Even though we're going to share the same setInterval(), but we're not sharing this map, so each of these subscribers is going to get a different execution of this map operation. That's why we also need to share this part. Then, once we do that, we see 81, 30, 60, 76, and those numbers appear here.
It's a good idea not to call share() everywhere because every time you call share() you're actually creating a new subject, and that might cause maybe problems with garbage collection. It's generally wise not to do operations that we don't need to do in the first place.
The tip here in order to avoid cold and hot problems is to remember that subscribe means invoke an execution of this collection, and then all of these executions are changed throughout these operators. Some of those executions we want to share, such as, "This random number, I wanna share it," or, "The setInterval() that's inside this creation, I wanna share that as well."
The other bits are going to behave exactly the same way no matter if you share them or not. That's why we don't do it. Over time, you get to develop a sense of what should be shared and what should not be shared, depending on these side effects that may occur.

2017년 6월 28일 수요일

2017년 6월 23일 금요일

raspberryPi zero 에서 USB 네트웍 연결이 안될 때.

집에선 안되고 밖에선 잘되는 알 수 없는 USB 네트웍 연결
raspberryPi Zero가 연결 안된 상태에서 ping 을 했을 때
$ ping raspberrypi.local
PING raspberrypi.local (218.38.137.28): 56 data bytes
64 bytes from 2xx.xx.xxx.xx: icmp_seq=0 ttl=53 time=3.452 ms
64 bytes from 2xx.xx.xxx.xx: icmp_seq=1 ttl=53 time=2.664 ms
64 bytes from 2xx.xx.xxx.xx: icmp_seq=2 ttl=53 time=3.632 ms
64 bytes from 2xx.xx.xxx.xx: icmp_seq=3 ttl=53 time=5.183 ms
64 bytes from 2xx.xx.xxx.xx: icmp_seq=4 ttl=53 time=3.374 ms
64 bytes from 2xx.xx.xxx.xx: icmp_seq=5 ttl=53 time=3.516 ms
^C
이렇게 다른 놈이 잡고 있어서다.
다른 네트웍(가령 테더링)으로 연결 후
ssh pi@raspberrypi.local
로 접근(기본 암호는 raspberry) 후 잘되면
sudo vi /etc/hostname
하여 기존 raspberrypi 를 적절한 것으로 변경하고 raspberryPi Zero를 재부팅 후 ssh pi@<변경한 hostname>.local ( <>는 입력하지 않음) 으로 접속한다.

2017년 6월 15일 목요일

Cycle.js 의 Driver에 대한 이야기

왜 이름이 Driver 인가
OS에서 외부하드웨어와 연결하는 소프트웨어를 Driver라고 하는데 외부로부터 영향(effect)를 주고 영향을 받는다는 점에서 아이디어를 얻음.

DOM Driver


Sink가 없는 형태의 Driver
function WSDriver(/* no sinks */) {
  return xs.create({
    start: listener => {
      this.connection = new WebSocket('ws://localhost:4000');
      connection.onerror = (err) => {
        listener.error(err)
      }
      connection.onmessage = (msg) => {
        listener.next(msg)
      }
    },
    stop: () => {
      this.connection.close();
    },
  });
}
websocket의 예

Driver 만드는 법
function myDriver(sink$, name /* optional */)
부터 시작.

다시 Sock(가짜 실시간 리얼타임 채널 API) 구현
// Establish a connection to the peer
let sock = new Sock('unique-identifier-of-the-peer');
// Subscribe to messages received from the peer
sock.onReceive(function (msg) {
  console.log('Received message: ' + msg);
});
// Send a single message to the peer
sock.send('Hello world');
이렇게 일단 가정.
effect가 어떤 것인지 가려내보자
write effect는 sock.send(msg) 일테고
read effect는 sock.onReceive
import {adapt} from '@cycle/run/lib/adapt';
function sockDriver(outgoing$) {
  outgoing$.addListener({
    next: outgoing => {
      sock.send(outgoing);
    },
    error: () => {},
    complete: () => {},
  });
  const incoming$ = xs.create({
    start: listener => {
      sock.onReceive(function (msg) {
        listener.next(msg);
      });
    },
    stop: () => {},
  });
  return adapt(incoming$);
}
구현은 이렇게.
adapt를 가져와서

  1. outgoing 스트림을 인자로 받는다.
  2. outgoing 스트림에 대해 Listener(subscriber)를 추가한다.
  3. subscriber는 outgoing 스트림에서 받아 sock.send 를 한다.
  4. incoming 스트림은 start에 sock이 데이터를 받을 때 해당 인자(listener)의 next로 받은 메시지를 보낸다.
  5. incoming 스트림을 adapt의 인자로 반환하는 것으로 마무리
여기까지가 sockDriver 라면 Sock을 생성하는 것을 포함한 makeSockDriver를 만들어본다.
import {adapt} from '@cycle/run/lib/adapt';
function makeSockDriver(peerId) {
  let sock = new Sock(peerId);

  function sockDriver(outgoing$) {
    outgoing$.addListener({
      next: outgoing => {
        sock.send(outgoing));
      },
      error: () => {},
      complete: () => {},
    });
    const incoming$ = xs.create({
      start: listener => {
        sock.onReceive(function (msg) {
          listener.next(msg);
        });
      },
      stop: () => {},
    });
    return adapt(incoming$);
  }
  return sockDriver;
}
makeSockDriver는 peerId라는 인자를 받아 Sock을 생성한다.

실제 사용.
function main(sources) {
  const incoming$ = sources.sock;
  // Create outgoing$ (stream of string messages)
  // ...
  return {
    sock: outgoing$
  };
}
run(main, {
  sock: makeSockDriver('B23A79D5-some-unique-id-F2930')
});
익숙한 방식이다.
https://github.com/Widdershin/cycle-animation-driver/blob/master/src/driver.js
requestAnimationFrame 을 사용한 Driver 를 보면서 응용의 폭을 생각해보자.

https://www.npmjs.com/package/cycle-canvas
Canvas에도 마찬가지로 적용할 수 있다.

https://github.com/cyclejs-community/cycle-canvas/blob/master/examples/flappy-bird/index.js
에서 KeysDriver도 흥미롭다.
function makeKeysDriver () {
  const keydown$ = Observable.fromEvent(document, 'keydown');
  function isKey (key) {
    if (typeof key !== 'number') {
      key = keycode(key);
    }
    return (event) => {
      return event.keyCode === key;
    };
  }
  return function keysDriver () {
    return {
      pressed: (key) => keydown$.filter(isKey(key))
    };
  };
}
이렇게 정의하고
function App ({Canvas, Keys, Time}) {
  ...
  const space$  = Keys.pressed('space');
  ...
}
이렇게 사용한다. space 키에 대한 이벤트에 대해서만 filter한 스트림이 space$가 된다.
Driver에 대해 이해하면 Cycle.js 가 더욱 가깝게 느껴진다.

https://github.com/cyclejs-community/cycle-canvas/blob/master/examples/flappy-bird/app.js
flappy bird 예제인데 state에서부터 반복적으로 발생하는 스트림, 화면 갱신 주기. 이 모든 걸 scan하는 것 등등 참으로 알차고 값진 예제다. 이해하기도 쉽고.

Meteor DDP 완전분석 - All about DDP

한번은 정리해야지 하는 생각으로 Websocket 모니터를 열고 DDP를 하나하나 각개 격파해보려고 한다.
클라이언트에서 요청별로 서버 응답 내용을 정리해보았다.


  • 로그인 없을 때
  1. 접속

    14:07:20.485   {'msg':'connect','version':'1','support':['1','pre2','pre1']}
    14:07:20.486 a {'server_id':'0'}
    14:07:20.634 a {'msg':'connected','session':'wqr3vuoJSYXvHLKyR'}

    정해진 JSON을 단순하게 전송하고 { msg: connected } 를 받는다. 단순!
  2. meteor_autoupdate_clientVersions 요청
    14:07:20.486   {'msg':'sub','id':'nhyS7NmraDAk8Y5Nc','name':'meteor_autoupdate_clientVersions','params':[]}
    14:07:20.636 a {'msg':'added','collection':'meteor_autoupdate_clientVersions','id':'HgGFXuNFEw4TeHb7S','fields':{'current':true}}
    14:07:20.639 a {'msg':'added','collection':'meteor_autoupdate_clientVersions','id':'version','fields':{'version':'3953cc195bf2ea8538cc04c228043b0222c64faa'}}
    14:07:20.639 a {'msg':'added','collection':'meteor_autoupdate_clientVersions','id':'version-cordova','fields':{'version':'none','refreshable':false}}
    14:07:20.640 a {'msg':'added','collection':'meteor_autoupdate_clientVersions','id':'version-refreshable','fields':{'version':'4d68205e4c57f75e9a5ca7e7fd446277ef11e2e4','assets':{'allCss':[{'url':'/merged-stylesheets.css?hash=20ae2c8d51b2507244e598844414ecdec2615ce3'}]}}}
    14:07:20.641 a {'msg':'ready','subs':['nhyS7NmraDAk8Y5Nc']}
    최초의 subscribe이다. meteor_autoupdate_clientVersion 이라는 미리 정의한 collection에 버전 정도 등등을 내려준다.
    subscribe시 요청한 ID 를 {'msg': 'ready' } 시 subs 와 맞춰보는 것으로 한 쌍을 이룬다.
  3. 로그인
  4. 로그인 서비스 설정

2017년 5월 28일 일요일

Firebase Functions - backend 로직을 구축해보자.

아예 functions라는 이름의 HTTP기반 node.js application 도 올릴 수 있다.
잘하면 meteor도 올릴 수 있을 거 같긴한데 일단 나중에 볼 일이고
https://firebase.google.com/docs/functions/get-started 따라 해보자
먼저 init 부터
$ firebase init functions

     🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥     🔥🔥🔥     🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥
     🔥🔥        🔥🔥  🔥🔥     🔥🔥 🔥🔥       🔥🔥     🔥🔥  🔥🔥   🔥🔥  🔥🔥       🔥🔥
     🔥🔥🔥🔥🔥🔥    🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥   🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥
     🔥🔥        🔥🔥  🔥🔥    🔥🔥  🔥🔥       🔥🔥     🔥🔥 🔥🔥     🔥🔥       🔥🔥 🔥🔥
     🔥🔥       🔥🔥🔥🔥 🔥🔥     🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥     🔥🔥  🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥

You're about to initialize a Firebase project in this directory:

  /Users/spectrum/Documents/js/_firebaseApp/sheepals

Before we get started, keep in mind:

  * You are initializing in an existing Firebase project directory


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

i  .firebaserc already has a default project, skipping

=== Functions Setup

A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

✔  Wrote functions/package.json
✔  Wrote functions/index.js
? Do you want to install dependencies with npm now? Yes

물어봐주면 Yes. 그러면 firebase-admin과 firebase-functions를 설치한다.

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!

설치 끝.
cd funtions 해서 뭐뭐가 있는지 보자.
index.js
node_modules
package.json
다 있다. 끝내주네. package.json 하고 node_modules 폴더 있는 걸 보면 게임 오버네.
var functions = require('firebase-functions');
// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions
exports.helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase!");
});
주석을 제거하고 실행해보자.
firebase deploy --only functions
로 functions만 적용이 가능하다.
꽤 시간이 걸리는데 과연 로컬환경 구성 같은 건 없나? 불편불편.

$ firebase deploy --only functions

=== Deploying to 'sheepals-2f2d6'...

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
i  runtimeconfig: ensuring necessary APIs are enabled...
✔  runtimeconfig: all necessary APIs are enabled
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (859 B) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: creating function helloWorld...
✔  functions[helloWorld]: Successful create operation. 
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/sheepals-2f2d6/overview
Function URL (helloWorld): https://us-central1-sheepals-2f2d6.cloudfunctions.net/helloWorld

하 테스트할 URL까지 뽑아주네.


Dashboard에 가면 이런게 생겼고

잘 작동한다.
Firebase.. 무서운 아이!
로그를 보면
실행하는데 얼마나 걸렸는지도 나오고 Functions별 검색도 아주 잘된다.
그래도 구글이 호구는 아닌게 https://github.com/firebase/functions-samples/tree/master/stripe 같은 예를 보면 non-Google 서비스를 사용할 때엔 유료플랜인 Blaze나 Flame으로 변경해야 되나보다.

2017년 5월 26일 금요일

Firebase 정적 호스팅

이번 google I/O도 그렇고 꾸준히 firebase를 밀고 있다.

디비 뿐만 아니라 정적 호스팅도 해준다는 소식에 혹 해서 살짝 내용을 살펴보았다.
https://firebase.google.com/docs/hosting/quickstart

https://console.firebase.google.com/
먼저 firebase 콘솔로 들어가서 프로젝트를 하나 만들자.
Add Project를 누르면

프로젝트 이름과 지역을 설정할 수 있다.
적절히 넣어주고 Create Project하여 프로젝트를 만들고

당연한 이야기지만 0.10.x 이상의 Node.js를 설치한 상태로 firebase-tools를 먼저 설치하자
npm install -g firebase-tools
좀 시간이 걸린다.
다 되면 폴더를 하나 만들고 그 안에서 init으로 초기화를 하자.
mkdir exam1
cd exam1
firebase init

     🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥     🔥🔥🔥     🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥
     🔥🔥        🔥🔥  🔥🔥     🔥🔥 🔥🔥       🔥🔥     🔥🔥  🔥🔥   🔥🔥  🔥🔥       🔥🔥
     🔥🔥🔥🔥🔥🔥    🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥   🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥
     🔥🔥        🔥🔥  🔥🔥    🔥🔥  🔥🔥       🔥🔥     🔥🔥 🔥🔥     🔥🔥       🔥🔥 🔥🔥
     🔥🔥       🔥🔥🔥🔥 🔥🔥     🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥 🔥🔥🔥🔥🔥🔥🔥🔥  🔥🔥     🔥🔥  🔥🔥🔥🔥🔥🔥  🔥🔥🔥🔥🔥🔥🔥🔥

You're about to initialize a Firebase project in this directory:

  /Users/spectrum/Documents/js/_firebaseApp/sheepals

? Which Firebase CLI features do you want to setup for this folder? Press Space to select featur
es, then Enter to confirm your choices.
⚠  You have have not selected any features. Continuing will simply associate this folder with a Firebase project. Press Ctrl + C if you want to start over.

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Select a default Firebase project for this directory: sheepals (sheepals-2f2d6)

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!

여기선 깨지는데 터미널에서 공격적인 Firebase ASCII ART를 볼 수 있다.
초기화가 끝나고 나면 덜렁 firebase.json과 .firebaserc 두 파일을 생성한다.
firebase init hosting 을 한번 더 해주면
=== Hosting Setup
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.
? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
✔  Wrote public/index.html
i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...
✔  Firebase initialization complete!
요렇게 몇 가지 더 확인하고 public 아래에 index.html 까지 다 만들어 준다.

firebase.json이 구조 정보가 들어가는 파일인데 package.json 같은 거라고 생각하면 된다.
기본적으로
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}
요런걸 생성하는데 node_modules도 써야하고 .idea, .gitignore 등등 필요없는 파일/디렉토리를 디플로이하지 않기 위해 ignore를 추가하고 public 위치도 ./app으로 바꿔본다.
{
  "hosting": {
    "public": "app",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}
https://firebase.google.com/docs/hosting/deploying 에서 북봍해서 적용해보자.
디폴트 경로는 public이 키인 ./app 아래에 index.html 하나 넣어보자.
ignore에 있는 내용은 실제 디플로이 되지 않는다.
디플로이 하자.
firebase deploy
너무 간단하다. 할말이 없다.
<project명-난수숫자키>.firebaseapp.com 으로 접속할 수 있다.
$ firebase deploy
=== Deploying to 'sheepals-2f2d6'...
i  deploying hosting
i  hosting: preparing app directory for upload...
✔  hosting: 1 files uploaded successfully
i  starting release process (may take several minutes)...
✔  Deploy complete!
Project Console: https://console.firebase.google.com/project/sheepals-2f2d6/overview
Hosting URL: https://sheepals-2f2d6.firebaseapp.com
마지막에 나온 Hosting URL로 접근해서 잘 나오면 오케이.
다음 내용을 보면서 functions와 custom domain을 숙지하고 계속 나아가자.

2017년 5월 10일 수요일

동적으로 로딩하는 Meteor i18n 다국어 적용기

작년부터 꾸려가던 서비스가 다행인지 불행인지 수입은 별론데 입소문과 방송이 마구 돌아서 해외에서도 요청이 들어오고 있다.

드디어 i18n! 즉, 다국어 지원을 해야할 시기가 온 것이다.
https://themeteorchef.com/tutorials/i18n-and-meteor 를 보고 tap-i18n(https://github.com/TAPevents/tap-i18n) 패키지를 써보기로 마음먹었다.

약간 삽질이 있었는데 처음에
meteor add tap:i18n
만 하면 나머지 다 잘 될줄 알았다.

아니었다. 역시 인생 실전...

내 경우는 json 파일을 정적으로 만드는 것이 아니라 개발팀이 아닌 일반 관리자가 새로 만든 상품에 대해서도 다국어 대응을 바로 해야하기 때문에 동적으로 로딩할 수 있도록 만들어야 했다.

먼저, 자원을 로드하는 건 별로 어렵지 않았는데
TAPi18n.loadTranslations(
    {
        es: {
            meteor_status_waiting: "Desconectado"
        },
        fr: {
            meteor_status_failed: "La connexion au serveur a échoué"
        }
    },
    "francocatena:status"
);
이런 식으로 언어별로 key/value 매핑을 하고 마지막 인자로 namespace를 주면 된다.
별로 떠오르는 이름이 없어서 위 예의 "francocatena:status" 대신 "project"라고 줬다.

html 헬퍼에서 {{{_ '번역할 문장'}}} 을 했더니 '_' 라는 펑션 없단다.
ecmascript 패키지와 충돌하느니 어쩌느니 얘기가 주절주절 많은데
결론은 project의 root에 project-tap.i18n 파일을 만들고
{
    "helper_name": "_",
    "supported_languages": ["ko", "en"]
}
이렇게 두 개의 키를 지정하면 된다.
supported_languages를 지정하지 않으면 TAPi18n.setLanguage 를 통해 현재 언어를 변경하는 것이 제대로 작동하지 않으니 주의할 것.

2017년 4월 17일 월요일

aframe 일기 - controller는 중구난방

webVR 쪽 일을 할 수도 있을 거 같아 들이대보려는 중.
aframe 이 확 끌린다.
x3dom은 솔직히 너무 구렸어. 그리고 느렸어. 별로야.
https://jsbin.com/suloji/edit?html,js,output
비슷한데 쉽다. korat을 사용해서 코드는 좀 독특한데 어쨌든.

https://aframe.io 는 데모도 좋고
ctrl+alt+I로 전용 inspector가 열리는 것도 좀 신박했어
내가 좋아하는 MagicaVoxelBlender도 지원하는 로더들이 있고
무엇보다 컨트롤러 지원이 있었어.
그래, 컨트롤러가 VR에서 반 이상은 되지.
원래 또 내가 한 컨트롤러 좋아하지.
난 gaze control이 너무 싫어.
정말 멍청한 생각이야.
도구를 쓰라고 도구를!

보니까 가격이 비싸든 싸든 헤드기어 쪽은 어느 정도 수준이 올라온 것 같은데 가격대비 공신력(?)에서 Daydream이 좀 끌리긴 했다.
싸고 가볍고 간편하고 구글이고 훌륭하지. 듀얼콘은 아니고 Wii 컨트롤러 같은데 감도도 그렇고 좋아보인다.
근데, 화웨이, 에이수스, 모또롤라, ZTE 미만 잡이래.
S7이 있음 뭐하나 제기랄.
https://github.com/domination/gvr-services-emulator/blob/master/apks/README.md
보니까 gvr-service를 손봐서 일반 폰에서 쓸 수 있게 한게 있는데 페인트 예제 앱 두 바퀴 빙빙 칠하다가 주저앉고 나서 더 해볼까 싶어도 결국 Daydream은 시작조차 해볼 수 없더라.

가지고 놀아보고 싶었는데 노는 건 나중에.
자료를 찾아보면 이젠 거의 자동인데 https://github.com/aframevr/awesome-aframe 시리즈 먼저 체크.
https://proxy-controls.donmccurdy.com/ 라는 훌륭한 것이 있군.
두개의 웹브라우저.
하나는 컨트롤러, 하나는 뷰어. 그리고 webRTC datachannel까지.
완벽해! 테스트해보자.

음? 좋긴한데 데스크톱에서 키보드나 게임패드로 폰을 제어하는 리모트라 내가 생각하던거랑은 반대.

https://github.com/ryanbetts/dayframe
오히려 이게 찾던 것.
데모는 웹소켓이로 Heroku라 엄청 느리다. 개량을 하던 새로 만들던 해야겠구만.
소스 코드를 보니

            window.addEventListener('devicemotion', handleMotionEvent, true);
            window.addEventListener('deviceorientation', handleOrientationEvent, true);
            // prevent the remote from moving around on the page
            window.addEventListener('touchstart', function (evt) {
                evt.preventDefault();
            });
            trackpadEl.addEventListener('touchstart', function (evt) {
                socket.emit('trackpad:touchstart');
                trackpadContactEl.classList.remove('hidden');
            });
            trackpadEl.addEventListener('touchmove', function (evt) {
             trackpadEl.addEventListener('touchend', function (evt) {
                socket.emit('trackpad:touchend');
                trackpadContactEl.classList.add('hidden');
            });
            trackpadHammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
            trackpadHammer.on('doubletap', function (evt) {
                socket.emit('trackpad:click');
            });
            trackpadHammer.on('swipeleft', function (evt) {
                socket.emit('trackpad:swipeleft');
            });
            trackpadHammer.on('swiperight', function (evt) {
                console.log('swiperight');
                socket.emit('trackpad:swiperight');
            });
            trackpadHammer.on('swipeup', function (evt) {
                console.log('swipeup');
                socket.emit('trackpad:swipeup');
            });
            trackpadHammer.on('swipedown', function (evt) {
                console.log('swipedown');
                socket.emit('trackpad:swipedown');
            });
            homeBtnHammer.on('tap', function (evt) {
                socket.emit('home:tap');
                orientationCenter = lastOrientation;
            });
            appBtnHammer.on('tap', function (evt) {
                socket.emit('app:tap');
            });
이정도가 핵심 코드인가 봄.

2017년 4월 11일 화요일

Meteor에서 yarn을 쓰는 간단한 방법

역시 Meteor는 짱짱입니다.
yarn을 좀 써보니까 괜찮길레 Meteor 에선 어떻게 하나 봤더니 진짜 간단하군요.
Meteor의 CLI는 자기 자신의 영역을 가지고 있습니다.
node를 설치를 했건 안했건 상관없이 meteor npm 이 가능하기 때문에 yarn, jsdoc 같은 걸 설치해서 쓸 수 있습니다.

meteor npm install -g yarn
한 후
meteor yarn 으로 package.json을 실행하시면 됩니다.
yarn은 기본으로 --save 옵션으로 관리하기 때문에 로컬 패키지 관리시 실수를 줄일 수 있어 편리합니다.

2017년 3월 16일 목요일

coffee는 쓰고 싶고 jsx는 쓰기 싫고. 그러면 만들어야지.

이 블로그를 꾸준히 보시는 분들은 아마도 {}, [] 같은 시작과 끝만 알리고 아무것도 실행하지 않는 주제에 오롯이 한 행을 죄다 차지하는게 싫어서 coffee나 jade나 stylus를 쓰는 변태영감탱이의 글들을 읽고 계실거다.

나는 Meteor를 매우 좋아하는 사람이고 Meteor 사용자의 저변을 넓히기 위해 frontend에서 가장 뜨거운 React를 Meteor에서 쓰면 좋다라고 열심히 약을 팔고 있는데.
정작 내게 불편한 것은 jsx였다.

https://facebook.github.io/react/docs/react-without-jsx.html
이런 것이 없는 건 아닌데
이따위로 만들어놨다는 얘기는 쓰지 말란 얘기지.
그리고 어짜피 문서가 아닌데 구태여 코드안에 html을 쓸 이유가 있나?
애초에 html,css,js 로 분리한 건 의미를 가진 문서(html)를 서식화(css)해서 로직(js)을 돌아가게 함인데 이미 html도 css도 쓰지 않는 시점에서 코드안에 html을 섞어쓰는 것은 내겐 매우 불편한 일이었다.

그렇다면 DOM과 같이 위계를 갖는 요소를 어떻게 표현할 것이냐는 의문이 생기는데
다행히도 좋은 모델(http://mithril.js.org/#dom-elements)이 있다.

그러고보니 여기는 기껏 jsx를 지원한다고 했는데 내가 하려고 하는 건 그 반대;; 역시 변태영감인 것이다.
<div>
  <hr>
  <ul>
    <li>
      First
    <li>
      <p style="font-weight: bold">Second</p>
    </li>
  </ul>
  <div>
    <span>110</span>
    <hr>
  </div>
  <div>
    <label for="message">Message</label>
    <input type="text" id="message">
    <button>click</button>
  </div>
  <p>HMM Taste Good!</p>
</div>
요정도를 표현할 수 있으면 좋겠다 싶어서 뽑아보았다.
React는 CSS를 별도로 작성하는 걸 좋아하지 않아보인다. style도 인라인으로 넣었다.
만일 jade(pug) 라면 마치 CSS Selector 처럼.
div
  hr
  ul
    li First
    li
      p(style='font-weight: bold') Second
  div
    span 110
  div
    label(for="message") Message
    input#message(type="text")
    button click
  p HMM Taste Good!
이렇게 만들 수 있을 것이다.
닫는 태그들을 생략하여 라인 수가 줄어들었고 보기에도 편하다.
아마도 내가 원하는 최종 결과물은 다음과 같을 것이다.
b 'div',
  b 'hr'
  b 'ul',
    b 'li', 'First'
    b 'li',
      b 'p', style: 'font-weight': 'bold', 'Second'
  b 'div',
    b 'span', 110
  b 'div',
    b 'label', 'for': 'message', 'Message'
    b 'input', id: 'message', type: 'text'
    b 'button', 'click'
  p 'HMM Taste Good!'
이렇게 하기 위해 React.createElement를 재정의할 필요가 있다.
React.createElement(https://facebook.github.io/react/docs/react-api.html#createelement)는 세개의 인자를 받는데
https://facebook.github.io/react/docs/react-api.html#createelement
React.createElement(
  type,
  [props],
  [...children]
)
type, props, children 세가지다. (props 는 사실 object 인데 왜 []안에 넣어놨는지 모르겠다;;; )
구조상 인자가 null이 들어가는게 아름답지 않고 children이 array면 []를 써야해서 싫다.
편의상 b(mithril은 m)라고 하고 syntactic sugar를 만들어보자.
b = ->
  a = Array.from arguments
  args=[]
  if a.length is 1
    args=[a[0]]
  else
    if a[1]?.$$typeof
      args=[a[0], null, a[1..]]
    else
      if typeof a[1] isnt 'object'
        args=[a[0], null, a[1]]
      else
        args=[a[0], a[1], a[2] and a[2..]]
  React.createElement.apply React, args
결과물은 대략 이렇다. React.createElement의 반환형이 .$$typeof를 가지고 있는 것으로 구분했다.
https://jsbin.com/xaqorac/edit?html,js,output
내 기준으론 만족스럽고 아름답다.

꼭 coffee를 쓰지 않고 js로 해도
b('div',
  b('hr'),
  b('ul',
    b('li', 'First'),
    b('li',
      b('p', {style: {'font-weight': 'bold'}}, 'Second')
    )
  ),
  b('div',
    b('span', 110)
  ),
  b('div',
    b('label', 'for': 'message', 'Message'),
    b('input', id: 'message', type: 'text'),
    b('button', 'click')
  ),
  p('HMM Taste Good!')
)
https://goo.gl/VFvnDU  decaffeinate (http://decaffeinate-project.org/repl/) 로 돌려서 js 변환해보았다.
충분히 아름답다.

* 추가 수정: 반복되는 요소를 표현할 때 map을 쓰는데 반환형이 array인 경우 처리를 포함해야해서 조금 수정했다. 하위 children들은 n개의 arguments로 확장할 수도 있어서 그냥 property가 없는 경우 null을 끼워넣는 식으로 구현하는게 낫겠다 싶어서 수정.
b = ->
  a = Array.from arguments
  args = a
  args.splice 1,0,null if (a[1]?.$$typeof or
    Array.isArray a[1]) or
    (typeof a[1] isnt 'object')
  React.createElement.apply React, args
좀 더 단순하고 유연한 것 같기도.

당연한 얘기지만 React-Native에서도 쓸 수 있다.
Element를 만드는 부분을 분리한다.
React = require 'react'
module.exports = ->
  a = Array.from arguments
  args = a
  args.splice 1,0,null if (a[1]?.$$typeof or
    Array.isArray a[1]) or
    (typeof a[1] isnt 'object')
  React.createElement.apply React, args
그리고 본체에서 사용한다.
React = require 'react'
{
  AppRegistry
  StyleSheet
  Text
  View
} = require 'react-native'
b = require './src/ceact'
class App extends React.Component
  render: ->
    b View, style: styles.container,
      b Text, "Open up main.js to start working on your app."
      b View,
        b Text, key:v, "#{v}number" for v in [1..5]
styles = StyleSheet.create
  container:
    flex: 1
    backgroundColor: '#fff'
    alignItems: 'center'
    justifyContent: 'center'
AppRegistry.registerComponent 'main', => App
coffee로 쓰면 style이 stylus같이 나와서 좋다.
React Native는 각각의 Component를 구성하는 방식이라 "div" 식으로 태그 이름을 쓸 필요가 없다.
사실 React 보다는 React-Native랑 더 어울린다고 본다.
React = require 'react'
{Component} = React
{
  AppRegistry
  StyleSheet
  Text
  View
} = require 'react-native'
b = require './src/ceact'
class App extends Component
  render: ->
    b View, style: styles.container,
      b Text, "Open up main.js to start working on your app."
      b View,
        b Text, key:v, "#{v}number" for v in [1..5]
styles = StyleSheet.create
  container:
    flex: 1
    backgroundColor: '#fff'
    alignItems: 'center'
    justifyContent: 'center'
AppRegistry.registerComponent 'main', => App
잘 작동한다. "" 없이 element를 쓰니 더 좋다.