safrano 0.5.5 → 0.6.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_ext/Date/format.rb +47 -0
  3. data/lib/core_ext/DateTime/format.rb +54 -0
  4. data/lib/core_ext/Dir/iter.rb +7 -5
  5. data/lib/core_ext/Hash/transform.rb +14 -6
  6. data/lib/core_ext/Numeric/convert.rb +1 -1
  7. data/lib/core_ext/Time/format.rb +71 -0
  8. data/lib/core_ext/date.rb +5 -0
  9. data/lib/core_ext/datetime.rb +5 -0
  10. data/lib/core_ext/time.rb +5 -0
  11. data/lib/odata/attribute.rb +8 -6
  12. data/lib/odata/batch.rb +3 -3
  13. data/lib/odata/collection.rb +5 -5
  14. data/lib/odata/collection_filter.rb +3 -1
  15. data/lib/odata/collection_media.rb +4 -27
  16. data/lib/odata/collection_order.rb +1 -1
  17. data/lib/odata/common_logger.rb +5 -27
  18. data/lib/odata/complex_type.rb +19 -21
  19. data/lib/odata/edm/primitive_types.rb +14 -19
  20. data/lib/odata/entity.rb +12 -12
  21. data/lib/odata/error.rb +7 -7
  22. data/lib/odata/expand.rb +2 -2
  23. data/lib/odata/filter/base.rb +10 -1
  24. data/lib/odata/filter/error.rb +2 -2
  25. data/lib/odata/filter/parse.rb +16 -2
  26. data/lib/odata/filter/sequel.rb +31 -4
  27. data/lib/odata/filter/sequel_datetime_adapter.rb +21 -0
  28. data/lib/odata/filter/token.rb +18 -5
  29. data/lib/odata/filter/tree.rb +83 -9
  30. data/lib/odata/function_import.rb +11 -9
  31. data/lib/odata/model_ext.rb +26 -29
  32. data/lib/odata/request/json.rb +171 -0
  33. data/lib/odata/transition.rb +2 -2
  34. data/lib/odata/url_parameters.rb +3 -3
  35. data/lib/odata/walker.rb +1 -1
  36. data/lib/safrano/multipart.rb +1 -3
  37. data/lib/safrano/rack_app.rb +2 -14
  38. data/lib/safrano/rack_builder.rb +0 -15
  39. data/lib/safrano/request.rb +3 -3
  40. data/lib/safrano/response.rb +3 -3
  41. data/lib/safrano/service.rb +13 -5
  42. data/lib/safrano/type_mapping.rb +4 -4
  43. data/lib/safrano/version.rb +1 -2
  44. data/lib/safrano.rb +3 -0
  45. metadata +51 -15
data/lib/odata/entity.rb CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'json'
4
4
  require 'rexml/document'
5
- require 'safrano.rb'
6
- require 'odata/model_ext.rb' # required for self.class.entity_type_name ??
5
+ require 'safrano'
6
+ require 'odata/model_ext' # required for self.class.entity_type_name ??
7
7
  require_relative 'navigation_attribute'
8
8
 
9
9
  module Safrano
@@ -86,9 +86,9 @@ module Safrano
86
86
  "#{self.class.uri}#{@odata_pk}"
87
87
  end
88
88
 
89
- D = 'd'.freeze
90
- DJ_OPEN = '{"d":'.freeze
91
- DJ_CLOSE = '}'.freeze
89
+ D = 'd'
90
+ DJ_OPEN = '{"d":'
91
+ DJ_CLOSE = '}'
92
92
 
93
93
  # Json formatter for a single entity (probably OData V1/V2 like)
94
94
  def to_odata_json(request:)
@@ -176,7 +176,7 @@ module Safrano
176
176
  destroy(transaction: false)
177
177
  end
178
178
  rescue StandardError => e
179
- raise SequelAdapterError.new(e)
179
+ raise SequelAdapterError, e
180
180
  end
181
181
 
