[ap4r-devel] [260] branches/200709_gihyo/async_shop: Modified: asynchronization.
kato-k at rubyforge.org
kato-k at rubyforge.org
Fri Aug 24 05:31:35 EDT 2007
Revision: 260
Author: kato-k
Date: 2007-08-24 05:31:34 -0400 (Fri, 24 Aug 2007)
Log Message:
-----------
Modified: asynchronization.
Modified Paths:
--------------
branches/200709_gihyo/async_shop/as_rails/app/controllers/async_shop_controller.rb
Added Paths:
-----------
branches/200709_gihyo/async_shop/as_ap4r/
branches/200709_gihyo/async_shop/as_ap4r/config/
branches/200709_gihyo/async_shop/as_ap4r/config/ap4r_settings.rb
branches/200709_gihyo/async_shop/as_ap4r/config/log4r.yaml
branches/200709_gihyo/async_shop/as_ap4r/config/queues.cfg
branches/200709_gihyo/async_shop/as_ap4r/config/queues_disk.cfg
branches/200709_gihyo/async_shop/as_ap4r/config/queues_mysql.cfg
branches/200709_gihyo/async_shop/as_ap4r/log/
branches/200709_gihyo/async_shop/as_ap4r/public/
branches/200709_gihyo/async_shop/as_ap4r/script/
branches/200709_gihyo/async_shop/as_ap4r/script/irm
branches/200709_gihyo/async_shop/as_ap4r/script/loop.cmd
branches/200709_gihyo/async_shop/as_ap4r/script/loop.rb
branches/200709_gihyo/async_shop/as_ap4r/script/mongrel_ap4r
branches/200709_gihyo/async_shop/as_ap4r/script/start
branches/200709_gihyo/async_shop/as_ap4r/script/stop
branches/200709_gihyo/async_shop/as_ap4r/tmp/
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/init.rb
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/ap4r_client.rb
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/async_helper.rb
branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/message_builder.rb
Added: branches/200709_gihyo/async_shop/as_ap4r/config/ap4r_settings.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/config/ap4r_settings.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/config/ap4r_settings.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,5 @@
+Ap4r::Configuration.setup {|services|
+ services.add 'queues_disk.cfg', :host => 'localhost', :name => :rm1
+# services.add 'queues.cfg', :name => :rm2
+}
+
Added: branches/200709_gihyo/async_shop/as_ap4r/config/log4r.yaml
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/config/log4r.yaml (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/config/log4r.yaml 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,61 @@
+purpose : Test
+description: This is the YAML doc
+ this yaml has many many unnecessary setting
+ for testing/explanation only.
+ edit properly for normal use.
+
+---
+
+log4r_config:
+ # define all pre config ...
+ pre_config:
+ global:
+ level: DEBUG
+ root :
+ level: DEBUG
+ parameters:
+ - name : x
+ value : aaa
+ - name : y
+ value : bbb
+
+ # define all loggers ...
+ loggers:
+ - name : qm_logger
+ level : DEBUG
+ additive : 'false'
+ trace : 'false'
+ outputters:
+ - stderr
+ - logfile
+
+ - name : yourlogger #not used yet...
+ level : INFO
+ outputters:
+ - stderr
+ - logfile
+
+ # define all outputters (incl. formatters)
+ outputters:
+ - type : StderrOutputter
+ name : stderr
+ level : DEBUG
+ only_at :
+ - INFO
+ - WARN
+ - FATAL
+ formatter:
+ date_pattern: '%y%m%d %H:%M:%S'
+ pattern : '%d %l: %m '
+ type : PatternFormatter
+
+ - type : DateFileOutputter
+ name : logfile
+ level : DEBUG
+ date_pattern: '%Y%m%d'
+ trunc : 'false'
+ dirname : "#{HOME}/logs"
+ formatter :
+ date_pattern: '%y%m%d %H:%M:%S'
+ pattern : '%d %l: %m'
+ type : PatternFormatter
Added: branches/200709_gihyo/async_shop/as_ap4r/config/queues.cfg
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/config/queues.cfg (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/config/queues.cfg 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,20 @@
+---
+store:
+ type: mysql
+ host: localhost
+ database: test
+ username: test
+ password:
+drb:
+ host:
+ port: 6438
+ acl: allow 127.0.0.1
+dispatchers:
+ -
+ targets: queue.*
+ threads: 1
+#carriers:
+# -
+# source_uri: druby://another.host.local:6438
+# threads: 1
+
Added: branches/200709_gihyo/async_shop/as_ap4r/config/queues_disk.cfg
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/config/queues_disk.cfg (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/config/queues_disk.cfg 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,15 @@
+---
+store:
+ type: disk
+drb:
+ host:
+ port: 6438
+ acl: allow 127.0.0.1 allow ::1 allow 10.0.0.0/8
+dispatchers:
+ -
+ targets: queue.*
+ threads: 1
+#carriers:
+# -
+# source_uri: druby://another.host.local:6438
+# threads: 1
Added: branches/200709_gihyo/async_shop/as_ap4r/config/queues_mysql.cfg
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/config/queues_mysql.cfg (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/config/queues_mysql.cfg 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,19 @@
+---
+store:
+ type: mysql
+ host: localhost
+ database: ap4r
+ username: ap4r
+ password: ap4r
+drb:
+ host:
+ port: 6438
+ acl: allow 127.0.0.1 allow 10.0.0.0/8 allow ::1
+dispatchers:
+ -
+ targets: queue.*
+ threads: 1
+#carriers:
+# -
+# source_uri: druby://another.host.local:6438
+# threads: 1
Added: branches/200709_gihyo/async_shop/as_ap4r/script/irm
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/irm (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/irm 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,4 @@
+require 'rubygems'
+require 'ap4r'
+
+require 'ap4r/util/irm'
Added: branches/200709_gihyo/async_shop/as_ap4r/script/loop.cmd
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/loop.cmd (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/loop.cmd 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,3 @@
+ at ECHO OFF
+
+ruby %~dp0loop.rb manager start -c queues%1.cfg
Added: branches/200709_gihyo/async_shop/as_ap4r/script/loop.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/loop.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/loop.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,8 @@
+load_path = "-Ilib/"
+
+loop{
+ puts; puts '=== queue manager starting ==='
+ system "ruby #{load_path} script/queues.rb #{ARGV.join(' ')}"
+ puts; puts '=== queue manager stopped ==='
+}
+
Added: branches/200709_gihyo/async_shop/as_ap4r/script/mongrel_ap4r
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/mongrel_ap4r (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/mongrel_ap4r 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,4 @@
+require 'rubygems'
+require 'ap4r'
+
+load 'ap4r/mongrel_ap4r.rb'
Added: branches/200709_gihyo/async_shop/as_ap4r/script/start
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/start (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/start 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,5 @@
+require 'rubygems'
+require 'ap4r/script/setup'
+
+require 'ap4r/script/queue_manager_control'
+Ap4r::Script::QueueManagerControl.new.start(ARGV)
Added: branches/200709_gihyo/async_shop/as_ap4r/script/stop
===================================================================
--- branches/200709_gihyo/async_shop/as_ap4r/script/stop (rev 0)
+++ branches/200709_gihyo/async_shop/as_ap4r/script/stop 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1 @@
+
Modified: branches/200709_gihyo/async_shop/as_rails/app/controllers/async_shop_controller.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_rails/app/controllers/async_shop_controller.rb 2007-08-24 07:58:12 UTC (rev 259)
+++ branches/200709_gihyo/async_shop/as_rails/app/controllers/async_shop_controller.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -18,8 +18,10 @@
ActiveRecord::Base.transaction do
@order = Order.new(params[:order])
@order.save
- payment(@order[:id])
+ ap4r.async_to({:action => 'payment'},
+ {:order_id => @order.id})
+
flash[:notice] = 'Order was successfully created.'
redirect_to :action => 'list'
end
@@ -29,12 +31,13 @@
end
end
- def payment(order_id)
+ def payment
sleep 5
payment = Payment.new
- payment.order_id = order_id
+ payment.order_id = params[:order_id]
payment.save
+ render :text => "true"
end
def destroy
Added: branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/init.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/init.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/init.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,11 @@
+# Author:: Shunichi Shinohara
+# Copyright:: Copyright (c) 2007 Future Architect Inc.
+# Licence:: MIT Licence
+
+require 'ap4r_client'
+
+class ActionController::Base
+ def ap4r
+ @ap4r_client ||= ::Ap4r::Client.new(self)
+ end
+end
Added: branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/ap4r_client.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/ap4r_client.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/ap4r_client.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,132 @@
+# Author:: Kiwamu Kato
+# Copyright:: Copyright (c) 2007 Future Architect Inc.
+# Licence:: MIT Licence
+
+require 'forwardable'
+require 'async_helper'
+
+module Ap4r #:nodoc:
+
+ # This +Client+ is the Rails plugin for asynchronous processing.
+ # Asynchronous logics are called via various protocols, such as XML-RPC,
+ # SOAP, HTTP POST, and more. Now default protocol is HTTP POST.
+ #
+ # Examples: The part of calling next asynchronous logics in a controller in the HelloWorld Sample.
+ #
+ # req = WorldRequest.new([:world_id => 1, :message => "World"})
+ # ap4r.async_to({:controller => 'async_world', :action => 'execute'},
+ # {:world_id => 1, :message => "World"},
+ # {:dispatch_mode => :HTTP}) # skippable
+ #
+ # render :action => 'response'
+ #
+ # Complement: Above +ap4r+ method is defiend init.rb in +%RAILS_ROOT%/vendor/plugin/ap4r/+.
+ #
+ class Client
+ extend Forwardable
+ include ::Ap4r::AsyncHelper::Base
+
+ def initialize controller
+ @controller = controller
+ end
+
+ def_delegators :@controller, :logger, :url_for
+
+ # Queue a message for next asynchronous logic. Some options are supported.
+ #
+ # Use options to specify target url, etc.
+ # Accurate meanings are defined by a individual converter class.
+ # * :controller (name of next logic)
+ # * :action (name of next logic)
+ #
+ # Use rm_options to pass parameter in queue-put.
+ # Listings below are AP4R extended options.
+ # See the reliable-msg docuememt for more details.
+ # * :target_url (URL of target, prevail over :controller)
+ # * :target_action (action of target, prevail over :action)
+ # * :target_method (HTTP method, e.g. "GET", "POST", etc.)
+ # * :dispatch_mode (protocol in dispatching)
+ # * :queue_name (prevail over :controller and :action)
+ #
+ # Object of argumemts (async_params, options and rm_options) will not be modified.
+ # Implementors (of this class and converters) should not modify them.
+ #
+ # Examples: the most simple
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'},
+ # {:world_id => 1, :message => "World"})
+ #
+ #
+ # Examples: taking block
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'}) do
+ # body :world_id, 1
+ # body :message, "World"
+ # end
+ #
+ #
+ # Examples: transmitting ActiveRecord object
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'}) do
+ # body :world, World.find(1)
+ # body :message, "World"
+ # end
+ #
+ #
+ # Examples: transmitting with xml format over http (now support text, json and yaml format).
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'}) do
+ # body :world, World.find(1)
+ # body :message, "World"
+ # format :xml
+ # end
+ #
+ #
+ # Examples: direct assignment for formatted message body
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'}) do
+ # world = World.find(1).to_xml :except => ...
+ # body_as_xml world
+ # end
+ #
+ #
+ # Examples: setting message header
+ #
+ # ap4r.async_to({:controller => 'next_controller', :action => 'next_action'}) do
+ # body :world_id, 1
+ # body :message, "World"
+ #
+ # header :priority, 1
+ # http_header "Content-type", ...
+ # end
+ #
+ alias :async_to :async_dispatch
+
+ # Provides at-least-once QoS level.
+ # +block+ are tipically composed of database accesses and +async_to+ calls.
+ # Database accesses are executed transactionallly by +active_record_class+'s transaction method.
+ # In the +block+, +async_to+ calls invoke NOT immediate queueing but just storing messages
+ # to the database (assumed to be the same one as application uses).
+ #
+ # If the execution of +block+ finishes successfully, database transaction is committed and
+ # forward process of each stored message begins.
+ # Forward process composed in two parts. First puts the message into a queue, secondary update
+ # or delete the entry from a management table.
+ #
+ # SAF (store and forward) processing like this guarantees that any message
+ # is never lost and keeps reasonable performance (without two phase commit).
+ #
+ # Examples: Just call async_to method in this block.
+ #
+ # ap4r.transaction do
+ # req = WorldRequest.new([:world_id => 1, :message => "World"})
+ # ap4r.async_to({:controller => 'async_world', :action => 'execute'},
+ # {:world_id => 1, :message => "World"})
+ #
+ # render :action => 'response'
+ # end
+ #
+ alias :transaction :transaction_with_saf
+
+ end
+end
Added: branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/async_helper.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/async_helper.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/async_helper.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,262 @@
+# Author:: Shunichi Shinohara
+# Copyright:: Copyright (c) 2007 Future Architect Inc.
+# Licence:: MIT Licence
+
+require 'reliable-msg'
+require 'ap4r/stored_message'
+require 'message_builder'
+
+module Ap4r
+
+ # This +AsyncHelper+ is included to +Ap4rClient+ and works the Rails plugin
+ # for asynchronous processing.
+ #
+ module AsyncHelper
+
+ module Base
+ Converters = {}
+
+ DRUBY_HOST = ENV['AP4R_DRUBY_HOST'] || 'localhost'
+ DRUBY_PORT = ENV['AP4R_DRUBY_PORT'] || '6438'
+ DRUBY_URI = "druby://#{DRUBY_HOST}:#{DRUBY_PORT}"
+
+ @@default_dispatch_mode = :HTTP
+ @@default_rm_options = { :delivery => :once, :dispatch_mode => @@default_dispatch_mode }
+ @@default_queue_prefix = "queue."
+
+ mattr_accessor :default_dispatch_mode, :default_rm_options, :default_queue_prefix, :saf_delete_mode
+
+ # This method is aliased as ::Ap4r::Client#transaction
+ #
+ def transaction_with_saf(active_record_class = ::Ap4r::StoredMessage, *objects, &block)
+
+ Thread.current[:use_saf] = true
+ Thread.current[:stored_messages] = {}
+
+ # store
+ active_record_class ||= ::Ap4r::StoredMessage
+ active_record_class.transaction(*objects, &block)
+
+ # forward
+ forwarded_messages = {}
+ begin
+
+ # TODO: reconsider forwarding strategy, 2006/10/13 kato-k
+ # Once some error occured, such as disconnect reliable-msg or server crush,
+ # which is smart to keep to put a message or stop to do it?
+ # In the case of being many async messages, the former strategy is not so good.
+ #
+ # TODO: add delayed forward mode 2007/05/02 by shino
+ Thread.current[:stored_messages].each {|k,v|
+ __queue_put(v[:queue_name], v[:queue_message], v[:queue_headers])
+ forwarded_messages[k] = v
+ }
+ rescue Exception => err
+ # Don't raise any Exception. Application logic has already completed and messages are saved.
+ logger.warn("Failed to put a message into queue: #{err}")
+ end
+
+ begin
+ StoredMessage.transaction do
+ options = {:delete_mode => @@saf_delete_mode || :physical}
+ forwarded_messages.keys.each {|id|
+ ::Ap4r::StoredMessage.destroy_if_exists(id, options)
+ }
+ end
+ rescue Exception => err
+ # Don't raise any Exception. Application logic has already completed and messages are saved.
+ logger.warn("Failed to put a message into queue: #{err}")
+ end
+
+ ensure
+ Thread.current[:use_saf] = false
+ Thread.current[:stored_messages] = nil
+ end
+
+ # This method is aliased as ::Ap4r::Client#async_to
+ #
+ def async_dispatch(url_options = {}, async_params = {}, rm_options = {}, &block)
+
+ if logger.debug?
+ logger.debug("url_options: ")
+ logger.debug(url_options.inspect)
+ logger.debug("async_params: ")
+ logger.debug(async_params.inspect)
+ logger.debug("rm_options: ")
+ logger.debug(rm_options.inspect)
+ end
+
+ # TODO: clone it, 2006/10/16 shino
+ url_options ||= {}
+ url_options[:controller] ||= @controller.controller_path.gsub("/", ".")
+ url_options[:url] ||= {:controller => url_options[:controller], :action => url_options[:action]}
+ url_options[:url][:controller] ||= url_options[:controller] if url_options[:url].kind_of?(Hash)
+
+ rm_options = @@default_rm_options.merge(rm_options || {})
+
+ # Only async_params is not cloned. options and rm_options are cloned before now.
+ # This is a current contract between this class and converter classes.
+ converter = Converters[rm_options[:dispatch_mode]].new(url_options, async_params, rm_options, self)
+ logger.debug{"druby uri for queue-manager : #{DRUBY_URI}"}
+
+ queue_name = __get_queue_name(url_options, rm_options)
+ queue_message = converter.make_params
+ queue_headers = converter.make_rm_options
+
+ message_builder = ::Ap4r::MessageBuilder.new(queue_name, queue_message, queue_headers)
+ if block_given?
+ message_builder.instance_eval(&block)
+ end
+ queue_name = message_builder.queue_name
+ queue_message = message_builder.format_message_body
+ queue_headers = message_builder.message_headers
+
+ if Thread.current[:use_saf]
+ stored_message = ::Ap4r::StoredMessage.store(queue_name, queue_message, queue_headers)
+
+ Thread.current[:stored_messages].store(
+ stored_message.id,
+ {
+ :queue_message => queue_message,
+ :queue_name => queue_name,
+ :queue_headers => queue_headers
+ } )
+ return stored_message.id
+ end
+
+ __queue_put(queue_name, queue_message, queue_headers)
+ end
+
+ private
+ def __queue_put(queue_name, queue_message, queue_headers)
+ # TODO: can use a Queue instance repeatedly? 2007/05/02 by shino
+ q = ReliableMsg::Queue.new(queue_name, :drb_uri => DRUBY_URI)
+ q.put(queue_message, queue_headers)
+ end
+
+ def __get_queue_name(options, rm_options)
+ if options[:url].kind_of?(Hash)
+ rm_options[:queue_name] ||=
+ @@default_queue_prefix.clone.concat(options[:url][:controller].to_s).concat('.').concat(options[:url][:action].to_s)
+ else
+ rm_options[:queue_name] ||=
+ @@default_queue_prefix.clone.chomp(".").concat(URI::parse(options[:url]).path.gsub("/", "."))
+ end
+ rm_options[:queue_name]
+ end
+
+ end
+
+ module Converters #:nodoc:
+
+ # A base class for converter classes.
+ # Responsibilities of subclasses are as folows
+ # * by +make_params+, convert async_params to appropriate object
+ # * by +make_rm_options+, make appropriate +Hash+ passed by <tt>ReliableMsg::Queue#put</tt>
+ class Base
+
+ # Difine a constant +DISPATCH_MODE+ to value 'mode_symbol' and
+ # add self to a Converters list.
+ def self.dispatch_mode(mode_symbol)
+ self.const_set(:DISPATCH_MODE, mode_symbol)
+ ::Ap4r::AsyncHelper::Base::Converters[mode_symbol] = self
+ end
+
+ def initialize(url_options, async_params, rm_options, url_for_handler)
+ @url_options = url_options
+ @async_params = async_params
+ @rm_options = rm_options
+ @url_for_handler = url_for_handler
+ end
+
+ # Returns a object which passed to <tt>ReliableMsg::Queue.put(message, headers)</tt>'s
+ # first argument +message+.
+ # Should be implemented by subclasses.
+ def make_params
+ raise 'must be implemented in subclasses'
+ end
+
+ # Returns a object which passed to <tt>ReliableMsg::Queue.put(message, headers)</tt>'s
+ # second argument +headers+.
+ # Should be implemented by subclasses.
+ def make_rm_options
+ raise 'must be implemented in subclasses'
+ end
+
+ private
+ # helper method for <tt>ActionController#url_for</tt>
+ def url_for(url_for_options, *parameter_for_method_reference)
+ return url_for_options if url_for_options.kind_of?(String)
+ @url_for_handler.url_for(url_for_options, *parameter_for_method_reference)
+ end
+
+ end
+
+ class Http < Base
+ dispatch_mode :HTTP
+
+ def make_params
+ @async_params
+ end
+
+ def make_rm_options
+ @rm_options[:target_url] ||= url_for(@url_options[:url])
+ @rm_options[:target_method] ||= 'POST'
+ #TODO: make option key to specify HTTP headers, 2006/10/16 shino
+ @rm_options
+ end
+ end
+
+ class WebService < Base
+ def make_params
+ message_obj = {}
+ @async_params.each_pair{|k,v| message_obj[k.to_sym]=v}
+ message_obj
+ end
+
+ def make_rm_options
+ @rm_options[:target_url] ||= target_url_name
+ @rm_options[:target_action] ||= action_api_name
+ @rm_options
+ end
+
+ def action_api_name
+ action_method_name = @url_options[:url][:action]
+ action_method_name.camelcase
+ end
+
+ def options_without_action
+ @url_options[:url].reject{ |k,v| k == :action }
+ end
+
+ end
+
+ class XmlRpc < WebService
+ dispatch_mode :XMLRPC
+
+ def target_url_name
+ url_for(options_without_action) + rails_api_url_suffix
+ end
+
+ private
+ def rails_api_url_suffix
+ '/api'
+ end
+ end
+
+ class SOAP < WebService
+ dispatch_mode :SOAP
+
+ def target_url_name
+ url_for(options_without_action) + rails_wsdl_url_suffix
+ end
+
+ private
+ def rails_wsdl_url_suffix
+ '/service.wsdl'
+ end
+ end
+
+ end
+ end
+end
Added: branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/message_builder.rb
===================================================================
--- branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/message_builder.rb (rev 0)
+++ branches/200709_gihyo/async_shop/as_rails/vendor/plugins/ap4r/lib/message_builder.rb 2007-08-24 09:31:34 UTC (rev 260)
@@ -0,0 +1,181 @@
+# Author:: Kiwamu Kato
+# Copyright:: Copyright (c) 2007 Future Architect Inc.
+# Licence:: MIT Licence
+
+require 'active_record'
+
+module Ap4r #:nodoc:
+
+ # This +MessageBuilder+ is the class for formatting message body.
+ # Current support formats are text, xml, json and yaml,
+ # and the formatted messages are sent over HTTP.
+ #
+ # Using +format+ method, this class automatically changes the format of
+ # the given message body and adds appropriate +Content-type+ to http header.
+ # Or using +body_as_*+ methods, you can directly assign formatted message body.
+ class MessageBuilder
+
+ def initialize(queue_name, queue_message, queue_headers)
+ @queue_name = queue_name
+ @message_body = queue_message
+ @message_headers = queue_headers
+ @format = nil
+ @message_body_with_format = nil
+ @to_xml_options = {:root => "root"}
+ end
+
+ attr_accessor :queue_name, :message_body, :message_headers
+ attr_reader :format, :to_xml_options
+
+ # Sets message body in async_to block.
+ # The first argument is key and the second one is value.
+ #
+ # options are for to_xml conversion on Array and Hash, ActiveRecord objects.
+ #
+ def body(k, v, options = { })
+ k ||= v.class
+ if v.kind_of? ActiveRecord::Base
+ @message_body[k.to_sym] = v
+ @to_xml_options = @to_xml_options.merge(options)
+ else
+ @message_body[k.to_sym] = v
+ end
+ end
+
+ # Sets message header in async_to block.
+ # The first argument is key and the second one is value.
+ #
+ # Now supports following keys:
+ # :expire
+ # :priority
+ # :delivery
+ # :max_deliveries
+ # :dispatch_mode
+ # :target_method
+ # :target_url
+ # :id
+ #
+ # For details, please refer the reliable-msg.
+ #
+ def header(k, v)
+ @message_headers[k.to_sym] = v
+ end
+
+ # Sets http header in async_to block such as 'Content_type'.
+ # The first argument is key and the second one is value.
+ #
+ def http_header(k, v)
+ @message_headers["http_header_#{k}".to_sym] = v
+ end
+
+ # Sets format message serialization.
+ # As to the format, automatically sets content-type.
+ # Unless any format, content-type is defined as "application/x-www-form-urlencoded".
+ #
+ def format(v)
+ case @format = v
+ when :text
+ set_content_type("text/plain")
+ when :xml
+ set_content_type("text/xml application/x-xml")
+ when :json
+ set_content_type("application/json")
+ when :yaml
+ set_content_type("text/plain text/yaml")
+ else
+ set_content_type("application/x-www-form-urlencoded")
+ end
+ end
+
+ # Sets text format message. No need to use +format+.
+ #
+ def body_as_text(text)
+ @message_body_with_format = text
+ format :text
+ end
+
+ # Sets xml format message. No need to use +format+.
+ #
+ def body_as_xml(xml)
+ @message_body_with_format = xml
+ format :xml
+ end
+
+ # Sets json format message. No need to use +format+.
+ #
+ def body_as_json(json)
+ @message_body_with_format = json
+ format :json
+ end
+
+ # Sets yaml format message. No need to use +format+.
+ #
+ def body_as_yaml(yaml)
+ @message_body_with_format = yaml
+ format :yaml
+ end
+
+ # Return converted message body, as to assigned format.
+ #
+ def format_message_body
+ return @message_body_with_format if @message_body_with_format
+
+ case @format
+ when :text
+ return @message_budy.to_s
+ when :xml
+ return @message_body.to_xml @to_xml_options
+ when :json
+ return @message_body.to_json
+ when :yaml
+ return @message_body.to_yaml
+ else
+ @message_body.each do |k,v|
+ if v.kind_of? ActiveRecord::Base
+ @message_body[k] = v.attributes
+ end
+ end
+ return query_string(@message_body)
+ end
+ end
+
+ private
+ def query_string(hash)
+ build_query_string(hash, nil, nil)
+ end
+
+ def build_query_string(hash, query = nil, top = nil)
+ query ||= []
+ top ||= ""
+
+ _top = top.dup
+
+ hash.each do |k,v|
+ top = _top
+ top += top == "" ? "#{urlencode(k.to_s)}" : "[#{urlencode(k.to_s)}]"
+ if v.kind_of? Hash
+ build_query_string(v, query, top)
+ elsif v.kind_of? Array
+ v.each do |e|
+ query << "#{top}[]=#{urlencode(e.to_s)}"
+ end
+ else
+ query << "#{top}=#{urlencode(v.to_s)}"
+ end
+ end
+ query.join('&')
+ end
+
+ def simple_url_encoded_form_data(params, sep = '&')
+ params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
+ end
+
+ def urlencode(str)
+ str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
+ end
+
+ def set_content_type(type)
+ http_header("Content-type", type)
+ end
+ end
+end
More information about the ap4r-devel
mailing list