karl-loris 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +1 -0
  3. data/TODO +21 -0
  4. data/VERSION +1 -0
  5. data/autotest/discover.rb +3 -0
  6. data/cucumber.yml +1 -0
  7. data/examples/self_test/jsl.conf +0 -0
  8. data/examples/self_test/spec/spec.rhino.js +6 -0
  9. data/features/run.feature +37 -0
  10. data/features/step_definitons/all.rb +61 -0
  11. data/features/support/env.rb +145 -0
  12. data/lib/always_continuer.rb +7 -0
  13. data/lib/extension_filter.rb +20 -0
  14. data/lib/file_actioner.rb +16 -0
  15. data/lib/file_filter.rb +14 -0
  16. data/lib/file_finder.rb +31 -0
  17. data/lib/icons/error.png +0 -0
  18. data/lib/icons/failure.png +0 -0
  19. data/lib/icons/info.png +0 -0
  20. data/lib/icons/success.png +0 -0
  21. data/lib/icons/warning.png +0 -0
  22. data/lib/loris.rb +108 -0
  23. data/lib/modified_filter.rb +21 -0
  24. data/lib/outputs/console_clearing_output.rb +14 -0
  25. data/lib/outputs/growl_output.rb +26 -0
  26. data/lib/outputs/output_collection.rb +23 -0
  27. data/lib/outputs/shell_output.rb +17 -0
  28. data/lib/poller.rb +16 -0
  29. data/lib/sleep_waiter.rb +11 -0
  30. data/lib/task_manager.rb +27 -0
  31. data/lib/tasks/javascript_lint_runner.rb +15 -0
  32. data/lib/tasks/javascript_lint_task.rb +63 -0
  33. data/lib/tasks/jspec_runner.rb +15 -0
  34. data/lib/tasks/jspec_task.rb +53 -0
  35. data/lib/tasks/list_task.rb +34 -0
  36. data/loris.tmproj +202 -0
  37. data/spec/file_actioner_spec.rb +41 -0
  38. data/spec/file_filter_spec.rb +28 -0
  39. data/spec/file_finder_spec.rb +73 -0
  40. data/spec/growl_output_spec.rb +33 -0
  41. data/spec/jspec_task_spec.rb +47 -0
  42. data/spec/list_task_spec.rb +58 -0
  43. data/spec/modified_filter_spec.rb +47 -0
  44. data/spec/poller_spec.rb +28 -0
  45. data/spec/shell_output_spec.rb +25 -0
  46. data/spec/task_manager_spec.rb +64 -0
  47. metadata +70 -19
  48. data/loris.gemspec +0 -33
