typer 0.1.0
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/Manifest +6 -0
- data/README.rdoc +23 -0
- data/Rakefile +14 -0
- data/bin/typer +2 -0
- data/lessons/example.txt +29 -0
- data/lib/typer.rb +183 -0
- data/typer.gemspec +32 -0
- metadata +81 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
= Typer
|
|
2
|
+
|
|
3
|
+
Simple touch typing trainer for developers/sysadmins or other nerds in love with the console.
|
|
4
|
+
|
|
5
|
+
== Installation
|
|
6
|
+
|
|
7
|
+
Install ruby[http://www.ruby-lang.org/] & rubygems[http://rubygems.org/], then:
|
|
8
|
+
|
|
9
|
+
[sudo] gem install typer
|
|
10
|
+
|
|
11
|
+
== Usage
|
|
12
|
+
|
|
13
|
+
typer
|
|
14
|
+
|
|
15
|
+
Press Ctrl+C to exit.
|
|
16
|
+
|
|
17
|
+
== Customize
|
|
18
|
+
|
|
19
|
+
Edit files in <tt>lessons/</tt> directory.
|
|
20
|
+
|
|
21
|
+
== License
|
|
22
|
+
|
|
23
|
+
WTFPL[http://sam.zoy.org/wtfpl/]
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'echoe'
|
|
4
|
+
|
|
5
|
+
Echoe.new('typer', '0.1.0') do |p|
|
|
6
|
+
p.description = "Touch typing practice for developers/sysadmins"
|
|
7
|
+
p.url = "http://github.com/bagilevi/typer"
|
|
8
|
+
p.author = "Levente Bagi"
|
|
9
|
+
p.email = "bagilevi@gmail.com"
|
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*", "lessons/private*"]
|
|
11
|
+
p.development_dependencies = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/bin/typer
ADDED
data/lessons/example.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
git status
|
|
2
|
+
git stash
|
|
3
|
+
spec spec/bots/bot_server_spec.rb
|
|
4
|
+
git rebase --abort
|
|
5
|
+
git log development
|
|
6
|
+
git stash apply
|
|
7
|
+
git reset test/factory.rb
|
|
8
|
+
git commit --amend -m "[Fix#381]"
|
|
9
|
+
git pull origin development
|
|
10
|
+
vim .gitignore
|
|
11
|
+
script/console production
|
|
12
|
+
script/server -p 3001
|
|
13
|
+
rake db:migrate RAILS_ENV=test
|
|
14
|
+
rake db:setup RAILS_ENV=production
|
|
15
|
+
rake -T
|
|
16
|
+
script/generate model Article title:string body:text user_id:integer
|
|
17
|
+
script/generate migration add_permalink_to_aricles
|
|
18
|
+
tail -fn 1000 log/production.log
|
|
19
|
+
spec spec/controllers/articles_controller_spec.rb
|
|
20
|
+
ps aux | grep ruby
|
|
21
|
+
sudo killall ruby
|
|
22
|
+
rm -Rf public/.sass-cache/
|
|
23
|
+
memcached -m 96 -vv -p 11211
|
|
24
|
+
mate ~/.bash_profile
|
|
25
|
+
rake db:schema:load RAILS_ENV=test
|
|
26
|
+
mate config/database.yml
|
|
27
|
+
rvm list
|
|
28
|
+
rvm use 1.9.1
|
|
29
|
+
rvm install 1.9.2
|
data/lib/typer.rb
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
module Typer
|
|
2
|
+
class Application
|
|
3
|
+
|
|
4
|
+
def initialize(options = {})
|
|
5
|
+
default_files = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lessons', '*.txt')))
|
|
6
|
+
@lines = LineCollection.new(options[:file] || default_files)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def run
|
|
10
|
+
catch :exit do
|
|
11
|
+
trap("INT") { throw :exit }
|
|
12
|
+
display_welcome
|
|
13
|
+
loop do
|
|
14
|
+
catch :next_line do
|
|
15
|
+
exercise_line @lines.get_random
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
puts "\n"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def exercise_line line
|
|
23
|
+
clear
|
|
24
|
+
|
|
25
|
+
loop do
|
|
26
|
+
display_line line
|
|
27
|
+
wait_for_user_to_be_ready
|
|
28
|
+
|
|
29
|
+
input_line, time = (read_line_with_time or throw :exit)
|
|
30
|
+
|
|
31
|
+
if input_line == line
|
|
32
|
+
clear
|
|
33
|
+
|
|
34
|
+
speed = Speed.calculate(input_line, time)
|
|
35
|
+
|
|
36
|
+
speed.influence_minimum!
|
|
37
|
+
if speed.good?
|
|
38
|
+
puts "GOOD! #{speed.long_string} - Next minimum: #{Speed.minimum}"
|
|
39
|
+
wait_for_enter
|
|
40
|
+
throw :next_line
|
|
41
|
+
else
|
|
42
|
+
display_retry "Too slow! Minimum: #{Speed.minimum}."
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
display_retry "Incorrect."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def clear
|
|
51
|
+
puts "\n" * 3
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def display_welcome
|
|
55
|
+
puts
|
|
56
|
+
puts "typer - developer and sysadmin typing trainer"
|
|
57
|
+
puts
|
|
58
|
+
puts "The phrases are selected from all *.txt files in lessons directory and are combined so that a line length is between #{@lines.min_length} and #{@lines.max_length}"
|
|
59
|
+
puts "The initial minimum is #{Speed.minimum.long_string} and will increase slowly depending on how fast you type."
|
|
60
|
+
puts "Press ^C to exit."
|
|
61
|
+
puts "Position your fingers over the home row and let the challenge begin!"
|
|
62
|
+
wait_for_enter "start"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def display_line line
|
|
66
|
+
print line
|
|
67
|
+
$stdout.flush
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def wait_for_user_to_be_ready
|
|
71
|
+
sleep 1
|
|
72
|
+
puts "\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def wait_for_enter action = "continue"
|
|
76
|
+
puts
|
|
77
|
+
sleep 1
|
|
78
|
+
print "Press ENTER to #{action}...";
|
|
79
|
+
$stdout.flush
|
|
80
|
+
gets
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def read_line_with_time
|
|
84
|
+
s = nil
|
|
85
|
+
t0 = Time.now
|
|
86
|
+
s = $stdin.readline.strip
|
|
87
|
+
t1 = Time.now
|
|
88
|
+
t = (t1 - t0).to_f
|
|
89
|
+
s ? [s, t] : nil
|
|
90
|
+
rescue
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def display_retry(reason)
|
|
95
|
+
puts ">>> #{reason} Try again!\n"
|
|
96
|
+
puts "\n\n"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def display_speed(speed)
|
|
100
|
+
puts "CPM: #{speed}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class Speed
|
|
106
|
+
@@min_cpm = 90
|
|
107
|
+
|
|
108
|
+
def initialize(cpm)
|
|
109
|
+
@cpm = cpm.to_i
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.minimum=(min_cpm)
|
|
113
|
+
@@min_cpm = min_cpm
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.minimum
|
|
117
|
+
self.new @@min_cpm
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Characters per minute
|
|
121
|
+
def self.calculate input_line, time
|
|
122
|
+
Speed.new( ((input_line.length / time.to_f) * 60).round )
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def good?
|
|
126
|
+
@cpm > @@min_cpm
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def influence_minimum!
|
|
130
|
+
@@min_cpm += ((@cpm - @@min_cpm) / 3).round
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def to_s
|
|
134
|
+
@cpm.to_s
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def long_string
|
|
138
|
+
"#{@cpm} hits per minute"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def method_missing(name, *args, &blk)
|
|
142
|
+
ret = @cpm.send(name, *args, &blk)
|
|
143
|
+
rescue
|
|
144
|
+
super
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class LineCollection
|
|
149
|
+
attr_accessor :min_length, :max_length, :input_files
|
|
150
|
+
|
|
151
|
+
def initialize(input_files)
|
|
152
|
+
@input_files = input_files
|
|
153
|
+
@lines = []
|
|
154
|
+
Array(input_files).each do |input_file|
|
|
155
|
+
@lines += File.read(input_file).split("\n")
|
|
156
|
+
end
|
|
157
|
+
@min_length = 100
|
|
158
|
+
@max_length = 140
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def get_random
|
|
162
|
+
line = ''
|
|
163
|
+
while line.length < @min_length do
|
|
164
|
+
max_diff = @max_length - line.length
|
|
165
|
+
item = nil
|
|
166
|
+
iter = 0
|
|
167
|
+
while item.nil? || item.length > max_diff - 1 do
|
|
168
|
+
i = rand(@lines.size)
|
|
169
|
+
item = @lines[i].strip
|
|
170
|
+
iter += 1
|
|
171
|
+
if iter > 10
|
|
172
|
+
item = @lines[i].strip[0..max_diff - 2]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
line = line.length == 0 ? item : line + ' ' + item
|
|
176
|
+
end
|
|
177
|
+
line
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
Typer::Application.new.run
|
data/typer.gemspec
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = %q{typer}
|
|
5
|
+
s.version = "0.1.0"
|
|
6
|
+
|
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
|
8
|
+
s.authors = ["Levente Bagi"]
|
|
9
|
+
s.date = %q{2010-08-22}
|
|
10
|
+
s.default_executable = %q{typer}
|
|
11
|
+
s.description = %q{Touch typing practice for developers/sysadmins}
|
|
12
|
+
s.email = %q{bagilevi@gmail.com}
|
|
13
|
+
s.executables = ["typer"]
|
|
14
|
+
s.extra_rdoc_files = ["README.rdoc", "bin/typer", "lib/typer.rb"]
|
|
15
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "bin/typer", "lessons/example.txt", "lib/typer.rb", "typer.gemspec"]
|
|
16
|
+
s.homepage = %q{http://github.com/bagilevi/typer}
|
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Typer", "--main", "README.rdoc"]
|
|
18
|
+
s.require_paths = ["lib"]
|
|
19
|
+
s.rubyforge_project = %q{typer}
|
|
20
|
+
s.rubygems_version = %q{1.3.7}
|
|
21
|
+
s.summary = %q{Touch typing practice for developers/sysadmins}
|
|
22
|
+
|
|
23
|
+
if s.respond_to? :specification_version then
|
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
25
|
+
s.specification_version = 3
|
|
26
|
+
|
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
28
|
+
else
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
end
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: typer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 27
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
- 0
|
|
10
|
+
version: 0.1.0
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Levente Bagi
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2010-08-22 00:00:00 +01:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies: []
|
|
21
|
+
|
|
22
|
+
description: Touch typing practice for developers/sysadmins
|
|
23
|
+
email: bagilevi@gmail.com
|
|
24
|
+
executables:
|
|
25
|
+
- typer
|
|
26
|
+
extensions: []
|
|
27
|
+
|
|
28
|
+
extra_rdoc_files:
|
|
29
|
+
- README.rdoc
|
|
30
|
+
- bin/typer
|
|
31
|
+
- lib/typer.rb
|
|
32
|
+
files:
|
|
33
|
+
- Manifest
|
|
34
|
+
- README.rdoc
|
|
35
|
+
- Rakefile
|
|
36
|
+
- bin/typer
|
|
37
|
+
- lessons/example.txt
|
|
38
|
+
- lib/typer.rb
|
|
39
|
+
- typer.gemspec
|
|
40
|
+
has_rdoc: true
|
|
41
|
+
homepage: http://github.com/bagilevi/typer
|
|
42
|
+
licenses: []
|
|
43
|
+
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options:
|
|
46
|
+
- --line-numbers
|
|
47
|
+
- --inline-source
|
|
48
|
+
- --title
|
|
49
|
+
- Typer
|
|
50
|
+
- --main
|
|
51
|
+
- README.rdoc
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
none: false
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
hash: 3
|
|
60
|
+
segments:
|
|
61
|
+
- 0
|
|
62
|
+
version: "0"
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
none: false
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
hash: 11
|
|
69
|
+
segments:
|
|
70
|
+
- 1
|
|
71
|
+
- 2
|
|
72
|
+
version: "1.2"
|
|
73
|
+
requirements: []
|
|
74
|
+
|
|
75
|
+
rubyforge_project: typer
|
|
76
|
+
rubygems_version: 1.3.7
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 3
|
|
79
|
+
summary: Touch typing practice for developers/sysadmins
|
|
80
|
+
test_files: []
|
|
81
|
+
|