karl-loris 0.0.5 → 0.0.6
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/.autotest +12 -0
- data/.gitignore +1 -0
- data/TODO +21 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +3 -0
- data/cucumber.yml +1 -0
- data/examples/self_test/jsl.conf +0 -0
- data/examples/self_test/spec/spec.rhino.js +6 -0
- data/features/run.feature +37 -0
- data/features/step_definitons/all.rb +61 -0
- data/features/support/env.rb +145 -0
- data/lib/always_continuer.rb +7 -0
- data/lib/extension_filter.rb +20 -0
- data/lib/file_actioner.rb +16 -0
- data/lib/file_filter.rb +14 -0
- data/lib/file_finder.rb +31 -0
- data/lib/icons/error.png +0 -0
- data/lib/icons/failure.png +0 -0
- data/lib/icons/info.png +0 -0
- data/lib/icons/success.png +0 -0
- data/lib/icons/warning.png +0 -0
- data/lib/loris.rb +108 -0
- data/lib/modified_filter.rb +21 -0
- data/lib/outputs/console_clearing_output.rb +14 -0
- data/lib/outputs/growl_output.rb +26 -0
- data/lib/outputs/output_collection.rb +23 -0
- data/lib/outputs/shell_output.rb +17 -0
- data/lib/poller.rb +16 -0
- data/lib/sleep_waiter.rb +11 -0
- data/lib/task_manager.rb +27 -0
- data/lib/tasks/javascript_lint_runner.rb +15 -0
- data/lib/tasks/javascript_lint_task.rb +63 -0
- data/lib/tasks/jspec_runner.rb +15 -0
- data/lib/tasks/jspec_task.rb +53 -0
- data/lib/tasks/list_task.rb +34 -0
- data/loris.tmproj +202 -0
- data/spec/file_actioner_spec.rb +41 -0
- data/spec/file_filter_spec.rb +28 -0
- data/spec/file_finder_spec.rb +73 -0
- data/spec/growl_output_spec.rb +33 -0
- data/spec/jspec_task_spec.rb +47 -0
- data/spec/list_task_spec.rb +58 -0
- data/spec/modified_filter_spec.rb +47 -0
- data/spec/poller_spec.rb +28 -0
- data/spec/shell_output_spec.rb +25 -0
- data/spec/task_manager_spec.rb +64 -0
- metadata +70 -19
- data/loris.gemspec +0 -33
data/.autotest
ADDED
@@ -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'
|
data/.gitignore
ADDED
@@ -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
|
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: features
|
File without changes
|
@@ -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,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
|
data/lib/file_filter.rb
ADDED
data/lib/file_finder.rb
ADDED
@@ -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
|
data/lib/icons/error.png
ADDED
Binary file
|
Binary file
|
data/lib/icons/info.png
ADDED
Binary file
|
Binary file
|
Binary file
|
data/lib/loris.rb
ADDED
@@ -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
|