jcarley-simplews 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Miguel Vazquez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ = simplews
2
+
3
+ This library simplifies the creation of SOAP web services. It is based on
4
+ soap4r, and adds a DSL for serving methods that create automatically the WSDL.
5
+ Additionally, it offers support for asynchronous jobs.
6
+
7
+ Documentation and detailed examples available at http://bioinfovm05.dacya.ucm.es/simplews/index.html
8
+
9
+ == Note on Patches/Pull Requests
10
+
11
+ * Fork the project.
12
+ * Make your feature addition or bug fix.
13
+ * Add tests for it. This is important so I don't break it in a
14
+ future version unintentionally.
15
+ * Commit, do not mess with rakefile, version, or history.
16
+ (if you want to have your own version, that is fine but
17
+ bump version in a commit by itself I can ignore when I pull)
18
+ * Send me a pull request. Bonus points for topic branches.
19
+
20
+ == Copyright
21
+
22
+ Copyright (c) 2009 Miguel Vazquez. See LICENSE for details.
data/bin/start_jobs_ws ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'simplews'
4
+ require "optparse"
5
+ require 'simplews/rake'
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{ $_ } [options] <config_file>"
10
+
11
+ opts.on("-h", "--host HOST", String, "Select host interface, localhost by default") do |host|
12
+ options[:host] = host
13
+ end
14
+
15
+ opts.on("-p", "--port PORT", Integer, "Select interface port, 1984 by default") do |port|
16
+ options[:port] = port
17
+ end
18
+
19
+ opts.on("-n", "--name NAME", String, "Select the name of the web server interface, SimpleWS by default") do |name|
20
+ options[:name] = name
21
+ end
22
+
23
+ opts.on("-d", "--description DESCRIPTION", String, "Description of the service") do |description|
24
+ options[:description] = description
25
+ end
26
+
27
+ opts.on("-w", "--wsdl FILENAME", String, "Save the WSDL file") do |filename|
28
+ options[:wsdl] = filename
29
+ end
30
+
31
+ opts.on("-t", "--path PATH", String, "Work directory for the WS") do |filename|
32
+ options[:directory] = filename
33
+ end
34
+
35
+ end.parse!
36
+
37
+ filename = ARGV[0]
38
+
39
+ server = SimpleWS::Jobs.new(options[:name], options[:description], options[:host], options[:port], options[:directory]) do
40
+ actions = File.open(filename)do |f| f.read end
41
+ eval actions
42
+ end
43
+
44
+ server.wsdl(options[:wsdl]) if options[:wsdl]
45
+
46
+ trap('INT'){
47
+ puts "Stopping server"
48
+ server.shutdown
49
+ }
50
+
51
+ server.start
52
+
53
+
data/bin/start_ws ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'simplews'
4
+ require "optparse"
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: #{ $_ } [options] <config_file>"
9
+
10
+ opts.on("-h", "--host HOST", String, "Select host interface, localhost by default") do |host|
11
+ options[:host] = host
12
+ end
13
+
14
+ opts.on("-p", "--port PORT", Integer, "Select interface port, 1984 by default") do |port|
15
+ options[:port] = port
16
+ end
17
+
18
+ opts.on("-n", "--name NAME", String, "Select the name of the web server interface, SimpleWS by default") do |name|
19
+ options[:name] = name
20
+ end
21
+
22
+ opts.on("-d", "--description DESCRIPTION", String, "Description of the service") do |description|
23
+ options[:description] = description
24
+ end
25
+
26
+ opts.on("-w", "--wsdl FILENAME", String, "Save the WSDL file") do |filename|
27
+ options[:wsdl] = filename
28
+ end
29
+
30
+ end.parse!
31
+
32
+ filename = ARGV[0]
33
+
34
+ server = SimpleWS.new(options[:name], options[:description], options[:host], options[:port]) do
35
+ actions = File.open(filename)do |f| f.read end
36
+ eval actions
37
+ end
38
+
39
+ server.wsdl(options[:wsdl]) if options[:wsdl]
40
+
41
+ trap('INT'){
42
+ puts "Stopping server"
43
+ server.shutdown
44
+ }
45
+
46
+ server.start
47
+
48
+
@@ -0,0 +1,237 @@
1
+ require 'rake'
2
+
3
+ # Include the step_dependencies and step_def methods to simplify Pipelines. Steps depend on
4
+ # the step strictly above by default. The output of the step is save marshaled,
5
+ # except for Strings which are save as text. The input of the step, the output
6
+ # of the previous step if availabe is accessed with the input method
7
+ #
8
+ # Example::
9
+ #
10
+ # step_def :text do
11
+ # "Text to revert"
12
+ # end
13
+ #
14
+ # step_def :revert do
15
+ # text = input
16
+ # text.reverse
17
+ # end
18
+ #
19
+ module Rake::Pipeline
20
+
21
+ module Rake::Pipeline::Step
22
+
23
+ class << self
24
+
25
+ @@step_descriptions = {}
26
+ def step_descriptions
27
+ @@step_descriptions
28
+ end
29
+
30
+ def add_description(re, step, message)
31
+ @@step_descriptions[re] = "#{ step }: #{ message }"
32
+ end
33
+
34
+ @@last_step = nil
35
+ def step_dependencies(name, dependencies = nil)
36
+
37
+ re = Regexp.new(/(?:^|\/)#{name}\/.*$/)
38
+
39
+ # Take the last_description and associate it with the name
40
+ if Rake.application.last_description
41
+ add_description(re, name, Rake.application.last_description)
42
+ end
43
+
44
+ if dependencies.nil? && ! @@last_step.nil?
45
+ dependencies = @@last_step
46
+ end
47
+ @@last_step = name
48
+
49
+ # Generate the Hash definition
50
+ case
51
+ when dependencies.nil?
52
+ re
53
+ when String === dependencies || Symbol === dependencies
54
+ {re => lambda{|filename| filename.sub(name.to_s,dependencies.to_s) }}
55
+ when Array === dependencies
56
+ {re => lambda{|filename| dependencies.collect{|dep| filename.sub(name.to_s, dep.to_s) } }}
57
+ when Proc === dependencies
58
+ {re => dependencies}
59
+ end
60
+
61
+ end
62
+
63
+ def parse_filename(filename)
64
+ filename.match(/^(.*?)([^\/]*)\/([^\/]*)$/)
65
+ {
66
+ :prefix => $1 == "" ? "." : $1,
67
+ :step => $2,
68
+ :job => $3,
69
+ }
70
+ end
71
+ end
72
+ end
73
+
74
+ module Rake::Pipeline::Info
75
+
76
+ def self.info_file(filename)
77
+ info = Rake::Pipeline::Step.parse_filename(filename)
78
+ "#{info[:prefix]}/.info/#{info[:job]}.yaml"
79
+ end
80
+
81
+ def self.load_info(t)
82
+ filename = t.name
83
+ info_filename = info_file(filename)
84
+
85
+ if File.exists? info_filename
86
+ YAML.load(File.open(info_filename))
87
+ else
88
+ {}
89
+ end
90
+ end
91
+
92
+ def self.save_info(t, info = {})
93
+ filename = t.name
94
+ info_filename = info_file(filename)
95
+
96
+ FileUtils.mkdir_p File.dirname(info_filename) unless File.exists? File.dirname(info_filename)
97
+ File.open(info_filename,'w'){|file|
98
+ file.write YAML.dump info
99
+ }
100
+ end
101
+ end
102
+
103
+ @@steps = []
104
+
105
+ def steps
106
+ @@steps
107
+ end
108
+
109
+
110
+ NON_ASCII_PRINTABLE = /[^\x20-\x7e\s]/
111
+ def is_binary?(file)
112
+ binary = file.read(1024) =~ NON_ASCII_PRINTABLE
113
+ file.rewind
114
+ binary
115
+ end
116
+
117
+ def step_descriptions
118
+ Rake::Pipeline::Step.step_descriptions
119
+ end
120
+
121
+ def step_dependencies(*args)
122
+ Rake::Pipeline::Step.step_dependencies(*args)
123
+ end
124
+
125
+ def step_dir(step = nil, filename = nil)
126
+ filename ||= @@current_task.name
127
+ info = Rake::Pipeline::Step.parse_filename(filename)
128
+ "%s/%s" % [info[:prefix], step || info[:step]]
129
+ end
130
+
131
+ def input_filename(task = nil, step = nil)
132
+ task ||= @@current_task
133
+ if step.nil?
134
+ task.prerequisites.first
135
+ else
136
+ File.join(step_dir(step, task.name), job_name)
137
+ end
138
+ end
139
+
140
+ def output_filename(task = nil)
141
+ task ||= @@current_task
142
+ task.name
143
+ end
144
+
145
+
146
+ def infile(task, step = nil, &block)
147
+ filename = input_filename(task, step)
148
+ File.open(filename) do |f|
149
+ block.call(f)
150
+ end
151
+ end
152
+
153
+ def outfile(task, &block)
154
+ filename = output_filename(task)
155
+ File.open(filename, 'w') do |f|
156
+ block.call(f)
157
+ end
158
+ end
159
+
160
+ def load_input(task, step = nil)
161
+ infile(task, step) do |f|
162
+ if is_binary?(f)
163
+ Marshal.load(f)
164
+ else
165
+ f.read
166
+ end
167
+ end
168
+ end
169
+
170
+
171
+ def save_output(task, output)
172
+ case
173
+ when output.nil?
174
+ nil
175
+ when String === output
176
+ outfile(task){|f| f.write output }
177
+ else
178
+ outfile(task){|f| f.write Marshal.dump(output) }
179
+ end
180
+ end
181
+
182
+ # We cannot load the input variable before the block.call, so we need another method
183
+
184
+ # Load the input data from the previous step
185
+ def input(step = nil)
186
+ load_input(@@current_task, step) if @@current_task
187
+ end
188
+
189
+ if defined? SimpleWS::Jobs
190
+ def method_missing(symbol, *args)
191
+ if $_current_job.methods.include? symbol.to_s
192
+ $_current_job.send(symbol, *args)
193
+ else
194
+ raise "Method #{ symbol } not found"
195
+ end
196
+ end
197
+ else
198
+ # Announce steps
199
+ def step(state, message ="")
200
+ puts "#{ state }: #{ message }"
201
+ end
202
+
203
+ # Add values to the info file
204
+ def info(values = {})
205
+ info = Rake::Pipeline::Info.load_info(@@current_task)
206
+ info = info.merge values
207
+ Rake::Pipeline::Info.save_info(@@current_task, info)
208
+ info
209
+ end
210
+
211
+ def job_name
212
+ File.basename(@@current_task.name)
213
+ end
214
+ end
215
+
216
+ # Define a new step, it depends on the previously defined by default. It
217
+ # saves the output of the block so it can be loaded by the input method of
218
+ # the next step
219
+ def step_def(name, dependencies = nil, &block)
220
+ @@steps << name
221
+ rule step_dependencies(name, dependencies) do |t|
222
+
223
+ # Save the task object to be able to load the input
224
+ @@current_task = t
225
+
226
+ output = block.call(t)
227
+
228
+ save_output(t, output)
229
+ end
230
+
231
+ end
232
+ end
233
+
234
+ if __FILE__ == $0
235
+
236
+ p Rake::Pipeline::Info.info_file('work/diseases/t')
237
+ end
data/lib/simplews.rb ADDED
@@ -0,0 +1,425 @@
1
+ require 'rubygems'
2
+ #gem 'soap4r'
3
+ gem 'soap4r-ruby1.9'
4
+ require 'soap/rpc/standaloneServer'
5
+ require 'builder'
6
+
7
+
8
+ # SimpleWS is a class that wraps SOAP::RPC::StandaloneServer to ease the
9
+ # implementation of Web Services, specifically the generation of the
10
+ # +WSDL+ file. It provides a particular syntax that allows to specify
11
+ # the methods that are going to be served along with the types of the
12
+ # parameters and of the response so that the +WSDL+ can be generated
13
+ # accordingly. Actual Servers can be instances of this class where
14
+ # methods are assigned dynamically or of classes that extend SimpleWS.
15
+ # class TestWS < SimpleWS
16
+ # def hi(name)
17
+ # "Hi #{name}, how are you?"
18
+ # end
19
+ #
20
+ # def initialize(*args)
21
+ # super(*args)
22
+ # serve :hi, %w(name), :name => :string, :return => :string
23
+ #
24
+ # serve :bye, %w(name), :name => :string, :return => :string do |name|
25
+ # "Bye bye #{name}. See you soon."
26
+ # end
27
+ #
28
+ # end
29
+ # end
30
+ #
31
+ # # Creating a server and starting it
32
+ #
33
+ # server = TestWS.new("TestWS", "Greeting Services", 'localhost', '1984')
34
+ # server.wsdl("TestWS.wsdl")
35
+ #
36
+ # Thread.new do
37
+ # server.start
38
+ # end
39
+ #
40
+ # # Client code. This could be run in another process.
41
+ #
42
+ # driver = SimpleWS::get_driver('http://localhost:1984', "TestWS")
43
+ # puts driver.hi('Gladis') #=> "Hi Gladis, how are you?"
44
+ # puts driver.bye('Gladis') #=> "Bye bye Gladis. See you soon."
45
+ #
46
+
47
+
48
+
49
+ class SimpleWS < SOAP::RPC::StandaloneServer
50
+ # Saves method defined in the class to be served by the instances
51
+ INHERITED_METHODS = {} unless defined? INHERITED_METHODS
52
+
53
+ # This is a helper function for clients. Given the +url+ where the
54
+ # server is listening, as well as the name of the server, it can
55
+ # manually access the +wsdl+ function and retrieve the complete +WSDL+
56
+ # file. This works *only* in web servers of class SimpleWS, not on
57
+ # the general SOAP::RPC::StandaloneServer or any other type of web
58
+ # server.
59
+ def self.get_wsdl(url, name)
60
+ require 'soap/rpc/driver'
61
+ driver = SOAP::RPC::Driver.new(url, "urn:#{ name }")
62
+ driver.add_method('wsdl')
63
+ driver.wsdl
64
+ end
65
+
66
+ # Builds on the get_wsdl function to provide the client with a
67
+ # reference to the driver. Again, only works with SimpleWS servers.
68
+ def self.get_driver(url, name)
69
+ require 'soap/wsdlDriver'
70
+ require 'fileutils'
71
+
72
+ tmp = File.open("/tmp/simpleWS.wsdl",'w')
73
+ tmp.write SimpleWS::get_wsdl(url, name)
74
+ tmp.close
75
+ driver = SOAP::WSDLDriverFactory.new("/tmp/simpleWS.wsdl").create_rpc_driver
76
+ FileUtils.rm "/tmp/simpleWS.wsdl"
77
+
78
+ driver
79
+ end
80
+
81
+ attr_accessor :description
82
+
83
+ # Creates an instance of a Server. The parameter +name+ specifies the
84
+ # +namespace+ used in the +WSDL+, +description+ is the description
85
+ # also included in the +WSDL+. The parameters +host+ and +port+,
86
+ # specify where the server will be listening, they are parameters of
87
+ # the +super+ class SOAP::RPC::StandaloneServer, they are made
88
+ # explicit here because they are included in the +WSDL+ as well.
89
+ def initialize(name=nil, description=nil, host=nil, port=nil, *args, &block)
90
+ name ||= self.class.to_s
91
+ description ||= "Web Server for #{ name }"
92
+ host ||= "localhost"
93
+ port ||= "1984"
94
+
95
+ super(description, "urn:#{ name }", host, port, *args)
96
+
97
+ @host = host
98
+ @port = port
99
+ @name = name
100
+ @description = description
101
+ @messages = []
102
+ @operations = []
103
+ @bindings = []
104
+ @method_descriptions = {}
105
+ @method_order = []
106
+
107
+ if block_given?
108
+ instance_eval &block
109
+ end
110
+
111
+ puts "Server #{ name } at #{ host }:#{ port }"
112
+
113
+
114
+ desc "Return the WSDL describing the web server"
115
+ param_desc :return => "WSDL description"
116
+ serve :wsdl, %w(), :return => :string
117
+
118
+ desc "Return HMLT table with documentation for the web service"
119
+ param_desc :return => "HTML table with documentation"
120
+ serve :documentation, %w(), :return => :string
121
+
122
+ INHERITED_METHODS.each{|name, info|
123
+ @@last_description = info[:description]
124
+ @@last_param_description = info[:param_descriptions]
125
+ serve name, info[:args], info[:types], &info[:block]
126
+ }
127
+ end
128
+
129
+ def name=(name)
130
+ @name = name
131
+ appname = name
132
+ default_namespace = "urn:#{name}"
133
+ end
134
+
135
+ # Add a description for the next method served.
136
+ @@last_description = nil
137
+ @@last_param_description = nil
138
+ def desc(text = "")
139
+ @@last_description = text
140
+ end
141
+
142
+ # Add descriptions for the parameters of the next method served
143
+ def param_desc(param_descriptions = {})
144
+ @@last_param_description = {}
145
+ param_descriptions.each{|param, description| @@last_param_description[param.to_s] = description}
146
+ end
147
+
148
+ # Add a description for the next method served defined in at the class level
149
+ def self.desc(text = "")
150
+ @@last_description = text
151
+ end
152
+
153
+ # Add descriptions for the parameters of the next method served at the class
154
+ # level
155
+ def self.param_desc(param_descriptions = {})
156
+ @@last_param_description = {}
157
+ param_descriptions.each{|param, description| @@last_param_description[param.to_s] = description}
158
+ end
159
+
160
+ # This method tells the server to provide a method named by the +name+
161
+ # parameter, with arguments listed in the +args+ parameter. The
162
+ # +types+ parameter specifies the types of the arguments as will be
163
+ # described in the +WSDL+ file (see the TYPES2WSDL constant). The
164
+ # actual method called will be the one with the same name. Optionally
165
+ # a block can be provided, this block will be used to define a
166
+ # function named as in name.
167
+ #
168
+ # If the method returns something, then the type of the return value
169
+ # must be specified in the +types+ parameter as :return. If not value
170
+ # for :return parameter is specified in the +types+ parameter the
171
+ # method is taken to return no value. Other than that, if a parameter
172
+ # type is omitted it is taken to be :string.
173
+ def serve(name, args=[], types={}, &block)
174
+
175
+ @method_descriptions[name] = {:args => args, :types => types, :block => block,
176
+ :description => @@last_description, :param_descriptions => @@last_param_description}
177
+
178
+ @@last_description = nil
179
+ @@last_param_description = nil
180
+
181
+ @method_order << name
182
+ if block
183
+ inline_name = "_inline_" + name.to_s
184
+ add_to_ruby(inline_name, &block)
185
+ add_method_as(self,inline_name, name.to_s,*args)
186
+ else
187
+ add_method(self,name.to_s,*args)
188
+ end
189
+
190
+ add_to_wsdl(name, args, types)
191
+
192
+ nil
193
+ end
194
+
195
+ # Saves the method to be served by the instances. The initialization of an
196
+ # instance check if there where any methods declared to be served in the class
197
+ # and add them.
198
+ def self.serve(name, args=[], types={}, &block)
199
+ INHERITED_METHODS[name] = {:args => args, :types => types, :block => block,
200
+ :description => @@last_description, :param_descriptions => @@last_param_description}
201
+ @@last_description = nil
202
+ @@last_param_description = nil
203
+ end
204
+
205
+ # If +filename+ is specified it saves the +WSDL+ file in that file. If
206
+ # no +filename+ is specified it returns a string containing the
207
+ # +WSDL+. The no parameter version is served by default, so that
208
+ # clients can use this method to access the complete +WSDL+ file, as
209
+ # used in get_wsdl and get_driver.
210
+ def wsdl(filename = nil)
211
+ wsdl = WSDL_STUB.dup
212
+ wsdl.gsub!(/\$\{MESSAGES\}/m,@messages.join("\n"))
213
+ wsdl.gsub!(/\$\{OPERATIONS\}/m,@operations.join("\n"))
214
+ wsdl.gsub!(/\$\{BINDINGS\}/m,@bindings.join("\n"))
215
+ wsdl.gsub!(/\$\{NAME\}/,@name)
216
+ wsdl.gsub!(/\$\{DESCRIPTION\}/,@description)
217
+ wsdl.gsub!(/\$\{LOCATION\}/,"http://#{ @host }:#{ @port }")
218
+
219
+ if filename
220
+ directory = File.dirname(File.expand_path(filename))
221
+ FileUtils.mkdir_p directory unless File.exists? directory
222
+ File.open(filename,'w') {|f| f.write wsdl }
223
+ nil
224
+ else
225
+ wsdl
226
+ end
227
+ end
228
+
229
+ def documentation(filename = nil)
230
+ html_table = Builder::XmlMarkup.new(:indent => 2).table :class => "WS_documentation" do |xml|
231
+ xml.thead do
232
+ xml.tr do
233
+ xml.th "Operation", :class => "WS_operation"
234
+ xml.th "Parameters", :class => "WS_parameters"
235
+ xml.th "Documentation", :class => "WS_documentation"
236
+ end
237
+ end
238
+ xml.tbody do
239
+ @method_order.each do |method|
240
+ desc = @method_descriptions[method][:description] || ""
241
+ case
242
+ when method.to_s == 'wsdl' || method.to_s == 'documentation'
243
+ type = 'WS_documentation'
244
+ when desc =~ /^Job management:/
245
+ type = 'WS_job_management'
246
+ when @method_descriptions[method][:args].include?( :sugested_name )
247
+ type = 'WS_task'
248
+ else
249
+ type = 'WS_normal'
250
+ end
251
+ xml.tr :class => type, :id => "WS_method_#{method}" do
252
+ xml.td method.to_s, :class => "WS_operation"
253
+
254
+ xml.td :class => "WS_parameters" do
255
+ description = @method_descriptions[method]
256
+ method_parameters = description[:args]
257
+ method_parameters += ['return'] unless description[:types][:return] == false || description[:types]['return'] == false
258
+ if method_parameters.any?
259
+ xml.dl :class => "WS_parameter_documentation" do
260
+ method_parameters.each do |param|
261
+ if param.to_s == 'return'
262
+ xml.dt param, :class => 'WS_return'
263
+ else
264
+ xml.dt param
265
+ end
266
+
267
+ if description[:param_descriptions]
268
+ xml.dd description[:param_descriptions][param.to_s] || ""
269
+ else
270
+ xml.dd
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ xml.td desc, :class => "WS_documentation"
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ if filename
283
+ directory = File.dirname(File.expand_path(filename))
284
+ FileUtils.mkdir_p directory unless File.exists? directory
285
+ File.open(filename,'w') {|f| f.write html_table }
286
+ nil
287
+ else
288
+ html_table
289
+ end
290
+ end
291
+
292
+ private
293
+
294
+ def add_to_ruby(name, &block)
295
+ self.class.send(:define_method, name, block)
296
+ end
297
+
298
+ def add_to_wsdl(name, args, types)
299
+ description = @method_descriptions[name]
300
+ message = Builder::XmlMarkup.new(:indent => 2).message :name => "#{ name }Request" do |xml|
301
+
302
+ args.each do |param|
303
+ type = types[param.to_s] || types[param.to_sym] || :string
304
+ type = type.to_sym
305
+
306
+ xml.part :name => param, :type => TYPES2WSDL[type] do
307
+ param_descriptions = description[:param_descriptions]
308
+
309
+ if param_descriptions && param_descriptions[param.to_s]
310
+ xml.documentation param_descriptions[param.to_s]
311
+ end
312
+ end
313
+
314
+ end
315
+ end
316
+ @messages << message
317
+
318
+ message = Builder::XmlMarkup.new(:indent => 2).message :name => "#{ name }Response" do |xml|
319
+ type = [types[:return], types["return"]].compact.first
320
+ type = :string if type.nil?
321
+ if type
322
+ type = type.to_sym
323
+ xml.part :name => 'result', :type => TYPES2WSDL[type] do
324
+ param_descriptions = description[:param_descriptions]
325
+
326
+ if param_descriptions && param_descriptions['return']
327
+ xml.documentation param_descriptions['return']
328
+ end
329
+ end
330
+ end
331
+ end
332
+ @messages << message
333
+
334
+ operation = Builder::XmlMarkup.new(:indent => 2).operation :name => "#{ name }" do |xml|
335
+ xml.documentation description[:description] if description[:description]
336
+ xml.input :message => "tns:#{ name }Request"
337
+ xml.output :message => "tns:#{ name }Response"
338
+ end
339
+
340
+ @operations << operation
341
+
342
+ binding = Builder::XmlMarkup.new(:indent => 2).operation :name => "#{ name }" do |xml|
343
+ xml.tag! 'soap:operation'.to_sym, :soapAction => "urn:${NAME}##{name}", :style => 'rpc'
344
+ xml.input do |xml|
345
+ xml.tag! 'soap:body'.to_sym, :namespace => "urn:${NAME}", :encodingStyle => "http://schemas.xmlsoap.org/soap/encoding/", :use => "encoded"
346
+ end
347
+ xml.output do |xml|
348
+ xml.tag! 'soap:body'.to_sym, :namespace => "urn:${NAME}", :encodingStyle => "http://schemas.xmlsoap.org/soap/encoding/", :use => "encoded"
349
+ end
350
+ end
351
+
352
+ @bindings << binding
353
+ end
354
+
355
+
356
+ if ! defined? WSDL_STUB
357
+ WSDL_STUB =<<EOT
358
+ <?xml version="1.0"?>
359
+ <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
360
+ xmlns:tns="${NAME}-NS"
361
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
362
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
363
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
364
+ xmlns:si="http://soapinterop.org/xsd"
365
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
366
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
367
+ xmlns="http://schemas.xmlsoap.org/wsdl/"
368
+ targetNamespace="${NAME}-NS">
369
+
370
+
371
+ <types>
372
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
373
+ targetNamespace="${NAME}-NS"
374
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
375
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
376
+ <complexType name="ArrayOfString">
377
+ <complexContent>
378
+ <restriction base="soapenc:Array">
379
+ <attribute ref="soapenc:arrayType"
380
+ wsdl:arrayType="string[]"/>
381
+ </restriction>
382
+ </complexContent>
383
+ </complexType>
384
+ </schema>
385
+ </types>
386
+
387
+ ${MESSAGES}
388
+ <portType name="${NAME}">
389
+ ${OPERATIONS}
390
+ </portType>
391
+
392
+ <binding name="${NAME}Binding" type="tns:${NAME}">
393
+ <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
394
+ ${BINDINGS}
395
+ </binding>
396
+ <service name="${NAME}">
397
+ <documentation>${DESCRIPTION}</documentation>
398
+
399
+ <port name="${NAME}" binding="tns:${NAME}Binding">
400
+ <soap:address location="${LOCATION}"/>
401
+ </port>
402
+ </service>
403
+
404
+ </definitions>
405
+ EOT
406
+ end
407
+
408
+ if ! defined? TYPES2WSDL
409
+ TYPES2WSDL = {
410
+ :boolean => 'xsd:boolean',
411
+ :string => 'xsd:string',
412
+ :text => 'xsd:string',
413
+ :tsv => 'xsd:string',
414
+ :yaml => 'xsd:string',
415
+ :integer => 'xsd:integer',
416
+ :float => 'xsd:float',
417
+ :array => 'tns:ArrayOfString',
418
+ :binary => 'xsd:base64Binary',
419
+ }
420
+ end
421
+
422
+
423
+ end
424
+
425
+