jcarley-simplews 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
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
+