ruby-igv 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -12
  3. data/lib/igv/version.rb +1 -1
  4. data/lib/igv.rb +242 -53
  5. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0cb49945b7684826529fbdd82ae84de01c45f3b309f1cf41e096dab651ca261
4
- data.tar.gz: a345c80a98c564cf7de1cb62768214f8273abf033b1c24ebb8575bb69fe00026
3
+ metadata.gz: 1ca8294136b5eab8a344a40bf1d5ebb6d8ebf08f2794a1c6e1f582f593432049
4
+ data.tar.gz: c36d4c09a163ade8bfd860519a50519a28ae3f93121a14c482bf1bceb6239cde
5
5
  SHA512:
6
- metadata.gz: db09a0dc972eb58b3c90412be2a7503015ca3b32152ea2f4be89065833ed94d674fb4bce97a19a5ba8721b59be14b8c8beb00849db865d6b9ee9cfe3ea0afffc
7
- data.tar.gz: b97c8c53963c3a9460a38c06bde171fd5f9494c7bdbc8b575ded5b17a854b0038ac421da45628f42865d9cecef7824485adc80e92af9281541bac6dc96466102
6
+ metadata.gz: 1a085655336dcd4d06db5b51269f6b72d8d7174767f0b2990796d29cd410fb90adc884b86df3cf97087e75b35d387f655ed557628145c3af9badf17d3b29c50e
7
+ data.tar.gz: d8e0741301b1504f7714077ef435a987b51c520fcc3788f85acaf42ad3785fcec7bf435e897666a5f9a16050d2f503cfab5daa68525a1fc86a18b5219018a6ed
data/README.md CHANGED
@@ -5,31 +5,82 @@
5
5
  [![The MIT License](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE.txt)
6
6
  [![DOI](https://zenodo.org/badge/281373245.svg)](https://zenodo.org/badge/latestdoi/281373245)
7
7
 
8
+
9
+ <img src="https://user-images.githubusercontent.com/5798442/182540876-c3ca2906-7d05-4c93-9107-ce4135ae9765.png" align="right">
10
+
8
11
  ## Installation
9
12
 
10
- Requirement: [IGV (Integrative Genomics Viewer)](http://software.broadinstitute.org/software/igv/) and [Ruby](https://github.com/ruby/ruby).
13
+ Requirement :
14
+
15
+ * [Ruby](https://github.com/ruby/ruby)
16
+ * [IGV (Integrative Genomics Viewer)](http://software.broadinstitute.org/software/igv/)
17
+ * [Enable IGV to listen on the port](https://software.broadinstitute.org/software/igv/Preferences#Advanced)
18
+ * View > Preference > Advanced > Enable port ☑
11
19
 
12
20
  ```ruby
13
21
  gem install ruby-igv
14
22
  ```
15
23
 
24
+ ## Quickstart
25
+
26
+ ```ruby
27
+ require 'igv'
28
+
29
+ igv = IGV.start # This launch IGV
30
+ igv.genome 'hg19'
31
+ igv.load 'http://hgdownload.cse.ucsc.edu/goldenPath/hg19/encodeDCC/' \
32
+ 'wgEncodeUwRepliSeq/wgEncodeUwRepliSeqK562G1AlnRep1.bam'
33
+ igv.go 'chr18:78,016,233-78,016,640'
34
+ igv.snapshot 'region.png'
35
+ igv.exit
36
+ ```
37
+
16
38
  ## Usage
17
39
 
18
- [Enable IGV to listen on the port](https://software.broadinstitute.org/software/igv/Preferences#Advanced): Preference > Advanced > Enable port
40
+ ### docs
41
+
42
+ See [the list of Batch commands](https://github.com/igvteam/igv/wiki/Batch-commands).
19
43
 
20
44
  ```ruby
21
- igv = IGV.new
22
- igv.genome 'hg19'
23
- igv.load 'http://hgdownload.cse.ucsc.edu/goldenPath/hg19/encodeDCC/wgEncodeUwRepliSeq/wgEncodeUwRepliSeqK562G1AlnRep1.bam'
24
- igv.go 'chr18:78,016,233-78,016,640'
25
- igv.save '/tmp/r/region.svg'
26
- igv.save '/tmp/r/region.png'
27
- igv.snapshot_dir = '/tmp/r2/'
28
- igv.save 'region.jpg' # save to /tmp/r2/region.png
29
- igv.send 'echo' # whatever you want
45
+ igv.commands # Show the IGV command reference in your browser
30
46
  ```
31
47
 
32
- See [the list of Batch commands](https://github.com/igvteam/igv/wiki/Batch-commands).
48
+ [docs](https://rubydoc.info/gems/ruby-igv)
49
+
50
+ ### send
51
+
52
+ Not all commands are implemented in Ruby. Commands that are not implemented can be sent using the send method.
53
+
54
+ ```ruby
55
+ igv.send("maxPanelHeight", 10)
56
+ ```
57
+
58
+
59
+ ### Launch IGV
60
+
61
+ Launch IGV from Ruby scripot.
62
+
63
+ ```ruby
64
+ igv = IGV.start # launch IGV app using spawn
65
+ ```
66
+
67
+ ### Open socket connection to IGV
68
+
69
+ ```ruby
70
+ igv = IGV.new # create an IGV object. Then you will type `igv.connect`
71
+ igv = IGV.open # create an IGV object and connect it to an already activated IGV.
72
+ ```
73
+
74
+ ### Close IGV
75
+
76
+ The behavior of the following methods is different.
77
+
78
+ ```ruby
79
+ igv.close # close the socket connection
80
+ igv.exit # send exit command to IGV
81
+ igv.quit # alias method to exit
82
+ igv.kill # kill group pid created with IGV.start
83
+ ```
33
84
 
34
85
  ## Contributing
35
86
 
data/lib/igv/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class IGV
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.7'
5
5
  end
data/lib/igv.rb CHANGED
@@ -9,103 +9,292 @@ require 'fileutils'
9
9
  class IGV
10
10
  class Error < StandardError; end
11
11
 
12
- attr_reader :host, :port, :snapshot_dir, :history
12
+ attr_reader :host, :port, :history
13
13
 
14
14
  def initialize(host: '127.0.0.1', port: 60_151, snapshot_dir: Dir.pwd)
15
15
  @host = host
16
16
  @port = port
17
+ @snapshot_dir = File.expand_path(snapshot_dir)
17
18
  @history = []
18
- connect
19
- set_snapshot_dir(snapshot_dir)
20
19
  end
21
20
 
22
- # def self.start
23
- # end
21
+ def self.open(host: '127.0.0.1', port: 60_151, snapshot_dir: Dir.pwd)
22
+ igv = new(host: host, port: port, snapshot_dir: snapshot_dir)
23
+ igv.connect
24
+ return igv unless block_given?
24
25
 
25
- def connect
26
+ begin
27
+ yield igv
28
+ ensure
29
+ @socket&.close
30
+ end
31
+ igv
32
+ end
33
+
34
+ # Launch IGV from ruby script
35
+
36
+ def self.start(port: 60_151, command: 'igv')
37
+ r, w = IO.pipe
38
+ pid_igv = spawn(command, '-p', port.to_s, pgroup: true, out: w, err: w)
39
+ pgid_igv = Process.getpgid(pid_igv)
40
+ Process.detach(pid_igv)
41
+ puts "\e[33m"
42
+ while (line = r.gets.chomp("\n"))
43
+ puts line
44
+ break if line.include? "Listening on port #{port}"
45
+ end
46
+ puts "\e[0m"
47
+ igv = open(port: port)
48
+ igv.instance_variable_set(:@pgid_igv, pgid_igv)
49
+ igv
50
+ end
51
+
52
+ # Kill IGV process by process group id
53
+
54
+ def kill
55
+ if instance_variable_defined?(:@pgid_igv)
56
+ warn \
57
+ 'This method kills the process with the group ID specified at startup. ' \
58
+ 'Please use exit or quit if possible.'
59
+ else
60
+ warn \
61
+ 'The kill method terminates only IGV commands invoked by the start method.' \
62
+ 'Otherwise, use exit or quit.'
63
+ return
64
+ end
65
+ pgid = @pgid_igv
66
+ Process.kill(:TERM, -pgid)
67
+ close
68
+ end
69
+
70
+ # Connect to IGV server
71
+
72
+ def connect(host2 = @host, port2 = @port, connect_timeout: nil)
73
+ @socket&.close
74
+ @socket = Socket.tcp(host2, port2, connect_timeout: connect_timeout)
75
+ end
76
+
77
+ # Close the socket.
78
+ # This method dose not exit IGV.
79
+
80
+ def close
26
81
  @socket&.close
27
- @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
28
- addr = Socket.sockaddr_in(port, host)
29
- @socket.connect(addr)
30
82
  end
31
83
 
32
- def go(position)
33
- send "goto #{position}"
84
+ def closed?
85
+ return true if @socket.nil?
86
+
87
+ @socket.closed?
88
+ end
89
+
90
+ # Send batch commands to IGV.
91
+ # @param [String] cmds
92
+
93
+ def send(*cmds)
94
+ cmd = \
95
+ cmds
96
+ .compact
97
+ .map do |cmd|
98
+ case cmd
99
+ when String, Symbol, Numeric then cmd.to_s
100
+ when ->(c) { c.respond_to?(:to_str) } then cmd.to_str
101
+ else raise ArgumentError, "#{cmd.inspect} is not a string"
102
+ end.strip.encode(Encoding::UTF_8)
103
+ end
104
+ .join(' ')
105
+ @history << cmd
106
+ @socket.puts(cmd)
107
+ @socket.gets&.chomp("\n")
108
+ end
109
+
110
+ # Show IGV batch commands in the browser.
111
+ # https://github.com/igvteam/igv/wiki/Batch-commands
112
+
113
+ def commands
114
+ require 'launchy'
115
+ Launchy.open('https://github.com/igvteam/igv/wiki/Batch-commands')
116
+ end
117
+
118
+ # Writes the value of "param" back to the response
119
+ #
120
+ # @param param [String] The parameter to echo.
121
+ # @return [String] The value of "param". If param is not specified, "echo".
122
+
123
+ def echo(param = nil)
124
+ send :echo, param
34
125
  end
35
- alias goto go
126
+
127
+ # Selects a genome by id, or loads a genome (or indexed fasta) from the supplied path.
128
+ #
129
+ # @param name_or_path [String] The genome to load
36
130
 
37
131
  def genome(name_or_path)
38
132
  path = File.expand_path(name_or_path)
39
133
  if File.exist?(path)
40
- send "genome #{path}"
134
+ send :genome, path
41
135
  else
42
- send "genome #{name_or_path}"
136
+ send :genome, name_or_path
43
137
  end
44
138
  end
45
139
 
46
- def load(path_or_url)
47
- if URI.parse(path_or_url).scheme
48
- send "load #{path_or_url}"
49
- else
50
- send "load #{File.expand_path(path_or_url)}"
51
- end
140
+ # Loads a data or session file by specifying a full path to a local file or a URL.
141
+ #
142
+ # @param path_or_url [String] The path to a local file or a URL
143
+ # @param index [String] The index of the file
144
+
145
+ def load(path_or_url, index: nil)
146
+ path_or_url = if URI.parse(path_or_url).scheme
147
+ path_or_url
148
+ else
149
+ File.expand_path(path_or_url)
150
+ end
151
+ index = "index=#{index}" if index
152
+ send :load, path_or_url, index
153
+ end
154
+
155
+ # Go to the specified location
156
+ #
157
+ # @param location [String] The location to go to.
158
+
159
+ def goto(position)
160
+ send :goto, position
52
161
  end
162
+ alias go goto
53
163
 
54
- def region(contig, start, end_)
55
- send ['region', contig, start, end_].join(' ')
164
+ # Defines a region of interest bounded by the two loci
165
+ #
166
+ # @param chr [String] The chromosome of the region
167
+ # @param start [Integer] The start position of the region
168
+ # @param end_ [Integer] The end position of the region
169
+
170
+ def region(chr, start, end_)
171
+ send :region, chr, start, end_
56
172
  end
57
173
 
58
174
  def sort(option = 'base')
59
- unless %w[base position strand quality sample readGroup].include? option
60
- raise 'options is one of: base, position, strand, quality, sample, and readGroup.'
61
- end
175
+ vop = %w[base position strand quality sample readGroup]
176
+ raise "options is one of: #{vop.join(', ')}" unless vop.include? option
177
+
178
+ send :sort, option
179
+ end
62
180
 
63
- send "sort #{option}"
181
+ # Expands the given track.
182
+ #
183
+ # @param track [String] The track to expand.
184
+ # If not specified, expands all tracks.
185
+
186
+ def expand(track = nil)
187
+ send :expand, track
188
+ end
189
+
190
+ # Collapses a given track.
191
+ #
192
+ # @param track [String] The track to collapse.
193
+ # If not specified, collapses all tracks.
194
+
195
+ def collapse(track = nil)
196
+ send :collapse, track
64
197
  end
65
198
 
66
- def expand(track = '')
67
- send "expand #{track}"
199
+ # Squish a given track.
200
+ #
201
+ # @param track [String] The track to squish.
202
+ # If not specified, squishes all tracks.
203
+
204
+ def squish(track = nil)
205
+ send :squish, track
68
206
  end
69
207
 
70
- def collapse(track = '')
71
- send "collapse #{track}"
208
+ # Set the display mode for an alignment track to "View as pairs".
209
+ #
210
+ # @param track [String] The track to set.
211
+ # If not specified, sets all tracks.
212
+
213
+ def viewaspairs(track = nil)
214
+ send :viewaspairs, track
72
215
  end
73
216
 
74
217
  def clear
75
- send 'clear'
218
+ send :clear
76
219
  end
77
220
 
221
+ # Exit (close) the IGV application.
222
+
78
223
  def exit
79
- send 'exit'
224
+ send :exit
225
+ @socket.close
80
226
  end
81
227
  alias quit exit
82
228
 
83
- def send(cmd)
84
- @history << cmd
85
- @socket.puts(cmd.encode(Encoding::UTF_8))
86
- @socket.gets&.chomp("\n")
87
- end
229
+ # Sets the directory in which to write images.
230
+ # Retruns the current snapshot directory if no argument is given.
231
+ #
232
+ # @param path [String] The path to the directory.
233
+
234
+ def snapshot_dir(dir_path = nil)
235
+ return @snapshot_dir if dir_path.nil?
236
+
237
+ dir_path = File.expand_path(dir_path)
238
+ return if dir_path == @snapshot_dir
88
239
 
89
- def snapshot_dir=(snapshot_dir)
90
- snapshot_dir = File.expand_path(snapshot_dir)
91
- return if snapshot_dir == @snaphot_dir
240
+ r = snapshot_dir_internal(dir_path)
241
+ @snapshot_dir = dir_path
242
+ r
243
+ end
92
244
 
93
- FileUtils.mkdir_p(snapshot_dir)
94
- send "snapshotDirectory #{snapshot_dir}"
95
- @snapshot_dir = snapshot_dir
245
+ private def snapshot_dir_internal(dir_path)
246
+ dir_path = File.expand_path(dir_path)
247
+ FileUtils.mkdir_p(dir_path)
248
+ send :snapshotDirectory, dir_path
96
249
  end
97
- alias set_snapshot_dir snapshot_dir=
98
250
 
99
- def save(file_path = nil)
100
- if file_path
101
- # igv assumes the path is just a single filename, but
102
- # we can set the snapshot dir. then just use the filename.
103
- dir_path = File.dirname(file_path)
104
- set_snapshot_dir(File.expand_path(dir_path)) if dir_path != '.'
105
- send "snapshot #{File.basename(file_path)}"
251
+ # Saves a snapshot of the IGV window to an image file.
252
+ # If filename is omitted, writes a PNG file with a filename generated based on the locus.
253
+ # If filename is specified, the filename extension determines the image file format,
254
+ # which must be either .png or .svg.
255
+ # @note In Ruby-IGV, it is possible to pass absolute or relative paths as well as file names;
256
+ # the Snapshot directory is set to Dir.pwd by default.
257
+ #
258
+ # @param file_path [String] The path to the image file.
259
+
260
+ def snapshot(file_path = nil)
261
+ return send(:snapshot) if file_path.nil?
262
+
263
+ dir_path = File.dirname(file_path)
264
+ filename = File.basename(file_path)
265
+ if dir_path != @snapshot_dir
266
+ snapshot_dir_internal(dir_path)
267
+ r = send :snapshot, filename
268
+ snapshot_dir_internal(@snapshot_dir)
269
+ r
106
270
  else
107
- send 'snapshot'
271
+ send :snapshot, filename
108
272
  end
109
273
  end
110
- alias snapshot save
274
+
275
+ # Temporarily set the preference named key to the specified value.
276
+ #
277
+ # @param key [String] The preference name
278
+ # @param value [String] The preference value
279
+
280
+ def preferences(key, value)
281
+ send :preferences, key, value
282
+ end
283
+
284
+ # Show "preference.tab" in your browser.
285
+
286
+ def show_preferences_table
287
+ require 'launchy'
288
+ Launchy.open('https://raw.githubusercontent.com/igvteam/igv/master/src/main/resources/org/broad/igv/prefs/preferences.tab')
289
+ end
290
+
291
+ # Save the current session.
292
+ # It is recommended that a full path be used for filename. IGV release 2.11.1
293
+ #
294
+ # @param filename [String] The path to the session file
295
+
296
+ def save_session(file_path)
297
+ file_path = File.expand_path(file_path)
298
+ send :saveSession, file_path
299
+ end
111
300
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-igv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - kojix2
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-02 00:00:00.000000000 Z
11
+ date: 2022-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: launchy
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement