2011年12月28日水曜日

nginxとunicorn上でrails3.1アプリを動かす。ついでにcapistranoを使ってデプロイ

かなりハマったので、メモ。

まずアプリ側。
Gemfileに以下を追加。

group :deployment do
gem 'capistrano'
gem 'capistrano_colors'
end

gem 'therubyracer'
gem 'unicorn'

で、bundle install。

次にcapistrano設定ファイルを作成
# capify .

#config/deploy.rb

# capistranoの出力がカラーになる
require 'capistrano_colors'

# cap deploy時に自動で bundle install が実行される
require "bundler/capistrano"

#rvm setting
set :rvm_type, :user
$:.unshift(File.expand_path('./lib', ENV['rvm_path']))
require "rvm/capistrano"
set :rvm_ruby_string, '1.9.2@rails3.1' #ここにgemset名を入力

set :user, "サーバーのユーザー名"
set :port, 22 #サーバーのポート番号
set :use_sudo, false #sudoをするかどうか。
ssh_options[:forward_agent] = true

#repository setting
set :application, "sample" #アプリケーション名
set :scm, :git #gitを使う
set :repository, "ssh://user@example.com:22/home/user/git/sample.git"
set :deploy_to, "/home/user/sample/"
default_environment["LD_LIBRARY_PATH"] = "$LD_LIBRARY_PATH:/usr/local/lib"



# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

role :web, "example.com" # Your HTTP server, Apache/etc
role :app, "example.com" # This may be the same as your `Web` server
role :db, "example.com", :primary => true # This is where Rails migrations will run

#sqlite3を使う場合、dbをshareフォルダに入れる。
task :db_setup, :roles => [:db] do
run "mkdir -p -m 775 #{shared_path}/db"
end

namespace :deploy do
task :start, :roles => :app do
run "cd #{current_path}; bundle exec unicorn_rails -c config/unicorn.rb -E production -D"
end
task :restart, :roles => :app do
if File.exist? "/tmp/unicorn.pid"
run "kill -s USR2 `cat /tmp/unicorn.pid`"
end
end
task :stop, :roles => :app do
run "kill -s QUIT `cat /tmp/unicorn.pid`"
end
end

namespace :assets do
task :precompile, :roles => :web do
run "cd #{current_path} && RAILS_ENV=production bundle exec rake assets:precompile"
end
task :cleanup, :roles => :web do
run "cd #{current_path} && RAILS_ENV=production bundle exec rake assets:clean"
end
end
after :deploy, "assets:precompile" #デプロイ後にassets compileをするように。
set :normalize_asset_timestamps, false #rails3.1対策


次にunicornの設定
#config/unicorn.rb

application = 'sample'

# ワーカーの数
worker_processes 2

# ソケット
listen "/tmp/unicorn.sock"
pid "/tmp/unicorn.pid"

# ログ
if ENV['RAILS_ENV'] == 'production'
shared_path = "/home/user/#{application}/shared"
stderr_path = "#{shared_path}/log/unicorn.stderr.log"
stdout_path = "#{shared_path}/log/unicorn.stdout.log"
end

# ダウンタイムなくす
preload_app true