182
182
  # TODO: differentiate between POST/PUT/PATCH/MERGE
@@ -203,7 +203,7 @@ module Safrano
203
203
  if req.walker.media_value
204
204
  odata_media_value_put(req)
205
205
  elsif req.accept?(APPJSON)
206
- data = JSON.parse(req.body.read)
206
+ data = Safrano::OData::JSON.parse_one(req.body.read, self.class)
207
207
  data.delete('__metadata')
208
208
 
209
209
  if req.in_changeset
@@ -220,7 +220,7 @@ module Safrano
220
220
  end
221
221
 
222
222
  def odata_patch(req)
223
- req.with_parsed_data do |data|
223
+ req.with_parsed_data(self.class) do |data|
224
224
  data.delete('__metadata')
225
225
 
226
226
  # validate payload column names
@@ -401,9 +401,9 @@ module Safrano
401
401
  include Entity
402
402
  def pk_uri
403
403
  pku = +''
404
- self.class.odata_upk_parts.each_with_index { |upart, i|
404
+ self.class.odata_upk_parts.each_with_index do |upart, i|
405
405
  pku = "#{pku}#{upart}#{pk[i]}"
406
- }
406
+ end
407
407
  pku
408
408
  end
409
409
 
@@ -419,7 +419,7 @@ module Safrano
419
419
  module EntityCreateStandardOutput
420
420
  # Json formatter for a create entity POST call / Standard version; return as json object
421
421
  def to_odata_create_json(request:)
422
- # TODO Perf: reduce method call overhead
422
+ # TODO: Perf: reduce method call overhead
423
423
  # we added this redirection for readability and flexibility
424
424
  to_odata_json(request: request)
425
425
  end
@@ -428,7 +428,7 @@ module Safrano
428
428
  module EntityCreateArrayOutput
429
429
  # Json formatter for a create entity POST call Array version
430
430
  def to_odata_create_json(request:)
431
- # TODO Perf: reduce method call overhead
431
+ # TODO: Perf: reduce method call overhead
432
432
  # we added this redirection for readability and flexibility
433
433
  to_odata_array_json(request: request)
434
434
  end
data/lib/odata/error.rb CHANGED
@@ -59,12 +59,12 @@ module Safrano
59
59
  message = (m = @msg.to_s).empty? ? to_s : m
60
60
  if req.accept?(APPJSON)
61
61
  # json is default content type so we dont need to specify it here again
