taverna-player 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,15 @@
1
1
  = Changes log for Taverna Player
2
2
 
3
+ == Version 0.3.0
4
+
5
+ * Refactor callback code out into its own module.
6
+ * Fix classname typo in port model templates.
7
+ * Enable getting deep list outputs from the port models.
8
+ * Add a method to get a route (path) to a port.
9
+ * [TAV-465] Refactor renderers and add list rendering.
10
+ * Factor out common list handling code for sharing.
11
+ * Rename and re-document renderers.
12
+
3
13
  == Version 0.2.1
4
14
 
5
15
  * Fix classname typo in port model templates.
@@ -172,9 +172,9 @@ They will be saved to <tt>lib/taverna_player_callbacks.rb</tt>. Don't forget to
172
172
  then require and register them in the Taverna Player initializer. There is more
173
173
  information on callbacks below.
174
174
 
175
- You can add to, or change, the workflow run outputs render methods to better
176
- suit your particular application. To copy the defaults that Taverna Player
177
- ships with into your application for customization run:
175
+ You can add to, or change, the workflow port render methods to better suit
176
+ your particular application. To copy the defaults that Taverna Player ships
177
+ with into your application for customization run:
178
178
 
179
179
  rails generate taverna_player:renderers
180
180
 
@@ -182,12 +182,19 @@ They will be saved to <tt>lib/taverna_player_renderers.rb</tt>. Don't forget to
182
182
  then require and register them in the Taverna Player initializer. There is more
183
183
  information on renderers below.
184
184
 
185
- == Taverna Player initializer
185
+ == Taverna Player initializers
186
186
 
