rmonitor 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +49 -40
  4. data/bin/rmonitor +38 -100
  5. data/lib/rmonitor.rb +17 -20
  6. data/lib/rmonitor/actions.rb +30 -0
  7. data/lib/rmonitor/actions/builder.rb +15 -0
  8. data/lib/rmonitor/actions/dsl.rb +23 -0
  9. data/lib/rmonitor/cache.rb +12 -0
  10. data/lib/rmonitor/capabilities.rb +38 -0
  11. data/lib/rmonitor/capabilities/builder.rb +15 -0
  12. data/lib/rmonitor/capabilities/dsl.rb +15 -0
  13. data/lib/rmonitor/commands/cl/base.rb +13 -0
  14. data/lib/rmonitor/commands/cl/invoke.rb +22 -0
  15. data/lib/rmonitor/commands/cl/list.rb +43 -0
  16. data/lib/rmonitor/commands/cl/update.rb +20 -0
  17. data/lib/rmonitor/commands/invoke.rb +23 -0
  18. data/lib/rmonitor/commands/list.rb +13 -0
  19. data/lib/rmonitor/commands/update.rb +18 -0
  20. data/lib/rmonitor/config.rb +27 -0
  21. data/lib/rmonitor/config/builder.rb +21 -0
  22. data/lib/rmonitor/config/dsl.rb +25 -0
  23. data/lib/rmonitor/invoker.rb +9 -0
  24. data/lib/rmonitor/matcher.rb +32 -0
  25. data/lib/rmonitor/profile/builder.rb +15 -0
  26. data/lib/rmonitor/profile/dsl.rb +15 -0
  27. data/lib/rmonitor/selector.rb +14 -0
  28. data/lib/rmonitor/strategies/base.rb +10 -0
  29. data/lib/rmonitor/strategies/optimistic.rb +15 -0
  30. data/lib/rmonitor/strategies/pessimistic.rb +17 -0
  31. data/lib/rmonitor/transformer.rb +38 -0
  32. data/lib/rmonitor/version.rb +1 -1
  33. data/lib/rmonitor/xrandr.rb +16 -0
  34. data/test/actions/builder_test.rb +27 -0
  35. data/test/actions_test.rb +60 -0
  36. data/test/cache_test.rb +30 -0
  37. data/test/capabilities/builder_test.rb +23 -0
  38. data/test/capabilities_test.rb +114 -0
  39. data/test/commands/cl/invoke_test.rb +38 -0
  40. data/test/commands/cl/list_test.rb +36 -0
  41. data/test/commands/cl/update_test.rb +24 -0
  42. data/test/commands/invoke_test.rb +86 -0
  43. data/test/commands/list_test.rb +50 -0
  44. data/test/commands/update_test.rb +67 -0
  45. data/test/config/builder_test.rb +51 -0
  46. data/test/config_test.rb +68 -0
  47. data/test/matcher_test.rb +90 -0
  48. data/test/profile/builder_test.rb +26 -0
  49. data/test/selector_test.rb +30 -0
  50. data/test/strategies/optimistic_test.rb +24 -0
  51. data/test/strategies/pessimistic_test.rb +26 -0
  52. data/test/test_helper.rb +19 -0
  53. data/test/transformer_test.rb +159 -0
  54. data/test/xrandr_test.rb +76 -0
  55. metadata +178 -28
  56. data/.rspec +0 -1
  57. data/Gemfile +0 -3
  58. data/Gemfile.lock +0 -24
  59. data/lib/rmonitor/devices.rb +0 -29
  60. data/lib/rmonitor/helpers/dsl_helpers.rb +0 -38
  61. data/lib/rmonitor/helpers/profile_helpers.rb +0 -48
  62. data/lib/rmonitor/helpers/xrandr_read_helpers.rb +0 -75
  63. data/lib/rmonitor/helpers/xrandr_write_helpers.rb +0 -55
  64. data/lib/rmonitor/profiles.rb +0 -51
  65. data/rmonitor.gemspec +0 -44
  66. data/spec/lib/helpers/dsl_helper_spec.rb +0 -29
  67. data/spec/lib/helpers/profile_helpers_spec.rb +0 -138
  68. data/spec/lib/helpers/xrandr_read_helpers_spec.rb +0 -259
  69. data/spec/lib/helpers/xrandr_write_helpers_spec.rb +0 -158
  70. data/spec/support/device_helper.rb +0 -3
  71. data/spec/support/profile_helper.rb +0 -3
  72. data/spec/support/string_helpers.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: efcffcebb400746bb0999ba82e922c65bf21d2e5
