Learning Phoenix Framework

Install Elixir

1
2
$ brew update
$ brew install elixir

Install Hex

Package manager for the Erlang ecosystem

Hex는 elixir, erlang 패키지를 관리해주는 시스템이다. node의 npm 이랑 같은거다.

1
$ mix local.hex

Install Phoenix Framework

phoenix를 설치 하기 위해서 아래와 같은 명령어를 입력한다.

1
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

Create Phoenix Project

  • phoenix는 brunch를 사용하고 있는거 같다. 사용하고 싶지 않으면 옵션으로 --no-brunch를 주면 된다.
  • phoenix는 ecto를 사용하고 있는거 같다. 사용하고 싶지 않으면 옵션으로 --no-ecto를 주면 된다.
  • phoenix는 PostgreSQL을 기본으로 사용하게 되어 있는데 MySQL을 사용하고 싶으면 --database mysql 옵션을 추가 해주면 된다.

ecto는 database관리, 쿼리를 위한 라이브러리이다.

1
$ mix phoenix.new project_name --no-brunch --database mysql

Modify database config

config/dev.exs 파일을 열어 보면 아래와 같이 설정이 되어 있는데 환경변수를 사용하기 위해서 다음과 같이 수정 해준다.

1
2
3
4
5
6
config :project_name, Project_name.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "",
  database: "hello_phoenix_dev"
  hostname: "localhost"

database 정보를 수정하기 위해서 config/dev.exs 파일에서 아래와 같은 부분을 수정 해준다.

1
2
3
4
5
6
7
# Configure your database
config :project_name, Project_name.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: System.get_env("DATABASE_USERNAME"),
  password: System.get_env("DATABASE_PASSWOR"),
  database: System.get_env("DATABASE_DB"),
  hostname: System.get_env("DATABASE_HOST"),

Create and migrate your database with

아래와 같은 명령어를 입력하면 database가 생성 되고 마이그레이션을 해준다.

1
$ mix ecto.create && mix ecto.migrate

Start Phoenix

아래와 같은 명령으로 phoenix를 서버를 실행하면 http://localhost:4000로 다음과 같은 화면을 볼 수 있다.

1
$ mix phoenix.server

"start phoenix server"

Run your app inside IEx

만약 IEx (Interactive Elixir)과 함께 서버를 실행하고 싶으면 아래와 같은 명령어로 실행하면 된다.

1
$ iex -S mix phoenix.server

Cocos2d-x 세로 모드

xcode 를 열어서 ios 디렉토리 안에 RootViewController.mm 파일을 열어서 아래 부분을 수정한다.

1
2
3
4
5
6
7
8
9
// 이부분을
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsLandscape( interfaceOrientation );
}

// 이렇게 수정
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsPortrait( interfaceOrientation );
}

자동회전없이 항상 세로화면으로 설정하고 싶으면 아래 부분도 수정한다.

1
2
3
4
5
6
7
8
9
// 이부분을
- (BOOL) shouldAutorotate {
    return YES;
}

// 이렇게 수정
- (BOOL) shouldAutorotate {
    return NO;
}

React 개발환경 설정

요즘 react에 흥미를 느끼면서 이거저거 해보고 공부를 하고 있다.

이전에는 프론트엔드 개발을 하면서 build 만 watch 같은 것들을 따로 설정하지 않고 작업을 했었는데

react를 공부하는 김에 겸사 겸사 공부를 하게 되었다.

기본 디렉토리 구조

디렉토리 구조는 아래와 같다.

src 에는 개발단계에서 파일들을 쪼개서 개발을 하고 webpack 으로 build 한 파일들을 build 디렉토리에 모아둔다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── build
│   ├── bundle.js
│   └── index.html
├── src
│   ├── css
│   │   └── style.css
│   ├── img
│   └── js
│       ├── components
│       └── app.js
├── preprocessor.json
├── package.json
├── gulpfile.js
└── webpack.config.js

webpack

webpack을 사용해서 파일들을 하나의 파일로 만들어준다.