187
- The {default initializer}[https://github.com/myGrid/taverna-player/blob/master/lib/generators/templates/initializer.rb]
188
- that is installed by the install generator requires minimal configuration for
189
- simple set ups. It is fully commented with everything that needs to be set but
190
- further explanations are given below.
187
+ Two initializers are installed by the install generator:
188
+ * {taverna_player.rb}[https://github.com/myGrid/taverna-player/blob/master/lib/generators/templates/player_initializer.rb]:
189
+ This contains configuration of Taverna Player.
190
+ * {taverna_server.rb.example}[https://github.com/myGrid/taverna-player/blob/master/lib/generators/templates/server_initializer.rb]:
191
+ This is used to configure Taverna Player's connection to a Taverna Server.
192
+ It is initially set up as an example file as you will need to distribute
193
+ this with your application but you must not check in the contents of the
194
+ configured version to your repository.
195
+
196
+ Both of these files require minimal configuration for simple set ups and are
197
+ fully commented with everything that needs to be set - more details below.
191
198
 
192
199
  === Essential (required) configuration
193
200
 
@@ -332,6 +339,9 @@ your own needs.
332
339
 
333
340
  You can override the following core components:
334
341
  * Run (model)
342
+ * RunPort (model)
343
+ * RunPort::Input (model)
344
+ * RunPort::Output (model)
335
345
  * RunsController
336
346
  * ServiceCredentialsController
337
347
 
@@ -405,28 +415,24 @@ A set of example callbacks can be installed with the generator detailed above.
405
415
  Don't forget to make sure any callback code is <tt>require</tt>d somewhere and
406
416
  the callbacks themselves registered in the initializer.
407
417
 
408
- == Rendering outputs
409
-
410
- Workflows can produce results in many different formats and Taverna Player
411
- tries to accomodate this as best it can. It provides basic facilities for
412
- rendering as many result types as it can and these are extendible wherever
413
- possible.
418
+ == Rendering workflow ports
414
419
 
415
- === Output helper
420
+ Workflows can accept inputs and produce results in many different formats and
421
+ Taverna Player tries to accomodate this as best it can. It provides basic
422
+ facilities for rendering as many types as it can and these are extensible
423
+ wherever possible.
416
424
 
417
- A very basic output helper is provided, <tt>show_output</tt>, which can be
418
- used in your views. This will format the output of each workflow output port in
419
- a generic way. If it is a singleton output then it is displayed normally. If it
420
- is a list output then the list is rendered as a HTML ordered list
421
- (<tt><ol></tt>).
425
+ Calling the port renderer is as simple as just passing it the port to be
426
+ rendered in your view.
422
427
 
423
- If you require anything more complex then you will have to override this helper
424
- or provide your own and call it from your views.
428
+ <% run.outputs.each do |output| %>
429
+ <%= TavernaPlayer.port_renderer.render(output) %>
430
+ <% end %>
425
431
 
426
432
  === Type renderers
427
433
 
428
434
  Taverna Player has a system of specific type renderers to handle different
429
- types of output. A number of defaults are supplied but these can be replaced
435
+ types of value. A number of defaults are supplied but these can be replaced
430
436
  and added to if required.
431
437
 
432
438
  To install a set of example renderers you can use the generator detailed above.
@@ -434,22 +440,22 @@ To install a set of example renderers you can use the generator detailed above.
434
440
  To register a renderer for use add it into the renderers block in the
435
441
  initializer:
436
442
 
437
- config.output_renderers do |renderers|
443
+ config.port_renderers do |renderers|
438
444
  ...
439
445
  end
440
446
 
441
447
  So to just register a single default renderer method (called
442
448
  "my_default_renderer") you would do this:
443
449
 
444
- config.output_renderers do |renderers|
450
+ config.port_renderers do |renderers|
445
451
  renderers.default(:my_default_renderer)
446
452
  end
447
453
 
448
- And it would be used to render every type of output. A more sensible example
454
+ And it would be used to render every type of value. A more sensible example
449
455
  would be to have a renderer for PNG-type images and a renderer for text
450
- outputs as well:
456
+ values as well:
451
457
 
452
- config.output_renderers do |renderers|
458
+ config.port_renderers do |renderers|
453
459
  renderers.default(:my_default_renderer)
454
460
  renderers.add("text/plain", :text_renderer, true)
455
461
  renderers.add("image/png", :show_image)
@@ -458,16 +464,16 @@ outputs as well:
458
464
  This does three things:
459
465
  * Registers a renderer for PNG images. This could be as simple as wrapping it
460
466
  in an <tt><img ../></tt> tag.
461
- * Registers a renderer for outputs of type "text/plain" and <em>sets this as
467
+ * Registers a renderer for values of type "text/plain" and <em>sets this as
462
468
  the default renderer for all other types beginning with "text"</em>. That is
463
469
  what the final parameter set to +true+ does.
464
470
  * Registers a default renderer for all other types. This should probably give
465
- an explanation as to why the output cannot be shown in the browser with a
466
- link to download the output to the user's computer.
471
+ an explanation as to why the value cannot be shown in the browser with a
472
+ link to download the value to the user's computer.
467
473
 
468
474
  Note the use of MIME types for specifying all types.
469
475
 
470
- Obviously outputs such as images and text are so common that Taverna Player
476
+ Obviously values such as images and text are so common that Taverna Player
471
477
  provides these renderers for you and has them set up and registered by default.
472
478
  You would only need to override them if you wanted extra information to be
473
479
  displayed as well, such as sizes next to images, etc.
@@ -485,22 +491,20 @@ show:
485
491
 
486
492
  Note that the same renderer callback is used for each one.
487
493
 
488
- === Writing your own renderers
494
+ === Rendering lists
489
495
 
490
- To be a renderer callback a method must accept two parameters (in this order):
491
- 1. The content to be rendered.
492
- 1. The MIME type of the content.
496
+ Taverna workflow inputs and output can be lists and rendering them requires a
493
497
 
494
- In the case of text outputs the content will likely (depending on the output
495
- helper you use, described above) be the entire text content of the output. In
496
- most other cases it will probably be a URI or path to the actual content for
497
- use in, for example, an <tt><img .../></tt> tag.
498
+ === Writing your own renderers
498
499
 
499
- The type is provided in case a renderer wishes to be general and differentiate
500
- between different subtypes.
500
+ To be a renderer callback a method must accept two parameters (in this order):
501
+ 1. The port to be rendered.
502
+ 1. A list of indices into the port. For a singleton port this will be an empty
503
+ list. For a port of depth 2 this would be a list with two items, e.g.
504
+ <tt>[0, 0]</tt>
501
505
 
502
- Note that all renderer callbacks are called by Taverna Player in a context that
503
- includes the {ActionView::Helpers}[http://api.rubyonrails.org/classes/ActionView/Helpers.html]
506
+ All renderer callbacks are called by Taverna Player in a context that includes
507
+ the {ActionView::Helpers}[http://api.rubyonrails.org/classes/ActionView/Helpers.html]
504
508
  so your callbacks have access to them too, including helpers from third-party
505
509
  gems that register their helpers correctly.
506
510
 
@@ -515,9 +519,9 @@ Taverna Player provides a plain text renderer that formats text with a
515
519
  monospaced font, converts URI-like things to clickable links and respects
516
520
  carriage returns and newlines. It looks something like this:
517
521
 
518
- def format_text(content, type)
522
+ def format_text(port, index = [])
519
523
  # Use CodeRay to format text so that newlines are respected.
520
- content = CodeRay.scan(content, :text).div(:css => :class)
524
+ content = CodeRay.scan(port.value(index), :text).div(:css => :class)
521
525
 
522
526
  # Use auto_link to turn URI-like text into links.
523
527
  auto_link(content, :html => { :target => '_blank' }, :sanitize => false)
@@ -534,10 +538,10 @@ This renderer is registered as the default for all "text" media types.
534
538
 
535
539
  This renderer catches "text/XML" outputs:
536
540
 
537
- def format_xml(content, type)
541
+ def format_xml(port, index = [])
538
542
  # Make sure XML is indented consistently.
539
543
  out = String.new
540
- REXML::Document.new(content).write(out, 1)
544
+ REXML::Document.new(port.value(index)).write(out, 1)
541
545
  CodeRay.scan(out, :xml).div(:css => :class, :line_numbers => :table)
542
546
  end
543
547
 
@@ -554,24 +558,59 @@ the XML declaration, e.g.:
554
558
 
555
559
  As described above, images are just dropped into an <tt><img ../></tt> tag:
556
560
 
557
- def show_image(content, type)
561
+ def show_image(port, index = [])
558
562
  # Can't use image_tag() here because the image doesn't really exist (it's
559
563
  # in a zip file, really) and this confuses the Rails asset pipeline.
560
- tag("img", :src => content)
564
+ tag("img", :src => port.path(index))
561
565
  end
562
566
 
563
567
  Note the comment about the Rails asset pipeline in there if you are writing
564
568
  your own image renderer and are using the asset pipeline.
565
569
 
566
- The content parameter here gives the path to the image to be displayed.
570
+ ==== Lists
571
+
572
+ Unless you can be absolutely sure that the workflows that will be run within
573
+ your installation of Taverna Player will only have lists of a certain depth
574
+ the lists renderer will need to be able to cope with anything that is thrown
575
+ at it. The supplied renderer uses recursion to cope with what could, at least
576
+ in theory, be infinitely deep lists:
577
+
578
+ def list_port(port, index = [], types = nil)
579
+ types = port.metadata[:type] if types.nil?
580
+
581
+ content = "<ol>"
582
+ i = 0
583
+ types.each do |type|
584
+ if type.is_a?(Array)
585
+ content += "<li><br />" +
586
+ list_port(port, index + [i], type) + "</li>"
587
+ else
588
+ content += "<li>(#{type})<p>" +
589
+ TavernaPlayer.port_renderer.render(port, index + [i]) +
590
+ "</p></li>"
591
+ end
592
+ i += 1
593
+ end
594
+
595
+ content += "</ol>"
596
+ end
597
+
598
+ This method has an extra parameter that is used to drive the recursion. The
599
+ +types+ parameter contains the list structure of the whole port so can be used
600
+ to loop over, or recurse into, each level as required.
601
+
602
+ Lists are simply rendered as a numbered list along with their type
603
+ information. Other registered renderers are called as necessary to render
604
+ individual values.
567
605
 
568
606
  ==== Other types catch-all
569
607
 
570
608
  The default renderer for other, or unknown types, is:
571
609
 
572
- def cannot_inline(content, type)
610
+ def cannot_inline(port, index = [])
573
611
  "Sorry but we cannot show this type of content in the browser. Please " +
574
- link_to("download it", content) + " to view it on your local machine."
612
+ link_to("download it", port.path(index)) + " to view it on your " +
613
+ "local machine."
575
614
  end
576
615
 
577
616
  == Service Credentials
data/Rakefile CHANGED
@@ -40,7 +40,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
40
40
  "app/models/taverna_player/run_port.rb",
41
41
  "app/models/taverna_player/service_credential.rb",
42
42
  "lib/taverna-player.rb",
43
- "lib/taverna_player/output_renderer.rb"
43
+ "lib/taverna_player/port_renderer.rb"
44
44
  ]
45
45
 
46
46
  rdoc.rdoc_files.include(files)
@@ -21,64 +21,5 @@ module TavernaPlayer
21
21
  end
22
22
  end
23
23
 
24
- def show_output(run, output)
25
- if output.depth == 0
26
- if output.value.blank?
27
- content = run_path(output.run_id) + "/output/#{output.name}"
28
- else
29
- if output.metadata[:size] < 255
30
- content = output.value
31
- else
32
- Zip::ZipFile.open(run.results.path) do |zip|
33
- content = zip.read(output.name)
34
- end
35
- end
36
- end
37
- raw(TavernaPlayer.output_renderer.render(content, output.metadata[:type]))
38
- else
39
- parse_port_list(run, output)
40
- end
41
- end
42
-
43
- private
44
-
45
- def parse_port_list(run, output)
46
- types = output.metadata[:type]
47
- content = String.new
48
-
49
- Zip::ZipFile.open(run.results.path) do |zip|
50
- content = deep_parse(types, output, zip)
51
- end
52
-
53
- content
54
- end
55
-
56
- def deep_parse(types, output, zip, index = [])
57
- content = "<ol>"
58
- i = 0
59
- types.each do |type|
60
- if type.is_a?(Array)
61
- content += "<li><br />" +
62
- deep_parse(type, output, zip, index + [i]) + "</li>"
63
- else
64
- # Text outputs are inlined here by us. Other types are linked and
65
- # inlined by the browser.
66
- content += "<li>(#{type})<p>"
67
- if type.starts_with?("text")
68
- path = (index + [i]).map { |j| j += 1 }.join("/")
69
- data = zip.read("#{output.name}/#{path}")
70
- else
71
- path = (index + [i]).join("/")
72
- data = run_path(output.run_id) + "/output/#{output.name}/#{path}"
73
- end
74
- content += TavernaPlayer.output_renderer.render(data, type)
75
- content += "</p></li>"
76
- end
77
- i += 1
78
- end
79
-
80
- raw(content += "</ol>")
81
- end
82
-
83
24
  end
84
25
  end
@@ -40,8 +40,40 @@ module TavernaPlayer
40
40
  # :method: value
41
41
  # :call-seq:
42
42
  # value -> string
43
+ # value(indices) -> string
43
44
  #
44
- # Return the value held in this port if there is one.
45
+ # Return the value held in this port if there is one. Pass in a list of
46
+ # indices if it is a list port.
47
+ #
48
+ # For a port of depth 0:
49
+ #
50
+ # value = output.value
51
+ #
52
+ # For a port of depth 2:
53
+ #
54
+ # value = output.value(0, 0)
55
+ # value = output.value([1, 2])
56
+ #
57
+ # Trying to get a list value out of a port of depth 0 will simply return
58
+ # the port's value.
59
+
60
+ ##
61
+ # :method: path
62
+ # :call-seq:
63
+ # path -> string
64
+ # path(indices) -> string
65
+ #
66
+ # Return a url path segment that addresses this output value. Pass in a
67
+ # list of indices if it is a list port.
68
+ #
69
+ # For a port of depth 0 called "OUT":
70
+ #
71
+ # path = output.path # => "/runs/1/output/OUT"
72
+ #
73
+ # For a port of depth 2 called "OUT_LIST":
74
+ #
75
+ # path = output.path(0, 0) # => "runs/1/output/OUT_LIST/0/0"
76
+ # path = output.path([1, 2]) # => "runs/1/output/OUT_LIST/1/2"
45
77
 
46
78
  ##
47
79
  # :method: metadata
@@ -26,7 +26,7 @@
26
26
  <b>Value:</b>
27
27
  <%= "(#{output.metadata[:type]})" if output.depth == 0 %>
28
28
  <%= link_to "Download", run_path(run) + "/download/output/#{output.name}" %>
29
- <%= show_output(run, output) %>
29
+ <%= TavernaPlayer.port_renderer.render(output) %>
30
30
  </p>
31
31
 
32
32
  <% end%>
@@ -24,7 +24,7 @@
24
24
 
25
25
  <p>
26
26
  <b>Value:</b>
27
- <%= show_output(run, output) %>
27
+ <%= TavernaPlayer.port_renderer.render(output) %>
28
28
  </p>
29
29
 
30
30
  <% end%>
@@ -22,7 +22,7 @@ module TavernaPlayer
22
22
  "config/initializers/taverna_player.rb"
23
23
 
24
24
  copy_file "server_initializer.rb",
25
- "config/initializers/taverna_server.example.rb"
25
+ "config/initializers/taverna_server.rb.example"
26
26
  end
27
27
 
28
28
  def show_readme
@@ -111,9 +111,9 @@ There is also some manual setup to do, if you haven't already done it:
111
111
  They will be saved to "lib/taverna_player_callbacks.rb". Don't forget to
112
112
  then require and register them in the Taverna Player initializer.
113
113
 
114
- 11. You can add to, or change, the workflow run outputs render methods to
115
- better suit your particular application. To copy the defaults that
116
- Taverna Player ships with into your application for customization run:
114
+ 11. You can add to, or change, the workflow port render methods to better
115
+ suit your particular application. To copy the defaults that Taverna
116
+ Player ships with into your application for customization run:
117
117
 
118
118
  rails generate taverna_player:renderers
119
119
 
@@ -16,41 +16,69 @@
16
16
  # defaults automatically.
17
17
  #
18
18
  # Each method MUST accept two parameters:
19
- # * The first (content) is what will be rendered. In the case of a text/*
20
- # type output this will be the actual text. In the case of anything else
21
- # this will be a path to the object to be linked to.
22
- # * The second (type) is the MIME type of the output as a string. This allows
23
- # a single method to handle multiple types or sub-types if needed.
19
+ # * The first (port) is the port to be rendered.
20
+ # * The second (index) is the index into the port. For singleton ports this
21
+ # will be the empty list.
24
22
  #
25
23
  # Note that you can use most of the ActiveView Helpers here as global methods
26
24
  # but the image_tag() method does not work as explained below.
27
25
 
28
- def format_text(content, type)
26
+ def format_text(port, index = [])
29
27
  # Use CodeRay to format text so that newlines are respected.
30
- content = CodeRay.scan(content, :text).div(:css => :class)
28
+ content = CodeRay.scan(port.value(index), :text).div(:css => :class)
31
29
 
32
30
  # Use auto_link to turn URI-like text into links.
33
31
  auto_link(content, :html => { :target => '_blank' }, :sanitize => false)
34
32
  end
35
33
 
36
- def format_xml(content, type)
34
+ def format_xml(port, index = [])
37
35
  # Make sure XML is indented consistently.
38
36
  out = String.new
39
- REXML::Document.new(content).write(out, 1)
37
+ REXML::Document.new(port.value(index)).write(out, 1)
40
38
  CodeRay.scan(out, :xml).div(:css => :class, :line_numbers => :table)
41
39
  end
42
40
 
43
- def show_image(content, type)
41
+ def show_image(port, index = [])
44
42
  # Can't use image_tag() here because the image doesn't really exist (it's in
45
43
  # a zip file, really) and this confuses the Rails asset pipeline.
46
- tag("img", :src => content)
44
+ tag("img", :src => port.path(index))
47
45
  end
48
46
 
49
- def workflow_error(content, type)
50
- link_to("This output is a workflow error.", content)
47
+ def workflow_error(port, index = [])
48
+ link_to("This output is a workflow error.", port.path(index))
51
49
  end
52
50
 
53
- def cannot_inline(content, type)
51
+ def cannot_inline(port, index = [])
54
52
  "Sorry but we cannot show this type of content in the browser. Please " +
55
- link_to("download it", content) + " to view it on your local machine."
53
+ link_to("download it", port.path(index)) + " to view it on " +
54
+ "your local machine."
55
+ end
56
+
57
+ # Rendering an empty port has no need of the index parameter.
58
+ def empty_port(port, _)
59
+ "<div>&lt;empty port&gt;</div>"
60
+ end
61
+
62
+ # Rendering a list port requires recursion. In this implementation an extra
63
+ # parameter (types) is added to drive the recursion; we can't just keep track
64
+ # of depth because we need to know the length of each sub-list - and types can
65
+ # be used to pass that through for us.
66
+ def list_port(port, index = [], types = nil)
67
+ types = port.metadata[:type] if types.nil?
68
+
69
+ content = "<ol>"
70
+ i = 0
71
+ types.each do |type|
72
+ if type.is_a?(Array)
73
+ content += "<li><br />" +
74
+ list_port(port, index + [i], type) + "</li>"
75
+ else
76
+ content += "<li>(#{type})<p>" +
77
+ TavernaPlayer.port_renderer.render(port, index + [i]) +
78
+ "</p></li>"
79
+ end
80
+ i += 1
81
+ end
82
+
83
+ content += "</ol>"
56
84
  end
@@ -79,22 +79,22 @@ TavernaPlayer.setup do |config|
79
79
  #config.run_failed_callback = "player_run_failed_callback"
80
80
  #config.run_failed_callback = :player_run_failed_callback
81
81
 
82
- # Callbacks to be run to render various types of workflow output. These can
83
- # be defined as Proc objects or as methods and referenced by name.
82
+ # Callbacks to be run to render various types of workflow port. These can be
83
+ # defined as Proc objects or as methods and referenced by name.
84
84
  #
85
85
  # Be careful! If a callback fails then users will see an Internal Server
86
- # Error (HTTP status code 500) instead of their run outputs!
86
+ # Error (HTTP status code 500) instead of their run inputs and/or outputs!
87
87
  #
88
88
  # Add callbacks in this initializer or define them elsewhere and require the
89
89
  # file as usual (if they are not pulled in by some other code). You can
90
- # create example stub callbacks using:
90
+ # create example callbacks using:
91
91
  # "rails generate taverna_player:renderers"
92
92
  # which will put them in "lib/taverna_player_renderers.rb".
93
93
  #require "taverna_player_renderers"
94
94
 
95
- # Renderers for each type of output (referenced by MIME type) must then be
95
+ # Renderers for each type of value (referenced by MIME type) must then be
96
96
  # registered. All the renderers shown below are supplied as defaults.
97
- #config.output_renderers do |renderers|
97
+ #config.port_renderers do |renderers|
98
98
  # Set a default renderer for if there is a workflow type that browsers
99
99
  # can't otherwise handle.
100
100
  #renderers.default(:cannot_inline)
@@ -104,7 +104,7 @@ TavernaPlayer.setup do |config|
104
104
  # aren't otherwise registered.
105
105
  #renderers.add("text/plain", :format_text, true)
106
106
 
107
- # This renderer overrides the default text/* renderer for text/xml outputs.
107
+ # This renderer overrides the default text/* renderer for text/xml values.
108
108
  #renderers.add("text/xml", :format_xml)
109
109
 
110
110
  # Browsers can't show all image types so just register very common ones.
@@ -116,6 +116,10 @@ TavernaPlayer.setup do |config|
116
116
  # This is the workflow error type and you should have a special renderer
117
117
  # for it.
118
118
  #renderers.add("application/x-error", :workflow_error)
119
+
120
+ # If your workflows have list outputs (probable) then something to render
121
+ # lists is also needed.
122
+ #renderers.list(:list_port)
119
123
  #end
120
124
  end
121
125
 
@@ -69,29 +69,30 @@ module TavernaPlayer
69
69
  mattr_accessor :current_user_callback
70
70
  @@current_user_callback = nil
71
71
 
72
- # Setup default output render callbacks.
73
- mattr_reader :output_renderer
74
- @@output_renderer = OutputRenderer.new
75
- @@output_renderer.default(:cannot_inline_tp_default)
76
- @@output_renderer.add("text/plain", :format_text_tp_default, true)
77
- @@output_renderer.add("text/xml", :format_xml_tp_default)
78
- @@output_renderer.add("image/jpeg", :show_image_tp_default)
79
- @@output_renderer.add("image/png", :show_image_tp_default)
80
- @@output_renderer.add("image/gif", :show_image_tp_default)
81
- @@output_renderer.add("image/bmp", :show_image_tp_default)
82
- @@output_renderer.add("application/x-error", :workflow_error_tp_default)
83
- @@output_renderer.add("application/x-empty", :empty_tp_default)
84
- @@output_renderer.add("inode/x-empty", :empty_tp_default)
72
+ # Setup default port render callbacks.
73
+ mattr_reader :port_renderer
74
+ @@port_renderer = PortRenderer.new
75
+ @@port_renderer.default(:cannot_inline_tp_default)
76
+ @@port_renderer.list(:list_tp_default)
77
+ @@port_renderer.add("text/plain", :format_text_tp_default, true)
78
+ @@port_renderer.add("text/xml", :format_xml_tp_default)
79
+ @@port_renderer.add("image/jpeg", :show_image_tp_default)
80
+ @@port_renderer.add("image/png", :show_image_tp_default)
81
+ @@port_renderer.add("image/gif", :show_image_tp_default)
82
+ @@port_renderer.add("image/bmp", :show_image_tp_default)
83
+ @@port_renderer.add("application/x-error", :workflow_error_tp_default)
84
+ @@port_renderer.add("application/x-empty", :empty_tp_default)
85
+ @@port_renderer.add("inode/x-empty", :empty_tp_default)
85
86
 
86
87
  # :call-seq:
87
- # output_renderers {|renderer| ...}
88
+ # port_renderers {|renderer| ...}
88
89
  #
89
90
  # Set up the renderers for each MIME type that you want to be able to show
90
91
  # in the browser. In most cases the supplied defaults will be sufficient.
91
92
  #
92
93
  # See the taverna_player initializer for more information.
93
- def self.output_renderers
94
- yield @@output_renderer if block_given?
94
+ def self.port_renderers
95
+ yield @@port_renderer if block_given?
95
96
  end
96
97
 
97
98
  # Path to place where files should be stored.
@@ -0,0 +1,29 @@
1
+ #------------------------------------------------------------------------------
2
+ # Copyright (c) 2013 The University of Manchester, UK.
3
+ #
4
+ # BSD Licenced. See LICENCE.rdoc for details.
5
+ #
6
+ # Taverna Player was developed in the BioVeL project, funded by the European
7
+ # Commission 7th Framework Programme (FP7), through grant agreement
8
+ # number 283359.
9
+ #
10
+ # Author: Robert Haines
11
+ #------------------------------------------------------------------------------
12
+
13
+ module TavernaPlayer
14
+ module Concerns
15
+ module Callback
16
+
17
+ extend ActiveSupport::Concern
18
+
19
+ def callback(cb, *params)
20
+ if cb.is_a? Proc
21
+ cb.call(*params)
22
+ else
23
+ method(cb).call(*params)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -17,6 +17,9 @@ module TavernaPlayer
17
17
 
18
18
  extend ActiveSupport::Concern
19
19
 
20
+ include TavernaPlayer::Concerns::Callback
21
+ include TavernaPlayer::Concerns::Utils
22
+
20
23
  included do
21
24
  respond_to :html, :json, :js
22
25
 
@@ -55,7 +58,7 @@ module TavernaPlayer
55
58
  return if params[:run][:embedded] == "true" || TavernaPlayer.user_proxy.nil?
56
59
 
57
60
  unless TavernaPlayer.current_user_callback.blank?
58
- user = method(TavernaPlayer.current_user_callback).call
61
+ user = callback(TavernaPlayer.current_user_callback)
59
62
  params[:run][:user_id] = user.id unless user.nil?
60
63
  end
61
64
  end
@@ -76,13 +79,6 @@ module TavernaPlayer
76
79
  end
77
80
  end
78
81
 
79
- # This is here because of Taverna's infinitely deep output ports :-(
80
- def recurse_into_lists(list, indexes)
81
- return list if indexes.empty? || !list.is_a?(Array)
82
- i = indexes.shift
83
- return recurse_into_lists(list[i], indexes)
84
- end
85
-
86
82
  # Choose a layout for the page depending on action and embedded status.
87
83
  def choose_layout
88
84
  if (action_name == "new" || action_name == "show") && @run.embedded?
@@ -77,6 +77,17 @@ module TavernaPlayer
77
77
  end
78
78
  end
79
79
 
80
+ def deep_value(index)
81
+ path = index.map { |j| j += 1 }.join("/")
82
+
83
+ data = String.new
84
+ Zip::ZipFile.open(file.path) do |zip|
85
+ data = zip.read("#{path}")
86
+ end
87
+
88
+ data
89
+ end
90
+
80
91
  end # included
81
92
 
82
93
  def display_name
@@ -87,10 +98,20 @@ module TavernaPlayer
87
98
  self[:value]
88
99
  end
89
100
 
90
- def value
91
- v = self[:value]
101
+ def value(*indices)
102
+ if depth == 0
103
+ v = self[:value]
104
+
105
+ (!v.blank? && !file.path.blank?) ? File.read(file.path) : v
106
+ else
107
+ deep_value([*indices].flatten)
108
+ end
109
+ end
92
110
 
93
- (!v.blank? && !file.path.blank?) ? File.read(file.path) : v
111
+ def path(*indices)
112
+ index = [*indices].flatten
113
+ path = index.empty? ? "" : "/" + index.join("/")
114
+ file_url_via_run + path
94
115
  end
95
116
 
96
117
  end
@@ -0,0 +1,30 @@
1
+ #------------------------------------------------------------------------------
2
+ # Copyright (c) 2013 The University of Manchester, UK.
3
+ #
4
+ # BSD Licenced. See LICENCE.rdoc for details.
5
+ #
6
+ # Taverna Player was developed in the BioVeL project, funded by the European
7
+ # Commission 7th Framework Programme (FP7), through grant agreement
8
+ # number 283359.
9
+ #
10
+ # Author: Robert Haines
11
+ #------------------------------------------------------------------------------
12
+
13
+ module TavernaPlayer
14
+ module Concerns
15
+ module Utils
16
+
17
+ extend ActiveSupport::Concern
18
+
19
+ # Taverna can have arbitrary (therefore effectively "infinite") port
20
+ # depths so we need to recurse into them. This code is common across a
21
+ # number of other modules.
22
+ def recurse_into_lists(list, indexes)
23
+ return list if indexes.empty? || !list.is_a?(Array)
24
+ i = indexes.shift
25
+ recurse_into_lists(list[i], indexes)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -12,14 +12,17 @@
12
12
 
13
13
  module TavernaPlayer
14
14
 
15
- # This class manages the rendering of many different output types that could
16
- # be produced by a workflow. It can be configured with new types and the
15
+ # This class manages the rendering of many different port types that could
16
+ # be associated with a workflow. It can be configured with new types and the
17
17
  # example renderers for each type can also be changed. An example of how to
18
18
  # set it up can be found in the taverna_player initializer.
19
19
  #
20
20
  # Each renderer has all of the ActionView::Helpers (such as link_to, tag,
21
21
  # etc) available to them.
22
- class OutputRenderer
22
+ class PortRenderer
23
+ include TavernaPlayer::Concerns::Callback
24
+ include TavernaPlayer::Concerns::Utils
25
+
23
26
  # The renderers are all called in the scope of this class so we include
24
27
  # ActionView::Helpers here so that they are all available to them.
25
28
  include ActionView::Helpers
@@ -75,21 +78,30 @@ module TavernaPlayer
75
78
  end
76
79
 
77
80
  # :call-seq:
78
- # render(content, mimetype) -> markup
81
+ # list(method)
79
82
  #
80
- # This is the method that calls the correct renderer for the given content
81
- # with the given MIME type and returns the resultant rendering.
82
- def render(content, mimetype)
83
- type = MIME::Types[mimetype].first
84
-
85
- renderer = @hash[type.media_type][type.sub_type] ||
86
- @hash[type.media_type][:default] || @hash[:default]
83
+ # Set a renderer to handle list ports. This will typically format the
84
+ # list somehow and render the list items with further calls to
85
+ # TavernaPlayer.port_renderer.render.
86
+ def list(method)
87
+ @hash[:list] = method
88
+ end
87
89
 
88
- if renderer.is_a? Proc
89
- renderer.call(content, type)
90
+ # :call-seq:
91
+ # render(port) -> markup
92
+ #
93
+ # This is the method that calls the correct renderer for the given port
94
+ # and returns the resultant rendering.
95
+ def render(port, index = [])
96
+ if port.depth > 0 && index.empty?
97
+ renderer = @hash[:list]
90
98
  else
91
- method(renderer).call(content, type)
99
+ type = MIME::Types[recurse_into_lists(port.metadata[:type], index.dup)].first
100
+ renderer = @hash[type.media_type][type.sub_type] ||
101
+ @hash[type.media_type][:default] || @hash[:default]
92
102
  end
103
+
104
+ raw(callback(renderer, port, index))
93
105
  end
94
106
 
95
107
  end
@@ -10,32 +10,53 @@
10
10
  # Author: Robert Haines
11
11
  #------------------------------------------------------------------------------
12
12
 
13
- def format_text_tp_default(content, type)
14
- content = CodeRay.scan(content, :text).div(:css => :class)
13
+ def format_text_tp_default(port, index = [])
14
+ content = CodeRay.scan(port.value(index), :text).div(:css => :class)
15
15
  auto_link(content, :html => { :target => '_blank' }, :sanitize => false)
16
16
  end
17
17
 
18
- def format_xml_tp_default(content, type)
18
+ def format_xml_tp_default(port, index = [])
19
19
  out = String.new
20
- REXML::Document.new(content).write(out, 1)
20
+ REXML::Document.new(port.value(index)).write(out, 1)
21
21
  CodeRay.scan(out, :xml).div(:css => :class, :line_numbers => :table)
22
22
  end
23
23
 
24
- def show_image_tp_default(content, type)
24
+ def show_image_tp_default(port, index = [])
25
25
  # Can't use image_tag() here because the image doesn't really exist (it's in
26
26
  # a zip file, really) and this confuses the Rails asset pipeline.
27
- tag("img", :src => content)
27
+ tag("img", :src => port.path(index))
28
28
  end
29
29
 
30
- def workflow_error_tp_default(content, type)
31
- link_to("This output is a workflow error.", content)
30
+ def workflow_error_tp_default(port, index = [])
31
+ link_to("This output is a workflow error.", port.path(index))
32
32
  end
33
33
 
34
- def cannot_inline_tp_default(content, type)
34
+ def cannot_inline_tp_default(port, index = [])
35
35
  "Sorry but we cannot show this type of content in the browser. Please " +
36
- link_to("download it", content) + " to view it on your local machine."
36
+ link_to("download it", port.path(index)) + " to view it on " +
37
+ "your local machine."
37
38
  end
38
39
 
39
- def empty_tp_default(content, type)
40
- "<div>&lt;empty output&gt;</div>"
40
+ def empty_tp_default(port, _)
41
+ "<div>&lt;empty port&gt;</div>"
42
+ end
43
+
44
+ def list_tp_default(port, index = [], types = nil)
45
+ types = port.metadata[:type] if types.nil?
46
+
47
+ content = "<ol>"
48
+ i = 0
49
+ types.each do |type|
50
+ if type.is_a?(Array)
51
+ content += "<li><br />" +
52
+ list_tp_default(port, index + [i], type) + "</li>"
53
+ else
54
+ content += "<li>(#{type})<p>" +
55
+ TavernaPlayer.port_renderer.render(port, index + [i]) +
56
+ "</p></li>"
57
+ end
58
+ i += 1
59
+ end
60
+
61
+ content += "</ol>"
41
62
  end
@@ -11,5 +11,5 @@
11
11
  #------------------------------------------------------------------------------
12
12
 
13
13
  module TavernaPlayer
14
- VERSION = "0.2.1"
14
+ VERSION = "0.3.0"
15
15
  end
@@ -12,6 +12,7 @@
12
12
 
13
13
  module TavernaPlayer
14
14
  class Worker
15
+ include TavernaPlayer::Concerns::Callback
15
16
 
16
17
  # How to get the interaction presentation frame out of the interaction page.
17
18
  INTERACTION_REGEX = /document\.getElementById\(\'presentationFrame\'\)\.src = \"(.+)\";/
@@ -29,7 +30,7 @@ module TavernaPlayer
29
30
  def perform
30
31
  unless TavernaPlayer.pre_run_callback.nil?
31
32
  status_message "Running pre-run tasks"
32
- run_callback(TavernaPlayer.pre_run_callback, @run)
33
+ callback(TavernaPlayer.pre_run_callback, @run)
33
34
  end
34
35
 
35
36
  status_message "Connecting to Taverna Server"
@@ -192,7 +193,7 @@ module TavernaPlayer
192
193
 
193
194
  unless TavernaPlayer.run_failed_callback.nil?
194
195
  status_message "Running post-failure tasks"
195
- run_callback(TavernaPlayer.run_failed_callback, @run)
196
+ callback(TavernaPlayer.run_failed_callback, @run)
196
197
  end
197
198
 
198
199
  backtrace = exception.backtrace.join("\n")
@@ -206,7 +207,7 @@ module TavernaPlayer
206
207
 
207
208
  unless TavernaPlayer.post_run_callback.nil?
208
209
  status_message "Running post-run tasks"
209
- run_callback(TavernaPlayer.post_run_callback, @run)
210
+ callback(TavernaPlayer.post_run_callback, @run)
210
211
  end
211
212
 
212
213
  @run.state = :finished
@@ -215,14 +216,6 @@ module TavernaPlayer
215
216
 
216
217
  private
217
218
 
218
- def run_callback(callback, *params)
219
- if callback.is_a? Proc
220
- callback.call(*params)
221
- else
222
- method(callback).call(*params)
223
- end
224
- end
225
-
226
219
  def download_log(run)
227
220
  Dir.mktmpdir(run.id, Rails.root.join("tmp")) do |tmp_dir|
228
221
  tmp_file_name = File.join(tmp_dir, "log.txt")
@@ -313,7 +306,7 @@ module TavernaPlayer
313
306
 
314
307
  unless TavernaPlayer.run_cancelled_callback.nil?
315
308
  status_message "Running post-cancel tasks"
316
- run_callback(TavernaPlayer.run_cancelled_callback, @run)
309
+ callback(TavernaPlayer.run_cancelled_callback, @run)
317
310
  end
318
311
 
319
312
  @run.state = :cancelled
@@ -38,22 +38,27 @@ module TavernaPlayer
38
38
  end
39
39
 
40
40
  test "should route to a run output" do
41
- assert_routing "/runs/1/output/OUT",
41
+ assert_routing @run2.outputs[0].path,
42
42
  { :controller => "taverna_player/runs", :action => "output",
43
- :id => "1", :port => "OUT" }, {}, {}, "Did not route correctly"
43
+ :id => "2", :port => "OUT" }, {}, {}, "Did not route correctly"
44
44
  end
45
45
 
46
46
  test "should route to a deep run output" do
47
- assert_routing "/runs/1/output/OUT/0/0",
47
+ assert_routing @run2.outputs[0].path(0, 0),
48
48
  { :controller => "taverna_player/runs", :action => "output",
49
- :id => "1", :port => "OUT", :path => "0/0" }, {}, {},
49
+ :id => "2", :port => "OUT", :path => "0/0" }, {}, {},
50
+ "Did not route correctly"
51
+
52
+ assert_routing @run2.outputs[0].path([1, 2]),
53
+ { :controller => "taverna_player/runs", :action => "output",
54
+ :id => "2", :port => "OUT", :path => "1/2" }, {}, {},
50
55
  "Did not route correctly"
51
56
  end
52
57
 
53
58
  test "should route to a run input" do
54
- assert_routing "/runs/1/input/IN",
59
+ assert_routing @run3.inputs[0].path,
55
60
  { :controller => "taverna_player/runs", :action => "input",
56
- :id => "1", :port => "IN" }, {}, {}, "Did not route correctly"
61
+ :id => "3", :port => "IN_Value" }, {}, {}, "Did not route correctly"
57
62
  end
58
63
 
59
64
  test "should route to cancel on a run" do
@@ -14,32 +14,6 @@ require 'test_helper'
14
14
 
15
15
  module TavernaPlayer
16
16
  class RunsHelperTest < ActionView::TestCase
17
- setup do
18
- @run1 = taverna_player_runs(:one)
19
- @port1 = taverna_player_run_ports(:one)
20
17
 
21
- @run2 = taverna_player_runs(:three)
22
- @port2 = taverna_player_run_ports(:four)
23
-
24
- @run3 = taverna_player_runs(:four)
25
- @port3 = taverna_player_run_ports(:five)
26
- end
27
-
28
- test "should show text outputs" do
29
- assert_equal "Hello, World!", @port1.value, "Unexpected workflow output"
30
- assert show_output(@run1, @port1).include?("<pre>Hello, World!</pre>"),
31
- "Workflow output not formatted correctly"
32
-
33
- assert_equal "Rob", @port2.value, "Unexpected workflow output"
34
- assert show_output(@run2, @port2).include?("<pre>Rob</pre>"),
35
- "Workflow output not formatted correctly"
36
- end
37
-
38
- test "should autolink text output" do
39
- assert_equal "(http://example.com/path?query=1)", @port3.value,
40
- "Unexpected workflow output"
41
- assert show_output(@run3, @port3).include?("(<a href=\"http://example.com/path?query=1\" target=\"_blank\">http://example.com/path?query=1</a>)"),
42
- "Workflow output not formatted correctly"
43
- end
44
18
  end
45
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taverna-player
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-30 00:00:00.000000000 Z
12
+ date: 2013-12-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -331,15 +331,17 @@ files:
331
331
  - lib/tasks/delete-cancelled-runs.rake
332
332
  - lib/tasks/delete-old-embedded-runs.rake
333
333
  - lib/taverna-player.rb
334
+ - lib/taverna_player/concerns/callback.rb
334
335
  - lib/taverna_player/concerns/controllers/runs_controller.rb
335
336
  - lib/taverna_player/concerns/controllers/service_credentials_controller.rb
336
337
  - lib/taverna_player/concerns/models/input_port.rb
337
338
  - lib/taverna_player/concerns/models/output_port.rb
338
339
  - lib/taverna_player/concerns/models/run.rb
339
340
  - lib/taverna_player/concerns/models/run_port.rb
341
+ - lib/taverna_player/concerns/utils.rb
340
342
  - lib/taverna_player/engine.rb
341
343
  - lib/taverna_player/model_proxy.rb
342
- - lib/taverna_player/output_renderer.rb
344
+ - lib/taverna_player/port_renderer.rb
343
345
  - lib/taverna_player/render_callbacks.rb
344
346
  - lib/taverna_player/version.rb
345
347
  - lib/taverna_player/worker.rb
@@ -373,7 +375,7 @@ files:
373
375
  - test/dummy/config/initializers/secret_token.rb
374
376
  - test/dummy/config/initializers/session_store.rb
375
377
  - test/dummy/config/initializers/taverna_player.rb
376
- - test/dummy/config/initializers/taverna_server.example.rb
378
+ - test/dummy/config/initializers/taverna_server.rb.example
377
379
  - test/dummy/config/initializers/wrap_parameters.rb
378
380
  - test/dummy/config/locales/en.yml
379
381
  - test/dummy/config/routes.rb
@@ -454,7 +456,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
454
456
  version: '0'
455
457
  segments:
456
458
  - 0
457
- hash: -3999716263175930370
459
+ hash: 1554949292676939893
458
460
  required_rubygems_version: !ruby/object:Gem::Requirement
459
461
  none: false
460
462
  requirements:
@@ -463,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
463
465
  version: '0'
464
466
  segments:
465
467
  - 0
466
- hash: -3999716263175930370
468
+ hash: 1554949292676939893
467
469
  requirements: []
468
470
  rubyforge_project:
469
471
  rubygems_version: 1.8.21
@@ -498,7 +500,7 @@ test_files:
498
500
  - test/dummy/config/initializers/secret_token.rb
499
501
  - test/dummy/config/initializers/session_store.rb
500
502
  - test/dummy/config/initializers/taverna_player.rb
501
- - test/dummy/config/initializers/taverna_server.example.rb
503
+ - test/dummy/config/initializers/taverna_server.rb.example
502
504
  - test/dummy/config/initializers/wrap_parameters.rb
503
505
  - test/dummy/config/locales/en.yml
504
506
  - test/dummy/config/routes.rb