shortcut 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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