62
- [self.http_code, EMPTY_HASH,
63
- { 'odata.error' => { 'code' => "#{http_code}",
62
+ [http_code, EMPTY_HASH,
63
+ { 'odata.error' => { 'code' => http_code.to_s,
64
64
  'type' => to_s,
65
65
  'message' => message } }.to_json]
66
66
  else
67
- [self.http_code, CT_TEXT, message]
67
+ [http_code, CT_TEXT, message]
68
68
  end
69
69
  end
70
70
  end
@@ -82,8 +82,8 @@ module Safrano
82
82
  if req.accept?(APPJSON)
83
83
  # json is default content type so we dont need to specify it here again
84
84
  [self.class.http_code, EMPTY_HASH,
85
- { 'odata.error' => { 'code' => "#{self.class.http_code}",
86
- 'type' => "#{self.class}",
85
+ { 'odata.error' => { 'code' => self.class.http_code.to_s,
86
+ 'type' => self.class.to_s,
87
87
  'message' => message } }.to_json]
88
88
  else
89
89
  [self.class.http_code, CT_TEXT, message]
@@ -184,7 +184,7 @@ module Safrano
184
184
  class BadRequestSelectInvalidProps < BadRequestError
185
185
  include ErrorInstance
186
186
  def initialize(model, iprops)
187
- @msg = ((iprops.size > 1) ? "Bad Request: the $select properties #{iprops.to_a.join(', ')} are invalid for entityset #{model.entity_set_name}" : "Bad Request: the $select property #{iprops.first} is invalid for entityset #{model.entity_set_name}")
187
+ @msg = (iprops.size > 1 ? "Bad Request: the $select properties #{iprops.to_a.join(', ')} are invalid for entityset #{model.entity_set_name}" : "Bad Request: the $select property #{iprops.first} is invalid for entityset #{model.entity_set_name}")
188
188
  end
189
189
  end
190
190
 
@@ -232,7 +232,7 @@ module Safrano
232
232
  @msg = 'The requested OData version is not yet supported'
233
233
  end
234
234
  # batch not implemented (Safrano specific)
235
- class BatchNotImplementedError < NotImplementedError
235
+ class BatchNotImplementedError < RuntimeError
236
236
  @msg = 'Not implemented: OData batch'
237
237
  end
238
238
 
data/lib/odata/expand.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'odata/error.rb'
3
+ require 'odata/error'
4
4
 
5
5
  # all dataset expanding related classes in our OData module
6
6
  # ie do eager loading
@@ -52,7 +52,7 @@ module Safrano
52
52
  # [1] --> { 1 => {} }
53
53
  DEEPH_1 = ->(inp) { inp.size > 1 ? { inp[0] => DEEPH_1.call(inp[1..-1]) } : { inp[0] => {} } }
54
54
 
55
- NODESEP = '/'.freeze
55
+ NODESEP = '/'
56
56
 
57
57
  def initialize(exstr)
58
58
  exstr.strip!
@@ -49,10 +49,13 @@ module Safrano
49
49
  class ArgTree < Tree
50
50
  end
51
51
 
52
- # Numbers (floating point, ints, dec)
52
+ # Numbers (floating point, ints)
53
53
  class FPNumber < Leave
54
54
  end
55
55
 
56
+ class DecimalLit < Leave
57
+ end
58
+
56
59
  # Literals are unquoted words without /
57
60
  class Literal < Leave
58
61
  end
@@ -70,5 +73,11 @@ module Safrano
70
73
  # Quoted Strings
71
74
  class QString < Leave
72
75
  end
76
+
77
+ # DateTime Literals
78
+ class DateTimeLit < Leave
79
+ end
80
+ class DateTimeOffsetLit < Leave
81
+ end
73
82
  end
74
83
  end
@@ -16,7 +16,7 @@ module Safrano
16
16
  # Parser errors
17
17
 
18
18
  class Error
19
- def Error.http_code
19
+ def self.http_code
20
20
  const_get(:HTTP_CODE)
21
21
  end
22
22
  HTTP_CODE = 400
@@ -70,7 +70,7 @@ module Safrano
70
70
  include ::Safrano::ErrorInstance
71
71
  def initialize(tok, typ, cur)
72
72
  super
73
- @msg = "Bad Request: wrong number of parameters for function #{cur.parent.value.to_s} in $filter"
73
+ @msg = "Bad Request: wrong number of parameters for function #{cur.parent.value} in $filter"
74
74
  end
75
75
  end
76
76
  # Invalid separator in this context (missing parenthesis?)
@@ -174,7 +174,21 @@ module Safrano
174
174
  @cursor.update_state(tok, typ)
175
175
  grow_at_cursor(FPNumber.new(tok))
176
176
  end
177
-
177
+ when :DecimalLit
178
+ with_accepted(tok, typ) do
179
+ @cursor.update_state(tok, typ)
180
+ grow_at_cursor(DecimalLit.new(tok))
181
+ end
182
+ when :DateTimeLit
183
+ with_accepted(tok, typ) do
184
+ @cursor.update_state(tok, typ)
185
+ grow_at_cursor(DateTimeLit.new(tok))
186
+ end
187
+ when :DateTimeOffsetLit
188
+ with_accepted(tok, typ) do
189
+ @cursor.update_state(tok, typ)
190
+ grow_at_cursor(DateTimeOffsetLit.new(tok))
191
+ end
178
192
  when :unmatchedQuote
179
193
  break unmatched_quote_error(tok, typ)
180
194
 
@@ -188,7 +202,7 @@ module Safrano
188
202
  break(@error) if @error
189
203
  end
190
204
  (@error = @tree.check_types) unless @error
191
- @error ? @error : Contract.valid(@tree)
205
+ @error || Contract.valid(@tree)
192
206
  end
193
207
  end
194
208
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative './base'
4
4
  require_relative './sequel_function_adapter'
5
-
5
+ require_relative './sequel_datetime_adapter'
6
6
  module Safrano
7
7
  module Filter
8
8
  # Base class for Leaves, Trees, RootTrees etc
@@ -184,9 +184,10 @@ module Safrano
184
184
  Contract.collect_result!(@children[0].leuqes(jh),
185
185
  @children[1].leuqes(jh)) do |c0, c1|
186
186
  if c1 == NullLiteral::LEUQES
187
- if @value == :eq
187
+ case @value
188
+ when :eq
188
189
  leuqes_op = :IS
189
- elsif @value == :ne
190
+ when :ne
190
191
  leuqes_op = :'IS NOT'
191
192
  end
192
193
  end
@@ -242,6 +243,12 @@ module Safrano
242
243
  end
243
244
  end
244
245
 
246
+ class DecimalLit
247
+ def leuqes(_jh)
248
+ success Sequel.lit(@value)
249
+ end
250
+ end
251
+
245
252
  # Literals are unquoted words
246
253
  class Literal
247
254
  def leuqes(jh)
@@ -271,7 +278,7 @@ module Safrano
271
278
 
272
279
  # Null
273
280
  class NullLiteral
274
- def leuqes(jh)
281
+ def leuqes(_jh)
275
282
  # Sequel's representation of NULL
276
283
  success LEUQES
277
284
  end
@@ -304,5 +311,25 @@ module Safrano
304
311
  success "%#{@value}%"
305
312
  end
306
313
  end
314
+
315
+ # DateTime literals datetime'2017-04-15T00:00:00'
316
+ class DateTimeLit
317
+ # datetime method is defined dynamically by adapter-specific include on startup
318
+ # --> sequel_datetime_adapter.rb
319
+ def leuqes(_jh)
320
+ # success Sequel.function(:datetime, @value)
321
+ success datetime(@value)
322
+ end
323
+ end
324
+
325
+ # DateTimeOffset literals datetimeoffset'2017-04-15T00:00:00+02:00'
326
+ class DateTimeOffsetLit
327
+ # datetime method is defined dynamically by adapter-specific include on startup
328
+ # --> sequel_datetime_adapter.rb
329
+ def leuqes(_jh)
330
+ # success Sequel.function(:datetime, @value)
331
+ success datetime(@value)
332
+ end
333
+ end
307
334
  end
308
335
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './tree'
4
+ require_relative './sequel'
5
+
6
+ module Safrano
7
+ module Filter
8
+ # sqlite adapter specific DateTime handler
9
+ module DateTimeSqlite
10
+ def datetime(_val)
11
+ Sequel.function(:datetime, @value)
12
+ end
13
+ end
14
+ # non-sqlite adapter specific DateTime handler
15
+ module DateTimeDefault
16
+ def datetime(_val)
17
+ Sequel.lit("'#{@value}'")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -15,9 +15,16 @@ module Safrano
15
15
  BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'
16
16
  BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'
17
17
  NOTRGX = 'not|NOT|Not'
18
- FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'
18
+ FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?[df]?'
19
+ DECIMALRGX = '\d+(?:\.\d+)[mM]'
19
20
  QUALITRGX = '\w+(?:\/\w+)+'
20
- RGX = /(#{FUNCRGX})|(#{NULLRGX})|([\(\),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
21
+ # datetime'yyyy-mm-ddThh:mm[:ss[.fffffff]]' NOTE: Spaces are not allowed between datetime and quoted portion.
22
+ # datetime is case-insensitive
23
+ DATIRGX = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?:\:\d{2})?(?:\.\d{1,7})?'
24
+ DATETIMERGX = /datetime'#{DATIRGX}[zZ]?'/i.freeze
25
+ DATIOFFRGX = /datetimeoffset'#{DATIRGX}(?:[zZ]|[+-]\d{2}:\d{2})'/.freeze
26
+
27
+ RGX = /(#{FUNCRGX})|(#{NULLRGX})|([(),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{DECIMALRGX})|(#{FPRGX})|(#{QUALITRGX})|(#{DATETIMERGX})|(#{DATIOFFRGX})|(\w+)|(')/.freeze
21
28
 
22
29
  def each_typed_token(inp)
23
30
  typ = nil
@@ -52,12 +59,18 @@ module Safrano
52
59
  when 6
53
60
  :QString
54
61
  when 7
55
- :FPNumber
62
+ :DecimalLit
56
63
  when 8
57
- :Qualit
64
+ :FPNumber
58
65
  when 9
59
- :Literal
66
+ :Qualit
60
67
  when 10
68
+ :DateTimeLit
69
+ when 11
70
+ :DateTimeOffsetLit
71
+ when 12
72
+ :Literal
73
+ when 13
61
74
  :unmatchedQuote
62
75
  end
63
76
  yield found, typ
@@ -45,6 +45,11 @@ module Safrano
45
45
  super(val, &block)
46
46
  end
47
47
 
48
+ # shortcut used for testing
49
+ def first_child_value
50
+ @children.first.value
51
+ end
52
+
48
53
  def attach(child)
49
54
  child.parent = self
50
55
  @children << child
@@ -66,7 +71,7 @@ module Safrano
66
71
  def accept?(tok, typ)
67
72
  case typ
68
73
  when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
69
- :UnopTree, :FPNumber
74
+ :UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
70
75
  nil
71
76
  when :Delimiter
72
77
  if tok == '('
@@ -232,7 +237,8 @@ module Safrano
232
237
 
233
238
  def update_state(_tok, typ)
234
239
  case typ
235
- when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
240
+ when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm,
241
+ :UnopTree, :FPNumber, :DecimalLit, :DateTimeLit, :DateTimeOffsetLit
236
242
  @state = :closed
237
243
  end
238
244
  end
@@ -297,7 +303,8 @@ module Safrano
297
303
  @state = :closed
298
304
  when :Separator
299
305
  @state = :sep
300
- when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
306
+ when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
307
+ :DateTimeLit, :DateTimeOffsetLit
301
308
  @state = :val
302
309
  end
303
310
  end
@@ -307,8 +314,8 @@ module Safrano
307
314
  case typ
308
315
  when :Delimiter
309
316
  if @value == '(' && tok == ')' && @state != :closed
310
- if (@parent.class == IdentityFuncTree) or
311
- (@parent.arity_full?(@children.size))
317
+ if (@parent.class == IdentityFuncTree) ||
318
+ @parent.arity_full?(@children.size)
312
319
 
313
320
  nil
314
321
  else
@@ -327,11 +334,10 @@ module Safrano
327
334
  elsif @state == :sep
328
335
  Parser::ErrorInvalidToken.new(tok, typ, self)
329
336
  end
330
- when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
337
+ when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber, :DecimalLit,
338
+ :DateTimeLit, :DateTimeOffsetLit
331
339
  if (@state == :open) || (@state == :sep)
332
- if @parent.arity_full?(@children.size)
333
- Parser::ErrorInvalidArity.new(tok, typ, self)
334
- end
340
+ Parser::ErrorInvalidArity.new(tok, typ, self) if @parent.arity_full?(@children.size)
335
341
  else
336
342
  Parser::ErrorInvalidToken.new(tok, typ, self)
337
343
  end
@@ -349,6 +355,13 @@ module Safrano
349
355
 
350
356
  # Numbers (floating point, ints, dec)
351
357
  class FPNumber
358
+ def initialize(val)
359
+ # 1.53f --> value 1.53
360
+ # 1.53d --> value 1.53
361
+ # 1.53 --> value 1.53
362
+ val[-1] =~ /[fd]/i ? super(val[0..-2]) : super(val)
363
+ end
364
+
352
365
  def accept?(tok, typ)
353
366
  case typ
354
367
  when :Delimiter, :Separator, :BinopBool, :BinopArithm
@@ -363,7 +376,28 @@ module Safrano
363
376
  :number
364
377
  end
365
378
  end
379
+ class DecimalLit
380
+ def initialize(val)
381
+ # 1.53m --> value 1.53
382
+ # Warning, this assumes that the m|M part in the input is really not optional
383
+ # cf. DECIMALRGX in token.rb
384
+
385
+ super(val[0..-2])
386
+ end
387
+
388
+ def accept?(tok, typ)
389
+ case typ
390
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
391
+ nil
392
+ else
393
+ Parser::ErrorInvalidToken.new(tok, typ, self)
394
+ end
395
+ end
366
396
 
397
+ def edm_type
398
+ :decimal
399
+ end
400
+ end
367
401
  # Literals are unquoted words without /
368
402
  class Literal
369
403
  def accept?(tok, typ)
@@ -409,6 +443,46 @@ module Safrano
409
443
  end
410
444
  end
411
445
 
446
+ # DateTimeLit
447
+ class DateTimeLit
448
+ def initialize(val)
449
+ # datetime'2000-12-12T12:00:53' --> value 2000-12-12T12:00:53
450
+ super(val[9..-2])
451
+ end
452
+
453
+ def accept?(tok, typ)
454
+ case typ
455
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
456
+ nil
457
+ else
458
+ Parser::ErrorInvalidToken.new(tok, typ, self)
459
+ end
460
+ end
461
+
462
+ def edm_type
463
+ :datetime
464
+ end
465
+ end
466
+ # DateTimeOffsetLit
467
+ class DateTimeOffsetLit
468
+ def initialize(val)
469
+ # datetimeoffset'2000-12-12T12:00:53+02:00' --> value 2000-12-12T12:00:53+02:00
470
+ super(val[15..-2])
471
+ end
472
+
473
+ def accept?(tok, typ)
474
+ case typ
475
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
476
+ nil
477
+ else
478
+ Parser::ErrorInvalidToken.new(tok, typ, self)
479
+ end
480
+ end
481
+
482
+ def edm_type
483
+ :datetimeoffset
484
+ end
485
+ end
412
486
  # Quoted Strings
413
487
  class QString
414
488
  DBL_QO = "''"
@@ -84,14 +84,14 @@ module Safrano
84
84
  def check_missing_params
85
85
  # do we have all parameters provided ? use Set difference to check
86
86
  pkeys = @params.keys.map(&:to_sym).to_set
87
- unless (idiff = @input.keys.to_set - pkeys).empty?
87
+ if (idiff = @input.keys.to_set - pkeys).empty?
88
+ Contract::OK
89
+ else
88
90
 
89
91
  Safrano::ServiceOperationParameterMissing.new(
90
92
  missing: idiff.to_a,
91
93
  sopname: @name
92
94
  )
93
- else
94
- Contract::OK
95
95
  end
96
96
  end
97
97
 
@@ -134,12 +134,14 @@ module Safrano
134
134
  # EntitySet= @entity_set ,
135
135
  'ReturnType' => @returning.type_metadata,
136
136
  'm:HttpMethod' => @http_method)
137
- @input.each do |iname, type|
138
- funky.add_element('Parameter',
139
- 'Name' => iname.to_s,
140
- 'Type' => type.type_name,
141
- 'Mode' => 'In')
142
- end if @input
137
+ if @input
138
+ @input.each do |iname, type|
139
+ funky.add_element('Parameter',
140
+ 'Name' => iname.to_s,
141
+ 'Type' => type.type_name,
142
+ 'Mode' => 'In')
143
+ end
144
+ end
143
145
  funky
144
146
  end
145
147