ruby-igv 0.0.4 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []