ruby_drills 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.
Files changed (42) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +6 -0
  6. data/CREDITS.txt +0 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE.txt +202 -0
  9. data/NOTICE.txt +11 -0
  10. data/README.md +118 -0
  11. data/Rakefile +6 -0
  12. data/bin/ruby_drills +13 -0
  13. data/lib/ruby_drills/array/all_drill.rb +28 -0
  14. data/lib/ruby_drills/array/all_names_drill.rb +30 -0
  15. data/lib/ruby_drills/array/array_drills.rb +18 -0
  16. data/lib/ruby_drills/array/chunk_drill.rb +34 -0
  17. data/lib/ruby_drills/array/drop_while_drill.rb +27 -0
  18. data/lib/ruby_drills/array/partition_drill.rb +27 -0
  19. data/lib/ruby_drills/array/reduce_drill.rb +24 -0
  20. data/lib/ruby_drills/array/take_while_drill.rb +26 -0
  21. data/lib/ruby_drills/array/zip_drill.rb +28 -0
  22. data/lib/ruby_drills/chomper.rb +23 -0
  23. data/lib/ruby_drills/cli.rb +76 -0
  24. data/lib/ruby_drills/commands.rb +106 -0
  25. data/lib/ruby_drills/config.rb +19 -0
  26. data/lib/ruby_drills/data/gambler.ascii +65 -0
  27. data/lib/ruby_drills/drill.rb +66 -0
  28. data/lib/ruby_drills/drills.rb +52 -0
  29. data/lib/ruby_drills/hash/hash_drills.rb +17 -0
  30. data/lib/ruby_drills/sessions/collector_client.rb +39 -0
  31. data/lib/ruby_drills/sessions/local.rb +45 -0
  32. data/lib/ruby_drills/sessions/timestamp.rb +9 -0
  33. data/lib/ruby_drills/string/string_drills.rb +16 -0
  34. data/lib/ruby_drills/version.rb +3 -0
  35. data/lib/ruby_drills/welcome/welcome_drills.rb +13 -0
  36. data/lib/starter.rb +68 -0
  37. data/ruby_drills.gemspec +29 -0
  38. data/spec/commands_spec.rb +24 -0
  39. data/spec/ruby_drills_spec.rb +7 -0
  40. data/spec/sessions/timestamp_spec.rb +10 -0
  41. data/spec/spec_helper.rb +2 -0
  42. metadata +194 -0
