loris 0.0.12
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/.gitignore +2 -0
- data/README.rdoc +9 -0
- data/Rakefile +41 -0
- data/TODO +21 -0
- data/VERSION +1 -0
- data/bin/loris +15 -0
- data/cucumber.yml +1 -0
- data/examples/self_test/jsl.conf +1 -0
- data/examples/self_test/spec/spec.rhino.js +6 -0
- data/features/javascript_lint.feature +34 -0
- data/features/run.feature +36 -0
- data/features/step_definitons/all.rb +69 -0
- data/features/support/env.rb +145 -0
- data/lib/always_continuer.rb +7 -0
- data/lib/browser_finder.rb +23 -0
- data/lib/file_actioner.rb +16 -0
- data/lib/file_finder.rb +31 -0
- data/lib/filters/ends_with_filter.rb +17 -0
- data/lib/filters/extension_filter.rb +20 -0
- data/lib/filters/file_filter.rb +14 -0
- data/lib/filters/modified_filter.rb +21 -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/js-test-driver/JsTestDriver-1.1.jar +0 -0
- data/lib/js-test-driver/plugins/coverage-1.1.jar +0 -0
- data/lib/loris.rb +170 -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/outputs/unix_console_clearing_output.rb +13 -0
- data/lib/outputs/windows_console_clearing_output.rb +13 -0
- data/lib/pinger.rb +23 -0
- data/lib/poller.rb +16 -0
- data/lib/sleep_waiter.rb +11 -0
- data/lib/task_manager.rb +45 -0
- data/lib/tasks/command_line_task.rb +28 -0
- data/lib/tasks/javascript_lint/javascript_lint_parser.rb +45 -0
- data/lib/tasks/javascript_lint/javascript_lint_runner.rb +25 -0
- data/lib/tasks/js_test_driver/js_test_driver_config.rb +22 -0
- data/lib/tasks/js_test_driver/js_test_driver_parser.rb +28 -0
- data/lib/tasks/js_test_driver/js_test_driver_runner.rb +28 -0
- data/lib/tasks/js_test_driver/js_test_driver_server.rb +38 -0
- data/lib/tasks/jspec/jspec_parser.rb +30 -0
- data/lib/tasks/jspec/jspec_runner.rb +25 -0
- data/lib/tasks/list_task.rb +34 -0
- data/lib/tasks/rspec/rspec_parser.rb +25 -0
- data/lib/tasks/rspec/rspec_runner.rb +25 -0
- data/lib/unix_process.rb +7 -0
- data/lib/windows_process.rb +12 -0
- data/loris.gemspec +133 -0
- data/loris.tmproj +232 -0
- data/spec/file_actioner_spec.rb +41 -0
- data/spec/file_finder_spec.rb +73 -0
- data/spec/filters/ends_with_filter_spec.rb +26 -0
- data/spec/filters/file_filter_spec.rb +28 -0
- data/spec/filters/modified_filter_spec.rb +47 -0
- data/spec/growl_output_spec.rb +33 -0
- data/spec/list_task_spec.rb +58 -0
- data/spec/poller_spec.rb +28 -0
- data/spec/shell_output_spec.rb +25 -0
- data/spec/task_manager_spec.rb +64 -0
- data/spec/tasks/javascript_lint/javascript_lint_runner_spec.rb +90 -0
- data/spec/tasks/js_test_driver/js_test_driver_runner_spec.rb +92 -0
- data/spec/tasks/jspec/jspec_parser_spec.rb +28 -0
- data/spec/tasks/jspec/jspec_runner_spec.rb +78 -0
- metadata +174 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
== Autotest for Javascript ==
|
2
|
+
|
3
|
+
Run your javascript unit tests any time a file in your project changes.
|
4
|
+
|
5
|
+
Runs Javascript Lint (if jsl.conf is found)
|
6
|
+
Runs JSpec (if spec/spec.rhino.js is found)
|
7
|
+
Runs JS Test Driver (if jsTestDriver.conf is found)
|
8
|
+
|
9
|
+
Also runs RSpec (if any file ending in _spec.rb is found in the spec directory)
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'cucumber'
|
5
|
+
require 'cucumber/rake/task'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "loris"
|
11
|
+
gem.summary = 'Automatically run javascript unit tests'
|
12
|
+
gem.description = 'Automatically run javascript unit tests'
|
13
|
+
gem.email = "loris@monket.net"
|
14
|
+
gem.homepage = "http://github.com/karl/loris"
|
15
|
+
gem.authors = ["Karl O'Keeffe"]
|
16
|
+
|
17
|
+
gem.add_dependency('visionmedia-bind', [">= 0.2.6"])
|
18
|
+
gem.add_dependency('karl-growl', [">= 1.0.3"])
|
19
|
+
gem.add_dependency('extensions', [">= 0.6.0"])
|
20
|
+
gem.add_dependency('win32-process', [">= 0.6.1"])
|
21
|
+
|
22
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
23
|
+
end
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
task :default => :spec
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
32
|
+
t.spec_files = Dir.glob('spec/**/*_spec.rb')
|
33
|
+
# t.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
37
|
+
t.cucumber_opts = "features --format pretty"
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
data/TODO
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
* Work out what is going on with fork in windows (running second copy of Loris?)
|
2
|
+
|
3
|
+
* Remove JsTestDriver.jar from lib dir
|
4
|
+
* Tidy Windows related if statements
|
5
|
+
* Tidy JSL filename removing
|
6
|
+
|
7
|
+
* javascript lint not installed? (package if not already installed?)
|
8
|
+
* JSpec not installed? (can require as gem)
|
9
|
+
|
10
|
+
* Detect file rename
|
11
|
+
* Detect file deletion/directory rename
|
12
|
+
|
13
|
+
* Factories to create tasks
|
14
|
+
|
15
|
+
* Deal with filesystem exceptions
|
16
|
+
* jspec task (just modified files?)
|
17
|
+
* javascript lint task (just modified files?)
|
18
|
+
* separate thread (create new and kill old)
|
19
|
+
* listen for interrupt
|
20
|
+
* cucumber task
|
21
|
+
* Tidy growl windows code
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.12
|
data/bin/loris
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
|
2
|
+
$:.unshift(File.dirname(__FILE__ + '.rb') + '/../lib') unless $:.include?(File.dirname(__FILE__ + '.rb') + '/../lib')
|
3
|
+
|
4
|
+
require 'loris'
|
5
|
+
begin
|
6
|
+
# The dup is to keep ARGV intact, so that tools like ruby-debug can respawn.
|
7
|
+
failure = Loris::CLI::Main.execute(ARGV.dup)
|
8
|
+
Kernel.exit(failure ? 1 : 0)
|
9
|
+
rescue SystemExit => e
|
10
|
+
Kernel.exit(e.status)
|
11
|
+
rescue Exception => e
|
12
|
+
STDERR.puts("#{e.message} (#{e.class})")
|
13
|
+
STDERR.puts(e.backtrace.join("\n"))
|
14
|
+
Kernel.exit 1
|
15
|
+
end
|
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: features
|
@@ -0,0 +1 @@
|
|
1
|
+
+process tmp/*.js
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Feature Javascript Lint
|
2
|
+
|
3
|
+
Scenario: Javascript Lint is run
|
4
|
+
Given I run loris --debug
|
5
|
+
When I create a file named "example.js" with:
|
6
|
+
"""
|
7
|
+
var foo = function() {
|
8
|
+
x = 'moo'
|
9
|
+
}
|
10
|
+
"""
|
11
|
+
And I wait until loris has finished processing changes
|
12
|
+
Then the Loris output should contain:
|
13
|
+
"""
|
14
|
+
Javascript Lint
|
15
|
+
warning
|
16
|
+
"""
|
17
|
+
And I should not see any errors
|
18
|
+
|
19
|
+
# Scenario: Javascript Lint is run when Loris starts
|
20
|
+
# When I create a file named "example.js" with:
|
21
|
+
# """
|
22
|
+
# var foo = function() {
|
23
|
+
# x = 'moo'
|
24
|
+
# }
|
25
|
+
# """
|
26
|
+
# Given I run loris --debug
|
27
|
+
# And I wait until loris has finished processing changes
|
28
|
+
# Then the Loris output should contain:
|
29
|
+
# """
|
30
|
+
# Javascript Lint
|
31
|
+
# warning
|
32
|
+
# """
|
33
|
+
# And I should not see any errors
|
34
|
+
#
|
@@ -0,0 +1,36 @@
|
|
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
|
@@ -0,0 +1,69 @@
|
|
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 /^the Loris output should contain:$/ do |text|
|
56
|
+
get_background_output.should include text
|
57
|
+
end
|
58
|
+
|
59
|
+
Then /^the Loris output should NOT contain:$/ do |text|
|
60
|
+
get_background_output.should_not include text
|
61
|
+
end
|
62
|
+
|
63
|
+
Then /^I should NOT see "([^\"]*)" in the Loris output$/ do |text|
|
64
|
+
get_background_output.should_not include text
|
65
|
+
end
|
66
|
+
|
67
|
+
Then /^I should not see any errors$/ do
|
68
|
+
get_background_error.strip.should == ""
|
69
|
+
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,23 @@
|
|
1
|
+
require 'win32/registry'
|
2
|
+
|
3
|
+
class BrowserFinder
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@ie = '"C:/Program Files/Internet Explorer/iexplore.exe" "%1"'
|
7
|
+
end
|
8
|
+
|
9
|
+
def getDefault
|
10
|
+
browser = getBrowserFromRegistry()
|
11
|
+
return browser != '' ? browser : @ie
|
12
|
+
end
|
13
|
+
|
14
|
+
def getBrowserFromRegistry
|
15
|
+
|
16
|
+
Win32::Registry::HKEY_CLASSES_ROOT.open('http\shell\open\command') do |reg|
|
17
|
+
reg_typ, reg_val = reg.read('')
|
18
|
+
return reg_val
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
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_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
|
@@ -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,21 @@
|
|
1
|
+
class ModifiedFilter
|
2
|
+
|
3
|
+
def initialize(file_class, last_modified = nil)
|
4
|
+
@file_class = file_class
|
5
|
+
@last_modified = last_modified
|
6
|
+
@modifieds = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter(path)
|
10
|
+
modified = @file_class.mtime(path)
|
11
|
+
@modifieds << modified
|
12
|
+
|
13
|
+
return @last_modified.nil? || modified > @last_modified
|
14
|
+
end
|
15
|
+
|
16
|
+
def complete
|
17
|
+
@last_modified = @modifieds.max
|
18
|
+
@modifieds = []
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/icons/error.png
ADDED
Binary file
|
Binary file
|
data/lib/icons/info.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|