
Socket.IO:マルチプロセスのnode.jsで、Roomに入室したクライアント一覧を取得する
- 2015/01/16
- suganuma
チャット等で、Roomに誰が入室しているのかを知る際の方法です。
Roomに入室したクライアント(socket id)一覧の取得は、socket.io 1.0以降では以下で取得できます。
io.nsps[yourNamespace].adapter.rooms[roomName]
ただしnode.jsをマルチプロセスで実行している場合、自プロセス以外のクライアント一覧は取得できないです。
そのためRedisのKVSを使い、以下のようにしました。
1.Roomに入室:
RedisのKVSに、各プロセス毎に、各ルームに入室したクライアント(socket id)一覧をセット
redisClient = redis.createClient(redisPort, redisIp, {})
process_name = localIp + 'p' + port # プロセス名:プロセスのIPとポート番号を結合した文字列
#
# socketをroomにjoinし、且つRedisにsetする
#
join = (socket, room)->;
socket.join room # socketをroomにjoin
key = room + ':' + process_name
value = []
if io.of('/').adapter.rooms[room]?
for socketId of io.of('/').adapter.rooms[room]
value.push socketId
redisClient.set key, JSON.stringify(value) # JSONの文字列として値をセット
roomName = 'works#' + work_id
join socket, roomName
下記のような感じでRedisにセットされます。
redis 127.0.0.1:6379[1]> keys *
1) "users#558:10.0.2.15p1338"
2) "works#1016:10.0.2.15p1337"
3) "works#1016:10.0.2.15p1338"
4) "users#10:10.0.2.15p1337"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1337
"[\"1ZFRYWVeyrMPaiqOAAAD\"]"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1338
"[\"vuturxDpPBL2ZHuLAAAK\"]"
2.クライアント一覧を取得:
RedisのKVSからワイルドカードを使ったkeysコマンドで該当するキーをすべて取得後、mgetコマンドで値を取得
#
# roomIDからsocket.idのリストを取得し、コールバック関数実行
#
clients = (room, cb, opts) ->
key = room + ':*'
redisClient.keys key, (err, replies)->
redisClient.mget replies, (err, vals)->
socketIds = []
for val in vals
socketIds = socketIds.concat JSON.parse val
cb(socketIds, opts)
roomName = 'works#' + work_id
clients roomName, (socketIds)->
console.log 'Roomに入室しているクライアント一覧:' + socketIds.join(',')
3.Roomから退室:
RedisのKVSの値を変更または削除
#
# socketをroomからleaveし、且つredisにsetする
#
leave = (socket, room)->
socket.leave room
key = room + ':' + process_name
value = []
if io.of('/').adapter.rooms[room]?
for socketId of io.of('/').adapter.rooms[room]
value.push socketId
if value.length > 0
redisClient.set key, JSON.stringify(value)
else
redisClient.del key
roomName = 'works#' + work_id
leave socket, roomName
一応これで実現可能ですが、クライアント一覧取得するためだけにredisのKVSを使うというのは、無駄にリソースを使っている感じであまりいい方法ではないように思います。
また、以下のような感じでプロセス終了時にredisのデータを全て削除する必要があります。
for signal in ['SIGINT', 'SIGHUP', 'SIGTERM']
process.on signal, ->
key = '*:' + process_name
redisClient.keys key, (err, replies)->
async.each replies, (reply)->
redisClient.del reply
process.exit 1
return
もっといい方法があれば、誰が教えて頂けたらと思います。






RoundCube1.0で追加されていたCanned Responses
- 2015/01/16
- aikawa

Android-ProgressDialogFragmentというライブラリを作りました。
- 2015/01/06
- uchida
1.Android-ProgressDialogFragmentというライブラリを作りました。
https://github.com/YuichiUchida/Android-ProgressDialogFragment
ロード中はこういう感じになります。
2.今まではこういう感じに作っていました。
ListFragmentなどがこういう実装になっていますね。
この方式は下の画像のように2つの画面を重ね
ロード中にはコンテンツ部分を消し、プログレス画面を表示し、
ロードが終るとコンテンツを表示し、プログレス画面を消すという方式になっています。
長所としては、できあがった奇麗な画面だけを表示できることですが
たくさんの画面に使うと動作が重く感じるようになってきます。
このライブラリは、やり方はほとんど同じですが
コンテンツ部分を常に表示し、プログレス画面だけを制御します。
3.使い方
Gradleに下記のように追記します。
support-v4ライブラリを使う場合
dependencies {
compile 'me.yuichi0301:pdfragment:1.1.+'
}
ライブラリを使用しない場合
dependencies {
compile 'me.yuichi0301:pdfragment-native:1.1.+'
}
あとはProgressDialogFragment継承して使用します。
public class SampleFragment extends ProgressDialogFragment {
// your code of fragment
}
ActionBarSherlockを使う場合
dependencies {
compile 'com.android.support:support-v4:21.0.3'
compile('me.yuichi0301:sherlockpdfragment:1.1.+') {
exclude module: 'support-v4'
}
}
public class SampleFragment extends SherlockProgressDialogFragment {
// your code of fragment
}