4
- data.tar.gz: 570c8a906f8a604302d2ef869988b8a9542fbd01
3
+ metadata.gz: 39988281a286deee26d615bb35fb4023b5bca9e0
4
+ data.tar.gz: 1b09ddd2c01ad30a7f31c75bcd2baf06624b1a90
5
5
  SHA512:
6
- metadata.gz: cb68858d52f4ee76132da86e9ea23cf3676bc9720fec28f4098c389eb26dc5914fdd014df856e61e566ad4e96a8bf7a925bc1e9d0041ab63c74acbadf388aaf4
7
- data.tar.gz: 8e0f383749c28649738cecaea38bc6a9ee5ffe241b18c39091cb1d655b3aa471235f80bb6b841d8da51ca7c6df7352cdb2ebec6cfd7b21a26478e387a641ab96
6
+ metadata.gz: 249da323f0ac72f0e52fee4f39cc55cb6d8702e7d098625362874a947dab993d71b2f299e85fbf15f732d6acc67543695de4868ab614e45f5623afeb225d2b68
7
+ data.tar.gz: a96ea8f0a0f87f286e882e1ea186b60600b9df3cfc3fee2bdf9a93004ff71f48373c48b5979d5d96ac8d8b88eedf00a1c0e8affa92e37d17804fcdc8f612600a
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2013 Jonas Amundsen
1
+ Copyright 2015 Jonas Amundsen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  ## RMonitor
2
2
 
