Skip to content

Commit 2bdb000

Browse files
document custom boutiques properties
1 parent e4c3773 commit 2bdb000

File tree

1 file changed

+149
-52
lines changed

1 file changed

+149
-52
lines changed

BrainPortal/lib/boutiques_support.rb

Lines changed: 149 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#
2525
# See also the Boutiques repository: https://github.com/boutiques/boutiques
2626
#
27-
# The modules provides one main Ruby class, BoutiquesSupport::BoutiquesDescriptor,
27+
# The module provides one main Ruby class, BoutiquesSupport::BoutiquesDescriptor,
2828
# and several smaller data classes representing components of a descriptor.
2929
# These classes are subclasses of RestrictedHash, a type of Hash class that
3030
# only recognize a select set of keys and raise an exception when other keys
@@ -58,49 +58,6 @@
5858
# appear in RDOC-generated documentation. Among these, many are
5959
# used by the BoutiquesTask integrator.
6060
#
61-
# # Returns the name of the tool NNNN, appropriate to use as
62-
# # a class name as BoutiquesTask::NNNN
63-
# desc.name_as_ruby_class
64-
#
65-
# # Returns all tags as a flat array
66-
# desc.flat_tag_list
67-
#
68-
# # Finds a specific BoutiquesSupport:Input by ID
69-
# desc.input_by_id(inputid)
70-
#
71-
# # Subset of the list of inputs with just the optional ones
72-
# desc.optional_inputs
73-
#
74-
# # Subset of the list of inputs with just the mandatory ones
75-
# desc.required_inputs
76-
#
77-
# # Subset of the list of inputs with just the multi-valued ones
78-
# desc.list_inputs
79-
#
80-
# # Subset of the list of inputs with just the File inputs
81-
# desc.file_inputs
82-
#
83-
# # List of File inputs that are optional
84-
# desc.optional_file_inputs
85-
#
86-
# # List of File inputs that are mandatory
87-
# desc.required_file_inputs
88-
#
89-
# # Returns the entry for a custom Boutiques integration module
90-
# desc.custom_module_info(modulename)
91-
#
92-
# # Utility for building a replacement hash for the inputs based on
93-
# # the values in invoke_structure
94-
# desc.build_substitutions_by_tokens_hash(invoke_structure)
95-
#
96-
# # Utility to perform the subsitutions of tokens in a string
97-
# desc.apply_substitutions(string, substitutions_by_tokens, to_strip=[])
98-
#
99-
# # Returns a new descriptor with the attributes in a canonical beautiful order
100-
# desc.pretty_ordered
101-
#
102-
# # Generates a JSON with nice spacing
103-
# desc.super_pretty_json
10461
#
10562
module BoutiquesSupport
10663

@@ -165,6 +122,7 @@ def initialize(hash={})
165122
self
166123
end
167124

125+
# Creates a new Boutiques object from string
168126
def self.new_from_string(text)
169127
json = JSON.parse(text)
170128
errors = BoutiquesSupport.validate(json)
@@ -173,13 +131,14 @@ def self.new_from_string(text)
173131
cb_error "Invalid Boutiques descriptor\n" + (errors.map { |e| e[:message] }.join("\n"))
174132
end
175133

134+
# Creates a new Boutiques object from a documents stored in a given path
176135
def self.new_from_file(path)
177136
obj = self.new_from_string(File.read(path))
178137
obj.from_file = path
179138
obj
180139
end
181140

182-
def validate
141+
def validate #:nodoc:
183142
BoutiquesSupport.validate(self) # amazingly, the JSON validator also work with our descriptor class
184143
end
185144

@@ -226,6 +185,7 @@ def name_as_ruby_class
226185
.camelize
227186
end
228187

188+
# Returns all tags as a flat arra
229189
def flat_tag_list
230190
tags = self.tags
231191
return [] if ! tags
@@ -235,31 +195,38 @@ def flat_tag_list
235195
end.flatten
236196
end
237197

198+
# Finds a specific Input by id
238199
def input_by_id(inputid)
239200
inputs.detect { |x| x.id == inputid } or
240201
cb_error "No input found with ID '#{inputid}'"
241202
end
242203

204+
# Lists optional inputs
243205
def optional_inputs
244206
inputs.select { |x| x.optional }
245207
end
246208

