2010-02-20
¢£[gae]GoogleAppEngine/JRuby+Rails¤Çscaffold¤«¤éTwitterBot¤Þ¤Ç
Ruby(MRI),Rails2.3.5,Java1.6¤Ï¥¤¥ó¥¹¥È¡¼¥ëºÑ¤ß¤«¤é¥¹¥¿¡¼¥È
(Java1.5¤Ê¾ì¹ç¤Ï1.5¤Ç¤âOK¤«¤â¤·¤ì¤Ê¤¤¤Î¤Ç¤½¤Î¤Þ¤Þ¤ä¤Ã¤Æ¤ß¤Æ¾å¼ê¤¯½ÐÍ褿¤é¶µ¤¨¤Æ¤¯¤À¤µ¤¤¡£)
% java -version java version "1.6.0_03-p3" Java(TM) SE Runtime Environment (build 1.6.0_03-p3-landonf_19_aug_2008_14_55-b00) Java HotSpot(TM) Server VM (build 1.6.0_03-p3-landonf_19_aug_2008_14_55-b00, mixed mode) % ruby -v ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin8] % rails -v Rails 2.3.5
google-appengine¤Î¥¤¥ó¥¹¥È¡¼¥ë
% sudo gem install google-appengine Successfully installed appengine-rack-0.0.6 Successfully installed appengine-sdk-1.3.0 Successfully installed appengine-jruby-jars-0.0.6 Successfully installed bundler-0.8.1 Successfully installed appengine-tools-0.0.9 Successfully installed google-appengine-0.0.9 6 gems installed # ¥Ð¡¼¥¸¥ç¥ó³Îǧ % gem list | grep -E "(appe|bundler)" appengine-jruby-jars (0.0.6) appengine-rack (0.0.6) appengine-sdk (1.3.0) appengine-tools (0.0.9) bundler (0.8.1) google-appengine (0.0.9)
Rails,scaffold
¤ò¤ä¤ê¤Þ¤¹¡£DataMapper¤ò»È¤¤¤¿¤¤¤Ê¤éhttp://gist.github.com/268192
% mkdir mybot
% cd mybot
% wget http://appengine-jruby.googlecode.com/hg/demos/rails2/rails2_td_appengine.rb
% ruby rails2_td_appengine.rb
=> Bundling gems
Calculating dependencies...
Updating source: http://gems.rubyforge.org
Caching: actionmailer-2.3.5.gem
Caching: actionpack-2.3.5.gem
Caching: activerecord-2.3.5.gem
Caching: activeresource-2.3.5.gem
Caching: activesupport-2.3.5.gem
Downloading appengine-apis-0.0.12.gem
Caching: appengine-rack-0.0.6.gem
Caching: builder-2.1.2.gem
Caching: i18n-0.3.3.gem
Caching: rack-1.0.1.gem
Caching: rails-2.3.5.gem
Caching: rails_tiny_ds-0.0.2.gem
Caching: rake-0.8.7.gem
Caching: tiny_ds-0.0.2.gem
Caching: tzinfo-0.3.16.gem
Installing builder (2.1.2)
Installing activesupport (2.3.5)
Installing i18n (0.3.3)
Installing actionmailer (2.3.5)
Installing rack (1.0.1)
Installing actionpack (2.3.5)
Installing appengine-rack (0.0.6)
Installing appengine-apis (0.0.12)
Installing tiny_ds (0.0.2)
Installing rake (0.8.7)
Installing activerecord (2.3.5)
Installing activeresource (2.3.5)
Installing rails (2.3.5)
Installing rails_tiny_ds (0.0.2)
Installing tzinfo (0.3.16)
Done.
=> Packaging gems
=> Installing JRuby
=> Installing JRuby-Rack
=> Installing appengine-sdk
=> Generating configuration files
exists
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create config/initializers
create config/locales
create db
create doc
create lib
create lib/tasks
create log
create public/images
create public/javascripts
create public/stylesheets
create script/performance
create test/fixtures
create test/functional
create test/integration
create test/performance
create test/unit
create vendor
create vendor/plugins
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create Rakefile
create README
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create config/database.yml
create config/routes.rb
create config/locales/en.yml
create db/seeds.rb
create config/initializers/backtrace_silencers.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/new_rails_defaults.rb
create config/initializers/session_store.rb
create config/environment.rb
create config/boot.rb
create config/environments/production.rb
create config/environments/development.rb
create config/environments/test.rb
create script/about
create script/console
create script/dbconsole
create script/destroy
create script/generate
create script/runner
create script/server
create script/plugin
create script/performance/benchmarker
create script/performance/profiler
create test/test_helper.rb
create test/performance/browsing_test.rb
create public/404.html
create public/422.html
create public/500.html
create public/index.html
identical public/favicon.ico
create public/robots.txt
create public/images/rails.png
create public/javascripts/prototype.js
create public/javascripts/effects.js
create public/javascripts/dragdrop.js
create public/javascripts/controls.js
create public/javascripts/application.js
create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log
+ ./LICENSE
+ ./README
+ ./Rakefile
+ ./init.rb
+ ./lib/active_record/connection_adapters/nulldb_adapter.rb
+ ./lib/nulldb_rspec.rb
+ ./spec/nulldb_spec.rb
+ ./tasks/database.rake
##
## Now type 'dev_appserver.rb .'
##
% dev_appserver.rb .
=> Booting DevAppServer
=> Press Ctrl-C to shutdown server
=> Generating configuration files
SKIP: add_gem_load_paths
SKIP: add_gem_load_paths
SKIP: add_gem_load_paths
["Dir.glob2", ["/Users/takeru/demo/rails-tokyo-48/mybot/app/metal/**/*.rb"], []]
SKIP: add_gem_load_paths
The server is running at http://localhost:8080/
¥Ö¥é¥¦¥¶¤Ç http://localhost:8080/ ¤ò³«¤¯¡£
¡ÖAbout your application¡Çs environment¡×¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¡ÖRuby version 1.8.7 (java)¡×Åù¤¬¸«¤ì¤ì¤Ð£Ï£Ë¡£
# addr/port¤ò»ØÄꤹ¤ë¤Ë¤Ï ¢ª dev_appserver.rb -a 0.0.0.0 -p 3000 .
¤¤¤¯¤Ä¤«MRI¤Îrubygems¤Ë¥¤¥ó¥¹¥È¡¼¥ë¡£
% sudo gem install rails -v "2.3.5" % sudo gem install rails_tiny_ds % sudo gem install activerecord-nulldb-adapter
./script/generate scaffold article title:string summary:text url:string pages:integer -f --skip-migration ./script/generate td_model article title:string summary:text url:string pages:integer -f
# º£²ó¤Ïtime¤Èlist¤Ï¤¤¤í¤¤¤íÌÌÅݤʤΤÇscaffold¤Ç¤Ï̵¤·¤Ç¡£
http://localhost:8080/articles
¤ò³«¤¤¤Æ³Îǧ¡£
¥Ç¥×¥í¥¤
AppEngine¤Çapp-id¤ò¼èÆÀ¡£
https://appengine.google.com/start/createapp
config.ru¤Î¡Ö:application => 'application-id'¡×¤ÎÉôʬ¤ò¼èÆÀ¤·¤¿app-id¤Ë½ñ¤´¹¤¨¤ë¡£
% appcfg.rb update .
http://[application-id].appspot.com/
¤¬¸«¤ì¤ì¤Ð¥Ç¥×¥í¥¤À®¸ù¡£
http://[application-id].appspot.com/articles
¤ÇRails¤Îưºî¤ò³Îǧ¡£(articles¤ÎÊý¤¬¥¨¥é¡¼¤Î¾ì¹ç¢ª¤³¤Îµ»ö¤ÎºÇ¸å¤ò»²¾È)
Twitter Bot
lib¤Ësimple-oauth.rb¤Ètwitter_api.rb¤òÆþ¤ì¤ë¡£
wget http://github.com/shibason/rb-simple-oauth/raw/master/simple-oauth.rb
1²Õ½ê¤À¤±½¤Àµ¡£
request['Authorization'] = auth_header(method, url, request.body)
- Net::HTTP.new(url.host, url.port).request(request)
+ Net::HTTP.new(url.host, url.port).request(request, request.body)
end
class TwitterAPI # TwitterAPI.new.fetch_friend_ids(:screen_name=>"urekat") # TwitterAPI.new.fetch_friend_ids(:id=>5416352) def fetch_friend_ids(params) request_json(:get, "http://twitter.com/friends/ids.json", params) end # TwitterAPI.new.fetch_follower_ids(:screen_name=>"urekat") # TwitterAPI.new.fetch_follower_ids(:id=>5416352) def fetch_follower_ids(params) request_json(:get, "http://twitter.com/followers/ids.json", params) end # TwitterAPI.new.fetch_user(:screen_name=>"urekat") # TwitterAPI.new.fetch_user(:id=>5416352) def fetch_user(params) request_json(:get, "http://twitter.com/users/show.json", params) end # pp TwitterAPI.new.tweet("hey") def tweet(status) request_json(:post, 'http://twitter.com/statuses/update.json', {:status=>status}) end def request_json(method, url, params={}) response = case method when :get unless params.blank? url = url + "?" + params.collect{|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&") end response = simple_oauth.get(url) when :post response = simple_oauth.post(url, params) else raise "unknown method #{method}" end if response.code!="200" raise "request failed code=#{response.code} method=#{method} url=#{url}" end obj = JSON.parse(response.body) if obj.kind_of?(Hash) && obj["error"] raise "error response: #{obj.inspect}" end obj end def simple_oauth unless @simple_oauth secret = $secret["twitter"] @simple_oauth ||= SimpleOAuth.new(secret["CONSUMER_KEY"], secret["CONSUMER_SECRET"], secret["TOKEN"], secret["TOKEN_SECRET"]) end @simple_oauth end # ruby -rlib/twitter_api.rb -e "TwitterAPI.get_token" def self.get_token require 'rubygems' require 'oauth' print "Consumer Key: " consumer_key = gets.chomp.strip print "Consumer Secret: " consumer_secret = gets.chomp.strip consumer = OAuth::Consumer.new(consumer_key, consumer_secret, :site => 'http://twitter.com') request_token = consumer.get_request_token puts "Access this URL and approve => #{request_token.authorize_url}" print "Input OAuth Verifier: " oauth_verifier = gets.chomp.strip access_token = request_token.get_access_token( :oauth_verifier => oauth_verifier ) puts "Access token: #{access_token.token}" puts "Access token secret: #{access_token.secret}" end end
botÍѤÎtwitter¤Î¥¢¥«¥¦¥ó¥È¤ò¼èÆÀ¤¹¤ë¡£
¼èÆÀ¤·¤¿botÍÑ¥¢¥«¥¦¥ó¥È¤Ç¥í¥°¥¤¥ó¤·¤Æ¡¢twitter¤Î¥¢¥×¥êÅÐÏ¿¤ò¤¹¤ë¡£
Application Type¤ÏClient¤Ç¡£
ConsumerKey¤ÈConsumerSecret¤ò¼¡¤Ç»È¤¤¤Þ¤¹¡£
% ruby -rlib/twitter_api.rb -e "TwitterAPI.get_token" Consumer Key: aaaaaaaaaaaaaaaaa Consumer Secret: bbbbbbbbbbbbbbbbb Access this URL and approve => http://twitter.com/oauth/authorize?oauth_token=xxxxxxxxxxxxx Input OAuth Verifier: 9999999 Access token: ccccccccccccccccc Access token secret: ddddddddddddddddd
config/initializer/secret.rb¤òÍѰդ¹¤ë¡£
$secret = {
"twitter" => {
"CONSUMER_KEY" => "aaaaaaaaaaaaaaaaa",
"CONSUMER_SECRET" => "bbbbbbbbbbbbbbbbb",
"TOKEN" => "ccccccccccccccccc",
"TOKEN_SECRET" => "ddddddddddddddddd"
}
}
config/environment.rb¤ÎºÇ¸å¤Ë
require "simple-oauth"
¤òÄɲá£
Gemfile¤ÎºÇ¸å¤Ë
gem 'json-jruby'
¤òÄɲá£
appcfg.rb run -S irb -r config/environment
> TwitterAPI.new.fetch_user(:screen_name=>"urekat")
> TwitterAPI.new.fetch_user(:id=>5416352)
> TwitterAPI.new.fetch_friend_ids(:screen_name=>"urekat")
> TwitterAPI.new.fetch_follower_ids(:screen_name=>"urekat")
> TwitterAPI.new.tweet("hey")
¤½¤Î¾¤Ï http://apiwiki.twitter.com/ ¤ò»²¾È¡£
Cron¤Ç¤Ä¤Ö¤ä¤¯
class TaskController < ApplicationController skip_before_filter :verify_authenticity_token before_filter :check_cron_header def check_cron_header if Rails.env=="development" return end case @action_name when /^cron_/ if request.headers["X-AppEngine-Cron"]!="true" render :text=>"not cron" end when /^job_/ # X-AppEngine-QueueName, the name of the queue (possibly default) # X-AppEngine-TaskName, the name of the task, or a system-generated unique ID if no name was specified # X-AppEngine-TaskRetryCount, the number of times this task has been retried; for the first attempt, this value is 0 if request.headers["X-AppEngine-QueueName"].nil? render :text=>"not job" end else render :text=>"OK" end end def cron_tweet TwitterAPI.new.tweet("Hey, it's #{Time.now}.") render :text=>"OK" end end
Cron¤Î¼Â¹Ô´Ö³Ö¤ÎÀßÄê¥Õ¥¡¥¤¥ë¤ò½ñ¤¤Þ¤¹¡£
./cron.yaml
cron: - description: tweet url: /task/cron_tweet schedule: every 3 minutes
¥Ð¥°²óÈò¤Î¤¿¤áWEB-INF/cron.xml¤òºî¤ë¡£(¼¡¥ê¥ê¡¼¥¹¤Ç½¤Àµ¤µ¤ì¤Þ¤¹¡£)
<?xml version="1.0" encoding="UTF-8"?><cronentries/>
¥Ç¥×¥í¥¤¤¹¤ì¤Ð3ʬ¤´¤È¤Ë»þ¹ï¤ò¤Ä¤Ö¤ä¤¯¡£¤Ï¤º¡£
¥í¥°¤ò¸«¤ë
¤Ë¹Ô¤Ã¤Æ¼«Ê¬¤Î¥¢¥×¥ê¤ò³«¤¡¢[Logs] ¢ªMinimum Severity:[Request Only]¤Ë¤¹¤ë¡£
spinup/spindown
AppEngine¤Ï¥µ¡¼¥Ð¥ê¥½¡¼¥¹¤ÎÀáÌó¤Î¤¿¤á¡¢¥ê¥¯¥¨¥¹¥È¤¬Ìµ¤¤¥¢¥×¥ê¥¤¥ó¥¹¥¿¥ó¥¹¤ò
¿ôʬ¤Ç¥·¥ã¥Ã¥È¥À¥¦¥ó¤µ¤»¤Þ¤¹(spindown)¡£
¥·¥ã¥Ã¥È¥À¥¦¥ó¤·¤Æ¤¤¤ë¥¢¥×¥ê¤ËÂФ·¤Æ¥¢¥¯¥»¥¹¤¹¤ë¤È
AppEngine¤Ï¡Ö¿·¤·¤¤¥¤¥ó¥¹¥¿¥ó¥¹¤òÀ¸À®(spinup)¡×¤·¤Æ¤«¤é¥ê¥¯¥¨¥¹¥È¤ò½èÍý¤·¤Þ¤¹¡£
AppEngine¤Ë¤Ï¡Ö30ÉÃ°ÊÆâ¤Ë½èÍý¤ò´°Î»¤·¤Ê¤¤¤È¶¯À©½ªÎ»¡×¤È¤¤¤¦¥ë¡¼¥ë¤¬¤¢¤ê¡¢
¤³¤Î¥ë¡¼¥ë¤Ïspinup¤Ë¤âŬÍѤµ¤ì¤Þ¤¹¡£
º¤¤Ã¤¿¤³¤È¤Ë¡¢JRuby+Rails¤Î¥¢¥×¥ê¤Ïspinup¤Ë30Éö᤯¤«¤«¤ê¤Þ¤¹(appengine-jruby-jars-0.0.6¤Î¾ì¹ç)¡£
¤®¤ê¤®¤ê29ÉäÇspinup¤Ç¤¤ì¤Ð£Ï£Ë¤Ç¤¹¤¬¡¢30Éäòͤ¨¤ë¤È500¥¨¥é¡¼¤Ç¤¹¡£
spinup¤·¤¿¤¢¤È¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ÏÉáÄ̤˿ô½½¡Á¿ôÉ´¥ß¥êÉäǽèÍý¤òÊÖ¤·¤Þ¤¹¡£
¤·¤«¤·¿ôʬ¥¢¥¯¥»¥¹¤¬Ìµ¤¤¤Èspindown¤·¤Æ¤·¤Þ¤¦¤Î¤Ç¡¢¼¡²ó¤Þ¤¿spinup¤«¤é¤ä¤êľ¤·¤Ç¤¹¡£
(JRuby1.5¤ò´Þ¤ó¤Àappengine-jruby-jars-0.0.7¤¬¥ê¥ê¡¼¥¹¤µ¤ì¤ë¤È16-18É䰤餤¤Ë¤Ê¤ê¤Þ¤¹¡£)
ºÇ¸å¤Î¼ê½ç
- http://twitter.com/rubyist_sg ¤ò¥Õ¥©¥í¡¼¤¹¤ë¡£
- http://rubyist-sg.appspot.com/ ¤Ë¤Î¤ë¡£
- RubyistSocialGraph¤òTwitter¤ÇÀëÅÁ¤¹¤ë
- 64 http://blog.yuyat.jp/archives/395
- 42 http://wiki.fdiary.net/rails/?RailsMeetingTokyo-0048
- 33 http://www.google.co.jp/search?q=jruby+on+rails+gae&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja-JP-mac:official&hl=ja&client=firefox-a
- 28 http://brass.to/blog/railstokyo-48.html
- 28 http://pipes.yahoo.com/pipes/pipe.info?_id=faa858a20082ef6d25ad27557e37e011
- 23 http://reader.livedoor.com/reader/
- 18 http://longurl.org
- 18 http://www.google.co.jp/search?hl=ja&lr=lang_ja&tbs=lr:lang_1ja&q=twitter+api+gem+timezone&aq=f&aqi=&aql=&oq=&gs_rfai=
- 15 http://www.google.co.jp/search?sourceid=chrome&ie=UTF-8&q=rails+unique+id
- 14 http://twitter.com/