Mac基本のキ:アプリを32ビットモードで起動する
- 2015/01/05
- murave
@murave 「32ビットモードで開く」で回避(とりあえず)。Thunderbird.appを右クリックして「情報を見る」から設定。
— murave (@murave) January 5, 2015
仕事始めの朝に食らいまいした。あけましておめでとうございます。
今回のように64ビットアプリでトラブルにあったときに32ビットモードで起動すると回避出来たりすることがありますので覚えておくといいことがあるかもしれません。
そして忘れてずっと32ビットモードで暮らしてしまうかもしれません。使えないよりはいいですけどね。
設定方法はつぶやいてるとおりですが、わかりやすいようにキャプチャとってみました。
右クリックして「情報を見る」
そして「32ビットモードで開く」にチェック。






雑用係、来年もよろしくお願いしますm(_ _)m
- 2014/12/26
- Kumiko.S

Jenkinsでphpcsする。
1.今回は、Jenkinsでphpcsを使う手順を書いていきます。
最近CakePHPの仕事をやっているのでcakephp-codesnifferを使用します。
2.phing、phpcs、cakephp-codesnifferのインストール
とりあえず、以下の作業はjenkinsユーザーで行いました。
phing、phpcsのインストール
phpcsはphingと一緒に入ります。
composer global require 'phing/phing:2.*'
cakephp-codesnifferのインストール
composer global require 'cakephp/cakephp-codesniffer'
全ユーザーでコマンドを使えるように/etc/profileに以下の行を追加
(composer globalだと対象ユーザーのホームの.composerにインストールされます。)
export PATH="$PATH:/var/lib/jenkins/.composer/vendor/bin"
phpcsでcakephp-codesnifferを使えるようにします。
/var/lib/jenkins/.composer/vendor/bin/phpcs --config-set installed_paths /var/lib/jenkins/.composer/vendor/cakephp/cakephp-codesniffer
以下のコマンドでCakePHPが表示されればOKです。
phpcs -i
3.phingの設定
Jenkinsの管理→システムの設定を押下
Phingの追加を押下し、以下のように入力します。
4.Jenkinsのプラグインのインストール
以下のプラグインをインストールします。
Checkstyle Plugin
Phing Plugin
PHP Plugin
Git Plugin
5.ジョブの実行手順
①gitでプロジェクトを取得
②phingでphpcsを実行
プロジェクトの構成は以下のようになります。
appがCakePHPのプロジェクトディレクトリで
build.xmlがphingのbuildファイル
buildディレクトリにphpcsの設定ファイルを入れています。
phingのbuildファイルは以下のようにしました。
build.xml
上記の設定の場合次のコマンドを実行するのと同じになります。
phpcs --standard=${basedir}/build/phpcs.xml --report=checkstyle --report-file=${logdir}/phpcs-report.xml ${appdir}
変数を展開すると以下のような感じ
phpcs --standard=/var/lib/jenkins/jobs/phpcs-test/workspace/build/phpcs.xml --report=checkstyle --report-file=/var/lib/jenkins/jobs/phpcs-test/workspace/build/logs/phpcs-report.xml /var/lib/jenkins/jobs/phpcs-test/workspace
phpcsの設定ファイルは以下のようにしました。
phpcs.xml
*database.php
*i18n.php
*core.php
*/app/Config/*
*/app/Console/*
*/app/Lib/*
*/app/Plugin/*
*/app/tmp/*
*/app/Vendor/*
*/app/webroot/*
*/app/Test/*
*/app/View/Helper/*
6.新規ジョブの作成
新規ジョブ作成を押下します。
ジョブ名を入力し、「フリースタイル・プロジェクトのビルド」を選択します。
ソースコード管理を設定後、ビルド手順の追加で「Phingの呼出し」を選択します。
使用するPhingでmyphingを選択
Phingの呼出しに「phpcs」と入力します。
設定ファイルとの関連は次のようになります。
7.試してみる
以下のようにメソッドを追加してビルドを試してみます。
log('hoge', LOG_DEBUG);
}
}
コメントでhogeと@return voidの間に1行必要
hoge(){でhoge()と{の間にスペースが必要とでています。
難しい・・。
8.sublime-phpcsを使ってみる
package controllでPhpcsをインストールします。
ローカルで以下のコマンドを実行します。
composer global require 'phing/phing:2.*'
composer global require 'cakephp/cakephp-codesniffer'
~/.composer/vendor/bin/vendor/bin/phpcs --config-set installed_paths ~/.composer/vendor/bin/vendor/cakephp/cakephp-codesniffer
Preferences→Package Settings→PHP Code Sniffer→Settings – Userと開きます。
以下のように記述します。
{
"phpcs_executable_path": "/ユーザーのホームディレクトリ/.composer/vendor/bin/vendor/bin/phpcs",
"phpcs_additional_args": {
"--standard": "CakePHP"
},
}
これでだめなところを表示してくれるようになりました。






iOSでプッシュ通知時の挙動を確認したい
- 2014/11/30
- haraguchi
- push Notification
今回はiOSアプリを開発している際にプッシュ通知時の挙動を確認したい時に使えそうな「houston」というライブラリを紹介したいと思います。
1. インストール
gem install houston
2. 送信用のrubyファイルを用意
ほぼREADMEにあったサンプルの通りのものです。
また、事前に開発用の「push_dev.pem」を同じディレクトリに置いています。
push.ruby
require 'houston'
# Environment variables are automatically read, or can be overridden by any specified options. You can also
# conveniently use `Houston::Client.development` or `Houston::Client.production`.
APN = Houston::Client.development
APN.certificate = File.read("push_dev.pem")
# An example of the token sent back when a device registers for notifications
token = "<《ここにプッシュ通知を送信したい端末のデバイストークンを指定します》>"
# Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
notification = Houston::Notification.new(device: token)
notification.alert = "ハロー, ワールド!"
# Notifications can also change the badge count, have a custom sound, have a category identifier, indicate available Newsstand content, or pass along arbitrary data.
notification.badge = 1
notification.sound = "sosumi.aiff"
notification.category = "INVITE_CATEGORY"
notification.content_available = true
notification.custom_data = {foo: "bar"}
# And... sent! That's all it takes.
APN.push(notification)
3. push通知を送信
ruby push.ruby
以上で、指定した端末にプッシュ通知が飛びます。
READMEにはデバイストークンの取得方法や証明書の作成方法なども載っておりすごく分かりやすいです。
この他にもコマンドラインからプッシュ通知を送信することが出来るようです。
apn push "<デバイストークン>" -c /path/to/apple_push_notification.pem -m "Hello from the command line!"






GradleでAndroid部品を社内共有しよう!
- 2014/11/30
- uchida
1.はじめに
Androidの開発をやっていると、前のプロジェクトで使った部品を次のプロジェクトでも
使いたいなんてことがでてくると思います。しかし、公開されているリポジトリに
登録できるような内容の部品じゃないということはよくあると思います。
ということで、社内のリポジトリにGradleでAndroid部品を共有する内容を書いていきます。
2.手順
①gitlabにリポジトリを作成(社内ではgitlabを使っているため)
②ライブラリプロジェクトを作成し、AARをリポジトリに登録
③②のライブラリを使うプロジェクトの作成
3.gitlabにリポジトリを作成
テスト的にこういう画像のカスタムビューを持つライブラリにします。
ということでプロジェクト名は「Android-BaseLine」にしました。
4.BaseLineライブラリを作成します。
EclipseだとAARが使えないためAndroid Studioを使用します。
5.BaseLineViewはこんな感じ
package com.lancard.baseline;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class BaseLineView extends View {
public BaseLineView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.rgb(0, 49, 236));
//paint.setStrokeWidth(5);
int height = getHeight();
int width = getWidth();
int y = 0;
while(y
6.AAR作成用にbuild.gradleを書きます。
ここのbuild.gradleになります。

元のソース
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "com.lancard.baseline"
minSdkVersion 18
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
次のように書き換えます。
apply plugin: 'android-library'
apply plugin: 'maven-publish'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "com.lancard.baseline"
minSdkVersion 18
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android.libraryVariants
publishing {
publications {
maven(MavenPublication) {
groupId 'com.lancard.lib'
artifactId 'baseline-aar'
version '1.0'
artifact source: file("${project.buildDir}/outputs/aar/${project.name}-release.aar")
}
}
repositories {
maven {
url "file:${projectDir}/maven-repo"
}
}
}
<やっていること>
①apply plugin: 'com.android.application'→apply plugin: 'android-library'で
アプリケーションプロジェクトからライブラリプロジェクトに
②apply plugin: 'maven-publish'を追加しMaven Publishプラグインを使えるようにする。
③ publishingを追加し、公開するファイルの設定をします。
(ちなみにURLはリポジトリ管理ツールで運用されていれば
直接転送できgit pushする必要はありません。)
7.Gradleのタスクを実行します。
Maven Publishプラグインを使用すると、「maven(MavenPublication) 」の名前から
publishMavenPublicationToMavenRepositoryというタスクができるので実行します。

以下のようにmaven-repoというディレクトリができますので全て
3.で作成したgitリポジトリにpushします。

8.BaseLineViewを使うプロジェクトを作成し、build.gradleを以下のように追記し、buildタスクを実行します。
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "com.lancard.hoge"
minSdkVersion 18
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
repositories {
maven {
url "https://{社内gitlab}/{username}/android-baseline/raw/master/maven-repo"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.lancard.lib:baseline-aar:1.0'
}
lintはとりあえず、オフにしています。
9.以下のように「com.lancard.baseline.BaseLineView」を使用するxmlを書いて実行できれば成功です。
10.バージョンがあがっていくとリポジトリはこのようになります。
11.SSLクライアント認証している場合
社内のリポジトリがSSLクライアント認証している場合が多いと思います。
SSLクライアント認証している場合は、gradleラッパーのJVMオプションで
証明書のパスを設定する必要があります。
プロジェクト配下にあるgradlewのDEFAULT_JVM_OPTSを以下のように書き換えます。
DEFAULT_JVM_OPTS=" \
-Djavax.net.ssl.keyStore=証明書のパス \
-Djavax.net.ssl.keyStoreType=pkcs12 \
-Djavax.net.ssl.keyStorePassword=証明書のパスワード \
"
あとはgradleコマンドではなくgradlewを実行するようにします。
./gradlew build





雑用係、OSX Yosemiteインストールに苦戦する
- 2014/11/28
- Kumiko.S
雑用係、生意気にもMac使いです(´∀`)
※ 職場の机はMacBookAir11と私物のiPadAirでこんな感じ。自宅はPro15。某マンガ&アニメキャラは無視してね。
もちろん、Yosemiteがリリースされてからサクッとアップデートしました。
んまー、フラットデザインけっこーイケるわぁーなんてホクホクしてました。
でもって、あまり機械モノが得意じゃない友人にも毎度のことと「とりあえず無料だから、今回もちゃんとアップデートしとけよ〜」と言ってしまったのが、題目の原因です(×_×)
メッセがきました。
友人A「インストール失敗しました、って、でるんだけど」
でるんだけど、と言われましても、失敗したことないので、雑用係には原因が全くわかりません。そもそも、私は単なる雑用係であって我が社の精鋭技術者のようなスキルはないんですYO!
メッセや電話で、どういう状況なのか、あれやってみてー、これやってみてーとやれども、同様の状況になったことのない雑用係は、何が一体どうなってるのかサッパリぽんです(´・ω・`)
仕方ないので、休みの日に友人Aの家へ出向きました。
奮闘の始まりです(´д`)






Node.jsを使ってチャット
Node.jsを使ってSocket.ioを用いたチャットルームを作成したいと思います
せっかくなので、前回紹介したYeomanを使って作成
generator-socketioというジェネレータがあるようなのでコレを使います。
まずはジェネレータのインストール
XXXXX:chat XXXXX$ sudo npm install -g generator-socketio
Password:
実行するディレクトリをつくったらyoで実行
今回はbootstrap使用、スタイルシートはCSSを使うことにします
XXXXX:workspace XXXXX$ mkdir chat
XXXXX:workspace XXXXX$ cd chat
XXXXX:chat XXXXX$ yo socketio
_-----_
| |
|--(o)--| .--------------------------.
`---------´ | Welcome to Yeoman, |
( _´U`_ ) | ladies and gentlemen! |
/___A___\ '__________________________'
| ~ |
__'.___.'__
´ ` |° ´ Y `
[?] What do you want to call this project? chat
[?] Write a brief description.
[?] What port number would you like to run on? 1337
chat
to run on port - 1337
[?] Do you want to use Bootstrap? Yes
true
[?] In what format would you like the Bootstrap stylesheets? css
css
create server.js
create views/index.ejs
create public/js/app.js
create Gruntfile.js
create package.json
create bower.json
create .editorconfig
create .jshintrc
create .bowerrc
create .gitignore
~~~
あとはgruntで実行するだけ。あーなんて楽ちん…
XXXXX:chat XXXXX$ grunt
あーbootstrap入れてなかった
Gruntfile.jsを確認した所、public/bowser_componentsにbootstrapファイルを
入れるといいみたいですね
XXXXX:chat XXXXX$ vi Gruntfile.js
~~~
cssmin: {
combine: {
files: {
'public/css/core.css': 'public/bower_components/bootstrap.css/css/bootstrap.css'
~~~
publicにbower_componentsディレクトリを作成し、公式サイトでDLしたbootstrapファイルを入れます
XXXXX:public XXXXX$ mkdir bower_components/
XXXXX:public XXXXX$ mkdir bower_components/bootstrap.css/
もう一回gruntで実行
おおーうまく行った!
すでに基本的なチャット機能が入っているようで、
もう一つブラウザを開いて文章のやりとりをしてみるときちんと動きます
せっかくなので、少し編集を加えてチャット部屋を用意してみることにします
TOP(localhost:1337)にアクセスしたらランダムなユーザ名と部屋IDを作成し、
各部屋(localhost:1337/id)に飛ぶイメージです
以下編集後
server.js 38行目〜
/*************************************
//
// chat app
//
**************************************/
// express magic
var express = require('express');
var app = express();
var server = require('http').createServer(app)
var io = require('socket.io').listen(server);
var device = require('express-device');
var runningPortNumber = process.env.PORT;
app.configure(function(){
// I need to access everything in '/public' directly
app.use(express.static(__dirname + '/public'));
//set the view engine
app.set('view engine', 'ejs');
app.set('views', __dirname +'/views');
app.use(device.capture());
});
// logs every request
app.use(function(req, res, next){
// output every request in the array
console.log({method:req.method, url: req.url, device: req.device});
// goes onto the next function in line
next();
});
app.get("/", function(req, res){
res.redirect('/'+ random_string(12));
});
app.get('/:room', function(req, res) {
res.render('index', {room: req.params.room, name: random_string(5)});
});
io.sockets.on('connection', function (socket) {
var room; // 部屋ID
var name; // ユーザ名
socket.emit('connected');
socket.on('init', function(data) {
room = data.room;
name = data.name;
socket.join(room);
socket.broadcast.to(room).emit('blast', {msg:""+ name +" connected"});
});
socket.on('blast', function(data, fn){
console.log(data);
io.sockets.to(room).emit('blast', {msg:data.msg});
fn();//call the client back to clear out the field
});
socket.on('disconnect', function() {
socket.broadcast.to(room).emit('blast', {msg:""+ name +" disconnected"});
socket.leave(room);
});
});
server.listen(runningPortNumber);
/**
* ランダムな文字列を返す
*/
function random_string(len) {
var base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
base = base.split('');
var str = '';
var count = base.length;
for(var i=0; i
index.ejsはnameとroomを追加しただけ
views/index.ejs 52行目〜
app.jsも同じくnameとroom追加
public/js/app.js 13行目〜
// shortcut for document.ready
$(function(){
//setup some common vars
var $blastField = $('#blast'),
$allPostsTextArea = $('#allPosts'),
$clearAllPosts = $('#clearAllPosts'),
$sendBlastButton = $('#send'),
$nameField = $('#name'),
$roomField = $('#room');
socket.emit('init', {name:$nameField.val(), room:$roomField.val()});
$sendBlastButton.click(function(e){
var blast = $blastField.val();
var name = $nameField.val();
if(blast.length){
socket.emit("blast", {msg:name +': '+ blast},
function(data){
$blastField.val('');
});
}
});
ここまで編集したらもう一回grunt実行
自動的に部屋へジャンプするようになりました
もう一つウィンドウを開き、同じURLにアクセスした後、
元の画面で確認すると部屋に別の人が入室したことを表示してくれています
もちろんチャットも出来ますし
ついでに退出を知らせる機能もあります
ウィンドウを消すと"●●● disconnected"と表示されることが確認できます
以上。らくちん。