209+
# Lists required inputs
247210
def required_inputs
248211
inputs.select { |x| ! x.optional }
249212
end
250213

214+
# Lists inputs
251215
def list_inputs
252216
inputs.select { |x| x.list }
253217
end
254218

219+
# Lists File inputs
255220
def file_inputs
256221
inputs.select { |x| x.type == 'File' }
257222
end
258223

224+
# Lists optional File inputs
259225
def optional_file_inputs
260226
file_inputs.select { |x| x.optional }
261227
end
262228

229+
# Lists mandatory File inputs
263230
def required_file_inputs
264231
file_inputs.select { |x| ! x.optional }
265232
end
@@ -338,7 +305,7 @@ def build_substitutions_by_tokens_hash(invoke_structure)
338305
end.compact.to_h
339306
end
340307

341-
# Replaces in +string+ all occurences of the keys in
308+
# Replaces in +string+ all occurrences of the keys in
342309
# +substitutions_by_tokens+ by the associated values.
343310
# This is typically used to build a templated string
344311
# using the "value-key" of the inputs of the descriptor.
@@ -519,6 +486,136 @@ def super_pretty_json
519486
new_json
520487
end
521488

489+
#-------------------------------------------------------------------------
490+
# Methods to access and document CBRAIN specific custom properties
491+
#-------------------------------------------------------------------------
492+
# see public/doc/boutiques_extensions for a list of these custom properties
493+
494+
# Returns a string with name(s) and emails(s) of the Boutiques descriptor authors, enlisted in
495+
# "cbrain:author" custom property of the descriptors. Emails are optional
496+
# and should be in angle brackets
497+
#
498+
# For example, given the descriptor with
499+
#
500+
# "custom": { "cbrain:author": "Full Name <email@address.ca>, Co-author Name <anotheremail@address.org>" }
501+
#
502+
# The method returns string
503+
# "Full Name <email@address.ca>, Co-author Name <anotheremail@address.org>"
504+
def custom_author
505+
authors = self.custom['cbrain:author']
506+
return authors if authors is_a? String
507+
return authors.join(", ") # if author field is arrays
508+
end
509+
510+
# Returns Boutiques CBRAIN custom property indicating
511+
# are forking sub-task(s) allowed. To submit a subtask, a task must create a JSON file
512+
# named ".new-task-*.json" in the root of its
513+
# work directory. An example of property definition in a tool descriptor:
514+
#
515+
# "custom: {
516+
# "cbrain:can-submit-new-tasks": true
517+
# }
518+
def custom_can_submit_new_tasks
519+
return self.custom["cbrain:can-submit-new-tasks"]
520+
end
521+
522+
# Returns Boutiques CBRAIN custom property indicating
523+
# the outputs which will not be saved.
524+
# An example of property definition in a tool descriptor:
525+
#
526+
# "custom: {
527+
# "cbrain:ignore_outputs": [output_id_1, output_id_2, output_id_3 ... ]
528+
# }
529+
def custom_ignore_outputs
530+
return self.custom["cbrain:ignore_outputs"]
531+
end
532+
533+
# Returns Boutiques CBRAIN custom property indicating
534+
# inputs which are saved back to the dataprovider
535+
# (the original data will be mutated).
536+
#
537+
# An example of property definition in a tool descriptor:
538+
# "custom: {
539+
# "cbrain:save_back_inputs": [id_1, id_2, id_3 ...]
540+
# }
541+
def custom_save_back_inputs
542+
return self.custom["cbrain:save_back_inputs"]
543+
end
544+
545+
# Returns Boutiques CBRAIN custom property indicating
546+
# that the tool does not modify inputs.
547+
# An example of property definition in a tool descriptor:
548+
#
549+
# "custom: {
550+
# "cbrain:readonly-input-files": true
551+
# }
552+
def custom_readonly_input_files
553+
return self.custom["cbrain:readonly-input-files"]
554+
end
555+
556+
# Returns Boutiques CBRAIN custom property indicating
557+
# if this task may alter its input files.
558+
# An example of property definition in a tool descriptor:
559+
#
560+
# "custom: {
561+
# "cbrain:alters-input-files": true
562+
# }
563+
def custom_alters_input_files
564+
return self.custom["cbrain:alters-input-files"]
565+
end
566+
567+
# Returns Boutiques CBRAIN custom property indicating for which outputs
568+
# the usual practice of adding a run id to output file names is cancelled,
569+
# list of output IDs where no run id inserted. Only allowed for MultiLevel
570+
# data-providers with "browse path" capability.
571+
# For listed outputs ids new results overwrite old files.
572+
# An example of property definition in a tool descriptor:
573+
#
574+
# "custom: {
575+
# "cbrain:no-run-id-for-outputs": "id_1, id_2, id_3 .."
576+
# }
577+
def custom_no_run_id_for_outputs
578+
return self.custom["cbrain:no-run-id-for-outputs"]
579+
end
580+
581+
# Returns Boutiques CBRAIN custom property indicating
582+
# for which inputs an empty string is a valid input.
583+
# An example of property definition in a tool descriptor:
584+
#
585+
# "custom: {
586+
# "cbrain:allow_empty_strings": [input_id]
587+
# }
588+
def custom_allow_empty_strings
589+
return self.custom["cbrain:allow_empty_strings"]
590+
end
591+
592+
# Experimental feature that affects the way tasks are executed.
593+
# The default implied value is 'simulate'
594+
# In the mode 'simulate', at the moment of creating
595+
# the tool's script in cluster_commands(), the
596+
# output of 'bosh exec simulate' will be substituted in
597+
# the script to generate the tool's command.
598+
# In the mode 'launch', an actual 'bosh exec launch' command
599+
# will be put in the script instead.
600+
# An example of property definition in a tool descriptor:
601+
#
602+
# "custom: {
603+
# "cbrain:boutiques_bosh_exec_mode": "launch"
604+
# }
605+
def custom_boutiques_bosh_exec_mode
606+
return self.custom["cbrain:boutiques_bosh_exec_mode"]
607+
end
608+
609+
# An advanced feature for seasoned CBRAIN experts only. That allows
610+
# overwrite the standard task behavior with custom class.
611+
# An example of property definition in a tool descriptor:
612+
# "custom: {
613+
# "cbrain:inherits-from-class": "MyClassName"
614+
# }
615+
def custom_inherits_from_class
616+
return self.custom["cbrain:inherits-from-class"]
617+
end
618+
522619
end # class BoutiquesSupport::BoutiquesDescriptor
523620