webpack config 파일은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var path = require('path');
var webpack = require('webpack');
var config = {
  entry: {
    src: ['./src/js/app.js']
  },

  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },

  resolve: {
    extensions: ['', '.js', '.jsx']
  },

  module: {
    loaders: [
      { test: /\.js$/, loader: 'jsx-loader!babel-loader' },
      { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  }
};

module.exports = config;

gulp

gulp 를 사용해서 파일이 변경이 되면 빌드를 다시 하고 서버를 다시 시작한다.

gulpfile은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var gulp = require('gulp');
var connect = require('gulp-connect');
var webpack = require('gulp-webpack');
var webpackConfig = require('./webpack.config.js');

// Initialize watch tasks
gulp.task('watch', ['run'], function() {
  gulp.watch(['./src/**/*'], ['build']);
});

// Build files for distribution
gulp.task('build', function() {
  return gulp.src('./src/js/app.js')
      .pipe(webpack(webpackConfig))
      .pipe(gulp.dest('build/'))
      .pipe(connect.reload());
});

// Run example server
gulp.task('run', ['build'], function(){
  connect.server({
    root: './build',
    port: 8080,
    livereload: true
  });
});

gulp.task('default', ['watch']);

React-native 시작하기

설치

1
$ npm install -g react-native-cli

프로젝트 생성

1
$ react-native init testProject

프로젝트 실행

1
2
$ cd testProject
$ open testProject.xcodeproj

위와 같이 명령어를 치면 xcode 에서 testProject가 열립니다.

run 을 실행하면 build 가 되고 터미널 창이 하나 열리면서 서버가 실행이 된다.

그리고 iOS Simulator 이 실행되고 다음과 같은 화면을 확인 할 수 있다.

"react-native run screen shot"

만약 xcode 에서 run 해서 터미널창이 열리는게 싫으면 testProject 디렉토리에서 다음과 같은 명령어로 서버를 실행 할 수 있다.

1
2
3
4
5
$ react-native start

or

$ npm start

Install Elasticsearch

설치

source 설치

1
2
3
$ curl -L -O http://download.elasticsearch.org/PATH/TO/VERSION.zip
$ unzip elasticsearch-$VERSION.zip
$ cd elasticsearch-$VERSION

실행

1
2
3
4
5
6
7
8
9
10
11
12
$ ./bin/elasticsearch
[2014-09-22 23:11:58,615][INFO ][node                     ] [Bevatron] version[1.3.2], pid[62710], build[dee175d/2014-08-13T14:29:30Z]
[2014-09-22 23:11:58,616][INFO ][node                     ] [Bevatron] initializing ...
[2014-09-22 23:11:58,631][INFO ][plugins                  ] [Bevatron] loaded [marvel], sites [marvel, bigdesk, head]
[2014-09-22 23:12:01,453][INFO ][node                     ] [Bevatron] initialized
[2014-09-22 23:12:01,453][INFO ][node                     ] [Bevatron] starting ...
[2014-09-22 23:12:01,577][INFO ][transport                ] [Bevatron] bound_address {inet[/127.0.0.1:9300]}, publish_address {inet[/127.0.0.1:9300]}
[2014-09-22 23:12:01,613][INFO ][discovery                ] [Bevatron] elasticsearch_J2P/fhrTURb8SSywjSVwOj3H8g
[2014-09-22 23:12:04,631][INFO ][cluster.service          ] [Bevatron] new_master [Bevatron][fhrTURb8SSywjSVwOj3H8g][Jung-ui-MacBook-Pro.local][inet[/127.0.0.1:9300]], reason: zen-disco-join (elected_as_master)
[2014-09-22 23:12:04,663][INFO ][http                     ] [Bevatron] bound_address {inet[/127.0.0.1:9200]}, publish_address {inet[/127.0.0.1:9200]}
[2014-09-22 23:12:04,663][INFO ][node                     ] [Bevatron] started
[2014-09-22 23:12:05,312][INFO ][gateway                  ] [Bevatron] recovered [3] indices into cluster_state

elasticsearch 압축을푼 디렉토리 안에 bin 디렉토리에 있는 elasticsearch 파일을 실행한다.

OSX brew install

1
2
3
4
5
6
7
8
9
10
11
12
$ brew update
$ brew install elasticsearch
Data:    /usr/local/var/elasticsearch/elasticsearch_J2P/
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_J2P.log
Plugins: /usr/local/var/lib/elasticsearch/plugins/

To have launchd start elasticsearch at login:
    ln -sfv /usr/local/opt/elasticsearch/*.plist ~/Library/LaunchAgents
Then to load elasticsearch now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
Or, if you don't want/need launchctl, you can just run:
    elasticsearch --config=/usr/local/opt/elasticsearch/config/elasticsearch.yml

실행

1
2
$ ln -sfv /usr/local/opt/elasticsearch/*.plist ~/Library/LaunchAgents
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist

링크를 걸어주고 launchctl 로 실행한다.

Testing

1
$ curl 'http://localhost:9200/?pretty'

위와 같이 curl 로 request 를 날리면 다음과 같은 결과를 볼수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "status" : 200,
  "name" : "Bevatron",
  "version" : {
    "number" : "1.3.2",
    "build_hash" : "dee175dbe2f254f3f26992f5d7591939aaefd12f",
    "build_timestamp" : "2014-08-13T14:29:30Z",
    "build_snapshot" : false,
    "lucene_version" : "4.9"
  },
  "tagline" : "You Know, for Search"
}

How to Install Psycopg2 on Os X

문제

djagno 에서 postgresql을 쓰기 위해서 psycopg2를 install 하려고 했는데 다음과 같은 에러가 발생했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Error: pg_config executable not found.



Please add the directory containing pg_config to the PATH

or specify the full executable path with the option:



    python setup.py build_ext --pg-config /path/to/pg_config build ...



or with the pg_config option in 'setup.cfg'.

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /Users/J2P/.virtualenvs/coupon/build/psycopg2
Storing debug log for failure in /var/folders/m7/mvzr8gc13yb5tk_b2zrqg3l80000gn/T/tmpy7yqQi

해결방법

고맙게도 참 잘 설명해준다.

먼저 pg_config PATH 설정하고 해봤지만 똑같은 오류가 발생해서 그냥 소스를 다운받아서 설치해보기로 했다.

소스를 다운받고 압축을 푼다.

1
2
$ wget http://initd.org/psycopg/tarballs/PSYCOPG-2-5/psycopg2-2.5.3.tar.gz
$ tar xzf psycopg2-2.5.3.tar.gz

압축이 풀린 디렉토리로 이동해서 build, install 을 한다.

pg_config 옵션을 주고 자신의 postgresql 설치되어 있는 곳에 bin/pg_config 파일의 path 를 넣어서 build, install 하면 된다.

나같은 경우엔 brew 로 postgresql을 설치해서 “/usr/local/Cellar/postgresql/9.3.4/bin/pg_config” path 를 넣어주면 됐다.

1
2
3
$ cd psycopg2-2.5.3
$ python setup.py build_ext --pg-config /path/to/pg_config build
$ python setup.py build_ext --pg-config /path/to/pg_config install

Remove Duplication From Python List

프론트 단에서 중복된 데이터를 삭제해서 사용하다가 백엔드에서 처리하고 데이터를 넘겨주는 방식이 더 좋다고 생각해서 Python으로 list에서 중복을 제거 하는 방법을 찾아봤다.

코드는 다음과 같다.

1
2
3
4
5
6
7
8
>>> arr = ['a', 'b', 'c', 'a', 'a', 'b', 'f', 'g', 'c']
>>> set_arr = set(tickets)

>>> print set_arr
>>> set(['a', 'c', 'b', 'g', 'f'])

>>> print list(set_arr)
['a', 'c', 'b', 'g', 'f']

위에서 set 함수가 몬가 하고 알아보니.

순서하고 상관없는 컬렉션(unorderd collection) 이고 집합이기 때문에 중복된 값을 넣을 수 없다는 것을 알게 되었다.

이렇게 중복을 제거 한 다음 다시 list 함수로 감싸주면 배열에서 중복된 값이 제거 list를 얻을 수 있다.

node.js로 Html파일 Indent 적용해서 파일 생성하기

간단하게 파일을 쓰려고 할때 다음과 같이 사용하면 된다.

1
2
3
4
fs.writeFile('파일명', '내용', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
});

근데 html 파일을 작성할때 내용을 자동으로 indent 해주는 모듈은 없을라나… 했는데

htmltidy

요런게 있구나~

1
2
3
4
5
6
7
8
9
10
11
12
var tidy = require('htmltidy').tidy;
var opts = {
  doctype: 'html5',
    indent: true
};
tidy('<ul><li>1</li><li>2</li><li>3</li></ul>, opts, function(err, html) {
 if (err) throw err;
 fs.writeFile('index.html', html, function (err) {
     if (err) throw err;
     console.log('It\'s saved!');
  });
});

요렇게 해주면 index.html 파일에 이쁘게 indent 가 적용되서 저장된다~ 좋은데?!

node.js로 크롤링해서 메일로 보내주기.

우리 회사는 교육 부분에 많은 서비스 하고 있다.

Google Play 교육 카테고리 최고 매출 순위를 보는 회사분들이 있는거 같다.

그래서 크롤링해서 메일로 보내주는 스크립트를 node.js로 한번 만들어 봤다.

크롤링은 jsdom 모듈을 사용하고, jsdom

메일 보내기는 nodemailer를 사용하였다. nodemailer

코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var fs = require('fs');
var jsdom = require('jsdom');
var nodemailer = require('nodemailer');

var smtpTransport = nodemailer.createTransport('SMTP', {
  service: 'Gmail',
  auth: {
    user: '구글 메일 주소',
    pass: '구글 메일 패스워드'
  }
});

function sendMail(mailOptions) {
  smtpTransport.sendMail(mailOptions, function (error, response) {
    if (error) {
      console.log(error);
    } else {
      console.log('Message sent :' + response.message);
    }

    smtpTransport.close();
  });
}

(function getMailOptions() {
  jsdom.env({
      url: 'https://play.google.com/store/apps/category/EDUCATION/collection/topgrossing',
      scripts: ['http://code.jquery.com/jquery-2.1.0.min.js'],
      done: function (err, window) {
          var $ = window.jQuery;
          var cards = $('.card-list .card');
          var result = '<div style="width: 100%; font-size: 16px;">';
              result += '<h1 style="font-size: 0.875em;">Google Play 최고 매출 - 교육</h1>';
              result += '<ul style="width: 100%; list-style:none; margin: 0 auto; padding: 0; overflow: hidden; position: relative;">';

          $.each(cards, function(i, card) {
            var $card = $(this);
            var cover = $card.find('.cover-image').attr('src');
            var title = $card.find('h2').text();
            var subtitle = $card.find('.subtitle').text();
            var link = $card.find('h2 a').attr('href');

            ++i;

            result += '<li style="border: 1px solid #ccc;padding: 0;margin: 5px; width: 15%; text-align: center; float: left; border-radius: 5px;">';
            result += '<h2 style="background: #ccc; margin: 0 0 5% 0; padding: 2% 0; font-size: 0.813em;">'+ i +'위</h2>'
            result += '<img src="'+ cover +'" alt="'+ title +'" style="width: 80%; height: auto;">';
            result += '<p style="word-wrap:break-word; width: 70%; height: 10%; font-size: 0.750em; max-height: 50px; overflow: hidden; margin: 2% auto; 0">';
            result += '<a href="'+ link +'">'+ title +'</a></p>';
            result += '<p style="word-wrap;break-word; font-size: 0.623em; color: #ccc;">'+ subtitle +'</p>';
          });

          result += '</ul></div>'

          // 파일로 저장해서 미리보기
          fs.writeFile('index.html', result, function (err) {
            if (err) throw err;
            console.log('It\'s saved!');
          });

          // 메일 보내기
          sendMail({
            from: '보내는사람 mail',
            to: '받는사람 mail',
            subject: '제목',
            html: result
          });
      }
  });
})();

html을 꾸며준답시고 css를 쑤셔 넣어서 코드가 좀 지저분 하다 ㅋㅋ

node.js 환경에 맞춰서 Config 불러오는 방법

development 환경 실행

1
$ NODE_ENV=development node app.js

production 환경 실행

1
$ NODE_ENV=production node app.js

config.js

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = function(){
    switch(process.env.NODE_ENV){
        case 'development':
            return {dev setting};

        case 'production':
            return {prod settings};

        default:
            return {error or other settings};
    }
};

사용

1
2
var Config = require('./config'),
    conf = new Config();