shortcut 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ jruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shortcut.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', cli: '--color' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Décio Ferreira
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Shortcut
2
+
3
+ Make your own Bot.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'shortcut'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install shortcut
18
+
19
+ ## Usage
20
+
21
+ ### Ubuntu 12.04 with Unity-2D
22
+
23
+ Ubuntu has a bug related to how unity-2d draws itself on screen.
24
+ To fix this issue run the following command before you run shortcut:
25
+
26
+ $ gconftool --set --type bool /apps/metacity/general/compositing_manager false
27
+
28
+ And then log out/in to apply the changes. To restore the default configuration run:
29
+
30
+ $ gconftool --set --type bool /apps/metacity/general/compositing_manager true
31
+
32
+ And again log out/in to apply the changes.
33
+
34
+ See more information about this [bug](https://bugs.launchpad.net/unity-2d/+bug/1081674).
35
+
36
+ ## Development
37
+
38
+ $ ruby --ng-server &
39
+ $ ruby --ng -S rspec spec
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+ task test: :spec
Binary file
@@ -0,0 +1,4 @@
1
+ module Shortcut
2
+ class UndefinedActionError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ module Shortcut
2
+ class Point
3
+ attr_reader :x, :y
4
+
5
+ def initialize(x, y)
6
+ @x, @y = x, y
7
+ end
8
+
9
+ def to_s
10
+ "#{@x}, #{@y}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ require 'java'
2
+
3
+ import java.awt.Rectangle
4
+ import java.awt.Toolkit
5
+
6
+ import javax.imageio.ImageIO
7
+
8
+ module Shortcut
9
+ class Screen
10
+ def initialize(opts = {})
11
+ dim = Toolkit.get_default_toolkit.get_screen_size
12
+ opts = {:x => 0, :y => 0, :width => dim.get_width, :height => dim.get_height}.merge(opts)
13
+ rectangle = Rectangle.new(opts[:x], opts[:y], opts[:width], opts[:height])
14
+
15
+ @robot = Robot.new
16
+ @image = @robot.create_screen_capture(rectangle)
17
+ end
18
+
19
+ def save_image(pathname)
20
+ file = java::io::File.new(pathname)
21
+ ImageIO.write(@image, "PNG", file)
22
+ end
23
+
24
+ def get_color(x, y)
25
+ @image.getRGB(x, y)
26
+ end
27
+
28
+ def get_color_hex(x, y)
29
+ color = get_color(x, y)
30
+ sprintf("%02X%02X%02X", (color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF)
31
+ end
32
+
33
+ def find_template(template)
34
+ for x in 0...(@image.getWidth - template.getWidth)
35
+ for y in 0...(@image.getHeight - template.getHeight)
36
+ if loop_through_template(template, x, y)
37
+ return Point.new(x, y)
38
+ end
39
+ end
40
+ end
41
+
42
+ raise "Couldn't find the game board!\nIs the game on the screen?"
43
+ end
44
+
45
+ private
46
+
47
+ def loop_through_template(template, x, y)
48
+ for i in 0...template.getWidth
49
+ for j in 0...template.getHeight
50
+ return false if get_color(x+i, y+j) - template.getRGB(i, j) != 0
51
+ end
52
+ end
53
+
54
+ return true
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ module Shortcut
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,157 @@
1
+ require 'java'
2
+ require 'jar/JNativeHook.jar'
3
+
4
+ import java.awt.Color
5
+ import java.awt.event.ActionListener
6
+ import java.awt.event.InputEvent
7
+ import java.awt.event.WindowListener
8
+ import java.awt.Robot
9
+
10
+ import java.io.ByteArrayInputStream
11
+
12
+ import javax.swing.JButton
13
+ import javax.swing.JFrame
14
+ import javax.swing.JScrollPane
15
+ import javax.swing.JTextArea
16
+
17
+ import javax.imageio.ImageIO
18
+
19
+ import org.jnativehook.GlobalScreen
20
+ import org.jnativehook.keyboard.NativeKeyListener
21
+ import org.jnativehook.NativeHookException
22
+
23
+ # https://github.com/jruby/jruby/wiki/Persistence
24
+ Color.__persistent__ = true
25
+ GlobalScreen.__persistent__ = true
26
+ JScrollPane.__persistent__ = true
27
+ JTextArea.__persistent__ = true
28
+
29
+ module Shortcut
30
+ class Window < JFrame
31
+ include ActionListener
32
+ include NativeKeyListener
33
+ include WindowListener
34
+
35
+ def create_button(text, action, options = {})
36
+ options = {enabled: true}.merge(options)
37
+ JButton.new(text).tap do |btn|
38
+ btn.setActionCommand(action)
39
+ btn.addActionListener(self)
40
+ btn.setEnabled(options[:enabled])
41
+ end
42
+ end
43
+
44
+ def robot
45
+ @robot ||= Robot.new
46
+ end
47
+
48
+ def click(x, y)
49
+ robot.mouseMove(x, y)
50
+ robot.mousePress(InputEvent::BUTTON1_MASK)
51
+ robot.mouseRelease(InputEvent::BUTTON1_MASK)
52
+ end
53
+
54
+ def feedback_text_area(start_text = '')
55
+ @feedback_text_area ||= JTextArea.new.tap do |text_area|
56
+ text_area.setEditable(false)
57
+ text_area.setText(start_text)
58
+ text_area.setBackground(Color::WHITE)
59
+ text_area.setForeground(Color::BLACK)
60
+ end
61
+ end
62
+
63
+ def feedback_scroll_panel(text_area, dimension)
64
+ JScrollPane.new(text_area).tap do |scroll_panel|
65
+ scroll_panel.setPreferredSize(dimension)
66
+ end
67
+ end
68
+
69
+ def display_info(text)
70
+ feedback_text_area.append("\n#{text}")
71
+ feedback_text_area.setCaretPosition(feedback_text_area.getLineStartOffset(feedback_text_area.getLineCount() - 1))
72
+ end
73
+
74
+ def load_image(path)
75
+ buffer_array = ByteArrayInputStream.new(File.read(path).to_java_bytes)
76
+ image_input_stream = ImageIO.createImageInputStream(buffer_array)
77
+ ImageIO.read(image_input_stream)
78
+ end
79
+
80
+ def operative_system
81
+ java.lang.System.getProperty("os.name").downcase
82
+ end
83
+
84
+ def run
85
+ setTitle(respond_to?(:default_title) ? default_title : 'Shortcut App')
86
+
87
+ always_on_top if respond_to?(:always_on_top)
88
+ GlobalScreen.getInstance.addNativeKeyListener(self)
89
+ addWindowListener(self)
90
+ setVisible true
91
+ end
92
+
93
+ class << self
94
+ def title(text)
95
+ send :define_method, :default_title do
96
+ text
97
+ end
98
+ end
99
+
100
+ def always_on_top(bool_value)
101
+ send :define_method, :always_on_top do
102
+ setAlwaysOnTop(bool_value)
103
+ end
104
+ end
105
+
106
+ def key_pressed(code, action = nil, &block)
107
+ send :define_method, "key_#{code}_pressed".to_sym do
108
+ if action
109
+ method_name = "action_#{action}_handler".to_sym
110
+ raise UndefinedActionError.new("undefined action `#{action}'") unless respond_to?(method_name)
111
+ self.send(method_name)
112
+ else
113
+ self.instance_eval(&block)
114
+ end
115
+ end
116
+ end
117
+
118
+ def action(name, &block)
119
+ send :define_method, "action_#{name}_handler".to_sym do
120
+ self.instance_eval(&block)
121
+ end
122
+ end
123
+ end
124
+
125
+ [:windowClosing, :windowIconified,
126
+ :windowDeiconified, :windowActivated, :windowDeactivated].each do |name|
127
+ define_method(name) { |e| }
128
+ end
129
+
130
+ def windowOpened(e)
131
+ requestFocusInWindow
132
+ GlobalScreen.registerNativeHook
133
+ rescue NativeHookException => e
134
+ display_info(e.toString)
135
+ end
136
+
137
+ def windowClosed(e)
138
+ GlobalScreen.unregisterNativeHook
139
+ java.lang.System.runFinalization
140
+ java.lang.System.exit(0)
141
+ end
142
+
143
+ [:nativeKeyReleased, :nativeKeyTyped].each do |name|
144
+ define_method(name) { |e| }
145
+ end
146
+
147
+ def nativeKeyPressed(event)
148
+ method_name = "key_#{event.getKeyCode}_pressed".to_sym
149
+ send(method_name) if respond_to?(method_name)
150
+ end
151
+
152
+ def actionPerformed(event)
153
+ method_name = "action_#{event.getActionCommand}_handler".to_sym
154
+ send(method_name) if respond_to?(method_name)
155
+ end
156
+ end
157
+ end
data/lib/shortcut.rb ADDED
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + '/shortcut/*.rb'].each { |file| require file }
data/shortcut.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shortcut/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "shortcut"
8
+ spec.version = Shortcut::VERSION
9
+ spec.authors = ["Décio Ferreira"]
10
+ spec.email = ["decioferreira@decioferreira.com"]
11
+ spec.description = %q{Make your own Bot}
12
+ spec.summary = %q{Make your own Bot}
13
+ spec.homepage = "https://github.com/decioferreira/shortcut"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_development_dependency "warbler"
25
+ spec.add_development_dependency "guard-rspec"
26
+ end
@@ -0,0 +1,5 @@
1
+ require "spec_helper"
2
+ require "shortcut/point"
3
+
4
+ describe Shortcut::Point do
5
+ end
@@ -0,0 +1,395 @@
1
+ require "spec_helper"
2
+ require "shortcut/errors"
3
+ require "shortcut/window"
4
+ require "ostruct"
5
+
6
+ import java.awt.Dimension
7
+ import java.awt.event.KeyEvent
8
+
9
+ # https://github.com/jruby/jruby/wiki/Persistence
10
+ Dimension.__persistent__ = true
11
+
12
+ # Title property
13
+ class TitleClass < Shortcut::Window; title 'Test title'; end
14
+
15
+ # AlwaysOnTop property
16
+ class AlwaysOnTopTrueClass < Shortcut::Window; always_on_top true; end
17
+ class AlwaysOnTopFalseClass < Shortcut::Window; always_on_top false; end
18
+ class AlwaysOnTopNoneClass < Shortcut::Window; end
19
+
20
+ # NativeKeyListener
21
+ class ListenerExampleClass < Shortcut::Window
22
+ attr_accessor :called_actions, :key_pressed_events
23
+
24
+ action 'some_action' do
25
+ self.called_actions ||= []
26
+ self.called_actions << 'some_action'
27
+ end
28
+
29
+ action 'action_q_key' do
30
+ self.called_actions ||= []
31
+ self.called_actions << 'action_q_key'
32
+ end
33
+
34
+ key_pressed KeyEvent::VK_Q, :action_q_key
35
+ key_pressed KeyEvent::VK_W, :undefined_action
36
+
37
+ key_pressed KeyEvent::VK_ESCAPE do
38
+ self.key_pressed_events ||= []
39
+ self.key_pressed_events << KeyEvent::VK_ESCAPE
40
+ end
41
+ end
42
+
43
+ describe Shortcut::Window do
44
+ let(:window) { Shortcut::Window.new }
45
+
46
+ it 'inherits from JFrame' do
47
+ window.should be_a(JFrame)
48
+ end
49
+
50
+ it 'includes NativeKeyListener' do
51
+ window.should be_a(NativeKeyListener)
52
+ end
53
+
54
+ it 'includes WindowListener' do
55
+ window.should be_a(WindowListener)
56
+ end
57
+
58
+ describe :create_button do
59
+ let(:jbutton) { double('jbutton').as_null_object }
60
+
61
+ it 'returns an instance of JButton' do
62
+ window.create_button('text', 'action').should be_a(JButton)
63
+ end
64
+
65
+ it 'creates a button with text' do
66
+ JButton.should_receive(:new).with('text').and_return(jbutton)
67
+ window.create_button('text', anything())
68
+ end
69
+
70
+ it 'creates a button with action' do
71
+ JButton.should_receive(:new).and_return(jbutton)
72
+ jbutton.should_receive(:setActionCommand).with('action')
73
+ window.create_button(anything(), 'action')
74
+ end
75
+
76
+ it 'returns the created button' do
77
+ JButton.should_receive(:new).and_return(jbutton)
78
+ window.create_button(anything(), anything()).should eq(jbutton)
79
+ end
80
+
81
+ it 'enables the button by default' do
82
+ JButton.should_receive(:new).and_return(jbutton)
83
+ jbutton.should_receive(:setEnabled).with(true)
84
+ window.create_button(anything(), anything())
85
+ end
86
+
87
+ it 'accepts an enabled option to disable the button' do
88
+ JButton.should_receive(:new).and_return(jbutton)
89
+ jbutton.should_receive(:setEnabled).with(false)
90
+ window.create_button(anything(), anything(), {enabled: false})
91
+ end
92
+
93
+ it 'adds self as action listener' do
94
+ JButton.should_receive(:new).and_return(jbutton)
95
+ jbutton.should_receive(:addActionListener).with(window)
96
+ window.create_button(anything(), anything())
97
+ end
98
+ end
99
+
100
+ describe :robot do
101
+ let(:robot) { double('robot') }
102
+
103
+ it 'returns an instance of Robot' do
104
+ window.robot.should be_a(Robot)
105
+ end
106
+
107
+ it 'creates and returns a Robot' do
108
+ Robot.should_receive(:new).and_return(robot)
109
+ window.robot.should eq(robot)
110
+ end
111
+
112
+ it 'caches result' do
113
+ Robot.should_receive(:new).once.and_return(robot)
114
+ window.robot.should eq(robot)
115
+ window.robot.should eq(robot)
116
+ end
117
+ end
118
+
119
+ describe :click do
120
+ let(:robot) { double('robot') }
121
+
122
+ before(:each) do
123
+ window.stub(:robot) { robot }
124
+ end
125
+
126
+ it 'moves mouse to correct position, presses and releases left mouse button, all in the correct order' do
127
+ robot.should_receive(:mouseMove).with(1, 2).ordered
128
+ robot.should_receive(:mousePress).with(InputEvent::BUTTON1_MASK).ordered
129
+ robot.should_receive(:mouseRelease).with(InputEvent::BUTTON1_MASK).ordered
130
+ window.click(1, 2)
131
+ end
132
+ end
133
+
134
+ describe :feedback_text_area do
135
+ let(:text_area) { double('text area').as_null_object }
136
+
137
+ it 'returns an instance of JTextArea' do
138
+ window.feedback_text_area.should be_a(JTextArea)
139
+ end
140
+
141
+ it 'sets the text' do
142
+ JTextArea.should_receive(:new).and_return(text_area)
143
+ text_area.should_receive(:setText).with('Text')
144
+ window.feedback_text_area('Text')
145
+ end
146
+
147
+ it 'disables the returned area' do
148
+ JTextArea.should_receive(:new).and_return(text_area)
149
+ text_area.should_receive(:setEditable).with(false)
150
+ window.feedback_text_area
151
+ end
152
+
153
+ it 'sets the background white' do
154
+ JTextArea.should_receive(:new).and_return(text_area)
155
+ text_area.should_receive(:setBackground).with(Color::WHITE)
156
+ window.feedback_text_area
157
+ end
158
+
159
+ it 'sets the foreground black' do
160
+ JTextArea.should_receive(:new).and_return(text_area)
161
+ text_area.should_receive(:setForeground).with(Color::BLACK)
162
+ window.feedback_text_area
163
+ end
164
+
165
+ it 'caches result' do
166
+ JTextArea.should_receive(:new).once.and_return(text_area)
167
+ window.feedback_text_area.should eq(text_area)
168
+ window.feedback_text_area.should eq(text_area)
169
+ end
170
+ end
171
+
172
+ describe :feedback_scroll_panel do
173
+ let(:text_area) { JTextArea.new }
174
+ let(:dimension) { Dimension.new }
175
+
176
+ it 'returns an instance of JScrollPane' do
177
+ window.feedback_scroll_panel(text_area, dimension).should be_a(JScrollPane)
178
+ end
179
+
180
+ it 'creates the scroll panel with the text_area argument' do
181
+ scroll_panel = JScrollPane.new
182
+ JScrollPane.should_receive(:new).with(text_area).and_return(scroll_panel)
183
+ window.feedback_scroll_panel(text_area, dimension)
184
+ end
185
+
186
+ it 'sets preferred size' do
187
+ scroll_panel = JScrollPane.new
188
+ JScrollPane.should_receive(:new).with(text_area).and_return(scroll_panel)
189
+ scroll_panel.should_receive(:setPreferredSize).with(dimension)
190
+ window.feedback_scroll_panel(text_area, dimension)
191
+ end
192
+ end
193
+
194
+ describe :display_info do
195
+ it 'append message to feedback text area' do
196
+ window.display_info('A')
197
+ window.display_info('B')
198
+ window.feedback_text_area.getText.should eq("\nA\nB")
199
+ end
200
+
201
+ it 'scrolls down' do
202
+ window.display_info('A')
203
+ window.feedback_text_area.getCaretPosition.should eq(1)
204
+ window.display_info('B')
205
+ window.feedback_text_area.getCaretPosition.should eq(3)
206
+ window.display_info('C')
207
+ window.feedback_text_area.getCaretPosition.should eq(5)
208
+ end
209
+ end
210
+
211
+ describe :load_image do
212
+ let(:reader) { double('reader').as_null_object }
213
+ let(:java_bytes_reader) { double('java bytes reader') }
214
+ let(:input_stream) { double('input stream') }
215
+ let(:image_input_stream) { double('image input stream') }
216
+ let(:image) { double('image') }
217
+
218
+ it 'loads an image' do
219
+ File.should_receive(:read).with('path').and_return(reader)
220
+ reader.should_receive(:to_java_bytes).and_return(java_bytes_reader)
221
+ ByteArrayInputStream.should_receive(:new).with(java_bytes_reader).and_return(input_stream)
222
+ ImageIO.should_receive(:createImageInputStream).with(input_stream).and_return(image_input_stream)
223
+ ImageIO.should_receive(:read).with(image_input_stream).and_return(image)
224
+
225
+ window.load_image('path').should eq(image)
226
+ end
227
+ end
228
+
229
+ describe :operative_system do
230
+ it 'returns lowercased os.name property' do
231
+ java.lang.System.should_receive(:getProperty).with("os.name").and_return('Linux')
232
+ window.operative_system.should eq('linux')
233
+ end
234
+ end
235
+
236
+ describe :run do
237
+ before(:each) do
238
+ Shortcut::Window.any_instance.stub(:setVisible)
239
+ end
240
+
241
+ describe :title do
242
+ it 'sets the JFrame title' do
243
+ title = TitleClass.new
244
+ title.run
245
+ title.getTitle.should eq('Test title')
246
+ end
247
+
248
+ it 'sets a JFrame default title of "Shortcut App"' do
249
+ window.run
250
+ window.getTitle.should eq('Shortcut App')
251
+ end
252
+ end
253
+
254
+ describe 'always on top property' do
255
+ it 'sets property to true' do
256
+ on_top_true = AlwaysOnTopTrueClass.new
257
+ on_top_true.should_receive(:setAlwaysOnTop).with(true)
258
+ on_top_true.run
259
+ end
260
+
261
+ it 'sets property to false' do
262
+ on_top_false = AlwaysOnTopFalseClass.new
263
+ on_top_false.should_receive(:setAlwaysOnTop).with(false)
264
+ on_top_false.run
265
+ end
266
+
267
+ it 'does not set property' do
268
+ on_top_none = AlwaysOnTopNoneClass.new
269
+ on_top_none.should_not_receive(:setAlwaysOnTop)
270
+ on_top_none.run
271
+ end
272
+ end
273
+
274
+ it 'adds a window listener' do
275
+ window.should_receive(:addWindowListener).with(window)
276
+ window.run
277
+ end
278
+
279
+ it 'adds a native key listener' do
280
+ GlobalScreen.getInstance.should_receive(:addNativeKeyListener).with(window)
281
+ window.run
282
+ end
283
+
284
+ it 'sets visibility to true' do
285
+ window.should_receive(:setVisible).with(true)
286
+ window.run
287
+ end
288
+ end
289
+
290
+ describe 'window listeners' do
291
+ [:windowOpened, :windowClosed, :windowClosing, :windowIconified,
292
+ :windowDeiconified, :windowActivated, :windowDeactivated].each do |event|
293
+ it { window.should respond_to(event).with(1).argument }
294
+ end
295
+
296
+ describe :windowOpened do
297
+ it 'requests focus in window' do
298
+ window.should_receive(:requestFocusInWindow)
299
+ window.windowOpened(anything())
300
+ end
301
+
302
+ it 'registers native hook' do
303
+ GlobalScreen.should_receive(:registerNativeHook)
304
+ window.windowOpened(anything())
305
+ end
306
+
307
+ it 'handles exception when registering native hook' do
308
+ GlobalScreen.should_receive(:registerNativeHook).and_raise(NativeHookException.new('error message!'))
309
+ window.should_receive(:display_info).with('org.jnativehook.NativeHookException: error message!')
310
+ window.windowOpened(anything())
311
+ end
312
+ end
313
+
314
+ describe :windowClosed do
315
+ before(:each) do
316
+ java.lang.System.stub(:exit)
317
+ end
318
+
319
+ it 'unregisters native hook' do
320
+ GlobalScreen.should_receive(:unregisterNativeHook)
321
+ window.windowClosed(anything())
322
+ end
323
+
324
+ it 'runs system finalization' do
325
+ java.lang.System.should_receive(:runFinalization)
326
+ window.windowClosed(anything())
327
+ end
328
+
329
+ it 'exit system with value 0' do
330
+ java.lang.System.should_receive(:exit).with(0)
331
+ window.windowClosed(anything())
332
+ end
333
+ end
334
+ end
335
+
336
+ describe 'native key listeners' do
337
+ [:nativeKeyReleased, :nativeKeyTyped, :nativeKeyPressed].each do |event|
338
+ it { window.should respond_to(event).with(1).argument }
339
+ end
340
+
341
+ describe :nativeKeyPressed do
342
+ it 'executes block from key_pressed function' do
343
+ key_event = OpenStruct.new(getKeyCode: KeyEvent::VK_ESCAPE)
344
+
345
+ listener_example = ListenerExampleClass.new
346
+ listener_example.nativeKeyPressed(key_event)
347
+
348
+ listener_example.key_pressed_events.should eq([KeyEvent::VK_ESCAPE])
349
+ end
350
+
351
+ it 'does not raise exception when handler not defined' do
352
+ key_event = OpenStruct.new(getKeyCode: KeyEvent::VK_0)
353
+
354
+ listener_example = ListenerExampleClass.new
355
+ expect { listener_example.nativeKeyPressed(key_event) }.not_to raise_exception
356
+ end
357
+
358
+ it 'works with an action' do
359
+ key_event = OpenStruct.new(getKeyCode: KeyEvent::VK_Q)
360
+
361
+ listener_example = ListenerExampleClass.new
362
+ listener_example.nativeKeyPressed(key_event)
363
+
364
+ listener_example.called_actions.should eq(['action_q_key'])
365
+ end
366
+
367
+ it 'works with an action' do
368
+ key_event = OpenStruct.new(getKeyCode: KeyEvent::VK_W)
369
+
370
+ listener_example = ListenerExampleClass.new
371
+ expect { listener_example.nativeKeyPressed(key_event) }.to raise_exception(Shortcut::UndefinedActionError, "undefined action `undefined_action'")
372
+ end
373
+ end
374
+ end
375
+
376
+ describe :actionPerformed do
377
+ it { window.should respond_to(:actionPerformed).with(1).argument }
378
+
379
+ it 'executes block from action function' do
380
+ action_event = OpenStruct.new(getActionCommand: 'some_action')
381
+
382
+ listener_example = ListenerExampleClass.new
383
+ listener_example.actionPerformed(action_event)
384
+
385
+ listener_example.called_actions.should eq(['some_action'])
386
+ end
387
+
388
+ it 'does not raise exception when handler not defined' do
389
+ action_event = OpenStruct.new(getActionCommand: 'undefined_action')
390
+
391
+ listener_example = ListenerExampleClass.new
392
+ expect { listener_example.actionPerformed(action_event) }.not_to raise_exception
393
+ end
394
+ end
395
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shortcut
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Décio Ferreira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: !binary |-
37
+ MA==
38
+ none: false
39
+ requirement: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: !binary |-
44
+ MA==
45
+ none: false
46
+ prerelease: false
47
+ type: :development
48
+ - !ruby/object:Gem::Dependency
49
+ name: warbler
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: !binary |-
55
+ MA==
56
+ none: false
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: !binary |-
62
+ MA==
63
+ none: false
64
+ prerelease: false
65
+ type: :development
66
+ - !ruby/object:Gem::Dependency
67
+ name: guard-rspec
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: !binary |-
73
+ MA==
74
+ none: false
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: !binary |-
80
+ MA==
81
+ none: false
82
+ prerelease: false
83
+ type: :development
84
+ description: Make your own Bot
85
+ email:
86
+ - decioferreira@decioferreira.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".ruby-version"
94
+ - Gemfile
95
+ - Guardfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - lib/jar/JNativeHook.jar
100
+ - lib/shortcut.rb
101
+ - lib/shortcut/errors.rb
102
+ - lib/shortcut/point.rb
103
+ - lib/shortcut/screen.rb
104
+ - lib/shortcut/version.rb
105
+ - lib/shortcut/window.rb
106
+ - shortcut.gemspec
107
+ - spec/lib/shortcut_point_spec.rb
108
+ - spec/lib/shortcut_window_spec.rb
109
+ - spec/spec_helper.rb
110
+ homepage: https://github.com/decioferreira/shortcut
111
+ licenses:
112
+ - MIT
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ segments:
122
+ - 0
123
+ version: !binary |-
124
+ MA==
125
+ hash: 2
126
+ none: false
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ segments:
132
+ - 0
133
+ version: !binary |-
134
+ MA==
135
+ hash: 2
136
+ none: false
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.8.24
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: Make your own Bot
143
+ test_files:
144
+ - spec/lib/shortcut_point_spec.rb
145
+ - spec/lib/shortcut_window_spec.rb
146
+ - spec/spec_helper.rb