3
3
  [![Build Status](https://travis-ci.org/badeball/rmonitor.png)](https://travis-ci.org/badeball/rmonitor)
4
+ [![Code Climate](https://codeclimate.com/github/badeball/rmonitor/badges/gpa.svg)](https://codeclimate.com/github/badeball/rmonitor)
5
+ [![Test Coverage](https://codeclimate.com/github/badeball/rmonitor/badges/coverage.svg)](https://codeclimate.com/github/badeball/rmonitor/coverage)
4
6
 
5
- RMonitor is a tool for creating monitor profiles that are easily invoked. This
6
- is useful when you often find yourself in situations with different monitor
7
- configurations. It consists of one executable and a ruby configuration file.
8
- The following example shows a configuration containing monitor profiles
9
- representing three different monitor setups.
7
+ RMonitor is a tool for defining monitor profiles that can be invoked. This is
8
+ useful when you often find yourself in situations with different monitor
9
+ configurations. The following example shows a configuration containing monitor
10
+ profiles representing three different setups.
10
11
 
11
12
  ```ruby
12
13
  profile "docked" do
@@ -24,66 +25,59 @@ profile "default" do
24
25
  end
25
26
  ```
26
27
 
27
- Usage:
28
+ A configuration is parsed and considered from top to bottom. The first profile
29
+ that is currently invokable (ie. the requested devices with modes and rates are
30
+ available) is preferred. Profiles can also be invoked by name. `xrandr` is used
31
+ behind the scenes to read devices and perform changes.
28
32
 
29
- ```
30
- Usage: rmonitor [option]
31
- -c, --create [NAME] Create and output a profile with an optional name
32
- -i, --invoke NAME Invoke a profile with a given name
33
- -u, --update Invoke the most preferable profile
34
- -v, --verbose Verbose output
35
- -d, --dry-run Do everything except actually update
36
- --config-path PATH Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb
37
- -h, --help Show this message
38
- --version Print the version number of rmonitor
39
- ```
40
-
41
- ### --create [NAME]
33
+ ## Installation
42
34
 
43
- Outputs a profile for your current setup.
35
+ The utility can be installed using `gem`.
44
36
 
45
37
  ```
46
- $ rmonitor --create "docked"
47
- profile "docked" do
48
- device "HDMI1", :mode => "1920x1080", :rate => "60.0", :pos => "0x0"
49
- device "HDMI2", :mode => "1920x1080", :rate => "60.0", :pos => "1920x0"
50
- end
38
+ $ gem install rmonitor
51
39
  ```
52
40
 
53
- ### --invoke NAME
41
+ It can be installed system-wide using the following options.
54
42
 
55
- Invokes a monitor profile by generating an xrandr query.
43
+ ```
44
+ $ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/local/bin rmonitor
45
+ ```
56
46
 
57
- ### --update
47
+ It is also packaged for Arch Linux.
58
48
 
59
- Parses the configuration file from top to bottom and invokes the first
60
- invokable profile based on what devices that are currently connected. This is
61
- likely the most used option and may for instance be used to automatically
62
- configure your screens upon startup.
49
+ ```
50
+ $ yaourt -S ruby-rmonitor
51
+ ```
63
52
 
64
- ## Installation
53
+ ## Usage
65
54
 
66
- The utility can be installed using `gem`, but is also packaged for Arch Linux.
55
+ The executable contains three sub-commands. `invoke` will invoke a monitor
56
+ profile by name.
67
57
 
68
58
  ```
69
- $ gem install rmonitor
59
+ $ rmonitor invoke --name "docked"
70
60
  ```
71
61
 
72
- It can be installed system-wide using the following options.
62
+ The `update` command will invoke the most preferred monitor profile. This
63
+ command can be mapped to XF86Display to achieve the functionality that one can
64
+ see in eg, windows or apple machines.
73
65
 
74
66
  ```
75
- $ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/bin rmonitor
67
+ $ rmonitor update
76
68
  ```
77
69
 
78
- ### Arch Linux
70
+ Lastly there is `list` which just list all defined monitor profiles.
79
71
 
80
72
  ```
81
- $ yaourt -Syua ruby-rmonitor
73
+ $ rmonitor list
82
74
  ```
83
75
 
84
76
  ## Configurable options
85
77
 
86
- The following examples shows how RMonitor can be configured.
78
+ The following examples shows how RMonitor can be configured. The default
79
+ configuration path is `~/.config/rmonitor/config.rb`, but can be configured
80
+ using the `--config-path` argument.
87
81
 
88
82
  ```ruby
89
83
  # Specify device location with position
@@ -136,6 +130,21 @@ end
136
130
 
137
131
  ## Changelog
138
132
 
133
+ ### 2.0.0
134
+
135
+ * Re-implementing the --config-path option.
136
+ * Re-implementing the :dpi profile option.
137
+
138
+ ### 2.0.0.rc2
139
+
140
+ * Relaxing the regular expression for matching device names. Previously it
141
+ would not match eg. `DP1-1`, but now it does.
142
+
143
+ ### 2.0.0.rc1
144
+
145
+ * Complete revamp of the codebase. Test coverage is wastly better. The code is
146
+ more maintainable and ready for change.
147
+
139
148
  ### 1.0.0
140
149
 
141
150
  * The -a option is removed.
@@ -1,112 +1,50 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'optparse'
4
- require 'rmonitor'
5
- require 'rmonitor/version'
6
-
7
- RMonitor.config_path = File.join(Dir.home, '.config', 'rmonitor', 'config.rb')
8
-
9
- class << self
10
- attr_accessor :options
3
+ require "commander/import"
4
+ require "rmonitor"
5
+ require "rmonitor/version"
6
+
7
+ program :name, "rmonitor"
8
+ program :version, RMonitor::VERSION
9
+ program :description, "A tool for creating monitor profiles that are easily invoked."
10
+
11
+ global_option "-c", "--config-path PATH", <<DESC.strip do |path|
12
+ Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb)
13
+ DESC
14
+ RMonitor::Config.config_path = path
11
15
  end
12
16
 
13
- self.options = { :action => :create }
14
-
15
- OptionParser.new do |opts|
16
- opts.banner = "Usage: rmonitor [option]"
17
-
18
- opts.on("-c", "--create [NAME]", String, "Create and output a profile with an optional name") do |name|
19
- options[:action] = :create
20
- options[:name] = name
21
- end
22
-
23
- opts.on("-i", "--invoke NAME", String, "Invoke a profile with a given name") do |name|
24
- options[:action] = :invoke
25
- options[:name] = name
26
- end
27
-
28
- opts.on("-u", "--update", "Invoke the most preferable profile") do
29
- options[:action] = :update
30
- end
31
-
32
- opts.on("-v", "--verbose", "Verbose output") do
33
- options[:verbose] = true
34
- end
35
-
36
- opts.on("-d", "--dry-run", "Do everything except actually update (implies -v)") do
37
- options[:dry_run] = true
38
- options[:verbose] = true
39
- end
40
-
41
- opts.on("--config-path PATH", String, "Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb") do |config_path|
42
- RMonitor.config_path = config_path
17
+ command :invoke do |c|
18
+ c.syntax = "invoke [options]"
19
+ c.description = "invoke a profile by name"
20
+ c.option "-n", "--name NAME", String
21
+ c.option "--dry-run", "don't run any destructive commands"
22
+ c.option "--verbose", "print every command ran"
23
+ c.action do |_, options|
24
+ RMonitor::Commands::CL::Invoke.new(
25
+ :verbose => options.verbose,
26
+ :dry_run => options.dry_run
27
+ ).execute(options.name)
43
28
  end
44
-
45
- opts.on_tail("-h", "--help", "Show this message") do
46
- puts opts
47
- exit
48
- end
49
-
50
- opts.on_tail("--version", "Print the version number of rmonitor") do
51
- puts "rmonitor #{RMonitor::VERSION}"
52
- exit
53
- end
54
- end.parse!
55
-
56
- def v_puts(content)
57
- if options[:verbose]
58
- puts content
59
- end
60
- end
61
-
62
- def exit_with(content)
63
- $stderr.puts content
64
- exit! 1
65
29
  end
66
30
 
67
- rm = RMonitor.load
68
-
69
- if options[:action] == :update
70
- # Find the first invokable profile
71
- profile = rm.profiles.find do |profile|
72
- RMonitor::Profiles.invokable?(rm.devices, profile)
73
- end
74
-
75
- if profile
76
- v_puts "Found #{profile[:name].inspect} that is invokable."
77
- options[:name] = profile[:name]
78
- options[:action] = :invoke
79
- else
80
- exit_with 'no invokable profile exists'
31
+ command :list do |c|
32
+ c.syntax = "list"
33
+ c.description = "lists all available profiles"
34
+ c.action do
35
+ RMonitor::Commands::CL::List.new.execute
81
36
  end
82
37
  end
83
38
 
84
- if options[:action] == :invoke
85
- profile = rm.profiles.find { |p| p[:name] == options[:name] }
86
-
87
- if profile
88
- if RMonitor::Profiles.invokable?(rm.devices, profile)
89
- command = RMonitor::Profiles.to_xrandr(rm.devices, profile)
90
- v_puts "Invoking #{profile[:name].inspect} by running #{command.inspect}."
91
- exec(command) unless options[:dry_run]
92
- else
93
- exit_with 'this profile is not invokable'
94
- end
95
- else
96
- exit_with 'no profile with that name exists'
97
- end
98
-
99
- elsif options[:action] == :create
100
- puts "profile #{(options[:name] || 'My profile').inspect} do"
101
- rm.devices.each do |device|
102
- if device[:enabled]
103
- puts ' device %s, :mode => %s, :rate => %s, :pos => %s' % [
104
- device[:name].inspect,
105
- device[:configuration][:mode].inspect,
106
- device[:configuration][:rate].inspect,
107
- device[:pos].inspect,
108
- ]
109
- end
39
+ command :update do |c|
40
+ c.syntax = "update"
41
+ c.description = "invokes the most preferred profile"
42
+ c.option "--dry-run", "don't run any destructive commands"
43
+ c.option "--verbose", "print every command ran"
44
+ c.action do |_, options|
45
+ RMonitor::Commands::CL::Update.new(
46
+ :verbose => options.verbose,
47
+ :dry_run => options.dry_run
48
+ ).execute
110
49
  end
111
- puts 'end'
112
50
  end
@@ -1,23 +1,20 @@
1
- require 'fileutils'
2
-
3
- require 'rmonitor/devices'
4
- require 'rmonitor/profiles'
1
+ require "rmonitor/commands/cl/invoke"
2
+ require "rmonitor/commands/cl/list"
3
+ require "rmonitor/commands/cl/update"
4
+ require "rmonitor/strategies/optimistic"
5
+ require "rmonitor/strategies/pessimistic"
6
+ require "rmonitor/actions"
7
+ require "rmonitor/cache"
8
+ require "rmonitor/capabilities"
9
+ require "rmonitor/config"
10
+ require "rmonitor/invoker"
11
+ require "rmonitor/matcher"
12
+ require "rmonitor/selector"
13
+ require "rmonitor/transformer"
14
+ require "rmonitor/xrandr"
5
15
 
6
16
  class RMonitor
7
- class XRandRArgumentError < ArgumentError; end
8
-
9
- class << self
10
- attr_accessor :config_path
11
- end
12
-
13
- attr_accessor :devices, :profiles
14
-
15
- def initialize(raw_devices_data, raw_profiles_data)
16
- @devices = Devices.parse(raw_devices_data)
17
- @profiles = Profiles.parse(raw_profiles_data)
18
- end
19
-
20
- def self.load
21
- self.new(`xrandr -q`, File.new(config_path).read)
22
- end
17
+ class UnknownProfileError < StandardError; end
18
+ class UninvokableProfileError < StandardError; end
19
+ class NoInvokableProfileError < StandardError; end
23
20
  end
@@ -0,0 +1,30 @@
1
+ require "rmonitor/actions/builder"
2
+
3
+ class RMonitor
4
+ class Actions
5
+ def initialize(options = {})
6
+ @capabilities = options[:capabilities] || Capabilities.new(options).parse
7
+ end
8
+
9
+ def create(profile)
10
+ actions = []
11
+
12
+ @avaialble_devices = @capabilities.map { |capability| capability[:name] }.uniq
13
+ @desired_devices = profile[:devices].map { |device| device[:name] }
14
+
15
+ (@avaialble_devices - @desired_devices).each do |device|
16
+ actions << {:action => :off, :name => device}
17
+ end
18
+
19
+ if profile[:dpi]
20
+ actions << {:action => :option, :name => :dpi, :value => profile[:dpi]}
21
+ end
22
+
23
+ profile[:devices].each do |device|
24
+ actions << device.merge(:action => :on)
25
+ end
26
+
27
+ actions
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require "rmonitor/actions/dsl"
2
+
3
+ class RMonitor
4
+ class Actions
5
+ class Builder
6
+ class << self
7
+ def define(&block)
8
+ dsl = RMonitor::Actions::DSL.new
9
+ dsl.instance_eval(&block)
10
+ dsl.actions
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ class RMonitor
2
+ class Actions
3
+ class DSL
4
+ attr_accessor :actions
5
+
6
+ def initialize
7
+ @actions = []
8
+ end
9
+
10
+ def off(name)
11
+ @actions << {:action => :off, :name => name}
12
+ end
13
+
14
+ def on(name, options = {})
15
+ @actions << options.merge(:action => :on, :name => name)
16
+ end
17
+
18
+ def dpi(value)
19
+ @actions << {:action => :option, :name => :dpi, :value => value}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ class RMonitor
2
+ class Cache
3
+ def initialize(options = {})
4
+ @output = {}
5
+ @invoker = options[:invoker] || Invoker.new(options)
6
+ end
7
+
8
+ def invoke(command)
9
+ @output[command] ||= @invoker.invoke(command)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ require "rmonitor/capabilities/builder"
2
+
3
+ class RMonitor
4
+ class Capabilities
5
+ def initialize(options = {})
6
+ @xrandr = options[:xrandr] || XRandR.new(options)
7
+ end
8
+
9
+ def parse(input = @xrandr.invoke)
10
+ capabilities = []
11
+ current_name = nil
12
+
13
+ input = input.split("\n").tap(&:shift).map(&:strip)
14
+
15
+ input.each do |line|
16
+ if /(?<name>[\w\d-]+) (dis)?connected/ =~ line
17
+ current_name = name
18
+ end
19
+
20
+ if /(?<mode>\d+x\d+)/ =~ line
21
+ line.scan(/\s(\d+(?:\.\d+)?)(?:( |\*)( |\+)?)?/) do |rate, native, current|
22
+ capabilities << {:name => current_name, :mode => mode, :rate => rate}
23
+
24
+ if native == "*"
25
+ capabilities.last[:native] = true
26
+ end
27
+
28
+ if current == "+"
29
+ capabilities.last[:current] = true
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ capabilities
36
+ end
37
+ end
38
+ end