diff --git a/.rspec b/.rspec index 83e16f8..7e3eb5c 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ --color --require spec_helper +--format doc diff --git a/.travis.yml b/.travis.yml index b2c8cf4..7f2e630 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ rvm: matrix: allow_failures: - rvm: rbx - -notifications: - recipients: - - remy@rymai.me +branches: + only: + - master diff --git a/Guardfile b/Guardfile index 19e969a..7aa8995 100644 --- a/Guardfile +++ b/Guardfile @@ -3,8 +3,17 @@ guard :bundler do watch(%r{^.+\.gemspec$}) end -guard :rspec do - watch(%r{^spec/.+_spec\.rb$}) - watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } - watch('spec/spec_helper.rb') { 'spec' } +guard :rspec, cmd: "bundle exec rspec" do + require "ostruct" + + rspec = OpenStruct.new + rspec.spec_dir = "spec" + rspec.spec = ->(m) { "#{rspec.spec_dir}/#{m}_spec.rb" } + rspec.spec_helper = "#{rspec.spec_dir}/spec_helper.rb" + rspec.spec_files = %r{^#{rspec.spec_dir}/.+_spec\.rb$} + rspec.lib_files = %r{^lib/(.+)\.rb$} + + watch(rspec.spec_files) + watch(rspec.lib_files) { |m| rspec.spec.(m[1]) } + watch(rspec.spec_helper) { rspec.spec_dir } end diff --git a/guard-bundler.gemspec b/guard-bundler.gemspec index 09f9096..22d72b0 100644 --- a/guard-bundler.gemspec +++ b/guard-bundler.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.9.2' s.add_dependency 'guard', '~> 2.2' - s.add_dependency 'guard-compat', '~> 1.0' + s.add_dependency 'guard-compat', '~> 1.1' s.add_dependency 'bundler', '~> 1.0' s.files = Dir.glob('{lib}/**/*') + %w[LICENSE README.md] diff --git a/lib/guard/bundler/templates/Guardfile b/lib/guard/bundler/templates/Guardfile index 80158fe..eeb89be 100644 --- a/lib/guard/bundler/templates/Guardfile +++ b/lib/guard/bundler/templates/Guardfile @@ -1,5 +1,11 @@ guard :bundler do - watch('Gemfile') - # Uncomment next line if your Gemfile contains the `gemspec' command. - # watch(/^.+\.gemspec/) + require 'guard/bundler' + require 'guard/bundler/verify' + helper = Guard::Bundler::Verify.new + + files = ['Gemfile'] + files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) } + + # Assume files are symlinked from somewhere + files.each { |file| watch(helper.real_path(file)) } end diff --git a/lib/guard/bundler/verify.rb b/lib/guard/bundler/verify.rb new file mode 100644 index 0000000..2b6047a --- /dev/null +++ b/lib/guard/bundler/verify.rb @@ -0,0 +1,54 @@ +require 'pathname' + +module Guard + class Bundler < Plugin + class Verify + if Gem.win_platform? + SYMLINK_NEEDED = <<-EOS + Error: Guard will not detect changes to your Gemfile! + + Solution: move the Gemfile to a watched directory and symlink it, so that + 'Gemfile' is symlinked e.g. to config/Gemfile. + + (See: https://github.com/guard/guard/wiki/Optimizing-for-large-projects) + + EOS + else + SYMLINK_NEEDED = <<-EOS + Error: Guard will not detect changes to your Gemfile! + + Solution: move the Gemfile to a watched directory and symlink it back. + + Example: + + $ mkdir config + $ git mv Gemfile config # use just 'mv' if this doesn't work + $ ln -s config/Gemfile . + + and add config to the `directories` statement in your Guardfile. + + (See: https://github.com/guard/guard/wiki/Optimizing-for-large-projects) + EOS + end + + def verify!(file) + watchdirs = Guard::Compat.watched_directories + + gemfile = Pathname.new(file) + config_dir = gemfile.realpath.dirname + return if watchdirs.include?(config_dir) + + Compat::UI.error SYMLINK_NEEDED + end + + def real_path(file) + verify!(file) + Pathname.new(file).realpath.relative_path_from(Pathname.pwd).to_s + end + + def uses_gemspec?(file) + IO.read(file).lines.map(&:strip).grep(/^gemspec$/).any? + end + end + end +end diff --git a/spec/guard/bundler/template_spec.rb b/spec/guard/bundler/template_spec.rb new file mode 100644 index 0000000..2b5e7fd --- /dev/null +++ b/spec/guard/bundler/template_spec.rb @@ -0,0 +1,74 @@ +require "guard/compat/test/template" + +require "guard/bundler" + +# This is included by the template +require "guard/bundler/verify" + +module Guard + RSpec.describe Bundler do + describe "template" do + subject { Compat::Test::Template.new(described_class) } + + let(:helper) { instance_double(Bundler::Verify) } + + before do + allow(Bundler::Verify).to receive(:new).and_return(helper) + allow(helper).to receive(:uses_gemspec?).and_return(true) + + allow(helper).to receive(:real_path).with('Gemfile'). + and_return('Gemfile') + + allow(Dir).to receive(:[]).and_return(%w(guard-foo.gemspec)) + + allow(helper).to receive(:real_path).with('guard-foo.gemspec'). + and_return('guard-foo.gemspec') + end + + context "when gemspec is used" do + before do + allow(helper).to receive(:uses_gemspec?).with('Gemfile'). + and_return(true) + end + + it "watches the gemspec" do + expect(subject.changed('config/guard-foo.gemspec')). + to eq(%w(config/guard-foo.gemspec)) + end + end + + context "when gemspec is not used" do + before do + allow(helper).to receive(:uses_gemspec?).with('Gemfile'). + and_return(false) + end + + it "does not watch the gemspec" do + expect(subject.changed('guard-foo.gemspec')).to eq(%w()) + end + end + + context "when Gemfile is not a symlink" do + before do + expect(helper).to receive(:real_path).with('Gemfile'). + and_return('Gemfile') + end + + it "watches Gemfile" do + expect(subject.changed('Gemfile')).to eq(%w(Gemfile)) + end + end + + context "with a symlinked Gemfile" do + before do + expect(helper).to receive(:real_path).with('Gemfile'). + and_return('config/Gemfile') + end + + it "watches the real Gemfile" do + expect(subject.changed('config/Gemfile')).to eq(%w(config/Gemfile)) + end + end + end + end +end diff --git a/spec/guard/bundler/verify_spec.rb b/spec/guard/bundler/verify_spec.rb new file mode 100644 index 0000000..ae2ebf0 --- /dev/null +++ b/spec/guard/bundler/verify_spec.rb @@ -0,0 +1,78 @@ +require "guard/bundler/verify" + +RSpec.describe Guard::Bundler::Verify do + describe '#uses_gemspec?' do + context "when gemspec is used" do + before do + allow(IO).to receive(:read).with('foo').and_return("\n\n gemspec \n") + end + + it "detects used gemspec" do + expect(subject.uses_gemspec?('foo')).to be_truthy + end + end + + context "when gemspec is not used" do + before do + allow(IO).to receive(:read).with('foo').and_return("gem 'rspec'\n") + end + + it "does not detects gemspec usage" do + expect(subject.uses_gemspec?('foo')).to be_falsey + end + end + end + + describe '#real_path' do + context "when Gemfile is not watched" do + before do + allow(Guard::Compat).to receive(:watched_directories).and_return(['/foo']) + end + + it 'shows error' do + expect(Guard::Compat::UI).to receive(:error). + with(/Guard will not detect changes/) + subject.real_path('Gemfile') + end + end + + context "when Gemfile is watched along with whole project" do + before do + allow(Guard::Compat).to receive(:watched_directories). + and_return([Pathname.pwd]) + end + + it 'shows no error' do + expect(Guard::Compat::UI).to_not receive(:error) + subject.real_path('Gemfile') + end + + it 'returns relative path to file' do + expect(subject.real_path('Gemfile')).to eq('Gemfile') + end + end + + context "when subdir is watched" do + before do + allow(Guard::Compat).to receive(:watched_directories). + and_return([Pathname.pwd + 'foo']) + end + + context "when Gemfile is symlinked" do + before do + allow_any_instance_of(Pathname).to receive(:realpath). + and_return(Pathname.pwd + 'foo/Gemfile') + end + + it "returns the relative real path" do + expect(subject.real_path('Gemfile')).to eq('foo/Gemfile') + end + + it 'shows no error' do + expect(Guard::Compat::UI).to_not receive(:error) + subject.real_path('Gemfile') + end + end + end + end +end