Module: Bio::Command::Wrapper

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



19
20
21
# File 'lib/wrapper.rb', line 19

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#class_nameObject

Return the class name



222
223
224
# File 'lib/wrapper.rb', line 222

def class_name
  self.class.name.split("::").last.downcase
end

#default_optionsObject



69
70
71
72
73
# File 'lib/wrapper.rb', line 69

def default_options
  options.select do |name, opts|
    opts.has_key? :default
  end
end

#initialize(binary = nil, options = {}) ⇒ Object



41
42
43
44
45
46
47
# File 'lib/wrapper.rb', line 41

def initialize(binary=nil, options={})
  @program = binary || self.class.program
  @options = options
  @params = {}
  @pipe_ahead = []
  @path = options.delete(:path) || "."
end

#normalize_params(separator = "=") ⇒ Object

Return the options and parameters formmatted as typed in the command line as a string opts is important not all the applications require a “=” for separating options and values TODO: need to be compliant with Bio::Command ? TODO: make a test because it should not return an empty string. TODO: refactor is not beauty



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/wrapper.rb', line 112

def normalize_params(separator="=")
  #use_aliases?
  args=params.to_a.map do |option|
    option_name = option[0].to_s
    option_values = option[1]
    #deprecated I'm not sure this code is good (at least the one with kind_of?)
    if option_values.kind_of? Hash
      #TODO: refactor this code and verify that the boolean needs a specific options setting.
      #"--#{option_name}" + ((option_values.has_key?(:type) && option_values[:type]==:boolean) ? ("="+ (option_values[:default] ? "true": "false")) :"=#{option_values[:default]}")
      if (option_values.has_key?(:type) && option_values[:type]==:boolean && option_values[:default])
        "--#{option_name}"
      else
        use_aliases? && options[option_name].has_key?(:aliases) ? "#{options[option_name][:aliases]} #{option_values[:default]}" : "--#{option_name}#{separator}#{option_values[:default]}"
      end
      #deprecated up to here
    else #is a value of the main hash. (mostly a parameter)
      if option_values == true
        use_aliases? && options[option_name].has_key?(:aliases) ? options[option_name][:aliases] : "--#{option_name}"
      elsif option_values != false
        use_aliases? && options[option_name].has_key?(:aliases) ? "#{options[option_name][:aliases]}#{options[option_name][:collapse] ? "": " "}#{option_values}" : "--#{option_name}#{separator}#{option_values}"
      end
    end
  end
end

#optionsObject



23
24
25
# File 'lib/wrapper.rb', line 23

def options
  self.class.options.merge(@options)
end

#options=(option = {}) ⇒ Object



27
28
29
30
31
# File 'lib/wrapper.rb', line 27

def options=(option={})
  #convert all keys symbols in strings
  option = option.inject({}){|h,item| h[item[0].to_s]=item[1]; h}
  @options.merge!(option)
end

#outputObject



137
138
139
# File 'lib/wrapper.rb', line 137

def output
  self.class.output || :file
end

#paramsObject

Return the options, which are by default, and the parameters setted by the user. Precedence goes to params setted from user



78
79
80
# File 'lib/wrapper.rb', line 78

def params
  default_options.merge(@params)
end

#params=(opts = {}) ⇒ Object

Parameters are accepted ONLY if the key is present as a key on the options hash. Sort of validation. ONLY the valid options are taken into account. It like a third level of configuration TODO: check :aliases in options as well, not that now only the main option name is verified



62
63
64
65
66
67
# File 'lib/wrapper.rb', line 62

def params=(opts={})
  #add the parameters only if in options
  opts.each_pair do |parameter, value|
    @params[parameter.to_s] = value if options.has_key?(parameter.to_s)
  end        
end

#pathObject



49
50
51
# File 'lib/wrapper.rb', line 49

def path
  @path
end

#path=(path) ⇒ Object



53
54
55
# File 'lib/wrapper.rb', line 53

def path=(path)
  @path=path
end

#pipe_aheadObject



87
88
89
90
# File 'lib/wrapper.rb', line 87

def pipe_ahead
  # TODO: recursive call to other Bio::Ngs wrapped commands
  @pipe_ahead
end

#pipe_ahead=(ary) ⇒ Object

If setted is an array described like the ones in Open3.pipeline www.ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/Open3.html#method-c-pipeline



98
99
100
# File 'lib/wrapper.rb', line 98

def pipe_ahead=(ary)
  @pipe_ahead=ary || []
