epitools 0.5.4 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/epitools.gemspec +5 -2
- data/lib/epitools/autoloads.rb +3 -1
- data/lib/epitools/core_ext/enumerable.rb +2 -0
- data/lib/epitools/core_ext/string.rb +9 -0
- data/lib/epitools/typed_struct.rb +67 -0
- data/lib/epitools/wm.rb +83 -0
- data/spec/core_ext_spec.rb +6 -0
- data/spec/typed_struct_spec.rb +16 -0
- metadata +5 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.5
|
data/epitools.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "epitools"
|
8
|
-
s.version = "0.5.
|
8
|
+
s.version = "0.5.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["epitron"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-05-01"
|
13
13
|
s.description = "Miscellaneous utility libraries to make my life easier."
|
14
14
|
s.email = "chris@ill-logic.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -60,6 +60,8 @@ Gem::Specification.new do |s|
|
|
60
60
|
"lib/epitools/sys.rb",
|
61
61
|
"lib/epitools/term.rb",
|
62
62
|
"lib/epitools/trie.rb",
|
63
|
+
"lib/epitools/typed_struct.rb",
|
64
|
+
"lib/epitools/wm.rb",
|
63
65
|
"lib/epitools/zopen.rb",
|
64
66
|
"spec/autoreq_spec.rb",
|
65
67
|
"spec/browser_spec.rb",
|
@@ -78,6 +80,7 @@ Gem::Specification.new do |s|
|
|
78
80
|
"spec/spec_helper.rb",
|
79
81
|
"spec/sys_spec.rb",
|
80
82
|
"spec/term_spec.rb",
|
83
|
+
"spec/typed_struct_spec.rb",
|
81
84
|
"spec/zopen_spec.rb"
|
82
85
|
]
|
83
86
|
s.homepage = "http://github.com/epitron/epitools"
|
data/lib/epitools/autoloads.rb
CHANGED
@@ -43,12 +43,14 @@ autoload :Ezdb, 'epitools/ezdb'
|
|
43
43
|
autoload :Browser, 'epitools/browser'
|
44
44
|
autoload :Rash, 'epitools/rash'
|
45
45
|
autoload :Ratio, 'epitools/ratio'
|
46
|
-
autoload :Sys, 'epitools/sys'
|
47
46
|
autoload :ProgressBar, 'epitools/progressbar'
|
48
47
|
autoload :Trie, 'epitools/trie'
|
49
48
|
autoload :MimeMagic, 'epitools/mimemagic'
|
50
49
|
autoload :Term, 'epitools/term'
|
51
50
|
autoload :Iter, 'epitools/iter'
|
51
|
+
autoload :WM, 'epitools/wm'
|
52
|
+
autoload :TypedStruct, 'epitools/typed_struct'
|
53
|
+
autoload :Sys, 'epitools/sys'
|
52
54
|
|
53
55
|
## Gems (common)
|
54
56
|
autoreq :Nokogiri, 'nokogiri'
|
@@ -257,6 +257,15 @@ class String
|
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
260
|
+
#
|
261
|
+
# Converts time duration strings (mm:ss, hh:mm:ss, or dd:hh:mm:ss) to seconds.
|
262
|
+
# (The reverse of Integer#to_hms)
|
263
|
+
#
|
264
|
+
def from_hms
|
265
|
+
nums = split(':').map(&:to_i)
|
266
|
+
nums_and_units = nums.reverse.zip %w[seconds minutes hours days]
|
267
|
+
nums_and_units.map { |num, units| num.send(units) }.sum
|
268
|
+
end
|
260
269
|
|
261
270
|
unless public_method_defined? :to_proc
|
262
271
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#
|
4
|
+
class TypedStruct < Struct
|
5
|
+
|
6
|
+
## TODO: Compact syntax: "a,b,c:int x:string y:date"
|
7
|
+
## TODO: Optional commas separating fields: "a, b, c:int, d:bool"
|
8
|
+
## TODO: booleans fields add "field?" methods
|
9
|
+
|
10
|
+
#
|
11
|
+
# A perhaps-too-clever table of { "typename" => convert_proc } mappings.
|
12
|
+
#
|
13
|
+
CONVERTERS = Hash[ *{
|
14
|
+
|
15
|
+
["str", "string"] => :passthru,
|
16
|
+
["int", "integer"] => proc { |me| me.to_i },
|
17
|
+
["hex"] => proc { |me| me.to_i(16) },
|
18
|
+
["bool", "boolean"] => proc { |me|
|
19
|
+
case me
|
20
|
+
when false, 0, "0", "off", "no", "false", nil
|
21
|
+
false
|
22
|
+
when true, 1, "1", "on", "yes", "true"
|
23
|
+
true
|
24
|
+
else
|
25
|
+
raise "Invalid boolean type: #{me.inspect}"
|
26
|
+
end
|
27
|
+
},
|
28
|
+
["date", "time", "datetime"] => proc { |me| DateTime.parse me },
|
29
|
+
["timestamp"] => proc { |me| Time.at me },
|
30
|
+
|
31
|
+
}.map { |names, converter| names.map { |n| [n, converter] } }.flatten ]
|
32
|
+
|
33
|
+
#
|
34
|
+
# Initialize a new struct.
|
35
|
+
#
|
36
|
+
def self.[](specs)
|
37
|
+
# create [name,type] pairs
|
38
|
+
pairs = specs.split.map do |spec|
|
39
|
+
name, type = spec.split(":")
|
40
|
+
|
41
|
+
type ||= "string"
|
42
|
+
unless converter = CONVERTERS[type]
|
43
|
+
raise "Unknown type: #{type}"
|
44
|
+
end
|
45
|
+
|
46
|
+
[name.to_sym, converter]
|
47
|
+
end
|
48
|
+
|
49
|
+
# initialize the C Struct
|
50
|
+
struct = new(*pairs.transpose.first)
|
51
|
+
|
52
|
+
# overload setter methods to call the proc
|
53
|
+
pairs.each do |field, converter|
|
54
|
+
next if converter == :passthru
|
55
|
+
struct.send(:define_method, "#{field}=") do |val|
|
56
|
+
self[field] = ( val and converter.call val )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
struct
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(*args)
|
64
|
+
members.zip(args).each { |field,value| send "#{field}=", value }
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/epitools/wm.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module WM
|
2
|
+
|
3
|
+
raise "Error: wmctrl not found." unless Path.which("wmctrl")
|
4
|
+
|
5
|
+
def self.windows; @windows ||= Window.all; end
|
6
|
+
def self.desktops; @desktops ||= Desktop.all; end
|
7
|
+
def self.processes; @processes ||= Hash[ Sys.ps.map { |process| [process.pid, process] } ] ; end
|
8
|
+
def self.current_desktop; @current = desktops.find { |d| d.current? }; end
|
9
|
+
|
10
|
+
class Desktop < TypedStruct["num:int current:bool resolution viewport desktop_geometry name"]
|
11
|
+
def self.all
|
12
|
+
# 0 - DG: 1680x1050 VP: N/A WA: 0,25 1680x974 Workspace 1
|
13
|
+
# 1 - DG: 1680x1050 VP: N/A WA: 0,25 1680x974 Workspace 2
|
14
|
+
# 2 * DG: 1680x1050 VP: 0,0 WA: 0,25 1680x974 Workspace 3
|
15
|
+
# 3 - DG: 1680x1050 VP: N/A WA: 0,25 1680x974 Workspace 4
|
16
|
+
# 4 - DG: 1680x1050 VP: N/A WA: 0,25 1680x974 Workspace 5
|
17
|
+
# 5 - DG: 1680x1050 VP: N/A WA: 0,25 1680x974 Workspace 6
|
18
|
+
# 0 1 2 3 4 5 6 7 8 9
|
19
|
+
`wmctrl -d`.lines.map(&:strip).map { |line| Desktop.from_line(line) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_line(line)
|
23
|
+
fields = line.split
|
24
|
+
fields[1] = (fields[1] == "*")
|
25
|
+
fields[5] = nil if fields[5] == "N/A"
|
26
|
+
|
27
|
+
name = fields[9..-1].join(" ")
|
28
|
+
|
29
|
+
new *(fields.values_at(0,1,3,5,8) + [name])
|
30
|
+
end
|
31
|
+
|
32
|
+
def current?
|
33
|
+
current
|
34
|
+
end
|
35
|
+
|
36
|
+
def windows
|
37
|
+
#binding.pry
|
38
|
+
@windows ||= WM.windows.select { |w| w.desktop_id == num }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
class Window < TypedStruct["addr desktop_id:int pid:int x:int y:int w:int h:int hostname title"]
|
44
|
+
|
45
|
+
def self.all
|
46
|
+
`wmctrl -lpG`.lines.map(&:strip).map { |line| Window.from_line(line) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.from_line(line)
|
50
|
+
# 0x01600031 -1 2562 0 0 1680 25 fizz Top Expanded Edge Panel
|
51
|
+
# 0x01600003 -1 2562 0 1998 1680 51 fizz Bottom Expanded Edge Panel
|
52
|
+
# 0x02c0001f 0 3012 849 173 783 667 fizz Terminal
|
53
|
+
# 0x048001f8 5 4080 311 186 1316 835 fizz Gorillaz - Highway (Under Construction)
|
54
|
+
# 0x02c28577 4 3012 66 461 1143 548 fizz Terminal
|
55
|
+
# 0x07c00003 0 14117 12 73 1298 948 fizz tr1984001_comp_soft.pdf
|
56
|
+
# 0x02d767d8 2 3012 520 470 1143 548 fizz Terminal
|
57
|
+
|
58
|
+
fields = line.split
|
59
|
+
title = fields[8..-1].join ' '
|
60
|
+
|
61
|
+
new *(fields[0..7] + [title])
|
62
|
+
end
|
63
|
+
|
64
|
+
def desktop
|
65
|
+
WM.desktops[desktop_id]
|
66
|
+
end
|
67
|
+
|
68
|
+
def sticky?
|
69
|
+
desktop == -1
|
70
|
+
end
|
71
|
+
|
72
|
+
alias_method :name, :title
|
73
|
+
|
74
|
+
def process
|
75
|
+
WM.processes[pid]
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect
|
79
|
+
"{ ::#{name}:: [#{desktop_id}]}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/spec/core_ext_spec.rb
CHANGED
@@ -613,3 +613,9 @@ describe "to_jsons and to_yamls" do
|
|
613
613
|
data.to_yaml.from_yaml.should == data
|
614
614
|
end
|
615
615
|
|
616
|
+
describe "to_hms and from_hms" do
|
617
|
+
60.to_hms.should == "01:00"
|
618
|
+
60.to_hms.from_hms.should == 60
|
619
|
+
"1:20:33".from_hms.to_hms.should == "01:20:33"
|
620
|
+
"5:01:20:33".from_hms.to_hms.should == "05:01:20:33"
|
621
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'epitools'
|
2
|
+
|
3
|
+
describe TypedStruct do
|
4
|
+
|
5
|
+
it "works" do
|
6
|
+
t = TypedStruct["a:int b c:boolean d:timestamp"].new
|
7
|
+
|
8
|
+
t.a.should == nil
|
9
|
+
t.a = "111"; t.a.should == 111
|
10
|
+
t.b = "111"; t.b.should == "111"
|
11
|
+
t.c = "yes"; t.c.should == true
|
12
|
+
#t.c?.should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epitools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -110,6 +110,8 @@ files:
|
|
110
110
|
- lib/epitools/sys.rb
|
111
111
|
- lib/epitools/term.rb
|
112
112
|
- lib/epitools/trie.rb
|
113
|
+
- lib/epitools/typed_struct.rb
|
114
|
+
- lib/epitools/wm.rb
|
113
115
|
- lib/epitools/zopen.rb
|
114
116
|
- spec/autoreq_spec.rb
|
115
117
|
- spec/browser_spec.rb
|
@@ -128,6 +130,7 @@ files:
|
|
128
130
|
- spec/spec_helper.rb
|
129
131
|
- spec/sys_spec.rb
|
130
132
|
- spec/term_spec.rb
|
133
|
+
- spec/typed_struct_spec.rb
|
131
134
|
- spec/zopen_spec.rb
|
132
135
|
homepage: http://github.com/epitron/epitools
|
133
136
|
licenses:
|