keyboard_battle 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keyboard_battle.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ keyboard_battle (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ rspec (2.12.0)
11
+ rspec-core (~> 2.12.0)
12
+ rspec-expectations (~> 2.12.0)
13
+ rspec-mocks (~> 2.12.0)
14
+ rspec-core (2.12.1)
15
+ rspec-expectations (2.12.0)
16
+ diff-lcs (~> 1.1.3)
17
+ rspec-mocks (2.12.0)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ keyboard_battle!
24
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Ben Cullen-Kerney
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,29 @@
1
+ # KeyboardBattle
2
+
3
+ KeyboardBattle is a simple program that compares the performance of keyboard layouts according to two metrics, reach effort (travel from the home row being increasingly effortful) and alternation effort (typing consecutive keys with a single hand being more effortful). For both, a lower value means less effort.
4
+
5
+ The program comes with the QWERTY, Dvorak, and Colemak layouts. The format for a keyboard layout description file can be discerned from `lib/keyboards/*.txt`.
6
+
7
+ ## Installation
8
+
9
+ $ gem install keyboard_battle
10
+
11
+ ## Usage
12
+
13
+ E.g., `keyboard_battle my_text.txt`, or try `keyboard_battle --bundled`
14
+
15
+ ## Limitations and shortcomings
16
+
17
+ Only ASCII characters will be counted (though any character may appear in the source text).
18
+
19
+ ## Bundled texts
20
+
21
+ Except for "the quick brown fox," bundled texts are sourced from archive.org.
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'keyboard_battle'
4
+
5
+ KeyboardBattle::CommandDispatcher.new(ARGF.argv)
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/keyboard_battle/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Ben Cullen-Kerney"]
6
+ gem.email = ["ben@beancuke.com"]
7
+ gem.description = %q{Keyboard Battle compares the performance of keyboard layouts according to reach effort and alternation effort.}
8
+ gem.summary =<<SUMMARY
9
+ KeyboardBattle is a simple program that compares the performance of keyboard layouts according to two metrics, reach effort (travel from the home row being increasingly effortful) and alternation effort (typing consecutive keys with a single hand being more effortful). For both, a lower value means less effort.
10
+
11
+ The program comes with the QWERTY, Dvorak, and Colemak layouts. The format for a keyboard layout description file can be discerned from lib/keyboards/*.txt.
12
+ SUMMARY
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($\)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.name = "keyboard_battle"
19
+ gem.require_paths = ["lib"]
20
+ gem.version = KeyboardBattle::VERSION
21
+
22
+ gem.add_development_dependency "rspec"
23
+ end
@@ -0,0 +1,45 @@
1
+ class KeyboardBattle::CommandDispatcher
2
+
3
+ def initialize(args)
4
+ if opt = args.first
5
+ case opt
6
+ when '--bundled'
7
+ self.run(Dir.glob('texts/*.txt'))
8
+ else
9
+ self.run(args)
10
+ end
11
+ else
12
+ help
13
+ end
14
+ end
15
+
16
+ def run(filenames)
17
+ filenames.each do |filename|
18
+ exercise = KeyboardBattle::Exercise.new(filename, keyboards)
19
+ print report(exercise)
20
+ end
21
+ end
22
+
23
+ def help
24
+ print <<HELP
25
+
26
+ What text(s) would you like to battle upon?
27
+ You can specify file(s) to exercise, e.g.:
28
+
29
+ $ keyboard_battle mytext1.txt mytext2.txt
30
+
31
+ Or, use --bundled to try it out with a few bundled texts:
32
+
33
+ $ keyboard_battle --bundled
34
+
35
+ HELP
36
+ end
37
+
38
+ def report(exercise)
39
+ KeyboardBattle::Report.new(exercise.filename, exercise.results).to_s
40
+ end
41
+
42
+ def keyboards
43
+ KeyboardBattle::Keyboard.all
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ class KeyboardBattle::Exercise
2
+
3
+ attr_accessor :results, :filename
4
+
5
+ def initialize(filename, keyboards)
6
+ @filename = filename
7
+ @keyboards = keyboards
8
+ @results = run
9
+ end
10
+
11
+ private
12
+
13
+ attr_accessor :keyboards
14
+
15
+ # tests a passage of text for reach effort expended (zero for home row,
16
+ # increasing for reach), and hand alternation effort. In both values,
17
+ # lower is better.
18
+ def run
19
+ results = { }
20
+ keyboards.each do |keyboard|
21
+ # set up container vars
22
+ prev_hand = nil
23
+ alternation_effort = 0
24
+ reach_effort = 0
25
+ open_and_process(filename,'r') do |file|
26
+ while line = file.gets
27
+ line.each_char do |char|
28
+ if keyboard.combined.include?(char)
29
+
30
+ # measure alternation efficiency
31
+ hand = keyboard.left.include?(char) ? 'l' : 'r'
32
+ if prev_hand
33
+ alternation_effort += (hand == prev_hand) ? 1 : 0
34
+ end
35
+ prev_hand = hand
36
+
37
+ # measure reach efficiency
38
+ reach_effort += EFFORT[keyboard.combined.find_index(char)]
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ results[keyboard.name.to_sym] = {
45
+ :alternation_effort => alternation_effort,
46
+ :reach_effort => reach_effort,
47
+ :raw_score => (alternation_effort + reach_effort)
48
+ }
49
+ end
50
+ results
51
+ end
52
+
53
+ def open_and_process(*args)
54
+ f = File.open(*args)
55
+ yield f
56
+ f.close()
57
+ end
58
+
59
+ EFFORT = ( # left hand + right hand effort values
60
+ %w(3 2 2 2 2 2 2 1 1 1 1 1 0 0 0 0 1 1 1 1 1 2 3 2 2 2 2 2 2 1 1 1 1 1 0 0 0 0 1 1 1 1 1 2) +
61
+ %w(2 2 2 2 2 2 1 1 1 1 1 1 2 3 1 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 2 3 1 0 0 0 0 1 1 1 1 1 1)
62
+ ).collect { |el| el.to_i }
63
+
64
+ end
@@ -0,0 +1,58 @@
1
+ class KeyboardBattle::Keyboard
2
+
3
+ class Qwerty < self
4
+ def name
5
+ "qwerty"
6
+ end
7
+
8
+ def left
9
+ %w(` 1 2 3 4 5 6 q w e r t a s d f g z x c v b ~ ! @ # $ % ^ Q W E R T A S D F G Z X C V B)
10
+ end
11
+
12
+ def right
13
+ %w(7 8 9 0 - = y u i o p [ ] \\ h j k l ; ' n m , . / & * ( ) _ + Y U I O P { } | H J K L : " N M < > ?)
14
+ end
15
+ end
16
+
17
+ class Dvorak < self
18
+ def name
19
+ "dvorak"
20
+ end
21
+
22
+ def left
23
+ %w(` 1 2 3 4 5 6 ' , . p y a o e u i ; q j k x ~ ! @ # $ % ^ " < > P Y A O E U I : Q J K X)
24
+ end
25
+
26
+ def right
27
+ %w(7 8 9 0 [ ] f g c r l / = \\ d h t n s - b m w v z & * ( ) { } F G C R L ? + | D H T N S _ B M W V Z)
28
+ end
29
+ end
30
+
31
+ class Colemak < self
32
+ def name
33
+ "colemak"
34
+ end
35
+
36
+ def left
37
+ %w(` 1 2 3 4 5 6 q w f p g a r s t d z x c v b ~ ! @ # $ % ^ Q W F P G A R S T D Z X C V B)
38
+ end
39
+
40
+ def right
41
+ %w(7 8 9 0 - = j l u y : [ ] \\ h n e i o ' k m , . / & * ( ) _ + J L U Y ; { } | H N E I O " K M < > ?)
42
+ end
43
+ end
44
+
45
+ attr_accessor :left, :right, :name
46
+
47
+ def combined
48
+ left + right
49
+ end
50
+
51
+ def self.all
52
+ [
53
+ KeyboardBattle::Keyboard::Qwerty.new,
54
+ KeyboardBattle::Keyboard::Dvorak.new,
55
+ KeyboardBattle::Keyboard::Colemak.new
56
+ ]
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ class KeyboardBattle::Report
2
+
3
+ def initialize(filename, results)
4
+ @filename = filename
5
+ @results = results
6
+ end
7
+
8
+ def to_s
9
+ out = "\n"
10
+ out << "#{@filename}:\n"
11
+ @results.each do |keyboard, performance|
12
+ out << " #{keyboard}:\n"
13
+ performance.each do |metric, result|
14
+ out << " #{metric}: #{result}\n"
15
+ end
16
+ end
17
+ out << "\n"
18
+ end
19
+
20
+ end
@@ -0,0 +1,3 @@
1
+ module KeyboardBattle
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "keyboard_battle/command_dispatcher"
2
+ require "keyboard_battle/exercise"
3
+ require "keyboard_battle/keyboard"
4
+ require "keyboard_battle/report"
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeyboardBattle::CommandDispatcher do
4
+
5
+ describe 'an instance with file args' do
6
+ it 'runs the specified files' do
7
+ KeyboardBattle::CommandDispatcher.any_instance.should_receive(:run).with(["texts/qbf.txt"])
8
+ KeyboardBattle::CommandDispatcher.new(["texts/qbf.txt"])
9
+ end
10
+ end
11
+
12
+ describe 'an instance with --bundled' do
13
+ it 'runs all bundled files' do
14
+ KeyboardBattle::CommandDispatcher.any_instance.should_receive(:run).with(Dir.glob("texts/*.txt"))
15
+ KeyboardBattle::CommandDispatcher.new(["--bundled"])
16
+ end
17
+ end
18
+
19
+ describe 'an instance without args' do
20
+ it 'prints the help' do
21
+ KeyboardBattle::CommandDispatcher.any_instance.should_receive(:help)
22
+ KeyboardBattle::CommandDispatcher.new([])
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeyboardBattle::Exercise do
4
+
5
+ describe 'an instance' do
6
+ let(:exercise) { KeyboardBattle::Exercise.new('texts/qbf.txt', KeyboardBattle::Keyboard.all) }
7
+
8
+ it "loads the keyboards" do
9
+ expect(exercise.send(:keyboards).map {|k| k.name}).to eq(%w(qwerty dvorak colemak))
10
+ end
11
+
12
+ describe "when processing texts" do
13
+ describe "with qwerty" do
14
+ it "calculates reach effort" do
15
+ expect(exercise.results[:qwerty][:reach_effort]).to eq(30)
16
+ end
17
+
18
+ it "calculates alternation effort" do
19
+ expect(exercise.results[:qwerty][:alternation_effort]).to eq(11)
20
+ end
21
+
22
+ it "calculates raw score" do
23
+ expect(exercise.results[:qwerty][:raw_score]).to eq(41)
24
+ end
25
+ end
26
+
27
+ describe "with dvorak" do
28
+ it "calculates reach effort" do
29
+ expect(exercise.results[:dvorak][:reach_effort]).to eq(21)
30
+ end
31
+
32
+ it "calculates alternation effort" do
33
+ expect(exercise.results[:dvorak][:alternation_effort]).to eq(13)
34
+ end
35
+
36
+ it "calculates raw score" do
37
+ expect(exercise.results[:dvorak][:raw_score]).to eq(34)
38
+ end
39
+ end
40
+
41
+ describe "with colemak" do
42
+ it "calculates reach effort" do
43
+ expect(exercise.results[:colemak][:reach_effort]).to eq(22)
44
+ end
45
+
46
+ it "calculates alternation effort" do
47
+ expect(exercise.results[:colemak][:alternation_effort]).to eq(9)
48
+ end
49
+
50
+ it "calculates raw score" do
51
+ expect(exercise.results[:colemak][:raw_score]).to eq(31)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeyboardBattle::Keyboard do
4
+
5
+ # Keyboards are fully exercised by Exercise
6
+ # See spec/keyboard_battle/exercise_spec.rb
7
+
8
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe KeyboardBattle::Report do
4
+
5
+ describe 'an instance' do
6
+ let(:battle) {
7
+ KeyboardBattle::Report.new('foo.txt', { dvorak: { a: 1, b: 2, c: 3 }})
8
+ }
9
+
10
+ it "should have an informative to_s" do
11
+ [
12
+ /foo(.*)dvorak(.*)a(.*)1/m,
13
+ /foo(.*)dvorak(.*)b(.*)2/m,
14
+ /foo(.*)dvorak(.*)c(.*)3/m,
15
+ ].each { |m|
16
+ battle.to_s.should match(m)
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ require 'keyboard_battle'