end

#pipe_ahead?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/wrapper.rb', line 92

def pipe_ahead?
  return !@pipe_ahead.empty?
end

#programObject



33
34
35
# File 'lib/wrapper.rb', line 33

def program
  @program
end

#reset_paramsObject



82
83
84
# File 'lib/wrapper.rb', line 82

def reset_params
  @params.clear
end

#run(opts = {:options=>{}, :arguments=>[], :output_file=>nil, :separator=>"="}) ⇒ Object

If parameters are passed they will overwrite those already defined but will not save the changes opts = :options=>{, :arguments=>[]} in the particular case the user wants to submit other options these must be passed in arguments like “option_name”=>value similar when settin params opts is important not all the applications require a “=” for separating options and values TODO handle output file with program which writes on stdout TODO: refactor mostly due to stdin/out



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/wrapper.rb', line 154

def run(opts = {:options=>{}, :arguments=>[], :output_file=>nil, :separator=>"="})
  if program.nil?
    warn "WARNING: no program is associated with #{class_name.upcase} task."
    return nil
  end  
  #REMOVE        params = opts[:options]
  if output == :stdout 
    raise "Can't write to any output file. With a program which writes on stdout you must provide a file name" if opts[:output_file].nil?
    file_stdlog = File.open(opts[:output_file], 'w')
    file_errlog = File.open(opts[:output_file]+".err",'w')
    #[program, sub_program, normalize_params(opts[:separator]), opts[:arguments]].flatten.compact
    Bio::Command.call_command_open3(to_cmd_ary(separator:opts[:separator], arguments:opts[:arguments])) do |pin, pout, perr|
      pout.sync = true
      perr.sync = true           
      t = Thread.start {pout.lines{|line| file_stdlog.puts line}}
      begin
        pin.close
      ensure
        t.join
      end
    end #command call open3
    file_stdlog.close
    file_errlog.close

  elsif pipe_ahead?
    #in case the user setted the pipeline we use it.
    Open3.pipeline(pipe_ahead, to_cmd_ary(separator:opts[:separator], arguments:opts[:arguments]))
  else
    # puts "Normlized #{normalize_params(opts[:separator])}"
    # puts "Arguments #{opts[:arguments]}"
    #puts [program, sub_program, normalize_params(opts[:separator]), opts[:arguments]].flatten.compact.inspect
  #Note: maybe seprator could be defined as a method  for each wrapped program ?
  Bio::Command.query_command(to_cmd_ary(separator:opts[:separator], arguments:opts[:arguments]))
  #[program, sub_program, normalize_params(opts[:separator]), opts[:arguments]].flatten.compact
  end #if
end

#sub_programObject



37
38
39
# File 'lib/wrapper.rb', line 37

def sub_program
  self.class.sub_program
end

#thor_task(klass, task_name, &block) ⇒ Object

Inject into the Thor::Sandbox::TaskName (klass) the options defined for this wrapper Example of call

desc "task_name ARG_ONE ARG_SECOND", "run tophat as from command line"
Bio::Ngs::Tophat.new.thor_task(self, :tophat) do |wrapper, task, ARG_ONE ARG_SECOND|
    puts ARG_ONE
    puts ARG_SECOND
    #you tasks here
end


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/wrapper.rb', line 200

def thor_task(klass, task_name, &block)
  if program.nil?
    warn "WARNING: no program is associated with #{class_name.upcase} task, does not make sense to create a thor task."
    return nil
  end          
  if klass
    wrapper = self   
    klass.class_eval do            
      wrapper.options.each_pair do |name, opt|
        method_option name, opt
      end #each_pair
      # Thor's behavior should be respected passing attributes
      define_method task_name do |*args|
        #it's mandatory that the first and second parameter are respectively wrapper and task
        raise ArgumentError, "wrong number of arguments (#{args.size} for #{block.parameters.size-2})" if args.size != block.parameters.size-2
        yield wrapper, self, *args
      end
    end#class_eval
  end #klass
end

#to_cmd_ary(opts = {arguments:[],separator:"="}) ⇒ Object

Return an array of elements of the command line



103
104
105
# File 'lib/wrapper.rb', line 103

def to_cmd_ary(opts={arguments:[],separator:"="})
  [program, sub_program, normalize_params(opts[:separator]), opts[:arguments]].flatten.compact
end

#use_aliases?Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/wrapper.rb', line 141

def use_aliases?
  self.class.aliases
end