guard 0.1.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Thibaud Guillaume-Gentil
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Guard
2
+
3
+ Documentation is coming.
data/bin/guard ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'guard'
4
+ require 'guard/cli'
5
+
6
+ Guard::CLI.start
data/bin/inotify_watch ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'rb-inotify'
4
+
5
+ folders = Array.new
6
+ notifier = INotify::Notifier.new
7
+
8
+ notifier.watch(ARGV.first || '.', :modify, :recursive) do |event|
9
+ dir = File.expand_path(File.dirname(event.absolute_name)) + '/'
10
+ if !folders.include?(dir)
11
+ folders << dir
12
+ end
13
+ end
14
+
15
+ while true do
16
+ notifier.process
17
+
18
+ if !folders.empty?
19
+ $stdout.puts folders.join(' ')
20
+ $stdout.flush
21
+ folders.clear
22
+ end
23
+
24
+ sleep(0.1)
25
+ end
data/ext/extconf.rb ADDED
@@ -0,0 +1,18 @@
1
+ # Workaround to make Rubygems believe it builds a native gem
2
+ require 'mkmf'
3
+ create_makefile('none')
4
+
5
+ if `uname -s`.chomp == 'Darwin'
6
+ gem_root = File.expand_path(File.join('..'))
7
+ darwin_verion = `uname -r`.to_i
8
+ sdk_verion = { 9 => '10.5', 10 => '10.6', 11 => '10.7' }[darwin_verion]
9
+
10
+ raise "Darwin #{darwin_verion} is not supported" unless sdk_verion
11
+
12
+ # Compile the actual fsevent_watch binary
13
+ system("CFLAGS='-isysroot /Developer/SDKs/MacOSX#{sdk_verion}.sdk -mmacosx-version-min=#{sdk_verion}' /usr/bin/gcc -framework CoreServices -o '#{gem_root}/bin/fsevent_watch' fsevent/fsevent_watch.c")
14
+
15
+ unless File.executable?("#{gem_root}/bin/fsevent_watch")
16
+ raise "Compilation of fsevent_watch failed (see README)"
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ #include <CoreServices/CoreServices.h>
2
+
3
+ void callback(ConstFSEventStreamRef streamRef,
4
+ void *clientCallBackInfo,
5
+ size_t numEvents,
6
+ void *eventPaths,
7
+ const FSEventStreamEventFlags eventFlags[],
8
+ const FSEventStreamEventId eventIds[]
9
+ ) {
10
+ // Print modified dirs
11
+ int i;
12
+ char **paths = eventPaths;
13
+ for (i = 0; i < numEvents; i++) {
14
+ printf("%s", paths[i]);
15
+ printf(" ");
16
+ }
17
+ printf("\n");
18
+ fflush(stdout);
19
+ }
20
+
21
+ int main (int argc, const char * argv[]) {
22
+ // Create event stream
23
+ CFStringRef pathToWatch = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8);
24
+ CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&pathToWatch, 1, NULL);
25
+ void *callbackInfo = NULL;
26
+ FSEventStreamRef stream;
27
+ CFAbsoluteTime latency = 0.1;
28
+ stream = FSEventStreamCreate(
29
+ kCFAllocatorDefault,
30
+ callback,
31
+ callbackInfo,
32
+ pathsToWatch,
33
+ kFSEventStreamEventIdSinceNow,
34
+ latency,
35
+ kFSEventStreamCreateFlagNone
36
+ );
37
+
38
+ // Add stream to run loop
39
+ FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
40
+ FSEventStreamStart(stream);
41
+ CFRunLoopRun();
42
+
43
+ return 2;
44
+ }
data/images/failed.png ADDED
Binary file
Binary file
Binary file
data/lib/guard.rb ADDED
@@ -0,0 +1,49 @@
1
+ module Guard
2
+
3
+ autoload :UI, 'guard/ui'
4
+ autoload :Dsl, 'guard/dsl'
5
+ autoload :Interactor, 'guard/interactor'
6
+ autoload :Listener, 'guard/listener'
7
+ autoload :Watcher, 'guard/watcher'
8
+ autoload :Notifier, 'guard/notifier'
9
+
10
+ class << self
11
+ attr_accessor :options, :guards, :listener
12
+
13
+ def start(options = {})
14
+ @options = options
15
+ @listener = Listener.new
16
+ @guards = []
17
+
18
+ Dsl.evaluate_guardfile
19
+ Interactor.init_signal_traps
20
+
21
+ listener.on_change do |files|
22
+ run do
23
+ guards.each do |guard|
24
+ paths = Watcher.match_files(guard, files)
25
+ guard.run_on_change(paths) unless paths.empty?
26
+ end
27
+ end
28
+ end
29
+
30
+ UI.info "Guard is now watching at '#{Dir.pwd}'"
31
+ guards.each(&:start)
32
+ listener.start
33
+ end
34
+
35
+ def add_guard(name, watchers = [], options = {})
36
+ require "guard/#{name.downcase}"
37
+ guard_class = ObjectSpace.each_object(Class).detect { |c| c.to_s.downcase.match "^guard::#{name.downcase}" }
38
+ @guards << guard_class.new(watchers, options)
39
+ end
40
+
41
+ def run
42
+ listener.stop
43
+ yield
44
+ listener.start
45
+ end
46
+
47
+ end
48
+
49
+ end
data/lib/guard/cli.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'thor'
2
+ require 'guard/version'
3
+
4
+ module Guard
5
+ class CLI < Thor
6
+ default_task :start
7
+
8
+ desc "start", "Starts guard"
9
+ method_option :clear, :type => :boolean, :default => false, :aliases => '-c', :banner => "Auto clear shell after each change"
10
+ def start
11
+ Guard.start(options)
12
+ end
13
+
14
+ desc "version", "Prints the guard's version information"
15
+ def version
16
+ Guard::UI.info "Guard version #{Guard::VERSION}"
17
+ end
18
+ map %w(-v --version) => :version
19
+ end
20
+ end
data/lib/guard/dsl.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Guard
2
+ class Dsl
3
+
4
+ def self.evaluate_guardfile
5
+ guardfile = "#{Dir.pwd}/Guardfile"
6
+ dsl = new
7
+ dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1)
8
+ rescue
9
+ UI.error "Guardfile not found or invalid"
10
+ exit 1
11
+ end
12
+
13
+ def guard(name, options = {}, &definition)
14
+ @watchers = []
15
+ definition.call
16
+ Guard.add_guard(name, @watchers, options)
17
+ end
18
+
19
+ def watch(pattern, &action)
20
+ @watchers << Guard::Watcher.new(pattern, action)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ module Guard
2
+ class Guard
3
+ attr_accessor :watchers, :options
4
+
5
+ def initialize(watchers = [], options = {})
6
+ @watchers, @options = watchers, options
7
+ end
8
+
9
+ # ================
10
+ # = Guard method =
11
+ # ================
12
+
13
+ def start
14
+ true
15
+ end
16
+
17
+ def stop
18
+ true
19
+ end
20
+
21
+ def reload
22
+ true
23
+ end
24
+
25
+ def run_all
26
+ true
27
+ end
28
+
29
+ def run_on_change(paths)
30
+ true
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ module Guard
2
+ module Interactor
3
+
4
+ def self.init_signal_traps
5
+ # Run all (Ctrl-\)
6
+ Signal.trap('QUIT') do
7
+ ::Guard.run do
8
+ ::Guard.guards.each(&:run_all)
9
+ end
10
+ end
11
+
12
+ # Stop (Ctrl-C)
13
+ Signal.trap('INT') do
14
+ ::Guard.listener.stop
15
+ if ::Guard.guards.all?(&:stop)
16
+ UI.info "Bye bye...", :reset => true, :clear => false
17
+ abort("\n")
18
+ else
19
+ ::Guard.listener.start
20
+ end
21
+ end
22
+
23
+ # Reload (Ctrl-Z)
24
+ Signal.trap('TSTP') do
25
+ ::Guard.run do
26
+ ::Guard.guards.each(&:reload)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ require 'sys/uname'
2
+
3
+ module Guard
4
+ class Listener
5
+ attr_reader :last_event, :callback, :pipe
6
+
7
+ def initialize
8
+ update_last_event
9
+ end
10
+
11
+ def on_change(&block)
12
+ @callback = block
13
+ end
14
+
15
+ def start
16
+ @pipe = case Sys::Uname.sysname
17
+ when 'Darwin'
18
+ IO.popen("#{bin_path}/fsevent_watch .")
19
+ when 'Linux'
20
+ IO.popen("#{bin_path}/inotify_watch .")
21
+ end
22
+ watch_change
23
+ end
24
+
25
+ def stop
26
+ Process.kill("HUP", pipe.pid) if pipe
27
+ end
28
+
29
+ private
30
+
31
+ def watch_change
32
+ while !pipe.eof?
33
+ if line = pipe.readline
34
+ modified_dirs = line.split(" ")
35
+ files = modified_files(modified_dirs)
36
+ update_last_event
37
+ callback.call(files)
38
+ end
39
+ end
40
+ end
41
+
42
+ def modified_files(dirs)
43
+ files = potentially_modified_files(dirs).select { |file| recent_file?(file) }
44
+ files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
45
+ end
46
+
47
+ def potentially_modified_files(dirs)
48
+ Dir.glob(dirs.map { |dir| "#{dir}*" })
49
+ end
50
+
51
+ def recent_file?(file)
52
+ File.mtime(file) >= last_event
53
+ end
54
+
55
+ def update_last_event
56
+ @last_event = Time.now
57
+ end
58
+
59
+ def bin_path
60
+ File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'bin'))
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,45 @@
1
+ require 'sys/uname'
2
+ require 'pathname'
3
+
4
+ case Sys::Uname.sysname
5
+ when 'Darwin'
6
+ require 'growl'
7
+ when 'Linux'
8
+ require 'libnotify'
9
+ end
10
+
11
+ module Guard
12
+ module Notifier
13
+
14
+ def self.notify(message, options = {})
15
+ unless ENV["GUARD_ENV"] == "test"
16
+ image = options[:image] || :success
17
+ title = options[:title] || "Guard"
18
+ case Sys::Uname.sysname
19
+ when 'Darwin'
20
+ Growl.notify message, :title => title, :icon => image_path(image), :name => "Guard"
21
+ when 'Linux'
22
+ Libnotify.show :body => message, :summary => title, :icon_path => image_path(image)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def self.image_path(image)
30
+ images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
31
+ case image
32
+ when :failed
33
+ images_path.join("failed.png").to_s
34
+ when :pending
35
+ images_path.join("pending.png").to_s
36
+ when :success
37
+ images_path.join("success.png").to_s
38
+ else
39
+ # path given
40
+ image
41
+ end
42
+ end
43
+
44
+ end
45
+ end
data/lib/guard/ui.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Guard
2
+ module UI
3
+ class << self
4
+
5
+ def info(message, options = {})
6
+ unless ENV["GUARD_ENV"] == "test"
7
+ reset_line if options[:reset]
8
+ clear if options.key?(:clear) ? options[:clear] : ::Guard.options[:clear]
9
+ puts reset_color(message) if message != ''
10
+ end
11
+ end
12
+
13
+ def error(message)
14
+ puts "ERROR: #{message}"
15
+ end
16
+
17
+ def reset_line
18
+ print "\r\e "
19
+ end
20
+
21
+ private
22
+
23
+ def clear
24
+ system("clear;")
25
+ end
26
+
27
+ def reset_color(text)
28
+ color(text, "\e[0m")
29
+ end
30
+
31
+ def color(text, color_code)
32
+ "#{color_code}#{text}\e[0m"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Guard
2
+ VERSION = "0.1.0.beta.1"
3
+ end
@@ -0,0 +1,35 @@
1
+ module Guard
2
+ class Watcher
3
+ attr_accessor :pattern, :action
4
+
5
+ def initialize(pattern, action = nil)
6
+ @pattern, @action = pattern, action
7
+ end
8
+
9
+ def self.match_files(guard, files)
10
+ guard.watchers.inject([]) do |paths, watcher|
11
+ files.each do |file|
12
+ if matches = file.match(watcher.pattern)
13
+ if watcher.action
14
+ begin
15
+ case watcher.action.arity
16
+ when -1
17
+ result = watcher.action.call
18
+ when 1
19
+ result = watcher.action.call(matches)
20
+ end
21
+ rescue
22
+ UI.info "Problem with watch action"
23
+ end
24
+ paths << result if result.is_a?(String) && result != ''
25
+ else
26
+ paths << matches[0]
27
+ end
28
+ end
29
+ end
30
+ paths
31
+ end
32
+ end
33
+
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guard
3
+ version: !ruby/object:Gem::Version
4
+ hash: 62196401
5
+ prerelease: true
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ - beta
11
+ - 1
12
+ version: 0.1.0.beta.1
13
+ platform: ruby
14
+ authors:
15
+ - Thibaud Guillaume-Gentil
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-10-03 00:00:00 +02:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: bundler
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ hash: 21
32
+ segments:
33
+ - 1
34
+ - 0
35
+ - 1
36
+ version: 1.0.1
37
+ type: :development
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: rspec
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ hash: 62196431
48
+ segments:
49
+ - 2
50
+ - 0
51
+ - 0
52
+ - beta
53
+ - 22
54
+ version: 2.0.0.beta.22
55
+ type: :development
56
+ version_requirements: *id002
57
+ - !ruby/object:Gem::Dependency
58
+ name: thor
59
+ prerelease: false
60
+ requirement: &id003 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ hash: 35
66
+ segments:
67
+ - 0
68
+ - 14
69
+ - 2
70
+ version: 0.14.2
71
+ type: :runtime
72
+ version_requirements: *id003
73
+ - !ruby/object:Gem::Dependency
74
+ name: sys-uname
75
+ prerelease: false
76
+ requirement: &id004 !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ~>
80
+ - !ruby/object:Gem::Version
81
+ hash: 55
82
+ segments:
83
+ - 0
84
+ - 8
85
+ - 4
86
+ version: 0.8.4
87
+ type: :runtime
88
+ version_requirements: *id004
89
+ - !ruby/object:Gem::Dependency
90
+ name: growl
91
+ prerelease: false
92
+ requirement: &id005 !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ hash: 17
98
+ segments:
99
+ - 1
100
+ - 0
101
+ - 3
102
+ version: 1.0.3
103
+ type: :runtime
104
+ version_requirements: *id005
105
+ - !ruby/object:Gem::Dependency
106
+ name: rb-inotify
107
+ prerelease: false
108
+ requirement: &id006 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ type: :runtime
118
+ version_requirements: *id006
119
+ - !ruby/object:Gem::Dependency
120
+ name: libnotify
121
+ prerelease: false
122
+ requirement: &id007 !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ~>
126
+ - !ruby/object:Gem::Version
127
+ hash: 29
128
+ segments:
129
+ - 0
130
+ - 1
131
+ - 3
132
+ version: 0.1.3
133
+ type: :runtime
134
+ version_requirements: *id007
135
+ description: Guard is a command line tool to easly manage script launch when your files change
136
+ email:
137
+ - thibaud@thibaud.me
138
+ executables:
139
+ - guard
140
+ extensions:
141
+ - ext/extconf.rb
142
+ extra_rdoc_files: []
143
+
144
+ files:
145
+ - bin/guard
146
+ - bin/inotify_watch
147
+ - images/failed.png
148
+ - images/pending.png
149
+ - images/success.png
150
+ - lib/guard/cli.rb
151
+ - lib/guard/dsl.rb
152
+ - lib/guard/guard.rb
153
+ - lib/guard/interactor.rb
154
+ - lib/guard/listener.rb
155
+ - lib/guard/notifier.rb
156
+ - lib/guard/ui.rb
157
+ - lib/guard/version.rb
158
+ - lib/guard/watcher.rb
159
+ - lib/guard.rb
160
+ - ext/extconf.rb
161
+ - ext/fsevent/fsevent_watch.c
162
+ - LICENSE
163
+ - README.rdoc
164
+ has_rdoc: true
165
+ homepage: http://rubygems.org/gems/guard
166
+ licenses: []
167
+
168
+ post_install_message:
169
+ rdoc_options: []
170
+
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ none: false
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ hash: 3
179
+ segments:
180
+ - 0
181
+ version: "0"
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ">"
186
+ - !ruby/object:Gem::Version
187
+ hash: 25
188
+ segments:
189
+ - 1
190
+ - 3
191
+ - 1
192
+ version: 1.3.1
193
+ requirements: []
194
+
195
+ rubyforge_project: guard
196
+ rubygems_version: 1.3.7
197
+ signing_key:
198
+ specification_version: 3
199
+ summary: Guard keep an eye on your files event
200
+ test_files: []
201
+