soap4r/wsdl解析処理を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#dbc333ce]
soap4rではwsdlを指定することで、Webサービスのoperation呼...
その際、
service = SOAP::WSDLDriverFactory.new(wsdlLocation).crea...
と記述します。今回はWSDLDriverFactory.newの部分を読むこと...
なお、今回対象としたバージョンは1.5.5です。soap4rはまだま...
*SOAP::WSDLDriverFactory(soap/wsdlDriver.rb) [#f229fe34]
ではまずSOAP::WSDLDriverFactoryです。initializeメソッドは...
def initialize(wsdl)
@wsdl = import(wsdl)
@methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@...
end
1行目のimportメソッドが解析処理をしていると考えられるので...
def import(location)
WSDL::Importer.import(location)
end
というわけで、WSDL::Importerに%%丸投げ%%委譲しています。R...
def one
1
end
と書けばoneメソッドの戻り値は1になります。
*WSDL::Importer(wsdl/importer.rb) [#u45cc0a3]
では、WSDL::Importer.importです。
def self.import(location, originalroot = nil)
new.import(location, originalroot)
end
Rubyを知らない人のためにその2ですがRubyではクラスメソッド...
さて本題に戻って、WSDL::Importerオブジェクトを作成し、イ...
*WSDL::XMLSchema::Importer(wsdl/xmlSchema/importer.rb) [#...
まず、initializeメソッド。@web_clientをnilにしています。
def initialize
@web_client = nil
end
次に、importメソッド。
def import(location, originalroot = nil)
unless location.is_a?(URI)
location = URI.parse(location)
end
content = parse(fetch(location), location, originalroot)
content.location = location
content
end
locationがURIオブジェクトでないならURIオブジェクトにして...
import(String location) {
import(new URI(location));
}
みたいにオーバーロードを用意することになることでしょう。
さて読解に戻って、locationで指定されたwsdlをfetchし、それ...
まず、fecthメソッドを見てみましょう。fetchメソッドではloc...
client = web_client.new(nil, "WSDL4R")
client.proxy = ::SOAP::Env::HTTP_PROXY
client.no_proxy = ::SOAP::Env::NO_PROXY
if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyN...
::SOAP::HTTPConfigLoader.set_options(client,
opt["client.protocol.http"])
end
content = client.get_content(location)
web_clientはwsdlをhttp経由で取得するためのクラスを返すメ...
def web_client
@web_client ||= begin
require 'http-access2'
if HTTPAccess2::VERSION < "2.0"
raise LoadError.new("http-access/2.0 or later ...
end
HTTPAccess2::Client
rescue LoadError
warn("Loading http-access2 failed. Net/http is ...
require 'soap/netHttpClient'
::SOAP::NetHttpClient
end
@web_client
end
まず、http-access2のロードおよびバージョンの確認を行って...
さてfetchメソッドに戻って次の行、環境変数からプロキシを設...
次のSOAP::Property.loadproperty(SOAP::PropertyName)ではプ...
*SOAP::Property(soap/property.rb) [#ea4b93a6]
**プロパティの読み込みとセット [#yefd5c9d]
さて、SOAP::Property.loadpropertyメソッドですが、SOAP::Pr...
def loadproperty(propname)
return loadpropertyfile(propname) if File.file?(propna...
$:.each do |path|
if File.file?(file = File.join(path, propname))
return loadpropertyfile(file)
end
end
nil
end
カレント以下にsoap/propertyファイルがあればそれを、なけれ...
self[key] = value
なので、次の[]=メソッドが呼ばれます。
def []=(name, value)
name_pair = name_to_a(name).freeze
hooks = assign(name_pair, value)
hooks.each do |hook|
hook.call(name_pair, value)
end
value
end
name_to_aメソッド ではnameはStringのはずなので以下のコ...
name.scan(/[^.\\]+(?:\\.[^.\\])*/) # split with unescape...
name.split(/\./)じゃ駄目なのでしょうか?
さて、[]=メソッドに戻って続きを見るとassignメソッドが呼ば...
def assign(ary, value)
ref = self
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
hook + ref.local_hook(last_key, true)
end
ary[0..-2]ではaryとして["aaa", "bbb", "ccc"]を想定してい...
def deref_key(key)
check_lock(key)
ref = @store[key] ||= self.class.new
unless propkey?(ref)
raise ArgumentError.new("key `#{key}' already define...
end
ref
end
@storeはHashでkeyとvalueの対応が登録されているようです。...
SOAP::Property(root): "aaa" => SOAP::Property("aaa")
SOAP::Property("aaa"): "bbb" => SOAP::Property("bbb")
みたいな階層構造が構築され、ループが終了した時点でrefにSO...
さて、assignメソッドに戻ると配列の最後"ccc"についてlocal_...
def local_assign(key, value)
check_lock(key)
if @locked
if propkey?(value)
raise FrozenError.new("cannot add any key to locke...
elsif propkey?(@store[key])
raise FrozenError.new("cannot override any key in ...
end
end
@store[key] = value
end
ロックはされていないはずなのでさくっと無視すると、selfは...
SOAP::Property("bbb"): "ccc" => value
が設定されます。[]=メソッドに戻ると登録されているフックが...
**プロパティのゲット [#n18ca000]
続いてプロパティのゲットを見てみましょう。[]メソッドはこ...
def [](name)
referent(name_to_a(name))
end
で、referentメソッド。
def referent(ary)
ary[0..-2].inject(self) { |ref, name|
ref.deref_key(to_key(name))
}.local_referent(to_key(ary.last))
end
injectメソッドはリファレンスマニュアルの説明によると、
最初に初期値 init と self の最初の要素を引数にブロック ...
2 回目以降のループでは、前のブロックの実行結果と self の...
引数に順次ブロックを実行します。そうして最 後の要素まで...
最後のブロックの実行結果を返します。
とのことなので、injectメソッドが実行されていくと
SOAP::Property(root) => SOAP::Property("aaa") => SOAP::P...
ということで、SOAP::Property("bbb")がinjectメソッドの結果...
unless Enumerable.instance_methods.include?('inject')
moduleにあるメソッドが含まれいるかどうかは上記のように調...
次にlocal_referentメソッドです。
def local_referent(key)
check_lock(key)
if propkey?(@store[key]) and @store[key].locked?
raise FrozenError.new("cannot split any key from loc...
end
@store[key]
end
というわけで、SOAP::Property("bbb")に登録されている"ccc"...
**フックの登録と呼び出し [#ob3cb227]
さて、WSDL::XMLSchema::Importer#fetchメソッドに戻るとSOAP...
client.proxy = options["proxy"]
options.add_hook("proxy") do |key, value|
client.proxy = value
end
というわけで、先ほど無視したフックが設定されています。設...
def add_hook(name = nil, cascade = false, &hook)
if name == nil or name == true or name == false
cascade = name
assign_self_hook(cascade, &hook)
else
assign_hook(name_to_a(name), cascade, &hook)
end
end
上記の例では"proxy"という値が渡されるのでassign_hookの方...
def local_assign_hook(key, cascade, &hook)
check_lock(key)
@store[key] ||= nil
(@hook[key] ||= []) << [hook, cascade]
end
というわけで"proxy"にフックが登録されます。
フックがいつ呼ばれるかを確認するためにもう一度assignメソ...
def assign(ary, value)
ref = self
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
hook + ref.local_hook(last_key, true)
end
というわけで、local_hookメソッドが階層構造の途中の要素で...
def local_hook(key, direct)
hooks = []
(@self_hook + (@hook[key] || NO_HOOK)).each do |hook, ...
hooks << hook if direct or cascade
end
hooks
end
というわけで階層の途中ではcascade=trueと指定されている場...
*SOAP::NetHttpClient(soap/netHttpClient.rb) [#d1bdf617]
ではそろそろメインの処理に戻りましょう。get_contentメソッ...
-startメソッドをブロック付きで呼び出す
--create_connectionメソッドを呼び出す
---プロキシやSSLを考慮してNet::HTTPオブジェクトを生成
--httpセッションを開始し、渡されたブロックを呼び出す。ブ...
-渡されたurlをget。結果、レスポンスが戻り値としてstartメ...
-レスポンスのbodyを返す
*WSDL::Parser(wsdl/parser.rb) [#v46a249f]
ようやくWSDLが取得できました。次に、WSDL::XMLSchema::Impo...
def parse(content, location, originalroot)
opt = {
:location => location,
:originalroot => originalroot
}
begin
WSDL::Parser.new(opt).parse(content)
rescue WSDL::Parser::ParseError
super(content, location, originalroot)
end
end
というわけでWSDL::Parserに処理が移っています。なお、origi...
まず、initializeメソッドです。
def initialize(opt = {})
@parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil
@lastnode = nil
@ignored = {}
@location = opt[:location]
@originalroot = opt[:originalroot]
end
で、parseメソッド。
def parse(string_or_readable)
@parsestack = []
@lastnode = nil
@textbuf = ''
@parser.do_parse(string_or_readable)
@lastnode
end
initializeメソッドでselfを渡しているのでXSD::XMLParser#do...
*XSD::XMLParser(xsd/xmlparser.rb) [#f7558157]
create_parserメソッドは次の通りです。
def create_parser(host, opt)
XSD::XMLParser::Parser.create_parser(host, opt)
end
う〜ん、何でこんな構造になっているのでしょう?ともかくXSD...
def self.create_parser(host, opt = {})
@@parser_factory.new(host, opt)
end
@@parser_factoryは?と上を見るとnilになっています。このま...
[
'xsd/xmlparser/xmlparser',
'xsd/xmlparser/xmlscanner',
'xsd/xmlparser/rexmlparser',
].each do |lib|
begin
require lib
loaded = true
break
rescue LoadError
end
end
というわけで複数のparser候補を順にrequireしてみて使えるpa...
class REXMLParser < XSD::XMLParser::Parser
(途中省略)
add_factory(self)
end
add_factoryメソッド。
def self.add_factory(factory)
@@parser_factory = factory
end
というわけでxsd/xmlparser/rexmlparser.rbが読み込まれると@...
以上のことからdo_parseメソッドが呼ばれるとREXMLParser#do_...
def do_parse(string_or_readable)
source = nil
source = REXML::SourceFactory.create_from(string_or_re...
source.encoding = charset if charset
# Listener passes a String in utf-8.
@charset = 'utf-8'
REXML::Document.parse_stream(source, self)
end
というわけでREXMLのStreamParserを使って解析を行っています...
def tag_start(name, attrs)
start_element(name, attrs)
end
def tag_end(name)
end_element(name)
end
def text(text)
characters(text)
end
スーパークラスであるXSD::XMLParser::Parserのstart_element...
def start_element(name, attrs)
@host.start_element(name, attrs)
end
def characters(text)
@host.characters(text)
end
def end_element(name)
@host.end_element(name)
end
これで、WSDL::Parserに制御が戻ってきました。
*WSDL::Parser再び [#z604e5d7]
さて、というわけでWSDLの各タグが見つかるとWSDL::Parser#st...
def start_element(name, attrs)
lastframe = @parsestack.last
ns = parent = nil
if lastframe
ns = lastframe.ns.clone_ns
parent = lastframe.node
else
ns = XSD::NS.new
parent = nil
end
attrs = XSD::XMLParser.filter_ns(ns, attrs)
node = decode_tag(ns, name, attrs, parent)
@parsestack << ParseFrame.new(ns, name, node)
end
decode_tagメソッドで渡されたタグに対するノードを作成し、...
def decode_tag(ns, name, attrs, parent)
o = nil
elename = ns.parse(name)
if !parent
if elename == DefinitionsName
o = Definitions.parse_element(elename)
o.location = @location
else
raise UnknownElementError.new("unknown element: #{...
end
o.root = @originalroot if @originalroot # o.root =...
else
if elename == XMLSchema::AnnotationName
(省略)
else
o = parent.parse_element(elename)
end
(省略)
# node could be a pseudo element. pseudo element ha...
o.root = parent.root
o.parent = parent if o.parent.nil?
end
attrs.each do |key, value|
attr_ele = ns.parse(key, true)
value_ele = ns.parse(value, true)
value_ele.source = value # for recovery; value may ...
unless o.parse_attr(attr_ele, value_ele)
(省略)
end
end
o
end
まず前半部分。parent => nilなのでDefinitions.parse_elemen...
次に、各属性について今生成したDefinitionsオブジェクトのpa...
次にstart_elementメソッドが呼ばれるとスタック上にあるDefi...
*WSDL::Import(wsdl/import.rb) [#xaba967c]
ツリーの構築は大体タグに対応するRubyオブジェクトを生成し...
def parse_attr(attr, value)
case attr
(中略)
when LocationAttrName
@location = URI.parse(value.source)
if @location.relative? and !parent.location.nil? and
!parent.location.relative?
@location = parent.location + @location
end
if root.importedschema.key?(@location)
@content = root.importedschema[@location]
else
root.importedschema[@location] = nil # placeh...
@content = import(@location)
if @content.is_a?(Definitions)
@content.root = root
if @namespace
@content.targetnamespace = @namespace
end
end
root.importedschema[@location] = @content
end
@location
else
nil
end
end
まず、相対パスなら絶対パスに変換しています。次にすでにimp...
次に、importメソッドです。
def import(location)
Importer.import(location, root)
end
というわけで、WSDL::Importerを用いて再帰的に解析が行われ...
*おわりに [#wcec87cb]
今回はsoap4rのうち、wsdl解析処理を読みました。今回学んだ...
-xmlからRubyオブジェクトを構築する方法
-利用可能なライブラリの検出、ラッピング方法
-メソッドが定義されているかの確認方法
-Ruby的なコードの読み方:-)
それではみなさんもよいコードリーディングを。
終了行:
#contents
*はじめに [#dbc333ce]
soap4rではwsdlを指定することで、Webサービスのoperation呼...
その際、
service = SOAP::WSDLDriverFactory.new(wsdlLocation).crea...
と記述します。今回はWSDLDriverFactory.newの部分を読むこと...
なお、今回対象としたバージョンは1.5.5です。soap4rはまだま...
*SOAP::WSDLDriverFactory(soap/wsdlDriver.rb) [#f229fe34]
ではまずSOAP::WSDLDriverFactoryです。initializeメソッドは...
def initialize(wsdl)
@wsdl = import(wsdl)
@methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@...
end
1行目のimportメソッドが解析処理をしていると考えられるので...
def import(location)
WSDL::Importer.import(location)
end
というわけで、WSDL::Importerに%%丸投げ%%委譲しています。R...
def one
1
end
と書けばoneメソッドの戻り値は1になります。
*WSDL::Importer(wsdl/importer.rb) [#u45cc0a3]
では、WSDL::Importer.importです。
def self.import(location, originalroot = nil)
new.import(location, originalroot)
end
Rubyを知らない人のためにその2ですがRubyではクラスメソッド...
さて本題に戻って、WSDL::Importerオブジェクトを作成し、イ...
*WSDL::XMLSchema::Importer(wsdl/xmlSchema/importer.rb) [#...
まず、initializeメソッド。@web_clientをnilにしています。
def initialize
@web_client = nil
end
次に、importメソッド。
def import(location, originalroot = nil)
unless location.is_a?(URI)
location = URI.parse(location)
end
content = parse(fetch(location), location, originalroot)
content.location = location
content
end
locationがURIオブジェクトでないならURIオブジェクトにして...
import(String location) {
import(new URI(location));
}
みたいにオーバーロードを用意することになることでしょう。
さて読解に戻って、locationで指定されたwsdlをfetchし、それ...
まず、fecthメソッドを見てみましょう。fetchメソッドではloc...
client = web_client.new(nil, "WSDL4R")
client.proxy = ::SOAP::Env::HTTP_PROXY
client.no_proxy = ::SOAP::Env::NO_PROXY
if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyN...
::SOAP::HTTPConfigLoader.set_options(client,
opt["client.protocol.http"])
end
content = client.get_content(location)
web_clientはwsdlをhttp経由で取得するためのクラスを返すメ...
def web_client
@web_client ||= begin
require 'http-access2'
if HTTPAccess2::VERSION < "2.0"
raise LoadError.new("http-access/2.0 or later ...
end
HTTPAccess2::Client
rescue LoadError
warn("Loading http-access2 failed. Net/http is ...
require 'soap/netHttpClient'
::SOAP::NetHttpClient
end
@web_client
end
まず、http-access2のロードおよびバージョンの確認を行って...
さてfetchメソッドに戻って次の行、環境変数からプロキシを設...
次のSOAP::Property.loadproperty(SOAP::PropertyName)ではプ...
*SOAP::Property(soap/property.rb) [#ea4b93a6]
**プロパティの読み込みとセット [#yefd5c9d]
さて、SOAP::Property.loadpropertyメソッドですが、SOAP::Pr...
def loadproperty(propname)
return loadpropertyfile(propname) if File.file?(propna...
$:.each do |path|
if File.file?(file = File.join(path, propname))
return loadpropertyfile(file)
end
end
nil
end
カレント以下にsoap/propertyファイルがあればそれを、なけれ...
self[key] = value
なので、次の[]=メソッドが呼ばれます。
def []=(name, value)
name_pair = name_to_a(name).freeze
hooks = assign(name_pair, value)
hooks.each do |hook|
hook.call(name_pair, value)
end
value
end
name_to_aメソッド ではnameはStringのはずなので以下のコ...
name.scan(/[^.\\]+(?:\\.[^.\\])*/) # split with unescape...
name.split(/\./)じゃ駄目なのでしょうか?
さて、[]=メソッドに戻って続きを見るとassignメソッドが呼ば...
def assign(ary, value)
ref = self
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
hook + ref.local_hook(last_key, true)
end
ary[0..-2]ではaryとして["aaa", "bbb", "ccc"]を想定してい...
def deref_key(key)
check_lock(key)
ref = @store[key] ||= self.class.new
unless propkey?(ref)
raise ArgumentError.new("key `#{key}' already define...
end
ref
end
@storeはHashでkeyとvalueの対応が登録されているようです。...
SOAP::Property(root): "aaa" => SOAP::Property("aaa")
SOAP::Property("aaa"): "bbb" => SOAP::Property("bbb")
みたいな階層構造が構築され、ループが終了した時点でrefにSO...
さて、assignメソッドに戻ると配列の最後"ccc"についてlocal_...
def local_assign(key, value)
check_lock(key)
if @locked
if propkey?(value)
raise FrozenError.new("cannot add any key to locke...
elsif propkey?(@store[key])
raise FrozenError.new("cannot override any key in ...
end
end
@store[key] = value
end
ロックはされていないはずなのでさくっと無視すると、selfは...
SOAP::Property("bbb"): "ccc" => value
が設定されます。[]=メソッドに戻ると登録されているフックが...
**プロパティのゲット [#n18ca000]
続いてプロパティのゲットを見てみましょう。[]メソッドはこ...
def [](name)
referent(name_to_a(name))
end
で、referentメソッド。
def referent(ary)
ary[0..-2].inject(self) { |ref, name|
ref.deref_key(to_key(name))
}.local_referent(to_key(ary.last))
end
injectメソッドはリファレンスマニュアルの説明によると、
最初に初期値 init と self の最初の要素を引数にブロック ...
2 回目以降のループでは、前のブロックの実行結果と self の...
引数に順次ブロックを実行します。そうして最 後の要素まで...
最後のブロックの実行結果を返します。
とのことなので、injectメソッドが実行されていくと
SOAP::Property(root) => SOAP::Property("aaa") => SOAP::P...
ということで、SOAP::Property("bbb")がinjectメソッドの結果...
unless Enumerable.instance_methods.include?('inject')
moduleにあるメソッドが含まれいるかどうかは上記のように調...
次にlocal_referentメソッドです。
def local_referent(key)
check_lock(key)
if propkey?(@store[key]) and @store[key].locked?
raise FrozenError.new("cannot split any key from loc...
end
@store[key]
end
というわけで、SOAP::Property("bbb")に登録されている"ccc"...
**フックの登録と呼び出し [#ob3cb227]
さて、WSDL::XMLSchema::Importer#fetchメソッドに戻るとSOAP...
client.proxy = options["proxy"]
options.add_hook("proxy") do |key, value|
client.proxy = value
end
というわけで、先ほど無視したフックが設定されています。設...
def add_hook(name = nil, cascade = false, &hook)
if name == nil or name == true or name == false
cascade = name
assign_self_hook(cascade, &hook)
else
assign_hook(name_to_a(name), cascade, &hook)
end
end
上記の例では"proxy"という値が渡されるのでassign_hookの方...
def local_assign_hook(key, cascade, &hook)
check_lock(key)
@store[key] ||= nil
(@hook[key] ||= []) << [hook, cascade]
end
というわけで"proxy"にフックが登録されます。
フックがいつ呼ばれるかを確認するためにもう一度assignメソ...
def assign(ary, value)
ref = self
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
hook + ref.local_hook(last_key, true)
end
というわけで、local_hookメソッドが階層構造の途中の要素で...
def local_hook(key, direct)
hooks = []
(@self_hook + (@hook[key] || NO_HOOK)).each do |hook, ...
hooks << hook if direct or cascade
end
hooks
end
というわけで階層の途中ではcascade=trueと指定されている場...
*SOAP::NetHttpClient(soap/netHttpClient.rb) [#d1bdf617]
ではそろそろメインの処理に戻りましょう。get_contentメソッ...
-startメソッドをブロック付きで呼び出す
--create_connectionメソッドを呼び出す
---プロキシやSSLを考慮してNet::HTTPオブジェクトを生成
--httpセッションを開始し、渡されたブロックを呼び出す。ブ...
-渡されたurlをget。結果、レスポンスが戻り値としてstartメ...
-レスポンスのbodyを返す
*WSDL::Parser(wsdl/parser.rb) [#v46a249f]
ようやくWSDLが取得できました。次に、WSDL::XMLSchema::Impo...
def parse(content, location, originalroot)
opt = {
:location => location,
:originalroot => originalroot
}
begin
WSDL::Parser.new(opt).parse(content)
rescue WSDL::Parser::ParseError
super(content, location, originalroot)
end
end
というわけでWSDL::Parserに処理が移っています。なお、origi...
まず、initializeメソッドです。
def initialize(opt = {})
@parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil
@lastnode = nil
@ignored = {}
@location = opt[:location]
@originalroot = opt[:originalroot]
end
で、parseメソッド。
def parse(string_or_readable)
@parsestack = []
@lastnode = nil
@textbuf = ''
@parser.do_parse(string_or_readable)
@lastnode
end
initializeメソッドでselfを渡しているのでXSD::XMLParser#do...
*XSD::XMLParser(xsd/xmlparser.rb) [#f7558157]
create_parserメソッドは次の通りです。
def create_parser(host, opt)
XSD::XMLParser::Parser.create_parser(host, opt)
end
う〜ん、何でこんな構造になっているのでしょう?ともかくXSD...
def self.create_parser(host, opt = {})
@@parser_factory.new(host, opt)
end
@@parser_factoryは?と上を見るとnilになっています。このま...
[
'xsd/xmlparser/xmlparser',
'xsd/xmlparser/xmlscanner',
'xsd/xmlparser/rexmlparser',
].each do |lib|
begin
require lib
loaded = true
break
rescue LoadError
end
end
というわけで複数のparser候補を順にrequireしてみて使えるpa...
class REXMLParser < XSD::XMLParser::Parser
(途中省略)
add_factory(self)
end
add_factoryメソッド。
def self.add_factory(factory)
@@parser_factory = factory
end
というわけでxsd/xmlparser/rexmlparser.rbが読み込まれると@...
以上のことからdo_parseメソッドが呼ばれるとREXMLParser#do_...
def do_parse(string_or_readable)
source = nil
source = REXML::SourceFactory.create_from(string_or_re...
source.encoding = charset if charset
# Listener passes a String in utf-8.
@charset = 'utf-8'
REXML::Document.parse_stream(source, self)
end
というわけでREXMLのStreamParserを使って解析を行っています...
def tag_start(name, attrs)
start_element(name, attrs)
end
def tag_end(name)
end_element(name)
end
def text(text)
characters(text)
end
スーパークラスであるXSD::XMLParser::Parserのstart_element...
def start_element(name, attrs)
@host.start_element(name, attrs)
end
def characters(text)
@host.characters(text)
end
def end_element(name)
@host.end_element(name)
end
これで、WSDL::Parserに制御が戻ってきました。
*WSDL::Parser再び [#z604e5d7]
さて、というわけでWSDLの各タグが見つかるとWSDL::Parser#st...
def start_element(name, attrs)
lastframe = @parsestack.last
ns = parent = nil
if lastframe
ns = lastframe.ns.clone_ns
parent = lastframe.node
else
ns = XSD::NS.new
parent = nil
end
attrs = XSD::XMLParser.filter_ns(ns, attrs)
node = decode_tag(ns, name, attrs, parent)
@parsestack << ParseFrame.new(ns, name, node)
end
decode_tagメソッドで渡されたタグに対するノードを作成し、...
def decode_tag(ns, name, attrs, parent)
o = nil
elename = ns.parse(name)
if !parent
if elename == DefinitionsName
o = Definitions.parse_element(elename)
o.location = @location
else
raise UnknownElementError.new("unknown element: #{...
end
o.root = @originalroot if @originalroot # o.root =...
else
if elename == XMLSchema::AnnotationName
(省略)
else
o = parent.parse_element(elename)
end
(省略)
# node could be a pseudo element. pseudo element ha...
o.root = parent.root
o.parent = parent if o.parent.nil?
end
attrs.each do |key, value|
attr_ele = ns.parse(key, true)
value_ele = ns.parse(value, true)
value_ele.source = value # for recovery; value may ...
unless o.parse_attr(attr_ele, value_ele)
(省略)
end
end
o
end
まず前半部分。parent => nilなのでDefinitions.parse_elemen...
次に、各属性について今生成したDefinitionsオブジェクトのpa...
次にstart_elementメソッドが呼ばれるとスタック上にあるDefi...
*WSDL::Import(wsdl/import.rb) [#xaba967c]
ツリーの構築は大体タグに対応するRubyオブジェクトを生成し...
def parse_attr(attr, value)
case attr
(中略)
when LocationAttrName
@location = URI.parse(value.source)
if @location.relative? and !parent.location.nil? and
!parent.location.relative?
@location = parent.location + @location
end
if root.importedschema.key?(@location)
@content = root.importedschema[@location]
else
root.importedschema[@location] = nil # placeh...
@content = import(@location)
if @content.is_a?(Definitions)
@content.root = root
if @namespace
@content.targetnamespace = @namespace
end
end
root.importedschema[@location] = @content
end
@location
else
nil
end
end
まず、相対パスなら絶対パスに変換しています。次にすでにimp...
次に、importメソッドです。
def import(location)
Importer.import(location, root)
end
というわけで、WSDL::Importerを用いて再帰的に解析が行われ...
*おわりに [#wcec87cb]
今回はsoap4rのうち、wsdl解析処理を読みました。今回学んだ...
-xmlからRubyオブジェクトを構築する方法
-利用可能なライブラリの検出、ラッピング方法
-メソッドが定義されているかの確認方法
-Ruby的なコードの読み方:-)
それではみなさんもよいコードリーディングを。
ページ名: