codersdojo 0.9.7 → 0.9.8

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.
@@ -0,0 +1,36 @@
1
+ class ArgumentParser
2
+
3
+ def initialize controller
4
+ @controller = controller
5
+ end
6
+
7
+ def parse params
8
+ command = params[0] ? params[0] : ""
9
+ if command.downcase == "help" then
10
+ @controller.help params[1]
11
+ elsif command.downcase == "setup" then
12
+ @controller.generate params[1], params[2] ? params[2] : '<kata_file>'
13
+ elsif command.downcase == "upload" then
14
+ @controller.upload params[1], params[2]
15
+ elsif command.downcase == "start" then
16
+ run_command = expand_run_command params[1]
17
+ @controller.start run_command, params[2]
18
+ elsif command.downcase == "spec" then
19
+ # 'spec" is for testing purpose only: do nothing special
20
+ else
21
+ raise ArgumentError
22
+ end
23
+ end
24
+
25
+ def expand_run_command command
26
+ if command.end_with?(".sh") then
27
+ "bash #{command}"
28
+ elsif command.end_with?(".bat") or command.end_with?(".cmd") then
29
+ "start #{command}"
30
+ else
31
+ command
32
+ end
33
+ end
34
+
35
+ end
36
+
data/app/codersdojo.rb CHANGED
@@ -1,622 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "tempfile"
4
- require "rexml/document"
5
- require 'rest_client'
6
-
7
- class Scheduler
8
-
9
- def initialize runner
10
- @runner = runner
11
- end
12
-
13
- def start
14
- @runner.start
15
- while true do
16
- sleep 1
17
- @runner.execute
18
- end
19
- end
20
-
21
- end
22
-
23
- class Runner
24
-
25
- attr_accessor :file, :run_command
26
-
27
- def initialize shell, session_provider
28
- @filename_formatter = FilenameFormatter.new
29
- @shell = shell
30
- @session_provider = session_provider
31
- end
32
-
33
- def start
34
- init_session
35
- execute
36
- end
37
-
38
- def init_session
39
- @step = 0
40
- @session_id = @session_provider.generate_id
41
- @shell.mkdir_p(@filename_formatter.session_dir @session_id)
42
- end
43
-
44
- def execute
45
- change_time = @shell.ctime @file
46
- if change_time == @previous_change_time then
47
- return
48
- end
49
- result = @shell.execute "#{@run_command} #{@file}"
50
- state_dir = @filename_formatter.state_dir @session_id, @step
51
- @shell.mkdir state_dir
52
- @shell.cp @file, state_dir
53
- @shell.write_file @filename_formatter.result_file(state_dir), result
54
- @step += 1
55
- @previous_change_time = change_time
56
- end
57
-
58
- end
59
-
60
- class Shell
61
-
62
- MAX_STDOUT_LENGTH = 100000
63
-
64
- def cp source, destination
65
- FileUtils.cp source, destination
66
- end
67
-
68
- def mkdir dir
69
- FileUtils.mkdir dir
70
- end
71
-
72
- def mkdir_p dirs
73
- FileUtils.mkdir_p dirs
74
- end
75
-
76
- def execute command
77
- spec_pipe = IO.popen(command, "r")
78
- result = spec_pipe.read MAX_STDOUT_LENGTH
79
- spec_pipe.close
80
- puts result
81
- result
82
- end
83
-
84
- def write_file filename, content
85
- file = File.new filename, "w"
86
- file << content
87
- file.close
88
- end
89
-
90
- def read_file filename
91
- file = File.new filename
92
- content = file.read
93
- file.close
94
- content
95
- end
96
-
97
- def ctime filename
98
- File.new(filename).ctime
99
- end
100
-
101
- def make_os_specific text
102
- text.gsub('%sh%', shell_extension).gsub('%:%', path_separator).gsub('%rm%', remove_command_name)
103
- end
104
-
105
- def remove_command_name
106
- windows? ? 'delete' : 'rm'
107
- end
108
-
109
- def shell_extension
110
- windows? ? 'cmd' : 'sh'
111
- end
112
-
113
- def path_separator
114
- windows? ? ';' : ':'
115
- end
116
-
117
- def windows?
118
- RUBY_PLATFORM.downcase.include? "windows"
119
- end
120
-
121
- end
122
-
123
- class SessionIdGenerator
124
-
125
- def generate_id time=Time.new
126
- year = format_to_length time.year, 4
127
- month = format_to_length time.month, 2
128
- day = format_to_length time.day, 2
129
- hour = format_to_length time.hour, 2
130
- minute = format_to_length time.min, 2
131
- second = format_to_length time.sec, 2
132
- "#{year}-#{month}-#{day}_#{hour}-#{minute}-#{second}"
133
- end
134
-
135
- def format_to_length value, len
136
- value.to_s.rjust len,"0"
137
- end
138
-
139
- end
140
-
141
- class StateReader
142
-
143
- attr_accessor :session_id, :next_step
144
-
145
- def initialize shell
146
- @filename_formatter = FilenameFormatter.new
147
- @shell = shell
148
- @next_step = 0
149
- end
150
-
151
- def state_count
152
- Dir.new(@filename_formatter.session_dir @session_id).count - 2
153
- end
154
-
155
- def enough_states?
156
- state_count >= 2
157
- end
158
-
159
- def get_state_dir
160
- @filename_formatter.state_dir(@session_id, @next_step)
161
- end
162
-
163
- def has_next_state
164
- File.exist?(get_state_dir)
165
- end
166
-
167
- def read_next_state
168
- state = State.new
169
- state_dir = get_state_dir
170
- state.time = @shell.ctime state_dir
171
- state.code = @shell.read_file @filename_formatter.source_code_file(state_dir)
172
- state.result = @shell.read_file @filename_formatter.result_file(state_dir)
173
- @next_step += 1
174
- state
175
- end
176
-
177
- end
178
-
179
- class FilenameFormatter
180
-
181
- CODERSDOJO_WORKSPACE = ".codersdojo"
182
- RESULT_FILE = "result.txt"
183
- STATE_DIR_PREFIX = "state_"
184
-
185
-
186
- def source_code_file state_dir
187
- Dir.entries(state_dir).each { |file|
188
- return state_file state_dir, file unless file =='..' || file == '.' ||file == RESULT_FILE }
189
- end
190
-
191
- def result_file state_dir
192
- state_file state_dir, RESULT_FILE
193
- end
194
-
195
- def state_file state_dir, file
196
- "#{state_dir}/#{file}"
197
- end
198
-
199
- def state_dir session_id, step
200
- session_directory = session_dir session_id
201
- "#{session_directory}/#{STATE_DIR_PREFIX}#{step}"
202
- end
203
-
204
- def session_dir session_id
205
- "#{CODERSDOJO_WORKSPACE}/#{session_id}"
206
- end
207
-
208
- end
209
-
210
- class Progress
211
-
212
- def self.write_empty_progress states
213
- STDOUT.print "#{states} states to upload"
214
- STDOUT.print "["+" "*states+"]"
215
- STDOUT.print "\b"*(states+1)
216
- STDOUT.flush
217
- end
218
-
219
- def self.next
220
- STDOUT.print "."
221
- STDOUT.flush
222
- end
223
-
224
- def self.end
225
- STDOUT.puts
226
- end
227
- end
228
-
229
- class Uploader
230
-
231
- def initialize hostname, framework, session_dir, state_reader = StateReader.new(Shell.new)
232
- @hostname = hostname
233
- @framework = framework
234
- @state_reader = state_reader
235
- @state_reader.session_id = session_dir.gsub('.codersdojo/', '')
236
- end
237
-
238
- def upload_kata
239
- RestClient.post "#{@hostname}#{@@kata_path}", {:framework => @framework}
240
- end
241
-
242
- def upload_state kata_id
243
- state = @state_reader.read_next_state
244
- RestClient.post "#{@hostname}#{@@kata_path}/#{kata_id}#{@@state_path}", {:code => state.code, :result => state.result, :created_at => state.time}
245
- Progress.next
246
- end
247
-
248
- def upload_states kata_id
249
- Progress.write_empty_progress @state_reader.state_count
250
- while @state_reader.has_next_state
251
- upload_state kata_id
252
- end
253
- Progress.end
254
- end
255
-
256
- def upload_kata_and_states
257
- kata = upload_kata
258
- upload_states(XMLElementExtractor.extract('kata/id', kata))
259
- "This is the link to review and comment your kata #{XMLElementExtractor.extract('kata/short-url', kata)}"
260
- end
261
-
262
- def upload
263
- return upload_kata_and_states if @state_reader.enough_states?
264
- return "You need at least two states"
265
- end
266
-
267
- private
268
- @@kata_path = '/katas'
269
- @@state_path = '/states'
270
-
271
- end
272
-
273
- class XMLElementExtractor
274
- def self.extract element, xml_string
275
- doc = REXML::Document.new xml_string
276
- return doc.elements.each(element) do |found|
277
- return found.text
278
- end
279
- end
280
- end
281
-
282
- class State
283
-
284
- attr_accessor :time, :code, :result
285
-
286
- def initialize time=nil, code=nil, result=nil
287
- @time = time
288
- @code = code
289
- @result = result
290
- end
291
-
292
- end
293
-
294
- class ArgumentParser
295
-
296
- def initialize controller
297
- @controller = controller
298
- end
299
-
300
- def parse params
301
- command = params[0] ? params[0] : ""
302
- if command.downcase == "help" then
303
- @controller.help params[1]
304
- elsif command.downcase == "show-examples" then
305
- @controller.show_examples
306
- elsif command.downcase == "setup" then
307
- @controller.generate params[1], params[2] ? params[2] : '<kata_file>'
308
- elsif command.downcase == "upload" then
309
- @controller.upload params[1], params[2]
310
- elsif command.downcase == "start" then
311
- run_command = expand_run_command params[1]
312
- @controller.start run_command, params[2]
313
- elsif command.downcase == "spec" then
314
- # 'spec" is for testing purpose only: do nothing special
315
- else
316
- raise ArgumentError
317
- end
318
- end
319
-
320
- def expand_run_command command
321
- if command.end_with?(".sh") then
322
- "bash #{command}"
323
- elsif command.end_with?(".bat") or command.end_with?(".cmd") then
324
- "start #{command}"
325
- else
326
- command
327
- end
328
- end
329
-
330
- end
331
-
332
- class Controller
333
-
334
- def initialize view, hostname
335
- @view = view
336
- @hostname = hostname
337
- end
338
-
339
- def help command=nil
340
- if command then
341
- @view.show_detailed_help command.downcase
342
- else
343
- @view.show_help
344
- end
345
- end
346
-
347
- def show_examples
348
- @view.show_examples
349
- end
350
-
351
- def generate framework, kata_file
352
- generator = GeneratorFactory.new.create_generator framework
353
- shell = Shell.new
354
- generator_text = generator.generate kata_file
355
- generator_text = shell.make_os_specific generator_text
356
- puts generator_text
357
- end
358
-
359
- def start command, file
360
- if not command or not file then
361
- @view.show_missing_command_argument_error "start"
362
- return
363
- end
364
- @view.show_start_kata command, file
365
- dojo = Runner.new Shell.new, SessionIdGenerator.new
366
- dojo.file = file
367
- dojo.run_command = command
368
- scheduler = Scheduler.new dojo
369
- scheduler.start
370
- end
371
-
372
- def upload framework, session_directory
373
- @view.show_upload_start @hostname
374
- if session_directory then
375
- uploader = Uploader.new @hostname, framework, session_directory
376
- p uploader.upload
377
- else
378
- @view.show_missing_command_argument_error "upload"
379
- end
380
- end
381
-
382
- end
383
-
384
-
385
- class ConsoleView
386
-
387
- @@VERSION = '0.9'
388
-
389
- def show_help
390
- puts <<-helptext
391
- Personal CodersDojo, Version #{@@VERSION}, http://www.codersdojo.com, Copyright by it-agile GmbH (http://www.it-agile.de)
392
- PersonalCodersDojo automatically runs your tests of a code kata.
393
-
394
- helptext
395
- show_usage
396
- end
397
-
398
- def show_usage
399
- puts <<-helptext
400
- Usage: #{$0} command [options]
401
- Commands:
402
- help, -h, --help Print this help text.
403
- help <command> See the details of the command.
404
- setup <framework> <kata_file> Setup the environment for running the kata.
405
- upload <framework> <session_dir> Upload the kata to http://www.codersdojo.org
406
- show-examples Show some example usages.
407
-
408
- Report bugs to <codersdojo@it-agile.de>
409
- helptext
410
- end
411
-
412
- def show_examples
413
- puts <<-helptext
414
- Examples:
415
- :/dojo/my_kata% #{$0} setup ruby.test/unit prime
416
- Show the instructions how to setup the environment for kata execution with ruby.
417
-
418
- :/dojo/my_kata$ #{$0} upload ruby.test/unit .codersdojo/2010-11-02_16-21-53
419
- Upload the kata (written in Ruby with the test/unit framework) located in directory ".codersdojo/2010-11-02_16-21-53" to codersdojo.com.
420
- helptext
421
- end
422
-
423
- def show_detailed_help command
424
- if command == 'setup' then
425
- show_help_setup
426
- elsif command == 'start' then
427
- show_help_start
428
- elsif command == 'upload' then
429
- show_help_upload
430
- else
431
- show_help_unknown command
432
- end
433
- end
434
-
435
- def show_help_setup
436
- puts <<-helptext
437
- setup <framework> <kata_file_no_ext> Setup the environment for the kata for the given framework and kata file.
438
- The kata_file should not have an extension. Use 'prime' and not 'prime.java'.
439
- By now <framework> is one of clojure.is-test, java.junit, javascript.jspec,
440
- python.unittest, ruby.test/unit.
441
- Use ??? as framework if your framework isn't in the list.
442
- helptext
443
- end
444
-
445
- def show_help_start
446
- puts <<-helptext
447
- start <shell_command> <kata_file> Start the continuous test runner, that runs <shell-command> whenever <kata_file>
448
- changes. The <kata_file> has to include the whole source code of the kata.
449
- Whenever the test runner is started, it creates a new session directory in the
450
- directory .codersdojo where it logs the steps of the kata.
451
- helptext
452
- end
453
-
454
- def show_help_upload
455
- puts <<-helptext
456
- upload <framework> <session_directory> Upload the kata written with <framework> in <session_directory> to codersdojo.com.
457
- <session_directory> is relative to the working directory.
458
- Take a look at the generate command for the supported frameworks.
459
- If you used another framework, use ??? and send an email to codersdojo@it-agile.de
460
- helptext
461
- end
462
-
463
- def show_help_unknown command
464
- puts <<-helptext
465
- Command #{command} not known. Try '#{$0} help' to list the supported commands.
466
- helptext
467
- end
468
-
469
- def show_start_kata command, file
470
- puts "Starting PersonalCodersDojo with command #{command} and kata file #{file}. Use Ctrl+C to finish the kata."
471
- end
472
-
473
- def show_missing_command_argument_error command
474
- puts "Command <#{command}> recognized but no argument was provided (at least one argument is required).\n\n"
475
- show_usage
476
- end
477
-
478
- def show_upload_start hostname
479
- puts "Start upload to #{hostname}"
480
- end
481
-
482
- def show_upload_result result
483
- puts result
484
- end
485
-
486
- def show_socket_error command
487
- puts "Encountered network error while <#{command}>. Is http://www.codersdojo.com down?"
488
- end
489
-
490
- end
491
-
492
- class GeneratorFactory
493
-
494
- def initialize
495
- @frameworks = {"clojure.is-test" => ClosureGenerator,
496
- "java.junit" => JavaGenerator,
497
- "javascript.jspec" => JavascriptGenerator,
498
- "python.unittest" => PythonGenerator,
499
- "ruby.test/unit" => RubyGenerator}
500
- end
501
-
502
- def create_generator framework
503
- generator_class = @frameworks[framework]
504
- if generator_class.nil? then
505
- generator_class = AnyGenerator
506
- end
507
- generator_class.new
508
- end
509
-
510
- end
511
-
512
- class AnyGenerator
513
-
514
- def generate kata_file
515
- <<-generate_help
516
- You have to create two shell scripts manually:
517
- Create a shell script run-once.%sh% that runs the tests of your kata once.
518
-
519
- Create a second shell script run-endless.sh with this content:
520
- #{$0} start run-once.%sh% #{kata_file}.<extension>
521
-
522
- Run run-endless.%sh% and start your kata.
523
-
524
- Assumptions:
525
- - The whole kata source code is in the one #{kata_file}.<extension>.
526
- generate_help
527
- end
528
- end
529
-
530
- class ClosureGenerator
531
- def generate kata_file
532
- <<-generate_help
533
- You have to create two shell scripts manually:
534
- Create a shell script run-once.%sh% with this content:
535
- java -cp clojure-contrib.jar%:%clojure.jar clojure.main #{kata_file}.clj
536
-
537
- Create a second shell script run-endless.sh with this content:
538
- #{$0} start run-once.%sh% #{kata_file}.clj
539
-
540
- Run run-endless.%sh% and start your kata.
541
-
542
- Assumptions:
543
- - Java is installed on your system and 'java' is in the path.
544
- - clojure.jar and clojure-contrib.jar are placed in your work directory.
545
- generate_help
546
- end
547
- end
548
-
549
- class JavaGenerator
550
- def generate kata_file
551
- <<-generate_help
552
- You have to create two shell scripts manually:
553
- Create a shell script run-once.%sh% with this content:
554
- %rm% bin/#{kata_file}.class
555
- javac -cp lib/junit.jar -d bin #{kata_file}.java
556
- java -cp lib/junit.jar%:%bin #{kata_file}
557
-
558
- Create a second shell script run-endless.sh with this content:
559
- #{$0} start run-once.%sh% src/#{kata_file}.java
560
-
561
- Run run-endless.%sh% and start your kata.
562
-
563
- Assumptions:
564
- - A Java JDK is installed on your system and 'java' and 'javac' are in the path.
565
- - junit.jar is placed in a directory named 'lib'.
566
- - The kata source file is placed in the 'src' directory.
567
- - The kata class file is generated into the 'class' directory.
568
- - In the source file the classes are placed in the default package.
569
- - The kata source file has a main method that starts the tests.
570
- - If your IDE (like Eclipse) compiles the source file, you should remove the first two lines
571
- in the run-once.%sh% file.
572
- generate_help
573
- end
574
- end
575
-
576
-
577
- class JavascriptGenerator
578
- def generate kata_file
579
- <<-generate_help
580
- You have to create two shell scripts manually:
581
- Create a shell script run-once.%sh% with this content:
582
- jspec --rhino run
583
-
584
- Create a second shell script run-endless.sh with this content:
585
- #{$0} start run-once.%sh% #{kata_file}.js
586
-
587
- Run run-endless.%sh% and start your kata.
588
- generate_help
589
- end
590
- end
591
-
592
-
593
- class PythonGenerator
594
- def generate kata_file
595
- <<-generate_help
596
- You have to create two shell scripts manually:
597
- Create a shell script run-once.%sh% with this content:
598
- python #{kata_file}.py
599
-
600
- Create a second shell script run-endless.sh with this content:
601
- #{$0} start run-once.%sh% #{kata_file}.py
602
-
603
- Run run-endless.%sh% and start your kata.
604
- generate_help
605
- end
606
- end
607
-
608
-
609
- class RubyGenerator
610
- def generate kata_file
611
- <<-generate_help
612
- You have to create one shell script manually:
613
- Create a shell script run-endless.%sh% with this content:
614
- #{$0} start ruby #{kata_file}.rb
615
-
616
- Run run-endless.%sh% and start your kata.
617
- generate_help
618
- end
619
- end
3
+ require 'console_view'
4
+ require 'controller'
5
+ require 'argument_parser'
620
6
 
621
7
  def called_from_spec args
622
8
  args[0] == "spec"
@@ -626,7 +12,7 @@ end
626
12
  if not called_from_spec(ARGV) then
627
13
  view = ConsoleView.new
628
14
  hostname = "http://www.codersdojo.com"
629
- #hostname = "http://localhost:3000"
15
+ # hostname = "http://localhost:3000"
630
16
  controller = Controller.new view, hostname
631
17
  begin
632
18
  arg_parser = ArgumentParser.new controller