keyboard_battle 0.0.2

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/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'