@@ -0,0 +1,76 @@
1
+ module RubyDrills
2
+
3
+ # Manage the processing of command line options
4
+ class CLI
5
+
6
+ class << self
7
+
8
+ # @return [Proc] The Proc defining the valid command line options.
9
+ attr_accessor :options
10
+
11
+ # @return [Array] The Procs that process the parsed options.
12
+ attr_accessor :option_processors
13
+
14
+ # @return [Array<String>] The input array of strings to process
15
+ # as CLI options.
16
+ attr_accessor :input_args
17
+
18
+ # Add another set of CLI options (a Slop block)
19
+ def add_options(&block)
20
+ if options
21
+ old_options = options
22
+ self.options = proc do
23
+ instance_exec(&old_options)
24
+ instance_exec(&block)
25
+ end
26
+ else
27
+ self.options = block
28
+ end
29
+ self
30
+ end
31
+
32
+ # Add a block responsible for processing parsed options.
33
+ def process_options(&block)
34
+ self.option_processors ||= []
35
+ option_processors << block
36
+ self
37
+ end
38
+
39
+ # Clear `options` and `option_processors`
40
+ def reset
41
+ self.options = nil
42
+ self.option_processors = nil
43
+ end
44
+
45
+ def parse_options(args=ARGV.dup)
46
+ unless options
47
+ raise NoOptionsError, "No command line options defined! Use RubyDrills::CLI.add_options to add command line options."
48
+ end
49
+
50
+ self.input_args = args
51
+
52
+ opts = Slop.parse!(args, :help => true, :multiple_switches => false, &options)
53
+
54
+ # Option processors are optional.
55
+ if option_processors
56
+ option_processors.each { |processor| processor.call(opts) }
57
+ end
58
+
59
+ self
60
+ end
61
+
62
+ end
63
+
64
+ reset
65
+ end
66
+ end
67
+
68
+ RubyDrills::CLI.add_options do
69
+ on :v, :version, "Display the version" do
70
+ puts "RubyDrills version #{RubyDrills::VERSION} on Ruby #{RUBY_VERSION}"
71
+ exit
72
+ end
73
+ end.process_options do |opts|
74
+ exit if opts.help?
75
+ Starter.new
76
+ end
@@ -0,0 +1,106 @@
1
+ module Commands
2
+ GAMBLER = File.read(File.join(File.dirname(__FILE__), 'data/gambler.ascii'))
3
+
4
+ # Each command returns a response indicating whether or not the drill is complete.
5
+ def continue
6
+ puts "\nPress any key to continue:"
7
+ Chomper.get_char
8
+ system('clear');
9
+ true
10
+ end
11
+
12
+ def fail(input, message=nil)
13
+ puts "\n\tnot yet...".yellow
14
+ RubyDrills::Config::SESSIONS.attempt(self.class.name, input, reference, 'fail')
15
+ puts message.yellow unless message.nil?
16
+ false
17
+ end
18
+
19
+ def win(input)
20
+ puts "\n\t!!! WIN !!!\n".green
21
+ RubyDrills::Config::SESSIONS.attempt(self.class.name, input, reference, 'pass')
22
+ if (reference != input.strip)
23
+ puts "How does your answer compare to the reference solution?"
24
+ puts reference
25
+ end
26
+ true
27
+ end
28
+
29
+ def skip
30
+ puts "\n\tskipping...for now...".yellow
31
+ true
32
+ end
33
+
34
+ def review
35
+ puts "\n\t#{reference}".green
36
+ puts "\n\ttry this one again soon...".yellow
37
+ true
38
+ end
39
+
40
+ def fold
41
+ puts GAMBLER
42
+ puts "\nYou got to know when to hold 'em, know when to fold 'em...\n".yellow
43
+ true
44
+ end
45
+
46
+ def hint
47
+ puts hints.rotate!.last
48
+ false
49
+ end
50
+
51
+ def back
52
+ puts "\tYou're already at the first drill".yellow if !previous
53
+ previous
54
+ end
55
+
56
+ def clear
57
+ system('clear')
58
+ false
59
+ end
60
+
61
+ def help
62
+ puts %{
63
+ For each drill, type in some Ruby.
64
+ Answer correctly and you'll get a 'WIN' on the board.
65
+ Otherwise, you'll see 'not yet'.
66
+
67
+ These commands are also available to you:
68
+
69
+ help:\tthis screen
70
+ show:\tshow the problem description
71
+ hint:\tget unstuck
72
+ back:\tback to the previous drill
73
+ skip:\ton to the next drill
74
+ review:\tsee the answer
75
+ clear:\tclear the screen
76
+ menu:\tback to the main menu
77
+ exit:\tend your session}
78
+ false
79
+ end
80
+
81
+ def welcome
82
+ puts %{
83
+ Welcome to Ruby Drills!
84
+
85
+ Drills are a way to engage in deliberate practice to master a language.
86
+ Challenges in Ruby Drills focus on a specific method.
87
+ Answers typically consist of a single line.
88
+ Your objective is to complete the drill with ease and joy,
89
+ without consulting any external documentation.
90
+
91
+ -----------------------------------------------------------------------
92
+ }
93
+ end
94
+
95
+ def quit
96
+ system('clear')
97
+ puts %{
98
+ Mastery...the mysterious process during which what is at first difficult
99
+ becomes progressively easier and more pleasurable through practice.
100
+
101
+ --- George Leonard}
102
+ puts
103
+ exit
104
+ end
105
+
106
+ end
@@ -0,0 +1,19 @@
1
+ module RubyDrills
2
+ class Config
3
+
4
+ SESSIONS = Sessions::CollectorClient.new
5
+
6
+ def self.pry_config
7
+ Pry.config.pager = false
8
+
9
+ Pry.config.prompt = [
10
+ proc { |target_self, nest_level, pry|
11
+ "#{":#{nest_level}" unless nest_level.zero?}>> "
12
+ },
13
+ proc { |target_self, nest_level, pry|
14
+ "#{":#{nest_level}" unless nest_level.zero?}* "
15
+ }
16
+ ]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ 7I,.:=,===~~,~~:~~:,~=~~:,:~~~,~::.....,..,,,,,~77:?+777I=I777?:+I?????????+,,~~
2
+ III++?,~~:~~:~=::~~.~~~~~.:~~~:,............,?7?:,:,,777I=I77I?+~,..,~::~~~~~,~~
3
+ III=?I::~~:~::~::~~.:~~~~.:~~~................,,.,,,,,,=?+?I7I???+I,~~~:~~~~:,~~
4
+ I??..:~~~~:~~,~::~:,,~~:~.:~~..................,......,.?+?77??+++?,:~~:~~~~:,~~
5
+ I??II,+=:~::~,:~:::,.~~:~.:~....................,,..,,...++I7I?++??,::~,~~::,,~~
6
+ I???=.++,,~:~:,:,~,:.~~:~.,:...................,,,,,:::,.==II?+=?I+.::~:~~~~,,~~
7
+ ????~,.,~.~::~::~~::.,~::.:.....................,~=,~+,.,,~II++=??+.::::~~::.,::
8
+ ??????~++:,~~:~:~~::,,:::......................,.,~,,::,,.=II===?=?,::::~:::.,:~
9
+ ??????..:,~,~,::::::,.~~~,................,.....,,,,,,:,.,=II===?+=,::::::::.:~~
10
+ ???I???+,?:.~::::~~:,.:~:.......................,,..,...,.=II++=?+~,::~::~::.:~~
11
+ ???????+:?:,,:::::~::.,::......................,..,,..,:..+?I+=~=+=,::~~:~:,.::~
12
+ ??I????....?.:~,::::,..~.................,,..,:.,......,..?=I=~~:+:,::~::::,.:~~
13
+ ????I.....,:,,:::::::,.:...,....,...,,,,,.,......,..,.....?=I==~:+,,::~::::..:~~
14
+ ++I..............,:~::..,,..........,,,:,,:,,:,.,..,,,,...?~+~~~,+,,::~~~::..:::
15
+ ??....................:=:..,,,....,...,,::,~::,,,,.,,,,,:.?~+~~~,~.,:,:~:::.,::~
16
+ ??:........................,==,.......,:,,,:::::,,,,,:,,,,I,=::~,~.,:::~::,.,:::
17
+ ???............................,==:...,,,,,,,,.,,:,,,::,,,I,=::,,:.,::~~::,.,::~
18
+ +?+?................................:+==,,..........,::,:~I.~,,,.:.:::~:::,.,:::
19
+ +++++,.....................................~++=,:,...,~:,??.:,,,.,.::~~~::,.::~:
20
+ +++?++?....,.....................................:==+=~~~??.,,,,.:.::~:~::,,::~,
21
+ +?????+?,..,,,,:~~+++::.,:,,,,,...................................,=I?=::,..::~.
22
+ ???????=...,,,.::~~:,:::~~,:~~==~...~?+:................................~?..::~.
23
+ ?+???+=,.....,,:.,..~:::==+===,,,=?:~++??????:.............................=:::,
24
+ ++???+,...........,~:::~=+++++++??+=+=+???==~,,..............................:,,
25
+ ++??+~.......,,...:.~~,~=+?+?+??????==+??+++=,.+?+=~,~.......................:,:
26
+ +?++=:.......=,..::I=:,~=++???????+=+=+++=+++??~~.=:~=:.~:::................::,:
27
+ ++++~........,..:=+++::~=++??++++++====++++?+I?+??+++~~:~:~~,..............~::,:
28
+ ???+,...........:++II,:~=++++?????==~~=++????+???????+~=+~~+,+.......,~::.,~:,,:
29
+ +++~............~??I=,:~==+++?????===+?I????????????++~+:.~=+:.....::~~:,.:~:,::
30
+ ++:.........:..,~+I??::~~==+?????=~:~+++++??I??????+++=+.:..~~..:::,:~~:,,~~:::,
31
+ +~..............,??I?::~~==+?????+=,,.:,,~=??I????+++=I?,...,:..::=~,:~:,~~::::~
32
+ =,..............~+???::~==+??=::,,::=:~~:,+++??+?+++II+?:,=:,..~~~~~:.....,:::~~
33
+ ...............,,????::~=+++,~.:,::::,::~::+?+?+++++??++:~=+.:++=~=~::,..,,..,~~
34
+ .................+?I?~:~=++,=,::,~::==:==~::+?+++++???I=:==:....,+~,,::~~~~:,:~:
35
+ ................:=+?+~:~=+===~++=+I?.,,==~=:+++++++?II?::=......~,,..:::~:=~~=~:
36
+ ................,????+?==+,==+=+++=+==~~,,~+?+?+++I???~~~?.....==,,=,:,,,::::~~=
37
+ ..........,.....,~==+?:,,::==+===~=~=+++++:~+++++?I?II,,.......,,,,~:,.=~,:,:::~
38
+ ......,.....,....,~:::=~,:,=+=++++=++++=++=~++++??????..........,,,,:.,+~:,,=::~
39
+ ...,...............:~,,~~:+=,?:==++++=+?+++:+++??+I?I+......,......,:.:,::,.,=::
40
+ ....,.......,....?,::.~===,~=~~=??==?I?+~+?=,..~??+?~..................:,:.:,,::
41
+ ............,..,.II,.~,~::==?~~+?+I++?I?+~+??==~+??+.......................:,.::
42
+ ...........,..,,.III...=,,,,=????+?=I7?IIII+?==?+=~...........................:~
43
+ ...,.,.....,.,.,=I7II...,,:,+:?=++?I+=+???+I????II,...........................,:
44
+ .,,........,,...IIIII7....,~:,+=?I?.,+~+?===,~IIII.............................:
45
+ ...,.,..........I=~.II=,,,,,::~::?:+=,=,=~+=IIIIII,............................,
46
+ ,,..,.,...,:.....7I..II::,,,,:,,,,,,:~~:++=?II?III=............................,
47
+ ,....,...,.,,,..,,,77777I====~:~~~~~==+++=+7III7II7.............................
48
+ ..,:,,,...:,,.,,,:I77777777I7==7==++++++=~+7I7I?77:,............................
49
+ ,,..,...::..?77777777777777777777?+=++=~~+II7I7,,.,.............................
50
+ ..,~..,,.77II77777777II77777777777777~~=:IIII,.,............................,...
51
+ ,,.,:,,,:I77777777III77777777777777777?,I7I+.,..............................,...
52
+ ,.,.,,....777II777I777777777I77777777I7III?=...................................,
53
+ :,........77I7I77I7777777777777777777777II?I,...................................
54
+ ,,,,,.....+777I777777777777777777777777777??..............................,.....
55
+ ,:,,,......77IIII77I77777777I77777777777777I..................,.................
56
+ ..,,.,.....?777777II77I7I7777777777777777I7I,...................................
57
+ ,,,.,.......77I++???????????I777777777777777,,,...........................,,.,.,
58
+ ,,..::..:+???I???I??IIIIIII7II777777777777777.,..........,...............,..,,.,
59
+ .,:,,=?I??IIIIII?I??I????????7777777777777777I.............................,.,,.
60
+ .,,,=IIIIIIII???=~,,77I777777I7777777I7III7777,...........................,...,,
61
+ :...+IIIIII?????I???IIIIIIII??III777II77I77I,,,........................,.......,
62
+ ...:??IIIIIIIIIII?IIIIIIIIIIIIII7II7I7I777II..............................,..,,,
63
+ ...:?IIIIIIIIIIII?+I??????+????I77I77777?III.,......................,.......,,,,
64
+ ,.,:?IIIIIIII?I?+++?IIIII??IIIIII7I77IIIII??...,.......................,..,,,,:,
65
+ ..:~?IIIIIIIIII7IIIIIIIIIIIIIII7777IIIIII+++.,,,,,....................,,,,,,,,,,
@@ -0,0 +1,66 @@
1
+ class Drill
2
+ include Commands
3
+ attr_accessor :next, :previous, :hints
4
+
5
+ def initialize
6
+ setup
7
+ @context = Pry.binding_for(self)
8
+ end
9
+
10
+ def drills
11
+ # Composite pattern: Allow for drill.start to work in the REPL or in tests.
12
+ [self]
13
+ end
14
+
15
+ def expected
16
+ eval(reference)
17
+ end
18
+
19
+ def done?(input)
20
+ case input.to_sym
21
+ when :menu
22
+ RubyDrills::Config::SESSIONS.command(self.class.name, input)
23
+ clear
24
+ true
25
+ when :exit
26
+ RubyDrills::Config::SESSIONS.command(self.class.name, input)
27
+ quit
28
+ when :show
29
+ RubyDrills::Config::SESSIONS.command(self.class.name, input)
30
+ show
31
+ when lambda {|s| Commands.instance_methods.include?(s) }
32
+ RubyDrills::Config::SESSIONS.command(self.class.name, input)
33
+ self.send(input)
34
+ else
35
+ check_answer(input)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def check_answer(input)
42
+ begin
43
+ Pry.run_command input, :context => @context, :output => answer = StringIO.new
44
+ Pry.run_command reference, :context => @context, :output => exp = StringIO.new
45
+
46
+ puts answer.string
47
+
48
+ return fail(input) if answer.string != exp.string
49
+ return false if !comparable_answer?(input)
50
+ win(input)
51
+ rescue SyntaxError => ex
52
+ puts "SyntaxError"
53
+ fail(input)
54
+ rescue StandardError => ex
55
+ puts "#{ex.inspect}"
56
+ fail(input)
57
+ end
58
+ end
59
+
60
+ def comparable_answer?(input)
61
+ valid?(input).tap do |pass|
62
+ fail(input, "\tyou have the right answer, but try a different method.") if !pass
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,52 @@
1
+ class Drills
2
+ include Commands
3
+
4
+ def start
5
+ drill = linked_drills[0]
6
+ while drill do
7
+ drill.show
8
+
9
+ begin
10
+ input = Readline.readline("\n>> ", true)
11
+ end while (!drill.done?(input))
12
+
13
+ case input
14
+ when 'back'
15
+ drill = drill.previous
16
+ clear
17
+ when 'menu'
18
+ break
19
+ else
20
+ drill = drill.next
21
+ continue
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ # Creates a linked list from the array of drills
28
+ def linked_drills
29
+ ordered_drills.tap do |linked|
30
+ for i in 0..linked.size-1
31
+ linked[i].previous = linked[i-1] unless (i == 0)
32
+ linked[i].next = linked[i+1] unless (i == linked.size-1)
33
+ end
34
+ end
35
+ end
36
+
37
+ # deliver drills in random order
38
+ def ordered_drills
39
+ drills.shuffle
40
+ end
41
+
42
+ # Determines the list of drills in this directory
43
+ def drills
44
+ mod = self.class.name.gsub(/Drills/, '').downcase
45
+ Dir["#{File.dirname(__FILE__)}/#{mod}/*drill.rb"].map do |f|
46
+ require f
47
+ name = File.basename(f, '.rb')
48
+ clazz = name.split('_').map(&:capitalize).join
49
+ Module.const_get(clazz).new
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,17 @@
1
+ class HashDrills < Drills
2
+
3
+ def banner
4
+ %{
5
+ Ruby Drills: Hash
6
+
7
+ A Hash is a dictionary-like collection of unique keys and their values.
8
+ Also called associative arrays, they are similar to Arrays, but where an
9
+ Array uses integers as its index, a Hash allows you to use any object type.
10
+
11
+ COMING SOON
12
+
13
+ ------------------------------------------------------------------
14
+ }
15
+ end
16
+
17
+ end
@@ -0,0 +1,39 @@
1
+ require 'securerandom'
2
+ require 'httparty'
3
+
4
+ module Sessions
5
+ class CollectorClient
6
+ include HTTParty
7
+ base_uri 'https://drill-collector.herokuapp.com'
8
+ # base_uri 'http://localhost:9091'
9
+ # debug_output $stderr
10
+
11
+ attr_reader :session_id
12
+
13
+ def initialize
14
+ @session_id = SecureRandom.urlsafe_base64
15
+ end
16
+
17
+ def command(name, input)
18
+ store({context: name, input: input, type: 'command'})
19
+ end
20
+
21
+ def attempt(name, input, reference, result)
22
+ store({context: name, input: input, reference: reference, result: result, type: 'attempt'})
23
+ end
24
+
25
+ private
26
+
27
+ def store(entry)
28
+ Thread.new do
29
+ begin
30
+ data = entry.merge(timestamp: Sessions::Timestamp.collector, session_id: @session_id)
31
+ self.class.post('/record', {:body => data})
32
+ rescue => e
33
+ # silent scream for now...
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'fileutils'
2
+ require 'pstore'
3
+
4
+ module Sessions
5
+ class Local
6
+ include FileUtils
7
+
8
+ DIR = '/usr/local/var/ruby_drills'
9
+
10
+ def initialize
11
+ ensure_directory_exists
12
+ @db = PStore.new(File.join(DIR, 'progress.pstore'))
13
+ end
14
+
15
+ def command(name, input)
16
+ store({context: name, input: input, type: 'command'})
17
+ end
18
+
19
+ def attempt(name, input, reference, result)
20
+ store({context: name, input: input, reference: reference, result: result, type: 'attempt'})
21
+ end
22
+
23
+ def stats
24
+ @db.transaction(true) do
25
+ @db.roots.each do |key|
26
+ puts @db[key].inspect
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def ensure_directory_exists
34
+ FileUtils.mkdir_p(DIR) if !File.exists?(DIR)
35
+ end
36
+
37
+ def store(entry)
38
+ @db.transaction do
39
+ t = Sessions::Timestamp.collector
40
+ @db[t] = entry.merge({ time: t })
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,9 @@
1
+ module Sessions
2
+ class Timestamp
3
+
4
+ def self.collector(t = Time.now)
5
+ t.strftime("%b %-d, %Y %-l:%M:%S %p %Z")
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class StringDrills < Drills
2
+
3
+ def banner
4
+ %{
5
+ Ruby Drills: String
6
+
7
+ A String object holds and manipulates an arbitrary sequence of bytes,
8
+ typically representing characters.
9
+
10
+ COMING SOON
11
+
12
+ ------------------------------------------------------------------
13
+ }
14
+ end
15
+
16
+ end
@@ -0,0 +1,3 @@
1
+ module RubyDrills
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ class WelcomeDrills
2
+ include Commands
3
+
4
+ def banner
5
+ welcome
6
+ end
7
+
8
+ def start
9
+ help
10
+ continue
11
+ end
12
+
13
+ end
data/lib/starter.rb ADDED
@@ -0,0 +1,68 @@
1
+ require 'slop'
2
+ require 'pry'
3
+ require 'colorize'
4
+ require 'readline'
5
+
6
+ require 'ruby_drills/version'
7
+ require 'ruby_drills/cli'
8
+ require 'ruby_drills/commands'
9
+ require 'ruby_drills/drill'
10
+ require 'ruby_drills/drills'
11
+ require 'ruby_drills/chomper'
12
+ require 'ruby_drills/sessions/collector_client'
13
+ require 'ruby_drills/sessions/timestamp'
14
+ require 'ruby_drills/config'
15
+
16
+ class Starter
17
+ include Commands
18
+
19
+ def initialize
20
+ RubyDrills::Config.pry_config
21
+ clear
22
+ welcome
23
+ menu(drills)
24
+ clear
25
+ quit
26
+ end
27
+
28
+ def menu(options)
29
+ choice = nil
30
+ valid_choices = valid_number_to_drill_associations(options)
31
+
32
+ while (choice != 'q')
33
+ if valid_choices.include?(choice)
34
+ clear
35
+ run_drill(options[choice.to_i])
36
+ elsif (choice == 's')
37
+ RubyDrills::Config::SESSIONS.stats
38
+ end
39
+
40
+ puts "\nWhat would you like to learn next?\n\n"
41
+ options.each_with_index {|opt, i| puts "\t#{i}: #{opt.capitalize}"}
42
+ puts "\n\ts: see your stats"
43
+ puts "\tq: quit"
44
+
45
+ choice = Readline.readline("\n>> ", true)
46
+ end
47
+ end
48
+
49
+ def drills
50
+ %w[welcome array string hash]
51
+ end
52
+
53
+ private
54
+
55
+ def valid_number_to_drill_associations(options)
56
+ (0...options.size).to_a.map {|x| x.to_s}
57
+ end
58
+
59
+ def run_drill(d)
60
+ require "ruby_drills/#{d}/#{d}_drills"
61
+ clazz = Module.const_get("#{d.capitalize}Drills")
62
+ drill = clazz.new
63
+ puts drill.banner
64
+ continue
65
+ drill.start
66
+ end
67
+
68
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ruby_drills/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ruby_drills"
8
+ gem.version = RubyDrills::VERSION
9
+
10
+ gem.authors = ["Bobby Norton"]
11
+ gem.email = ["bobby@testedminds.com"]
12
+ gem.description = "A deliberate practice tool for the core Ruby API's."
13
+ gem.summary = gem.description
14
+ gem.homepage = "http://rubydrills.com"
15
+ gem.license = "Apache 2.0"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.add_dependency("pry", "0.9.12.1")
23
+ gem.add_dependency("colorize", "0.5.8")
24
+ gem.add_dependency("httparty", "0.11.0")
25
+
26
+ gem.add_development_dependency("rake", "10.0.4")
27
+ gem.add_development_dependency("rspec", "~> 2.14.0.rc1")
28
+ gem.add_development_dependency("wrong", "0.7.0")
29
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands do
4
+ describe "#hint" do
5
+ it "returns hints in order" do
6
+ wrapper = Class.new do
7
+ include Commands
8
+ attr_accessor :hints
9
+ def setup
10
+ @hints = %w[first second third]
11
+ end
12
+ end.new
13
+ wrapper.setup
14
+ expect(wrapper).to receive(:puts).with("first").ordered
15
+ expect(wrapper).to receive(:puts).with("second").ordered
16
+ expect(wrapper).to receive(:puts).with("third").ordered
17
+ expect(wrapper).to receive(:puts).with("first").ordered
18
+ wrapper.hint
19
+ wrapper.hint
20
+ wrapper.hint
21
+ wrapper.hint
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe "RubyDrills" do
4
+ it "should have a version number" do
5
+ assert { RubyDrills::VERSION }
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sessions::Timestamp do
4
+
5
+ specify "collector format" do
6
+ t = Time.parse("2011-10-17 22:38:00 +0600")
7
+ Sessions::Timestamp.collector(t).should == "Oct 17, 2011 11:38:00 AM CDT"
8
+ end
9
+
10
+ end
@@ -0,0 +1,2 @@
1
+ require 'starter'
2
+ require 'wrong/adapters/rspec'