524621
#------------------------------------------------------
@@ -543,23 +640,23 @@ def dup #:nodoc:
543640
copy
544641
end
545642

546-
# This method return the parameter name for an input identified
643+
# This method returns the parameter name for an input identified
547644
# by input_id.
548645
# We put all input Boutiques parameters under a 'invoke' substructure.
549646
# E.g. for a input with ID 'abcd' in a task, we'll find the value
550647
# in task.params['invoke']['abcd'] and the parameter name is thus
551648
# "invoke[abcd]". The as_list option appends "[]" to the name
552649
# to make it an array parameter.
553-
def self.cb_invoke_name(input_id, as_list = nil)
650+
def self.cb_invoke_name(input_id, as_list = nil) #:nodoc:
554651
return "invoke[#{input_id}][]" if as_list
555652
return "invoke[#{input_id}]"
556653
end
557654

558-
def self.cb_invoke_html_name(input_id, force_list = nil)
655+
def self.cb_invoke_html_name(input_id, force_list = nil) #:nodoc:
559656
self.cb_invoke_name(input_id, force_list).to_la
560657
end
561658

562-
def self.cb_invoke_html_id(input_id, force_list = nil)
659+
def self.cb_invoke_html_id(input_id, force_list = nil) #:nodoc:
563660
self.cb_invoke_name(input_id, force_list).to_la_id
564661
end
565662

@@ -576,12 +673,12 @@ def cb_invoke_name(force_list = nil)
576673
self.class.cb_invoke_name(self.id, as_list)
577674
end
578675

579-
def cb_invoke_html_name(force_list = nil)
676+
def cb_invoke_html_name(force_list = nil) #:nodoc:
580677
as_list = (self.list && force_list.nil?) || force_list == true
581678
self.class.cb_invoke_html_name(self.id, as_list)
582679
end
583680

584-
def cb_invoke_html_id(force_list = nil)
681+
def cb_invoke_html_id(force_list = nil) #:nodoc:
585682
as_list = (self.list && force_list.nil?) || force_list == true
586683
self.class.cb_invoke_html_id(self.id, as_list)
587684
end

0 commit comments

Comments
 (0)