0

HerokuでMongoDB+Sinatra

試しにメモ帳作った。
http://shokai-memo.herokuapp.com

ソースコード https://github.com/shokai/heroku-sinatra-mongo-memo


MongoHQMongoLabを使う。

MongoHQはHerokuが管理してるっぽくて16MBまで無料。MongoLabはObjectLabs Corporationがやってて240MBまで無料らしい。
MongoLabの方が明らかに得だけど、容量を2GBまで増やすとMongoHQの方が安くなる。


Herokuのプロジェクト作る

git init
heroku create --stack cedar


MongoDBのアドオンを有効にする

とりあえず無料プランで、どちらかをaddする
heroku addons:add mongolab:starter
heroku addons:add mongohq:free

するとMONGOLAB_URIかMONGOHQ_URLという環境変数が増えるので、確かめる
heroku config


RubyではMongoid2.4をMongoのラッパーとして使うのが良いと思う。
Herokuにデプロイしたら環境変数にMongoDBのアドレスとパスワード等が入った状態でSinatraが起動するので、読み込ませる
Mongoid.configure do |conf|
conf.from_hash {'uri' => ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL']}
end


最終的にこうなった。
mongoidは普通mongoid.ymlから設定を読む。
MongoLabやMongoHQの接続設定が環境変数にあったらそっちを使うし、無ければmongoid.ymlを読んでローカルのMongoDBに接続する。
Mongoid.logger.level =
case ENV['RACK_ENV']
when 'production'
Logger::WARN
else
Logger::DEBUG
end

Mongoid.configure do |conf|
h = {'uri' => ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL']}
unless h['uri']
yaml = YAML.load(open(File.expand_path 'mongoid.yml', File.dirname(__FILE__)).read)
h = yaml[ ENV['RACK_ENV'] || 'development' ]
end
conf.from_hash h
end



メモ帳作る

Memo modelを定義する
class Memo
include Mongoid::Document
field :created_at, :type => Time, :default => lambda{ Time.now }
field :body, :type => String, :default => ""

def self.find_by_id(id)
self.where(:_id => id).first
end

def self.latests(num=10)
self.all.desc(:created_at).limit(num)
end

def to_s
"#{body} - #{created_at}"
end
end


適当にSinatraで書いて完成。
get '/' do
mems = Memo.latests(100).map{|m|
"<p>#{Rack::Utils.escape_html m.body} - <a href='/#{m.id}'>#{m.created_at}<a><p>"
}.join('')

"<html><form method='POST' action='/'><input type='text' name='body' size=70 /><input type='submit' /></form>#{mems}</html>"
end

post '/' do
m = Memo.new :body => params[:body]
m.save!
redirect '/'
end

get '/:id' do
m = Memo.find_by_id params[:id]
halt 404, 'not found' unless m
"<html><p><a href='/'>top</a></p><p>#{Rack::Utils.escape_html m.to_s}</p></html>"
end


ローカルのRubyからHerokuのMongoDBに接続する


手元で起動したSinatraも、環境変数さえセットされていればHerokuのMongoDBに接続される
heroku config --shell | ruby -lane 'puts "export "+$_'
でてきたMONGOLAB_URIとかをターミナルにコピペすれば環境変数をセットできる。


管理画面

https://api.heroku.com/myappsからアプリを選んで、右上のaddonsからMongoLab/MongoHQの管理画面に行ける。

0

エゴサーチツール feedim

エゴサーチツール feedimを作った。3ヶ月ぐらい使い続けている。
以前はtwitter検索で “shokai” とか検索した結果をRSSリーダーで読んでいたんだけど、最近ビリケン商会とか大塚商会とかキラキラ商会とか、 “shokai” をユーザ名やURLに含むtweetが増えてきたし、邪魔なbotからのtweetも除外したいのでなんとかするツールを作った。

で、feedを吐くのもいいけどせっかくAndroid持っているから、im.kayac.comを使ってAndroidにpush通知するようにした。
im.kayac.comを使っているのでGoogle TalkやiPhoneのpush通知でも受信できる。

主にtwitterで使っているけど、feedなら何でも定期的に監視できる。
ソースはgithubにある github.com/shokai/feedim


先にim.kayac.comでユーザ登録して通知先を設定しておいてください


■インストール

git clone git://github.com/shokai/feedim.git


■必要なもの
mongodb 1.6以上が必要。起動しておく。

必要なrubygemsは、bundlerで入れる。

cd feedim
gem install bundler
bundle install
mongoid2betaとか、俺が以前作ったim-kayacのgemとかがインストールされる。


■設定
config.yamlファイルを作る。

cp sample.config.yaml config.yaml
config.yamlファイルを編集する。上の方でim.kayac.comのユーザ名とか、認証タイプを選ぶ。MongoDBのDB名とかも選ぶ。



監視するfeedを列挙する。除外したい内容をfilterに正規表現で書く。
filterは本文とURL両方にかけるfilterで、description_filterとurl_filterはそれぞれ本文とURLどちらかにかけるフィルタ。
# list of feeds
feeds :
- "http://search.twitter.com/search.rss?q=shokai"
- "http://search.twitter.com/search.rss?q=%E6%A9%8B%E6%9C%AC%E5%95%86%E4%BC%9A"
- "http://search.twitter.com/search.rss?q=%E6%A9%8B%E6%9C%AC+%E7%BF%94"
- "http://search.twitter.com/search.rss?q=%E3%81%8B%E3%81%9A%E5%8A%A9"
- "http://search.twitter.com/search.rss?q=%E3%81%8B%E3%81%9A%E3%81%99%E3%81%91"
- "http://search.twitter.com/search.rss?q=%E3%83%8F%E3%82%B7%E3%83%A2%E3%83%86%E3%82%A3%E3%82%A6%E3%82%B9"

# filter by "url" and "description" property of entries
filters :
- "honeybee-cd"

# filter by "description" property of entries
description_filters:
- "_shokai"
- "shokai_"
- "\-shokai"
- "shokai\-"
- "shokai\.co"
- "shokai\d"
- "\dshokai"
- "キラキラ商会"
- "ビリケン"
- "大塚商会"

# filter by "url" property of entries
url_filters :
- "twitter\.com\/shokai\/"
- "twitter\.com\/shokai_log\/"
- "bot"
- "kirakira"
こんな感じに書くと、ほぼ橋本商会とかshokaiは全部漏らさずに、ビリケン商会とか大塚商会とかキラキラ商会とか、邪魔なbotを除外できる。



■動かす
クロールして、IMとAndroidに送る。
ruby store.rb
ruby publish.rb

crontabで10分おきに実行している。

0

mongoid使ってみる

mongo単体で使ってみててだいたい分かったので、mongoidというmapperを使ってみる。

mongoidの良いのは

  • default値を入れておきたい場合も簡単に書ける。created_atとか。
  • _idでdocumentを取り出すとき、素のmongoだとcollection.find_one(BSON::ObjectID(id))とかしないとならないけどmongoidだと_idに文字列でID入れればいい
とかがぱっと使ってみて思った。。
そもそもこういうのmongoの機能にあるかもしれないけど。

■ドキュメント
■インストール
sudo gem install mongoid
1.9.1を使う。–pre付けるとRails3対応の2.x系統が入る。

■modelを作る
適当にperson class作って、Mongoid::Documentにする

person.rb
require 'rubygems'

class Person
include Mongoid::Document
field :fullname # 指定無しでtype=>stringになる
field :username
field :age, :type => Integer
field :created_at, :type => DateTime, :default => lambda{Time.now}
end
string以外は型指定する。型はArray, BigDecimal, Boolean, Date, DateTime, Float, Integer, String, Symbol, Timeがある。
Mongoid Documentation: Documents

defaultで現在時刻を入れるようにした。

■mongodbへ接続
Mongoid.configureのブロック内で接続する。
conf.masterに普通のmongoで接続してdbを指定した時の返り値(Mongo::DBオブジェクト)を与えれば、mongoidで使える。
require&nbsp;'rubygems'
require&nbsp;'mongoid'
require&nbsp;File.dirname(__FILE__)+'/person'

Mongoid.configure&nbsp;do&nbsp;|conf|
&nbsp;&nbsp;conf.master&nbsp;=&nbsp;Mongo::Connection.new('localhost',&nbsp;27017).db('mongoid-test')
end
■modelの操作
新しいpersonオブジェクト作って保存
person = Person.new(:fullname => 'sho hashimoto',
:username => 'shokai',
:age => 25)

puts person.fullname
puts person.age

person.save
保存されてるか、mongoのコンソールで確かめる
personで保存したら、自動的に複数形のpeopleになってた。ActiveRecordっぽい。
% mongo
MongoDB&nbsp;shell&nbsp;version:&nbsp;1.4.4
url:&nbsp;test
connecting&nbsp;to:&nbsp;test
type&nbsp;"help"&nbsp;for&nbsp;help
>&nbsp;show&nbsp;dbs
admin
chirpstream_shokai
local
mongoid-test
people
povietest
test
testdb
>&nbsp;use&nbsp;mongoid-test
switched&nbsp;to&nbsp;db&nbsp;mongoid-test
>&nbsp;show&nbsp;collections
people
system.indexes
>&nbsp;db.people.find()
{&nbsp;"_id"&nbsp;:&nbsp;"4c61463c2f7306e9fe000001",&nbsp;"created_at"&nbsp;:&nbsp;"Tue&nbsp;Aug&nbsp;10&nbsp;2010&nbsp;21:29:48&nbsp;GMT+0900&nbsp;(JST)",&nbsp;"fullname"&nbsp;:&nbsp;"sho&nbsp;hashimoto",&nbsp;"username"&nbsp;:&nbsp;"shokai",&nbsp;"age"&nbsp;:&nbsp;25&nbsp;}
{&nbsp;"_id"&nbsp;:&nbsp;"4c614d652f73060653000001",&nbsp;"created_at"&nbsp;:&nbsp;"Tue&nbsp;Aug&nbsp;10&nbsp;2010&nbsp;22:00:21&nbsp;GMT+0900&nbsp;(JST)",&nbsp;"fullname"&nbsp;:&nbsp;"sho&nbsp;hashimoto",&nbsp;"username"&nbsp;:&nbsp;"shokai",&nbsp;"age"&nbsp;:&nbsp;25&nbsp;}
2回保存したから複数保存されてた

■find
探す。Mongoid Documentation: Queryingにqueryの書き方が載ってる。
適当にユーザ名shokaiの最初の一件を取得して、表示する
person = Person.first(:conditions => {:username => 'shokai'})
puts person._id
puts person.username
puts person.created_at
他にも、全件とか色々な書き方ができる。
person = Person.find(:first, :conditions => {:username => 'shokai'})
person = Person.all(:conditions => {:username => 'shokai'}).first
person = Person.first(:conditions => {:_id => '4c61463c2f7306e9fe000001'})
person = Person.where(:username => 'shokai').first
allで検索したら結果が1件しか無くても、collectionで返ってくる。eachで回せる。

■modelに書いてない値を入れてみる
modelにない、person.placeを入れてみる
person = Person.new(:fullname => 'sho hashimoto',
:username => 'shokai',
:age => 25,
:place => 'fujisawa')

puts person.fullname
puts person.age
puts person.place

person.save
普通に入ってた。このへんは複数人でやるときは何か考えないとならないな。
でもクロールしてきた値とかを適当にどんどん入れてしまうのにはすごくいい。twitterのchirp streamとか。

数値はlt,gtで大なり小なり条件指定できるらしい。
あとmongodbは空間型があるはずだけどそれは使えないのかな?filedの型になかったけど。