ruby-igv 0.0.4 → 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 +75 -13
  3. data/lib/igv/version.rb +3 -1
  4. data/lib/igv.rb +248 -54
  5. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8915f9a26a6b4b4bbe15dc5dd35d156ca9c84a5b2737c4b81ffa1441b5049e2d
4
- data.tar.gz: 44980727b24b348a3167566179e9a75755cda0023761624de1fb72fdfe3701cd
3
+ metadata.gz: 1ca8294136b5eab8a344a40bf1d5ebb6d8ebf08f2794a1c6e1f582f593432049
4
+ data.tar.gz: c36d4c09a163ade8bfd860519a50519a28ae3f93121a14c482bf1bceb6239cde
5
5
  SHA512:
6
- metadata.gz: 232bc8a829604e65fb67e7b39aa0480e3c0165e1291022c1162469ed5d126caefe0881b124e70fb4c386a51937e1530d812fb5ec459d8067fa9264e78b368202
7
- data.tar.gz: c57afdcf2dcbf5d9829fd3815e4fdc91148a1757906027f663690598438ec3e284b54ff75b07f8ab8c699be93b7674192b78821953cc0846cc333a48bc4572f8
6
+ metadata.gz: 1a085655336dcd4d06db5b51269f6b72d8d7174767f0b2990796d29cd410fb90adc884b86df3cf97087e75b35d387f655ed557628145c3af9badf17d3b29c50e
7
+ data.tar.gz: d8e0741301b1504f7714077ef435a987b51c520fcc3788f85acaf42ad3785fcec7bf435e897666a5f9a16050d2f503cfab5daa68525a1fc86a18b5219018a6ed
data/README.md CHANGED
@@ -1,34 +1,86 @@
1
- # Ruby-IGV
1
+ # ruby-igv
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/ruby-igv.svg)](https://badge.fury.io/rb/ruby-igv)
4
4
  [![Docs Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://rubydoc.info/gems/ruby-igv)
5
5
  [![The MIT License](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE.txt)
6
+ [![DOI](https://zenodo.org/badge/281373245.svg)](https://zenodo.org/badge/latestdoi/281373245)
6
7
 
7
- Using [Integrative Genomics Viewer (IGV)](http://software.broadinstitute.org/software/igv/) with the Ruby language.
8
8
 
9
- Based on [brentp/bio-playground/igv](https://github.com/brentp/bio-playground).
9
+ <img src="https://user-images.githubusercontent.com/5798442/182540876-c3ca2906-7d05-4c93-9107-ce4135ae9765.png" align="right">
10
10
 
11
11
  ## Installation
12
12
 
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 ☑
19
+
13
20
  ```ruby
14
21
  gem install ruby-igv
15
22
  ```
16
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
+
17
38
  ## Usage
18
39
 
40
+ ### docs
41
+
42
+ See [the list of Batch commands](https://github.com/igvteam/igv/wiki/Batch-commands).
43
+
19
44
  ```ruby
20
- igv = IGV.new
21
- igv.genome 'hg19'
22
- igv.load 'http://hgdownload.cse.ucsc.edu/goldenPath/hg19/encodeDCC/wgEncodeUwRepliSeq/wgEncodeUwRepliSeqK562G1AlnRep1.bam'
23
- igv.go 'chr18:78,016,233-78,016,640'
24
- igv.save '/tmp/r/region.svg'
25
- igv.save '/tmp/r/region.png'
26
- igv.snapshot_dir = '/tmp/r2/'
27
- igv.save 'region.jpg' # save to /tmp/r2/region.png
28
- igv.send 'echo' # whatever you want
45
+ igv.commands # Show the IGV command reference in your browser
29
46
  ```
30
47
 
31
- * [Controlling IGV through a Port](https://software.broadinstitute.org/software/igv/PortCommands)
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
+ ```
32
84
 
33
85
  ## Contributing
34
86
 
@@ -37,6 +89,16 @@ igv.send 'echo' # whatever you want
37
89
  * Write, clarify, or fix documentation
38
90
  * Suggest or add new features
39
91
 
92
+ ```
93
+ Do you need commit rights to my repository?
94
+ Do you want to get admin rights and take over the project?
95
+ If so, please feel free to contact me @kojix2.
96
+ ```
97
+
98
+ ## Acknowledgement
99
+ This gem is strongly inspired by a Python script developed by Brent Pedersen.
100
+ * [brentp/bio-playground/igv](https://github.com/brentp/bio-playground).
101
+
40
102
  ## License
41
103
 
42
104
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/igv/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class IGV
2
- VERSION = '0.0.4'.freeze
4
+ VERSION = '0.0.7'
3
5
  end
data/lib/igv.rb CHANGED
@@ -1,106 +1,300 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'igv/version'
2
4
  require 'socket'
3
5
  require 'fileutils'
4
6
 
7
+ # The Integrative Genomics Viewer (IGV)
8
+ # https://software.broadinstitute.org/software/igv/
5
9
  class IGV
6
10
  class Error < StandardError; end
7
11
 
8
- attr_reader :host, :port, :snapshot_dir, :history
12
+ attr_reader :host, :port, :history
13
+
9
14
  def initialize(host: '127.0.0.1', port: 60_151, snapshot_dir: Dir.pwd)
10
15
  @host = host
11
16
  @port = port
17
+ @snapshot_dir = File.expand_path(snapshot_dir)
12
18
  @history = []
13
- connect
14
- set_snapshot_dir(snapshot_dir)
15
19
  end
16
20
 
17
- # def self.start
18
- # 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?
25
+
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.
19
79
 
20
- def connect
80
+ def close
21
81
  @socket&.close
22
- @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
23
- addr = Socket.sockaddr_in(port, host)
24
- @socket.connect(addr)
25
82
  end
26
83
 
27
- def go(position)
28
- send 'goto ' + position
84
+ def closed?
85
+ return true if @socket.nil?
86
+
87
+ @socket.closed?
29
88
  end
30
- alias goto go
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
125
+ end
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
31
130
 
32
131
  def genome(name_or_path)
33
132
  path = File.expand_path(name_or_path)
34
133
  if File.exist?(path)
35
- send 'genome ' + path
134
+ send :genome, path
36
135
  else
37
- send 'genome ' + name_or_path
136
+ send :genome, name_or_path
38
137
  end
39
138
  end
40
139
 
41
- def load(path_or_url)
42
- if URI.parse(path_or_url).scheme
43
- send 'load ' + path_or_url
44
- else
45
- send 'load ' + File.expand_path(path_or_url)
46
- 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
47
153
  end
48
154
 
49
- def region(contig, start, end_)
50
- send ['region', contig, start, end_].join(' ')
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
161
+ end
162
+ alias go goto
163
+
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_
51
172
  end
52
173
 
53
174
  def sort(option = 'base')
54
- if %w[base position strand quality sample readGroup].include? option
55
- send 'sort ' + option
56
- else
57
- raise 'options is one of: base, position, strand, quality, sample, and readGroup.'
58
- 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
59
179
  end
60
180
 
61
- def expand(_track = '')
62
- send "expand #{track}"
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
63
188
  end
64
189
 
65
- def collapse(_track = '')
66
- send "collapse #{track}"
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
197
+ end
198
+
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
206
+ end
207
+
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
67
215
  end
68
216
 
69
217
  def clear
70
- send 'clear'
218
+ send :clear
71
219
  end
72
220
 
221
+ # Exit (close) the IGV application.
222
+
73
223
  def exit
74
- send 'exit'
224
+ send :exit
225
+ @socket.close
75
226
  end
76
227
  alias quit exit
77
228
 
78
- def send(cmd)
79
- @history << cmd
80
- @socket.puts(cmd.encode(Encoding::UTF_8))
81
- @socket.gets&.chomp("\n")
82
- 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.
83
233
 
84
- def snapshot_dir=(snapshot_dir)
85
- snapshot_dir = File.expand_path(snapshot_dir)
86
- return if snapshot_dir == @snaphot_dir
234
+ def snapshot_dir(dir_path = nil)
235
+ return @snapshot_dir if dir_path.nil?
87
236
 
88
- FileUtils.mkdir_p(snapshot_dir)
89
- send "snapshotDirectory #{snapshot_dir}"
90
- @snapshot_dir = snapshot_dir
237
+ dir_path = File.expand_path(dir_path)
238
+ return if dir_path == @snapshot_dir
239
+
240
+ r = snapshot_dir_internal(dir_path)
241
+ @snapshot_dir = dir_path
242
+ r
91
243
  end
92
- alias set_snapshot_dir snapshot_dir=
93
244
 
94
- def save(file_path = nil)
95
- if file_path
96
- # igv assumes the path is just a single filename, but
97
- # we can set the snapshot dir. then just use the filename.
98
- dir_path = File.dirname(file_path)
99
- set_snapshot_dir(File.expand_path(dir_path)) if dir_path != '.'
100
- send 'snapshot ' + File.basename(file_path)
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
249
+ end
250
+
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
101
270
  else
102
- send 'snapshot'
271
+ send :snapshot, filename
103
272
  end
104
273
  end
105
- 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
106
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.4
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: 2020-08-23 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
@@ -66,7 +80,7 @@ dependencies:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
- description: Operate IGV (Integrative Genomics Viewer) from Ruby.
83
+ description: Control IGV (Integrative Genomics Viewer) with Ruby.
70
84
  email:
71
85
  - 2xijok@gmail.com
72
86
  executables: []
@@ -89,15 +103,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
103
  requirements:
90
104
  - - ">="
91
105
  - !ruby/object:Gem::Version
92
- version: 2.3.0
106
+ version: '2.4'
93
107
  required_rubygems_version: !ruby/object:Gem::Requirement
94
108
  requirements:
95
109
  - - ">="
96
110
  - !ruby/object:Gem::Version
97
111
  version: '0'
98
112
  requirements: []
99
- rubygems_version: 3.1.2
113
+ rubygems_version: 3.3.7
100
114
  signing_key:
101
115
  specification_version: 4
102
- summary: Operate IGV (Integrative Genomics Viewer) from Ruby.
116
+ summary: Control IGV (Integrative Genomics Viewer) with Ruby.
103
117
  test_files: []