├── .gitignore ├── .rspec ├── Gemfile ├── LICENSE ├── Rakefile ├── Readme.md ├── guard-shell.gemspec ├── lib └── guard │ ├── shell.rb │ └── shell │ ├── templates │ └── Guardfile │ └── version.rb └── spec ├── lib └── guard │ └── shell_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | Gemfile.lock 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | group :development, :test do 5 | gem 'rake' 6 | gem 'rspec', '~> 3.1' 7 | end 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Joshua Hawxwell 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. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require 'rspec/core/rake_task' 4 | RSpec::Core::RakeTask.new(:spec) 5 | task default: :spec 6 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Guard::Shell 2 | 3 | > Please see for the currently maintained repo. 4 | 5 | This little guard allows you to run shell commands when files are altered. 6 | 7 | 8 | ## Install 9 | 10 | Make sure you have [guard](http://github.com/guard/guard) installed. 11 | 12 | Install the gem with: 13 | 14 | gem install guard-shell 15 | 16 | Or add it to your Gemfile: 17 | 18 | gem 'guard-shell' 19 | 20 | And then add a basic setup to your Guardfile: 21 | 22 | guard init shell 23 | 24 | 25 | ## Usage 26 | 27 | If you can do something in your shell, or in ruby, you can do it when a file changes 28 | with guard-shell. It simply executes the block passed to watch if a change is 29 | detected, and if anything is returned from the block it will be printed. For example 30 | 31 | ``` ruby 32 | guard :shell do 33 | watch /.*/ do |m| 34 | m[0] + " has changed." 35 | end 36 | end 37 | ``` 38 | 39 | will simply print a message telling you a file has been changed when it is changed. 40 | This admittedly isn't a very useful example, but you hopefully get the idea. To run 41 | everything on start pass `:all_on_start` to `#guard`, 42 | 43 | ``` ruby 44 | guard :shell, :all_on_start => true do 45 | # ... 46 | end 47 | ``` 48 | 49 | There is also a shortcut for easily creating notifications, 50 | 51 | ``` ruby 52 | guard :shell do 53 | watch /.*/ do |m| 54 | n m[0], 'File Changed' 55 | end 56 | end 57 | ``` 58 | 59 | `#n` takes up to three arguments; the first is the body of the message, here the path 60 | of the changed file; the second is the title for the notification; and the third is 61 | the image to use. There are three (four counting `nil` the default) different images 62 | that can be specified `:success`, `:pending` and `:failed`. 63 | 64 | 65 | ### Examples 66 | 67 | #### Saying the Name of the File You Changed and Displaying a Notification 68 | 69 | ``` ruby 70 | guard :shell do 71 | watch /(.*)/ do |m| 72 | n m[0], 'Changed' 73 | `say -v cello #{m[0]}` 74 | end 75 | end 76 | ``` 77 | 78 | #### Rebuilding LaTeX 79 | 80 | ``` ruby 81 | guard :shell, :all_on_start => true do 82 | watch /^([^\/]*)\.tex/ do |m| 83 | `pdflatex -shell-escape #{m[0]}` 84 | `rm #{m[1]}.log` 85 | 86 | count = `texcount -inc -nc -1 #{m[0]}`.split('+').first 87 | msg = "Built #{m[1]}.pdf (#{count} words)" 88 | n msg, 'LaTeX' 89 | "-> #{msg}" 90 | end 91 | end 92 | ``` 93 | 94 | #### Check Syntax of a Ruby File 95 | 96 | ``` ruby 97 | guard :shell do 98 | watch /.*\.rb$/ do |m| 99 | if system("ruby -c #{m[0]}") 100 | n "#{m[0]} is correct", 'Ruby Syntax', :success 101 | else 102 | n "#{m[0]} is incorrect", 'Ruby Syntax', :failed 103 | end 104 | end 105 | end 106 | ``` 107 | -------------------------------------------------------------------------------- /guard-shell.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/guard/shell/version", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "guard-shell" 6 | s.author = "Joshua Hawxwell" 7 | s.email = "m@hawx.me" 8 | s.summary = "Guard gem for running shell commands" 9 | s.homepage = "http://github.com/hawx/guard-shell" 10 | s.license = 'MIT' 11 | s.version = Guard::ShellVersion::VERSION 12 | 13 | s.description = <<-DESC 14 | Guard::Shell automatically runs shell commands when watched files are 15 | modified. 16 | DESC 17 | 18 | s.add_dependency 'guard', '>= 2.0.0' 19 | s.add_dependency 'guard-compat', '~> 1.0' 20 | 21 | s.files = %w(Readme.md LICENSE) 22 | s.files += Dir["{lib}/**/*"] 23 | end 24 | -------------------------------------------------------------------------------- /lib/guard/shell.rb: -------------------------------------------------------------------------------- 1 | require 'guard/compat/plugin' 2 | require 'guard/shell/version' 3 | 4 | module Guard 5 | class Shell < Plugin 6 | 7 | # Calls #run_all if the :all_on_start option is present. 8 | def start 9 | run_all if options[:all_on_start] 10 | end 11 | 12 | # Defined only to make callback(:stop_begin) and callback(:stop_end) working 13 | def stop 14 | end 15 | 16 | # Call #run_on_change for all files which match this guard. 17 | def run_all 18 | run_on_modifications(Compat.matching_files(self, Dir.glob('{,**/}*{,.*}'))) 19 | end 20 | 21 | # Print the result of the command(s), if there are results to be printed. 22 | def run_on_modifications(res) 23 | $stdout.puts res if res 24 | end 25 | 26 | end 27 | 28 | class Dsl 29 | # Easy method to display a notification 30 | def n(msg, title='', image=nil) 31 | Compat::UI.notify(msg, :title => title, :image => image) 32 | end 33 | 34 | # Eager prints the result for stdout and stderr as it would be written when 35 | # running the command from the terminal. This is useful for long running 36 | # tasks. 37 | def eager(command) 38 | require 'pty' 39 | 40 | begin 41 | PTY.spawn command do |r, w, pid| 42 | begin 43 | $stdout.puts 44 | r.each {|line| print line } 45 | rescue Errno::EIO 46 | # the process has finished 47 | end 48 | end 49 | rescue PTY::ChildExited 50 | $stdout.puts "The child process exited!" 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/guard/shell/templates/Guardfile: -------------------------------------------------------------------------------- 1 | # Add files and commands to this file, like the example: 2 | # watch(%r{file/path}) { `command(s)` } 3 | # 4 | guard :shell do 5 | watch(/(.*).txt/) {|m| `tail #{m[0]}` } 6 | end 7 | -------------------------------------------------------------------------------- /lib/guard/shell/version.rb: -------------------------------------------------------------------------------- 1 | module Guard 2 | module ShellVersion 3 | VERSION = '0.7.1' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/lib/guard/shell_spec.rb: -------------------------------------------------------------------------------- 1 | require "guard/compat/test/helper" 2 | require "guard/shell" 3 | 4 | RSpec.describe Guard::Shell do 5 | describe "#start" do 6 | it "works" do 7 | subject.start 8 | end 9 | end 10 | 11 | describe "#stop" do 12 | it "works" do 13 | subject.stop 14 | end 15 | end 16 | 17 | describe "#run_all" do 18 | before do 19 | allow(Dir).to receive(:glob).and_return(%w(foo bar)) 20 | allow(Guard::Compat).to receive(:matching_files).with(subject, %w(foo bar)).and_return(%w(bar)) 21 | end 22 | 23 | it "delegates to run_on_modifications" do 24 | expect($stdout).to receive(:puts).with(%w(bar)) 25 | subject.run_all 26 | end 27 | end 28 | 29 | describe "#run_on_modifications" do 30 | it "outputs to the screen" do 31 | expect($stdout).to receive(:puts).with(%w(bar)) 32 | subject.run_on_modifications(%w(bar)) 33 | end 34 | end 35 | end 36 | 37 | RSpec.describe Guard::Dsl do 38 | describe '#n' do 39 | it "uses Guard to notify" do 40 | expect(Guard::Compat::UI).to receive(:notify).with('foo', title: 'bar', image: 'baz') 41 | subject.n('foo', 'bar', 'baz') 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.expect_with :rspec do |expectations| 3 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 4 | end 5 | 6 | config.mock_with :rspec do |mocks| 7 | mocks.verify_partial_doubles = true 8 | end 9 | 10 | config.filter_run :focus 11 | config.run_all_when_everything_filtered = true 12 | 13 | config.disable_monkey_patching! 14 | 15 | #config.warnings = true 16 | 17 | if config.files_to_run.one? 18 | config.default_formatter = 'doc' 19 | end 20 | 21 | #config.profile_examples = 10 22 | 23 | config.order = :random 24 | 25 | Kernel.srand config.seed 26 | end 27 | --------------------------------------------------------------------------------