before_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.connection.disconnect!
end
old_pid = "/tmp/unicorn.pid.oldbin"
if File.exists?(old_pid) && server.pid != old_pid
begin
Process.kill("QUIT", File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
end

after_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
end
end


environmentsのproduction内で
config.serve_static_assets = true
にする。
※こうしないとcssがロードされない。

あとdatabase.ymlのproductionを
database: ../../shared/db/production.sqlite3
にする。



次はサーバ側。
nginxをyumでインストールするために、repoに追加。
# sudo vim /etc/yum.repo.d/nginx.repo


[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
gpgcheck=0
enabled=1


で、インストール。
#sudo yum install nginx

nginxの設定ファイルはこんな感じ。
#sudo vim /etc/nginx/conf.d/sample.conf


upstream unicorn {
server unix:/tmp/unicorn.sock;
}

server {
listen 80;
server_name example.com;

root /home/user/sample/current/public;
error_log /home/user/sample/current/log/error.log;

location / {
if (-f $request_filename) { break; }
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://unicorn;
}

}


あとは、capistranoでデプロイして、nginxを起動するだけ。必要があればchkconfigで自動起動設定をする。
capistranoでデプロイするときは、
#cap deploy:setup
#cap db_setup
#cap deploy:cold
の順で。


参考:
http://d.hatena.ne.jp/ntaku/20111112/1321093327
http://aerial.st/archive/2011/06/16/nginx-unicorn-rails-on-mac/

2011年12月8日木曜日

jQuery UIを使って、オートコンプリート機能を実装してみる@rails3.1

facebookなどの検索BOXで途中まで入力すれば、結果の一部が出てくるあれです。

rails3.1からjQueryがデフォルトになったのですが、jQuery UIはまだ有効になっていないので有効にする。

application.jsの//= require jqueryの下あたりに以下を追記
//= require jquery-ui

で、ビューに検索BOXをJSで作る。

<input type="text" id="textbox1"></input>
<script type="text/javascript" charset="utf-8">
$(function(){
$("#textbox1").autocomplete({
source : "/auto_complete"
});
})
</script>



sourceの部分をルーティングで設定する。
get 'auto_complete' => 'api#auto_complete'

コントローラーはこんな感じで


def auto_complete
if request.xhr?
data = Array.new
data_items = Data.where('name like ?', "%#{params[:term]}%")
data_items.each do |f|
data << f.name
end
return render data
end
end



検索BOXで入力したキーワードは都度、sourceで設定したアドレスに対して、params[:term]で送られます。
で、結果をjsonで返せば動作するのですが、そのまま返すと不必要なデータもそのまま送ってしまうので、必要なカラムのデータだけ送るよう、配列を作りなおしてます。
もう少しうまいやり方もありそうですが。。。
あと、request.xhr?と指定すると、ajax以外からのアクセスを弾いてくれるようです。

ちなみにCSSを使う場合には、assetsのcssフォルダ下にjquery-ui-1.8.16.custom.cssをおいて、jsを呼び出すのと同じように
*= require_jquery-ui-1.8.16.custom
をかけばOK
画像は、assets/image/jquery-uiの下にimageフォルダごとコピーすればOK。

参考:http://d.hatena.ne.jp/naoty_k/20110925/1316969446
http://blog.livedoor.jp/satoyansoft/archives/65458957.html

Railsでapiっぽいのを作って、iOSアプリと連携してみる

iOS(iphone)アプリで位置情報を取得、それをrailsアプリに送信してDBに登録するようにしてみます。
なおiOSについてはまだまだ勉強不足のため、「まるごと学ぶiPhoneアプリ制作教室」内に記載してあったコードを参考にしています。
またrailsアプリ内には位置情報の取得まで作っておきますが、iOS部分では省きます。

まずはiOSアプリの方。
ViewController.m

#import "ViewController.h"
#import "Location.h"
#import "JSON.h"

@implementation ViewController
@synthesize codeTextField;

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
[self setCodeTextField:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (NSString *)getCurrentDate {
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
NSString *dateFormat = @"yyyy/MM/dd-mm:ss:SSS";
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"JST"]];
[dateFormatter setDateFormat:dateFormat];
NSString *date = [dateFormatter stringFromDate:[NSDate date]];
return date;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {


Location *myLocation = [[[Location alloc] init] autorelease];
myLocation.latitude = [NSString stringWithFormat:@"%f", newLocation.coordinate.latitude];
myLocation.longitude = [NSString stringWithFormat:@"%f", newLocation.coordinate.longitude];
myLocation.time = [self getCurrentDate];
myLocation.identificationCode = [codeTextField text];

NSDictionary *locationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
myLocation.latitude, @"latitude",
myLocation.longitude, @"longitude",
myLocation.time, @"time",
myLocation.identificationCode, @"identificationCode",
nil];
NSString* jsonString = [locationDictionary JSONRepresentation];
NSLog(@"JSON: %@", jsonString);

NSURL *serviceURL = [NSURL URLWithString:@"http://0.0.0.0:3000/location.json"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:serviceURL];
[req setHTTPMethod:@"POST"];
[req addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[req setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];

NSURLResponse *resp= nil;
NSError *error= nil;
NSData *result = [NSURLConnection sendSynchronousRequest:req returningResponse:&resp error:&error];

if (error) {
NSLog(@"error!");
} else {
NSLog(@"Result:%@", result);
}

}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
}
- (IBAction)logStartButton:(id)sender {
if (locationManager == nil) {
locationManager = [[CLLocationManager alloc] init];
}
locationManager.delegate = self;
[locationManager startUpdatingLocation];
}
- (void)dealloc {
[codeTextField release];
[super dealloc];
}
@end


ViewController.h

#import <UIKit/UIKit.h>
#import "CoreLocation/CoreLocation.h"

@interface ViewController : UIViewController <CLLocationManagerDelegate> {
@private
UITextField *codeTextField;
UIButton *logStartButton;
CLLocationManager *locationManager;
}
@property (retain, nonatomic) IBOutlet UITextField *codeTextField;
- (IBAction)logStartButton:(id)sender;

@end


Location.h

#import <Foundation/Foundation.h>

@interface Location : NSObject {
NSString *latitude;//緯度
NSString *longitude;//経度
NSString *time;//時間
NSString *identificationCode;//自分を特定するためのIDコード
}
@property (nonatomic, assign) NSString *latitude;
@property (nonatomic, assign) NSString *longitude;
@property (nonatomic, assign) NSString *time;
@property (nonatomic, assign) NSString *identificationCode;

@end


#import "Location.h"

@implementation Location
@synthesize latitude;
@synthesize longitude;
@synthesize time;
@synthesize identificationCode;

@end



Location.m

#import "Location.h"

@implementation Location
@synthesize latitude;
@synthesize longitude;
@synthesize time;
@synthesize identificationCode;

@end

CoreLocationライブラリは別途入れてください。
またここから「JSON v2.3.2 (iOS)」をダウンロードして、その中からClassesフォルダを同じプロジェクトファイル内にコピーしておいてください。
なおserviceURL = [NSURL URLWithString:@"http://0.0.0.0:3000/location.json"]のドメイン部分は自分なりに。
xibも適当にボタンとテキストフィールドを。名称は、それぞれ「logStartButton」「codeTextField」で。
わからないときは、「まるごと学ぶiPhoneアプリ制作教室」を参考にしてください。
サンプルコードがここにあったりします。

次にrailsの方。
ApiController

class ApiController < ApplicationController

def post
location = Location.new(params[:api])
respond_to do |format|
if location.save
format.json { head :ok }
else
format.json { render json: location.errors, status: :unprocessable_entity }
end
end
end

def get
@location = Location.where(:identificationCode => params[:identificationCode]).limit(5)

respond_to do |format|
format.json { render json: @location }
end
end

end

ルーティングとして以下を追加。

post 'location(.:format)' => 'api#post'
get 'location(.:format)' => 'api#get'

データベースに以下のカラムを作る
「latitude」
「longitude」
「time」
「identificationCode」
で、マイグレして起動して、iPhoneアプリを起動し、ボタンを押せば、1秒ごとにrailsのDBに位置情報とID、時間が登録されていくはずです。
ポイントは、jsonデータをpostメソッドで送信するとそれぞれのparams[:項目名]で取得できること。
それができれば簡単ではないかと。

今回簡易的にするため外のpostメソッドをそのまま受け入れましたが、セキュリティ的には問題あるので、実用にはもうちょい工夫が必要そうです。

2011年10月18日火曜日

プログラム歴9ヶ月でも1日でサイトが作れる!ハッカソンに参加する3つの理由

今年の1月後半からプログラミングを始め、はや9ヶ月。
時間をかければ簡単なWEBサービスを作れるところまできましたが、ハッカソンに参加したらなんと1日で(簡単なものとはいえ)WEBサービスを作ることができました。
作ったのはreblogramというサービスで、簡単にいうとinstagram上の写真をワンクリックでtumblrにreblogできるサイトです。
instagramのPCビューアも兼ねているので、PCでinstagramのTLを眺めつつ、気に入った写真はreblogするという使い方ができます。
(本当はスマホ用のデザインもしたかったのですが、時間が足りませんでした。。。)

tumblrのAPIとinstagramのAPIを使ったので、非常に作りが簡素化されていますが、自分でもまさか一日で作ることができるとは思いませんでした。
そこで、なぜハッカソンならたった一日でもWEBサービスを作ることができるのか、僕なりに気がついたことを3つにまとめました。

●不必要なハマリがなくなり、必要なことに集中できる
プログラミングを覚えたてのころ、特に独学で勉強している人ほど経験があると思いますが、後から見ると本当にくだらないことではまってしまうことがありますよね?
しかしハッカソンなら、まわりにそのことを経験済みの人がいる可能性が高いため、不必要なハマリがなくなります。
初心者レベルでハマる程度のことだと、言語に依存するような内容は少ないので、自分の同じ言語の人はいるのか?というのはあまり気にする必要がないと思います。
(実際今回は3人でやりましたが、PHP、Ruby、 Object-Cとそれぞれ別の言語でした。それでもお互いのハマリを解決しあえたと思います。)

●自分の持ってない知識を活用できる
今回reblogramを作るにあたりデザインにはtwitterのBootstrapを利用しました。
これがなければデザインまで一日では完成していなかったと思うほど、非常に便利なものでしたが、これは他のメンバーから教えてもらった情報です。
ハッカソンでは、問題の解決という側面だけではなく、サービスを作るまでのショートカットの方法も共有しあえるという面でも効果があったと思いました。

●集中するしかなくなる
これがハッカソンに参加する意味、効果として一番大きいかもしれません。
サボりたくても周りではひたすらコードを書いている人しかいないので、自分もサボるわけにはいきませんw
食事と一緒に海に休憩しにいった時以外は、ひたすらコードを書くか、詰まったところの相談をしていました。
この集中度がなければあったからこその成果だったと思います。

●おまけ
ただコードをひたすら書くだけでしたが、非常に楽しい体験でした。
今回は一旦都内で集合したあと、会場の民宿まで移動したのですが、その移動時間に作るサービスの相談や情報交換などを行えて、移動時間すら有益な時間だったと思います。
お試しとして3人くらいで開催したのですが、いい経験だったのでまたやりたいという話をメンバーの人としました。
次はもう少しメンバーを増やしてやりたいと思いますので、興味ある方は責任者の@masumikawasakiまでご連絡ください。
(僕じゃないですけど・・・)

2011年9月5日月曜日

Mac ports からhomebrewへ移行

homebrewはmac portsと競合するらしいので、先にmac portsをアンインストールする。

へんなところでつまずくと嫌なので、念のためmac portsでインストールしたものをテキストファイルで保存
#port installed

アンインストール
# sudo port -f uninstall installed
# sudo rm -rf /opt/local/


homebrewのインストール
#ruby -e "$(curl -fsSL https://gist.github.com/raw/323731/install_homebrew.rb)"

homebrewはgitで更新するらしいのでgitをインストール
#brew install git

homebrewのアップデート
#brew update

あとは個別に必要なものをインストール

2011年9月4日日曜日

railsでpaypalを使う方法

rails2.3.5の場合です。
ライブラリが用意してあるので、基本的に簡単なのですが、ちょっとはまったので。

まずはアカウントを用意。
sandboxに登録する。
「Create a preconfigured account」で、BuyerとSellerのアカウントをひとつずつ作る。
Buyerの方には残高を入れておくことを忘れないように。

次にsandbox test siteにsellerのアカウントでログイン。
ProfileからAPI Accessで「API Username」「API Password」「Signature」を取得。
これで準備は完了です。

ライブラリのインストール。
#sudo gem install activemerchant -v 1.10
わざわざ1.10にしているのは、それ以上だとrails2.3.11以上にしか対応していないから。

#app/controller/payments_controller.rb

class PaymentsController < ApplicationController
include ActiveMerchant::Billing
def index

end

def confirm
redirect_to :action => 'index' unless params[:token]

details_response = gateway.details_for(params[:token])

if !details_response.success?
@message = details_response.message
render :action => 'error'
return
end

@address = details_response.address
end

def complete


purchase = gateway.purchase(100,
:ip => request.remote_ip,
:payer_id => params[:payer_id],
:token => params[:token]
)

if !purchase.success?
@message = purchase.message
render :action => 'error'
return
end
end


def checkout
setup_response = gateway.setup_purchase(100,
:ip => request.remote_ip,
:return_url => url_for(:action => 'confirm', :only_path => false),
:cancel_return_url => url_for(:action => 'index', :only_path => false)
)
redirect_to gateway.redirect_url_for(setup_response.token)
end

private
def gateway
@gateway ||= PaypalExpressGateway.new(
:login => 'API Username',
:password => 'API Password',
:signature => 'Signature'
)
end

end


#app/paments/index.html.erb


Purchase Sprockets


Thank you for your decision to purchase our sprockets.


Your order total is 100円



<%= link_to image_tag('https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif'), :action => 'checkout' %>




#app/paments/confirm.html.erb

Please Confirm Your Payment Details












Name<%= @address['name'] %>
Company<%= @address['company'] %>
Address 1<%= @address['address1'] %>
Address 2<%= @address['address2'] %>
City<%= @address['city'] %>
State<%= @address['state'] %>
Country<%= @address['country'] %>
Zip<%= @address['zip'] %>


<% form_tag :action => 'complete', :token => params[:token], :payer_id => params[:PayerID] do %>
<%= submit_tag 'Complete Payment' %>
<% end %>


#app/paments/confirm.html.erb

Thank you for your purchase



#app/paments/confirm.html.erb

Error


Unfortunately an error occurred: <%= @message %>


The confirm.html.erb templates displays the customer's PayPal address and asks them if they want to complete the payment. Displaying the review page represents Integration Point 3a from the Integration guide. This is the customer's opportunity to review the details of their order, change shipping methods, etc. It looks like this:


#config/enviroment.rb
config.gem "activemerchant"
を追記

#config/enviroments/development.rb
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :test
ActiveMerchant::Billing::PaypalExpressGateway.default_currency = "JPY"
end
を追記

これでpaypalのテストサイトで課金のテストができます。
本番サーバでやるなら、本番サーバ用にアカウントを用意して、
#config/enviroments/production.rb
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :production
ActiveMerchant::Billing::PaypalExpressGateway.default_currency = "JPY"
end

で、行けるはず。
試してませんが。。。

テスト環境と本番環境でかなり動作が違うらしい。
日本じゃpaypalアカウント持っている人少ないから、クレジットカードの欄を優先させたいんだけど、どうやるんだろう?
本番環境なら優先されるのかな?

あと、定期的な支払い請求もできるらしいけど、それもどれかわからず・・・

2011年8月5日金曜日

rubyでfacebookをいじってみる

facebookアプリに登録して、自分のlikeデータを取得してみます。

まずはここから適当にアプリを登録する
https://developers.facebook.com/apps

で、Access Tokenが必要なので、
上部の「アプリ→アプリ名」部分のアプリ名を選択。
真ん中くらいにAccess Tokenって欄があるので、そこのリンクをクリックして発行する。


次にライブラリのインストール
#sudo gem install rest-fraph

でこんな感じのものを書く。
access_tokenのところは、さっき取得したaccess_tokenを入れてください。


require 'rubygems'
require 'rest-graph'

rg = RestGraph.new(:access_token => 'access_token')
like = rg.get('me/likes')

like.values.first.each do |f|
puts f['name']
end


一覧でlikeしたものの名前が出てきます。

2011年8月2日火曜日

hamlを使ってみる。

ruby1.8.7 rails2.3.5 Netbeans6.9.1のケースです。

まずはhamlのインストール
#sudo gem install haml

あとnetbeansを使って開発してるので、専用のプラグインも入れます。
ここからプラグインをダウンロード。
http://code.google.com/p/postcomment/downloads/detail?name=org-netbeans-modules-haml.nbm&can=2&q=

で、Netbeansを起動して、ツール→プラグインからインストール。

railsの新規プロジェクトを作成したら、
environment.rbに config.gem "haml"
を記述。

あとは、index.html.hamlみたいなやつを読むようになってくれます。

使ってみた雑感としては、IDEが対応してないので、逆に開発速度が遅くなるかも。
ただ単体としてみればerbより、記述するコードが少ないので早そう。

むしろSassのほうが独立しているし、簡単に導入できるのではないかと思いました。

2011年7月17日日曜日

face.comのAPIを使って、類似の顔を検索するサービスを作ってみる。

このへんで使った技術を使って、実際にサービスを作ってみる。
face.comのAPIを使って、似た顔を判別させる方法
face.comのAPIをrubyのライブラリを使って遊んでみる

今回はface.comにAKB48の顔写真DBを作り、twitterのアイコンがどれだけAK48のメンバーに似てるか、表示させます。
DBは予め登録してある前提で。

コントローラー
class IndexController < ApplicationController

def index
end

def show
  require 'face'
  require 'open-uri'
  require 'uri'
  require 'net/http'
  @entries = Array.new
  http = Net::HTTP.new( 'api.twitter.com', 80 )
  req = Net::HTTP::Get.new( "http://api.twitter.com/1/users/profile_image/#{params[:user]}.json?size=original" )
  res = http.request( req )
  img_url = URI.parse( res['location'] ).to_s

  client = Face.get_client(:api_key => '' , :api_secret => '')

  data = client.faces_recognize(:urls => img_url , :uids => 'all@akbtter')
  photos = data['photos']
  photos.each do |photo|
    photo['tags'].each do |tags|
     unless tags['uids'] == []
      confidence = tags['uids'][0]['confidence']
      uid = tags['uids'][0]['uid']
      member = Member.find_by_uid( uid )

      hash = {"name" => member.name, "confidence" => confidence }
      @entries << hash
    end
   end
  end
 end
end

ビュー
#index/index.html.erb

AKBったー


twitterのIDを入力すれば、そのユーザーのアイコンがAKBの誰に似ているか判定してくれます。

※現在は神7にのみ対応
<% form_tag :action => 'show' do %>
 <%= text_field_tag 'user' %>
 <%= submit_tag 'チェック' %>
<% end %>


#index/show.html.erb
<% unless @entries.count == 0 %>
<% @entries.each do |f| %>
<%= f["name"] %>に似ています。

似ている度:<%= f["confidence"] %>%
<% end %>
<% else %>
似ているメンバーはいません。
<% end %>

マイグレーション
class CreateMembers < ActiveRecord::Migration
 def self.up
  create_table :members do |t|
   t.string :name, :uid
   t.timestamps
  end
 end

 def self.down
  drop_table :members
 end
end

で、デザインとか整えたりすると、こんな感じのサービスになります。
http://akb.heroku.com

2011年7月16日土曜日

ralisでPC用とiPhone用のデザインを振り分ける

iPhoneからアクセスされると専用のビューを表示させるように設定する。

config/initalizes/mime_types.rbの以下の文のコメントアウトをはずす。
Mime::Type.register_alias "text/html", :iphone

動作させたいコントローラーに以下を追記

before_filter :adjust_format_for_iphone

private

def adjust_format_for_iphone
 request.format = :iphone if request_iphone?
end

public

def request_iphone?
  request.user_agent && request.user_agent[/iPhone|Android/]
end


で、専用のビューを作成する。(ここではindexというコントローラーとindexというアクション名で説明)

layouts/index.iphone.erb #iPhone用のレイアウトファイル
index/index.iphone.erb #index用のレイアウトファイル


あとはそれぞれデザインすればOK。

iPhone用のデザインはUiUIKitというのがあって、それを使うと比較的簡単に作れる。


参考
http://yusukezzz.net/blog/archives/1484
http://d.hatena.ne.jp/t-1732/20100828/1282970904

追記
androidにも対応してみました。
iPhoneと同じデザインですけど。。。
もし個別でやるなら、

private

def adjust_format_for_iphone
 request.format = :iphone if request_iphone?
 request.format = :android if request_android?
end

public

def request_iphone?
  request.user_agent && request.user_agent[/iPhone/]
end

def request_android?
   request.user_agent && request.user_agent[/Android/]
end

にして、index.android.erbですかね?
試してないのでわかりません><

2011年7月15日金曜日

face.comのAPIを使って、似た顔を判別させる方法

face.comのAPIを使うと顔を判断してくれるのですが、このときface.comのDBに似た顔があれば、それを判別して返してくれます。
具体的にどのようなことをすればいいのか日本語で説明しているサイトがなかったので、ちょっと紹介してみます。
なお書いているコードはRubyですが、基本的な流れは他の言語でも一緒です。

face.comの登録はこちらからしてください。
http://developers.face.com/signup/?g

登録したら「API Key」、「API Sercret」、「namespace」をメモっといてください。
都度使用します。
また途中「uid」が出てきますが、これは「任意のID@namespace」です。
任意のIDはどこかで定義することなく、いきなりAPIに飛ばして平気です。
またこの任意のID部分で、なんの写真か判断させますので、人物ごとにユニークなIDをふってください。

【流れ】
1:faces.detectもしくはfaces.recognizeでAPIを叩き、tags.addで使用するデータを取得。
2:1で取得した情報をもとにtags.addでタグ付け。
3:faces.trainで、2で設定したタグをface.comのDBに登録する。
4:faces.recognizeを使って写真を投げると、DBに似た顔の写真があればそのuidを返してくれます。

具体的なコードはこちらです。
登録用と似た写真を判断するようにメソッドを分けてます。

と、その前に
# sudo gem install face
でface.com用のライブラリをインストールしといてください。

require 'rubygems'
require 'face'

#APIkeyの設定
api_key = ''
api_secret = ''

#使用する画像の設定
@img_url = "http://a2.twimg.com/profile_images/1049472282/image_bigger.jpg"
@label = "篠田麻里子"

#shinoda@namespaceが帰ってきたら篠田麻里子だと判定するようDBなどに登録しておくといいんじゃない?
#uidで誰か判定させるので、人物ごとのユニークにしてください。
@uid = "shinoda@namespace"

@client = Face.get_client(:api_key => api_key , :api_secret => api_secret)

#画像の登録
def regist
 data = @client.faces_recognize(:urls => @img_url , :uids => @uid) #face.comのAPIになげる
 photos = data['photos']
 photos.each do |photo|
    photo['tags'].each do |tags|
     result = @client.tags_add(:tid => tags['tid'],
          :uid => @uid,
          :label => @label,
          :url => photo['url'],
          :x => tags['center']['x'],
          :y => tags['center']['y'],
          :width => tags['width'],
          :height => tags['height'],
          :tagger_id => tags['tagger_id']
) #帰ってきたデータをもとにタグを登録。
p result
@client.faces_train(:uids => @uid, :namespace => 'namespace') #タグ付けした情報をface.comのDBに登録。
  end
 end
end

#画像の判定時
def tag_show
 data = @client.faces_recognize(:urls => @img_url , :uids => 'all@namespace') #all@namespaceに対して投げると、namespaceに入っているデータ全部で認定してくれる。
 photos = data['photos']
 photos.each do |photo|
  photo['tags'].each do |tags|
   unless tags['uids'] == []
    puts "#{tags['uids'][0]['uid']}に#{tags['uids'][0]['confidence']}%似ています。"
   end
  end
 end
end


regist
#tag_show


all@namespaceに対してfaces.recognizeさせると、namespaceに登録してあるIDのうち、似ている画像があれば、すべて返してくれる。
投げた写真が、篠田麻里子と前田敦子に似ていた場合、shinoda@namespace,maeda@namespaceみたいな感じで返してくれます。

なお個別に似ているかどうか判断したい場合は個別のuidを指定してください。
(投げた画像が篠田麻里子にどの程度似ているか判定させたい場合、shinoda@namespace宛にfaces.recognizeさせる。
 このとき前田敦子にどれだけ似ていてもその判定はされない。)

似ているどはconfidenceの中に入ってます。

参考:http://journal.mycom.co.jp/column/tool/022/index.html

2011年6月29日水曜日

anemoneの使用時のメモリ問題解決法

anemoneを使ってページ全体をクロールしようとするとメモリをやたら食います。
どうやらこれはデータをメモリにキャッシュしているかららしい。

というわけで、キャッシュをmongodbに保存するようにして、解決する。

まずはyumリポジトリを追加
# sudo vim /etc/yum.repos.d/10gen-mongodb.repo

name=10gen Repository
baseurl=http://downloads.mongodb.org/distros/centos/5.4/os/x86_64/
gpgcheck=0
enabled=0
これを書きこんで保存。

で、必要なものをインストール。
# sudo yum install mongo-10gen mongo-10gen-server
# sudo gem install bson_ext SystemTimer

で、起動する。
# sudo /etc/init.d/mongod start

mongodbを使うように書きなおす。

require 'rubygems'
require 'anemone'

# mongodbを使うのとquery_stringsは取得しないように設定。またアクセス間隔を2秒にする。
  opts = {
   :storage => Anemone::Storage.MongoDB,
   :skip_query_strings => true,
   :delay => 2,
  }

Anemone.crawl("http://example.com/", opts) do |anemone|
 anemone.on_every_page do |page|
   puts page.url
  end
 end
end

参考;
http://work.blog.eggplant.org.uk/2011/03/web-anemone.html

2011年6月28日火曜日

anemoneを使って、サイト内のURLをすべて取得する。

anemoneさん便利すぎでしょ・・・

#sudo gem install anemone


require 'rubygems'
require 'anemone'

Anemone.crawl("http://example.com/") do |anemone|
 anemone.on_every_page do |page|
   puts page.url
  end
 end
end

2011年6月27日月曜日

face.comのAPIをrubyのライブラリを使って遊んでみる

face.comのAPIを使って、flickrのinterestingに掲載された画像のうち、顔の画像だけ選択し、顔の部分には笑い男の画像をつけてみます。
なお笑い男の画像は自分で用意してください。
あとAPIはhttp://developers.face.com/で簡単に登録できます。
画像の修正にはrmagickを使ってますので、昨日の記事を参考にインストールしといてください。

rubyにface.comを扱えるライブラリがあるので、インストール
#sudo gem install face

以下のコードに、api部分を入力する、

require 'rubygems'
require 'face'
require 'open-uri'
require 'nokogiri'
require 'RMagick'
include Magick


client = Face.get_client(:api_key => 'api_key', :api_secret => 'api_secret')
url = "http://www.flickr.com/explore/interesting/2011/06/"
laughingman_image = Magick::Image.read('laughingman.gif').first
url_list = Array.new
img_url_list = Array.new

#flickrからURLを取得
data = open( url )
doc = Nokogiri::HTML( data )

doc.css('a').each do |f|
 begin
  if /\/explore\/interesting\/(\d+)\/(\d+)\/(\d+)\// =~ f.attribute('href').value
   url_list << f.attribute('href').value
  end
 rescue
  next
 end 
end

puts 'flickrの日付URL取得完了'

url_list.each do |f|
 img_url = URI.join(url, f)
 img_data = open( img_url )
 img_data = Nokogiri::HTML( img_data )
 img_data.css('img.pc_img').each do |img|
  img_url_list << img.attribute('src').value
 end
end

puts 'imgの個別URL取得完了'

#face.comのAPIにURLをわたして画像を認識させ、顔があれば、顔の部分に笑い男を貼り付ける。
img_url_list.each do |img_url|
 data = client.faces_detect(:urls => img_url )
 filename = File.basename( img_url )

 photo = data['photos']
 photo.each do |photo|
  if photo['tags'].first #tagsは配列形式で帰ってくるので、firstを付けてます。
   image = Magick::Image.read( img_url ).first #RMagickで読み込んだファイルも配列なので、firstを付ける。
   begin
    photo['tags'].each do |tag|
     width = photo['width'] * (tag['width'] / 100)
     height = photo['height'] * (tag['height'] / 100)
     x = photo['width'] * (tag['center']['x'] / 100)
     y = photo['height'] * (tag['center']['y'] / 100)
     overlay_image = laughingman_image.resize_to_fit(width, height)
     image.composite!(overlay_image, x-width/2, y-height/2, OverCompositeOp)
    end
    image.write("./img/#{filename}")
    puts filename
   rescue
    puts '処理に失敗しました。'
    next
   end
  end
 end
end

参考;http://labs.appshelf.info/category/face-com/

2011年6月26日日曜日

flickrから人気の画像を取得し、rmagickを使って、サムネイルとしてリサイズする

flickrにはその日人気になった写真みたいなのをだしているので、その画像を取得し、150×150の画像にリサイズするプログラムです。
写真のサイズはそれぞれバラバラなので、大きい方の辺を150にし、足りない部分は背景を黒にしています。

一応サーバ上で動かすこと前提としています。

まずはサーバにImagemagickとRmagick、必要なライブラリをインストール

#画像編集に必要なライブラリをインストール(先にいれといたほうがいいらしいです)
# sudo yum install libjpeg-devel libpng-devel


#Image magickのインストール
# wget ftp://ftp.kddlabs.co.jp/graphics/ImageMagick/ImageMagick-6.7.0-8.tar.gz
# tar xvzf ImageMagick-6.7.0-8.tar.gz
# cd ImageMagick-6.7.0-8
# ./configure
# sudo make & make install

#Rmagickのインストール
# sudo gem install rmagick

このままだとなぜかlibMagickCore.so.2を読めないとか出てくるので、bash_profileに以下を追加
# vim .bash_profile
export LD_LIBRARY_PATH=/lib/
↑これを追加する。

これでインストールは完了。

次にこんな感じのファイルを作って、保存して、実行。

require 'rubygems'
require "RMagick"
require 'open-uri'
require 'nokogiri'
require 'uri'
include Magick #多分必要。


def flickr_img_download
 url = "http://www.flickr.com/explore/interesting/2011/06/"
 url_list = Array.new
 img_url_list = Array.new

 data = open( url )
 doc = Nokogiri::HTML( data )

#URLのリストを取得してます。
 doc.css('a').each do |f|
  begin
   if /\/explore\/interesting\/(\d+)\/(\d+)\/(\d+)\// =~ f.attribute('href').value
    url_list << f.attribute('href').value
   end
  rescue
   next
  end 
 end

 url_list.each do |f|
  img_url = URI.join(url, f)
  img_data = open( img_url )
  img_data = Nokogiri::HTML( img_data )
  img_data.css('img.pc_img').each do |img|
   img_url_list << img.attribute('src').value
  end
 end

 #画像を保存してます。
 img_url_list.each do |f|
  begin
   filename = File.basename( f )
   open("img/#{filename}", 'wb') do |file|
    open( f ) do |doc|
     file.write(doc.read)
     puts filename
    end
   end
  rescue
   next
  end
 end
end

def resize
 #背景の画像を作成
 whitefil = Magick::Image.new(150,150){self.background_color = 'none'}

 #フォルダ内の画像リストを作る。
 dir = Dir.glob(["./img/*.jpg", "./img/*.png"])

 dir.each do |filename|
  begin
   image = Magick::Image.read( filename ).first
   if image.columns < 150 or image.rows < 150 #サイズが150以下ならリサイズせずに余白だけ付ける。
     image = whitefil.composite(image, Magick::CenterGravity, Magick::OverCompositeOp)
   else image.columns > 150 or image.rows > 150 #サイズが150以上なら最大の幅を150になるよう、ファイルの比率は変えずに変換。
     image = image.resize_to_fit!(150,150)
     image = whitefil.composite(image, Magick::CenterGravity, Magick::OverCompositeOp)
   end
     image.write( filename )
  rescue Magick::ImageMagickError
   File.delete( filename )
   puts filename
  end
 end
end

flickr_img_download
resize

メソッド二回に分けてリサイズしているけど、本当は一回のメソッドで、かつ、リサイズ前のバックアップ用ファイルを別フォルダに保存しておきたい。
なぜか一回のメソッド中にやろうとすると、保存に失敗するので、こんな感じにしてます。
やり方知っている人がいたら助けてください。。。

2011年6月5日日曜日

検索からきた人に向けて、関連記事のリンクを出す

ITmediaとかで使われてる↓これのことです。正式名称なんていうんでしょうか?


面倒なのでとりあえずGoogleだけ対応。
あと複数キーワードがあったら、最初のキーワードだけ優先するようにしています。

リファラーの情報から、検索キーワードの部分だけを正規表現で取ってきて、

複数ヶ所で呼び出すことも考えて、applicationhelperに記述。

 def search_result(request)
  @request_name = Array.new
  @request_num = 0
   unless request == nil
    ref = request
    ref = URI.decode(ref).to_s
    if /google\./ =~ ref && ref != nil
     ref = ref.scan(/&q=(.*?)$|\?q=(.*?)$/).to_s
     ref = ref.scan(/^(.*?)&/).to_s if /&/ =~ ref
     ref = ref.gsub(/\+| /, ':?').to_s if /\+| / =~ ref
     ref = ref.to_s.split(':?') if /:?/ =~ ref
     @request_name = ref[0]
     ref = "%" + ref[0] + "%"
     @request_num = Entry.find(:all, :conditions => ["title like ? or discription like ?", ref, ref]).size
   end
  end
  end

コントローラーで呼び出す。
このときヘルパーのメソッドを呼び出せるようにincludeする。
request.refererにリファラー情報がある。

class EntriesController < ApplicationController
 include ApplicationHelper
 def index
 search_result(request.referer)
 @entries = Entry.all
 end
end

最後にビュー
<% if @request_num > 0 %>
 <%= link_to "#{@request_name}の関連記事:#{@request_num}件。", :controller => 'search', :id=>"#{@request_name}" %>
<% end %>

こんな感じです。

2011年6月3日金曜日

railsでsitemapを動的に生成する

#コントローラー
class SitemapController < ApplicationController
 def sitemap
  @entries = Entry.find(:all)
  headers["Content-Type"] = "text/xml; charset=utf-8"
  respond_to do |format|
   format.xml {render :layout => false}
  end
 end
end

#view(sitemap.xml.builder)
xml.instruct!
xml.urlset(:xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9") do
 xml.url do
  xml.loc(url_for(:controller => :entries, :only_path => false))
 end

 @entries.each do |entry|
  xml.url do
   xml.loc(url_for(:controller => :entries, :action => :show, :id => entry.id, :only_path => false))
  end
 end
end

#routes.rb
map.xml 'sitemap.xml' , :controller => 'sitemap', :action => 'sitemap'

参考
http://higelog.brassworks.jp/?p=56

2011年5月31日火曜日

railsのタイムスタンプを無効にする

特定コントローラーの場合は、無効にしたい動作のメソッド内で
Entry.record_timestamps = false

railsの更新をgitを使って行う@serversman

サーバとローカル環境にgitのインストールは事前に行ってください。
serverにインストールっていってもyum install gitでほぼOKだけど。

まずはローカル環境でgitのファイルをコミットする。

Railsアプリの作成。すでにある場合は、そのフォルダの中に移動する。
# rails sample
# cd sample

gitの初期化。
# git init

余計なファイルが入らないよう、無視するファイルを指定する。
# vim .gitignore

以下の内容を追記
.bundle
db/*.sqlite3
log/*.log
tmp/**/*
tmp/*
vendor/bundle
doc/api
doc/app
*.swp
*~
.DS_Store
で、保存する。

gitのファイルを追加して、コミットする。
# git add .
# git commit -m 'comment'

これでローカルの部分は完了。
次はサーバ。
まずはリポジトリを保存するディレクトリを作る。
ここでは/home/user/repo/sample.gitに置くことにする。
# mkdir repo
# cd repo
# mkdir sample.git
# cd sample.git

初期化。--bareを付けること。
# git init --bare

ローカル環境からサーバへデータを更新する。IPとポートはそれぞれに置き換えてください。ここではデフォルトのポート名でやってます。
git push ssh://user@xxx.xxx.xxx.xx:3843/home/user/repo/sample.git master

これで更新がサーバに飛んだので、ファイルを公開用フォルダにコピーする。
# cd /var/www/
# git clone /home/user/repo/sample.git

完了。

2011年5月30日月曜日

2011年5月16日月曜日

grouponからのメールを解析

grouponに登録していると毎日HTMLメールが送られてくるので、それを解析して、商品とそのURLだけを抜き出す。
メールをrubyに受け渡す方法は、昨日の記事参考。

require 'tmail'
require 'nokogiri'
class Mailget < ActionMailer::Base
def receive(email)
data = TMail::Mail.parse(email)
if data.from = 'no-reply@groupon.jp'
doc = Nokogiri::HTML( data.body )
doc.css('table').css('a').each do |f|
puts f.text
puts f.attribute('href').value
end
end
end
end
これだと同じURL二回でちゃうので、何かに利用する場合は、適度に加工してください。

やろうとしてたことがうまくいかなかったので、暇つぶし的に・・・

2011年5月15日日曜日

postfixで受信したメールをrailsで解析する

Actionmailerで解析できるはずなのだけど、うまくできなかったのでTmailで実現してみた。

まず対応するモデルを作成
ruby script/generate mailer Mailget

で、モデルを修正

require 'tmail'
require 'kconv'
class Mailget < ActionMailer::Base
 def receive(email)
  entry = Entry.new
  mailadd = TMail::Mail.parse(email)
  entry.from = mailadd.from[0]
  entry.discription = mailadd.body.to_s.toutf8
  entry.save
 end
end

あとは、postfixで受信したメールを流してやる。
testアカウントできたメールを処理するように設定。

virtualの設定
# sudo vim /etc/postfix/virtual
下記を一番下に追記
test.example.com anything
test@test.example.com test

aliasesの設定
# sudo vim /etc/aliases

mailer-daemon: postmaster
postmaster: root
test: "| /usr/bin/ruby /var/www/mail/script/runner -e production 'Mailget.receive STDIN.read'" ※追記

これでpost@test.example.com宛にメールがきたら、解析して、DBに送信のメールアドレスと本文を日本語で入れる。
うまく処理されているかどうかをログで確認して終了
# vim /var/log/maillog

もしDBなどにうまく書き込めていなければ、パーミッションのエラーの可能性があるので、その時は
777などに変更してください。

serversmanにpostfixを導入

最初からsendmailが入っているみたけど、postfixにしたかったので、導入する。

まずはドメインの設定
mx mail.example.com 10
a mail.example.com xxx.xxx.xxx.xxx

サーバのホストネームを設定
# sudo vim /etc/sysconfig/network
→ HOSTNAME="example.com” ※追加


次にオレオレ証明書の導入
# sudo yum list mod_ssl
# cd /etc/pki/tls/certs
# make server.key ※パスワードを入力
apache再起動時に毎回パスワードを聞かれるようになるので、修正。下記を入力
# sudo openssl rsa -in server.key -out server.key
# sudo make server.csr ※Country Name と Common Name以外は空白でOK。個人的には全部空白でしてしまったけど。。。
# openssl x509 -in server.csr -out server.pem -req -signkey server.key -days 3650
# openssl x509 -in server.pem -outform DER -out server.der
# sudo chmod 400 server.key
# sudo chmod 400 server.csr
# sudo chmod 400 server.pem
# sudo vim /etc/httpd/conf.d/ssl.conf
→ DocumentRoot "/var/www/html" ※コメントアウト
→ SSLCertificateFile /etc/pki/tls/certs/server.pem ※書き換え
→ SSLCertificateKeyFile /etc/pki/tls/certs/server.key ※書き換え
# sudo /etc/init.d/httpd restart

証明書を使えるようにする。
# yum install cyrus-sasl-md5 cyrus-sasl-plain

システムアカウントとの連携
# sudo vim /etc/sysconfig/saslauthd
→ MECH=shadow ※変更
# sudo /etc/init.d/saslauthd restart ※リブート
# sudo /sbin/chkconfig saslauthd on

次にpostfixの導入
# sudo yum remove sendmail
# sudo yum install postfix
# sudo vim /etc/postfix/main.cf
→ myhostname = mail.example.com ※追加
→ mydomain = temog.info ※追加
→ myorigin = $mydomain ※追加

→ inet_interfaces = all ※コメントアウト解除
→ #inet_interfaces = localhost ※コメントアウト

→ home_mailbox = Maildir/ ※コメントアウト解除

→ #mydestination = $myhostname, localhost.$mydomain, localhost ※コメントアウト
→ mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain ※コメントアウト解除

→ smtpd_banner = $myhostname ESMTP MyMTA(0.1) ※追加

最終行に追加
----------------------------------
# smtp auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination

# 受信メールサイズ制限(10M)
message_size_limit = 10485760

# ssl setting
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/pki/tls/certs/server.pem
smtpd_tls_key_file = /etc/pki/tls/certs/server.key
smtpd_tls_session_cache_database = btree:/etc/postfix/smtpd_schache
----------------------------------


設定ファイルの修正
# sudo vim /etc/postfix/master.cf
→ smtps inet n - n - - smtpd ※コメントアウト解除
→ -o smtpd_tls_wrappermode=yes ※コメントアウト解除
→ -o smtpd_sasl_auth_enable=yes ※コメントアウト解除

メールを保存する場所を作成
# cd
# mkdir -p Maildir/{new,cur,tmp}
# chmod -R 700 Maildir/

新規アカウントを作った場合に、自動的につくられるように
# sudo mkdir -p /etc/skel/Maildir/{new,cur,tmp}
# sudo chmod -R 700 /etc/skel/Maildir/


dovecotのインストール
# sudo yum -y install dovecot
# sudo vim /etc/dovecot.conf
→ protocols = imap imaps pop3 pop3s ※コメントアウト解除
→ mail_location = maildir:~/Maildir

設定の反映と起動
# sudo /etc/init.d/dovecot restart
# sudo /sbin/chkconfig dovecot on

2011年5月9日月曜日

下位タグの考え方

次のサービスで使う予定なので、思いつきをメモ。

すでにあるのかもしれないけど、タグとディレクトリの間として、「下位タグ」の考え方を使いたい。

例えば「A」というタグがあって、その下位タグとして「A-1」や「A-2」があるという考え方。
「A-1」や「A-2」は「A」に従属していて、「A」は「A-1」「A-2」を包括している。
「A」が部署名で、「A-1」は個人名みたいな感じで使う。


まあたいしたことには使えなさそうだけど、個人的なメモとして。

2011年4月18日月曜日

railsでセッション情報を更新する方法

config/initializars/session_storeの
:keyの上辺りに
:expire_after => 2.years
を記述すれば、最終セッションから2年伸びる。

これでブラウザを落としてもセッション情報が更新される。

2011年4月16日土曜日

ServersmanでNokogiriを使うときは気をつけろ

serversmanってよりCentOSだけど。
CentOS上で提供されているlibxmlのバージョンが少し古く、Nokogiri自体のバージョンが一緒でも、ローカル環境と違った動きをすることがある。

なので、バージョンをあげてあげよう。

# wget ftp://xmlsoft.org/libxml2/libxml2-2.7.8.tar.gz
# tar xvzf libxml2-2.7.8.tar.gz
# cd libxml2-2.7.8
# sh configure
# make check
# sudo make install

多分これで出来るはず。

2011年4月14日木曜日

SBI証券のサイトから、リアルタイム株価を取得する方法

SBI証券のサイトから、リアルタイム株価を取得する方法

通常のサイトはよく分からなかったので、バックアップサイトから取得する。

ログインして取得するのでIDとパスワードが必要。


require 'rubygems'
require 'mechanize'
require 'kconv'

$id = ''
$pass = ''

stockcode = ['9501']

def stockprice(stockcode)

# SBI証券にログイン
agent = Mechanize.new
agent.get( 'https://k.sbisec.co.jp/bsite/visitor/top.do' )
agent.page.encoding = 'utf8'
agent.page.form_with('form1'){|form|
form['username'] = $id
form['password'] = $pass
form.click_button
}

# 株価データの取得
stockcode.each do |stock|
agent.get( 'https://k.sbisec.co.jp/bsite/price/search.do' )
puts agent.page.uri
agent.page.form_with(:name => nil ){|form|
form['ipm_product_code'] = stock
form.click_button
}
print stock, "の現在値は", agent.page.at('font.ltext').text, "円¥n"
end

end

stockcode.each do |stockcode|
stockprice(stockcode)
end



mechanizeすげーけど、難しい……
twitterとかに投稿するようにしたり、サーバに負荷かけるようにしたらあかんよ、一応。

2011年4月13日水曜日

ページ内からリンクの貼られている画像だけを保存する方法

require 'rubygems'
require 'nokogiri'
require 'open-uri'

url = 'http://yahoo.co.jp'

doc = Nokogiri.HTML(open( url ))
doc.css('a').css('img').each do |f|
img_url = f.attribute('src').value
puts img_url
filename = File.basename(img_url)
open(filename, 'wb') do |file|
open(img_url) do |data|
file.write(data.read)
end
end
end


ちなみに純粋に画像だけを取得するなら、
doc.css('img').each do |f|
に修正すればいい。




参考:
http://d.hatena.ne.jp/gan2/20080531/1212227507

2011年3月23日水曜日

ruby+webrickで、html内の入力フォームデータをDBに書きこむ

htmlのフォームから飛ばしたデータをrubyで処理する方法を紹介。
簡易的なサービスを想定しているので、
WEBrick+SQlite3を使用。
SQliteはdbiライブラリで扱っている。

【index.html】
# ヘッダー等は割愛
<form action="entry">
<input type="text" name="name">
<input type="submit" value=" 送信 ">
</form>

【server.rb】
#! /usr/local/bin/ruby -Ks
# -*- encoding: UTF-8 -*-

require 'rubygems'
require 'webrick'
require 'erb'
require 'dbi'

config = {
:port => 8099,
:DocumentRoot => '.'
}

server = WEBrick::HTTPServer.new( config )

server.mount_proc("/entry") { |req, res|
dbh = DBI.connect( 'DBI:SQlite3:test.db' )
dbh.do("insert into date
values('#{req.query['name']}');")
dbh.disconnect

template = ERB.new( File.read('entried.html') )
res.body << template.result( binding )
}

trap(:INT) do
server.shutdown
end

server.start

【entried.html】
登録完了

action名と同じ名前をmount_porcでしてあげると、それ以下の処理をしてくれる。
その際templateで返答するデータを決めているので、html形式で返す(erbなどでもOK)
trapはControl+cでサーバを停止させる仕組み。

ちなみに
WEBrick::HTMLServlet::FileHandler.add_handler("erb", WEBrick::HTTPServlet::ERBHandler)
とserver = WEBrick::HTTPServer.new( config )の下あたりにでも追記すると、拡張子erbを処理済みのファイルとして扱えるようになる。

セキュリティレベルは推して知るべし。

2011年3月13日日曜日

Ruby on railsでacts_as_taggableの導入にはまった話

通常通りacts_as_taggableを入れると
acts_as_taggable_on_steroids has been moved to github: http://github.com/jviney/acts_as_taggable_on_steroids
とエラー表示が出る。

なので、プロジェクトフォルダに移動して、
# ruby script/plugin install http://github.com/jviney/acts_as_taggable_on_steroids
もしすでにインストールされてるよ!と怒られたら、一度アンインストールするか、もしくは
# ruby script/plugin install http://github.com/jviney/acts_as_taggable_on_steroids --force
で、OK

Ruby on railsでfile_columnの導入にはまった話

uninitialized constant FileColumn::ClassMethods::Inflector
という表示がされて動かない。

plugins/file_column/lib/file_column.rb

my_options = FileColumn::init_options(options,
Inflector.underscore(self.name).to_s,
attr.to_s)

my_options = FileColumn::init_options(options,
self.name.to_s.underscore,
attr.to_s)

に書き換えれば動くらしい。

参考:
http://d.hatena.ne.jp/aki-s-119/20081201/1228168584

Ruby on railsでrestful_authenticationの導入にはまった話

現時点で、techno-weenie.netからrestful_authenticationプラグインを入れることができない。
代わりにgemとして用意されている。

# gem install restful_authentication

それにともない、machinistと、fakerが必要になる。
# gem install machinest
# gem install faker
# gem install aasm

で、enviroment.rbに以下を追記
config.gem "restful_authentication", :version => ">= 1.1.6"
config.gem "aasm", :version => ">= 2.1.5"
config.gem "faker"
config.gem "machinist"

で、マイグレーションをして、完了。

Ruby on railsでgettextの導入にはまった話。

Rails of ruby on railsを参考に導入したらはまった。
どうやらgettextのバージョンが変わって、呼び出すときに
gettext/railsでは呼び出せなくなっていて、
代わりにgettext_railsが独立したgemとして用意されるようになったみたい。

だから
# gem install gettext_rails
をする必要がある。

また設定も、enviroment.rbに

Rails::Initializer.run do |config|
……

config.gem "locale_rails"
config.gem "gettext_activerecord"
config.gem "gettext"
config.gem "gettext_rails"
……
end
の記述が必要となった。

さらにgettext_railsにはバグがあり、
i18.rb内のLocale.xxx(いろんな関数)のすべての部分を::Local.xxxに書き換え擦る必要がある。

2011年3月6日日曜日

ruby on railsのユーザー認証「restful_authentication」導入まで

# gem install restful_authentication
# gem install machinst
# gem install faker
# gem install aasm

で、構成のenviroment.rbファイルのendのうえあたりに
config.gem "restful_authentication", :version => ">= 1.1.6"
config.gem "aasm", :version => ">= 2.1.5"
config.gem "faker"
config.gem "machinist"
を追加。

2011年3月5日土曜日

正規表現チェック

このサイトを使うと正規表現をチェックしながらかけるので、かなり便利。
http://www.rubular.com/

serversmanで再起動できないときの対処法

普通にログインしようとすると、
PTY allocation request failed on channel 0
と表示が出て、ログインできなかった。

原因を調べると、 Airdisplayでログアウトしてなかったのが、すべてプロセスとして残って、そのせいでSSHのログイン上限数を超えてしまったかららしい。

MyDTIからサーバーを再起動して、ログイン出来る状態にした。

2011年3月3日木曜日

serversmanでrubyのtwitter_oauthを入れるまで

管理者ユーザの作成
# useradd user
# passwd user

パスワードを2回入力

sudoの許可
# visudo
「# %wheel ALL=(ALL) ALL」の#を削除

作ったユーザを管理者IDとして設定
# usermod -G wheel user

動くか試す。
# ssh user@xxx.xxx.xxx.xxx
# su

うごいたら
# cd
# sudo vi /etc/ssh/sshd_config

で、「# PermitRootLogin yes」の下に
「PermitRootLogin no」を追加。

設定はこれで終了なので再起動
# sudo /etc/rc.d/init.d/sshd restart

rootでログイン出来ないことを確認。

で、必要そうなパッケージのインストール
# wget http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.i386.rpm
# rpm -Uhv rpmforge-release-0.5.2-2.el5.rf.i386.rpm
#yum update
#yum -y install apt
# apt-get update

rubyのインストール
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz
# tar zxvf ruby-1.8.7-p72.tar.gz
# cd ruby-1.8.7-p72
# ./configure --prefix=/usr
# make && make install

rubygemsのインストール
# wget 'http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz'
# tar -zxvf rubygems-1.3.5.tgz
# cd rubygems-1.3.5
# ruby setup.rb

zlibのインストール
# yum install zlib-devel
# ruby extconf.rb --with-zlib-include=/usr/include -with-zlib-lib=/usr/lib
# make && make install

OpenSSLのインストール
# yum install openssl-devel
# cd ruby-1.8.7-p249/ext/openssl
# ruby extconf.rb --with-openssl-dir=/usr/local/ssl
# make && make install

最後にtwitter_oauthのインストール
# gem install twitter_oauth

もしかしたら何か足りないかも。

2011年3月2日水曜日

Serversman設定リセット

serversmanの設定をリセット。
なんどもやり直せるってすてき☆

やり方は、MyDTIにログイン後、「契約中サービス」>「各種初期化」でできる。

その後Macないに以前のRSAキーが残っているので、
# cd ~/.ssh
# ls -al
# rm known_hosts

でOK、前と同じように接続できるようになる。

serversmanにruby環境を構築する

ServersmanのVPSを借りた。

いろいろはまったけど、無事ruby1.8をインストールできたので、そのメモ

ターミナルの[シェル]>[新規リモート接続]>[ssh]を選択
ユーザー欄にはユーザー名
下の欄「〇〇.〇〇.〇〇.〇〇 -p ポート番号」を入れる。
デフォルトではポート番号22で接続するようになっているが、serversmanでは別に設定してあるので 「-p ポート番号」で指定してあげる必要がある。

その後パスワードを入力すれば、接続完了。


そのまま入れるとrubyをいれるとバージョンが古いのでサーバーからダウンロードして入れる。

まずはコンパイラのインストール
# yum install gcc


その後は、
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz
# tar zxvf ruby-1.8.7-p72.tar.gz
# cd ruby-1.8.7-p72
# ./configure --prefix=/usr
# make && make install

で完了。

checkinstallを使う方法があるらしいのだけど、うまくインストールできなかった。

rubygemsも同じ感じで
# wget http://rubyforge.org/frs/download.php/43985/rubygems-1.3.0.tgz
# tar zxvf rubygems-1.3.0.tgz
# cd rubygems-1.3.0
# ruby setup.rb

2011年2月28日月曜日

2月成果報告

とりあえずやろうと思ってた物は作れた。
月一ペースでサービスを提供していこうという試みは今のところ成功している。

3月は、メアドと地域、洗車の間隔を登録しておけば、洗車の周期近くで晴れてる週末が着たら、自動的にメールでお知らせするサービス作る。

WEB上での登録作業が必要なので、RoRとMySQLの簡単なヤツが必要になるんじゃないかと思ってる。

2011年2月26日土曜日

Twitter投稿

ローカル環境の日付がおかしいと、updateで投稿できないっぽい。

2011年2月12日土曜日

ローカル変数の扱いがわからない

というメモ。あとで解決する。

!= で否定形として使える。

while 〇〇 != △△ && □□ < 5

〇〇が△△とイコールになるまで、
または□□が5以下の間は繰り返すということができる。

2011年1月29日土曜日

Mac Book AirにRAMDISK作った

Chromeがときどき固まってうざかったから。

以下やったこと。
esperance DVってソフトをここから落とした。
http://download.cnet.com/Esperance-DV/3000-2094_4-10558573.html

Macintosh HD/ライブラリ/PreferencePanes/
の中に、EsperanceDV.prefPaneを移動。

システム環境設定から、起動して、ログイン時に自動的に起動するように設定。

で、ターミナルで

cd /Users/username/Library/Caches/Google/Chrome/Default

rm -rf ./Cache

ln -s /Volumes/.RamDisk/ /Users/username/Library/Caches/Google/Chrome/Default/Cache

って順番に入力した。
以上。

今日から始めるRuby開発

とりあえずTwitterの検索結果をブログに投稿するスクリプトを組む