@@ -0,0 +1,12 @@
1
+ # Automatically reload this config file if it changes
2
+ require 'autotest/restart'
3
+
4
+ require 'autotest/growl'
5
+
6
+ Autotest::Growl::hide_label = true
7
+
8
+ # Use remote notification to avoid problem where notifications sometime don't show
9
+ Autotest::Growl::remote_notification = true
10
+
11
+ # Don't busy wait on OSX
12
+ # require 'autotest/fsevent'
@@ -0,0 +1 @@
1
+ .DS_Store
data/TODO ADDED
@@ -0,0 +1,21 @@
1
+ * Tidy Windows related if statements
2
+ * Tidy JSL filename removing
3
+
4
+ * javascript lint not installed? (package if not already installed?)
5
+
6
+ * JSpec not installed? (can require as gem)
7
+
8
+ * Add JS Test Driver support
9
+
10
+ * Detect file deletion/directory rename
11
+
12
+ * Deal with filesystem exceptions
13
+ * Deal with task exceptions
14
+ * jspec task (just modified files?)
15
+ * javascript lint task (just modified files?)
16
+ * separate thread (create new and kill old)
17
+ * listen for interrupt
18
+ * detect file deletion
19
+ * rspec task
20
+ * cucumber task
21
+ * Tidy growl windows code
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.6
@@ -0,0 +1,3 @@
1
+ Autotest.add_discovery do
2
+ "rspec"
3
+ end
@@ -0,0 +1 @@
1
+ default: features
File without changes
@@ -0,0 +1,6 @@
1
+
2
+ load('/Library/Ruby/Gems/1.8/gems/visionmedia-jspec-2.2.1/lib/jspec.js')
3
+
4
+ JSpec
5
+ .run({ formatter : JSpec.formatters.Terminal })
6
+ .report()
@@ -0,0 +1,37 @@
1
+ Feature: Run Loris
2
+
3
+ Scenario: Creating a file triggers loris
4
+ Given I run loris --debug
5
+ When I create a file named "created.txt"
6
+ And I wait until loris has finished processing changes
7
+ Then I should see "created.txt" in the Loris output
8
+ And I should not see any errors
9
+
10
+ Scenario: Modifying a file triggers loris
11
+ Given I create a file named "modified.txt"
12
+ When I run loris --debug
13
+ And I wait until loris has finished processing changes
14
+ And I start recording the Loris output
15
+ And I modify the "modified.txt" file
16
+ And I wait until loris has finished processing changes
17
+ Then I should see "modified.txt" in the recorded output
18
+ And I should not see any errors
19
+
20
+ Scenario: Modified only triggered once
21
+ Given I create a file named "modified.txt"
22
+ When I run loris --debug
23
+ And I wait until loris has finished processing changes
24
+ And I start recording the Loris output
25
+ And I modify the "modified.txt" file
26
+ And I wait until loris has finished processing changes
27
+ And I wait until loris has finished processing changes
28
+ Then I should only see "modified.txt" once in the recorded output
29
+ And I should not see any errors
30
+
31
+ Scenario: Directories not included
32
+ Given I run loris --debug
33
+ When I create a directory named "dir"
34
+ And I wait until loris has finished processing changes
35
+ Then I should NOT see "dir" in the Loris output
36
+ And I should not see any errors
37
+
@@ -0,0 +1,61 @@
1
+ require 'spec/expectations'
2
+
3
+ When /^I run loris(.*)$/ do |loris_opts|
4
+ run_in_background "#{Loris::RUBY_BINARY} #{Loris::BINARY} #{loris_opts}"
5
+ end
6
+
7
+ Given /^(?:I create )a file named "([^\"]*)"$/ do |file_name|
8
+ @current_dir = working_dir
9
+ create_file(file_name, '')
10
+ end
11
+
12
+ Given /^(?:I create )a directory named "([^\"]*)"$/ do |dir_name|
13
+ @current_dir = working_dir
14
+ create_dir(dir_name)
15
+ end
16
+
17
+ Given /^(?:I create )a file named "([^\"]*)" with:$/ do |file_name, file_content|
18
+ @current_dir = working_dir
19
+ create_file(file_name, file_content)
20
+ end
21
+
22
+ When /^I modify the "([^\"]*)" file$/ do |file_name|
23
+ @current_dir = working_dir
24
+ touch_file(file_name)
25
+ end
26
+
27
+ When /^I wait until loris has finished processing changes$/ do
28
+ len = get_background_output.length
29
+ new_output = ""
30
+ while not new_output =~ /\[Poll complete\]/
31
+ new_output = get_background_output[len..-1]
32
+ sleep 0.5
33
+ end
34
+ end
35
+
36
+ When /^I start recording the Loris output$/ do
37
+ @pre_recorded_length = get_background_output.length
38
+ end
39
+
40
+ Then /^I should see "([^\"]*)" in the recorded output$/ do |text|
41
+ recorded = get_background_output[@pre_recorded_length..-1]
42
+ recorded.should include text
43
+ end
44
+
45
+ Then /^I should only see "([^\"]*)" once in the recorded output$/ do |text|
46
+ recorded = get_background_output[@pre_recorded_length..-1]
47
+ recorded.should include text
48
+ recorded.scan(text).length.should equal 1
49
+ end
50
+
51
+ Then /^I should see "([^\"]*)" in the Loris output$/ do |text|
52
+ get_background_output.should include text
53
+ end
54
+
55
+ Then /^I should NOT see "([^\"]*)" in the Loris output$/ do |text|
56
+ get_background_output.should_not include text
57
+ end
58
+
59
+ Then /^I should not see any errors$/ do
60
+ get_background_error.strip().should == ""
61
+ end
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'spec/expectations'
4
+ require 'fileutils'
5
+ require 'forwardable'
6
+
7
+ require 'lib/loris.rb'
8
+
9
+ class CucumberWorld
10
+ extend Forwardable
11
+ def_delegators CucumberWorld, :examples_dir, :self_test_dir, :working_dir, :cucumber_lib_dir
12
+
13
+ def self.examples_dir(subdir=nil)
14
+ @examples_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../examples'))
15
+ subdir ? File.join(@examples_dir, subdir) : @examples_dir
16
+ end
17
+
18
+ def self.self_test_dir
19
+ @self_test_dir ||= examples_dir('self_test')
20
+ end
21
+
22
+ def self.working_dir
23
+ @working_dir ||= examples_dir('self_test/tmp')
24
+ end
25
+
26
+ def cucumber_lib_dir
27
+ @cucumber_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
28
+ end
29
+
30
+ def initialize
31
+ @current_dir = self_test_dir
32
+ end
33
+
34
+ private
35
+ attr_reader :last_exit_status, :last_stderr
36
+
37
+ # The last standard out, with the duration line taken out (unpredictable)
38
+ def last_stdout
39
+ strip_duration(@last_stdout)
40
+ end
41
+
42
+ def strip_duration(s)
43
+ s.gsub(/^\d+m\d+\.\d+s\n/m, "")
44
+ end
45
+
46
+ def replace_duration(s, replacement)
47
+ s.gsub(/\d+m\d+\.\d+s/m, replacement)
48
+ end
49
+
50
+ def create_file(file_name, file_content)
51
+ file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
52
+ in_current_dir do
53
+ File.open(file_name, 'w') { |f| f << file_content }
54
+ end
55
+ end
56
+
57
+ def create_dir(dir_name)
58
+ in_current_dir do
59
+ # create any subdirectories if needed
60
+ FileUtils.mkdir(dir_name)
61
+ end
62
+ end
63
+
64
+ def touch_file(file_name)
65
+ in_current_dir do
66
+ FileUtils.touch(file_name)
67
+ end
68
+ end
69
+
70
+ def background_jobs
71
+ @background_jobs ||= []
72
+ end
73
+
74
+ def in_current_dir(&block)
75
+ Dir.chdir(@current_dir, &block)
76
+ end
77
+
78
+ def run(command)
79
+ stderr_file = Tempfile.new('loris-stderr')
80
+ stderr_file.close
81
+
82
+ stdout_file = Tempfile.new('loris-stdout')
83
+ stdout_file.close
84
+
85
+ in_current_dir do
86
+ `#{command} 1> #{stdout_file.path} 2> #{stderr_file.path}`
87
+ @last_exit_status = $?.exitstatus
88
+ end
89
+ @last_stdout = IO.read(stdout_file.path)
90
+ @last_stderr = IO.read(stderr_file.path)
91
+ end
92
+
93
+ def run_in_background(command)
94
+ @stderr_file = Tempfile.new('loris-stderr')
95
+ @stderr_file.close
96
+
97
+ @stdout_file = Tempfile.new('loris-stdout')
98
+ @stdout_file.close
99
+
100
+ pid = fork
101
+ in_current_dir do
102
+ if pid
103
+ background_jobs << pid
104
+ else
105
+ cmd = "#{command} 1> #{@stdout_file.path} 2> #{@stderr_file.path}"
106
+ exec cmd
107
+ end
108
+ end
109
+ end
110
+
111
+ def terminate_background_jobs
112
+ if @background_jobs
113
+ @background_jobs.each do |pid|
114
+ Process.kill(Signal.list['TERM'], pid)
115
+ end
116
+ end
117
+ end
118
+
119
+ def get_background_output
120
+ return IO.read(@stdout_file.path) rescue return ""
121
+ end
122
+
123
+ def get_background_error
124
+ return IO.read(@stderr_file.path) rescue return ""
125
+ end
126
+
127
+ end
128
+
129
+ World do
130
+ CucumberWorld.new
131
+ end
132
+
133
+ Before do
134
+ FileUtils.rm_rf CucumberWorld.working_dir
135
+ FileUtils.mkdir CucumberWorld.working_dir
136
+ end
137
+
138
+ After do
139
+ err = get_background_error
140
+ if (err != "")
141
+ puts 'Background process errors:'
142
+ puts err
143
+ end
144
+ terminate_background_jobs
145
+ end
@@ -0,0 +1,7 @@
1
+ class AlwaysContinuer
2
+
3
+ def continue?
4
+ return true
5
+ end
6
+
7
+ end
@@ -0,0 +1,20 @@
1
+ class ExtensionFilter
2
+
3
+ def initialize(file_class, extension)
4
+ @file_class = file_class
5
+ @extension = extension.downcase
6
+ end
7
+
8
+ def filter(file_name)
9
+ return file_type(file_name) == @extension
10
+ end
11
+
12
+ # Return the part of the file name string after the last '.'
13
+ def file_type(file_name)
14
+ @file_class.extname(file_name).gsub( /^\./, '' ).downcase
15
+ end
16
+
17
+ def complete()
18
+ end
19
+
20
+ end
@@ -0,0 +1,16 @@
1
+ class FileActioner
2
+
3
+ def initialize(file_finder, task_manager)
4
+ @file_finder = file_finder
5
+ @task_manager = task_manager
6
+ end
7
+
8
+ def run
9
+ files = @file_finder.find
10
+
11
+ if (files[:filtered] != [])
12
+ @task_manager.run(files)
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,14 @@
1
+ class FileFilter
2
+
3
+ def initialize(file_class)
4
+ @file_class = file_class
5
+ end
6
+
7
+ def filter(path)
8
+ return @file_class.file?(path)
9
+ end
10
+
11
+ def complete()
12
+ end
13
+
14
+ end
@@ -0,0 +1,31 @@
1
+ class FileFinder
2
+
3
+ def initialize(finder, dir)
4
+ @finder = finder
5
+ @dir = dir
6
+ @filters = []
7
+ end
8
+
9
+ def add_filter(filter)
10
+ @filters << filter
11
+ end
12
+
13
+ def find
14
+ all_files = []
15
+ filtered_files = []
16
+
17
+ @finder.find(@dir) do |path|
18
+ all_files << path
19
+
20
+ keep = @filters.inject(true) { |k, filter| k && filter.filter(path) }
21
+ filtered_files << path if keep
22
+ end
23
+
24
+ @filters.each do |filter|
25
+ filter.complete
26
+ end
27
+
28
+ return { :all => all_files, :filtered => filtered_files }
29
+ end
30
+
31
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,108 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'bind'
6
+ require 'rbconfig'
7
+ require 'find'
8
+ require 'Growl'
9
+
10
+
11
+ require 'file_finder'
12
+ require 'poller'
13
+ require 'sleep_waiter'
14
+ require 'always_continuer'
15
+ require 'file_actioner'
16
+ require 'modified_filter'
17
+ require 'file_filter'
18
+ require 'task_manager'
19
+ require 'extension_filter'
20
+
21
+ require 'outputs/output_collection'
22
+ require 'outputs/shell_output'
23
+ require 'outputs/console_clearing_output'
24
+ require 'outputs/growl_output'
25
+
26
+ require 'tasks/list_task'
27
+ require 'tasks/jspec_task'
28
+ require 'tasks/jspec_runner'
29
+ require 'tasks/javascript_lint_task'
30
+ require 'tasks/javascript_lint_runner'
31
+
32
+
33
+ include Config
34
+
35
+ class DebugObserver
36
+ def update
37
+ puts '[Poll complete]'
38
+ end
39
+ end
40
+
41
+
42
+ module Loris
43
+ BINARY = File.expand_path(File.dirname(__FILE__) + '/../bin/loris')
44
+ LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../lib')
45
+ RUBY_BINARY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
46
+
47
+ module CLI
48
+
49
+ class Main
50
+
51
+ class DummyActioner
52
+
53
+ def initialize(actioner, stream)
54
+ @actioner = actioner
55
+ @stream = stream
56
+ end
57
+
58
+ def run()
59
+ @actioner.run()
60
+ @stream.puts '[Poll complete]'
61
+ @stream.flush
62
+ end
63
+
64
+ end
65
+
66
+ class << self
67
+ def execute(args)
68
+ puts 'Loris is running!'
69
+
70
+ debug = args.length > 0
71
+
72
+ dir = Dir.pwd
73
+
74
+ w = SleepWaiter.new(1)
75
+ c = AlwaysContinuer.new
76
+ ff = FileFinder.new(Find, dir)
77
+ ff.add_filter(FileFilter.new(File))
78
+ ff.add_filter(ModifiedFilter.new(File))
79
+
80
+ oc = OutputCollection.new()
81
+ oc.add(ShellOutput.new($stdout))
82
+ oc.add(ConsoleClearingOutput.new())
83
+
84
+ if (!debug)
85
+ oc.add(GrowlOutput.new(Growl))
86
+ end
87
+
88
+ tm = TaskManager.new(oc)
89
+ tm.add(ListTask.new()) if debug
90
+ tm.add(JavascriptLintTask.new(JavascriptLintRunner.new(dir), dir))
91
+ tm.add(JSpecTask.new(JSpecRunner.new(dir)))
92
+
93
+ a = FileActioner.new(ff, tm)
94
+
95
+ da = DummyActioner.new(a, $stdout)
96
+
97
+ p = Poller.new(w, c, debug ? da : a)
98
+
99
+ p.start()
100
+
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end