pax_global_header00006660000000000000000000000064145000112310014475gustar00rootroot0000000000000052 comment=18a24afd04c6b46fa0f50d64dfd07bc2e6be5557 guard-2.18.1/000077500000000000000000000000001450001123100126705ustar00rootroot00000000000000guard-2.18.1/.github/000077500000000000000000000000001450001123100142305ustar00rootroot00000000000000guard-2.18.1/.github/release-drafter.yml000066400000000000000000000007461450001123100200270ustar00rootroot00000000000000name-template: "v$NEXT_PATCH_VERSION" tag-template: "v$NEXT_PATCH_VERSION" categories: - title: "⚠️ Breaking Changes" label: "⚠️ Breaking" - title: "✨ New Features" label: "✨ Feature" - title: "🐛 Bug Fixes" label: "🐛 Bug Fix" - title: "📚 Documentation" label: "📚 Docs" - title: "🏠 Housekeeping" label: "🏠 Housekeeping" change-template: "- $TITLE (#$NUMBER) @$AUTHOR" no-changes-template: "- No changes" template: | $CHANGES guard-2.18.1/.github/workflows/000077500000000000000000000000001450001123100162655ustar00rootroot00000000000000guard-2.18.1/.github/workflows/push.yml000066400000000000000000000004331450001123100177670ustar00rootroot00000000000000on: push name: Push jobs: draftRelease: name: Draft Release runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Draft Release uses: toolmantim/release-drafter@v5.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} guard-2.18.1/.gitignore000066400000000000000000000003451450001123100146620ustar00rootroot00000000000000pkg/* doc/* *.gem *.rbc .*.swp *.bak .bundle bundle .yardoc .rbx .rvmrc .rbenv-version .idea Gemfile.lock bin/fsevent_watch_guard Makefile .guard_result coverage/ .ruby-version .tm_properties man/guard man/guard.html /tags /tmp/ guard-2.18.1/.hound.yml000066400000000000000000000000621450001123100146040ustar00rootroot00000000000000ruby: enabled: true config_file: .rubocop.yml guard-2.18.1/.rspec000066400000000000000000000001011450001123100137750ustar00rootroot00000000000000--fail-fast --format documentation --color --require spec_helper guard-2.18.1/.rubocop.yml000066400000000000000000000004741450001123100151470ustar00rootroot00000000000000inherit_from: - vendor/hound/config/style_guides/ruby.yml - .rubocop_todo.yml # Files you want to exclude AllCops: TargetRubyVersion: 2.4 Exclude: - vendor/**/* - db/schema.rb - tmp/aruba/Guardfile - tmp/aruba/Gemfile - Guardfile - config/Guardfile # TODO: put your overrides here: guard-2.18.1/.rubocop_todo.yml000066400000000000000000000053131450001123100161710ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2016-08-06 16:49:52 +0200 using RuboCop version 0.42.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 9 Lint/NestedMethodDefinition: Exclude: - 'lib/guard/commands/all.rb' - 'lib/guard/commands/change.rb' - 'lib/guard/commands/notification.rb' - 'lib/guard/commands/pause.rb' - 'lib/guard/commands/reload.rb' - 'lib/guard/commands/scope.rb' - 'lib/guard/commands/show.rb' - 'lib/guard/jobs/pry_wrapper.rb' # Offense count: 7 # Configuration parameters: Include. # Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb Rails/Exit: Exclude: - 'lib/**/*.rake' - 'lib/guard.rb' - 'lib/guard/cli.rb' - 'lib/tasks/releaser.rb' # Offense count: 39 # Cop supports --auto-correct. Style/MutableConstant: Exclude: - 'Rakefile' - 'lib/guard/dsl.rb' - 'lib/guard/guardfile.rb' - 'lib/guard/guardfile/evaluator.rb' - 'lib/guard/guardfile/generator.rb' - 'lib/guard/internals/debugging.rb' - 'lib/guard/internals/groups.rb' - 'lib/guard/internals/session.rb' - 'lib/guard/jobs/pry_wrapper.rb' - 'lib/guard/notifier.rb' - 'lib/guard/plugin.rb' - 'lib/guard/plugin_util.rb' - 'lib/guard/runner.rb' - 'lib/guard/ui/colors.rb' - 'lib/guard/version.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'lib/guard/dsl_describer.rb' - 'lib/guard/terminal.rb' # Offense count: 1 # Cop supports --auto-correct. Layout/SpaceInsideArrayPercentLiteral: Exclude: - 'guard.gemspec' # Offense count: 41 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: space, no_space Layout/SpaceInsideStringInterpolation: Exclude: - 'lib/guard.rb' - 'lib/guard/cli.rb' - 'lib/guard/commander.rb' - 'lib/guard/commands/all.rb' - 'lib/guard/commands/reload.rb' - 'lib/guard/commands/scope.rb' - 'lib/guard/dsl.rb' - 'lib/guard/guardfile/evaluator.rb' - 'lib/guard/jobs/pry_wrapper.rb' - 'lib/guard/plugin.rb' - 'lib/guard/plugin_util.rb' - 'lib/guard/runner.rb' - 'lib/guard/ui.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Exclude: - 'Rakefile' guard-2.18.1/.travis.yml000066400000000000000000000005231450001123100150010ustar00rootroot00000000000000language: ruby rvm: - 2.4.9 - 2.5.7 - 2.6.5 - 2.7.3 - 3.0.1 - jruby-9.2.8.0 jdk: - openjdk8 env: global: - JRUBY_OPTS="--debug" script: - bundle exec rake bundler_args: --without development cache: bundler addons: code_climate: repo_token: 185a714e42214b9369217d0ddfd5a02d528d9a616a34482119a16c63241d6afd guard-2.18.1/.yardopts000066400000000000000000000003331450001123100145350ustar00rootroot00000000000000--title 'Guard Documentation' --no-private --embed-mixin ClassMethods --exclude lib/guard/internals --readme README.md --markup markdown --markup-provider redcarpet --output-dir ./doc lib/**/*.rb - CHANGELOG.md LICENSE guard-2.18.1/CHANGELOG.md000066400000000000000000000001141450001123100144750ustar00rootroot00000000000000# Moved to [Github releases](https://github.com/guard/guard/releases) page. guard-2.18.1/CONTRIBUTING.md000066400000000000000000000072201450001123100151220ustar00rootroot00000000000000Contribute to Guard =================== File an issue ------------- Please check guard's [GitHub issue tracker](https://github.com/guard/guard/issues) for known issues. Additionally you should check [listen's issue tracker](https://github.com/guard/listen/issues) for issues which affect guard's behaviour; for example, there is currently a nasty [bug preventing listen from watching files inside symlinked directories](https://github.com/guard/listen/issues/25). You can report bugs and feature requests to [GitHub Issues](https://github.com/guard/guard/issues). **Please don't ask question in the issue tracker**, instead ask them at one of our other places: * Use the guard tag at [StackOverflow](http://stackoverflow.com/questions/tagged/guard). * [Google+ community](https://plus.google.com/u/1/communities/110022199336250745477) * [Google group](http://groups.google.com/group/guard-dev) * IRC channel `#guard` (irc.freenode.net) for chatting Try to figure out where the issue belongs to: is it an issue with Guard itself or with a Guard plugin you're using (e.g. guard-rspec, guard-cucumber, etc.)? When you file a bug, please try to follow these simple rules if applicable: * Make sure you've read the README carefully. * Make sure you run Guard with `bundle exec` first. * Add debug information to the issue by running Guard with the `--debug` option * Add your `Guardfile` and `Gemfile` to the issue. * Provide information about your environment: * Your current versions of your OS, Ruby, Rubygems and Bundler. * Shared project folder with services like Dropbox, NFS, etc. * Make sure that the issue is reproducible with your description. * If Guard is not responding to file changes and/or is not firing rules correctly: * see [listen](http://github.com/guard/listen) for more info on troubleshooting. * run guard with the `LISTEN_GEM_DEBUGGING` environment variable set to 1 (info) or 2 (debug) which shows what's happening under the hood and how fast) * If you are using plugins, check out their respective README files (disabling spring, adding bundle to plugin's command, special debug options, etc.) **It's most likely that your bug gets resolved faster if you provide as much information as possible!** Development ----------- * Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/guard/master/frames). * Source hosted at [GitHub](https://github.com/guard/guard). * The [wiki](https://github.com/guard/guard/wiki/) has useful developer documentation, including: * [how to create a guard plugin](https://github.com/guard/guard/wiki/Create-a-guard), and * [understanding Guard](https://github.com/guard/guard/wiki/Understanding-Guard), which contains useful debugging tips. Pull requests are very welcome! Please try to follow these simple rules if applicable: * Please create a topic branch for every separate change you make. * TIP: run `rubocop` locally before pushing (so your PR won't trigger HoundCI comments) * Make sure your patches are well tested. All specs must pass when run on [Travis CI](https://travis-ci.org/guard/guard). * Update the [Yard](http://yardoc.org/) documentation. * Update the [README](https://github.com/guard/guard/blob/master/README.md). * Please **do not change** the version number. The title of your PR will automatically be included in the release notes for the next version of the gem. A maintainer can add one of the following GitHub labels to the PR to automatically categorize it when the release notes are generated: - ⚠️ Breaking - ✨ Feature - 🐛 Bug Fix - 📚 Docs - 🏠 Housekeeping For questions please join us in our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net). guard-2.18.1/Gemfile000066400000000000000000000027121450001123100141650ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" gemspec unless ENV["USE_INSTALLED_GUARD"] == "1" gem "rake" # The development group will not be # installed on Travis CI. group :development do # Force rubocop local runs to match HoundCI rubocop version # (https://github.com/houndci/linters/blob/master/Gemfile.lock). # # This needs to be manually updated until there's a resolution # on HoundCI's side. # # See https://github.com/houndci/hound/issues/1250 gem "rubocop", "0.54.0", require: false gem "guard-rubocop", require: false gem "guard-ronn", require: false, platform: :mri gem "redcarpet", require: false, platform: :mri gem "yard", require: false, platform: :mri # Used for release gem "gems", require: false, platform: :mri gem "netrc", require: false, platform: :mri gem "octokit", require: false, platform: :mri end # The test group will be # installed on Travis CI # group :test do # Both guard-rspec and guard-cucumber are used by cucumber features gem "guard-cucumber", "~> 2.1", require: false gem "guard-rspec", require: false gem "aruba", "~> 0.14", require: false gem "notiffany", ">= 0.0.6", require: false gem "rspec", ">= 3.0.0", require: false gem "simplecov", "~> 0.17", require: false end # Needed for Travis # See http://docs.travis-ci.com/user/languages/ruby/#Rubinius # platforms :rbx do gem "json" gem "psych" gem "racc" gem "rubinius-coverage" # gem "rubysl", "~> 2.0" end guard-2.18.1/Guardfile000077700000000000000000000000001450001123100176022config/Guardfileustar00rootroot00000000000000guard-2.18.1/LICENSE000066400000000000000000000021101450001123100136670ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2009-2016 Thibaud Guillaume-Gentil Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. guard-2.18.1/README.md000066400000000000000000000205721450001123100141550ustar00rootroot00000000000000# Guard **IMPORTANT: Please upgrade to Ruby >= 2.4 before installing Guard! To install for older versions, update Bundler at least 1.12: `gem update bundler` and Bundler should correctly resolve to earlier gems for your given Ruby version.** - [Ruby 2.1 is officially outdated and unsupported!](https://www.ruby-lang.org/en/news/2016/03/30/ruby-2-1-9-released/) - [Ruby 2.2 is officially outdated and unsupported!](https://www.ruby-lang.org/en/news/2018/06/20/support-of-ruby-2-2-has-ended/) - [Ruby 2.3 is officially outdated and unsupported!](https://www.ruby-lang.org/en/news/2019/03/31/support-of-ruby-2-3-has-ended/) :exclamation: Guard is currently accepting more maintainers. Please [read this](https://github.com/guard/guard/wiki/Maintainers) if you're interested in joining the team. [![Gem Version](https://img.shields.io/gem/v/guard.svg?style=flat)](https://rubygems.org/gems/guard) [![Build Status](https://travis-ci.org/guard/guard.svg?branch=master)](https://travis-ci.org/guard/guard) [![Code Climate](https://codeclimate.com/github/guard/guard/badges/gpa.svg)](https://codeclimate.com/github/guard/guard) [![Test Coverage](https://codeclimate.com/github/guard/guard/badges/coverage.svg)](https://codeclimate.com/github/guard/guard) [![Inline docs](http://inch-ci.org/github/guard/guard.svg)](http://inch-ci.org/github/guard/guard) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) Guard Icon Guard automates various tasks by running custom rules whenever file or directories are modified. It's frequently used by software developers, web designers, writers and other specialists to avoid mundane, repetitive actions and commands such as "relaunching" tools after changing source files or configurations. Common use cases include: an IDE replacement, web development tools, designing "smart" and "responsive" build systems/workflows, automating various project tasks and installing/monitoring various system services. For a full categorized list of known Guard plugins, look here: https://github.com/guard/guard/wiki/Guard-Plugins If you have any questions about Guard or want to share some information with the Guard community, please go to one of the following places: * [Guard Wiki](https://github.com/guard/guard/wiki) * [Google+ community](https://plus.google.com/communities/110022199336250745477). * [Google group](http://groups.google.com/group/guard-dev). * [StackOverflow](http://stackoverflow.com/questions/tagged/guard). * IRC channel `#guard` (irc.freenode.net) for chatting. Before you file an issue, make sure you have read the _[known issues](#issues)_ and _[file an issue](#file-an-issue)_ sections that contains some important information. ## Features * File system changes handled by our awesome [Listen](https://github.com/guard/listen) gem. * Support for visual system notifications. * Huge eco-system with [more than 300](https://rubygems.org/search?query=guard-) Guard plugins. * Tested against the latest Ruby 2.4.x, 2.5.x, 2.6.x, JRuby & Rubinius. See [`.travis-ci.yml`](https://github.com/guard/guard/blob/master/.travis.yml) for the exact versions. ## Screencast Two nice screencasts are available to help you get started: * [Guard](http://railscasts.com/episodes/264-guard) on RailsCast. * [Guard is Your Best Friend](http://net.tutsplus.com/tutorials/tools-and-tips/guard-is-your-best-friend) on Net Tuts+. ## Installation The simplest way to install Guard is to use [Bundler](http://bundler.io). Add Guard (and any other dependencies) to a `Gemfile` in your project’s root: ```ruby group :development do gem 'guard' end ``` then install it by running Bundler: ```bash $ bundle ``` Generate an empty `Guardfile` with: ```bash $ bundle exec guard init ``` Run Guard through Bundler with: ```bash $ bundle exec guard ``` If you are on Mac OS X and have problems with either Guard not reacting to file changes or Pry behaving strange, then you should [add proper Readline support to Ruby on macOS](https://github.com/guard/guard/wiki/Add-Readline-support-to-Ruby-on-Mac-OS-X). ## Avoiding gem/dependency problems **It's important that you always run Guard through Bundler to avoid errors.** If you're getting sick of typing `bundle exec` all the time, try one of the following: * (Recommended) Running `bundle binstub guard` will create `bin/guard` in your project, which means running `bin/guard` (tab completion will save you a key stroke or two) will have the exact same result as `bundle exec guard`. * Or, you can `alias be="bundle exec"` in your `.bashrc` or similar and the execute only `be guard`. **Protip**: It will work for all comands executed in `bundle exec` context! * Or, for RubyGems >= 2.2.0 (at least, though the more recent the better), simply set the `RUBYGEMS_GEMDEPS` environment variable to `-` (for autodetecting the Gemfile in the current or parent directories) or set it to the path of your Gemfile. (To upgrade RubyGems from RVM, use the `rvm rubygems` command). *NOTE: this Rubygems feature is still under development still lacks many features of bundler* * Or, for RubyGems < 2.2.0 check out the [Rubygems Bundler](https://github.com/rvm/rubygems-bundler). ## Add Guard plugins Guard is now ready to use and you should add some Guard plugins for your specific use. Start exploring the many Guard plugins available by browsing the [Guard organization](https://github.com/guard) on GitHub or by searching for `guard-` on [RubyGems](https://rubygems.org/search?utf8=%E2%9C%93&query=guard-). When you have found a Guard plugin of your interest, add it to your `Gemfile`: ```ruby group :development do gem '' end ``` See the init section of the Guard usage below to see how to install the supplied plugin template that you can install and to suit your needs. ## Usage Guard is run from the command line. Please open your terminal and go to your project work directory. Look here for a full [list of Guard commands](https://github.com/guard/guard/wiki/List-of-Guard-Commands) ### Start Just launch Guard inside your Ruby or Rails project with: ```bash $ bundle exec guard ``` Guard will look for a `Guardfile` or `guardfile.rb` in your current directory. If it does not find one, it will look in your `$HOME` directory for a `.Guardfile`. Please look here to see all the [command line options for Guard](https://github.com/guard/guard/wiki/Command-line-options-for-Guard) ## Interactions Please read how to [interact with Guard](https://github.com/guard/guard/wiki/Interacting-with-Guard) on the console and which [signals](https://github.com/guard/guard/wiki/Interacting-with-Guard#guard-signals) Guard accepts ## Guardfile DSL For details on extending your `Guardfile` look at [Guardfile examples](https://github.com/guard/guard/wiki/Guardfile-examples) or look at a list of commands [Guardfile-DSL / Configuring-Guard](https://github.com/guard/guard/wiki/Guardfile-DSL---Configuring-Guard) ## Issues Before reporting a problem, please read how to [File an issue](https://github.com/guard/guard/blob/master/CONTRIBUTING.md#file-an-issue). ## Development / Contributing See the [Contributing Guide](https://github.com/guard/guard/blob/master/CONTRIBUTING.md#development). ## Releasing ### Prerequisites * You must have commit rights to the GitHub repository. * You must have push rights for rubygems.org. ### How to release 1. Determine which would be the correct next version number according to [semver](http://semver.org/). 1. Update the version in `./lib/guard/version.rb`. 1. Commit the version in a single commit, the message should be "Bump VERSION to X.Y.Z". 1. Push and open a pull request. 1. Once CI is green, merge the pull request. 1. Pull the changes locally and run `bundle exec rake release:full`; this will tag, push to GitHub, publish to rubygems.org, and publish the [release notes](https://github.com/guard/guard/releases) . ### Author [Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](https://twitter.com/thibaudgg)) ### Core Team * R.I.P. :broken_heart: [Michael Kessler](https://github.com/netzpirat). * [Rémy Coutable](https://github.com/rymai). * [Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](https://twitter.com/thibaudgg), [thibaud.gg](https://thibaud.gg/)). ### Contributors [https://github.com/guard/guard/graphs/contributors](https://github.com/guard/guard/graphs/contributors) guard-2.18.1/Rakefile000066400000000000000000000032011450001123100143310ustar00rootroot00000000000000require "nenv" require "bundler/gem_tasks" require "tasks/releaser" default_tasks = [] require "rspec/core/rake_task" default_tasks << RSpec::Core::RakeTask.new(:spec) do |t| t.verbose = Nenv.ci? end require "guard/rake_task" unless defined?(JRUBY_VERSION) Guard::RakeTask.new(:guard, "--plugin ronn") end require "cucumber/rake/task" Cucumber::Rake::Task.new(:features) do |t| t.cucumber_opts = "features --format pretty" t.profile = Nenv.ci? ? 'guard' : 'travis' end default_tasks << Struct.new(:name).new(:features) unless Nenv.ci? require "rubocop/rake_task" default_tasks << RuboCop::RakeTask.new(:rubocop) end task default: default_tasks.map(&:name) # Coveralls: # # TODO: uncomment to merge results from RSpec and Cucumber # require "coveralls/rake/task" # Coveralls::RakeTask.new # task :default => [:spec, :features, 'coveralls:push'] # # TODO: for the above to work, also change Coveralls.wear_merged! instead of # wear! in spec/spec_helper.rb PROJECT_NAME = "Guard" CURRENT_VERSION = Guard::VERSION class GuardReleaser def self.releaser @releaser ||= Releaser.new( project_name: PROJECT_NAME, gem_name: "guard", github_repo: "guard/guard", version: CURRENT_VERSION ) end end namespace :release do desc "Push #{PROJECT_NAME} #{CURRENT_VERSION} to RubyGems and publish"\ " its GitHub release" task full: ["release:gem", "release:github"] desc "Push #{PROJECT_NAME} #{CURRENT_VERSION} to RubyGems" task :gem do GuardReleaser.releaser.rubygems end desc "Publish #{PROJECT_NAME} #{CURRENT_VERSION} GitHub release" task :github do GuardReleaser.releaser.github end end guard-2.18.1/_config.yml000066400000000000000000000000311450001123100150110ustar00rootroot00000000000000theme: jekyll-theme-dinkyguard-2.18.1/bin/000077500000000000000000000000001450001123100134405ustar00rootroot00000000000000guard-2.18.1/bin/_guard-core000077500000000000000000000003301450001123100155510ustar00rootroot00000000000000#!/usr/bin/env ruby require "guard" begin require "guard/aruba_adapter" rescue LoadError => e abort "#{e.inspect} - perhaps you need to run using `bundle exec`?" end Guard::ArubaAdapter.new(ARGV.dup).execute! guard-2.18.1/bin/guard000077500000000000000000000042601450001123100144720ustar00rootroot00000000000000#!/usr/bin/env ruby require "pathname" class GuardReloader class Config def using_rubygems? ENV["RUBYGEMS_GEMDEPS"] end def setup_rubygems_for_deps require "rubygems" end def current_bundler_gemfile ENV["BUNDLE_GEMFILE"] end def using_bundler? ENV["BUNDLE_GEMFILE"] end def setup_bundler_env(gemfile) ENV["BUNDLE_GEMFILE"] = gemfile end def setup_bundler require "rubygems" require "bundler/setup" end def program_path Pathname(__FILE__) end def program_arguments ARGV end def windows? Gem.win_platform? end def exit_with(code) exit(code) end def spawn_with(*args) spawn(*args) end def wait_ignoring_interrupts(pid) Process.wait2(pid)[1].exitstatus rescue Interrupt retry rescue Errno::ECHILD 1 end def exist?(path) path.exist? end def guard_core_path Gem.bin_path("guard", "_guard-core") end end attr_reader :config def initialize(config) @config = config end def setup return config.setup_bundler if config.using_bundler? return config.setup_rubygems_for_deps if config.using_rubygems? # No dependency management detected - check if binstubbed by bundler relative_to_binstub = config.program_path + "../../Gemfile" if config.exist?(relative_to_binstub) config.setup_bundler_env(relative_to_binstub.to_s) config.setup_bundler return end unless config.exist?(Pathname("Gemfile")) # Running guard with bare ruby here - it's up to the user # to setup/install missing deps return end STDERR.puts "Warning: you have a Gemfile, but you're not using"\ " bundler or RUBYGEMS_GEMDEPS" end def auto_restart args = [Gem.ruby, config.guard_core_path] + config.program_arguments loop do exitcode = config.wait_ignoring_interrupts(config.spawn_with(*args)) config.exit_with(exitcode) if exitcode != 2 end end end unless ENV["GUARD_SPECS_RUNNING"] config = GuardReloader::Config.new reloader = GuardReloader.new(config) reloader.setup reloader.auto_restart end guard-2.18.1/config/000077500000000000000000000000001450001123100141355ustar00rootroot00000000000000guard-2.18.1/config/Guardfile000066400000000000000000000030211450001123100157560ustar00rootroot00000000000000scope(groups: %w(specs)) directories %w(spec lib config features man) watch ("config/Guardfile") { UI.info "Exiting guard because config changed"; exit 0 } group :specs, halt_on_fail: true do guard :rspec, cmd: "bundle exec rspec", failed_mode: :keep do require "guard/rspec/dsl" dsl = Guard::RSpec::Dsl.new(self) # Feel free to open issues for suggestions and improvements # RSpec files rspec = dsl.rspec watch(rspec.spec_helper) { rspec.spec_dir } watch(rspec.spec_support) { rspec.spec_dir } watch(rspec.spec_files) # Ruby files ruby = dsl.ruby dsl.watch_spec_files_for(ruby.lib_files) # watch("lib/guard/notifier.rb") { "spec/guard/notifiers" } # watch("lib/guard/interactor.rb") { "spec/guard/commands" } # watch(%r{^lib/guard/(guard|plugin).rb$}) { "spec/guard/plugin" } end guard :rubocop, all_on_start: false, cli: "--rails -c .rubocop.yml" do watch(%r{.+\.rb$}) { |m| m[0] } watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) } end guard "cucumber", keep_failed: true, all_on_start: false, cmd_additional_args: '--profile guard' do watch(%r{^features/.+\.feature$}) watch(%r{^features/support/.+$}) { "features" } watch(%r{^features/step_definitions/(.+)_steps\.rb$}) do |m| Dir[File.join("**/#{m[1]}.feature")][0] || "features" end end end if !defined?(JRUBY_VERSION) if ENV["CI"] != "true" group :docs do guard :ronn do watch(%r{^man/.+\.ronn?$}) end end end end guard-2.18.1/config/cucumber.yml000066400000000000000000000001731450001123100164660ustar00rootroot00000000000000default: --format pretty --strict --color guard: --format pretty --strict --color travis: --format pretty --strict --color guard-2.18.1/features/000077500000000000000000000000001450001123100145065ustar00rootroot00000000000000guard-2.18.1/features/api.feature000066400000000000000000000010561450001123100166360ustar00rootroot00000000000000Feature: using Guard API In order to reuse existing Guard functionality As a user I want to call Guard API without Guard Background: Guard is installed through bundler Given my Gemfile includes "gem 'rake'" And Guard is bundled using source @spawn Scenario: Call notifier Given my Rakefile contains: """ require "bundler/setup" require "guard/notifier" task :default do Guard::Notifier.notify "foo", title: "bar" end """ Given I run `bundle exec rake` Then the output should match /bar\nfoo/ guard-2.18.1/features/callbacks.feature000066400000000000000000000015521450001123100200050ustar00rootroot00000000000000Feature: callbacks In order to run custom actions before and after tasks As a user I want to add callback hooks Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Add a callback hook Given my Guardfile contains: """ require 'guard/plugin' module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) do watch(/foo/) callback(:run_on_additions_end) do $stdout.puts "Callback called!" $stdout.flush end end """ Given I start `bundle exec guard -n f` And I create a file "foo" And I wait for Guard to become idle And I stop guard Then the output should match /Callback called!/ guard-2.18.1/features/handle_failing_task.feature000066400000000000000000000020261450001123100220310ustar00rootroot00000000000000Feature: gracefully handling plugin failures In order to prevent restarting Guard after plugin failures As a user I want Guard to gracefully ignore plugin failures Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Continue after a failing task Given my Guardfile contains: """ require 'guard/plugin' module ::Guard class EpicFail < Plugin def run_on_modifications(files) fail "epic fail!" end end class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:epic_fail) { watch('foo') } guard(:myplugin) { watch('bar') } """ Given an empty file named "foo" When I start `bundle exec guard -n f` And I append to the file "foo" And I create a file "bar" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["bar"\]/ guard-2.18.1/features/ignores.feature000066400000000000000000000015351450001123100175350ustar00rootroot00000000000000Feature: ignore files and directories In order to receive only relevant changes As a user I want to specify which files and directories to ignore globally Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Ignore events matching ignore regexp Given my Guardfile contains: """ require 'guard/plugin' ignore /bar/ module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) { watch(/ba/) } """ Given I start `bundle exec guard -n f` And I create a file "baz" And I create a file "bar" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["baz"\]/ guard-2.18.1/features/init.feature000066400000000000000000000033631450001123100170330ustar00rootroot00000000000000Feature: Guard "init" command In order to quickly start a new project with Guard As a user I want Guard to create a Guardfile template for me @in-process Scenario: Create an empty Guardfile When I run `guard init -b` Then the output should match /Writing new Guardfile to .*Guardfile$/ And the file "Guardfile" should contain "# A sample Guardfile" @in-process Scenario: Create a Guardfile using a plugin's template When I run `guard init rspec` Then the output should match /Writing new Guardfile to .*Guardfile$/ And the file "Guardfile" should match /^guard :rspec, cmd: ['"]bundle exec rspec["'] do$/ @in-process Scenario: Add plugin to when empty Guardfile exists Given my Guardfile contains: """ """ When I run `guard init rspec` Then the output should match /rspec guard added to Guardfile, feel free to edit it$/ And the file "Guardfile" should match /^guard :rspec, cmd: ['"]bundle exec rspec["'] do$/ @in-process Scenario: Add plugin when Guardfile contains only options Given my Guardfile contains: """ notification :off """ When I run `guard init rspec` Then the output should match /rspec guard added to Guardfile, feel free to edit it$/ And the file "Guardfile" should match /^guard :rspec, cmd: ['"]bundle exec rspec["'] do$/ @in-process Scenario: Avoid adding plugin multiple times Given my Guardfile contains: """ """ When I run `guard init rspec` And I run `guard init rspec` Then the output should match /rspec guard added to Guardfile, feel free to edit it/ And the output should match /.*Guardfile already includes rspec guard/ And the file "Guardfile" should match /^guard :rspec, cmd: ['"]bundle exec rspec["'] do$/ guard-2.18.1/features/interrupt.feature000066400000000000000000000013771450001123100201270ustar00rootroot00000000000000Feature: handle while editing CTRL-C In order to cancel a command in Pry As a user I want CTRL-C to clear the Pry prompt Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Continue after a failing task Given my Guardfile contains: """ require 'guard/plugin' module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) { watch('bar') } """ Given an empty file named "foo" When I start `bundle exec guard -n f` And I press Ctrl-C And I type in "1+2" And I stop guard Then the output should match /=> 3/ guard-2.18.1/features/logging.feature000066400000000000000000000027221450001123100175140ustar00rootroot00000000000000Feature: setting logger options In order to customize logging output As a user I want to specify the logger options Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Customize logger template Given my Guardfile contains: """ require 'guard/plugin' logger(template: '[Custom - :severity - :time - :progname] :message') module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard :myplugin do watch('foo') end """ Given I start `bundle exec guard -n f` And I create a file "foo" And I wait for Guard to become idle And I stop guard Then the output should match /\[Custom - INFO - \d\d:\d\d:\d\d - Guard]/ @spawn Scenario: Customize logger level Given my Guardfile contains: """ require 'guard/plugin' logger(level: :warn) module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard :myplugin do watch('foo') end """ Given I start `bundle exec guard -n f` And I create a file "foo" And I wait for Guard to become idle And I stop guard Then the output should not contain "INFO" guard-2.18.1/features/notifiers.feature000066400000000000000000000011341450001123100200640ustar00rootroot00000000000000Feature: Notifiers In order to know what notifiers are available As a developer using Guard I want to see a table of notifiers and their options @in-process Scenario: Show notifiers and their configuration Given a file named "Guardfile" with: # NOTE: don't actually add notifiers, because Guard detects notifier client # mode - where Notifier.add() will fail """ guard :cucumber do end """ When I run `guard notifiers` Then the output should match /^\s+\| Name \s*\| Available \s*\|/ Then the output should match /^\s+\| terminal_title \s* \| .\s* \|/ guard-2.18.1/features/show.feature000066400000000000000000000016171450001123100170500ustar00rootroot00000000000000Feature: Show In order to know the defined groups and plugins As a developer using Guard I want to see a table of groups and plugins @in-process Scenario: Show error when no Guardfile When I run `guard show` Then the output should match /No Guardfile found, please create one with `guard init`\./ And the exit status should not be 0 @in-process Scenario: Show error when Guardfile has no plugins Given an empty file named "Guardfile" When I run `guard show` Then the output should match /No Guard plugins found in Guardfile, please add at least one\.$/ # TODO: this step fails # And the exit status should not be 0 @in-process Scenario: Show plugins and their configuration Given a file named "Guardfile" with: """ guard :cucumber do end """ When I run `guard show` Then the output should match /^\s+\| Default \| Cucumber\s+\|/ guard-2.18.1/features/start.feature000066400000000000000000000013661450001123100172260ustar00rootroot00000000000000Feature: Guard "start" command In order to automate my workflow As a user I want Guard to respond to file changes Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Run a task Given my Guardfile contains: """ require 'guard/plugin' module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard :myplugin do watch('foo') end """ When I start `bundle exec guard -n f` And I create a file "foo" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["foo"\]/ guard-2.18.1/features/step_definitions/000077500000000000000000000000001450001123100200545ustar00rootroot00000000000000guard-2.18.1/features/step_definitions/guard_steps.rb000066400000000000000000000032521450001123100227230ustar00rootroot00000000000000Given(/^my Guardfile contains:$/) do |contents| write_file("Guardfile", contents) end Given(/^my Rakefile contains:$/) do |contents| write_file("Rakefile", contents) end Given(/^my Gemfile includes "([^"]*)"$/) do |gem| (@gems ||= []) << gem end Given(/^Guard is bundled using source$/) do gems = @gems || [] gems << "gem 'guard', path: File.expand_path(File.join(Dir.pwd, '..', '..'))" write_file("Gemfile", "#{gems.join("\n")}\n") run_command_and_stop("bundle install --quiet", fail_on_error: true) end When(/^I start `([^`]*)`$/) do |cmd| skip_this_scenario if defined?(JRUBY_VERSION) @interactive = run_command(cmd) step "I wait for Guard to become idle" end When(/^I create a file "([^"]*)"$/) do |path| write_file(path, "") # give guard time to respond to change type "sleep 1" end When(/^I append to the file "([^"]*)"$/) do |path| append_to_file(path, "modified") # give guard time to respond to change type "sleep 1" end When(/^I stop guard$/) do close_input end When(/^I wait for Guard to become idle$/) do expected = "guard(main)>" begin Timeout.timeout(aruba.config.exit_timeout) do loop do break if last_command_started.stdout.include?(expected) sleep 0.1 end end rescue Timeout::Error STDERR.puts all_stdout STDERR.puts all_stderr fail end end When(/^I type in "([^"]*)"$/) do |line| type line end When(/^I press Ctrl-C$/) do skip_this_scenario if Nenv.ci? # Probably needs to be fixed on Windows obj = @interactive.instance_variable_get(:@delegate_sd_obj) pid = obj.instance_variable_get(:@process).pid Process.kill("SIGINT", pid) step "I wait for Guard to become idle" end guard-2.18.1/features/support/000077500000000000000000000000001450001123100162225ustar00rootroot00000000000000guard-2.18.1/features/support/env.rb000066400000000000000000000015131450001123100173370ustar00rootroot00000000000000require "aruba" require "aruba/cucumber" require "aruba/in_process" require "aruba/spawn_process" require "guard/aruba_adapter" Before("@spawn") do aruba.config.command_launcher = :spawn gemfile_path = expand_path("Gemfile") set_environment_variable "BUNDLE_GEMFILE", File.expand_path(gemfile_path) set_environment_variable "RUBY_OPT", "-W0" set_environment_variable( "GUARD_NOTIFIERS", "---\n"\ "- :name: :file\n"\ " :options:\n"\ " :path: '/dev/stdout'\n" ) end Before("@in-process") do aruba.config.command_launcher = :in_process aruba.config.main_class = Guard::ArubaAdapter end Before do set_environment_variable "INSIDE_ARUBA_TEST", "1" home = expand_path("home") set_environment_variable "HOME", home FileUtils.mkdir(home) @aruba_timeout_seconds = Cucumber::JRUBY ? 45 : 15 end guard-2.18.1/features/version.feature000066400000000000000000000004141450001123100175470ustar00rootroot00000000000000Feature: Guard "version" command In order to know if the Guard is up to date As a user I want to get the Guard version @in-process Scenario: Show Guard's version When I run `guard version` Then the output should match /^Guard version \d+.\d+.\d+$/ guard-2.18.1/features/watched_directories.feature000066400000000000000000000043521450001123100221020ustar00rootroot00000000000000Feature: watch directories In order to receive only relevant changes As a user I want to specify which directories Guard should monitor Background: Guard is installed through bundler Given Guard is bundled using source @spawn Scenario: Watch current directory by default Given my Guardfile contains: """ require 'guard/plugin' module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) { watch(/foo/) } """ Given I start `bundle exec guard -n f` And I create a file "foo" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["foo"\]/ @spawn Scenario: Watch only given directory Given my Guardfile contains: """ $stdout.sync = true require 'guard/plugin' module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) { watch(/.*/) } """ Given a directory named "not_watched" And a directory named "watched" And I start `bundle exec guard -n f -w watched` And I create a file "watched/foo" And I create a file "not_watch/foo" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["watched.foo"\]/ @spawn Scenario: Watch directories provided in Guardfile Given my Guardfile contains: """ $stdout.sync = true $stderr.sync = true require 'guard/plugin' directories ['watched'] module ::Guard class Myplugin < Plugin def run_on_additions(files) $stdout.puts "Files added: #{files.inspect}" $stdout.flush end end end guard(:myplugin) { watch(/.*/) } """ Given a directory named "not_watched" And a directory named "watched" And I start `bundle exec guard -n f` And I create a file "watched/foo" And I create a file "not_watch/foo" And I wait for Guard to become idle And I stop guard Then the output should match /Files added: \["watched.foo"\]/ guard-2.18.1/guard.gemspec000066400000000000000000000025271450001123100153450ustar00rootroot00000000000000# encoding: utf-8 $LOAD_PATH.push File.expand_path("../lib", __FILE__) require "guard/version" Gem::Specification.new do |s| s.name = "guard" s.version = Guard::VERSION s.platform = Gem::Platform::RUBY s.license = "MIT" s.authors = ["Thibaud Guillaume-Gentil"] s.email = ["thibaud@thibaud.gg"] s.homepage = "https://guard.github.io/guard/" s.summary = "Guard keeps an eye on your file modifications" s.description = "Guard is a command line tool to easily handle events"\ " on file system modifications." s.required_ruby_version = ">= 1.9.3" s.add_runtime_dependency "thor", ">= 0.18.1" s.add_runtime_dependency "listen", ">= 2.7", "< 4.0" s.add_runtime_dependency "pry", ">= 0.13.0" s.add_runtime_dependency "lumberjack", ">= 1.0.12", "< 2.0" s.add_runtime_dependency "formatador", ">= 0.2.4" s.add_runtime_dependency "nenv", "~> 0.1" s.add_runtime_dependency "shellany", "~> 0.0" s.add_runtime_dependency "notiffany", "~> 0.0" git_files = `git ls-files -z`.split("\x0") files = git_files.select { |f| %r{^(?:bin|lib)/.*$} =~ f } files += %w(CHANGELOG.md LICENSE README.md) files += %w(man/guard.1 man/guard.1.html) # skip the large images/guard.png files += %w(images/pending.png images/failed.png images/success.png) s.files = files s.executables = %w[guard _guard-core] s.require_path = "lib" end guard-2.18.1/images/000077500000000000000000000000001450001123100141355ustar00rootroot00000000000000guard-2.18.1/images/failed.png000066400000000000000000000076211450001123100160750ustar00rootroot00000000000000PNG  IHDR00  pHYs  gAMAo3 cHRMlronA1tl-IDATxL M567j[KR4KldHm@pۥmp:C9"Gx::.'3)Z6W{MwY_[=mTϡ PFsaeWhtAg# A1Y@P.oPOp$F8L!F°4f n$E_|ef<h%U:ޯgC/>7b \0E[c03EgD@-ϯbdeUG\`Y^1B"6Xߊ[& -AO(\ZD AI:H'h\B(K;O r%r =z'xRUGۋpfJx|`Ȁo$ؾaSЋUX{:V7'.[j~E={*=,Wy3 QB?>(  "BbF@@뇲 m"oMlFkRA,J (ě^<ԣɓx""RzɓDoŃ 56$э[l&M 1{ߛIz\<Gn7ɒGX5C~#3}:%U= ˅|y~°ڍJb3CwK! lP_ͤ„3ܬ1!F.l<`6$+ر8MX 6fOJgɥſ-5'Ekͽ$^RZ4̨DVg ̶w9޾׮%3*{FgmڞwF)T#ȏ ̯oVb1I%BP M30=K":ѷ/˱ۙzi(!Q(帵 FdX!#wsPb_ȞDQ ;Lq9p90o((mu$vkі]ײ-J&x/L*I< 0$MB=*Z@GVmdehMBlYRm$N.uH+s>|-:]yinmcesҭ. ܱ~߇/!>P#-mSiG>副5*ozRR "}Ne[c ᲥjPNN\pԚc{_{_Lmysϻj cX5O`R"4ʋcs/x;lCRH!Uyd\A 0`*X|~\]LK\l?tqL_Dj!m͂dtO-QwÈ)+7lz#fsXf.+7erYs$<}PJۺه á-W$ |mOFNGC 4cb LA݀ sEzKL flƸhNpvLNX q٘ C4:xy&s_] ׃ y=f[k9WSK5>KsM\Zoؘ;eu5 Hv?DpjS-,osp; .Ue$Jbp:af'hbcDR9JeZDg۫p^o ~\Tw\ٷcؘV% D{ oB7Y坻bOuY|gwlk[2mteNf2!`-Gj}Jߐ&W> % @}arvIEdώE4`5MT] ״ϊT SĚoleǿ]ֻF c H26h4$H/񽉑hILxo!@b@2Qp0Ÿnn=ڮۺqbS|=_?ݎq|)[W)HGH+}`,wDo Bo m,TI o93 ev?ݻԿ6VIt=(xk9W4wHy -Cs6h=?-sSa5*BLSPXCTZUTb"R$ 3cPC1aSSX*=Va#ZHk xV*+ KnFd`#=ZHsxFUk0EӾgUs@WLڰjԎȽ3m"]'i`0ZثEt}-Gbuo #hl@pny)+ULhxR)z'hR2D5M9 'Q \.]jS tEL&Mr8^78W[kX+cK\:'iN ϏDT>xYE̩^(-!0(pDOTd0yYKo,Օ0†,iGAe9A{.Tq??ID|px_vxo"PyaROUS!:+u%gVX"(-!GÄZB b zYoEJtV'P_$/T5)="DPf hIz7K=t"dA! C^I,#evd3--L3-sgn ^ȋEwWfPFnhIENDB`guard-2.18.1/images/guard.png000066400000000000000000004725621450001123100157650ustar00rootroot00000000000000PNG  IHDR~LQ5 IDATx[sdyv[ ; F\H䈦Dɦp(a?GSٟo[vX!h^$ҲHQx.==(T"qd".3(ϊ*$7uk ,X` ,XA[`OuE Y@,oR߱ ~L-X'U KI|s} ?xⷢ/5 |KnDKJL0_ƀ쀯mV1BnM+O g , i#PL:zAn-<\M|ȷ ]`OR#΁oYBQ+;~=zI+u^~,X҂zfRA;7 31="Q$ďs(u{B@1#QU ` oy;^i Ґ*ar07VL_`BH dǑAb؋e&sPQh[,XijTU8^JMD$ә>YS kddVPEG#)&S Uj;`\D<ȝ/[Q"ވ6cK+Z]Jx BZgj)nC1a`qL8U/y#; YP]5`,ML ^AA?XyKkDy܊VfLk/Ĵ`! Ro]Ұک&v7yhVK?fwy`(C w.ō.ʪ_nbqG$E1ЛZJx BZg"hs?ĝ(fl*gB ]A}*#}&)i[gy$EaD7k=~>wbA|61Fč\bۅ2z= rJLZc;o !-X/CB XH0L"bs(HPrPij}Ri\.4'(NгI*wyLO5 o,`d-o01fKvQYP@ F;*[Bެ.44i7Py6L;%2E_s܈LL0jN?DwdsT?g.9|%qyBH  ~C'Az e/BLSx={l=d }s %Q9Ceend k`ݙ"N\w]If~|j) \PsNEhҟ\Vz~3L?ت\} BZ#DB)&wOSF! P'{,!Ǯe EGSV%<|_\7@1VX&e3qEiPz repj ZQq1*sD:W9li*V3`! ~LVSP.EF`#i3AClj@9XcpTT |:mGфQ:QB* `suci3E_:?mqvnŷJ菑{vtoSwOɸp Cqdz=DoJg\^̥>n740 hQH BZ𳉿ߵu2 n~󛬸z }!5p4"[?HD2j~bN< UH`5/0CX Lh"`H 1Fk![;wo$>OBy#A9ksG 2aP6'#؛Oa^/(?$?ǿkه`! |~Wٿ кx!}vxo=ᠨ{gPz[Ôu6ț] 1P 8j2+Ұ)Iap9°rۥvC[k{ؽjgJ ފfH(Oy O0;̼Jw 97ߥ9Fb|}1LOp^} BZ3Ic`3ľrWĿkΙ F_q jCg> :ӵ"Et=08 P0Ţw]*H)vMKG(5t\4Nj& iZPLMЅ!H Pʄ։ăGgSGO{}ÄΫ`[ ds^qS# nHVI< mbIٯ&x :HLB-O＀(,XH=-X҂`^Cnŀ1ଭ`mUAlyxO0܊ A4sRз'X;q9*;_IWQ|sotOdw׫So~oʷRwx]4OzC4E@E\XU4SD 8}]v)'຋2~T\%k"$Eo{ۧ_u)\1Lr ? ]5"u)0~ۜ4%"Q~$ogӰ s)@3<7c19ǂ !- ooTFv vqw"@%*ֆC1}MIDxȱ,mg٠xFxXڅM;K]Nj]vJnjM&@XCPBLSn올\&L1(vrϖ@~ߛe[SߜWJþ۰'bʾվ:E->' gy.u.׵za!viT ] )v]R)7{{),0MY0vq D\X*9D,{[SefLvks]T4ύWݯwU}_U#{!.Gi]#L= DŽz湢X҂ܥCٝ'n- Vh8EmgPHl+ #|J~@0~ǔ[\5POOϺOCw")_(V ڀH,aNI X)g.*2ڮW`d f' ( C"6WS^̚6>3(#dC .JA<""Րr/.=RzZ說K"-lN,`,yدVSx wbW}h(+٭3W`!x7!nd>McTϐ_OoE`BH ~b|I 5LHjC̾)2oK'*}Xĺ$U)*]_~d^/["CTP՝_~K܀+Cmx\L%S&2Mfeo5;7y^3uDlZzfW^[YA{^9E|$v|qKS:ئ9 tlVŐSz0 m*NQGGlF"7y1[ef7)KAq1yk?ksj6wPO[޷M~9B[3j Âo og&.PVQU2+q$Ew$PiF˽:!ͫk6Y[|;(AWm6 do]z䭡C2CU4H}.*ytR5q-nede&99N.#S%TWm}a*" YaFCsjhhk$୭v1״(UHZQ"BsbO8TM}F6k ]MU_ަ(D]97)w.gm^Xc;_NtԂ,h%*7j^<=pC35|ںgͼITPFDA3⣄$ ࢳ?N\ ^P.`졾e +%O&oUVF\킏:`Eԩ?LkZw1.[kνА04o&Fbu!N"TL.J|(JHEv59NPۛ;Cڵ;GDa|ENBE&Z;t9AV8v+wMNiG ۳)[-FD}5Vgk(k)n^ʧo"* BZ/h?$y X͇L$-X[pKKɹCc(C鯄c}3̸(g6y]>!/V0=]諵 ic)Ԉ'SuטU8l^p+:MI.t11Y;Q2Myd-P5ZBhɶ54*9$uNs@⠞1frI:fKmfqP3c¡YIуrro-fbê _ߘmgA&*Bc$uȬ uO zͽv! !-0[Cq+ 8Pi G8m|s3#k;oE}fo/|OQ?=P sx_zo6LMḊ\puq5E\UåД DIʯ&<fI;r.ǍsPjwػs!kf5d6!EKnnà0`oqhGb.Fj;l8.൥~ʬY+Id.ΔwY2׍! I~mAcv-oa3 9ZtYj -Xiq9yGS7A()er7-t@o jqatVruENl_)SYMjj-tvehcE[ v8=f(Z63kmY8bV4#R|$fztċvc)yQ}I^Q4߸`VS[d;ʜI9lԙ<;y8jq$'947$м K¼N}>z~j=/ƐܿJ=Rx͈=È@~sq-Xi83^";ZMg Ewyʏp5؟=]_$z^t}264ՔmaJnmڤ!lpIU0u]9%E(6nRC }ԔPΏ51E$2j5$<綕4͎Dx)Cm-8,MSf[I2P($Et|K_kVZgVckZzE5OA}.&Q'zlmFZ/xH !-8? *LЙfan sߨz`x35azasi6ƛ;Qf?6r Nu>l:.LQ]>ke9Gun/PODԊV-8<ר#5E:U*MM^뛉(!w 1-Xi c^bQ~rxŃHښ_\ц{VsI=C*^"[1W#|B0O ih76 e5M%"6!ϧkhB-I;ULgӺٮ;٣=TLNy8=2=JlR8}[zK9jL} =SWBHL8&A }R1Xɯ޷^?I_[.w7+(w`}y+N+c#Ժ]/USLR6M@5V6k$& oʜ֒R 5S3@ gft퐈`tb{!:R60gSO樜ޡ$u|rN?qtG 螤8<"?s5⢓2$)vd\ۄQA.(*m@G7NP/\~[ga__BZWAPڡ37t:-nd1Í`V< 1& zd0.Ӭ2㘀*)j)h&u5tb=hdup\k{ͼhzu]I\&[)Ja{Չ{}Ȏp8mW:Ou{ZOVM:Gdijje7gqyHyJ4v;Fft8(D|Q*m~`dx08UNx>Up}Y}s~9E/]=]R.2>Kn%ҧ~FTV3vAs$2sI)YwOھ6[#}!?#8692`(#_Ԗ}U )+'j{ۆ]g{Fj=}rXn=RCmQM*-ڸEb.qi q‚qҕh:sv:j>]yz(2(|c爢:Im`u?W7?8}~?ྋjb>LUNoRb ̫.t];EJ`2ŶtWؾWW^(k@y}a}Qo.lƙRhQ:|{a. !-*FoqŸދ^lf+w13 }]\Sc}#vAmGP_Ck裐MɨvKn:Jzi̩>fNO8N8޵vZG%a'<9OTpBFߩwh=yd85(T_Љ9>C@ASIj~RƱʨl1;Ĝܐ)%rMA^eeh1۰,g(mpsI6:.rG+ Y!U/.ǣ 7{6.C.PVzms&*/ŐbB 4J#doRHois fVH5$Gs}4&ļ Q%NcHT"HPŜy|4jޭh VZɸ F'n_[嬞UuRe<,e mn -NhN('3}m=..cg'*ָw&&>m};?_"voWϯI|1=UD؆zqe^V+wzһqY]~BZ+{<=M1";f 3)*@<|.kw)]jqmT3#6.DW7pމMUDܵYhE+ɍGT) -QP~NC3q9."H(hax#byW-֣-D\wPں2.:CC|Ayz+'psEv\dc}Rp)GxpNM~[ǯݩʚO#koX:=Լ E癄bR5̭xf̼(0;t ٝ2/sC|nu5+)w;D:3ܛqm*7^^FcQH ~ W >d]"kKa /:9Ee'[ߊp6'i-m 9ޭL}/C;%M/;Yq9q_9=<|0:M1٣yc|}BC'dwwDL/;N1/;Cs,|\{Qw:'xj:|TN O= WS $nJeע&".3t˼0<c*Gܓ%8&})4Jc=pQ>'/CO&IAH8|袛_Z?&^멂:)4 Nqo;" OULL'F7汦1mJowǰw2cm=1/{l//rfYC$vqZwv:56idtVr<"瘽743P{^Pq;=y)3CfQH ~)=Gvs??yC݇xvW9׈O9U\7/}{|wmvwvNq5urĥIzLLt1zx/9q龡'n=)u7@]ăS}4dN{/>!G < =h'sO4EM re{E}pN~^̏kQ8'yBqgkZ#4.R M qf]yC%"QyzGId;I;yfjgqy)In.úӷ0>>|dh/wj5'Tp&nBH |WCwFsnm ĺ9k먇=D?_vveYaFÔ[ȻN_`GH}>ait2p(kNsu8s3I'6|x!s*w;NKEw7=R\:4g/\ϫ[=N|_r2ia3ƙv@f_[귊!Bmmkq0ʮ27q~-^ߠbxnF;/ Gȝ y-?!YE%>}rLgjľo"96@`W*^FBZc7bדUU^]sDZ17Wv@՞z o/Y:s(v-}A~\->̤ aIf2:aWA_rmYm%Tܓl ǵ7=ڟQPiPL'j݉jʓOcxd8ly9ꬽ!m'<=&_4x1 ; s3ZZ#- ]wAbTݔϨizIzIә-?ºs@f~RnspvmͱUm5"#)Zhg=^ܿRZiObA5QX {\GҐǀ`#m~)(>Zm5yy]QE0;>Nlܜ 'r>10/ĽYǑ7LV]:D 6꺩C|û5aI);|![迋xc'7eB?>qՖB92^#l7_ӯc}^ZQ:E/idgxÉ#QD3P7Y%oTk~wB>Ya@0ՇƉs9Qz*|g˜ωpN {K<* =f:WCS-+N tTzؔ*sETAN?1dQ\]Nmg*Ȣ]5k2>[To1^8xmƷΖ\c:E- Nإ}LBftfNTS9ܩO'Asok:r:{%v0a*)=ꫝ!Z!t~N^jS9˰ݶ軎f?5Aͯivͭf`%p(:h'-m6[WF6O~2 0_'y2?`CSDIeXnuUV9{q"0#WȌ<^ MI;Ƅk>1A TD `ȂĻnW>/݃g0`QƩ~B%|5hf>` `AH LPp5vs5`y.ٶ-5X˯ѓjΞ㡰 y/Q~_s#=hȵ m/ s1_ #VEG'yQ 4{h\ 6 w%}B5 V}kNPb¢6 V[`qh2Jl',M;Pt,m+)͢MV^hkX !/>o)"wfժƱ$h^:kF0 ٧'u xLus*vH ќG7p ćKc,d &?-t,̻ :k+73hCK[8 M "ڋ<!9#D/,]7棋ٶ… tΧwhD~wסYiEŸ1'N zn&fUDo5^gT][4Χx߬"uXP>oP\V(X7 z#ԟk'T4]k7[KniZ&9FGN~ M 8B8Fi"(L4x-EG~ n [Fa?wʺ [A\V?9p#/B!X_cR/PGO'x)H1:7d_[ P&PB#1:02@yv"_yaP BrrąKSYSx+`00mwH-lvUr;ۮG;qB[tJ-]bu>12$_jڵŶMFS=ϡh}e&tFo"mӮy6/ BZ'ή٤MkNMgۥ01UѽR1^VPU2o0ɐ '%GekKq?w fLָ2[c$$]Px׹]$%f :?G᳃Ip9ps W0;?I5r]e r{م]> :dp QR?JU"_'5шdT:H% Mn \qŠzbfOЅ]uěk97@]Vuۄrv( n+ AZw|N}J~6ti#؊nwgьNYPdrn:yVΦj>! ƹw)vwO+6NWY3pDLd7-@_1}g `? <v?V 2O!Uȿ_үK, +п챒Tܼ \f`<ԌTL10@:2++F8b T9'nm#OGC]QZv"E ŷs^qE]kFS]ABu{&.cρM묏Y{Ŵ .}~fEv6sFu\3HK5\**Τ 9OQg(jRqJ, (d0&'OYD&Gx=L?ٲ/J9CF|9^ үq'Crxq1N-0C1!:82x>V>d)xK#@4Z,]HN[";.dum?鬪fCȫ;qIP]r4TyoQa*6@jfǙgscIV_3Me}0zM*׻['7o:$]5GNPWq튀&qx5YT5i\*Z*VyPro+,m kkm6Tpa-(l--ԷBg$Wڻ(-"hbAj'j @ri2F'MȏMx-GLЭ;A '30 , #vT/ү!=G2w cc8|| B =x%bCӅp3!똉xK~+Wi;Q>ۣY.I`64®Zf b)ruq݈Eccv;RmE‘Ew܍S}Y=rnij+DAGL?)AXtUsU#.Skj)sQ~dw$.c? _)&Ϟ-jQV>ͯ<IZ` 86vJJm0o^GVkOvf!frN>2k޳d'&(J=ԯy[|WYw]JGxҟWCv ܾv%A ǂk8,j/W ~]Dp 寀C`%u(L4 v <x- ot 1]M fC-HTI V hSVM馞Q5PL8upS=U;-g^Az,u~Ғd5Iwsz kC?_>.1u\Pn 7^ {36SfC&52ݣ9\8 jn\\j}圡Cyh㴚{8c+J}7[3 {>$wp8@]"&dD*  (ہL]4sF qWkY>/VB/W#LQN‡@;q9=*j9?Ɵ51;6;g;<G|KhOWJd!y@JDϋe[u&-C첋bGݼlf$=_22ΫÀ8 VZs6"AUv ,;]]c'mPZm ;7߿9K \@/,کG7r}%7k-BdOLI s *x&H8iEA)c,@mpr) NLςg_~JܟAw>'0Q(KrŽ@7. \X>3;c8NeLP܊́h| W@^DRw$N0EBHZX#eUR,AG$/37`6C.$gU5s:Cf!Ȃn|u V5[Ş+X+ i~4[O)F/13m8vtɪ?붌 NYq/ȺLCgQJ) A:{_ wJؿ𑗬:10oR1C☈}? Ưu ~u.#RpX:r4+ Ir>vhǻECi\Pq7_bikzp݁'c 7eTR~Rw= n@vϫ 8fT0g 'p5q2^d`BWS%9`d4Qvj> i/rR uxW$0wxE76;Vq֟Vܖ |v.F]@\Y}ٹmݾ9#6JoDђT8JSH z_A(W=RV{ֵ_{ڣՋ:'I˲t{Phl0䑐hMy^uwO"8&#C v!`&NE!8ؕ8%' ^ /Oʚ8 ?>^Ct x!Uԧgb_2`Uz4hrr9a.ZОK 1 3Z%ͽ #zbj>hVyCUa]xBy&ᅓ fD[Ϝuܵu۩ NТde2y8A JBǚ 5$_ڃ]Q^"%xa!VȻE kԛaOU~^E N.׹+|5^ ݽQ֛-:{XEʡwzWgZHC«JI7%9 L%h;1{F&]CHV6^:q"A05 ]$M#r?cQ}&G1$@2 +rZXNk1 ՌQu/X ,:rBJ^Vg+FD)wן@;v 糃җ <gMّ!*>7|Wg$aGbm7gm1LkYEAJ>G|[僲q j{l8ŭ5b`v,{b)xMn3"0+r0lNdy`ff>qpq%j Iw&xQ 7i b3ߒŕ%|mx)H//x(1c&ϧn6& b-S>AW a?Ui?LMҩCD뱅3j'^0cU\[`ֶֻ(BތBM|eCT%9^?ʱA$ΊtUqVu͋*}E0C T!U㺚*ʢ*BZM*83j~F\R}ܓ P)U^Ny"U"l|PϷ_eƈǚ#*6E!EePKRmb~V gCG> qWA+M-.Bv4 L."p Ѓ!K~|)"zCɘ31uJLz)H//8l%@th8V~2 W86C"C 21R8q(bD6q# C^,xٹ:v[k3{d9%>uRBDP:tsPOFCژ~gv[>C;g9lzdy.nbkEg枚Ok+bk&XtZ γ6m>xY*x*8?L*ִߊ*V_kѾ*ƱsDkV)fYFPŌ,JLP((huqo@z!MF==IPq\y]#*7x%;>s%vxҁ8+q%krHE9_y7XrZ$;\c2xը4T1u Wi;2p!~]9.cQLYڅ`$1qe EWwsCK[o9咃sff#98#pyvw<WFFhz_J݇q 4!!C TFnW̍7bb^W;j=Z4 ZօzrCɺhHڟ0ه)BURwϕҪ,Ґ~ƫъSE waBrF?-$\ fr.9#pfc=a+zGKK>1ǽ>w=yh8%ϾHܯA`/#oL_k>!߱p @&,CvƉҬy7xq4 '`/D3rd@\PLQD gFm0Ɣ`ZblrWU)m4׈yZ r-qKX-MvgV~C[nq)$xYncǟ<<ʢY)9wu)hpb8zl E(l}A)\qg(wfejO4i%iN$ofZHnq}/^=p ^?McqH^ wr<jaz!%ґ` g-0'1_?oq!rO;;tS 'pw86L70xf& C:@scé y:&irٰ iGI_A ;voOE!HƁz}mN{)H/iQʵ #ayiGs$?)1kx!h_>J`4^:8<%9E `d9u)M!\zɈS1ڧΧnm1YNw}U23L(:^WicGa{~;3UWPkUۈٳL 0VB7ڤ"MZ^fkeǪἅauc1CX? *r~9' >(k`JYM*XVѓ`X23%(d69eޜIw{gp;{"BIKiGErzҪ|'T v-d'.Kؿ|{ 8~亞17'cO.8(X'y(y2!F|#gfW%f&rml,,"/B\-zZOs]][SUYneۋMGKD=v-+_\Lթw׆V0t hJoXBl&]]6$>y7ܑD0+G9!ymaqT? QuVPh:8הKz)H_TESu2؇g vEE7"!ٙtp8>x՝WI.a AX2`pg2M&cY}Ҧ7ߢgWHIɓ-v>sΧ.%o>)<65|Q?"_.ډaf[ D}Fn'͹;HDA-xϫ UgGE!-HK(e IDATz^JkGŪ*v{)lZG}F#Wms3g֯Cf2ŠB=ۅ04rAI2w1ӯ3q gk;CǻG {8&r`K$Oq*tqx)HlSYj gnPJ"&uE˥}j'&"~a<Qo#'v8LЅ7r"BDP`fy^ܐ*iy>=?@b%fxĔs ֍mctkI{W7ђOqz)H/A}X>Q?.S؃K8A 0L>/il߼$$Gq%#-N  0VJ ^PԀ/ *\Ʀ B3g8'_8]qlY0v(-1m@`KYܓxNݯ%#} !I8(7X@;}JNjɛi\_^ 7 vq -pLgC7O  d wep~y8gMgjMG&J'Ǘr͏;6s!{GM3qt<ӎ qx0\LU6# GI{pu Tj \+JHFC jps2=KgK1S4S=ipZZ g>t8ɭb;Y8$޽zz8+v5w_%}Z Hupr*?R/6:>]捊mbПF1bWZmu*U%EFf:4sm}(AJ"\Ξ']ů5=?O>p@t}"%:&Wm|-w&]0hR1^1Š/ kkm\",2"JDH>M:K䗂G7Q˩+HWIszl^yNt|Q?A1k@5Zmk9*x])%d)_a pǓ );&pOD۳䟚94}!ߡ&M/] r{wȘ ?p(kxx .00EGAT9⧵G C9IIb7P#GE=:EϴrY턻#9-z4kEܦXY,-qVm]zFV-\Y4A.gq(ϟpPifk[n'N8a=Ŗ6yNM:ܺ%)X6=ΫXsA= hWVRbO-^7;+"gs--ڞ͜XS{}FU!h.Y{S2#Nn-;D\e$sɏM:vY(SIm;A>1DH)8nyңߡt^~^ /Oᄀ9'"&,׎ۯ >|s"/3yI>LT80YQ 0JnDAZ ]J,2I>%_}jc=y܉Էv̞}ܰv[z\Ԃ{^HKˬ "fҿq38@kjԅbƱ 0{:#<;VC3:DZl긯3:b8ơsy]Xpe/b=V:mCOn;̊e?:i2H1||_>>(<@  {" tN>Ix.FN¯E O5[ 7A!JuL , R,Bi'rb+a=ՅNpR*i{- wY{$iôa0ۢf6Oߢ:_y _p+ ['цox zBAMlqڼU3vXV5QOlE`ȍ~J v`:"쐬5i R=g~ \[pȣ8r!Bk[]Zʠu&9~ 99Bg`b 4D F(& f0 Sc "B$B FR^l1F.ܼ507GU.{BeTf;`|E3Oq}̺, ݟ6J7٥K$ #(Ț7U4`1/M.f0F,Χp S5^\FƗ K<:Its3h5$ZBST-ւm&kּU:dh31MD1TԅҡrbL1'.4o3˔Ir܅o^73~qHh@ٕ['A'+%_cc&{g~D 58`< dѤ[4s:ѸwrS^?^^#D[I(rʻ#t+rrw[,'n .s9rӲڦ@[<Wx -_%> OSfE^^g6! [k.Xc4ED6(;'؆ M. Xei/z}KXTH5:Mq\-O:"؅\SvO-rM,ށ$Eb DabL;M $$W)Gou|)H/y~,}EaG.31ɂ0E?޽A7"`ހ8 8 9GF͑E&^?O%xbM۶r}ޗݎ9ͱ Zʛb+ >6sD3fͶPjTWtp=,Nxa?6ŷ}Z7y֙Kx!+.(H'gdʨϑ۔ܸ_؍g|~cݺSkj>tV|{.kنhB$L~+ Z7z=Rg(M>?M8 n9Q~FԠf_3T-1Pqtr w$k!C#3/<91iՉu<.D^eQ J"2;XfWB2IQ0dzlW!~÷@0;jUʇ)Ϛ)"6/w[#CN1e]b,ω7oeggW{g! aA7aN z#€xƂeC/ & ,irHÞfOMuUV֭JgH63}č8)m$/ҋ3=O,^0"+u5] M^~J z_?>V=\T`5ڼ|EջG]jNhY'FS h{Of\"uxY Nŏj؎Sx Sjafg~oQQB,ĔfH9<ğ jˬ=ة&@gp\^\)nv+y^ yÍ}U)zU@\ԉG0ƩH:PYie%50̦KT+:hqJ Cqc0Nu% !UI^" F7@7Eśj/߂lߜmvgx:Sca;۹l XpRLt{:0rYWG H{Q]J3m5pګiXmg]Dy5װfi8ru^ҿүZw{pl.\7u3^7buݫ@e=b(YԢT3;BIP\V0eB;2㸧w#A N;]B705⢞&v^V>+]h|I I 7Lmq껀 j2Oj& G1KPDF)YJfl 3p5]TEKPV47Gy23X|"\ԭV*, rEh5މ6gY~A&~oV?݅`^jlEk5 5hqa[:֓ږ2*nI@T PW4ĨKILYkdC}nוTqR7Z(;C(d)NۓՐ5pV λYפ痰GWX}؅}N\ 8SxvG’$au;M!#,b;kD^!DڍDUQMo0 0@ً8L*i]/fx0U.VՒ`h Ԙtw0U3!Z`P)dfS!@S'\tGj(sgRcud(vb7ub9'mI\iElaLnn0bBt#pTjfX=ZP_zxRŊzagsĚ޳m}Q#X^MuCԡ)cdgۉ}]4[5Bl͛:,q sc ^1qDӒɪksǭK\R;+Ixm&Ppe*T7uQ̈́N&4. л"k .ʨ?LBSSNuڐEpp Oc"A.``]셙i*?K pdj'#<y(1immGhcщ_&4l(vLwmx=c[dy=pQ' _r}J" B"W]7CWܻkۛs'uSf+= 62|^.cDQ梗Qΰڻ!o%/upB-.hɟZGZlcOX}=kה\_ +tNEx^?Kf&, d21< l,S fR@^[{f=lک y>l@;R`r$OJn6]n*l!!=ؑ QT7ZS/E12c"#GS.c? 6֮ΪU])p2{"{&,}5XVvg9'ds_ -n$&7Y}RbgXֹaXlibPC񤞇lםmm񚐋oZAŅw-pvV;rLV!هm۟gi? qm6|z᦭쁅ku u,t{$K,6⎥W'DDU5jɽb"#ծ_7{y(KFQrN7a&.ALD], !8?ڟF_OCz8?&{! ŏl+H IDAT}dɏ Sfeah8%Uf$UG2 $bH`61q3Wyj@-DvT bYwbW06DzE3tS_Ֆ6HqIkى|,\fZ+ݫxV' <6Ts$؎2J7u#eq^êkpQU7 6,9g #FbhU*oulqTvT7"kWVqo9VpvF)+)al#bM9 0LfT"BN,]RN;4%#NBYR0f?51 =LR7-=?wX_!G4#J. x?  {>O#L@Ox'؛2J`6ebLviv'],= ZȄS#+LdlDkSonYrȺ>2\-nB+SUw.5Ō0AzUV;XG~\)۱eY 5ރ~Zff:vıZ7{\y {d5'њ+.t8gS7GZy֎]=F~sf \ܸ\"IP҈WIH0 %8H[x"y}G^#F9q&GEYDP\hJ"0! /0s{(HZzk-L^2EI+~FˬSn}{ l J:Eɪ]#D{tM"{"n٤AM%ɦ"0tOw޿$>fn_ULª}^itMg=F%>κF0/{*nݏ7[ ]SsXhEONfXt-ytw6xRgƧ:+u4XEV@65*dfrB*".XԹ&8W]RWQM43GNY q]Q*Ezg@GwhBT hD4/f}\05'Dq_Pc hElX59l'UuUbP;΅pȚ bp5)4B $Kub"L v;G@0a P ck-6鮜=- 懂7Rȡx# wjKؓSSN)D GDCJf)6! ᤀ<ṕ\R톅WJ^9F /*?GVMKhf1|Ⱓ:H 5 %՜4blҧ&x/>vFL mW%5!Zqn#$[†5ZZ1*:6caqtq"}6=K7HV+ツמ:]1HbʩR鹴Ww9cFxjN ."*)j¤Y4‘!4p 4 [YDdfnixg{igF"p1p:&|~)F߆JUH ቯ =,h~&|=~ 5B[KP>E>|U B" 0)wBAƒW) ´w0j퉮;[kҁl Krnm55;;}E:^MGqߨ.\Y XO^Frׄ [\gZ籥Ʃ+njU0i-؜π\yNC"۸XxiU;1Y,m{}oηy:"%I%؏ O]^p@8m`yk0f97Jb. a 7 0)OBpU]++/aTP>IDhLU޻RsՍyma,`q1)M`<`8 w`|-IdwS8yl/0ų$(D&#NUWbh1܍ݰWL" FX660+Oմnb,m{{\TA p!Z'Za>룳FȨxxmQZ@;ޝ}t|A/thcN8Fm 3XHݰd-PqsQ×z vr^F"qsHi*#K 1̴>P8lszՓ{)+ S<ǜ% t}"0; `KkPA;vox~R %V4^! ҞxVn?)B%ob!+X;$ $RL[~ TMlF+$EXjs>~V`ǁ-^a}&mW#PBuCV ,Y9tWmmԵqjqbkÎ qjW# khx6rz;i>סoC7xiF>^ 5(k6r_< q̺GvgĮ/n;jQuhm:TkRl)餵[+ s}Ljb1'Q`PS}85~4=FMjDIƙLO3JJ&?@:CA#:*:#׀i5O͒R 0/EiCш1q0WSCp,̓+H8pnUQU 1 uW OYO'$]f w΂uDA[qq#׏y P.^mp%+v܊*:MWPGotu# V9FúMZk}Ԋk%̪3՘fCa J5otn: Sk\MD,5{$RjG(űr@" ;nu8=Q&t+>!а:f"I asN &%ڦeR[5N9+ j{n$0f19g(MX!0׌zǍ?8L _gs8eЋz \*;Bčnޥ<+tΐ&Bfb;i-^hb%%e@侔 Ng:'P=ޓؙ'=mqB.]?Xt^k(n[ŇV6fF 爽Db#-Q1&bɶC?ȴ+oXW%m3Nxj'vKī0l˽\xcHו쁉]$&0U߀ =To{1Z7bvRI,w oULb=*qɀ$zo#Ǣ{SDZFS~_1!u"՜ӮwUP[ԊtxRlE2V F\Rkdہ;  U.?e]cUk93flf[oꊊf8 s!161G"kw|EyUba +P뒰ruUc]zm\`n#MH[1HШfYhQ GSrҌ]d5&P.a/YO I&gF. /՞ # `7k2AH [0hv+  o؈p` 2kck׉7쬝n(Րآ&(EP݊ H&Gg'i:;뿿~[DvZo{F/&h\g|t:f+c[!яl纆nGC,oedxYzm|:lMwr6}ztjU%toԪOmIVz:Th0U4" VH**Z/b^̦zA[D3G6*1~ݥ_Ũs[>1XqrQXq+{sUwza6M"'NH@&B5/)Nc $ĝQ!3 {{3k1K| b/$ fz1^ax< L{Z(iCkܡY,YFL6eaƺd?9[[o4jVq^p^KTE#i޵1 3ەJ AM@m \_}f"b f<ΣntmژFb}l7@QEj4F DiV"]}r*(- N;c蕽p]J^iDM/U]xԤ-N0 Z4%3d6BU,QRx9 '3oM"d{a.ГD@teZJ= g D@vV m +įw9o )b8 4$@#+Bt%4f'Q+zRMmʀ~NZgQbprMӊA{wghˡ2ɺ/zvoo:_$fhTzBG:l3[ƤyNyCU-:xƊh8g!Lᣊ]$af]>7 .Jw_Q S:cĐOe"/8|xߝF^wo*IA3dV[RR~qTJ.$pM㼧Zgč1SXXb1Z0PYn֟lM]zxY`VАT[ۛ7}d"@Ed.,hJ4w `p^mu@YhxmFo-#rQ|"/&ULuYc/׾mo?eKvRh؆L\7|! sdZF:U_1r$U%ɜU0\J ){PN :$52fktBË(? ؍O?z3N5/Y0\ RN Ⴠ j,l߇u7{t7+N 7<2:YF KЙFíA c%XLVӎr8Yy{ժ8ܧ׸XmoBmV6v@oX g?fLE(1,bLe ai^F^"yHC(Emv4VJݣX$ΞrrcdQ  fSM=2H7ɧ83}v^P>Gk/=eKXnYh/pGK|DTbT30LW2z'ҥLbw,E6K!Iq8^?xGD)7Rd ]Soz5Tws}CWR"K Hsƛ&g "E?T (tZP:*=$,qģ.1ZHŘrεk,R+Y0 k? ݞ)el@RLH kL,iNdjcʁB|͐vh֗ 4 pY;N\^^WW5ͳ~{뿫-nTO(ƓZq޳J^t"T 1'?QE0d}3 %mynlְvzݢ_zר{/R9xciaNV}ޘV-鴓Byyh,幻ǷlDAs \ T@'9\9vH 3=aqi8vJfx3BEp˂O ^kh:Nwn1ݺc[wq؛$bŷ]5ݠV^EDj2mdejnSq[)W IDATe^[nfx  x Z(93 K> 3'|#_Ȗiw{&74qB #Kb4n1Ē;U`8^&rK F;nQ=mXxT<;Zobo%N8ZW9OmXԺxw-EC)HPҊV\ eꔮRP[3BS?BYwoUО/[OXmT٭{KlǸZR[mQujT(մ4w={}NԽ@]_3Jn#%d-vOʄTmR?JaF%>ߔ3BAE]T1,4C!ja6@i PU=nl9,(Āqz(#0gv12eTdfe"U_-Ŧ{=B"~t,l%.A'%Nܾ8~, {'Sfݭ/ތߟE.F!JXT{$q=;p/ ?bԨ&K kua>p[]LXGdBoYD`9/լV rCB,)n NT5i%[#ҷZH _ٺhosxkE\ԣ.u>VēOHv#TuDpM+<eGVh؋樽#}0ي+nLaZ0YeǑt=QcK$(zz\%1 x3Ϙ0eeDbT53Y2ZHd4Ә'BdD.xjhY2 ~䆇:~ï?o;>}b4??_} Z:z7s1\ok>!cOwh88EտͰ$j H4~|=9_"sL(ĸIJ"uH_{nĘffʬmvrVq\qC)*+apNZF^`m ͽ.`^^[9lfĽ-p$|#WRk]4V͹-ymM!F:|&l:6 G ꠭V.N*z'Sy7Da\2JA 1bWOiu)1Z,l(PHa?OMT2͒R 'q;B>yTf巸1;=??y/1ƈ__4O-}AI]ww:ҫK㉌$iM"inwi]&*!@ @bjkSWu"ǂxώNY_"6G-ې5fv>Lp1`-c k)%ׇi__w?pTo Xhs-.0̠$~0c &\p`NLd<\pZfI2f׺ _ lmeM;Nq9EZ1\P#QiVEHEMXmq(B6RH)ZcWm*f'\tjkvN jj}w['q G_E>} pFܽ~)+o;ofd[Y$#QZ +躧!dׅm"MKһwj\^vp39gDH]M"0Tq!oQIKQLc`u%_> =SsΟZRJO/.ݴW_G0P3ru} =pc.{f@#( 3'aϸ!%)@`:օ !{+tG^T!aB`ARb/I(KE 6&Al"[ Pvv)mj4Ȭj'[/X"B7;n >J6oiARnF9n]RŒ 㢾X:=ױTYE2[VR ZTaβ,G 6'(-;h)I"`n@'0:/ik S2Isf ]Mk[Fn7&nWԐ,[߻Xmwn7)YmE k:)g\V !3e!KNHr꭪ݚ"Uk@WbY=))=nz~!V0pqdNK'?7~?w?G+;lve'DBlb{b4 *5I8۷.fbBk>BX"6@MbvomyDu 6nkRhƆv+*sqS pBgꪊ]jco Y [=xaX{Wmpgӝnwbzu>u}m6{IsP>XrǶtLJ8:s>ZgU f^S `7h܌3g9nm 'cو5 (pR{ jv^x>ڒ,/v~v{tvW~p8|i1ƸbAqqqǏc}nO(m~㟇=N-$Nӣ L^c* 5XL-))K;x,0 Nz(SekHYf{k]; e5ΛPeTۏwSitk5-VHES"vu ]#dS:R1C0hQGEfp]2C^hXR)I]AjfC\/̖aʩt^OȵUl@ÅCNUzt:&NJ3YfTg4³:s`u,m4֝F!8!V#4kE.T͕׼H1F+c;-.cf(!\ 6lĻG>GW}D^J!ՇOq8=Cabaq83rX.v p~o_[Һi@܀I`LSqT9a*n6)FF3Tb:q$X[ kn zvs:xM&fь `n)[MSD2⫻?}ohO^ "^yv2;њkmqt0GV&N$ H7vQnZ+tz[!ό\XXti *sPl}CX1Y%5=U8tёy>.j,Xӝf(֍ݕ"1x9 A?h ƛHJm} x*dp|_tB8"ޝWoMCv(?'|M(  ɦW0i6]!;x47㕩^j{;9 vf!rQa.\M\ "mڼpkf7iBgYv.ΡC+ \5@X4f.jȪ9#*9;1B\Hc[Vܥb ##ST,&TKzb,z|P3Ph2mG-V'7S*%a{8[xh7sU_3J Ĥ5TFksb' ޛ= 36:3u@"U ` Ql#]j>>pK78wIc-ISJ=? N1^ YB{˜򛻯[Ǔ_3*2\a><߼eP<$wx#R@D)=qqq H!C+ݸ$ɣ/_n~髿惶\%-DBb=oZŪPYԫ!i2(` tFkPiU1b'(,BfڂĊ^waX:hI}|c+nb=ob:"F*ΨkGbUFWtbhCLWv874.ݸ'oF9hMָ ux;U\;gYQv_bno9'~ #|bϵH[,D: n=qHlijG3`D9P._^ 8]Lq*f3/[l;%y(wD!DCArXv{7޾;ijvWjz_/~滧7s?x~AR2 FP}rk,): {+"(fsFtP Ա:omY42Ɖgq}g躢mݸfp L; OceFՋg5ln ZlHrw#WqⲀ^ۚ2 \MXKg!KAEݤsm+3٥;uǶQs;`=tK^>n=h+FPr/X{ ;#=~G rj7 ]Qt##쩲9OjdjSy\åjB,(L `2 ||(H܏{OP T`f{?hna1#yB~ ݄W#,ӿn}_M/+_HFp KѺ?m'{\2P=@/n/võ_HWj]V |cb4}OCD U{Z-`i&hSY T)ԵS5֝\c)d* ܝAe-#`a91,"g,~`J^\, /_B^ ⮈`%Nz5De%73gWvbhʏ0PƦ+]v!!x_j"laUUFr,{tH5B3b? =^r.%SHR5-w9+jq+mӒijVŌ6`^{ vOnvl>R>1^#K%|q&z3>Hh/^ Kc1,:}uj}%wf\֚s Ƙk͵2/ݔO!RwfD䊈ߥz5)0yO#ˮY2sH2L@LB) H8MEVBvĜ"x$*Wnڞo-jxm\jR,)#b1 Mx<}>.0~D~7s A6$MtL>= GHrޖYI `{:A P-AZ~yO?@l4D5I3ϰInDϰd8 al!1@bbJx "@ mEw8v$xt8pABkvжPM"v΁N> UҼ]*j,oo.`އrɮÚWrU_C}6k|ů c- tv)V3^TTxK,nr\00Fg eCH w 2[D= Y7`QluQ'j(ѢJ2{ CLÅ w?16xI9MMB#?ۋ~xBDO1nSI*cpVzc i}#9әy1Ĉ4})G_n۾?.! @6:wKzMUg8E!ꅜewo*[ÚM auK~sd)GV*CM]{vhW:xj>EU1"ǀ64;Bh1tE50dHѣ-&e/1"8h~8dƬs}KZPD!9`}(E\$ZYDDK, ʓnMjs_-$qs<)ҫEWBErXPy,kIU#XZe2]h`{"(u@.=QBE@:SV4=e)&3\O[f\ڔDfN'\ [  1F 0y_ʇR{?~z?ۨFZI(ƪ;̖H![هUG1p0Ƙ_e1Měm C 0B D DfAzŔ4ŋ6- `N@v}oqDF{)11"F""Nmj"BV4M~dE!K !bsuSS16RdvgM0bR2 s}N>'R]<4]0U.TQ.gf]}!*8I;j *өU:͍DLٍ~'蚩m7̕YjHyM"v"V IDAT(sÀ] k _bp-5$' Fŋ&|P;>~vާp" *OQ58kSM* m]=6Y236 G_^^QlbfyEٿ?y=GŜ,H; H9{,G;a0;]!99,^꤇i]iyE`Me`xkg2,INHf4K)H?A5"""q:ju 2ϠR w>b@/}o緟]^<b4nA's`x{:ŧgVvcmR5ſ2 LN @H rV@IS)-ѱmM{3+Rbs8n'd3Chь8% Mk_G3ct1hk*hl {J'G&U!)!tO)FDъɡߠI#6Ap]H"Cvt8i"ar(D򎂆EBWaI+yDh@ 4E^ԝLUHo%; 0ZgA{a7D"@g-H@c+ȬtH%DJpMPeܜ3fz<əvz}B6<95ifw8oS?["Zpp]h>%?x!޸$*9HO[dUVQ5UA5p,] E&u b؄3;UntuSp2DNX6c+62V@Hz,/b;0=bգcx3CBK7tIo ҏ64q*P+Da@l־ c86 ~SB#7_]W]֟~ 8hr+UQ퉨8CvT5ӌ("SvFϐ(u 'CYL%23By;YNm5!# ;dRck38T b1c"bHFbhfU-Rm7 "%@btrA+Mf}o;[V QiaZvga`lrY6i.X`1%g^%xC]1DdO-yJ*~%ɡ'U[{j&Jgވ8Nl$Us&p6FU)D;֒2kc!oD=IU*ǯ:Ƞ >1Ɂqx!kBRRlAoQ ɿ7'M43Q+f7|Q4&9#]XԶ !`٠i|򣧐 4,mtuu]; %/ׄi/-w3 113c6eb (tk&Ff{,IhҚ&I*Vkl kG/HBd䔎?!Lm )gW;KLy1C#FAM0DbF  9(ڶUt! Ե6we S"H*1؁]0&`jK#-"IB)*kWm '՝.=;)`1g^g Z-,x"P$cpO=ytb_6e3Ufߕ$TEl:{ 4ߝJ5 *jT: `~RE5(yDLNFzD;%nvVGփJv#堐ݛꎠ`= sV-5M!q3֮Bxbۡm[<www=~ۜs?8rįQ+ߓeEK-JNw=x|T Ԭ)k-w^i,INy-3a;{LĔ8zDۭ-}n|y 71ZQ>îMX1LM3 l. 1-/p?Gvqlv\P= 89G ׊=X;&"xDpآS{aRVT@uA*Ienz,yYߌlo\9`3~ٻf;/$Q`ꈨB 4@BR&YYPlEe hDZR \W39+93)~qfzEd6r&=bU!G]&=?B Ut g Q 2Հ)H?!vZenU4e, 2"8.=x!!t] iG3?pwGWo_{7>@ #{1q° EؾV; |1SE"#"V\ Jq*S,ad@D=-$MD#Wpnz]^][`D]g]];Ϳ6 wPDঁNRx{ !¶mMtG2nJ3鳰 SM>0ȭP]U>|#SgMɼZq-}eQJW]Tk ɊVGp߿r"PYS15=\}wJ]i -/4l*}h=/'(EVQ=BΫRxW[?2@vU2=؜GR5G&"sf |6JyĭmA @K`3o`,s١:)H_hA7S{ˡش9gN)}xq*XfFuZ:N9gO=NF wo{p,Hb{B;m"C("B2C-Sx]!()Ab]bs@BdT"m3 YcB3"hMӠ{41b݂u5[t-P]n=xDhp"!^_Ms v FhHo<#BCgϐN_c8!uH0G uW)&ه6i1|XM9''EY*Z&g8N!}(U/j\K$@c (cQ*TEhA p!T1ܨ.SC M l4="WHĴH <'^/)MA1Ĝ`YKŃa^gf;'9=uofn-NÀDQp׽5w??Mc_>=1^ƞ]+j0aYS#ځN'I;V.$NZ|\:`ŦmƀYHKǎ4@ت |"T{R+/[buF4{{C}oFmr͐^?ltBwtQQrFNq Q{$_LC1WwYNW{PⰃMrD!$@r.ԺZ\jO~)sgWfu&XmJ!5uO2ȃkjȊXry<WXf<o-_,~okcg*BVDU{ޯBU,Mؿ1V?"=+Y5j46~ߣjK@]/0?M؀@v_/jN6ZaYCJ.epB07RNDBMt<-bӠWl6L{->0^_9*3CW6o%V5Ru93NC7+e>k>Yi}^ [$إ>vZXܭ!=,"*cWAwhsK06MpW UOꌽVxVU4YoL>pgýY7(J+(BGeBi(0%7p; *#6=} l%5 Yp<ͩa~~~'x<"ƈׯ_g"^=~;T^}AZ )vD<0n!.>AtSBqnS/;0F|Ʊ:Gv6M~o Etݼ88}C@49;`trFܝm >3B!:C8#" { !Bh;P0N4΋{/^x)/E}>hP #2(8N@Jn,;iY|7..E9rT/ 7jrv8,+V-yHԢmMƦ:QK!7ƭ ;'j^6]= eS.FEIQPݏ&9^zUϓKE#I#e{- ULbA6LE/p8\N74jq5=z-+D|#\}f|/*ñ/t  P@țW|cFdLbi#^ e] [fET2]X۶K8Gy+]?Gs5M<;]cE)ko:qѧ f1rwiqqueb\wsDڅuӬ}bMmq&Ov4e7-q8԰' 4qN!/m4&UE# t2:!]4 1g(4gƈ(EdBbΡ<]^;sqLa|Z=%ӍVC:,ڭthՕW=W\ [P«g9rόzB:uLU-JiU$%k@_ātѐ}Ӈ(;yu=qj h|?l;Zq'2ha3Ļ$] ?@oе-Dԣ-ۦ雦PU}oo~k0޽%y2:s١eӬu[@SgB1lwgl8Dn6 2.l&ѻ?[4}o'_cl@LS)0[a:Nb`i[ s7}o:c6(Ljng"6=nwhSL %%4f`L_ẙ2g#u݆\f!{R܋X>h.>U*{uyW}Vߙ4U`jEʩ.*}1?+7,}P둤q9}Vgt2׸IJ`(IP=grdEJ@M|aEpNP j1$J aEuhmסmlLPд-G.lWΛrۘVmKm(Ō+`=ѻ)ǻBOgfD#qOOt:o~)wwa,z#%4%wvgDDzJ Tl@PfvMPEP8u9yaU0S-]ٗKSN\ mD"t' sv:jc"@K:=*ͳODa^ EfYt$NT6"@]IݳmܒECIMQNXrG kbw.#gsPAQd8&}~#e k 9nۯqL]^=yd=Ժz8K|vRuC Elx9&<H|0P1!vk+n^9<3So o"I&XP_dERĪ-qO(o OmM!n3qJPlaW 6Dl:# bRs[b/E w~}ʯ'O󯜓bbBeѱ*;r<.6h>'q1=~dmcBgC Owr-l0V۶F{7: V$٘f ?r ťz'oĠ8ՄYc"+- idq)%)J^&".Wx9n^pi!Z2x~C`й'"$q]%1:;O 5^1 -E،Fuv[(\ZfZ ϣ!ZfD $ ?6N=q?9W  ifllV0_qbj6 L9#[IU5LD;!IPB\H0 ȏ?hMu:W-??l y!dUM5 ../\=5D &jaFC#"3v1AMk3~b49NEHvK!5 S^;]=FEjF_2!qg/aZ788-Y$zB +N pCQ8NtVɻ23+@y))TvWrNu0;SDkC->}\ V4tTdU|L:S<]La/3%Zլ@Q&0l}b+2A%j b@kЫ" Q{`(6n },Ƹ =IS6ci5mv,sR*di@'[Hv]Ǐx<c-S ~k__T'! D4O86O; bInz;9ܼBյ0]]|w6M4q $5,@I82PZ ?hJ[< G[˂Y]&sV5zoi&o;O=RJ Zuc Jgos zEaPut+.~ V@m["xF/}O6B9n&J5O͆YM5ӈDM.I%c ///ib_/~*HM&$+v;|_nCwPUܼi68"-#^AJT`igI,WWfcOJג,}Vj^tHE:DDgtIu)dž0mS[{1M1`{qĀGo;=-BTFn;t 4`p] ًp(Z/P-pvpf0~"6;&ZC.W:Gb:J=Xt锟g#,f?1I+$a=|Y@x6ђG/5DZw3Cf:0OJ iT}o`Qɢ(b*^C6ho)  ,| l# =V6Q4zxSuL@c0B'U,/v<-N\W=o* f!5EO[CςwBeΤKoZU.nmx\gy 9b1{*Nd'UV B `<})"H:*gW@VG:<^/>v8W$EVJ"QBKb=(齜7lS۶W6Ԫh;E"ilwh!pkGB`;Z®[ŊN R0:֣^1ª~ND>JS3Bˢ߲9/RNE64 B.vg81k#P1o@B`ºs9yP\Qw{Tͼ^I19kLQ1!ѺtB>kZT0B%~I%@lu!:[w;nU꭬Yد-KUaUE [e>@jHS+7HGU1ET;/N;,o\p]q֠:e.]1XԹ<\ 3--i!"Sruukq8q8txzf||[_Bvɚn0././k|?gp/! m|@x:͚J'T0 C*xMK\__cF̯p_Wc|k_'+@y!DSM$%O>s^ Ըlcl<)P8M`(Uq(9C1џ9#*G(aM`ExqQ0Ãx_y#޿'zoso߾oKp{ À~zqKh!wޕ6;0m[G7| | pRYi&qj'0kI?7BU{9p̖ e4 `MFle-e ΃=!pO tV \sI݅(Ŝ#+#ʌf[[-^Y犔|^Zs-`ܹ"I[f)1csb3|rɎ1\f$cJ>`pߑyvN E NQd@ {Ч%Ɓ8CpૄAeJF;ީM ?`K@QvDȹ@ޅҧ h ;/;*s]r@x!^жɸR4QFq@AKMH23R8Sj+Y^P~Cwkf.ۮ~.C7%3#vfL MpMI7YQ\J)ڼev6.ƒ+"eȤAUMJ uUVՕ"Pd%*U&$~P-u`f*n]^fGRPq\>ϔ#Ίxa: "?T7H$!Gjg Pd '0H`%vMzv5_/u1 #޿iE8|;YL̈1an7vUd]yN=Vm^7Um uP⋹!Yɭr_$;N+aD3?2zHlmDx nb4>`f(F+j$$Ѥ&%6Crl+aYi JV>o4Zci f/zlkNK)%$Z ,+v&xSd˘}amzMuN1% Hm,Vc YYc'MXE p$4Ȁ,ĬE [4'wbCMƆM!L9[8u9s=y$T~V,%'g}*kHn p[(S:9!Z>}-Ԉ wZzRELfXϓ]=_\gՕJ+%٦Ҿ؁4;LPaDD'DAHIZ+U!i&a/{JfKd_ҟ/_am@MorV_U&DϠK>.zGs #q53~_W jMk~<^ iqb{㈇Ϗ)͛H)t:[jip!|G|qo߾_埣mZ[*B4.q,;cY Uk\Lr(d)mqFtߤ=RRH)l}ISz͑KnEF~wG{_)&xkB{kFA63f!aj&1)!0g1*5\ccl[LMmR~Ar)ܥh5;osV`j8+{,0Lg,U/Ѿ-w#3|b`#dRfg lJ`C=fNx9'aTZ0sZsDx^.","3Or1Q] Bώg G"pGGK$o!qdG8ٵ} Hp6@ˡ]i* jJig=s+;~Z`ϥmJ.`eVWOup̊.WV!PgVd}.` H99kEE"m̎gR˹Rl/ٞǍg2r7)K Y4hFSM}ys $J_ɮW Dɾ *>|hI.ENfֻhHJJ{L6T>hǔg)IjnwMR2kd9XDXN@@)KX Q@lltt cPԦgk| ݶڐK@t[Ra*p0:^ⲉ}^0 lRVw9rH6"t}mtzsJ`<\?O<`g?~xwnm/*?8H)_aەRVKy%L PD_\lV\;?ΤTRR~ϔaI{е ήm̯p-oIs Cǟ~R,z"Y߸fhbD`F;MCȠalL@}BRCj-׊jJı*+9w$lU`VDʲQfJp#8>T}!$voaٿ"س#Кo2ߨ./unRwN`]SѭVZ*n^&[É/Xk4V᫣͆a(C4ac<4F+vY f /ŋeGkb7VT^REL*p+pUC\JW_GTF4$Vd/e}ꅝށ-jq%Y/ĆX|6z6K[)+u@vR(^]|Jx[΂ H)mƉys$h뉜@" n&p"xTg 77C\bL^6p^y;$Sɺe3W&*Q*(S$MB7B !ƨa,ӈ=0ex͛׋Z5D Am-$iD-ף]9UPŸ"(aM؊jLdO˞2@k͆FlGÂ)=Tjzlm1? GM2d/Q4m[L@)~Lj.%4+, 9a0> +U8F9Ru2(N:E_J(z,e20+9Fdu9@r/kc-*@Ĺ@Ȑ-jE,p^q>YMCCn@sI&pBL@1D7M6[^'DpN~ ΰ/O~7N o{(Wj3U"2yPj0~p|y<8ۯj%F$ >xi0ڦA~R>gAt@ʆrFp>CvL1ET`}e1'oYA0AYGK0g#*7cEujKb\/9(q%o`/CTE8Ċ\3H ka#Dsov^}Yy>A9 <]d>MB ߩDmՖ*3h@AI( }ɐ~Ö}!]1v{4+ EsaW8eidjB-u6ńq1sӔF.e٢Ɖq>фe4:寜yOg<>>; rR^d1AvYզO6|@ui¬leGḦq.wJIiT"BZ Ei*u}eukfت*#2nu^ZWYUp[9 cQՃ]?V7=Xuu:ˎc렫FTBg A׬Pwr.qzF={ A*4*}\XR_3CskENUifq5 VACd:tK#j"H%UFO$F"ڙПPLB Q} 0=@\RiD<.Y J'ZodH{Dxu~:[l}/Z!R6AX^2 Y)6#4miÏwx;@۶xqwrG@K$N3G$fKaNfY,8Zc݇Xm(;"B 1% 3N>4J C==j EM`R_leyHg<#αHmRKle2`{=4tA|zsCί6ٖ5i3[ωM)DcDF$ۆ+b 9X( ,0Z9+gDKIܓx!"dFdjCU)B rTqOss?)dB6RIb`]0GĢȠH#!ٿ@j3!jYpt 0Q?CGC'+o@ P˺| A$p#;AցGB A셰~cxx(jѻtW~՝pٍ:nfIw`rV3iWUT7!xZ(?G1n7h<<.ђxѶI:<~^ X#uhF'~7oV3O&uᷠ0sq}i $|p{{(KB)Ija?bcvB8#>~(X=vGs8!x;r H)-@մn[] =< IDATMȲ*+"F-CC XE]-JE8#` f#3>&5etUhM%Q*v{$}aP{i ^FXT򕈼jC4%a$QK(ιbKV6+dh"o\:҆,Z͠ȁ$2 K4&wGl,j31n&zh 텰 :D N $v GXٶ^x_2_wwp` 楖`#A&lTTk߯Pj.n[dΡ8 0O3I{>^] J<\as`a, pGefe7oy 0 %FJ`7smT^JPYFKcQ.jpW_)5yu?1 #N"Ph]Ϣ=Dl?BP@sxW>o^˗hfoM4o77"L<DZ4cD׵^}C> 4Й H FfSB?J ,PUPj6(6srš!6cg}Wld\" hc[)bb8EvKyj[zU :"Ķ3h;z%u-BdjU:+$&􏱹(Ysuk\ՆǮg vP7ItHG0h.䲔44V#7C_p-CP U~f?6+ڟqŢ·[kW4mi*?Ԩe*9MDrDE9xg(D:( >.-:e8i65ڟkvJd'=4‡.~ Zϗl츳xq9#Glsgs _ǧ%i$ڟJq~ףmhV81~ߗtٛ7+fW]9pssݾ:Ϥ4 rײ`hJ@=#nnW1YE1Fy#`+"B[8~)Fӄa!4U5C ºv˳"X^::ѵ-1Msɢ/ztUh"!He1UD^x)kiེ 'L aRRB?υ*i*,RR( :(^%Y,j2\dΩ)ێE0s'5Y 6uƔJoCuبsTe!auUg&/jɨ L N$(="`=0N$`j@91{]@"kq[p"0_fr5Xnu$z _9j8a.eL.j+\J`赽Z-rO3pkJUtc>"rDo{[(5k A@ x22^Y4*PLm.eAN%;nJ/>Oq^#׀J/{19bAD@#ʢ5]#S9A {)Lz̬Fr^Smo.빥Mob$=ڑ6vC喲0eyV,Z`('+e&=j~#9O蟷{2U 2 e6ӊL|-]{Lzl@L$ltj1@fW+\1Y F14St 4 6G鋨Oz<ٯf/Br nVTq.V×B(WbaO 4m~ <"ٲ"(KI/%޽{ů~-R^Kj)YF3N|p^Ln~:W|_ҝl 0ƣbLE 7׿Ƌ =s:mZܿn÷~[(0a`_y4Mmطi|ݯ7_/tIY9^|_8VhEeJWAjIe 컠o5jJb2iW8NI3)T.%t)Aˬ?aĄ&%xNZD@Mt˓jۉ {Ba@YQE_+jU$-q@Uq]:gRȌ7љfo3v)\; )af69^Y^;侓 FEǺcXDN#@v"G'ȩgyD0E1dbd@ A0O!Ƙ82;:X;`J@T;!8ðSx⎖Ou3 H|8HϓW K/_qbkUXr˝Y{m6]kѶلOއ%&WxRh4Qɔ]V6lf){rmZ-#yvhVŃ9,**۵";<o* .EfXeK20Uj리"Y)~JC]e GLQBb. I>nNyWm6+?XQU:MR׭+%[ꃽ͌ k&-B\휼u*!=:Z2UoHSd Gi,5  {˸xsW_ |paW63 |l'WF l}Tkv;٣T>î-B'#D]!4.Hh}2\VHS5ft }(䊶m8n= <|~~g%bb8)Om4z ^,%k"`PA\fɶxU? ^[_Ƴʑwg8~հynE,٫w)4QK֤L#$G*uq Z鴘9AͽA|.U%sVUd rB9rXt҈YEWfr'– zEUeܱvmby(D$"r`'"h1* 64$? `u6 Tx#-.e~b!MK@k+ml˯: e/Pc$ 3ay# g8Je[* |9o8MNgիg fSeWʖY@1 CN]hwww%s!"¦÷"9fBJn+dp,>x](l-&/|묶|`f,Y/w%,\oE۬|k3*SjaA%"#aӟk.ׯ-lAʐ|- |M3š2mR* # 8 | _Awe)9+n+ge\ s3?fzOf`'A -U"Nevș?HĬ˾LHj*,`_с  gݶ`K@wwpd&TV\*A(αO $y2/y۶"g 4T%.[Zw8nе-`JM ¨ t][ #_~.\ڋ\gU*+*SM-ߨ T xxx%I癌kvcjDB(K>A9aF;p"hRTe>EtIP~-A,xfhm'05y5-P#Bg})a2k/Ef^93_AC٦B] kGpKts8~ˑ-ۙX>hP ) Ȼ'uFXprԭ 7Äzt}y#E[/#f}[qt]O5 >Dׇke/4ֻ5A!,p2rtqTelނbN)s}|1."#ńi0Z/)my- B̌5j®⚒VQ-@M{zҳVϒ@OL=~μWt ۟-rBuyE+٥xqp{fx~z~"j۶(j7ѺcA Av8ޠ{v;v;-)j#ZS{(*k 1,#ŨY{/~[sūR\#\QWR!%VͭEV-5;84, BZ~q1\{"$ ށsA7yu 'gR"Ew,-+&{8}k̬-'~2!iyE]"3%x-bGLrBcDxb^0 p XH@"Xpfhc.sf,X&0BFZA1~q޿i5PJ"jB@۵`/S Zlg{ jt鄦 h8pt\lFH޲:-IAmSGCAe;0{fOaG{tm^[@׷:gd,%G*&kFAs\jP4UY5uݺL'ກʶws?t= Im)QSvӪgҰeAVꯕGg`hvpi!$9fBHuf0c7Zdnߏ Yf\l&͎:'HhVn;{Ns`wjG:bk/YUTgdMG!ac|/#V&9֠ ,X&'2`Aiu$N70Ɂiz][-Hi}J}+g3iǏm8w' 8l1uĩ b:hHjJIT]ס BS9N8ߙ&8urugt:ab\4C}mez) #>~FGLaP_@@Ʃx(,d=v_np83D}j A׷H>d͚2j($\E-9xP(sRV2mŠ~Ź6UsɈ2zG|v^gq\btA *)W+B:6G8!m-"13iPaضC IU\>aL^CL"\hl/tC,<| Hu_o 9`HՇv\QO,l Q2#YRmu 0t~˾VfOyMB6M憹cǔ8|Shvfuu((&;p_-Qd -VFm:jjYeARgczHrL!>RBJ͌fJja=w{-T(yH>Zkѱ =3,P4gKQ[ =R)I1{$ Mc ޖQCdLkqd)sV f-eIS<#J'@/_~_AKqw8WUMuY9g&xtՔo;*1A׶0 ޹f8{gK->=>gwww` Ӕ! ]Μ=8xq.&i]a#Re*>Nyi]YKuӀ?o%2Q\Zǧ2bd=K?LQ<6Aޚl)V fʪBƘTޜhBpCimzY9[ &B 8!}9&eAKsXWk.%tILj}b|dj'{QHy:f,1q,82c͑eecҒazVT@q.o 2D!4pMPɛ1!% HtADdeSN]WίeLтN,C=)@I&"jN^2!|x*UdYڼxqΧ3J:#6hV4Aq:,%3TL~_L+!^ /TrQ 2U%cURYo̊&&oB˨sޡ{4Ư DDKr 3Bל3XA z˰bvA #]sHNn7Q3W d1(,Tl(rG*%A^SeSAQ8[0lK,Ż`ra/f"{HT=y`RdNXN3 V%YNc|"IY"})2qJ!()% ?7_kfϛjiOHQK| MaөZ u+i|~;Sͅ˹ È>Z7 +)3ZË/@Dx'<=WD16+k 5v H}|:CDk݁Cst#a(Vyi/_A>~O8|!߇lpvt# eC" #<~c/8y9DO9\ 6gS/tHA`)%@[ܺf\fT7yY3|XaYΜ3ĕ̬8q @cQe^|OlsJ~|˖lcǣq2R$K˂@Caey#E1ry,V~<hP&Y483"{||BJ8ZMPv,g&,bHK/If&xQ\qкƋT}3'Y ^W÷Z #uCX"x^?=Į:j~UFإ39g6/5r_R. UYMV8 H;ڂP,±R-ˢKMS-s/48Ȕz`D|`H/!E9kd/ *pEOG z8pDsJ!Ns&gVN634}:`Ԋ,]Cީj(t:[wBP98Y90g3)%UBߒKr9?XwE"{;;7ߣ{ÏSA/_@sP>~G GnOBUe&?,&@ts3T;hͺ"3F }TZKD)Ue!xbU:N{D18Qsc4iz;Y"4hRB#:dPU`!@byWJmbe!ˌRؔ܏. bAEŤdp\ptA\˰T`'Ť:7[,I~5sK.WuW/ 0 /QgSWP(#$FQ̔fǎGHoɮ\"<<h6bQkFXvG0oÀoaڶ⦏{Àj6S!F+F,5?E`_}9^x FK^H aW_}"w߽AuyO:{s^՗bF#xGp0 Zq'Jc{~ L'ebF~C~Rq!3 sQy?PvamEJ;EOh#R]7M/I(S@H}H!o 12އ%g3e&{S$a⤾3Dbd}.*QGbӱ BD2_ \ʼR;c*HA=;Bq)Ÿ"i^&5` + p70a1W|O@Nubc_1v h@KtJc5#`?2K'wGd_Q;F CAoql=a>+(wBq*nxj]&̏;UVȂ8F}?_ad脛v)rV.m=u98B00c%,0ʡ45:$O:nJMf6O' Sg~=>H`t/"33"ĺK9~Sb|<b0&Q ?-jx:L{2$na=;g{/2 N!ZxMѸ& &`jM`SRP~K;ĉYb#*(NwLyW&[sFJVQ\JUld:0:bt&^sGN0u<0p1R3@fro/E<[kig g o*q}-)hjGG O}?rFKyDF͋,eN,Y+*AvpaTOLW'qꢎs@a~0X5.η؞mZ/Qn:H}>VU;w߽+z󼿹8z\^clwwl8;`\O?} 1OQKt8^<.\,xvu,Pիo(%5٣J5z~挎+%CUEpx>m694p^e9wxlaGC^[ep(f쿄 1ω "9O,H!|cbZh!5a#B@, B²USHo8:~y;.g%Va*Zdu H8atԘPzKs0L--: gsLQIbSs4@#W1@pvcq+H 0{7Fu^{Цbm";eG``#霏|S7_Q/F0T}`]dŋb!4`zA:ft֋ͦ!xTLQ@G֞Rӥ: [ˡ{!>Ι܋+B46|pcP[@>V}Y>T0V @r$pIGU~ɁyABXDn!O+IF= #^=@a@&cfd|RU9h"b*!7S7v'ן]T轘gQ!u: L}6cJ;–bx#/w|YLQ_ǒl:1=ffvo6]<E0F:$"Bb =^͔{8Ei樌AL}<K{,0,0뉲,L/A#I:b\PH[7)iqFD-9 e6=f h 8:"2}tz^}<}r鮳ȂP(̐tꗅ(Ex;A y=8 iҸUa*!DBkMaж= Z|!ܡFv{s1aqYF|A!`v.o޼E,p~~c >#E ED}!ƚ <*~{wi]J[N6y޿1xM2z"zG/_p8!k5"<<0AR\7k Oww;"" uΉrYxsSq/rDUUa$EKNd'bR a-V?eYDPUʳW-i׭\]7*喃us&b˻3NXq1˥eG6{Q;Eeo>ha.*X2cnEaY,I}Z)VKYi@LATh"6-IF\)}XuC6&pZs pvf6Mo% M̌޾Ɵ/=`^b\93, |]ံm?Gۡ*|WX-K8{ph,*zQ+,epb!'>gZ.* +roqs{"Ͳ)S7v{+A0bM`׿Avx ablUC<82e̶ѽVD\e X5<3onQW*r(^uęO6΅qzj@:Κ$e4#(σh $hz%1EY8Bg`Qc$=X.F )Xmz.T+ l 91#i~R!HB h#s,|[3"qđ5)UJ Cy$# 5! V;T[IkE)kB|cރp"#':[sn-G!0PGЮ!_k{ cYށb*1@hG 1ZO Ty=K\>tH?mYDXUU.yg!vg::/ЬY$k<6Şp8BhDdg(c:[èT:4UU2m!%(*eLJBe!Fa!w'Hgb3C"&MLϹDz~BK8Y눴 D:ʎҝY*616+K|vW:>3,<3oMfq&U]?Rq xW*!(e"07fFτ{QG:])0a0JsӭT"bV$^Ȼ?8)#a $ #GGPaGjxHtuG$MOLǓ-6 #:PA7!%"AL@@ ~Dl-5u ~/r&T=~2èʤ7$:7$#1C^(XOQ8_v[cqĻwq8%>%./8ߞY6 rQtG >g(*v VSiSe1SA)Fm;QVtKN `\‹rAq{{azՃ5t'uTQjVe=ω)TNIhmc2ݦ►T8%:[>7tNN;qrp2ƧrJLHDpUTcB0 5:HOX1z ~d8ZߋQ/Xt&3y[ٰMI`AXayc`J 2-3;pD#oel'"Hq$k>0(\:J .2`"R: `+ƍb)<+2m,H OԁM}q F<LLx0h݂p7,I _\.W3cc5Y p:+qq31L>9ќ`@;/-=xq>1Kf( 8x.η:y1bbď1/E^C0U-TՈ𦐽d\ѣLGH t`'4 NƇ!0J7 qJmע *OE-X VvD (q\h ~Y>u;)i{kP3;"-HO%2&edm-B0#E.;(WHiX-pkYeQ4HSyiO%ߘ)ϲS@DD7y4`O%4X1Ш^x0A_؆a|4rbza t4<#o!EGi , G*3Ⱦ.wmb!:FAfQ])D` ;Q2H5+8/(}(H??/qMQ2k45]XeTPL:vʪ2' q1jO>0G$>c{@ =b/aG|;z=Hj?wo0zhK|g8;[?²i4_9a[G^QA~'x" -s«U>>=՟&-f{vJ0x(,~8L $e]hg HǶQ.H=\ t] rY.&Sk!0.pl۬bT NqD԰˜ZԬd}g QI  Qf]FńEQ;+/uXt|䈃suA#V!d Qd`cLKUTZE_XAwGUp*eݩSl V4`# rg{ 9 񆶪{Mcd܌jUcW1y!(2ԲY5^ eZIMiVբ@OE'@Rp+6"m"^<(Hv"κMb s"λO@S:*mdP[Zb}Bua[ IDATQY<~zûr`lp{%^pvj̇(B:bLSm x0hQ4* r>ѩ0$ML`$\HDv]=[w)B{vIPi,{|O´Jhx.;=U%S]%ģ#%' `"Z1FgA N%RN)J>+~1 ,5G&T3}]} $Kmt)Ly&o@F)>]ƏCܤ(]G׌}$ԑ4])߆x00:|dAo"{途cLj0e"Gb>FƐ.Sr3g= *F:L!6 "@wCU(# tK `|V__ Me|4vGSz4$n|ӟRFas5t1'Ñg;#J4 ш~WӪ1n7wk4|v.1F%cGGzzgWX, n63T9^>]y4.9pE))F3, bjf>7h`0vY`Rnsd5NFu}!Y8t,r$S]UYJB@*8;]*Ɉt_CdP 8gZr9cm%n]UC9&>/0DHkQ4Yx("x~EFwl.qElD="(4cĚz$ؓwzk1&F,}\Y@^Ӹb,:9uP4p!L2NOQk-l4ŚyXTjPLk"G,ݑc 7824 80y~(,0P[ c( _warف)on92 CA[??l֛1&꩜^ q޳T0LcIT鄡 &T1H+l*3o_mׁOpv1XٛyV8o߽G,pqyvLtlVK1؞11pB D1 mL 8:E3mM{m WPaj1HKUrtT:"\>?GAg)׃Jy\ng(>)x؉${qIdܶm^\.z)F LFHrt6#'wFo)%ADQ $NBT5sʴZg͌ n61b$, bW"R&SMgxUBk;Wc|-g- 퐠>">M90cUK/~ ܺݻ3 vpg܍@B(1[B; \ta5،i4LÀM=uCE'Ǽ}[>fm6#&CHqܝ.&̉4vFCP73jyҢ| 24:phqs{Ѷ-1C]U8;[:ˋs&_޾ ۳ ?0x07328z{]ۣz坟aj,8 ΈS2Ŵ{cL w8?h+*E\bLjRqIb5I!i֮#!rQV"p KhEMzSi:%es99f̭irg!A9pċ,)4j060XѤ <#lʔD譕H|>2D1>£ci%UBQ`<"؀y 7V   cCJN(#7B-؋;mE`D 9X` O8s 3HUaR]~(H'O>cRJR3q)ԐAPuP`1`8EOD_a`g\%0x{c77^%|k`?bg}"uD#۳]ݩ$Y?Sg%ΞuGNf᫫KxVPL}9cA}HO).际ņJS|< Itob=dAHĦBȨtD!\b(2*FT1b$Bo :cZ`ԴʌmvVL Xvk> 8PwXv<6j9DUğPb- *QfYC$r+Xi0AFtShb4WOߡv P[6~c3CA+n/_Kch5@OOaӛrnIeVJ cǜ/$Tn֊m;-w;a/c#6ie<A#(kdooqwCϞ]bUZW 泫 LyQn0 "Gde3];O_dRMA>nGd12땠pBc4sj̔ѕNICF)!KwibuFD/ŰP 1FୄA|S;A(]c.jEdžr#ixlS-PʢT"&nq] RP:vAu AQxTQ;c 1å{ܕE"Co}YDVˆ10΢C`1&`>09bfaM^19'BɼQC`X,Μ[cMmͮx'*%Și=<`,Em g+Dt{]tpbZaZлqpύN_?E":+gEi٬V0 : $lJÎ3rrJ >LMz;g9Qcz`\23W:C}0ϝńq90|iO#'پFH۶YUzǶ!BuKY0g)4晑{s9x_-ǔ7R~QMu1iR_~Bgiʲ xJ&(m' kqzz"DcP6N%htd^׀k71B""8u%wqcH1@0iofp$prXEBDc%O|kX ߩ TEAb=qC1Àz|{e۶Mu3G#̕U1]8kt-u&[I5Uo-nnnnxuUajt/ȡW߾AuWz@㷯^plGj.GXk'a^?s|}?෿}áU iBys0zTˢܤD$$os"NZ:!c k%psYu"S*:Hzl8;tYjG !ֱms7z{wK5wÐfzVy4hVGL'RYʽ]IӦk}?5J94Q@>Ao8 O ~ W}|236.?NA@ UBt`Fg "A18GBw,p=u ]dA/>MkzpG9co;bgGWMnϸ u8VZ AЭ_s)q~@`\;9_Jy~(H'ϯ!4l h5SBccG櫺#Ȥ4*od$}c%bOb4è,$Y0_tH0~!w/>p@{p8smF sfIa5֫%qVVGmjpwb2ߩXG:ntbp?{-@X=@;绣Eo"^'Qi0߇ykxv$kOHs+r0ir*HAln(<ȡ ciY^?VBAx&Z]?zI"] eO,3onU:ZJ\j<igfFSiWfƥX )@aj1Sg0fzq%uq2JtJ.Տ)AƬi )"(/3̯ -b09.rrdg5- Eg QD:-ҿ DD2~:+;j:x<-%5 ̝:*w@QE&`zKggl$_%;R`tv (U_ejD+FsVdj9|B&L9Ym73}a_xlWX6 ~ӯp~E׶F?F τ8y؂! V%nqdVݣ, 4̌c ..-oF,3H6Lx̌ 68[/:#cjӟ|0z]?<ʼC}l0Ţy $Zy23ڡ+| ˥DCh àɵRȣe09Zq8CÃ>Gi|DVUP?*U!q7 19cmɑ|t5S"g6HzR0Iih{T@Mo g 3s?Q+> 1H36Ίe( a Q#05^ňzJz4f+,C@85:c`q v; 1^{ sX 0 h@Wf\.W 875 #] kP0,a n3TS쇂V Q VD`+D3}Q)GL1ExzP,V$Hn3:ڶ.%D/} CQիZ#JU"祴$&Z:exBJG|)+SqtUs '-T9 #81yO`;"yF"Vq( IDATG%'8Z0 S\3%GU `ŵPQ 3sYWٳOsCMi7TJv,锼5dԡ\a\d";tn&zT_KSS.hX ǸZIѧfzzñHXG{a`r簆eΩiWӶv\^m ]+{lg^G<F*dGk LakR |ϗux!b>61#8pж-+ պӑQ!cDp"E-Uf s4ܕD`O)('9I Ӿ@*ZG&9B~M>&SsIVN@Ʃ[tYdksXO `ƃJTLvo ̿c(A?@<&#kj%c;1K(7 @]3%$(:GZ9 d3F'nmݲdeقP qjp*b|\ʂU*ڒr+]'Emxl5>&,J 7MStWR^G`$D'Hp{{̂-yIDeAb ..0d%֫)/rÄt[Axiﶻ fxq_{c991>$N1+FC;]CTwb#ƗYBQ."ڶ\Q `E]U)Uzġ*'1/Q184Fܼ4 (XݔeEd~`4'?]TLq驃bzHfVG>u(g&~h)STJ\puř Gn16!}! !- wMXo*Yv pE|@]|._2E w>uܰD'8)K m5O~4Lo\=YZ,U۶{2^_( 7tr6*_a IO i/XHexMrRhy$%1g5sV/Y3I&&S2lUGb(s|u]al0#rp_QA:lS! ;[MQaE;ZOcN/fFu'AXH>!C:sVYyI*>͜\/iFvZ4NJ"Nes9y|ѥؑN& On/T7#BXi)o4RB4j0êih=E={970Y>#50|z$vJtF?i|p!) d4 /@ψ Ї}DM _~z).# 24yN|'2ӕ=!bc)tI#.}G!h܈ۻ;pqqme$#y"8[D,b_LfkcXX"  ,\tH#R|xʯQr\]^}=Hx~h3ND釘B^3ajg]gf? èBaB%{z(ԋ,p:p>)(O ҡfTt"a(Xvʻ<Pg^H"K1&"X>'aWMiSįbӢ n,Fv()=7)/u<} ̛E]vL@0r5lzpwP3׆[Uu/ =oH:$b]𗌡Nx2A,l c(R-o8@G9~CA=~i`k"*3G);{ۗ; cS'5Kt?X"$5]*}%65M|>AZm%(p! <]Cq;di6#˜ |QZRz *ڼ_3$f>$M], 0mٳ )4ZVF3M]id({Bh۾gI-:0_^lywќ.41Ƭ~4FY*%=8br-ʥkQ&]z *ϻ4*qwT: ]XK!἖Aqdd98baquX{~+8F rL0P%(~ t0:"n_Bԝxj/gC6L_rEc`q) m|ϿjYa3Y'B29<c7.F)UZ|W R}z:CߋM1u!^8 @< A"\z1n7 )+2t2YJJTdT=*gqsX5@}"Yvj-E F{DMɕr(y'R,O3JH?"EUX)ȩH)eTģ w'/V\Q}R99O #*vA1Qx\e $HpLM=%j79"V6&_TP^, Mr<9;/nn#|x3pU8p 81)>"9VX0@NSa9O7܌DO8Xl<1Ǚkx&T4odz-_>16 RXYTqz#▀QCeo77(ғC:C%eAB:L1ZV,wƖc;MO)yTONU &Y.7d{Ђ)D9sTiyeSG#c`͐D51UZs)DyqFHEʬDz⃴9UG_$\6!ZwC8&#= U}yPd$';)zR7W=,OI9fd2f*q5,pL]c1EȨ%ϟplq{f~"1z=쉍+K8  k!0ĥ (D LH䵏Js>s>VBeN'9V\^~9$-NP1 ʠr$w2jfʣi ヘ'R)xM sWHv0PufQg_A;-a$Z5WK,38:4M-{}܂,+Q^~6Nȣ3r*fjՠDl)Vrg0`;舮SR BV|1!舮D }KdTE0/6(:%IMAϯɱ-:ާ (<(5ӂG9׈Ti2cK_z.Rdi (be6өw+ľr.yaŮbWwղaǀ?͟l~0 | 1 XbfFhRM"Yɼ/+lY$3OGil+Pk|pqq{{_!M,9o 2_<O/(zㅝ\mak)*Ibs0`,}!Q#m+`ɥZێhz5%] ofgi z@sQ22D8:֎N}~Gw8?"ZpJcƞ%##8}oN)L.QcfH>|pG?<R ޲Y޺G)Gfx*6WMJ)Zpzl\4;%'(%Q0{/'ʑ*G-? mKDjb׋-6l5!ht &_f*v{Bd7#1,|8fϣZ[4 23G `/߿x<^_7_Z &ܒsS,J||s`ߵ%Z)r xшհh[mT; o\M% )v;cûG)xq<3s]M 8l'LI1Ja'?!%^b=XaU@A'g;HbFEmOG"l b`'Me>j;T^>'ܕYLǩ'=MH{";/< )w!S}~ `uHD\d'=c2(J#g4{"z&|Esb2X ӭoǯӄ7*K]㟅O=3I{'Pn !Cnڌn=#}$B|ptwC "MuHfG1REV*fU888儷M6L,&Q!"ؓ\ꨌy5KzG(`f'±QDؾŮyThij埗w@5l[<vϘ&1SuO0 AujǖmW*J4Qu?y;7sL"E/5TTvdX3ߦ !1ٙ*yN<<>pss7 ؟sfDݶGߵ Mo13#nnY2:t@ڰwN3؄$H%Pwѱ ROe-PV/o3`ߪ0)wGE6J]$BAI  gOLfNpϳ?93OMȃ#)u nM1 i5M( u=5d$BY jԂNP~!3d#vDDv&wWa"M)쒖WߢG*QJmQ2B9dQ^v}GTsף8fd`a0UӍE͋QXO5ԙ/l}Vkxqlozf<`o$ }ڷ@)T>3T%bjr$=&>m|_yC{ bWXriְq8;g/m\7jر xbfEJRS ޼{_|5aO}kM"<_T*jgn+mmDҋX#]]InBBVnt8uAHnF!qi m ;%S-L\`fL Nxa-3I FŤ>#žg`J&&f/Ʃ&(aj#{+tWn%E޽9LQ6)Qj%dd/`tӓe>oNKV%GdFbGV/;a#KorկMq^x]t}O@iҝ6o:kq }:sWjdT(B }Ato^C)収C?(CEEeś/nowZ$u"En1ZunOMb8f4Cݮ"lw[#5h XlRicRf{Bs ?RQip(k$Awk\::@5+4:&n}A }Zk<<]x^CS iD$Y^0 n𧇭(";݃ PBDb X.v=_(ŇS65|PAT]KRVcʚ-&de]+GosduG6O-B»sRPbRabO;>[_ lSex*Hߵ,OW* *YvmZ'V.^AtYٮ%NdwfcB-w G. >my|t^bb )m܉8AKg*3Ӥcq ^jI.&T8Rx/kw@C p 6;9 V&e_zsO5V# τ8SG+zŞdbVC<{xggg2OO|7؇EtSA*>6/ED+£.Lvˢo6iڪA"z@D rp>N19«0A & )$=C,h|4"BEͰNDӽ v:I) ƛaJ#X4+EM)@Jt} 'icڶjǪsؐqH' ܬxtެ!J`MKa~(_]_P74ŴBd&q̫0ܺ׉|RB6EOdEɚg X`A*[SLTuOiJ(:8p+wxKBv9ǣgSAZ/Dd!*ܕ!ߊjCZ9KdsFeFgmb5RfJ\Rv H׻rH, exOC@.E'877$"tpܰ@445Kzd%`e,iT¥0Mx6e;sME\'O2sCEBo 45Iļ@B'k` pwyQ M]IDL~7̕jpvf OxagND裏rT?)V%$iG?HvqpOZf/-gx)RBjQI.", tDNƒGo4 /&fKfψZ< ҷ]kroXh2MX3~jRhlOx6-ZW:aGî=-^:2=:>$crM.Bg.64: %Hf9,9 ;\8!(_(62hEpC(X΅ZڹS 5*/9Cjr g~X@j8[D'#΋gRG&|&UW:'/𽗟p<78L ؤ; ^.Zso$*3DcIu-Tc>O'OX'caᐳbe LDB><>>$>gj⶝ET;'LHmc3ԌȢ#%3&BD#3BvӦg298o>8;ɤmj׹禔D*m0N3жMi }:v~ ԉNߵ-vMdӵM= 4k?< }QKk>]C9MX ;v;yҊM&:W K!lMjCPI~yI܌lWl{eS*ۇ0r,擑ˡdJNg>Mgxxxė꯽mZ!ѶꝂ޾5P3q~5{ٙZ'SA.&V'.7Y(%Qژ~^ZapQ,_ QMM/a7DeY2K@Ux||tK>!hu['%Rn1ܦN RHeEݡ uߓζh!%h\nLhd2j䭉ֶb-_@s`zTRg$ĵ#.'N~ϲ0ŨWeR{1k;֌%_:}=\\JIv~[[/ Ɵk(6$鞊=wEܟmp.]a:kϟ_5Oa8>;?T*ˊ1RrTn<Ǡ;BgJ)yypն%BP^Jxx\}PN /[R; E/p0ϓ!&Gʕ.gΉk-*Q睟mX7~W4SzSY I@b룃1GjC$@#iVn6PJFXu6 ]T\ : mG_!b$L+YFq#fBu i+;|*bm7ysm;DTLyQrQ mӄN?8/~i,Nx r2ba? {𢵥v9v-[ij=Sx7OSAZ6 kFeErga0nrˮ{n-&t7RvHp[-snpHmŘ <4/@1C+D!BNOC.ЏACB ]agG05rT)a,bA:kP@G;qklF=m1N{8 QSyl/ ~sSNJPh pf ӆyQ8%mVvE syõlpv ;sWB_sÇy qdۅZ]YWЅhP-~g\x_yz_ӑTNMFb*tD~ [\"*ŧ+N~BHyRm "V I`d-*=Ws3I^o N7S_h'XXSij`hs{?Gܡr<kfRJ() .nXl4"uIZ=þD vq$<>>xsB4 6 c]'R¹mN3Й*M1zpS 2lSqG):( r4b#R6gg{)γxwgM>}񈯾z //,.?*>O*DK;l6@׳Zk}=kK)OSA:}Hp:#{rpZlF.:K!%[V2I.c-$[s2>[W 8Hgг[ Xߕ/FNi$ l-EvhMcZcf3DUh@2g([}^l~0F~;ϐRb۸ in چ^IkXdS4nB1>yLa,,AnǙ#6bl)q8it͢׮ 8EmiG9_>ծrhFk ;;hW׵W8x}ahT+Q^P׋vYsI)ɋx3hm0 p81xv?;#QƊg7K(0TQaYJԾukDplI Z/:dXy;^63ZH&" KdafǰZA)%D\2U{I{ʌ,>kSRkr{۴a8! }uRq<# ³gwGHK?db"^g7K[lg<ƘƘ4|_`-~: Ψ)|UDuUWndo~< Sŝ,ޣnE^ ?Dmyvo3H)qqqnh/n7k/Y|ķ 6E">vaWDe|`oRmscV,:9ZA ljai..7hpm(5.̰WFLv&XS Rhl#EU\ߙݔ9"ү!#/HlB30"# uJ#P{%+l܏MӔ1HoH!,Y#b0^Ƕ668`<7Il=CVٹz%,鏨ޛ{ Qڈ]-,Pz22f@˰la!sB^MA Hi}#InPJqƘ30?p=OFkDSD)];LrWc=jmDj&E(=Т#5 Zz)x$S2~N䦑w-H\uS=iBץHhnup{kA  PM`we؂lFa/q`\/8M9}kiZ( ~O&G)ڭ%faqb 9i᫃$+I)u:eA+d+A.`T{!Z@i)g%s*.i*5zmvx"Ϟ]@ggg)J)|0 #~Ài`['jx/cڶ_֯4Z{g=|,CB)&hmDm6[5; j:7փl_AkHlpfch6X:-Di+:Ju{$QPP~vBsyXM# t&ZuBTnC̙[NHk 7 c8IQs6R{LffFO/zͼ4T$8{[NdVo,ppyZ6wxE,Q2MSA.~*wEdɫN^ACe+:}`On8L<c0ύ1_{ s7 հ(n<1t-'ق^_^^߿o}9}7o3mΠq- TMH<WE6$سQWW1;RvD4D2): QT̳3%'[31f )$-6>B9!p8bg<>"(G9 ^};"n"FѓOF0 (6LӌoÃ_SF)ߦ>(p-|_C8ĚHڠ89W٥TbnRU}3\^^k[Oמiq%\-a›M00>ˋsJ)|PJa&oy40_DJ?"QV!Sb>E:'g+aApL#D#&+&3C<4@]p1M5DEډh^ ~h>Fk{mϯn7\:p?R .'B.ѳgg{a@25VY0<=ޙA ÈqtJH_0!"luBr!:R]=q{"88ƃrwQɹ&.Den'"}ѡz(Rqk(ll7h={!orɆ]#06l6h&y1_.`^}Y!̬ kkq3?_K"|x ҷW_aTvr_rwUr"3imFZk{8Rw'_^@+SbٺJph&I#GuxB4ŒS0J*.s6̕g=dF>'w ! !s 'VճƬ-RӨXE>{~]WLLY A0H$ V,Ω.4(ϳNMC6G'sy"pY!?mζm0 CEȨuƓuwwZkY4ڷ~Teg|vK6}A!wOjT{A?}q[ BzK;cH0_1|OX|Ȳ~ 0DŽGVZǐ{{o"z!wݝ F9>6:qqݱL/s;߹`-^BȘ:>@/8_`^l2a[GUѺp<:{13g/?k0[} ?4e=e$s"ƀyaeAPPŨ&/J*[g=#|)Q01 nnn!t!^~󬡔0L.rT IDAT4͐B *NWU8md G%k?LD4XtOWv[!(ԤE}H7i@lΔ=5ROF+j#qs|?zen$F<! i/ҔY|mwTeCKqL77Ʉ݁F{S= PcGoj!3M[['!Y䌻T9nac^~~4 lxY19Uxp)fN"Fpo6H!7|8?OǯN򧂴]tu>u߅Cb!F1<t*.{myXA|,r~V) {"[R[WH":)€shE !i_2%ZFG CNOg'nTD}t4;HqF_3k Cs[Uz?,w|3K&K`y+(vY)EhmzJP트5w]SCC`g*qŅͦvnA/?Jae]k!]<#<'m;obAI5(⼢\'BSA"N.ZЅ%-0Z`-R5ZH @`|QgDHQmRB鳞BA lp r=B㴢*ŽfN`J;<ѷEnSR %]""!eH '2ʤp%X:֏8M /I7FBᡷEl!2{N/#,m6hI G k-囮in?ܡvGΕ;ȮQ떅W8Wm[fQ`,vАЮբ`X%;*jTm΃mDФa~c'$QFNhX j,v[t\]q8\F{x{RN@e7[@gA]ey{*=CҿW@JO!FDa.,e)$lB)U>$C~' | YAa-awEZkg\8Dl!ܮȲ-v6҉pk\44e JS2^fGFM=^r=np#Ӡ2@f:eEEzI UW#r J]ٸd\Q(b~Joc)a x;a}"7! _Z*. X;ZC.%]lo9|QjR 7~%VTJB)ҴT_a+0"m/~Cbsv>;# cbPL= !-^(:0{Nm&YքBE}Toii*qxc o^kE|lg.'z T"6Y)YۨrqsW\꧵qvKDh֊,*ijd jD|fɷ!uﺸ"^!xW ѿ !BJ}C7w o޾Ǭ5n\l˻7oݿz:.HqdOUs?ɦ]~y`O )+_wem.]DIjΦƧ&K!KL;%9',iaA5M`gNnQΉa*^)*NJ(rrmh^?fb% ֧A4IzLH vePJSaA^L?6/rڗ<`3"CߣӋlNeA}yI q똬@׷h 1p05wxlx ((J*x WW71V .e[4a :$Re.ybJQ쵙-9 I)*wr˾'[lIrD YGРV+s5é=xEg }/?f9ww"4{M!ƏCPr8e'FzZp.߹^#x #1^D BlTڵc./^4fy(YI0k 0Nxny~'}Gg ҿi 8m_,V)V&:]2H g8;۵ew`?! qv3J b2^? q1r4!Obp`^,s|)dd)$]AQρmr07"(Xb~e~68kz#\* o&v4 sO.x¡;'Z,NNj`xh7wQ{d=l}ҾGXfrI$szOwѴM?Skpf0nA74*&QܺBHMws6y֗l0kZL+ky7x8<>>iv`f}8<O o4ܠo-Z g_0{4GkS(w7C-hbt&Y}Zc|gD8 c%[d8V(HYa:-bԸuhqy#{<~Txxtc6Q!9&$|#d;o~C׵zv};}H9\}|ӝ1|wB;M4h=M84Mݠ{= ixx~*^s++Qާ.:CQǷG{WgOt7ZoB5JQ&7nّOEw8+3OX )2f_yTMcj޺=)bq93u'3af@ #M@4%Gha¾0 D:z`puvO cû,YVb(U`QiE״pa (R]ZLevҒZ\pyyg.[Z;I-ۇ$7&BK|&mmq{simu8n[{9Ϻ+RbU48EG T맔gMw\7aOqaahW>U&#I-PN\(&|q?naFa̭R !T<|TH?cjHłNęՍMKWrVwh;| lkF@ )<%z".?u{ ,$x0 4{ wj-(U9 46 ;н"vMiɭd)&05xݳ=6 3\pIHmВ-'[S4"$U9c*|.vJ!8Tц]*6:BDTja턊"ƺ4: &ѵl6vnRk}9 0O?7D9S۴o01-O770 qӻwiwo5y4y*^mB?o-FȊUd.ۓZA*Te!3Xba&Cnb82򷴎KW~5{MV"@c?yUC"%H64&g muR@=LxZqaϞ߂Y&ԯa5k>tޯLY+:QZMo wS۬pv)ùȎs꯵ٴT c[LJ`r X'ǿA0 Rs30=p#T 3r q+zBK ;5$#Tnae6@DB9l,4w2yhOsW} 6;ʋ"vw%hDX0rzDfsOBÇ#Te. E<ۆc=Ϳi\=@׶GpaDE$/pELgSB,ibi5IE/4!(IN X lWV*2 Kqa_ 5Y4-zJϳk3VE7UgmnҙV,_gF!z/FX s89ɢb%tE_Jw:oj IDAT uXA} ?# !B\n6lu؄ȍ%DH>1: 1FZcx[5O;KSJqŨZVZk&"荵/]_OnI7+͜]V _'Z]O?9}b&0 @h}t~Ǐɓ <~HLڮs;a4#K»6ua&4`y5 5!?dlԘKhbaFOD0HIB'y?EٕfFcI &t>i,f%m"r$~j5y8_~R)o?g@vS|d4vi))k`>EgE`ɀl6[ M9 4UJ^mѢCv7{YQN kv<5@Cݥݢ_X!I9viAB0?B/fLOщu!!MSIɷQN](@L7"0{(i8zqINgĢGd"(ǞO6r,#Ibkn6-nnop=VUss{Uvzݶnm;ԟ2U#FK *~N&!t&7]B>0/|JlT2ͽ|Fb 2i>Ťōf:(OJH)rR!- #,0{cC]lvҚNT?z&z/qBU3\bI #+c,l'p$%I8yqQZH1T_ V)e\ެ۷1D-&f fe,P"7iҘ7 >0)Ebj\tDbpi{>v\:)r0PaSnYNk:NAkB4IuW|aHzws8^P9\=2l lZ12Y\y7Y!;3)'|o+)%!ڙN[TXĪvKbk_g}!$ְf t(α=X%".(i~-#l6_~vfy8nNJNCj's䚉MGNH0%,3m>c߹A0ސoԚ_[? zx4!2y$'8e`{ R$tqK !T7~U.n,]l;|! d |%!1`xz} M0E%\vF.A4l7ר ?^ɺŇu]7UUUmpPJQJm5E+[uW$N0(4Hԉ :Ĉf9 %e\Ӯ,hf abNd;s2XJs0?~|0 3ͺsv&_‹A"X9Wm=T]7(\G PU5c儖 o!{D lf-DD5 6J] ,N2[nL:{]:6 <ܾMY_ʲp{1C5{DXk{5IB|e] 45'*eqN6|٬DY i8/ΚA;"t{Ģ:뤢sÔ]GOtk7Bz 69-dRJnW^nKv[kQUus{{uu8 íRa kXu~A)=6vK*Np#"9',*J œ9%O/x!P^>NhI+s J@)vwxK#Ἣ DKE[>-ȳ t}v [~0ӏ" h뢁fEY@S&rzZE ن"jAxZeV򚾷4œy/* G/a6z6.^ k+(ts,K]i@g%X7LU˅ׅix~FNm8$hNJ% hh{pMۢk{T;n/.?\Vá9in~׶B R}makKNJ,w iGIpԶ)'8t9" hnbT#a* gp*)sscV3{(U) 1trJpY~wTGL7ųș`TfkD{-m81>g"rق&X@ť*6%u_:EWĐb>׿~٬}]֦]ʹz㤠 ghJ - 0ŵQ&lV]NZ3;;sK㗧E9luv ҵ, f%ch4jҟ٥:R ڌ`ؙ9l'`΀AJH+ zߤظLxQ5\}CUUx7x ^~JyFTU[8 M]RF*';(u)e7]x~&d/|h\u&g"bحVmtzw3A˳40S2~$ydBZكNVN;\wrdLMmہ s} % cQUڶ e;馠uZxSFh8Ny3^IHO顉^1p :I{` yqhX`1a1#scf cBgr ZFu#ʤgA)I П,3nUgJ &@J+`H3Mlq faX]=a~;(Ui/_毟CU'p Lxh#du4,xa;N29rbȎcn&xO H1| e~/Â7dR>zb-P,l\ى%Ԃ\)LP9AZj&̩NRJ`L"R{+&P`\>$Ch9Q!Ke5pU'2!(zU'NMﯦwWhşg57e+M(tÔpR@댿 =5yzpiex 쩢=Y"dm'zr8#봜bZOW$8D\,jnz/rrZoሮmC۴6Xn~#|݊RR^`%OLH F'!Z;7)iC IaAe Y&s|̳0@.*p9s-\Xkw4M {*|^B_x5V{ͭuhIp*0c/1ӃwP ~ MB*_ȑ9>y/acXFG(е=96\&/ A4}fI$Ḹx,p~Dv֙:s<~ggK<y,ˀUG6"EJ=Et&( ,Q49-toe={'KXby=ݓs?c_nx]b/:ǙmCNє3iG VD`y:w+Ȝ73dq<&vdch-!DSH>`'>_?G0xVJ(sM=9/T V#aYm<7jY8??s}RJ(18JK ad*nb<\idtw4|ja"T+H:3?WLYXXWe2AdV7;"7g'$9D۶rP("ݣz2L>}nLd6/eM -i*ך#&b e.,uBlNؒTMm0ty'%C+v;uw<pu#ڮ=t}B|#|3HF)4][vHS)7;$8UDtiFL>>js)`,csyi"-ԉvٯpGAG|M1rk>-,\)稪y}bwz ce$3%3aГ?/^C>5"]B3ByrOpetfevVxPKFǔ6gLA{1 W(LSMa8N1ebH8yO9OINH`<_ ^J1f};v|>|>s;v$ڌ { 窉ɩ{ꌠ@vAPHaIy,B$ጨDMo0%ML9CLYh m(2n IDATkw{ݭ ޿ kz뺮n1 Vw X+K & 8"#Q()3Y}DdٱXaT:u'] h wH,|+Ȯ̲lIN1i}izb$ SzF-{oYb}S 98Zxylƹ]cE8>z"7F( - ; '뎄q a$$fO~~v!O!#4/4BRI4?x-noUݬw0 o;1R~Rr7(\)pPgQa bKF]3$aʭQ&E l2,{UsGM7kJJ!h!,-1ùe;l \22]ɪM,1]ݑ33 xdyaX.r>G9ڦ5!0{MNX'\=r'BbMO/[j؉>1O{aTdE_jkjgRFv@zI]H:S-.7ZGOvpKNFntUM\W'Bfq1>j,@B21 #Jlw;ys7+qZ5wuWj_RFW`R8(F pN&&SUh /g1> Qp؈qwfV)L$I9OmU[+z){=D#D\>%Ƞ($`blWukG`@'@`e7ؾ]P';Xs,subCӶgmkLx0x28P)|L'SS)$]rCsY9CJ$fWMX!O]]^Cp0atTDDf 8I1|̅EbƙюEo^b1|>ãGgxt0$ S\c]A cRҰ t &SO xS@B$i[WkW5ڶǏW8im47uݾc/J)u`Br[NqTXG!*ͣ]EiF2Kة\mq9[ZhC`*"8m@ 9L)HB I5dL`xes͢V{M7 @.1ٵ>Ig%dP9꺁RUxnB@vu˞\Z<>RwDno7J$'YDyЧ/ٜ0u{a=x:dg˺3()ƶɄY$d&Ɖ9'wS̄Y[!;yx7}:/ +MG u3wGTW>!"',>8%/mBth^nÇUӴ~B ]V He\ mXt~qݩ얇r7vpxLPD 9GFrĩA)Zц\1HƑҽE3;".U"똑'ci_5ap,}=ڳgO 9~+7)aQӑ0p:D(^'[~3!`L[6<{b\.ǣVoжvDPFyjg7F3&%.&Sa6:(Lau \gXJ}SY׬u޹p%C&!)`Yvb[4EQf2?u&->4#<:?B;ħ&@@;M[oM aJW:be9sW[Ɉ1d'z$42bMd]Wwkv{Vw8꺻^7?J)n~~+T`=қWn 8-scáG*uʒAFO<2`k_~E6L2‘' ;6 = L=EZcVKxBR$'fYGH%M?RӘ4"wAӴK:2,JMćp'ɟ5:] giQYNЙ,<5B+ iì,s6Ϸn;k|cp-Xqvz"w %PQCi~(\6.\1⶟C۝*ձBulXVolMSnۯmv}/bI6[ ##'58a;R7`vz/ R}›N|@Dp_\,)1qil(43i$ sqooXȚ?à^DqTBb~Z1<[syAאҐ6д0D(DoU8="^Bsfz8VIiZƮm;*~TB\1mD~=IZ7r۠EN(Ɖ ˲Lr{hgΰӈ9 Mb{Q n:$fyY% '+SEXcQ{r#ϲ aa>+KwNǣ[ˡ,+t;g۸Q[~3Sp7Yٺnp<qyyk;݇ձJ lx tU;NLKFCF{irg@ P)>)N3ڢ&='@H)CV͂Of7TRɑY#Y(` KQH<[8ŭY))4SdZU}Qj{|t*1؏靑b>ógOP7ڡ~=A4PuG}󑣮R㽈pԿ$1,ܴT{$[ދCT:#(bIjYnO_Ng8 (q!L&(QUXЛ. @f7IZ箈0H{Com5׉zwp,sEbƒlA{A:Z I{*"F,!ISevsU{m[G{A6nluu0R)={ = 5f/J"@۞5Fhr]2*Jߵ_sC:VUL L0%ة&H s,BQU(3EJIrwHUxHj3Tgƙ CbdHbFp  1p ͣ(#⫔g-k] u6q`N`U3O-B zQZo`(܄ms Bm;ZЅpL/N>>3Sa+Fb~RS+՞:k4I5<i Ha\31a^(rM4#NB[]:0j6bѽ"Gg˰q(٩Ų5Pk.l (Bsi:|>w!޾v+GqيT_A~/ +6Z'DT .46#Hf<:_=ӥm:=#]tJ fR/XR,]R> 3{h/3+ED8)?E=NH"{GE{E:^.fwÓDvGU7{@&V]ډ\OMdz$z:Eຮ&t3ƴ+<'Ҽrv@t8+Gl6[/_a^iд]?\I[]`k(&f1ԜE/8v݌ )^e8$ >$~I 9qOL'-J>?*XRՉXY0&?$WR!tSANG,jOg2n$+] 2;%9"R>zq_j7QiTs?fc7s`J >?S!CNIbd KRMu(NBaZ=4u -~5 DmЙbnG2{)%:dً c19iqrIh#C"W8uGб$tsGYXU۷q8qss+}æ~[U7 G)A) GN#ix^kRWKC '" ݍ4lSf9. ,Ŭ,'{[*`wnxRma 埔B%)NkdX"]!hFe9 RbYZ&+b;.40*4&;xQOHB.S>NM& _(lZh0X|FV4 :pAY.x1ʲDUծx)B y&mX?%LLSitpU"BtD&w]jAL5  \_ /_LZ RnvG(55A-1kkfEQ8$P1upE8MX:)9J)H6uݠ4Mi^ߋfkvîR]/G V1pm|':MIz#DlvWʔ$y} rb"+t0I̞|DvP&O&[Tb1Wx@7YSJ(a # kBxQJA9?C}oYB!,(x ҔJ΂Ɉ%2V{nA%K+ixZ8'AL)LTY-lV2O\NLB4W@S%~?5kR;Rĝ ]\aD(uWJH 5~H\!Z.Hy:⌘MY۸衢HQԎlHtmW0g zrW'ŏSf޽M wG𕜞:ɢ,'nZ) #z*9¸eK&'#"M4ғ#M3JnK1B vh?\vYuͶ7mׯۮR=`2MĨvRR:i'T5a!QV{tBGS~; MOJ ~W2^l8'^IF{-H~ioR7`|?tmz!'2;l18OS~ʄvJhvKlr1N; )AnD#^wJI:h6c:`Mi  u'"2 %>Mܠzt كAQkkJRoY7rR kqTfQR Ibd QPmvőU4 x4!/ C]ڶCv(g%?rO$|_n^u^ޮm{z!V)4ږ87L:!/ IDATRx 0:&TRz!3p nvDc]R ֯AlL1W%?k"CUDLP$|px^Hy줔Ada ܏irܹiwʱEc]< Xε(6E 4 vTmӢ*ӎzp {c"ŧ4vsS !&{L-&)0a;/\bw<G ĜKZeb4(,g0FUոv'oЭwM;Q`[#p~!逥q3HSTC:ju@ZڤHK3BF8 '+ "?O 3*><}\s9vM+D0~GR,8s?7}/\I&̃x~雓",50p5YI0X,H̽?`믿~o޽Vwˮnz1T]/QZjQ%4s. CzDe":7R2 0F* cVtTo=Yy2To~w X68 fA2B#]@qDCc"7ݵy`B{Nއ Fs.1M&jC` 0Q-{,c!X̰ϱ0vQya9A'Puݠ;|˗7o~>^k7f= ߘLcLB=a1hXM&bJ2ӓ"Sb %& h'"v<RDlc (Ggݿ焱Kфqs1{Zg+bqF0a@'ڮaHg>j0 ;bKiINHG\XM (8D`G?q}؅cHȝV*I8O8Z k9M]Q$4F)O#mkYVݎ[pwD1'jjBו$DrؽzbPml4t5<0(gȲ,0 y_\:nbv{|x-?\~7߾ñ]UbU?ȵ.>| ƺP?dW$X qަ2ϊp'R=b̠ɥ#1*!$X݈a '%NF2@Te}.H?6 r y6HR~ GwŇx]m,i&I¥Uݮ'۷U!sL)&0d†GHa/C%ey'sdavz/gY6zz1hcCg%X,]cCnm2yMD `臩Ĺ~} |hR[#a)6p sE'K0n7|MEj4C{Cb2GHp`]:3MgYYYl%M#S3H.X e`*5pjRK {ǦB$AJqVSYyLz- E)߾LCaT4M9P鯞)dǴ˟,Nq=#X嬸z3t,Ki~]1ɱ0@`pXj,;C %,H$tsR!e-P99ʲIPg:ҼѶ-D]7c7މáwp~_Uuuh$xevC\L#`Lw9V@7ux:2<%n9=γ|r@E H4[|#'5SW WcTt86S#E ZtF@nÿ 51tX. S)jbz`3ɹwb1r^U؈nfY3Kl \]1>3cДX/sƣx>$Ibd!Ąsi(0Izi9Cu !50!m} ?1(Hև,bv`E*WCaY]l; ip)qh K"g 蛣,K;>!=/5ڡ\ʲD l;Gl6[|lx8Th ـ8ցL/vI#`\&% nKNSRŚڔwъ١rϓ""6 tOl"\H#ȟb^my1 ~oxTUlJ*EqIQD$I$SO4-ߦ8P}ĊCJYtrjdCP^i+W._E@8{!Āi!Y ĠWAr* cCH-7EUƘK=p-,~L_iY;YJq͹7RRk֬}sSb"q~DY40To [RBӴ]w+TU%߾Z^_vuӈc6BU`L"A^㍦%KA #qYPf,@ !CӰ l]xz8e1:" ?i<.&D:=Q8Ϟ\};\$aC\ɻMtX=iV20e&>#UmZ&f.ܫ< ɉIVȤX @1+>f<R3 10kJw+\"IA^9AbA +HYq<h͟]יHᦃQk[`""([00ӕ۳0 eI 8ԽvK"όAj pdi!nzOQ$o4ϐg)Ζ( ]sf3, יo;v+ݭ'l6[|xݺ^Ci ̰SFQ)`SW஛0c BgB3)^ k(8>tmavj|RgXytxdzB@bY9gHA}6?K*IJEJXn3H\q"L<1b֛$:Rh_A\KҾr0I >OQ%$ ڃO8?2מ>"c93#F2Tkșqܻu_y|(w2yDtqq<i@Ji{fn{yZcUth 4VӶ]D)LeL;FQ !! X5p11$%68d4!p)@Nw_@ sA/k%w *N߁y]]v ݿŕR\0l(m < I{PjhHa) VtX Nvg+ͺYxq҅,>Ӧw0!bcA_ָ@8;=(I& m~CJ$'!4%L-:B( ;Żyi؟OLf{+ ˢ$]Ԃ@ B=f\Qd,3erju+}f)~h`1ѡpSqSwc0OLT&i&9cPIE `T89`d hvљ"Dg }G2 C wkXwM %!R֌hW<9'6nTHau-D_>zE=v KoxOW<@k↖ ^E bc-\AiH \#i4j~A߉!tMQ99U΁4˫j⢺ߖ%կ@ng`бs?:8<ȵ:eGSvB|̂MOBWB`. hL?9cS>$OQO1 cFP.Gc@g">:C o;w)5)߇xfz&)MYwf.}p會%zIx![@R'ZXs0R謷 yboP%8/9%w%YKZ() #`=:,T?VȺXE2Ƚ `[Tg21VF@鮬/5*v_5l;x~q6-nwsC80/1PXy%Win1pm,C (C sPEm/',|( Gb@9b=k0N8t*y4V?+SmT3 wFO7`7V%$EI0;"߀4lXM D}]ڦE"xwKvIlBy|2Md|40&h0A #40ˠH/璐X)8x(`\!(BaSBwk"o^N{DTxkPFoe #Ra˲|#E7Dzp~yU?U[=nw s}nDBnlK+H,l S'cHQK!S; 8*J0(PDGv5^ ;DR؜N0Frϰ׫ ?Ι^9|[%swSd(۩. & -lۮ'Ww>Ofba"MuzIm# 𚃸H!Y?Pla}@c툄X /T ?/:7٬O D) OGboQV3mϚYé:;.F2sf٨;;{ hZB%~cӶx{rm۹nkDD ,E.OCQ2,#O u}',BgEj;h)2C"z:bU>Uc*76k#Cj%L@&2e֨DZ5,縈8/Dq;^N0}F"Dm_ݞڮs˾-wgHtL1 Ճ êȕo^҄j.F[ a,-d&'d+ ł, &E<+E =jWS)0tP$Ϥ1d 6 4SZYWBDLY $C ,]<~~6mW,^ۂ\Svʒԓ؏jv>` O`(z`|vn-3׫h|7"K q \X#,Y4,G'sUrQ)7G]AG7 +F IDATd*5^cX0E-25@`lܤ,,!b1w} 9,`>î~+?/.9Wյ{¶sg6 JdP"(h(B\D@1(%$T4Z^F|5^bT^ ;,&U.(x -ǖ&3qwAWK &QF9|9i p4ܳ)Q[Y$Ȋ  ~lڮ]כjv'q'  e)< cd,D H6Z "WJȈ!QڌDk#ȃ£8=>!r7k`DՏ=>6=&iюq_VScVV~'ؖ~ ԙVwm1abMMIr71P+`zC$+YX\JP,I?Ȩ$`<eYr9<>yEYo 's=\\\zvUֻ5mQ;[v IJ73q3ڢ@3ڤѥ_SGsj__Yp0:l/0l6[X6x./ oWkl:M۵dF3PXF50 B "0. 9.lTH%N=)308 BR +ച'!.`檄iJ̈́_X"~^p.tj`j ߹ab3kowOsGrH]_f:*\ f |..k^ ;AiV0Se aQP "2B{N$&1[; HtݶEu0͂ +`( zQaܧ!^s/H8Gö;p?mUU /ۦܾn\۶شu})Ѳ@4Rb;SAO_1rF"$`yω8婪ܘ "rDwm@FGV&ƥ8{SW\^ qyu3yK|(H}LN:}H' MKp\no?g?9+C"׻щEAQJ E8Hw2*ˆbH / "@~Ή$ .<\T&X@y2bEK@? Q1&$ g,wpchaA4O>Ŷi Vm9tЎsBAFnCcNjD ȁ*Bef<=#CSW" $5WYHG c3]!2ZBsHB DlHY/JiXL͜( N?)#+S":dG#"I#/m^=Q[o^ޮrZv\.7@"vTςaj^l:|䜃uY?oF]RC<I* !U Z`c $<"O}u]`Ri΢(xJ2"ђEYBa  =X,qk-\x=<;ivvU#BUι{4")$^owߵ~pb,vC ϢI跲40X{^dvm@O1-O >Q bWd+Ѩ0)vT  nGgn}V 6ia bg 092ж 4M 77ujf on\Uzumz7H3; ܴTO͠jbKTihCR0cG`v!5EDCp\Ql =ʨ>nL`*0zYpm|@pxA.g&8e6(º#p)yR&> zl;o7_o`L6"V^>P)6$AZ:׷痟~{/.ooib=fq )F;| GQV@#thNL8s”) | em|&v[]Pj1}(xcP%0 s;X[h_޴UUju7*״L!:hk#Si}(=)ߪ=~<+0q{^\1"P..j%=70r`*I덤e9 fYFYf ;RίU@rhVn6^4teIꛉF$CQΔ7+ s[}(֛n;Wտ'gOw `ߧTB r 椀/x W@Q9!"hFȧ ` 89"E C! AJQK F#灼!B&1_ArLM{h9 .C ڶO> >kzV;qqV@6M xKYgG{ FMqibHTFNAsCJP)[L]]hY 2"q  Yi  &!2g7 τԩPBhUo! }^4I1_VG.BDH((d8=v|nC+?n>m.Wِ3 >]u7(NfW{WYk?zIH.IN%Y{sq='^}Ԃ1%8n?ry~rf7 x( @, #s h tXƴS0 [oݍav Ԡ  S-z?FI/;nob(f( (hnKaZO 'bpVgpt|GP%ڶzUϟvW~P ^imlU ޣʄAӮ(S'pV&ɇb9Ug/?r|"޷Bٟ2J0!zLc"5yhL'M(&BǍW}4M )_,'76чʻE<Bc|QoVr:A]b.>7_JTܳgoP>8Nh 1Dw®ȄsHhzCS90 Img q} IQaNYo'.~ d((80$xQQ.BGjL4cMՅιC2l 1taNh:Au 77wnnnWmvvDŌƤH5a50LZ!$a5_MN 5^i K3*OlQP>p*Rw!OHd^;CBsTs'"=ofҜd!Md"\(#f:d .><|j`/v>v,fǡ.> [MlV\ޭVo|,/ h(U?[c !"%a֔19xB#e#t\MY3ʋz#?dGeAt8|LЏH3װ/jꦅ~4$0ȏn^hq%<>=|>8j%l7;h.//ϸ/7[\oqVj#dRzDv& l 82/5t[qZ>~|ggpU&|0Jc$aG܂[ڠE0@  I$ e?&%tP,(S C,2*,ڸ}%\Ȝ 9m 67JVDm оU?qyhفe*–u~dZɟ___^] ַF%t]}5u|oIrSxOQGMĞ'm0m\0mxp_(dNBPxNvRy!b*9(, f3G,\C( U]ϟ ^\]z}Su#aH'l[EGwɲB8̂ TQA/h5@ MÑqD%^;* A{ =q DY2"ԡF]P3z^GZ-y-TJĩ><]kbhL2AKGB/Qb & Ƈg?g*,S H87gО!{Z^_]_?={xz|")4a%73Rq=$F(AP}Xʏq@JR^iWnhd(տ7 cM,R>|>Б3y5=fܵܮfMm[ Z' L &8/4Ǐ:!VP=+Վ F>HPb,$MYب#u^>:&\3&$e>қ RmO~R{DM f7xoe.('zBv+U7ǟ)\e|XX/}~~׾G)|P%TU=u=VX Vﱰ!?x' ]{d[[KD1L9@oL1 3  3Ta#^v;a?v;<~Q=պC-8S#s"p@̩@ڷ)(Iʹ(gH#kCG2A%Fa =Tx9N9ŷ.޻5uu`?c; ?0,,Mfup1@XH;&9N5zSHoB\Ѝ!<85_^~TL!;\CJMr:_1^92|z5qAQ 97J^fwgTPQo A:U鞐T<YŮU\@4pjXl*mݴ\_CzTt7uA]Q vdy)v  <-#T4@fR &PWjp,jW-xdGp~ͯ}}󗟞=.wf,W]U١-ھD(5yx: *By!N GCb {6EP /L`TVnvj퀄fDǢcna=W^ v{|~qY]Vv_Um=!w0@c6]Xfj(d! CSRJ_v[4񂤽Ӕ᪲ "Q+`dɫ0ZlID(-2 4.cX!U/ێAcox<' uH3^8|)> / 1y$vJY5%'ヺmf0,6n[vvmk~rꚦ~@d(:4">l-N46$E`8@~c8(yO%#SX"IMnf777ps{ܳgsTuH-l="í|vPPt+*2D1ꯜ<~|Rok8::GX̡1,Ⱦ3\&-K!sBc{*!ۈX8!'7\7 v;./`^o \VljS7m[WUι \cEDvؔLsB{Uq2LaKÙ`*HɅ#A3,*YJ($N} aWA ,>B'69Ǣ9ߘ JsA"?J?B Ccҵ¨`ltB{2lz*.`C<FВ'M F"ڦp>:\uS7>m7779Dd@ GsU@@o!pܵSȱt4Z ȍc>O:7Ѐu]Cl^j뗸ZVn֫Ͷn #2n@B{B܀&&${$_24Ҩ`42͋6OU_p" Xo&Q`1>eH|2(8<)>^Gf˷r8'A[F^X,f((!2E*1ٔy>6m|$$̟jy<ѻ/2"=ƟpojίnVW}h߹_GOyxxprrr<#xh G0+K(g^W&## 1" 34ޓZkhV4M/_@4 wm4xw6mivmsm-9frΔW AjJ0&w2?B]J}qk %$HH-o(縂.wlBdDb;gM.1b=#Ȍ.FT3F^Rc .'.x(/>n"%͆˷KU@(鑎C{~rk -sr#X}/Ol69(̆y"֏9=F p0$va J҇ 8r02}U&h !nh?ݮO? W/]]׸Zmsuw=R`0&`l;B6Fō^N|3Lnlr.^T\T=Ó2HNۣ@O0H҂2MA8fxLERMb&HŜ&xaTC_Ve]ɼeq@  _#%M` g ݣs> x D\QtOlTCY2]P9=%|a솀w;bN&xv3]QIg4G@.ɣGo>yrvztr`\“'gpxxKJX.}vsUy4b" @Lveiw=l;hv 6l[=a]7nm۹zkw~U~ݻ훦r=jDtq.P=FGqv%Hh(w#X*Oi%ɺG;?gɓhyl{)>yrggڿ=;k`XءX̭C)ͫ،E)RrSpnzkۻuiݭ`˗7jUmW;m\CD- TAӲDg7Wϝ/:V/IHߕzuALQ'U{(bjK_z?H *lj@2ykc%*^XCX!]m$Sl] :֯ #(@I I:@ZسEHtIyP}nyaj6T($? 6 ?^c!Ŋ4[o}`ΊtoNo/NbY(rX̭5`4U> ;0 kD1ЏB@GM@#m]}u];Ukڦm۶MmZ\[UuuHH/0eh5^mYTZ@>-&c wWS ܘpb3=A90C`,F%"VUzS}u];׷վ^ a}m` 5]@`lDž*8lӫ0/k<יQ~v0=w7m *k}>GjO}'OڅAx&c)-3S2 ,,C)M!efJ?:^3V pHK0a3#͇U\0`i+gP=>79J9*;&eƀr#bq,r@_0"EŅ~bG U6v((B35>Q`dYPƾI8d2ӃmeMFsKr~024X%6/wѩ<6WN띐"C-ܖ0h*WLlD2O?d szc |Au5-**'eP$-[&-G0R|QfB eEVd-ٟIQ۸Nb^ vo+ Dzȹl1(gRr#Z^W%v-@ C @6҉'dP@RleӔ{V*ѳP|FQM&O q`$! 9o;͘ 113@!+`jS\"؄;g"VGb߹[a(IMz&XS8nf:BΦd&zBH^7)ىzn 8mYd@+9 .|DI4&sSo(PFx1qo e;D g`DH9R2[x@H_b_J.Ye8GmOA(.^[DHQ?ߍD]FfYqS=94ڢ: x+JTn yfwfD#͔QqV <̏[@6'? Ҵo^0>0#ab89D)dJV-30+]Cu|("7TPq ~vjяԋ _D|SvyX"J'"l-eȽެp `Zg:޼B)wģsmByS/ (B .Z 1&&S |`?LycKR MW'WQJÐ3| XT$IDqav>b&DqKh)?-VBՖ]dA}2Aza[=&pZmxtAhJؼYNNy]ZZ:L  `"#i"&Q (rY\e|4dRl=~|s_WbtD45%Bᧄ:F,2aq*wpFOJ, OEf;t;]I4 y@BG!iJQDh1Am SJLԮjqPq/<ϯg>#ok(ajr&K a0=Д9ԚϝSzN\V׻BzSn''CesS#S])HrY.fyAHP)!"ʾRx-a#SMN}z7R;xH8@@(8'իnЛR?f>llIENDB`guard-2.18.1/images/pending.png000077500000000000000000000057161450001123100163030ustar00rootroot00000000000000PNG  IHDR00  pHYs  gAMAo3 cHRMlronA1tl- DIDATxl (X@a e f0(Nc{c"H})r޵ByՀpK b$Uv킸UN>3;!!r--- t 0RP!!d6{*`g:ҐZ;"']u}T9g !4 )%Uamf%}G$iY*a,q,&u9hۖu]@'x=jP1>8RƬ CA^x1$Vn"K :!~8Snp * fD)5^:In;ww^ hD03̋aF!HnѲ,=i=r~1.KDDlZoMbT 7 vl6ABϠVzb8B׃v^t:fo%IB4F>P|Jt:jJ}V\.itPӴ nsJ8R0 ND/$2'Iyc@۶GͯA(HR <σ  "1LAeB11wX8HEQdYx<cY8{'"(jZڈd&3cQHKAG! 6BF )h@+W.l\i-. -T,)VJ[ Mئ%h}{9޹Ȁ{^.y< G]]vwwiP(*|u`uuZ l6#{l=0>>N(P.ewߘI[[[ݣIlNNNIK]|>OQX[ayE"4xtww|}t:vvv*~?(]$DD5zzzLNN.{{{H$eٍb+* qߧ)b[\\$Y rSSӇyf Nhee6tUxNt:iffbSmTVѥ w.%q`Y J=ՃK466~xҾH#6%B,& aQr8;>J383|wfyіТU*h'Nd=JiHCNSplC];x`=zTd2>zv?8. . $v4Mܽ{SSStCC/%1 kI>={<($ $AeرX mmm^ӗ.]Jo;奴aW (e(BEn,,, c|| `-ȗwg]]5::jMLLXSSSփOZ[eYtںxUSSӻ璭-$- p8p: ,ӉǕ`mWvVQ׳*"qYB@ASSߏ j0|k[#GkeeL&ŐH$077T*L&y(**;Ά(++˗H$Յh4j0 aww DN:(ţGс[a|~ C<ϗXW\aTUq333**P(PPP3g***`&2 pƍ͛7?Y-'TꏪRtvvc޽FTx Ϸ 3X ܹszhoo@ cϔ* J)FFFz3v^^˲_|>tϞ=,`spp0oslQ[_"V%Ji},R8˲`b1$H丵lqM<ϫeee|>h4gYA4qtll웭؞t(幹4 R6'O@Ӵt]?tΝ٭RkǕn؂90 C~$zN)gYv)< W'Ԏr;v%a`[ ,'00 t] !LNN6n`pEմd_1 s޽{MIeBH# `d,qwMM]V @wsrr<(JFQchbwb_̗後OR~WrB *IENDB`guard-2.18.1/images/success.png000066400000000000000000000061121450001123100163130ustar00rootroot00000000000000PNG  IHDR00  pHYs  gAMAo3 cHRMlronA1tl- IDATxb?6.б6⿪`\B| ^^e`?CʠO?de;C[.Fۏ/10000 3?QC40223000001001000200>V ڍDU{?u>IV5ee(hpSs.c^S"Vf6{3<}J?Oue=?`u_n]uvy-TЫg_o)ߟ8ߟ ܻ!.5, ?}>?VV'\6F—LH1 b^.ۧٻ*R,(?v o~b````c|C˿b@,Тԏ^{AP}@MZC 9gr:A! E#uii ĕi#G+# S? 4А$/e *e]pM]<ێMܬ|?9<}|Xv?648ߝ㯨i޾GFnnHRvTn$H#cw9Փ>SMG 3I /LpfFG,gڤqG8čC9ic.k[۳hg#!.WǮ$)1op[g{ ĩGc@T]̳CRҺ֌m{:02>z]*.%)IAxӪyP7"+{h1DUfRP ,hiBB56Mvs\vM&i}7o^I&; ۙӫKm*-z./%(,ǟwh4ih s)v8|s )9UP(? CC.5=VZUܤu΢Õc8% KP(l?z~ pM?X!R*:UD9#2:ӏP5#pߛ?Xőݞ. If, i-,u0|AR1_3 az$1@C]!s@l ZXdH )[a[1>4PlߕfWw h-3E;%ctp SCMk:x; ?x]<"u}30{2k66}TguQýb|-mjڔ6? kn;xUEIIENDB`guard-2.18.1/lib/000077500000000000000000000000001450001123100134365ustar00rootroot00000000000000guard-2.18.1/lib/guard.rb000066400000000000000000000121261450001123100150670ustar00rootroot00000000000000require "thread" require "listen" require "guard/config" require "guard/deprecated/guard" unless Guard::Config.new.strict? require "guard/internals/helpers" require "guard/internals/debugging" require "guard/internals/traps" require "guard/internals/queue" # TODO: remove this class altogether require "guard/interactor" # Guard is the main module for all Guard related modules and classes. # Also Guard plugins should use this namespace. module Guard Deprecated::Guard.add_deprecated(self) unless Config.new.strict? class << self attr_reader :state attr_reader :queue attr_reader :listener attr_reader :interactor # @private api include Internals::Helpers # Initializes the Guard singleton: # # * Initialize the internal Guard state; # * Create the interactor # * Select and initialize the file change listener. # # @option options [Boolean] clear if auto clear the UI should be done # @option options [Boolean] notify if system notifications should be shown # @option options [Boolean] debug if debug output should be shown # @option options [Array] group the list of groups to start # @option options [Array] watchdir the directories to watch # @option options [String] guardfile the path to the Guardfile # # @return [Guard] the Guard singleton def setup(cmdline_options = {}) init(cmdline_options) @queue = Internals::Queue.new(Guard) _evaluate(state.session.evaluator_options) # NOTE: this should be *after* evaluate so :directories can work # TODO: move listener setup to session? @listener = Listen.send(*state.session.listener_args, &_listener_callback) ignores = state.session.guardfile_ignore @listener.ignore(ignores) unless ignores.empty? ignores = state.session.guardfile_ignore_bang @listener.ignore!(ignores) unless ignores.empty? Notifier.connect(state.session.notify_options) traps = Internals::Traps traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) } traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) } @interactor = Interactor.new(state.session.interactor_name == :sleep) traps.handle("INT") { @interactor.handle_interrupt } self end def init(cmdline_options) @state = Internals::State.new(cmdline_options) end # Asynchronously trigger changes # # Currently supported args: # # @example Old style hash: # async_queue_add(modified: ['foo'], added: ['bar'], removed: []) # # @example New style signals with args: # async_queue_add([:guard_pause, :unpaused ]) # def async_queue_add(changes) @queue << changes # Putting interactor in background puts guard into foreground # so it can handle change notifications Thread.new { interactor.background } end private # Check if any of the changes are actually watched for # TODO: why iterate twice? reuse this info when running tasks def _relevant_changes?(changes) # TODO: no coverage! files = changes.values.flatten(1) scope = Guard.state.scope watchers = scope.grouped_plugins.map do |_group, plugins| plugins.map(&:watchers).flatten end.flatten watchers.any? { |watcher| files.any? { |file| watcher.match(file) } } end def _relative_pathnames(paths) paths.map { |path| _relative_pathname(path) } end def _listener_callback lambda do |modified, added, removed| relative_paths = { modified: _relative_pathnames(modified), added: _relative_pathnames(added), removed: _relative_pathnames(removed) } _guardfile_deprecated_check(relative_paths[:modified]) async_queue_add(relative_paths) if _relevant_changes?(relative_paths) end end # TODO: obsoleted? (move to Dsl?) def _pluginless_guardfile? state.session.plugins.all.empty? end def _evaluate(options) evaluator = Guardfile::Evaluator.new(options) evaluator.evaluate UI.reset_and_clear msg = "No plugins found in Guardfile, please add at least one." UI.error msg if _pluginless_guardfile? if evaluator.inline? UI.info("Using inline Guardfile.") elsif evaluator.custom? UI.info("Using Guardfile at #{ evaluator.path }.") end rescue Guardfile::Evaluator::NoPluginsError => e UI.error(e.message) end # TODO: remove at some point # TODO: not tested because collides with ongoing refactoring def _guardfile_deprecated_check(modified) modified.map!(&:to_s) regexp = %r{^(?:.+/)?Guardfile$} guardfiles = modified.select { |path| regexp.match(path) } return if guardfiles.empty? guardfile = Pathname("Guardfile").realpath real_guardfiles = guardfiles.detect do |path| /^Guardfile$/.match(path) || Pathname(path).expand_path == guardfile end return unless real_guardfiles UI.warning "Guardfile changed -- _guard-core will exit.\n" exit 2 # nonzero to break any while loop end end end guard-2.18.1/lib/guard/000077500000000000000000000000001450001123100145405ustar00rootroot00000000000000guard-2.18.1/lib/guard/aruba_adapter.rb000066400000000000000000000030461450001123100176620ustar00rootroot00000000000000require "guard/cli" module Guard class ArubaAdapter def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel) @argv = argv @stdin = stdin @stdout = stdout @stderr = stderr @kernel = kernel if ENV["INSIDE_ARUBA_TEST"] == "1" UI.options = UI.options.merge(flush_seconds: 0) end end def execute! exit_code = execute # Proxy our exit code back to the injected kernel. @kernel.exit(exit_code) end def execute # Thor accesses these streams directly rather than letting # them be injected, so we replace them... $stderr = @stderr $stdin = @stdin $stdout = @stdout # Run our normal Thor app the way we know and love. CLI.start(@argv) # Thor::Base#start does not have a return value, assume # success if no exception is raised. 0 rescue StandardError => e # The ruby interpreter would pipe this to STDERR and exit 1 in the case # of an unhandled exception b = e.backtrace @stderr.puts "#{b.shift}: #{e.message} (#{e.class})" @stderr.puts b.map { |s| "\tfrom #{s}" }.join("\n") 1 rescue SystemExit => e e.status ensure # flush the logger so the output doesn't appear in next CLI invocation Guard.listener.stop if Guard.listener UI.logger.flush UI.logger.close UI.reset_logger # ...then we put them back. $stderr = STDERR $stdin = STDIN $stdout = STDOUT end end end guard-2.18.1/lib/guard/cli.rb000066400000000000000000000137761450001123100156520ustar00rootroot00000000000000require "thor" require "guard/version" require "guard/dsl_describer" require "guard/cli/environments/valid" require "guard/cli/environments/evaluate_only" module Guard # Facade for the Guard command line interface managed by # [Thor](https://github.com/wycats/thor). # # This is the main interface to Guard that is called by the Guard binary # `bin/guard`. Do not put any logic in here, create a class and delegate # instead. # class CLI < Thor default_task :start desc "start", "Starts Guard" method_option :clear, type: :boolean, default: false, aliases: "-c", banner: "Auto clear shell before each action" method_option :notify, type: :boolean, default: true, aliases: "-n", banner: "Notifications feature" method_option :debug, type: :boolean, default: false, aliases: "-d", banner: "Show debug information" method_option :group, type: :array, default: [], aliases: "-g", banner: "Run only the passed groups" method_option :plugin, type: :array, default: [], aliases: "-P", banner: "Run only the passed plugins" # TODO: make it plural method_option :watchdir, type: :array, aliases: "-w", banner: "Specify the directories to watch" method_option :guardfile, type: :string, aliases: "-G", banner: "Specify a Guardfile" method_option :no_interactions, type: :boolean, default: false, aliases: "-i", banner: "Turn off completely any Guard terminal interactions" method_option :no_bundler_warning, type: :boolean, default: false, aliases: "-B", banner: "Turn off warning when Bundler is not present" # Listen options method_option :latency, type: :numeric, aliases: "-l", banner: 'Overwrite Listen\'s default latency' method_option :force_polling, type: :boolean, default: false, aliases: "-p", banner: "Force usage of the Listen polling listener" method_option :wait_for_delay, type: :numeric, aliases: "-y", banner: 'Overwrite Listen\'s default wait_for_delay' method_option :listen_on, type: :string, aliases: "-o", default: nil, banner: "Specify a network address to Listen on for "\ "file change events (e.g. for use in VMs)" def self.help(shell, subcommand = false) super command_help(shell, default_task) end # Start Guard by initializing the defined Guard plugins and watch the file # system. # # This is the default task, so calling `guard` is the same as calling # `guard start`. # # @see Guard.start # def start if defined?(JRUBY_VERSION) unless options[:no_interactions] abort "\nSorry, JRuby and interactive mode are incompatible.\n"\ "As a workaround, use the '-i' option instead.\n\n"\ "More info: \n"\ " * https://github.com/guard/guard/issues/754\n"\ " * https://github.com/jruby/jruby/issues/2383\n\n" end end exit(Cli::Environments::Valid.new(options).start_guard) end desc "list", "Lists Guard plugins that can be used with init" # List the Guard plugins that are available for use in your system and # marks those that are currently used in your `Guardfile`. # # @see Guard::DslDescriber.list # def list Cli::Environments::EvaluateOnly.new(options).evaluate DslDescriber.new.list end desc "notifiers", "Lists notifiers and its options" # List the Notifiers for use in your system. # # @see Guard::DslDescriber.notifiers # def notifiers Cli::Environments::EvaluateOnly.new(options).evaluate # TODO: pass the data directly to the notifiers? DslDescriber.new.notifiers end desc "version", "Show the Guard version" map %w(-v --version) => :version # Shows the current version of Guard. # # @see Guard::VERSION # def version $stdout.puts "Guard version #{ VERSION }" end desc "init [GUARDS]", "Generates a Guardfile at the current directory"\ " (if it is not already there) and adds all installed Guard plugins"\ " or the given GUARDS into it" method_option :bare, type: :boolean, default: false, aliases: "-b", banner: "Generate a bare Guardfile without adding any"\ " installed plugin into it" # Initializes the templates of all installed Guard plugins and adds them # to the `Guardfile` when no Guard name is passed. When passing # Guard plugin names it does the same but only for those Guard plugins. # # @see Guard::Guardfile.initialize_template # @see Guard::Guardfile.initialize_all_templates # # @param [Array] plugin_names the name of the Guard plugins to # initialize # def init(*plugin_names) env = Cli::Environments::Valid.new(options) exitcode = env.initialize_guardfile(plugin_names) exit(exitcode) end desc "show", "Show all defined Guard plugins and their options" map %w(-T) => :show # Shows all Guard plugins and their options that are defined in # the `Guardfile` # # @see Guard::DslDescriber.show # def show Cli::Environments::EvaluateOnly.new(options).evaluate DslDescriber.new.show end end end guard-2.18.1/lib/guard/cli/000077500000000000000000000000001450001123100153075ustar00rootroot00000000000000guard-2.18.1/lib/guard/cli/environments/000077500000000000000000000000001450001123100200365ustar00rootroot00000000000000guard-2.18.1/lib/guard/cli/environments/bundler.rb000066400000000000000000000011621450001123100220160ustar00rootroot00000000000000require "guard/ui" module Guard module Cli module Environments class Bundler def verify return unless File.exist?("Gemfile") return if ENV["BUNDLE_GEMFILE"] || ENV["RUBYGEMS_GEMDEPS"] UI.info < e UI.error(e.message) abort end end end end end guard-2.18.1/lib/guard/cli/environments/valid.rb000066400000000000000000000040071450001123100214630ustar00rootroot00000000000000require "guard/cli/environments/bundler" require "guard/commander" require "guard/guardfile/generator" module Guard module Cli module Environments class Valid def initialize(options) @options = options end def start_guard # TODO: just to make sure tests are ok Bundler.new.verify unless @options[:no_bundler_warning] Guard.start(@options) rescue Dsl::Error, Guardfile::Evaluator::NoPluginsError, Guardfile::Evaluator::NoGuardfileError, Guardfile::Evaluator::NoCustomGuardfile => e # catch to throw message instead of call stack UI.error(e.message) abort end def initialize_guardfile(plugin_names = []) bare = @options[:bare] Guard.init(@options) session = Guard.state.session generator = Guardfile::Generator.new begin Guardfile::Evaluator.new(session.evaluator_options).evaluate rescue Guardfile::Evaluator::NoGuardfileError generator.create_guardfile rescue Guard::Guardfile::Evaluator::NoPluginsError # Do nothing - just the error end return 0 if bare # 0 - exit code # Evaluate because it might have existed and creating was skipped begin Guardfile::Evaluator.new(session.evaluator_options).evaluate rescue Guard::Guardfile::Evaluator::NoPluginsError end begin if plugin_names.empty? generator.initialize_all_templates else plugin_names.each do |plugin_name| generator.initialize_template(plugin_name) end end rescue Guardfile::Generator::Error => e UI.error(e.message) return 1 end # TODO: capture exceptions to show msg and return exit code on # failures 0 # exit code end end end end end guard-2.18.1/lib/guard/commander.rb000066400000000000000000000060401450001123100170320ustar00rootroot00000000000000require "listen" require "guard/notifier" require "guard/interactor" require "guard/runner" require "guard/dsl_describer" require "guard/internals/state" module Guard # Commands supported by guard module Commander # Start Guard by evaluating the `Guardfile`, initializing declared Guard # plugins and starting the available file change listener. # Main method for Guard that is called from the CLI when Guard starts. # # - Setup Guard internals # - Evaluate the `Guardfile` # - Configure Notifiers # - Initialize the declared Guard plugins # - Start the available file change listener # # @option options [Boolean] clear if auto clear the UI should be done # @option options [Boolean] notify if system notifications should be shown # @option options [Boolean] debug if debug output should be shown # @option options [Array] group the list of groups to start # @option options [String] watchdir the director to watch # @option options [String] guardfile the path to the Guardfile # @see CLI#start # def start(options = {}) setup(options) UI.debug "Guard starts all plugins" Runner.new.run(:start) listener.start watched = Guard.state.session.watchdirs.join("', '") UI.info "Guard is now watching at '#{ watched }'" exitcode = 0 begin while interactor.foreground != :exit Guard.queue.process while Guard.queue.pending? end rescue Interrupt rescue SystemExit => e exitcode = e.status end exitcode ensure stop end def stop listener&.stop interactor&.background UI.debug "Guard stops all plugins" Runner.new.run(:stop) Notifier.disconnect UI.info "Bye bye...", reset: true end # Reload Guardfile and all Guard plugins currently enabled. # If no scope is given, then the Guardfile will be re-evaluated, # which results in a stop/start, which makes the reload obsolete. # # @param [Hash] scopes hash with a Guard plugin or a group scope # def reload(scopes = {}) UI.clear(force: true) UI.action_with_scopes("Reload", scopes) Runner.new.run(:reload, scopes) end # Trigger `run_all` on all Guard plugins currently enabled. # # @param [Hash] scopes hash with a Guard plugin or a group scope # def run_all(scopes = {}) UI.clear(force: true) UI.action_with_scopes("Run", scopes) Runner.new.run(:run_all, scopes) end # Pause Guard listening to file changes. # def pause(expected = nil) paused = listener.paused? states = { paused: true, unpaused: false, toggle: !paused } pause = states[expected || :toggle] fail ArgumentError, "invalid mode: #{expected.inspect}" if pause.nil? return if pause == paused listener.public_send(pause ? :pause : :start) UI.info "File event handling has been #{pause ? 'paused' : 'resumed'}" end def show DslDescriber.new.show end end extend Commander end guard-2.18.1/lib/guard/commands/000077500000000000000000000000001450001123100163415ustar00rootroot00000000000000guard-2.18.1/lib/guard/commands/all.rb000066400000000000000000000015311450001123100174360ustar00rootroot00000000000000# required for async_queue_add require "pry" require "guard" module Guard module Commands class All def self.import Pry::Commands.create_command "all" do group "Guard" description "Run all plugins." banner <<-BANNER Usage: all Run the Guard plugin `run_all` action. You may want to specify an optional scope to the action, either the name of a Guard plugin or a plugin group. BANNER def process(*entries) scopes, unknown = Guard.state.session.convert_scope(entries) unless unknown.empty? output.puts "Unknown scopes: #{ unknown.join(', ') }" return end Guard.async_queue_add([:guard_run_all, scopes]) end end end end end end guard-2.18.1/lib/guard/commands/change.rb000066400000000000000000000012311450001123100201100ustar00rootroot00000000000000require "pry" require "guard" module Guard module Commands class Change def self.import Pry::Commands.create_command "change" do group "Guard" description "Trigger a file change." banner <<-BANNER Usage: change Pass the given files to the Guard plugin `run_on_changes` action. BANNER def process(*files) if files.empty? output.puts "Please specify a file." return end Guard.async_queue_add(modified: files, added: [], removed: []) end end end end end end guard-2.18.1/lib/guard/commands/notification.rb000066400000000000000000000007341450001123100213600ustar00rootroot00000000000000require "pry" require "guard/notifier" module Guard module Commands class Notification def self.import Pry::Commands.create_command "notification" do group "Guard" description "Toggles the notifications." banner <<-BANNER Usage: notification Toggles the notifications on and off. BANNER def process Notifier.toggle end end end end end end guard-2.18.1/lib/guard/commands/pause.rb000066400000000000000000000011121450001123100177760ustar00rootroot00000000000000require "pry" require "guard" module Guard module Commands class Pause def self.import Pry::Commands.create_command "pause" do group "Guard" description "Toggles the file listener." banner <<-BANNER Usage: pause Toggles the file listener on and off. When the file listener is paused, the default Guard Pry prompt will show the pause sign `[p]`. BANNER def process ::Guard.async_queue_add([:guard_pause]) end end end end end end guard-2.18.1/lib/guard/commands/reload.rb000066400000000000000000000015041450001123100201340ustar00rootroot00000000000000require "pry" require "guard" module Guard module Commands class Reload def self.import Pry::Commands.create_command "reload" do group "Guard" description "Reload all plugins." banner <<-BANNER Usage: reload Run the Guard plugin `reload` action. You may want to specify an optional scope to the action, either the name of a Guard plugin or a plugin group. BANNER def process(*entries) scopes, unknown = Guard.state.session.convert_scope(entries) unless unknown.empty? output.puts "Unknown scopes: #{ unknown.join(', ') }" return end Guard.async_queue_add([:guard_reload, scopes]) end end end end end end guard-2.18.1/lib/guard/commands/scope.rb000066400000000000000000000015621450001123100200030ustar00rootroot00000000000000require "pry" require "guard/interactor" require "guard" module Guard module Commands class Scope def self.import Pry::Commands.create_command "scope" do group "Guard" description "Scope Guard actions to groups and plugins." banner <<-BANNER Usage: scope Set the global Guard scope. BANNER def process(*entries) scope, unknown = Guard.state.session.convert_scope(entries) unless unknown.empty? output.puts "Unknown scopes: #{unknown.join(',') }" return end if scope[:plugins].empty? && scope[:groups].empty? output.puts "Usage: scope " return end Guard.state.scope.from_interactor(scope) end end end end end end guard-2.18.1/lib/guard/commands/show.rb000066400000000000000000000007101450001123100176440ustar00rootroot00000000000000require "pry" module Guard module Commands class Show def self.import Pry::Commands.create_command "show" do group "Guard" description "Show all Guard plugins." banner <<-BANNER Usage: show Show all defined Guard plugins and their options. BANNER def process Guard.async_queue_add([:guard_show]) end end end end end end guard-2.18.1/lib/guard/config.rb000066400000000000000000000004641450001123100163360ustar00rootroot00000000000000require "nenv" module Guard config_class = Nenv::Builder.build do create_method(:strict?) create_method(:gem_silence_deprecations?) end class Config < config_class def initialize super "guard" end def silence_deprecations? gem_silence_deprecations? end end end guard-2.18.1/lib/guard/deprecated/000077500000000000000000000000001450001123100166405ustar00rootroot00000000000000guard-2.18.1/lib/guard/deprecated/dsl.rb000066400000000000000000000025421450001123100177520ustar00rootroot00000000000000require "guard/config" fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict? module Guard module Deprecated module Dsl def self.add_deprecated(dsl_klass) dsl_klass.send(:extend, ClassMethods) end MORE_INFO_ON_UPGRADING_TO_GUARD_2 = <<-EOS.gsub(/^\s*/, "") For more information on how to upgrade for Guard 2.0, please head over to: https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0%s EOS module ClassMethods # @deprecated Use # `Guard::Guardfile::Evaluator.new(options).evaluate_guardfile` # instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How # to upgrade for Guard 2.0 # EVALUATE_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard::Dsl.evaluate_guardfile(options)' is deprecated. Please use 'Guard::Guardfile::Evaluator.new(options).evaluate_guardfile' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods-1'} EOS def evaluate_guardfile(options = {}) require "guard/guardfile/evaluator" require "guard/ui" UI.deprecation(EVALUATE_GUARDFILE) ::Guard::Guardfile::Evaluator.new(options).evaluate_guardfile end end end end end guard-2.18.1/lib/guard/deprecated/evaluator.rb000066400000000000000000000020241450001123100211650ustar00rootroot00000000000000require "guard/config" fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict? require "guard/ui" module Guard module Deprecated module Evaluator def self.add_deprecated(klass) klass.send(:include, self) end EVALUATE_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.3 'Guard::Evaluator#evaluate_guardfile' is deprecated in favor of '#evaluate'. EOS REEVALUATE_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.3 'Guard::Evaluator#reevaluate_guardfile' is deprecated in favor of '#reevaluate'. NOTE: this method no longer does anything since it could not be implemented reliably. EOS def evaluate_guardfile UI.deprecation(EVALUATE_GUARDFILE) evaluate end def reevaluate_guardfile # require guard only when needed, because # guard's deprecations require us require "guard" UI.deprecation(REEVALUATE_GUARDFILE) end end end end guard-2.18.1/lib/guard/deprecated/guard.rb000066400000000000000000000237411450001123100202760ustar00rootroot00000000000000require "guard/config" fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict? require "forwardable" require "guard/ui" require "guard/internals/session" require "guard/internals/state" require "guard/guardfile/evaluator" module Guard # @deprecated Every method in this module is deprecated module Deprecated module Guard def self.add_deprecated(klass) klass.send(:extend, ClassMethods) end module ClassMethods MORE_INFO_ON_UPGRADING_TO_GUARD_2 = <<-EOS.gsub(/^\s*/, "") For more information on how to upgrade for Guard 2.0, please head over to: https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0%s EOS # @deprecated Use `Guard.plugins(filter)` instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # GUARDS = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard.guards(filter)' is deprecated. Please use 'Guard.plugins(filter)' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods'} EOS def guards(filter = nil) ::Guard::UI.deprecation(GUARDS) ::Guard.state.session.plugins.all(filter) end # @deprecated Use `Guard.add_plugin(name, options = {})` instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # ADD_GUARD = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard.add_guard(name, options = {})' is deprecated. Please use 'Guard.add_plugin(name, options = {})' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods'} EOS def add_guard(*args) ::Guard::UI.deprecation(ADD_GUARD) add_plugin(*args) end # @deprecated Use # `Guard::PluginUtil.new(name).plugin_class(fail_gracefully: # fail_gracefully)` instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # GET_GUARD_CLASS = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard.get_guard_class(name, fail_gracefully = false)' is deprecated and is now always on. Please use 'Guard::PluginUtil.new(name).plugin_class(fail_gracefully: fail_gracefully)' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods'} EOS def get_guard_class(name, fail_gracefully = false) UI.deprecation(GET_GUARD_CLASS) PluginUtil.new(name).plugin_class(fail_gracefully: fail_gracefully) end # @deprecated Use `Guard::PluginUtil.new(name).plugin_location` instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # LOCATE_GUARD = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard.locate_guard(name)' is deprecated. Please use 'Guard::PluginUtil.new(name).plugin_location' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods'} EOS def locate_guard(name) UI.deprecation(LOCATE_GUARD) PluginUtil.new(name).plugin_location end # @deprecated Use `Guard::PluginUtil.plugin_names` instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # # Deprecator message for the `Guard.guard_gem_names` method GUARD_GEM_NAMES = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard.guard_gem_names' is deprecated. Please use 'Guard::PluginUtil.plugin_names' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods'} EOS def guard_gem_names UI.deprecation(GUARD_GEM_NAMES) PluginUtil.plugin_names end RUNNING = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.7.1 it was discovered that Guard.running was never initialized or used internally. EOS def running UI.deprecation(RUNNING) nil end LOCK = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.7.1 it was discovered that this accessor was never initialized or used internally. EOS def lock UI.deprecation(LOCK) end LISTENER_ASSIGN = <<-EOS.gsub(/^\s*/, "") listener= should not be used EOS def listener=(_) UI.deprecation(LISTENER_ASSIGN) ::Guard.listener end EVALUATOR = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.2 this method shouldn't be used EOS def evaluator UI.deprecation(EVALUATOR) options = ::Guard.state.session.evaluator_options ::Guard::Guardfile::Evaluator.new(options) end RESET_EVALUATOR = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.2 this method shouldn't be used EOS def reset_evaluator(_options) UI.deprecation(RESET_EVALUATOR) end RUNNER = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.2 this method shouldn't be used EOS def runner UI.deprecation(RUNNER) ::Guard::Runner.new end EVALUATE_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.2 this method shouldn't be used EOS def evaluate_guardfile UI.deprecation(EVALUATE_GUARDFILE) options = ::Guard.state.session.evaluator_options evaluator = ::Guard::Guardfile::Evaluator.new(options) evaluator.evaluate msg = "No plugins found in Guardfile, please add at least one." ::Guard::UI.error msg if _pluginless_guardfile? end OPTIONS = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.9.0 Guard.options is deprecated and ideally you should be able to set specific options through an API or a DSL method. Feel free to add feature requests if there's something missing. EOS def options UI.deprecation(OPTIONS) Class.new(Hash) do def initialize super(to_hash) end def to_hash session = ::Guard.state.session { clear: session.clearing?, debug: session.debug?, watchdir: Array(session.watchdirs).map(&:to_s), notify: session.notify_options[:notify], no_interactions: (session.interactor_name == :sleep) } end extend Forwardable delegate [:to_a, :keys] => :to_hash delegate [:include?] => :keys def fetch(key, *args) hash = to_hash verify_key!(hash, key) hash.fetch(key, *args) end def []=(key, value) case key when :clear ::Guard.state.session.clearing(value) else msg = "Oops! Guard.option[%s]= is unhandled or unsupported." \ "Please file an issue if you rely on this option working." fail NotImplementedError, format(msg, key) end end private def verify_key!(hash, key) return if hash.key?(key) msg = "Oops! Guard.option[%s] is unhandled or unsupported." \ "Please file an issue if you rely on this option working." fail NotImplementedError, format(msg, key) end end.new end ADD_GROUP = <<-EOS.gsub(/^\s*/, "") add_group is deprecated since 2.10.0 in favor of Guard.state.session.groups.add EOS def add_group(name, options = {}) UI.deprecation(ADD_GROUP) ::Guard.state.session.groups.add(name, options) end ADD_PLUGIN = <<-EOS.gsub(/^\s*/, "") add_plugin is deprecated since 2.10.0 in favor of Guard.state.session.plugins.add EOS def add_plugin(name, options = {}) UI.deprecation(ADD_PLUGIN) ::Guard.state.session.plugins.add(name, options) end GROUP = <<-EOS.gsub(/^\s*/, "") group is deprecated since 2.10.0 in favor of Guard.state.session.group.add(filter).first EOS def group(filter) UI.deprecation(GROUP) ::Guard.state.session.groups.all(filter).first end PLUGIN = <<-EOS.gsub(/^\s*/, "") plugin is deprecated since 2.10.0 in favor of Guard.state.session.group.add(filter).first EOS def plugin(filter) UI.deprecation(PLUGIN) ::Guard.state.session.plugins.all(filter).first end GROUPS = <<-EOS.gsub(/^\s*/, "") group is deprecated since 2.10.0 in favor of Guard.state.session.groups.all(filter) EOS def groups(filter) UI.deprecation(GROUPS) ::Guard.state.session.groups.all(filter) end PLUGINS = <<-EOS.gsub(/^\s*/, "") plugins is deprecated since 2.10.0 in favor of Guard.state.session.plugins.all(filter) EOS def plugins(filter) UI.deprecation(PLUGINS) ::Guard.state.session.plugins.all(filter) end SCOPE = <<-EOS.gsub(/^\s*/, "") scope is deprecated since 2.10.0 in favor of Guard.state.scope.to_hash EOS def scope UI.deprecation(SCOPE) ::Guard.state.scope.to_hash end SCOPE_ASSIGN = <<-EOS.gsub(/^\s*/, "") scope= is deprecated since 2.10.0 in favor of Guard.state.scope.to_hash EOS def scope=(scope) UI.deprecation(SCOPE_ASSIGN) ::Guard.state.scope.from_interactor(scope) end end end end end guard-2.18.1/lib/guard/deprecated/guardfile.rb000066400000000000000000000056161450001123100211370ustar00rootroot00000000000000require "guard/config" fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict? require "guard/guardfile/generator" module Guard module Deprecated module Guardfile def self.add_deprecated(dsl_klass) dsl_klass.send(:extend, ClassMethods) end module ClassMethods MORE_INFO_ON_UPGRADING_TO_GUARD_2 = <<-EOS.gsub(/^\s*/, "") For more information on how to upgrade for Guard 2.0, please head over to: https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0%s EOS # @deprecated Use {Guardfile::Generator#create_guardfile} instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # CREATE_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard::Guardfile.create_guardfile(options)' is deprecated. Please use 'Guard::Guardfile::Generator.new(options).create_guardfile' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods-2'} EOS def create_guardfile(options = {}) UI.deprecation(CREATE_GUARDFILE) ::Guard::Guardfile::Generator.new(options).create_guardfile end # @deprecated Use {Guardfile::Generator#initialize_template} instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # # Deprecator message for the `Guardfile.initialize_template` method INITIALIZE_TEMPLATE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard::Guardfile.initialize_template(plugin_name)' is deprecated. Please use 'Guard::Guardfile::Generator.new.initialize_template(plugin_name)' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods-2'} EOS def initialize_template(plugin_name) UI.deprecation(INITIALIZE_TEMPLATE) ::Guard::Guardfile::Generator.new.initialize_template(plugin_name) end # @deprecated Use {Guardfile::Generator#initialize_all_templates} # instead. # # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # # Deprecator message for the `Guardfile.initialize_all_templates` method INITIALIZE_ALL_TEMPLATES = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.0 'Guard::Guardfile.initialize_all_templates' is deprecated. Please use 'Guard::Guardfile::Generator.new.initialize_all_templates' instead. #{MORE_INFO_ON_UPGRADING_TO_GUARD_2 % '#deprecated-methods-2'} EOS def initialize_all_templates UI.deprecation(INITIALIZE_ALL_TEMPLATES) ::Guard::Guardfile::Generator.new.initialize_all_templates end end end end end guard-2.18.1/lib/guard/deprecated/watcher.rb000066400000000000000000000014201450001123100206170ustar00rootroot00000000000000require "guard/config" fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict? module Guard module Deprecated module Watcher def self.add_deprecated(klass) klass.send(:extend, ClassMethods) end module ClassMethods MATCH_GUARDFILE = <<-EOS.gsub(/^\s*/, "") Starting with Guard 2.8.3 this method is deprecated. EOS def match_guardfile?(files) require "guard/guardfile/evaluator" UI.deprecation(MATCH_GUARDFILE) options = ::Guard.state.session.evaluator_options evaluator = ::Guard::Guardfile::Evaluator.new(options) path = evaluator.guardfile_path files.any? { |file| File.expand_path(file) == path } end end end end end guard-2.18.1/lib/guard/dsl.rb000066400000000000000000000337551450001123100156640ustar00rootroot00000000000000require "guard/guardfile/evaluator" require "guard/interactor" require "guard/notifier" require "guard/ui" require "guard/watcher" require "guard/deprecated/dsl" unless Guard::Config.new.strict? require "guard" module Guard # The Dsl class provides the methods that are used in each `Guardfile` to # describe the behaviour of Guard. # # The main keywords of the DSL are {#guard} and {#watch}. These are necessary # to define the used Guard plugins and the file changes they are watching. # # You can optionally group the Guard plugins with the {#group} keyword and # ignore and filter certain paths with the {#ignore} and {#filter} keywords. # # You can set your preferred system notification library with {#notification} # and pass some optional configuration options for the library. If you don't # configure a library, Guard will automatically pick one with default options # (if you don't want notifications, specify `:off` as library). Please see # {Notifier} for more information about the supported libraries. # # A more advanced DSL use is the {#callback} keyword that allows you to # execute arbitrary code before or after any of the {Plugin#start}, # {Plugin#stop}, {Plugin#reload}, {Plugin#run_all}, # {Plugin#run_on_changes}, {Plugin#run_on_additions}, # {Plugin#run_on_modifications} and {Plugin#run_on_removals} # Guard plugins method. # You can even insert more hooks inside these methods. Please [checkout the # Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for # more details. # # The DSL will also evaluate normal Ruby code. # # There are two possible locations for the `Guardfile`: # # * The `Guardfile` or `guardfile.rb` in the current directory where Guard # has been started # * The `.Guardfile` in your home directory. # # In addition, if a user configuration `.guard.rb` in your home directory is # found, it will be appended to the current project `Guardfile`. # # @see https://github.com/guard/guard/wiki/Guardfile-examples # class Dsl Deprecated::Dsl.add_deprecated(self) unless Config.new.strict? # Wrap exceptions during parsing Guardfile class Error < RuntimeError end WARN_INVALID_LOG_LEVEL = "Invalid log level `%s` ignored. "\ "Please use either :debug, :info, :warn or :error." WARN_INVALID_LOG_OPTIONS = "You cannot specify the logger options"\ " :only and :except at the same time." # Set notification options for the system notifications. # You can set multiple notifications, which allows you to show local # system notifications and remote notifications with separate libraries. # You can also pass `:off` as library to turn off notifications. # # @example Define multiple notifications # notification :ruby_gntp # notification :ruby_gntp, host: '192.168.1.5' # # @param [Symbol, String] notifier the name of the notifier to use # @param [Hash] opts the notification library options # # @see Guard::Notifier for available notifier and its options. # def notification(notifier, opts = {}) Guard.state.session.guardfile_notification = { notifier.to_sym => opts } end # Sets the interactor options or disable the interactor. # # @example Pass options to the interactor # interactor option1: 'value1', option2: 'value2' # # @example Turn off interactions # interactor :off # # @param [Symbol, Hash] options either `:off` or a Hash with interactor # options # def interactor(options) # TODO: remove dependency on Interactor (let session handle this) case options when :off Interactor.enabled = false when Hash Interactor.options = options end end # Declares a group of Guard plugins to be run with `guard start --group # group_name`. # # @example Declare two groups of Guard plugins # group :backend do # guard :spork # guard :rspec # end # # group :frontend do # guard :passenger # guard :livereload # end # # @param [Symbol, String, Array] name the group name called # from the CLI # @param [Hash] options the options accepted by the group # @yield a block where you can declare several Guard plugins # # @see Group # @see Guard.add_group # @see #guard # def group(*args) options = args.last.is_a?(Hash) ? args.pop : {} groups = args groups.each do |group| next unless group.to_sym == :all fail ArgumentError, "'all' is not an allowed group name!" end if block_given? groups.each do |group| # TODO: let groups be added *after* evaluation Guard.state.session.groups.add(group, options) end @current_groups ||= [] @current_groups.push(groups) yield @current_groups.pop else UI.error \ "No Guard plugins found in the group '#{ groups.join(', ') }',"\ " please add at least one." end end # Declares a Guard plugin to be used when running `guard start`. # # The name parameter is usually the name of the gem without # the 'guard-' prefix. # # The available options are different for each Guard implementation. # # @example Declare a Guard without `watch` patterns # guard :rspec # # @example Declare a Guard with a `watch` pattern # guard :rspec do # watch %r{.*_spec.rb} # end # # @param [String] name the Guard plugin name # @param [Hash] options the options accepted by the Guard plugin # @yield a block where you can declare several watch patterns and actions # # @see Plugin # @see Guard.add_plugin # @see #watch # @see #group # def guard(name, options = {}) @plugin_options = options.merge(watchers: [], callbacks: []) yield if block_given? @current_groups ||= [] groups = @current_groups && @current_groups.last || [:default] groups.each do |group| opts = @plugin_options.merge(group: group) # TODO: let plugins be added *after* evaluation Guard.state.session.plugins.add(name, opts) end @plugin_options = nil end # Defines a pattern to be watched in order to run actions on file # modification. # # @example Declare watchers for a Guard # guard :rspec do # watch('spec/spec_helper.rb') # watch(%r{^.+_spec.rb}) # watch(%r{^app/controllers/(.+).rb}) do |m| # 'spec/acceptance/#{m[1]}s_spec.rb' # end # end # # @example Declare global watchers outside of a Guard # watch(%r{^(.+)$}) { |m| puts "#{m[1]} changed." } # # @param [String, Regexp] pattern the pattern that Guard must watch for # modification # # @yield a block to be run when the pattern is matched # @yieldparam [MatchData] m matches of the pattern # @yieldreturn a directory, a filename, an array of # directories / filenames, or nothing (can be an arbitrary command) # # @see Guard::Watcher # @see #guard # def watch(pattern, &action) # Allow watches in the global scope (to execute arbitrary commands) by # building a generic Guard::Plugin. @plugin_options ||= nil return guard(:plugin) { watch(pattern, &action) } unless @plugin_options @plugin_options[:watchers] << Watcher.new(pattern, action) end # Defines a callback to execute arbitrary code before or after any of # the `start`, `stop`, `reload`, `run_all`, `run_on_changes`, # `run_on_additions`, `run_on_modifications` and `run_on_removals` plugin # method. # # @example Add callback before the `reload` action. # callback(:reload_begin) { puts "Let's reload!" } # # @example Add callback before the `start` and `stop` actions. # # my_lambda = lambda do |plugin, event, *args| # puts "Let's #{event} #{plugin} with #{args}!" # end # # callback(my_lambda, [:start_begin, :start_end]) # # @param [Array] args the callback arguments # @yield a callback block # def callback(*args, &block) @plugin_options ||= nil fail "callback must be called within a guard block" unless @plugin_options block, events = if args.size > 1 # block must be the first argument in that case, the # yielded block is ignored args else [block, args[0]] end @plugin_options[:callbacks] << { events: events, listener: block } end # Ignores certain paths globally. # # @example Ignore some paths # ignore %r{^ignored/path/}, /man/ # # @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths # def ignore(*regexps) # TODO: use guardfile results class Guard.state.session.guardfile_ignore = regexps end # TODO: deprecate alias filter ignore # Replaces ignored paths globally # # @example Ignore only these paths # ignore! %r{^ignored/path/}, /man/ # # @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths # def ignore!(*regexps) @ignore_regexps ||= [] @ignore_regexps << regexps # TODO: use guardfile results class Guard.state.session.guardfile_ignore_bang = @ignore_regexps end # TODO: deprecate alias filter! ignore! # Configures the Guard logger. # # * Log level must be either `:debug`, `:info`, `:warn` or `:error`. # * Template supports the following placeholders: `:time`, `:severity`, # `:progname`, `:pid`, `:unit_of_work_id` and `:message`. # * Time format directives are the same as `Time#strftime` or # `:milliseconds`. # * The `:only` and `:except` options must be a `RegExp`. # # @example Set the log level # logger level: :warn # # @example Set a custom log template # logger template: '[Guard - :severity - :progname - :time] :message' # # @example Set a custom time format # logger time_format: '%h' # # @example Limit logging to a Guard plugin # logger only: :jasmine # # @example Log all but not the messages from a specific Guard plugin # logger except: :jasmine # # @param [Hash] options the log options # @option options [String, Symbol] level the log level # @option options [String] template the logger template # @option options [String, Symbol] time_format the time format # @option options [Regexp] only show only messages from the matching Guard # plugin # @option options [Regexp] except does not show messages from the matching # Guard plugin # def logger(options) if options[:level] options[:level] = options[:level].to_sym unless [:debug, :info, :warn, :error].include? options[:level] UI.warning(format(WARN_INVALID_LOG_LEVEL, options[:level])) options.delete :level end end if options[:only] && options[:except] UI.warning WARN_INVALID_LOG_OPTIONS options.delete :only options.delete :except end # Convert the :only and :except options to a regular expression [:only, :except].each do |name| next unless options[name] list = [].push(options[name]).flatten.map do |plugin| Regexp.escape(plugin.to_s) end options[name] = Regexp.new(list.join("|"), Regexp::IGNORECASE) end UI.options = UI.options.merge(options) end # Sets the default scope on startup # # @example Scope Guard to a single group # scope group: :frontend # # @example Scope Guard to multiple groups # scope groups: [:specs, :docs] # # @example Scope Guard to a single plugin # scope plugin: :test # # @example Scope Guard to multiple plugins # scope plugins: [:jasmine, :rspec] # # @param [Hash] scope the scope for the groups and plugins # def scope(scope = {}) # TODO: use a Guardfile::Results class Guard.state.session.guardfile_scope(scope) end def evaluate(contents, filename, lineno) # :nodoc instance_eval(contents, filename.to_s, lineno) rescue StandardError, ScriptError => e prefix = "\n\t(dsl)> " cleaned_backtrace = _cleanup_backtrace(e.backtrace) backtrace = "#{prefix}#{cleaned_backtrace.join(prefix)}" msg = "Invalid Guardfile, original error is: \n\n%s, \nbacktrace: %s" raise Error, format(msg, e, backtrace) end # Sets the directories to pass to Listen # # @example watch only given directories # directories %w(lib specs) # # @param [Array] directories directories for Listen to watch # def directories(directories) directories.each do |dir| fail "Directory #{dir.inspect} does not exist!" unless Dir.exist?(dir) end Guard.state.session.watchdirs = directories end # Sets Guard to clear the screen before every task is run # # @example switching clearing the screen on # clearing(:on) # # @param [Symbol] on ':on' to turn on, ':off' (default) to turn off # def clearing(on) Guard.state.session.clearing(on == :on) end private def _cleanup_backtrace(backtrace) dirs = { File.realpath(Dir.pwd) => ".", } gem_env = ENV["GEM_HOME"] || "" dirs[gem_env] = "$GEM_HOME" unless gem_env.empty? gem_paths = (ENV["GEM_PATH"] || "").split(File::PATH_SEPARATOR) gem_paths.each_with_index do |path, index| dirs[path] = "$GEM_PATH[#{index}]" end backtrace.dup.map do |raw_line| path = nil symlinked_path = raw_line.split(":").first begin path = raw_line.sub(symlinked_path, File.realpath(symlinked_path)) dirs.detect { |dir, name| path.sub!(File.realpath(dir), name) } path rescue Errno::ENOENT path || symlinked_path end end end end end guard-2.18.1/lib/guard/dsl_describer.rb000066400000000000000000000102431450001123100176710ustar00rootroot00000000000000# encoding: utf-8 require "formatador" require "guard/ui" require "guard/notifier" require "guard" require "set" require "ostruct" module Guard # The DslDescriber evaluates the Guardfile and creates an internal structure # of it that is used in some inspection utility methods like the CLI commands # `show` and `list`. # # @see Guard::Dsl # @see Guard::CLI # class DslDescriber def initialize(options = nil) fail "options passed to DslDescriber are ignored!" unless options.nil? end # List the Guard plugins that are available for use in your system and marks # those that are currently used in your `Guardfile`. # # @see CLI#list # def list # TODO: remove dependency on Guard in this whole file # collect metadata data = PluginUtil.plugin_names.sort.inject({}) do |hash, name| hash[name.capitalize] = Guard.state.session.plugins.all(name).any? hash end # presentation header = [:Plugin, :Guardfile] final_rows = [] data.each do |name, used| final_rows << { Plugin: name, Guardfile: used ? "✔" : "✘" } end # render Formatador.display_compact_table(final_rows, header) end # Shows all Guard plugins and their options that are defined in # the `Guardfile`. # # @see CLI#show # def show # collect metadata groups = Guard.state.session.groups.all objects = [] empty_plugin = OpenStruct.new empty_plugin.options = [["", nil]] groups.each do |group| plugins = Array(Guard.state.session.plugins.all(group: group.name)) plugins = [empty_plugin] if plugins.empty? plugins.each do |plugin| options = plugin.options options = [["", nil]] if options.empty? options.each do |option, raw_value| value = raw_value.nil? ? "" : raw_value.inspect objects << [group.title, plugin.title, option.to_s, value] end end end # presentation rows = [] prev_group = prev_plugin = prev_option = prev_value = nil objects.each do |group, plugin, option, value| group_changed = prev_group != group plugin_changed = (prev_plugin != plugin || group_changed) rows << :split if group_changed || plugin_changed rows << { Group: group_changed ? group : "", Plugin: plugin_changed ? plugin : "", Option: option, Value: value } prev_group = group prev_plugin = plugin prev_option = option prev_value = value end # render Formatador.display_compact_table( rows.drop(1), [:Group, :Plugin, :Option, :Value] ) end # Shows all notifiers and their options that are defined in # the `Guardfile`. # # @see CLI#show # def notifiers supported = Notifier.supported Notifier.connect(notify: true, silent: true) detected = Notifier.detected Notifier.disconnect detected_names = detected.map { |item| item[:name] } final_rows = supported.each_with_object([]) do |(name, _), rows| available = detected_names.include?(name) ? "✔" : "✘" notifier = detected.detect { |n| n[:name] == name } used = notifier ? "✔" : "✘" options = notifier ? notifier[:options] : {} if options.empty? rows << :split _add_row(rows, name, available, used, "", "") else options.each_with_index do |(option, value), index| if index == 0 rows << :split _add_row(rows, name, available, used, option.to_s, value.inspect) else _add_row(rows, "", "", "", option.to_s, value.inspect) end end end rows end Formatador.display_compact_table( final_rows.drop(1), [:Name, :Available, :Used, :Option, :Value] ) end private def _add_row(rows, name, available, used, option, value) rows << { Name: name, Available: available, Used: used, Option: option, Value: value } end end end guard-2.18.1/lib/guard/dsl_reader.rb000066400000000000000000000014001450001123100171640ustar00rootroot00000000000000require "guard/dsl" module Guard # TODO: this should probably be a base class for Dsl instead (in Guard 3.x) class DslReader < Dsl attr_reader :plugin_names def initialize super @plugin_names = [] end def guard(name, _options = {}) @plugin_names << name.to_s end # Stub everything else def notification(_notifier, _opts = {}) end def interactor(_options) end def group(*_args) end def watch(_pattern, &_action) end def callback(*_args, &_block) end def ignore(*_regexps) end def ignore!(*_regexps) end def logger(_options) end def scope(_scope = {}) end def directories(_directories) end def clearing(_on) end end end guard-2.18.1/lib/guard/group.rb000066400000000000000000000034201450001123100162200ustar00rootroot00000000000000module Guard # A group of Guard plugins. There are two reasons why you want to group your # Guard plugins: # # * You can start only certain groups from the command line by passing the # `--group` option to `guard start`. # * Abort task execution chain on failure within a group with the # `:halt_on_fail` option. # # @example Group that aborts on failure # # group :frontend, halt_on_fail: true do # guard 'coffeescript', input: 'spec/coffeescripts', # output: 'spec/javascripts' # guard 'jasmine-headless-webkit' do # watch(%r{^spec/javascripts/(.*)\..*}) do |m| # newest_js_file("spec/javascripts/#{m[1]}_spec") # end # end # end # # @see Guard::CLI # class Group attr_accessor :name, :options # Initializes a Group. # # @param [String] name the name of the group # @param [Hash] options the group options # @option options [Boolean] halt_on_fail if a task execution # should be halted for all Guard plugins in this group if a Guard plugin # throws `:task_has_failed` # def initialize(name, options = {}) @name = name.to_sym @options = options end # Returns the group title. # # @example Title for a group named 'backend' # > Guard::Group.new('backend').title # => "Backend" # # @return [String] # def title @title ||= name.to_s.capitalize end # String representation of the group. # # @example String representation of a group named 'backend' # > Guard::Group.new('backend').to_s # => "#" # # @return [String] the string representation # def to_s "#<#{self.class} @name=#{name} @options=#{options}>" end end end guard-2.18.1/lib/guard/guardfile.rb000066400000000000000000000015701450001123100170320ustar00rootroot00000000000000require "guard/config" if Guard::Config.new.strict? abort "Error: Deprecated file #{__FILE__} is being used" else require "guard/deprecated/guardfile" # TODO: remove this file in next major version module Guard unless Guard::Config.new.silence_deprecations? UPGRADE_WIKI_URL = "https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0" STDERR.puts <<-EOS (guard/guardfile.rb message) You are including "guard/guardfile.rb", which has been deprecated since 2013 ... and will be removed. Migration is easy, see: #{UPGRADE_WIKI_URL} This file was included from: #{caller[0..10] * "\n >"} Sorry for the inconvenience and have a nice day! (end of guard/guardfile.rb message) EOS end module Guardfile extend Deprecated::Guardfile::ClassMethods end end end guard-2.18.1/lib/guard/guardfile/000077500000000000000000000000001450001123100165025ustar00rootroot00000000000000guard-2.18.1/lib/guard/guardfile/evaluator.rb000066400000000000000000000144651450001123100210430ustar00rootroot00000000000000require "guard/config" require "guard/deprecated/evaluator" unless Guard::Config.new.strict? require "guard/options" require "guard/plugin" require "guard/dsl" require "guard/dsl_reader" module Guard module Guardfile # This class is responsible for evaluating the Guardfile. It delegates to # Guard::Dsl for the actual objects generation from the Guardfile content. # # @see Guard::Dsl # # TODO: rename this to a Locator or Loader or something class Evaluator Deprecated::Evaluator.add_deprecated(self) unless Config.new.strict? DEFAULT_GUARDFILES = %w( guardfile.rb Guardfile ~/.Guardfile ).freeze ERROR_NO_GUARDFILE = "No Guardfile found,"\ " please create one with `guard init`." attr_reader :options, :guardfile_path ERROR_NO_PLUGINS = "No Guard plugins found in Guardfile,"\ " please add at least one." class Error < RuntimeError end class NoGuardfileError < Error end class NoCustomGuardfile < Error end class NoPluginsError < Error end def guardfile_source @source end # Initializes a new Guard::Guardfile::Evaluator object. # # @option opts [String] guardfile the path to a valid Guardfile # @option opts [String] contents a string representing the # content of a valid Guardfile # def initialize(opts = {}) @type = nil @path = nil @user_config = nil opts = _from_deprecated(opts) if opts[:contents] @type = :inline @contents = opts[:contents] elsif opts[:guardfile] @type = :custom @path = Pathname.new(opts[:guardfile]) # may be updated by _read end end # Evaluates the DSL methods in the `Guardfile`. # # @example Programmatically evaluate a Guardfile # Guard::Guardfile::Evaluator.new.evaluate # # @example Programmatically evaluate a Guardfile with a custom Guardfile # path # # options = { guardfile: '/Users/guardfile/MyAwesomeGuardfile' } # Guard::Guardfile::Evaluator.new(options).evaluate # # @example Programmatically evaluate a Guardfile with an inline Guardfile # # options = { contents: 'guard :rspec' } # Guard::Guardfile::Evaluator.new(options).evaluate # def evaluate inline? || _use_provided || _use_default! contents = _guardfile_contents fail NoPluginsError, ERROR_NO_PLUGINS unless /guard/m =~ contents Dsl.new.evaluate(contents, @path || "", 1) end # Tests if the current `Guardfile` contains a specific Guard plugin. # # @example Programmatically test if a Guardfile contains a specific Guard # plugin # # File.read('Guardfile') # => "guard :rspec" # # Guard::Guardfile::Evaluator.new.guardfile_include?('rspec) # => true # # @param [String] plugin_name the name of the Guard # @return [Boolean] whether the Guard plugin has been declared # # TODO: rename this method to it matches RSpec examples better def guardfile_include?(plugin_name) reader = DslReader.new reader.evaluate(@contents, @path || "", 1) reader.plugin_names.include?(plugin_name) end attr_reader :path def custom? @type == :custom end # Gets the content of the `Guardfile` concatenated with the global # user configuration file. # # @example Programmatically get the content of the current Guardfile # Guard::Guardfile::Evaluator.new.guardfile_contents # => "guard :rspec" # # @return [String] the Guardfile content # def guardfile_contents config = File.read(_user_config_path) if File.exist?(_user_config_path) [_guardfile_contents_without_user_config, config].compact.join("\n") end def inline? @type == :inline end private def _guardfile_contents_without_user_config @guardfile_contents || "" end def _instance_eval_guardfile(contents) Dsl.new.evaluate(contents, @guardfile_path || "", 1) rescue => ex UI.error "Invalid Guardfile, original error is:\n#{ $! }" raise ex end def _fetch_guardfile_contents _use_inline || _use_provided || _use_default @evaluated = true return if _guardfile_contents_usable? UI.error "No Guard plugins found in Guardfile,"\ " please add at least one." end def _use_inline source_from_option = @source.nil? && options[:guardfile_contents] inline = @source == :inline return false unless source_from_option || inline @source = :inline @guardfile_contents = options[:guardfile_contents] UI.info "Using inline Guardfile." true end def _use_provided return unless custom? @path, @contents = _read(@path) true rescue Errno::ENOENT fail NoCustomGuardfile, "No Guardfile exists at #{ @path }." end def _use_default! DEFAULT_GUARDFILES.each do |guardfile| begin @path, @contents = _read(guardfile) @type = :default break rescue Errno::ENOENT if guardfile == DEFAULT_GUARDFILES.last fail NoGuardfileError, ERROR_NO_GUARDFILE end end end end def _read(path) full_path = Pathname.new(path.to_s).expand_path [full_path, full_path.read] rescue Errno::ENOENT fail rescue SystemCallError => e UI.error "Error reading file #{full_path}:" UI.error e.inspect UI.error e.backtrace abort end def _guardfile_contents @user_config ||= Pathname.new("~/.guard.rb").expand_path.read [@contents, @user_config].compact.join("\n") rescue Errno::ENOENT @contents || "" end def _guardfile_contents_usable? guardfile_contents && guardfile_contents =~ /guard/m end def _from_deprecated(opts) res = opts.dup if opts.key?(:guardfile_contents) res[:contents] = opts[:guardfile_contents] end res end end end end guard-2.18.1/lib/guard/guardfile/generator.rb000066400000000000000000000067521450001123100210270ustar00rootroot00000000000000require "guard/ui" require "guard/plugin_util" # Add Pathname#binwrite to 1.9.3 unless Pathname.instance_methods.include?(:binwrite) class Pathname def binwrite(*args) IO.binwrite(to_s, *args) end end end module Guard module Guardfile # This class is responsible for generating the Guardfile and adding Guard' # plugins' templates into it. # # @see Guard::CLI # class Generator require "guard" require "guard/ui" INFO_TEMPLATE_ADDED = "%s template added to Guardfile, feel free to edit it" # The Guardfile template for `guard init` GUARDFILE_TEMPLATE = File.expand_path( "../../../guard/templates/Guardfile", __FILE__ ) # The location of user defined templates begin HOME_TEMPLATES = Pathname.new("~/.guard/templates").expand_path rescue ArgumentError # home isn't defined. Set to the root of the drive. Trust that there # won't be user defined templates there HOME_TEMPLATES = Pathname.new("/").expand_path end class Error < RuntimeError end class NoSuchPlugin < Error attr_reader :plugin_name, :class_name def initialize(plugin_name) @plugin_name = plugin_name @class_name = plugin_name.delete("-").capitalize end def message "Could not load 'guard/#{plugin_name}'"\ " or '~/.guard/templates/#{plugin_name}'"\ " or find class Guard::#{class_name}\n" end end # Creates the initial Guardfile template when it does not # already exist. # # @see Guard::CLI#init # def create_guardfile path = Pathname.new("Guardfile").expand_path if path.exist? _ui(:error, "Guardfile already exists at #{path}") abort end _ui(:info, "Writing new Guardfile to #{path}") FileUtils.cp(GUARDFILE_TEMPLATE, path.to_s) end # Adds the Guardfile template of a Guard plugin to an existing Guardfile. # # @see Guard::CLI#init # # @param [String] plugin_name the name of the Guard plugin or template to # initialize # def initialize_template(plugin_name) guardfile = Pathname.new("Guardfile") plugin_util = PluginUtil.new(plugin_name) # TODO: change to "valid?" method plugin_class = plugin_util.plugin_class(fail_gracefully: true) if plugin_class begin plugin_util.add_to_guardfile rescue Errno::ENOENT => error # TODO: refactor template = plugin_class.template(plugin_util.plugin_location) _ui(:error, "Found class #{plugin_class} but loading it's template"\ "failed: '#{template}'") _ui(:error, "Error is: #{error}") return end return end template_code = (HOME_TEMPLATES + plugin_name).read guardfile.binwrite(format("\n%s\n", template_code), open_args: ["a"]) _ui(:info, format(INFO_TEMPLATE_ADDED, plugin_name)) rescue Errno::ENOENT fail NoSuchPlugin, plugin_name.downcase end # Adds the templates of all installed Guard implementations to an # existing Guardfile. # # @see Guard::CLI#init # def initialize_all_templates PluginUtil.plugin_names.each { |g| initialize_template(g) } end private def _ui(*args) UI.send(*args) end end end end guard-2.18.1/lib/guard/interactor.rb000066400000000000000000000024361450001123100172440ustar00rootroot00000000000000require "forwardable" module Guard class Interactor # Initializes the interactor. This configures # Pry and creates some custom commands and aliases # for Guard. # def initialize(no_interaction = false) @interactive = !no_interaction && self.class.enabled? # TODO: only require the one used require "guard/jobs/sleep" require "guard/jobs/pry_wrapper" job_klass = interactive? ? Jobs::PryWrapper : Jobs::Sleep @idle_job = job_klass.new(self.class.options) end def interactive? @interactive end extend Forwardable delegate [:foreground, :background, :handle_interrupt] => :idle_job # TODO: everything below is just so the DSL can set options # before setup() is called, which makes it useless for when # Guardfile is reevaluated class << self def options @options ||= {} end # Pass options to interactor's job when it's created attr_writer :options # TODO: allow custom user idle jobs, e.g. [:pry, :sleep, :exit, ...] def enabled? @enabled || @enabled.nil? end alias_method :enabled, :enabled? # TODO: handle switching interactors during runtime? attr_writer :enabled end private attr_reader :idle_job end end guard-2.18.1/lib/guard/internals/000077500000000000000000000000001450001123100165375ustar00rootroot00000000000000guard-2.18.1/lib/guard/internals/debugging.rb000066400000000000000000000027031450001123100210210ustar00rootroot00000000000000# Because it's used by Sheller require "open3" require "logger" require "guard/ui" require "guard/internals/tracing" module Guard # @private api module Internals class Debugging class << self TRACES = [ [Kernel, :system], [Kernel, :spawn], [Kernel, :`], [Open3, :popen3] ] # Sets up debugging: # # * aborts on thread exceptions # * Set the logging level to `:debug` # * traces execution of Kernel.system and backtick calls def start return if @started ||= false @started = true Thread.abort_on_exception = true UI.level = Logger::DEBUG TRACES.each { |mod, meth| _trace(mod, meth, &method(:_notify)) } @traced = true end def stop return unless @started ||= false UI.level = Logger::INFO _reset end private def _notify(*args) UI.debug "Command execution: #{args.join(' ')}" end # reset singleton - called by tests def _reset @started = false return unless @traced TRACES.each { |mod, meth| _untrace(mod, meth) } @traced = false end def _trace(mod, meth, &block) Tracing.trace(mod, meth, &block) end def _untrace(mod, meth) Tracing.untrace(mod, meth) end end end end end guard-2.18.1/lib/guard/internals/groups.rb000066400000000000000000000016301450001123100204030ustar00rootroot00000000000000require "guard/group" module Guard # @private api module Internals class Groups DEFAULT_GROUPS = [:common, :default] def initialize @groups = DEFAULT_GROUPS.map { |name| Group.new(name) } end def all(filter = nil) return @groups if filter.nil? matcher = matcher_for(filter) @groups.select { |group| matcher.call(group) } end def add(name, options = {}) all(name).first || Group.new(name, options).tap do |group| fail if name == :specs && options.empty? @groups << group end end private def matcher_for(filter) case filter when String, Symbol ->(group) { group.name == filter.to_sym } when Regexp ->(group) { group.name.to_s =~ filter } else fail "Invalid filter: #{filter.inspect}" end end end end end guard-2.18.1/lib/guard/internals/helpers.rb000066400000000000000000000004061450001123100205260ustar00rootroot00000000000000module Guard # @private api module Internals module Helpers def _relative_pathname(path) full_path = Pathname(path) full_path.relative_path_from(Pathname.pwd) rescue ArgumentError full_path end end end end guard-2.18.1/lib/guard/internals/plugins.rb000066400000000000000000000023541450001123100205510ustar00rootroot00000000000000require "guard/plugin_util" require "guard/group" require "guard/plugin" module Guard # @private api module Internals class Plugins def initialize @plugins = [] end def all(filter = nil) return @plugins if filter.nil? matcher = matcher_for(filter) @plugins.select { |plugin| matcher.call(plugin) } end def remove(plugin) @plugins.delete(plugin) end # TODO: should it allow duplicates? (probably yes because of different # configs or groups) def add(name, options) @plugins << PluginUtil.new(name).initialize_plugin(options) end private def matcher_for(filter) case filter when String, Symbol shortname = filter.to_s.downcase.delete("-") ->(plugin) { plugin.name == shortname } when Regexp ->(plugin) { plugin.name =~ filter } when Hash lambda do |plugin| filter.all? do |k, v| case k when :name plugin.name == v.to_s.downcase.delete("-") when :group plugin.group.name == v.to_sym end end end end end end end end guard-2.18.1/lib/guard/internals/queue.rb000066400000000000000000000022771450001123100202200ustar00rootroot00000000000000module Guard module Internals class Queue def initialize(commander) @commander = commander @queue = ::Queue.new end # Process the change queue, running tasks within the main Guard thread def process actions = [] changes = { modified: [], added: [], removed: [] } while pending? if (item = @queue.pop).first.is_a?(Symbol) actions << item else item.each { |key, value| changes[key] += value } end end _run_actions(actions) return if changes.values.all?(&:empty?) Runner.new.run_on_changes(*changes.values) end def pending? !@queue.empty? end def <<(changes) @queue << changes end private def _run_actions(actions) actions.each do |action_args| args = action_args.dup namespaced_action = args.shift action = namespaced_action.to_s.sub(/^guard_/, "") if @commander.respond_to?(action) @commander.send(action, *args) else fail "Unknown action: #{action.inspect}" end end end end end end guard-2.18.1/lib/guard/internals/scope.rb000066400000000000000000000070661450001123100202060ustar00rootroot00000000000000require "guard" module Guard # @private api module Internals class Scope def initialize @interactor_plugin_scope = [] @interactor_group_scope = [] end def to_hash { plugins: _hashify_scope(:plugin), groups: _hashify_scope(:group) }.dup.freeze end # TODO: refactor def grouped_plugins(scope = { plugins: [], groups: [] }) items = nil plugins = _find_non_empty_scope(:plugins, scope) if plugins items = Array(plugins).map { |plugin| _instantiate(:plugin, plugin) } end unless items # TODO: no coverage here!! found = _find_non_empty_scope(:groups, scope) found ||= Guard.state.session.groups.all groups = Array(found).map { |group| _instantiate(:group, group) } if groups.any? { |g| g.name == :common } items = groups else items = ([_instantiate(:group, :common)] + Array(found)).compact end end items.map do |plugin_or_group| group = nil plugins = [plugin_or_group] if plugin_or_group.is_a?(Group) # TODO: no coverage here! group = plugin_or_group plugins = Guard.state.session.plugins.all(group: group.name) end [group, plugins] end end def from_interactor(scope) @interactor_plugin_scope = Array(scope[:plugins]) @interactor_group_scope = Array(scope[:groups]) end def titles(scope = nil) hash = scope || to_hash plugins = hash[:plugins] groups = hash[:groups] return plugins.map(&:title) unless plugins.nil? || plugins.empty? return hash[:groups].map(&:title) unless groups.nil? || groups.empty? ["all"] end private # TODO: move to session def _scope_names(new_scope, name) items = Array(new_scope[:"#{name}s"] || new_scope[name]) if items.empty? # Convert objects to names items.map { |p| p.respond_to?(:name) ? p.name : p } end # TODO: let the Plugins and Groups classes handle this? # TODO: why even instantiate?? just to check if it exists? def _hashify_scope(type) # TODO: get cmdline passed to initialize above? cmdline = Array(Guard.state.session.send("cmdline_#{type}s")) guardfile = Guard.state.session.send(:"guardfile_#{type}_scope") interactor = instance_variable_get(:"@interactor_#{type}_scope") # TODO: session should decide whether to use cmdline or guardfile - # since it has access to both variables items = [interactor, cmdline, guardfile].detect do |source| !source.empty? end # TODO: not tested when groups/plugins given don't exist # TODO: should already be instantiated Array(items).map do |obj| if obj.respond_to?(:name) obj else name = obj (type == :group ? _groups : _plugins).all(name).first end end.compact end def _instantiate(meth, obj) # TODO: no coverage return obj unless obj.is_a?(Symbol) || obj.is_a?(String) Guard.state.session.send("#{meth}s".to_sym).all(obj).first end def _find_non_empty_scope(type, local_scope) [Array(local_scope[type]), to_hash[type]].map(&:compact).detect(&:any?) end def _groups Guard.state.session.groups end def _plugins Guard.state.session.plugins end end end end guard-2.18.1/lib/guard/internals/session.rb000066400000000000000000000107131450001123100205510ustar00rootroot00000000000000require "guard/internals/plugins" require "guard/internals/groups" require "guard/options" module Guard # @private api module Internals # TODO: split into a commandline class and session (plugins, groups) # TODO: swap session and metadata class Session attr_reader :plugins attr_reader :groups DEFAULT_OPTIONS = { clear: false, debug: false, no_bundler_warning: false, # User defined scopes group: [], plugin: [], # Notifier notify: true, # Interactor no_interactions: false, # Guardfile options: # guardfile_contents guardfile: nil, # Listener options # TODO: rename to watchdirs? watchdir: Dir.pwd, latency: nil, force_polling: false, wait_for_delay: nil, listen_on: nil } def cmdline_groups @cmdline_groups.dup.freeze end def cmdline_plugins @cmdline_plugins.dup.freeze end def initialize(new_options) @options = Options.new(new_options, DEFAULT_OPTIONS) @plugins = Plugins.new @groups = Groups.new @cmdline_groups = @options[:group] @cmdline_plugins = @options[:plugin] @clear = @options[:clear] @debug = @options[:debug] @watchdirs = Array(@options[:watchdir]) @notify = @options[:notify] @interactor_name = @options[:no_interactions] ? :sleep : :pry_wrapper @guardfile_plugin_scope = [] @guardfile_group_scope = [] @guardfile_ignore = [] @guardfile_ignore_bang = [] @guardfile_notifier_options = {} end def guardfile_scope(scope) opts = scope.dup groups = Array(opts.delete(:groups)) group = Array(opts.delete(:group)) @guardfile_group_scope = Array(groups) + Array(group) plugins = Array(opts.delete(:plugins)) plugin = Array(opts.delete(:plugin)) @guardfile_plugin_scope = Array(plugins) + Array(plugin) fail "Unknown options: #{opts.inspect}" unless opts.empty? end # TODO: create a EvaluatorResult class? attr_reader :guardfile_group_scope attr_reader :guardfile_plugin_scope attr_accessor :guardfile_ignore_bang attr_reader :guardfile_ignore def guardfile_ignore=(ignores) @guardfile_ignore += Array(ignores).flatten end def clearing(on) @clear = on end def clearing? @clear end alias :clear? :clearing? def debug? @debug end def watchdirs @watchdirs_from_guardfile ||= nil @watchdirs_from_guardfile || @watchdirs end # set by Dsl with :directories() command def watchdirs=(dirs) dirs = [Dir.pwd] if dirs.empty? @watchdirs_from_guardfile = dirs.map { |dir| File.expand_path dir } end def listener_args if @options[:listen_on] [:on, @options[:listen_on]] else listener_options = {} [:latency, :force_polling, :wait_for_delay].each do |option| listener_options[option] = @options[option] if @options[option] end expanded_watchdirs = watchdirs.map { |dir| File.expand_path dir } [:to, *expanded_watchdirs, listener_options] end end def evaluator_options opts = { guardfile: @options[:guardfile] } # TODO: deprecate :guardfile_contents if @options[:guardfile_contents] opts[:contents] = @options[:guardfile_contents] end opts end def notify_options names = @guardfile_notifier_options.keys return { notify: false } if names.include?(:off) { notify: @options[:notify], notifiers: @guardfile_notifier_options } end def guardfile_notification=(config) @guardfile_notifier_options.merge!(config) end attr_reader :interactor_name # TODO: call this from within action, not within interactor command def convert_scope(entries) scopes = { plugins: [], groups: [] } unknown = [] entries.each do |entry| if plugin = plugins.all(entry).first scopes[:plugins] << plugin elsif group = groups.all(entry).first scopes[:groups] << group else unknown << entry end end [scopes, unknown] end end end end guard-2.18.1/lib/guard/internals/state.rb000066400000000000000000000010631450001123100202040ustar00rootroot00000000000000require "guard/group" require "guard/plugin_util" require "guard/internals/session" require "guard/internals/scope" require "guard/runner" module Guard module Internals class State # Minimal setup for non-interactive commands (list, init, show, etc.) def initialize(cmdline_opts) @session = Session.new(cmdline_opts) @scope = Scope.new # NOTE: must be set before anything calls Guard::UI.debug Debugging.start if session.debug? end attr_reader :scope attr_reader :session end end end guard-2.18.1/lib/guard/internals/tracing.rb000066400000000000000000000016421450001123100205160ustar00rootroot00000000000000module Guard module Internals module Tracing def self.trace(mod, meth) meta = (class << mod; self; end) original_meth = "original_#{meth}".to_sym if mod.respond_to?(original_meth) fail "ALREADY TRACED: #{mod}.#{meth}" end meta.send(:alias_method, original_meth, meth) meta.send(:define_method, meth) do |*args, &block| yield(*args) if block_given? mod.send original_meth, *args, &block end end def self.untrace(mod, meth) meta = (class << mod; self; end) original_meth = "original_#{meth}".to_sym unless mod.respond_to?(original_meth) fail "NOT TRACED: #{mod}.#{meth} (no method: #{original_meth})" end meta.send(:remove_method, meth) meta.send(:alias_method, meth, original_meth) meta.send(:undef_method, original_meth) end end end end guard-2.18.1/lib/guard/internals/traps.rb000066400000000000000000000003061450001123100202140ustar00rootroot00000000000000module Guard module Internals module Traps def self.handle(signal, &block) return unless Signal.list.key?(signal) Signal.trap(signal, &block) end end end end guard-2.18.1/lib/guard/jobs/000077500000000000000000000000001450001123100154755ustar00rootroot00000000000000guard-2.18.1/lib/guard/jobs/base.rb000066400000000000000000000006451450001123100167410ustar00rootroot00000000000000module Guard module Jobs class Base def initialize(_options) end # @return [Symbol] :stopped once job is finished # @return [Symbol] :exit to tell Guard to terminate def foreground end def background end # Signal handler calls this, so avoid actually doing # anything other than signaling threads def handle_interrupt end end end end guard-2.18.1/lib/guard/jobs/pry_wrapper.rb000066400000000000000000000221371450001123100204010ustar00rootroot00000000000000require "guard/commands/all" require "guard/commands/change" require "guard/commands/notification" require "guard/commands/pause" require "guard/commands/reload" require "guard/commands/scope" require "guard/commands/show" require "shellany/sheller" require "guard/jobs/base" module Guard module Jobs class TerminalSettings def initialize @settings = nil @works = Shellany::Sheller.run("hash", "stty") || false end def restore return unless configurable? && @settings Shellany::Sheller.run("stty #{ @setting } 2>#{IO::NULL}") end def save return unless configurable? @settings = Shellany::Sheller.stdout("stty -g 2>#{IO::NULL}").chomp end def echo return unless configurable? Shellany::Sheller.run("stty echo 2>#{IO::NULL}") end def configurable? @works end end class PryWrapper < Base # The default Ruby script to configure Guard Pry if the option `:guard_rc` # is not defined. GUARD_RC = if ENV["XDG_CONFIG_HOME"] && File.exist?(ENV["XDG_CONFIG_HOME"] + "/guard/guardrc") ENV["XDG_CONFIG_HOME"] + "/guard/guardrc" else "~/.guardrc" end # The default Guard Pry history file if the option `:history_file` is not # defined. HISTORY_FILE = if ENV["XDG_DATA_HOME"] && File.exist?(ENV["XDG_DATA_HOME"] + "/guard/history") ENV["XDG_DATA_HOME"] + "/guard/history" else "~/.guard_history" end # List of shortcuts for each interactor command SHORTCUTS = { help: "h", all: "a", reload: "r", change: "c", show: "s", scope: "o", notification: "n", pause: "p", exit: "e", quit: "q" } def initialize(options) @mutex = Mutex.new @thread = nil @terminal_settings = TerminalSettings.new _setup(options) end def foreground UI.debug "Start interactor" @terminal_settings.save _switch_to_pry # TODO: rename :stopped to continue _killed? ? :stopped : :exit ensure UI.reset_line UI.debug "Interactor was stopped or killed" @terminal_settings.restore end def background _kill_pry end def handle_interrupt thread = @thread fail Interrupt unless thread thread.raise Interrupt end private attr_reader :thread def _pry_config Pry.config end def _pry_commands Pry.commands end def _switch_to_pry th = nil @mutex.synchronize do unless @thread @thread = Thread.new { Pry.start } @thread.join(0.5) # give pry a chance to start th = @thread end end # check for nill, because it might've been killed between the mutex and # now th.join unless th.nil? end def _killed? th = nil @mutex.synchronize { th = @thread } th.nil? end def _kill_pry @mutex.synchronize do unless @thread.nil? @thread.kill @thread = nil # set to nil so we know we were killed end end end def _setup(options) _pry_config.should_load_rc = false _pry_config.should_load_local_rc = false _configure_history_file(options[:history_file] || HISTORY_FILE) _add_hooks(options) Commands::All.import Commands::Change.import Commands::Notification.import Commands::Pause.import Commands::Reload.import Commands::Show.import Commands::Scope.import _setup_commands _configure_prompt end def _configure_history_file(history_file) history_file_path = File.expand_path(history_file) # Pry >= 0.13 if _pry_config.respond_to?(:history_file=) _pry_config.history_file = history_file_path else _pry_config.history.file = history_file_path end end # Add Pry hooks: # # * Load `~/.guardrc` within each new Pry session. # * Load project's `.guardrc` within each new Pry session. # * Restore prompt after each evaluation. # def _add_hooks(options) _add_load_guard_rc_hook(Pathname(options[:guard_rc] || GUARD_RC)) _add_load_project_guard_rc_hook(Pathname.pwd + ".guardrc") _add_restore_visibility_hook if @terminal_settings.configurable? end # Add a `when_started` hook that loads a global .guardrc if it exists. # def _add_load_guard_rc_hook(guard_rc) _pry_config.hooks.add_hook :when_started, :load_guard_rc do guard_rc.expand_path.tap { |p| load p if p.exist? } end end # Add a `when_started` hook that loads a project .guardrc if it exists. # def _add_load_project_guard_rc_hook(guard_rc) _pry_config.hooks.add_hook :when_started, :load_project_guard_rc do load guard_rc if guard_rc.exist? end end # Add a `after_eval` hook that restores visibility after a command is # eval. def _add_restore_visibility_hook _pry_config.hooks.add_hook :after_eval, :restore_visibility do @terminal_settings.echo end end def _setup_commands _replace_reset_command _create_run_all_command _create_command_aliases _create_guard_commands _create_group_commands end # Replaces reset defined inside of Pry with a reset that # instead restarts guard. # def _replace_reset_command _pry_commands.command "reset", "Reset the Guard to a clean state." do output.puts "Guard reset." exec "guard" end end # Creates a command that triggers the `:run_all` action # when the command is empty (just pressing enter on the # beginning of a line). # def _create_run_all_command _pry_commands.block_command(/^$/, "Hit enter to run all") do Pry.run_command "all" end end # Creates command aliases for the commands: `help`, `reload`, `change`, # `scope`, `notification`, `pause`, `exit` and `quit`, which will be the # first letter of the command. # def _create_command_aliases SHORTCUTS.each do |command, shortcut| _pry_commands.alias_command shortcut, command.to_s end end # Create a shorthand command to run the `:run_all` # action on a specific Guard plugin. For example, # when guard-rspec is available, then a command # `rspec` is created that runs `all rspec`. # def _create_guard_commands Guard.state.session.plugins.all.each do |guard_plugin| cmd = "Run all #{guard_plugin.title}" _pry_commands.create_command guard_plugin.name, cmd do group "Guard" def process Pry.run_command "all #{ match }" end end end end # Create a shorthand command to run the `:run_all` # action on a specific Guard group. For example, # when you have a group `frontend`, then a command # `frontend` is created that runs `all frontend`. # def _create_group_commands Guard.state.session.groups.all.each do |group| next if group.name == :default cmd = "Run all #{group.title}" _pry_commands.create_command group.name.to_s, cmd do group "Guard" def process Pry.run_command "all #{ match }" end end end end # Configures the pry prompt to see `guard` instead of # `pry`. # def _configure_prompt prompt_procs = [_prompt(">"), _prompt("*")] prompt = if Pry::Prompt.is_a?(Class) Pry::Prompt.new("Guard", "Guard Pry prompt", prompt_procs) else prompt_procs end _pry_config.prompt = prompt end # Returns the plugins scope, or the groups scope ready for display in the # prompt. # def _scope_for_prompt titles = Guard.state.scope.titles.join(",") titles == "all" ? "" : titles + " " end # Returns a proc that will return itself a string ending with the given # `ending_char` when called. # def _prompt(ending_char) proc do |target_self, nest_level, pry| process = Guard.listener.paused? ? "pause" : "guard" level = ":#{ nest_level }" unless nest_level.zero? "[#{ _history(pry) }] #{ _scope_for_prompt }#{ process }"\ "(#{ _clip_name(target_self) })#{ level }#{ ending_char } " end end def _clip_name(target) Pry.view_clip(target) end def _history(pry) if pry.respond_to?(:input_ring) pry.input_ring.size else pry.input_array.size end end end end end guard-2.18.1/lib/guard/jobs/sleep.rb000066400000000000000000000007511450001123100171350ustar00rootroot00000000000000require "guard/jobs/base" require "guard/ui" module Guard module Jobs class Sleep < Base def foreground UI.debug "Guards jobs done. Sleeping..." sleep UI.debug "Sleep interrupted by events." :stopped rescue Interrupt UI.debug "Sleep interrupted by user." :exit end def background Thread.main.wakeup end def handle_interrupt Thread.main.raise Interrupt end end end end guard-2.18.1/lib/guard/notifier.rb000066400000000000000000000033301450001123100167030ustar00rootroot00000000000000require "notiffany/notifier" require "guard/ui" module Guard class Notifier def self.connect(options = {}) @notifier ||= nil fail "Already connected!" if @notifier begin opts = options.merge(namespace: "guard", logger: UI) @notifier = Notiffany.connect(opts) rescue Notiffany::Notifier::Detected::UnknownNotifier => e UI.error "Failed to setup notification: #{e.message}" fail end end def self.disconnect @notifier && @notifier.disconnect @notifier = nil end DEPRECATED_IMPLICIT_CONNECT = "Calling Notiffany::Notifier.notify()"\ " without a prior Notifier.connect() is"\ " deprecated" def self.notify(message, options = {}) unless @notifier # TODO: reenable again? # UI.deprecation(DEPRECTED_IMPLICIT_CONNECT) connect(notify: true) end @notifier.notify(message, options) rescue RuntimeError => e UI.error "Notification failed for #{@notifier.class.name}: #{e.message}" UI.debug e.backtrace.join("\n") end def self.turn_on @notifier.turn_on end def self.toggle unless @notifier.enabled? UI.error NOTIFICATIONS_DISABLED return end if @notifier.active? UI.info "Turn off notifications" @notifier.turn_off return end @notifier.turn_on end # Used by dsl describer def self.supported Notiffany::Notifier::SUPPORTED.inject(:merge) end # Used by dsl describer def self.detected @notifier.available.map do |mod| { name: mod.name.to_sym, options: mod.options } end end end end guard-2.18.1/lib/guard/options.rb000066400000000000000000000011571450001123100165640ustar00rootroot00000000000000require "thor/core_ext/hash_with_indifferent_access" module Guard # A class that holds options. Can be instantiated with default options. # class Options < Thor::CoreExt::HashWithIndifferentAccess # Initializes an Guard::Options object. `default_opts` is merged into # `opts`. # # @param [Hash] opts the options # @param [Hash] default_opts the default options # def initialize(opts = {}, default_opts = {}) super(default_opts.merge(opts || {})) end # workaround for: https://github.com/erikhuda/thor/issues/504 def fetch(name) super(name.to_s) end end end guard-2.18.1/lib/guard/plugin.rb000066400000000000000000000226031450001123100163660ustar00rootroot00000000000000require "guard" require "guard/internals/groups" module Guard # Base class from which every Guard plugin implementation must inherit. # # Guard will trigger the {#start}, {#stop}, {#reload}, {#run_all} and # {#run_on_changes} ({#run_on_additions}, {#run_on_modifications} and # {#run_on_removals}) task methods depending on user interaction and file # modification. # # {#run_on_changes} could be implemented to handle all the changes task case # (additions, modifications, removals) in once, or each task can be # implemented separately with a specific behavior. # # In each of these Guard task methods you have to implement some work when # you want to support this kind of task. The return value of each Guard task # method is not evaluated by Guard, but it'll be passed to the "_end" hook # for further evaluation. You can throw `:task_has_failed` to indicate that # your Guard plugin method was not successful, and successive Guard plugin # tasks will be aborted when the group has set the `:halt_on_fail` option. # # @see Guard::Group # # @example Throw :task_has_failed # # def run_all # if !runner.run(['all']) # throw :task_has_failed # end # end # # Each Guard plugin should provide a template Guardfile located within the Gem # at `lib/guard/guard-name/templates/Guardfile`. # # Watchers for a Guard plugin should return a file path or an array of files # paths to Guard, but if your Guard plugin wants to allow any return value # from a watcher, you can set the `any_return` option to true. # # If one of those methods raises an exception other than `:task_has_failed`, # the `Guard::GuardName` instance will be removed from the active Guard # plugins. # class Plugin TEMPLATE_FORMAT = "%s/lib/guard/%s/templates/Guardfile" require "guard/ui" # Get all callbacks registered for all Guard plugins present in the # Guardfile. # def self.callbacks @callbacks ||= Hash.new { |hash, key| hash[key] = [] } end # Add a callback. # # @param [Block] listener the listener to notify # @param [Guard::Plugin] guard_plugin the Guard plugin to add the callback # @param [Array] events the events to register # def self.add_callback(listener, guard_plugin, events) Array(events).each do |event| callbacks[[guard_plugin, event]] << listener end end # Notify a callback. # # @param [Guard::Plugin] guard_plugin the Guard plugin to add the callback # @param [Symbol] event the event to trigger # @param [Array] args the arguments for the listener # def self.notify(guard_plugin, event, *args) callbacks[[guard_plugin, event]].each do |listener| listener.call(guard_plugin, event, *args) end end # Reset all callbacks. # # TODO: remove (not used anywhere) def self.reset_callbacks! @callbacks = nil end # When event is a Symbol, {#hook} will generate a hook name # by concatenating the method name from where {#hook} is called # with the given Symbol. # # @example Add a hook with a Symbol # # def run_all # hook :foo # end # # Here, when {Guard::Plugin#run_all} is called, {#hook} will notify # callbacks registered for the "run_all_foo" event. # # When event is a String, {#hook} will directly turn the String # into a Symbol. # # @example Add a hook with a String # # def run_all # hook "foo_bar" # end # # When {Guard::Plugin::run_all} is called, {#hook} will notify # callbacks registered for the "foo_bar" event. # # @param [Symbol, String] event the name of the Guard event # @param [Array] args the parameters are passed as is to the callbacks # registered for the given event. # def hook(event, *args) hook_name = if event.is_a? Symbol calling_method = caller[0][/`([^']*)'/, 1] "#{ calling_method }_#{ event }" else event end UI.debug "Hook :#{ hook_name } executed for #{ self.class }" self.class.notify(self, hook_name.to_sym, *args) end attr_accessor :group, :watchers, :callbacks, :options # Returns the non-namespaced class name of the plugin # # # @example Non-namespaced class name for Guard::RSpec # Guard::RSpec.non_namespaced_classname # #=> "RSpec" # # @return [String] # def self.non_namespaced_classname to_s.sub("Guard::", "") end # Returns the non-namespaced name of the plugin # # # @example Non-namespaced name for Guard::RSpec # Guard::RSpec.non_namespaced_name # #=> "rspec" # # @return [String] # def self.non_namespaced_name non_namespaced_classname.downcase end # Specify the source for the Guardfile template. # Each Guard plugin can redefine this method to add its own logic. # # @param [String] plugin_location the plugin location # def self.template(plugin_location) File.read(format(TEMPLATE_FORMAT, plugin_location, non_namespaced_name)) end # Called once when Guard starts. Please override initialize method to # init stuff. # # @raise [:task_has_failed] when start has failed # @return [Object] the task result # # @!method start # Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard # quits). # # @raise [:task_has_failed] when stop has failed # @return [Object] the task result # # @!method stop # Called when `reload|r|z + enter` is pressed. # This method should be mainly used for "reload" (really!) actions like # reloading passenger/spork/bundler/... # # @raise [:task_has_failed] when reload has failed # @return [Object] the task result # # @!method reload # Called when just `enter` is pressed # This method should be principally used for long action like running all # specs/tests/... # # @raise [:task_has_failed] when run_all has failed # @return [Object] the task result # # @!method run_all # Default behaviour on file(s) changes that the Guard plugin watches. # # @param [Array] paths the changes files or paths # @raise [:task_has_failed] when run_on_changes has failed # @return [Object] the task result # # @!method run_on_changes(paths) # Called on file(s) additions that the Guard plugin watches. # # @param [Array] paths the changes files or paths # @raise [:task_has_failed] when run_on_additions has failed # @return [Object] the task result # # @!method run_on_additions(paths) # Called on file(s) modifications that the Guard plugin watches. # # @param [Array] paths the changes files or paths # @raise [:task_has_failed] when run_on_modifications has failed # @return [Object] the task result # # @!method run_on_modifications(paths) # Called on file(s) removals that the Guard plugin watches. # # @param [Array] paths the changes files or paths # @raise [:task_has_failed] when run_on_removals has failed # @return [Object] the task result # # @!method run_on_removals(paths) # Returns the plugin's name (without "guard-"). # # @example Name for Guard::RSpec # Guard::RSpec.new.name # #=> "rspec" # # @return [String] # def name @name ||= self.class.non_namespaced_name end # Returns the plugin's class name without the Guard:: namespace. # # @example Title for Guard::RSpec # Guard::RSpec.new.title # #=> "RSpec" # # @return [String] # def title @title ||= self.class.non_namespaced_classname end # String representation of the plugin. # # @example String representation of an instance of the Guard::RSpec plugin # # Guard::RSpec.new.title # #=> "# @watchers=[] @callbacks=[] @options={all_after_pass: # true}>" # # @return [String] the string representation # def to_s "#<#{self.class} @name=#{name} @group=#{group} @watchers=#{watchers}"\ " @callbacks=#{callbacks} @options=#{options}>" end private # Initializes a Guard plugin. # Don't do any work here, especially as Guard plugins get initialized even # if they are not in an active group! # # @param [Hash] options the Guard plugin options # @option options [Array] watchers the Guard plugin file # watchers # @option options [Symbol] group the group this Guard plugin belongs to # @option options [Boolean] any_return allow any object to be returned from # a watcher # def initialize(options = {}) group_name = options.delete(:group) { :default } @group = Guard.state.session.groups.add(group_name) @watchers = options.delete(:watchers) { [] } @callbacks = options.delete(:callbacks) { [] } @options = options _register_callbacks end # Add all the Guard::Plugin's callbacks to the global @callbacks array # that's used by Guard to know which callbacks to notify. # def _register_callbacks callbacks.each do |callback| self.class.add_callback(callback[:listener], self, callback[:events]) end end end end guard-2.18.1/lib/guard/plugin_util.rb000066400000000000000000000136451450001123100174310ustar00rootroot00000000000000require "guard/ui" module Guard # This class contains useful methods to: # # * Fetch all the Guard plugins names; # * Initialize a plugin, get its location; # * Return its class name; # * Add its template to the Guardfile. # class PluginUtil ERROR_NO_GUARD_OR_CLASS = "Could not load 'guard/%s' or" \ " find class Guard::%s" INFO_ADDED_GUARD_TO_GUARDFILE = "%s guard added to Guardfile,"\ " feel free to edit it" attr_accessor :name # Returns a list of Guard plugin Gem names installed locally. # # @return [Array] a list of Guard plugin gem names # def self.plugin_names valid = Gem::Specification.find_all.select do |gem| _gem_valid?(gem) end valid.map { |x| x.name.sub(/^guard-/, "") }.uniq end # Initializes a new `Guard::PluginUtil` object. # # @param [String] name the name of the Guard plugin # def initialize(name) @name = name.to_s.sub(/^guard-/, "") end # Initializes a new `Guard::Plugin` with the given `options` hash. This # methods handles plugins that inherit from the deprecated `Guard::Guard` # class as well as plugins that inherit from `Guard::Plugin`. # # @see Guard::Plugin # @see https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 How to # upgrade for Guard 2.0 # # @return [Guard::Plugin] the initialized plugin # @return [Guard::Guard] the initialized plugin. This return type is # deprecated and the plugin's maintainer should update it to be # compatible with Guard 2.0. For more information on how to upgrade for # Guard 2.0, please head over to: # https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0 # def initialize_plugin(options) klass = plugin_class fail "Could not load class: #{_constant_name.inspect}" unless klass if klass.ancestors.include?(Guard) klass.new(options.delete(:watchers), options) else begin klass.new(**options) rescue ArgumentError => e fail "Failed to call #{klass}.new(options): #{e}" end end end # Locates a path to a Guard plugin gem. # # @return [String] the full path to the plugin gem # def plugin_location @plugin_location ||= _full_gem_path("guard-#{name}") rescue Gem::LoadError UI.error "Could not find 'guard-#{ name }' gem path." end # Tries to load the Guard plugin main class. This transforms the supplied # plugin name into a class name: # # * `guardname` will become `Guard::Guardname` # * `dashed-guard-name` will become `Guard::DashedGuardName` # * `underscore_guard_name` will become `Guard::UnderscoreGuardName` # # When no class is found with the strict case sensitive rules, another # try is made to locate the class without matching case: # # * `rspec` will find a class `Guard::RSpec` # # @option options [Boolean] fail_gracefully whether error messages should # not be printed # # @return [Class, nil] the loaded class # def plugin_class(options = {}) options = { fail_gracefully: false }.merge(options) const = _plugin_constant fail TypeError, "no constant: #{_constant_name}" unless const @plugin_class ||= Guard.const_get(const) rescue TypeError begin require "guard/#{ name.downcase }" const = _plugin_constant @plugin_class ||= Guard.const_get(const) rescue TypeError => error UI.error "Could not find class Guard::#{ _constant_name }" UI.error error.backtrace.join("\n") # TODO: return value or move exception higher rescue LoadError => error unless options[:fail_gracefully] msg = format(ERROR_NO_GUARD_OR_CLASS, name.downcase, _constant_name) UI.error(msg) UI.error("Error is: #{error}") UI.error(error.backtrace.join("\n")) # TODO: return value or move exception higher end end end # Adds a plugin's template to the Guardfile. # def add_to_guardfile klass = plugin_class # call here to avoid failing later require_relative "guardfile/evaluator" # TODO: move this to Generator? options = Guard.state.session.evaluator_options evaluator = Guardfile::Evaluator.new(options) begin evaluator.evaluate rescue Guard::Guardfile::Evaluator::NoPluginsError end if evaluator.guardfile_include?(name) UI.info "Guardfile already includes #{ name } guard" else content = File.read("Guardfile") File.open("Guardfile", "wb") do |f| f.puts(content) f.puts("") f.puts(klass.template(plugin_location)) end UI.info INFO_ADDED_GUARD_TO_GUARDFILE % name end end private # Returns the constant for the current plugin. # # @example Returns the constant for a plugin # > Guard::PluginUtil.new('rspec').send(:_plugin_constant) # => Guard::RSpec # def _plugin_constant @_plugin_constant ||= Guard.constants.detect do |c| c.to_s.casecmp(_constant_name.downcase).zero? end end # Guesses the most probable name for the current plugin based on its name. # # @example Returns the most probable name for a plugin # > Guard::PluginUtil.new('rspec').send(:_constant_name) # => "Rspec" # def _constant_name @_constant_name ||= name.gsub(%r{/(.?)}) { "::#{ $1.upcase }" }. gsub(/(?:^|[_-])(.)/) { $1.upcase } end def _full_gem_path(name) Gem::Specification.find_by_name(name).full_gem_path end class << self def _gem_valid?(gem) return false if gem.name == "guard-compat" return true if gem.name =~ /^guard-/ full_path = gem.full_gem_path file = File.join(full_path, "lib", "guard", "#{gem.name}.rb") File.exist?(file) end end end end guard-2.18.1/lib/guard/rake_task.rb000066400000000000000000000016141450001123100170330ustar00rootroot00000000000000#!/usr/bin/env ruby require "rake" require "rake/tasklib" require "guard/cli" module Guard # Provides a method to define a Rake task that # runs the Guard plugins. # class RakeTask < ::Rake::TaskLib # Name of the main, top level task attr_accessor :name # CLI options attr_accessor :options # Initialize the Rake task # # @param [Symbol] name the name of the Rake task # @param [String] options the CLI options # @yield [Guard::RakeTask] the task # def initialize(name = :guard, options = "") @name = name @options = options yield self if block_given? desc "Starts Guard with options: '#{options}'" task name => ["#{name}:start"] namespace(name) do desc "Starts Guard with options: '#{options}'" task(:start) do ::Guard::CLI.start(options.split) end end end end end guard-2.18.1/lib/guard/runner.rb000066400000000000000000000101131450001123100163720ustar00rootroot00000000000000require "lumberjack" require "guard/ui" require "guard/watcher" module Guard # The runner is responsible for running all methods defined on each plugin. # class Runner # Runs a Guard-task on all registered plugins. # # @param [Symbol] task the task to run # # @param [Hash] scope_hash either the Guard plugin or the group to run the task # on # def run(task, scope_hash = {}) Lumberjack.unit_of_work do items = Guard.state.scope.grouped_plugins(scope_hash || {}) items.each do |_group, plugins| _run_group_plugins(plugins) do |plugin| _supervise(plugin, task) if plugin.respond_to?(task) end end end end PLUGIN_FAILED = "%s has failed, other group's plugins will be skipped." MODIFICATION_TASKS = [ :run_on_modifications, :run_on_changes, :run_on_change ] ADDITION_TASKS = [:run_on_additions, :run_on_changes, :run_on_change] REMOVAL_TASKS = [:run_on_removals, :run_on_changes, :run_on_deletion] # Runs the appropriate tasks on all registered plugins # based on the passed changes. # # @param [Array] modified the modified paths. # @param [Array] added the added paths. # @param [Array] removed the removed paths. # def run_on_changes(modified, added, removed) types = { MODIFICATION_TASKS => modified, ADDITION_TASKS => added, REMOVAL_TASKS => removed } UI.clearable Guard.state.scope.grouped_plugins.each do |_group, plugins| _run_group_plugins(plugins) do |plugin| UI.clear types.each do |tasks, unmatched_paths| next if unmatched_paths.empty? match_result = Watcher.match_files(plugin, unmatched_paths) next if match_result.empty? task = tasks.detect { |meth| plugin.respond_to?(meth) } _supervise(plugin, task, match_result) if task end end end end # Run a Guard plugin task, but remove the Guard plugin when his work leads # to a system failure. # # When the Group has `:halt_on_fail` disabled, we've to catch # `:task_has_failed` here in order to avoid an uncaught throw error. # # @param [Guard::Plugin] plugin guard the Guard to execute # @param [Symbol] task the task to run # @param [Array] args the arguments for the task # @raise [:task_has_failed] when task has failed # def _supervise(plugin, task, *args) catch self.class.stopping_symbol_for(plugin) do plugin.hook("#{ task }_begin", *args) result = UI.options.with_progname(plugin.class.name) do begin plugin.send(task, *args) rescue Interrupt throw(:task_has_failed) end end plugin.hook("#{ task }_end", result) result end rescue ScriptError, StandardError, RuntimeError UI.error("#{ plugin.class.name } failed to achieve its"\ " <#{ task }>, exception was:" \ "\n#{ $!.class }: #{ $!.message }" \ "\n#{ $!.backtrace.join("\n") }") Guard.state.session.plugins.remove(plugin) UI.info("\n#{ plugin.class.name } has just been fired") $! end # Returns the symbol that has to be caught when running a supervised task. # # @note If a Guard group is being run and it has the `:halt_on_fail` # option set, this method returns :no_catch as it will be caught at the # group level. # # @param [Guard::Plugin] guard the Guard plugin to execute # @return [Symbol] the symbol to catch # def self.stopping_symbol_for(guard) guard.group.options[:halt_on_fail] ? :no_catch : :task_has_failed end private def _run_group_plugins(plugins) failed_plugin = nil catch :task_has_failed do plugins.each do |plugin| failed_plugin = plugin yield plugin failed_plugin = nil end end UI.info format(PLUGIN_FAILED, failed_plugin.class.name) if failed_plugin end end end guard-2.18.1/lib/guard/templates/000077500000000000000000000000001450001123100165365ustar00rootroot00000000000000guard-2.18.1/lib/guard/templates/Guardfile000066400000000000000000000011671450001123100203700ustar00rootroot00000000000000# A sample Guardfile # More info at https://github.com/guard/guard#readme ## Uncomment and set this to only include directories you want to watch # directories %w(app lib config test spec features) \ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")} ## Note: if you are using the `directories` clause above and you are not ## watching the project directory ('.'), then you will want to move ## the Guardfile to a watched dir and symlink it back, e.g. # # $ mkdir config # $ mv Guardfile config/ # $ ln -s config/Guardfile . # # and, you'll have to watch "config/Guardfile" instead of "Guardfile" guard-2.18.1/lib/guard/terminal.rb000066400000000000000000000004561450001123100167050ustar00rootroot00000000000000require "shellany/sheller" module Guard class Terminal class << self def clear cmd = Gem.win_platform? ? "cls" : "printf '\33c\e[3J';" exit_code, _, stderr = Shellany::Sheller.system(cmd) fail Errno::ENOENT, stderr unless exit_code == 0 end end end end guard-2.18.1/lib/guard/ui.rb000066400000000000000000000201551450001123100155050ustar00rootroot00000000000000require "guard/ui/colors" require "guard/ui/config" require "guard/terminal" require "forwardable" # TODO: rework this class from the bottom-up # - remove dependency on Session and Scope # - extract into a separate gem # - change UI to class module Guard # The UI class helps to format messages for the user. Everything that is # logged through this class is considered either as an error message or a # diagnostic message and is written to standard error ($stderr). # # If your Guard plugin does some output that is piped into another process # for further processing, please just write it to STDOUT with `puts`. # module UI include Colors class << self # Get the Guard::UI logger instance # def logger @logger ||= begin require "lumberjack" Lumberjack::Logger.new(options.device, options.logger_config) end end # Since logger is global, for Aruba in-process to properly # separate output between calls, we need to reset # # We don't use logger=() since it's expected to be a Lumberjack instance def reset_logger @logger = nil end # Get the logger options # # @return [Hash] the logger options # def options @options ||= Config.new end # Set the logger options # # @param [Hash] options the logger options # @option options [Symbol] level the log level # @option options [String] template the logger template # @option options [String] time_format the time format # # TODO: deprecate? def options=(options) @options = Config.new(options) end # Assigns a log level def level=(new_level) options.logger_config.level = new_level @logger.level = new_level if @logger end # Show an info message. # # @param [String] message the message to show # @option options [Boolean] reset whether to clean the output before # @option options [String] plugin manually define the calling plugin # def info(message, options = {}) _filtered_logger_message(message, :info, nil, options) end # Show a yellow warning message that is prefixed with WARNING. # # @param [String] message the message to show # @option options [Boolean] reset whether to clean the output before # @option options [String] plugin manually define the calling plugin # def warning(message, options = {}) _filtered_logger_message(message, :warn, :yellow, options) end # Show a red error message that is prefixed with ERROR. # # @param [String] message the message to show # @option options [Boolean] reset whether to clean the output before # @option options [String] plugin manually define the calling plugin # def error(message, options = {}) _filtered_logger_message(message, :error, :red, options) end # Show a red deprecation message that is prefixed with DEPRECATION. # It has a log level of `warn`. # # @param [String] message the message to show # @option options [Boolean] reset whether to clean the output before # @option options [String] plugin manually define the calling plugin # def deprecation(message, options = {}) unless ENV["GUARD_GEM_SILENCE_DEPRECATIONS"] == "1" backtrace = Thread.current.backtrace[1..5].join("\n\t >") msg = format("%s\nDeprecation backtrace: %s", message, backtrace) warning(msg, options) end end # Show a debug message that is prefixed with DEBUG and a timestamp. # # @param [String] message the message to show # @option options [Boolean] reset whether to clean the output before # @option options [String] plugin manually define the calling plugin # def debug(message, options = {}) _filtered_logger_message(message, :debug, :yellow, options) end # Reset a line. # def reset_line $stderr.print(color_enabled? ? "\r\e[0m" : "\r\n") end # Clear the output if clearable. # def clear(opts = {}) return unless Guard.state.session.clear? fail "UI not set up!" if @clearable.nil? return unless @clearable || opts[:force] @clearable = false Terminal.clear rescue Errno::ENOENT => e warning("Failed to clear the screen: #{e.inspect}") end # TODO: arguments: UI uses Guard::options anyway # @private api def reset_and_clear @clearable = false clear(force: true) end # Allow the screen to be cleared again. # def clearable @clearable = true end # Show a scoped action message. # # @param [String] action the action to show # @param [Hash] scope hash with a guard or a group scope # def action_with_scopes(action, scope) titles = Guard.state.scope.titles(scope) info "#{action} #{titles.join(', ')}" end private # Filters log messages depending on either the # `:only`` or `:except` option. # # @param [String] plugin the calling plugin name # @yield When the message should be logged # @yieldparam [String] param the calling plugin name # def _filter(plugin) only = options.only except = options.except plugin ||= _calling_plugin_name match = !(only || except) match ||= (only && only.match(plugin)) match ||= (except && !except.match(plugin)) return unless match yield plugin end # @private def _filtered_logger_message(message, method, color_name, options = {}) message = color(message, color_name) if color_name _filter(options[:plugin]) do reset_line if options[:reset] logger.send(method, message) end end # Tries to extract the calling Guard plugin name # from the call stack. # # @param [Integer] depth the stack depth # @return [String] the Guard plugin name # def _calling_plugin_name name = caller.lazy.map do |line| %r{(?] files the changed files # @return [Array] the matched watcher response # def self.match_files(guard, files) return [] if files.empty? files.inject([]) do |paths, file| guard.watchers.each do |watcher| matches = watcher.match(file) next(paths) unless matches if watcher.action result = watcher.call_action(matches) if guard.options[:any_return] paths << result elsif result.respond_to?(:empty?) && !result.empty? paths << Array(result) else next(paths) end else paths << matches[0] end break if guard.options[:first_match] end guard.options[:any_return] ? paths : paths.flatten.map(&:to_s) end end def match(string_or_pathname) m = pattern.match(string_or_pathname) m.nil? ? nil : Pattern::MatchResult.new(m, string_or_pathname) end # Executes a watcher action. # # @param [String, MatchData] matches the matched path or the match from the # Regex # @return [String] the final paths # def call_action(matches) @action.arity > 0 ? @action.call(matches) : @action.call rescue => ex UI.error "Problem with watch action!\n#{ex.message}" UI.error ex.backtrace.join("\n") end end end guard-2.18.1/lib/guard/watcher/000077500000000000000000000000001450001123100161755ustar00rootroot00000000000000guard-2.18.1/lib/guard/watcher/pattern.rb000066400000000000000000000012221450001123100201740ustar00rootroot00000000000000require "guard/ui" require_relative "pattern/match_result" require_relative "pattern/matcher" require_relative "pattern/deprecated_regexp" require_relative "pattern/simple_path" require_relative "pattern/pathname_path" module Guard class Watcher class Pattern def self.create(pattern) if DeprecatedRegexp.new(pattern).deprecated? DeprecatedRegexp.show_deprecation(pattern) return DeprecatedRegexp.convert(pattern) end return PathnamePath.new(pattern) if pattern.is_a?(Pathname) return SimplePath.new(pattern) if pattern.is_a?(String) Matcher.new(pattern) end end end end guard-2.18.1/lib/guard/watcher/pattern/000077500000000000000000000000001450001123100176525ustar00rootroot00000000000000guard-2.18.1/lib/guard/watcher/pattern/deprecated_regexp.rb000066400000000000000000000023561450001123100236570ustar00rootroot00000000000000require_relative "matcher" module Guard class Watcher class Pattern # TODO: remove before Guard 3.x class DeprecatedRegexp def initialize(pattern) @original_pattern = pattern end def self.convert(pattern) Matcher.new(Regexp.new(pattern)) end def deprecated? regexp = /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/ @original_pattern.is_a?(String) && regexp.match(@original_pattern) end def self.show_deprecation(pattern) @warning_printed ||= false unless @warning_printed msg = "*" * 20 + "\nDEPRECATION WARNING!\n" + "*" * 20 msg += <<-MSG You have a string in your Guardfile watch patterns that seem to represent a Regexp. Guard matches String with == and Regexp with Regexp#match. You should either use plain String (without Regexp special characters) or real Regexp. MSG UI.deprecation(msg) @warning_printed = true end new_regexp = Regexp.new(pattern).inspect UI.info "\"#{pattern}\" will be converted to #{new_regexp}\n" end end end end end guard-2.18.1/lib/guard/watcher/pattern/match_result.rb000066400000000000000000000006521450001123100226740ustar00rootroot00000000000000module Guard class Watcher class Pattern class MatchResult def initialize(match_result, original_value) @match_result = match_result @original_value = original_value end def [](index) return @match_result[index] if index.is_a?(Symbol) return @original_value if index.zero? @match_result.to_a[index] end end end end end guard-2.18.1/lib/guard/watcher/pattern/matcher.rb000066400000000000000000000013551450001123100216260ustar00rootroot00000000000000module Guard class Watcher class Pattern class Matcher attr_reader :matcher def initialize(obj) @matcher = obj end # Compare with other matcher # @param other [Guard::Watcher::Pattern::Matcher] # other matcher for comparing # @return [true, false] equal or not def ==(other) matcher == other.matcher end def match(string_or_pathname) @matcher.match(normalized(string_or_pathname)) end private def normalized(string_or_pathname) path = Pathname.new(string_or_pathname).cleanpath return path.to_s if @matcher.is_a?(Regexp) path end end end end end guard-2.18.1/lib/guard/watcher/pattern/pathname_path.rb000066400000000000000000000004171450001123100230120ustar00rootroot00000000000000require_relative "simple_path" module Guard class Watcher class Pattern class PathnamePath < SimplePath protected def normalize(string_or_pathname) Pathname.new(string_or_pathname).cleanpath end end end end end guard-2.18.1/lib/guard/watcher/pattern/simple_path.rb000066400000000000000000000007631450001123100225120ustar00rootroot00000000000000module Guard class Watcher class Pattern class SimplePath def initialize(string_or_pathname) @path = normalize(string_or_pathname) end def match(string_or_pathname) cleaned = normalize(string_or_pathname) return nil unless @path == cleaned [cleaned] end protected def normalize(string_or_pathname) Pathname.new(string_or_pathname).cleanpath.to_s end end end end end guard-2.18.1/lib/tasks/000077500000000000000000000000001450001123100145635ustar00rootroot00000000000000guard-2.18.1/lib/tasks/releaser.rb000066400000000000000000000055731450001123100167240ustar00rootroot00000000000000# TODO: extract to gem? class Releaser def initialize(options = {}) @project_name = options.delete(:project_name) do fail "project_name is needed!" end @gem_name = options.delete(:gem_name) do fail "gem_name is needed!" end @github_repo = options.delete(:github_repo) do fail "github_repo is needed!" end @version = options.delete(:version) do fail "version is needed!" end end def full rubygems github end def rubygems input = nil loop do STDOUT.puts "Release #{@project_name} #{@version} to RubyGems? (y/n)" input = STDIN.gets.chomp.downcase break if %w(y n).include?(input) end exit if input == "n" Rake::Task["release"].invoke end def github tag_name = "v#{@version}" require "gems" _verify_released _verify_tag_pushed require "octokit" gh_client = Octokit::Client.new(netrc: true) gh_release = _detect_gh_release(gh_client, tag_name, true) return unless gh_release STDOUT.puts "Draft release for #{tag_name}:\n" STDOUT.puts gh_release.body STDOUT.puts "\n-------------------------\n\n" _confirm_publish return unless _update_release(gh_client, gh_release, tag_name) gh_release = _detect_gh_release(gh_client, tag_name, false) _success_summary(gh_release, tag_name) end private def _verify_released if @version != Gems.info(@gem_name)["version"] STDOUT.puts "#{@project_name} #{@version} is not yet released." STDOUT.puts "Please release it first with: rake release:gem" exit end end def _verify_tag_pushed tags = `git ls-remote --tags origin`.split("\n") return if tags.detect { |tag| tag =~ /v#{@version}$/ } STDOUT.puts "The tag v#{@version} has not yet been pushed." STDOUT.puts "Please push it first with: rake release:gem" exit end def _success_summary(gh_release, tag_name) href = gh_release.rels[:html].href STDOUT.puts "GitHub release #{tag_name} has been published!" STDOUT.puts "\nPlease enjoy and spread the word!" STDOUT.puts "Lack of inspiration? Here's a tweet you could improve:\n\n" STDOUT.puts "Just released #{@project_name} #{@version}! #{href}" end def _detect_gh_release(gh_client, tag_name, draft) gh_releases = gh_client.releases(@github_repo) gh_releases.detect { |r| r.tag_name == tag_name && r.draft == draft } end def _confirm_publish input = nil loop do STDOUT.puts "Would you like to publish this GitHub release now? (y/n)" input = STDIN.gets.chomp.downcase break if %w(y n).include?(input) end exit if input == "n" end def _update_release(gh_client, gh_release, tag_name) result = gh_client.update_release(gh_release.rels[:self].href, draft: false) return true if result STDOUT.puts "GitHub release #{tag_name} couldn't be published!" false end end guard-2.18.1/man/000077500000000000000000000000001450001123100134435ustar00rootroot00000000000000guard-2.18.1/man/guard.1000066400000000000000000000067421450001123100146400ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "GUARD" "1" "November 2014" "" "" . .SH "NAME" \fBguard\fR \- Guard keeps an eye on your file modifications\. . .SH "DESCRIPTION" Guard is a command line tool to easily handle events on file system modifications\. . .SH "SYNOPSIS" \fBguard \fR . .SH "COMMANDS" . .SS "start" Starts Guard\. This is the default command if none is provided\. . .P The following options are available: . .P \fB\-c\fR, \fB\-\-clear\fR Clears the Shell after each change\. . .P \fB\-n\fR, \fB\-\-notify\fR \fIFLAG\fR Disable notifications (Growl or Libnotify depending on your system)\. Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false\. FLAG can be \fBtrue\fR/\fBfalse\fR or \fBt\fR/\fBf\fR\. . .P \fB\-g\fR, \fB\-\-group\fR \fIGROUP1\fR \fIGROUP2\fR\.\.\. Scopes the Guard actions to the groups specified by GROUP1, GROUP2, etc\. Group names should be separated by spaces\. Plugins that don\'t belong to a group are considered global and are always run\. . .P \fB\-P\fR, \fB\-\-plugin\fR \fIPLUGIN1\fR \fIPLUGIN2\fR\.\.\. Scopes the Guard actions to the plugins specified by PLUGIN1, PLUGIN2, etc\. Plugin names should be separated by spaces\. . .P \fB\-d\fR, \fB\-\-debug\fR Runs Guard in debug mode\. . .P \fB\-w\fR, \fB\-\-watchdir\fR \fIPATH\fR Tells Guard to watch PATH instead of \fB\./\fR\. . .P \fB\-G\fR, \fB\-\-guardfile\fR \fIFILE\fR Tells Guard to use FILE as its Guardfile instead of \fB\./Guardfile\fR or \fB~/\.Guardfile\fR\. . .P \fB\-i\fR, \fB\-\-no\-interactions\fR Turn off completely any Guard terminal interactions\. . .P \fB\-B\fR, \fB\-\-no\-bundler\-warning\fR Turn off warning when Bundler is not present\. . .P \fB\-l\fR, \fB\-\-latency\fR Overwrite Listen\'s default latency\. . .P \fB\-p\fR, \fB\-\-force\-polling\fR Force usage of the Listen polling listener\. . .P \fB\-y\fR, \fB\-\-wait\-for\-delay\fR Overwrite Listen\'s default \fBwait_for_delay\fR, useful for kate\-like editors through ssh access\. . .SS "init [GUARDS]" If no Guardfile is present in the current directory, creates an empty Guardfile\. . .P If \fIGUARDS\fR are present, add their default Guardfile configuration to the current Guardfile\. Note that \fIGUARDS\fR is a list of the Guard plugin names without the \fBguard\-\fR prefix\. For instance to initialize guard\-rspec, run \fBguard init rspec\fR\. . .SS "list" Lists Guard plugins that can be used with the \fBinit\fR command\. . .SS "\-T, show" List defined groups and Guard plugins for the current Guardfile\. . .SS "\-h, help [COMMAND]" List all of Guard\'s available commands\. . .P If \fICOMMAND\fR is given, displays a specific help for \fITASK\fR\. . .SH "EXAMPLES" Initialize Guard and a specific Guard plugin at the same time: . .P \fB[bundle exec] guard init [rspec]\fR . .P Run Guard: . .P \fB[bundle exec] guard [start] \-\-watchdir ~/dev \-\-guardfile ~/env/Guardfile \-\-clear \-\-group backend frontend \-\-notify false \-\-debug\fR . .P or in a more concise way: . .P \fB[bundle exec] guard [start] \-w ~/dev \-G ~/env/Guardfile \-c \-g backend frontend \-n f \-d\fR . .SH "AUTHORS / CONTRIBUTORS" Thibaud Guillaume\-Gentil is the main author\. . .P A list of contributors based on all commits can be found here: https://github\.com/guard/guard/contributors . .SH "CHANGELOG" The changelog can be found at: https://github\.com/guard/guard/blob/master/CHANGELOG\.md . .P This manual has been written by Remy Coutable\. . .SH "WWW" http://guardgem\.org/ guard-2.18.1/man/guard.1.html000066400000000000000000000160211450001123100155720ustar00rootroot00000000000000 guard(1) - Guard keeps an eye on your file modifications.
  1. guard(1)
  2. guard(1)

NAME

guard - Guard keeps an eye on your file modifications.

DESCRIPTION

Guard is a command line tool to easily handle events on file system modifications.

SYNOPSIS

guard <COMMAND> <OPTIONS>

COMMANDS

start

Starts Guard. This is the default command if none is provided.

The following options are available:

-c, --clear Clears the Shell after each change.

-n, --notify FLAG Disable notifications (Growl or Libnotify depending on your system). Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false. FLAG can be true/false or t/f.

-g, --group GROUP1 GROUP2... Scopes the Guard actions to the groups specified by GROUP1, GROUP2, etc. Group names should be separated by spaces. Plugins that don't belong to a group are considered global and are always run.

-P, --plugin PLUGIN1 PLUGIN2... Scopes the Guard actions to the plugins specified by PLUGIN1, PLUGIN2, etc. Plugin names should be separated by spaces.

-d, --debug Runs Guard in debug mode.

-w, --watchdir PATH Tells Guard to watch PATH instead of ./.

-G, --guardfile FILE Tells Guard to use FILE as its Guardfile instead of ./Guardfile or ~/.Guardfile.

-i, --no-interactions Turn off completely any Guard terminal interactions.

-B, --no-bundler-warning Turn off warning when Bundler is not present.

-l, --latency Overwrite Listen's default latency.

-p, --force-polling Force usage of the Listen polling listener.

-y, --wait-for-delay Overwrite Listen's default wait_for_delay, useful for kate-like editors through ssh access.

init [GUARDS]

If no Guardfile is present in the current directory, creates an empty Guardfile.

If GUARDS are present, add their default Guardfile configuration to the current Guardfile. Note that GUARDS is a list of the Guard plugin names without the guard- prefix. For instance to initialize guard-rspec, run guard init rspec.

list

Lists Guard plugins that can be used with the init command.

-T, show

List defined groups and Guard plugins for the current Guardfile.

-h, help [COMMAND]

List all of Guard's available commands.

If COMMAND is given, displays a specific help for TASK.

EXAMPLES

Initialize Guard and a specific Guard plugin at the same time:

[bundle exec] guard init [rspec]

Run Guard:

[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug

or in a more concise way:

[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d

AUTHORS / CONTRIBUTORS

Thibaud Guillaume-Gentil is the main author.

A list of contributors based on all commits can be found here: https://github.com/guard/guard/contributors

CHANGELOG

The changelog can be found at: https://github.com/guard/guard/blob/master/CHANGELOG.md

This manual has been written by Remy Coutable.

WWW

http://guardgem.org/

  1. November 2014
  2. guard(1)
guard-2.18.1/man/guard.1.ronn000066400000000000000000000060041450001123100156020ustar00rootroot00000000000000guard(1) -- Guard keeps an eye on your file modifications. ======================================================== ## DESCRIPTION Guard is a command line tool to easily handle events on file system modifications. ## SYNOPSIS `guard ` ## COMMANDS ### start Starts Guard. This is the default command if none is provided. The following options are available: `-c`, `--clear` Clears the Shell after each change. `-n`, `--notify` Disable notifications (Growl or Libnotify depending on your system). Notifications can be disabled globally by setting a GUARD_NOTIFY environment variable to false. FLAG can be `true`/`false` or `t`/`f`. `-g`, `--group` ... Scopes the Guard actions to the groups specified by GROUP1, GROUP2, etc. Group names should be separated by spaces. Plugins that don't belong to a group are considered global and are always run. `-P`, `--plugin` ... Scopes the Guard actions to the plugins specified by PLUGIN1, PLUGIN2, etc. Plugin names should be separated by spaces. `-d`, `--debug` Runs Guard in debug mode. `-w`, `--watchdir` Tells Guard to watch PATH instead of `./`. `-G`, `--guardfile` Tells Guard to use FILE as its Guardfile instead of `./Guardfile` or `~/.Guardfile`. `-i`, `--no-interactions` Turn off completely any Guard terminal interactions. `-B`, `--no-bundler-warning` Turn off warning when Bundler is not present. `-l`, `--latency` Overwrite Listen's default latency. `-p`, `--force-polling` Force usage of the Listen polling listener. `-y`, `--wait-for-delay` Overwrite Listen's default `wait_for_delay`, useful for kate-like editors through ssh access. ### init [GUARDS] If no Guardfile is present in the current directory, creates an empty Guardfile. If are present, add their default Guardfile configuration to the current Guardfile. Note that is a list of the Guard plugin names without the `guard-` prefix. For instance to initialize guard-rspec, run `guard init rspec`. ### list Lists Guard plugins that can be used with the `init` command. ### -T, show List defined groups and Guard plugins for the current Guardfile. ### -h, help [COMMAND] List all of Guard's available commands. If is given, displays a specific help for . ## EXAMPLES Initialize Guard and a specific Guard plugin at the same time: `[bundle exec] guard init [rspec]` Run Guard: `[bundle exec] guard [start] --watchdir ~/dev --guardfile ~/env/Guardfile --clear --group backend frontend --notify false --debug` or in a more concise way: `[bundle exec] guard [start] -w ~/dev -G ~/env/Guardfile -c -g backend frontend -n f -d` ## AUTHORS / CONTRIBUTORS Thibaud Guillaume-Gentil is the main author. A list of contributors based on all commits can be found here: https://github.com/guard/guard/contributors ## CHANGELOG The changelog can be found at: https://github.com/guard/guard/blob/master/CHANGELOG.md This manual has been written by Remy Coutable. ## WWW http://guardgem.org/ guard-2.18.1/spec/000077500000000000000000000000001450001123100136225ustar00rootroot00000000000000guard-2.18.1/spec/.gitignore000066400000000000000000000000141450001123100156050ustar00rootroot00000000000000/fake-home/ guard-2.18.1/spec/lib/000077500000000000000000000000001450001123100143705ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/000077500000000000000000000000001450001123100154725ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/bin_spec.rb000066400000000000000000000100171450001123100176000ustar00rootroot00000000000000# frozen_string_literal: true path = File.expand_path("../../../bin/guard", __dir__) load path RSpec.describe GuardReloader do let(:config) { instance_double(described_class::Config) } subject { described_class.new(config) } let(:guard_core_path) { "/home/me/.rvm/gems/ruby-2.2.2/bin/_guard-core" } before do allow(described_class::Config).to receive(:new).and_return(config) allow(config).to receive(:current_bundler_gemfile) .and_return(bundle_gemfile_env) allow(config).to receive(:using_bundler?).and_return(bundle_gemfile_env) allow(config).to receive(:guard_core_path).and_return(guard_core_path) allow(config).to receive(:program_arguments).and_return(%w[foo bar baz]) allow(config).to receive(:using_rubygems?).and_return(rubygems_deps_env) allow(config).to receive(:program_path).and_return(program_path) end let(:program_path) { Pathname("/home/me/.rvm/gems/ruby-2.2.2/bin/guard") } let(:rubygems_deps_env) { nil } # or any gemfile path context "when running with bundler" do let(:bundle_gemfile_env) { "./Gemfile" } it "sets up bundler" do expect(config).to receive(:setup_bundler) subject.setup end end context "when not running with bundler" do let(:bundle_gemfile_env) { nil } context "when running with rubygems_gemdeps" do let(:rubygems_deps_env) { "-" } # or any gemfile path it "sets up rubygems" do expect(config).to receive(:setup_rubygems_for_deps) subject.setup end end context "when not running with rubygems_gemdeps" do let(:rubygems_deps_env) { nil } context "when running as binstub" do let(:program_path) { Pathname("/my/project/bin/guard") } context "when the relative Gemfile exists" do before do allow(config).to receive(:exist?) .with(Pathname("/my/project/Gemfile")).and_return(true) allow(config).to receive(:setup_bundler) allow(config).to receive(:setup_bundler_env) end it "sets up bundler" do expect(config).to receive(:setup_bundler) subject.setup end it "sets the Gemfile" do expect(config).to receive(:setup_bundler_env) .with("/my/project/Gemfile") subject.setup end end context "when the relative Gemfile does not exist" do before do allow(config).to receive(:exist?) .with(Pathname("/my/project/Gemfile")).and_return(false) allow(config).to receive(:exist?).with(Pathname("Gemfile")) .and_return(false) end it "does not setup bundler" do subject.setup end it "does not setup rubygems" do subject.setup end it "shows no warning" do expect(STDERR).to_not receive(:puts) subject.setup end end end context "when not run as binstub" do let(:program_path) do Pathname("/home/me/.rvm/gems/ruby-2.2.2/bin/guard") end before do allow(config).to receive(:exist?).with( Pathname("/home/me/.rvm/gems/ruby-2.2.2/Gemfile") ).and_return(false) end context "when Gemfile exists" do before do allow(config).to receive(:exist?).with(Pathname("Gemfile")) .and_return(true) end it "shows a warning" do expect(STDERR).to receive(:puts).with(/Warning: you have a Gemfile/) subject.setup end end context "when no Gemfile exists" do before do allow(config).to receive(:exist?).with(Pathname("Gemfile")) .and_return(false) end it "shows no warning" do expect(STDERR).to_not receive(:puts) subject.setup end end end end end end guard-2.18.1/spec/lib/guard/cli/000077500000000000000000000000001450001123100162415ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/cli/environments/000077500000000000000000000000001450001123100207705ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/cli/environments/bundler_spec.rb000066400000000000000000000032701450001123100237640ustar00rootroot00000000000000# frozen_string_literal: true require "guard/cli/environments/bundler" # TODO: instead of shared examples, use have_received if possible RSpec.shared_examples "avoids Bundler warning" do it "does not show the Bundler warning" do expect(Guard::UI).to_not have_received(:info).with(/Guard here!/) end end RSpec.shared_examples "shows Bundler warning" do it "shows the Bundler warning" do expect(Guard::UI).to have_received(:info).with(/Guard here!/) end end RSpec.describe Guard::Cli::Environments::Bundler do describe "#verify" do let(:gemdeps) { nil } let(:gemfile) { nil } before do allow(ENV).to receive(:[]).with("BUNDLE_GEMFILE").and_return(gemfile) allow(ENV).to receive(:[]).with("RUBYGEMS_GEMDEPS").and_return(gemdeps) allow(File).to receive(:exist?).with("Gemfile") .and_return(gemfile_present) subject.verify end context "without an existing Gemfile" do let(:gemfile_present) { false } include_examples "avoids Bundler warning" end context "with an existing Gemfile" do let(:gemfile_present) { true } context "with Bundler" do let(:gemdeps) { nil } let(:gemfile) { "Gemfile" } include_examples "avoids Bundler warning" end context "without Bundler" do let(:gemfile) { nil } context "with Rubygems Gemfile autodetection or custom Gemfile" do let(:gemdeps) { "-" } include_examples "avoids Bundler warning" end context "without Rubygems Gemfile handling" do let(:gemdeps) { nil } include_examples "shows Bundler warning" end end end end end guard-2.18.1/spec/lib/guard/cli/environments/evaluate_only_spec.rb000066400000000000000000000042501450001123100251770ustar00rootroot00000000000000# frozen_string_literal: true require "guard/cli/environments/evaluate_only" RSpec.describe Guard::Cli::Environments::EvaluateOnly do subject { described_class.new(options) } let(:options) { double("options") } describe "#evaluate" do let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } before do allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) allow(evaluator).to receive(:evaluate) allow(Guard).to receive(:init) allow(Guard).to receive(:state).and_return(state) allow(state).to receive(:session).and_return(session) allow(session).to receive(:evaluator_options) end it "calls Guard.init" do expect(Guard).to receive(:init) subject.evaluate end it "initializes Guard with options" do expect(Guard).to receive(:init).with(options) subject.evaluate end it "evaluates the guardfile" do expect(evaluator).to receive(:evaluate) subject.evaluate end it "passes options to evaluator" do evaluator_options = double("evaluator_options") allow(session).to receive(:evaluator_options) .and_return(evaluator_options) expect(Guard::Guardfile::Evaluator).to receive(:new) .with(evaluator_options).and_return(evaluator) subject.evaluate end [ Guard::Dsl::Error, Guard::Guardfile::Evaluator::NoPluginsError, Guard::Guardfile::Evaluator::NoGuardfileError, Guard::Guardfile::Evaluator::NoCustomGuardfile ].each do |error_class| context "when a #{error_class} error occurs" do before do allow(Guard).to receive(:init) .and_raise(error_class, "#{error_class} error!") end it "aborts" do expect { subject.evaluate }.to raise_error(SystemExit) end it "shows error message" do expect(Guard::UI).to receive(:error).with(/#{error_class} error!/) begin subject.evaluate rescue SystemExit end end end end end end guard-2.18.1/spec/lib/guard/cli/environments/valid_spec.rb000066400000000000000000000173301450001123100234320ustar00rootroot00000000000000# frozen_string_literal: true require "guard/cli/environments/valid" require "guard/cli/environments/bundler" RSpec.describe Guard::Cli::Environments::Valid do subject { described_class.new(options) } let(:options) { double("options") } before do # TODO: start should be an instance method of something allow(Guard).to receive(:start) end describe "#start_guard" do let(:bundler) { instance_double("Guard::Cli::Environments::Bundler") } before do allow(Guard::Cli::Environments::Bundler).to receive(:new) .and_return(bundler) allow(bundler).to receive(:verify) end context "with a valid bundler setup" do before do allow(bundler).to receive(:verify) allow(options).to receive(:[]).with(:no_bundler_warning) .and_return(false) end it "starts guard" do expect(Guard).to receive(:start) subject.start_guard end it "start guard with options" do expect(Guard).to receive(:start).with(options) subject.start_guard end it "returns exit code" do exitcode = double("exitcode") expect(Guard).to receive(:start).and_return(exitcode) expect(subject.start_guard).to be(exitcode) end [ Guard::Dsl::Error, Guard::Guardfile::Evaluator::NoPluginsError, Guard::Guardfile::Evaluator::NoGuardfileError, Guard::Guardfile::Evaluator::NoCustomGuardfile ].each do |error_class| context "when a #{error_class} error occurs" do before do allow(Guard).to receive(:start) .and_raise(error_class, "#{error_class} error!") end it "aborts" do expect { subject.start_guard }.to raise_error(SystemExit) end it "shows error message" do expect(Guard::UI).to receive(:error).with(/#{error_class} error!/) begin subject.start_guard rescue SystemExit end end end end end context "without no_bundler_warning option" do subject { described_class.new(no_bundler_warning: false) } it "verifies bundler presence" do expect(bundler).to receive(:verify) subject.start_guard end context "without a valid bundler setup" do before do allow(bundler).to receive(:verify).and_raise(SystemExit) end it "does not start guard" do expect(Guard).to_not receive(:start) begin subject.start_guard rescue SystemExit end end end end context "with no_bundler_warning option" do subject { described_class.new(no_bundler_warning: true) } it "does not verify bundler presence" do expect(bundler).to_not receive(:verify) subject.start_guard end it "starts guard" do expect(Guard).to receive(:start) subject.start_guard end end describe "return value" do let(:exitcode) { double("Fixnum") } subject { described_class.new(no_bundler_warning: true) } before do allow(Guard).to receive(:start).and_return(exitcode) end it "matches return value of Guard.start" do expect(subject.start_guard).to be(exitcode) end end end describe "#initialize_guardfile" do let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } let(:generator) { instance_double("Guard::Guardfile::Generator") } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } before do stub_file("Gemfile") allow(evaluator).to receive(:evaluate) allow(generator).to receive(:create_guardfile) allow(generator).to receive(:initialize_all_templates) allow(session).to receive(:evaluator_options) allow(state).to receive(:session).and_return(session) allow(Guard::Internals::State).to receive(:new).and_return(state) allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) allow(Guard::Guardfile::Generator).to receive(:new).and_return(generator) end context "with bare option" do before do expect(options).to receive(:[]).with(:bare).and_return(true) end it "Only creates the Guardfile without initializing any Guard template" do allow(evaluator).to receive(:evaluate) .and_raise(Guard::Guardfile::Evaluator::NoGuardfileError) allow(File).to receive(:exist?).with("Gemfile").and_return(false) expect(generator).to receive(:create_guardfile) expect(generator).to_not receive(:initialize_template) expect(generator).to_not receive(:initialize_all_templates) subject.initialize_guardfile end it "returns an exit code" do # TODO: ideally, we'd capture known exceptions and return nonzero expect(subject.initialize_guardfile).to be_zero end end context "with no bare option" do before do expect(options).to receive(:[]).with(:bare).and_return(false) end it "evaluates created or existing guardfile" do expect(evaluator).to receive(:evaluate) subject.initialize_guardfile end it "creates a Guardfile" do expect(evaluator).to receive(:evaluate) .and_raise(Guard::Guardfile::Evaluator::NoGuardfileError).once expect(evaluator).to receive(:evaluate) expect(Guard::Guardfile::Generator).to receive(:new) .and_return(generator) expect(generator).to receive(:create_guardfile) subject.initialize_guardfile end it "initializes templates of all installed Guards" do allow(File).to receive(:exist?).with("Gemfile").and_return(false) expect(generator).to receive(:initialize_all_templates) subject.initialize_guardfile end it "initializes each passed template" do allow(File).to receive(:exist?).with("Gemfile").and_return(false) expect(generator).to receive(:initialize_template).with("rspec") expect(generator).to receive(:initialize_template).with("pow") subject.initialize_guardfile(%w[rspec pow]) end context "when passed a guard name" do context "when the Guardfile is empty" do before do allow(evaluator).to receive(:evaluate) .and_raise Guard::Guardfile::Evaluator::NoPluginsError allow(generator).to receive(:initialize_template) end it "works without without errors" do expect(subject.initialize_guardfile(%w[rspec])).to be_zero end it "adds the template" do expect(generator).to receive(:initialize_template).with("rspec") subject.initialize_guardfile(%w[rspec]) end end it "initializes the template of the passed Guard" do expect(generator).to receive(:initialize_template).with("rspec") subject.initialize_guardfile(%w[rspec]) end end it "returns an exit code" do expect(subject.initialize_guardfile).to be_zero end context "when passed an unknown guard name" do before do expect(generator).to receive(:initialize_template).with("foo") .and_raise(Guard::Guardfile::Generator::NoSuchPlugin, "foo") end it "returns an exit code" do expect(::Guard::UI).to receive(:error).with( "Could not load 'guard/foo' or '~/.guard/templates/foo'"\ " or find class Guard::Foo\n" ) expect(subject.initialize_guardfile(%w[foo])).to be(1) end end end end end guard-2.18.1/spec/lib/guard/cli_spec.rb000066400000000000000000000102731450001123100176030ustar00rootroot00000000000000# frozen_string_literal: true require "guard/cli" RSpec.describe Guard::CLI do let(:valid_environment) { instance_double("Guard::Cli::Environments::Valid") } let(:bare_environment) do instance_double("Guard::Cli::Environments::EvaluateOnly") end let(:dsl_describer) { instance_double("Guard::DslDescriber") } before do @options = {} allow(subject).to receive(:options).and_return(@options) allow(::Guard::DslDescriber).to receive(:new).with(no_args) .and_return(dsl_describer) allow(Guard::Cli::Environments::EvaluateOnly).to receive(:new) .and_return(bare_environment) allow(Guard::Cli::Environments::Valid).to receive(:new) .and_return(valid_environment) end describe "#start" do before do allow(valid_environment).to receive(:start_guard).and_return(0) end it "delegates to Guard::Environment.start" do pending "needs JRuby support first" if defined?(JRUBY_VERSION) expect(valid_environment).to receive(:start_guard).and_return(0) begin subject.start rescue SystemExit end end it "exits with given exit code" do pending "needs JRuby support first" if defined?(JRUBY_VERSION) allow(valid_environment).to receive(:start_guard).and_return(4) expect { subject.start }.to raise_error(SystemExit) do |exception| expect(exception.status).to eq(4) exception end end it "passes options" do pending "needs JRuby support first" if defined?(JRUBY_VERSION) expect(Guard::Cli::Environments::Valid).to receive(:new).with(@options) .and_return(valid_environment) begin subject.start rescue SystemExit end end end describe "#list" do before do allow(bare_environment).to receive(:evaluate) allow(dsl_describer).to receive(:list) subject.list end it "calls the evaluation" do expect(bare_environment).to have_received(:evaluate) end it "outputs the Guard plugins list" do expect(dsl_describer).to have_received(:list) end end describe "#notifiers" do before do allow(bare_environment).to receive(:evaluate) allow(dsl_describer).to receive(:notifiers) subject.notifiers end it "calls the evaluation" do expect(bare_environment).to have_received(:evaluate) end it "outputs the notifiers list" do expect(dsl_describer).to have_received(:notifiers) end end describe "#version" do it "shows the current version" do expect(STDOUT).to receive(:puts).with(/#{ ::Guard::VERSION }/) subject.version end end describe "#init" do before do allow(Guard::Cli::Environments::Valid).to receive(:new) .and_return(valid_environment) allow(valid_environment).to receive(:initialize_guardfile).and_return(0) end it "delegates to Guard::Environment.start" do begin subject.init rescue SystemExit end end it "exits with given exit code" do allow(valid_environment).to receive(:initialize_guardfile).and_return(4) expect { subject.init }.to raise_error(SystemExit) do |exception| expect(exception.status).to eq(4) end end it "passes options" do expect(Guard::Cli::Environments::Valid).to receive(:new).with(@options) .and_return(valid_environment) begin subject.init rescue SystemExit end end it "passes plugin names" do plugins = [double("plugin1"), double("plugin2")] expect(valid_environment).to receive(:initialize_guardfile).with(plugins) begin subject.init(*plugins) rescue SystemExit end end end describe "#show" do before do allow(bare_environment).to receive(:evaluate) allow(dsl_describer).to receive(:show) subject.show end it "calls the evaluation" do expect(bare_environment).to have_received(:evaluate) end it "outputs the Guard::DslDescriber.list result" do expect(dsl_describer).to have_received(:show) end end end guard-2.18.1/spec/lib/guard/commander_spec.rb000066400000000000000000000207301450001123100210000ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commander" RSpec.describe Guard::Commander do subject { Guard } let(:interactor) { instance_double("Guard::Interactor") } let(:runner) { instance_double("Guard::Runner", run: true) } let(:scope) { instance_double("Guard::Internals::Scope") } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } before do allow(state).to receive(:scope).and_return(scope) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(Guard::Interactor).to receive(:new) { interactor } allow(Guard::Runner).to receive(:new).and_return(runner) end describe ".start" do let(:listener) do instance_double("Listen::Listener", start: true, stop: true) end let(:watched_dir) { Dir.pwd } before do stub_guardfile(" ") stub_user_guard_rb # from stop() allow(Guard).to receive(:setup) allow(Guard).to receive(:listener).and_return(listener) allow(session).to receive(:watchdirs).and_return(%w[dir1 dir2]) allow(Guard).to receive(:interactor).and_return(interactor) # Simulate Ctrl-D in Pry, or Ctrl-C in non-interactive mode allow(interactor).to receive(:foreground).and_return(:exit) allow(interactor).to receive(:background) allow(Guard::Notifier).to receive(:disconnect) end it "calls Guard setup" do expect(Guard).to receive(:setup).with(foo: "bar") Guard.start(foo: "bar") end it "displays an info message" do expect(Guard::UI).to receive(:info) .with("Guard is now watching at 'dir1', 'dir2'") Guard.start end it "tell the runner to run the :start task" do expect(runner).to receive(:run).with(:start) allow(listener).to receive(:stop) Guard.start end it "start the listener" do expect(listener).to receive(:start) Guard.start end context "when finished" do it "stops everything" do expect(interactor).to receive(:foreground).and_return(:exit) # From stop() expect(interactor).to receive(:background) expect(listener).to receive(:stop) expect(runner).to receive(:run).with(:stop) expect(Guard::UI).to receive(:info).with("Bye bye...", reset: true) Guard.start end end context "when listener.start raises an error" do it "calls Commander#stop" do allow(listener).to receive(:start).and_raise(RuntimeError) # From stop() expect(interactor).to receive(:background) expect(listener).to receive(:stop) expect(runner).to receive(:run).with(:stop) expect(Guard::UI).to receive(:info).with("Bye bye...", reset: true) expect { Guard.start }.to raise_error(RuntimeError) end end context "when setup raises an error" do it "calls Commander#stop" do # Reproduce a case where an error is raised during Guardfile evaluation # before the listener and interactor are instantiated. expect(Guard).to receive(:listener).and_return(nil) expect(Guard).to receive(:interactor).and_return(nil) expect(Guard).to receive(:setup).and_raise(RuntimeError) # From stop() expect(runner).to receive(:run).with(:stop) expect(Guard::UI).to receive(:info).with("Bye bye...", reset: true) expect { Guard.start }.to raise_error(RuntimeError) end end end describe ".stop" do let(:runner) { instance_double("Guard::Runner", run: true) } let(:listener) { instance_double("Listen::Listener", stop: true) } before do allow(Guard::Notifier).to receive(:disconnect) allow(Guard).to receive(:listener).and_return(listener) allow(listener).to receive(:stop) allow(Guard).to receive(:interactor).and_return(interactor) allow(interactor).to receive(:background) Guard.stop end it "turns off the interactor" do expect(interactor).to have_received(:background) end it "turns the notifier off" do expect(Guard::Notifier).to have_received(:disconnect) end it "tell the runner to run the :stop task" do expect(runner).to have_received(:run).with(:stop) end it "stops the listener" do expect(listener).to have_received(:stop) end end describe ".reload" do let(:runner) { instance_double("Guard::Runner", run: true) } let(:group) { instance_double("Guard::Group", name: "frontend") } before do allow(Guard::Notifier).to receive(:connect) allow(Guard::UI).to receive(:info) allow(Guard::UI).to receive(:clear) allow(scope).to receive(:titles).and_return(["all"]) stub_guardfile(" ") stub_user_guard_rb end it "clears the screen" do expect(Guard::UI).to receive(:clear) Guard.reload end it "reloads Guard" do expect(runner).to receive(:run).with(:reload, groups: [group]) Guard.reload(groups: [group]) end end describe ".run_all" do let(:group) { instance_double("Guard::Group", name: "frontend") } before do allow(::Guard::Notifier).to receive(:connect) allow(::Guard::UI).to receive(:action_with_scopes) allow(::Guard::UI).to receive(:clear) end context "with a given scope" do it "runs all with the scope" do expect(runner).to receive(:run).with(:run_all, groups: [group]) subject.run_all(groups: [group]) end end context "with an empty scope" do it "runs all" do expect(runner).to receive(:run).with(:run_all, {}) subject.run_all end end end describe ".pause" do context "when unpaused" do let(:listener) { instance_double("Listen::Listener") } before do allow(::Guard::Notifier).to receive(:connect) allow(Guard).to receive(:listener).and_return(listener) allow(listener).to receive(:paused?) { false } end [:toggle, nil, :paused].each do |mode| context "with #{mode.inspect}" do before do allow(listener).to receive(:pause) end it "pauses" do expect(listener).to receive(:pause) Guard.pause(mode) end it "shows a message" do expected = /File event handling has been paused/ expect(Guard::UI).to receive(:info).with(expected) Guard.pause(mode) end end end context "with :unpaused" do it "does nothing" do expect(listener).to_not receive(:start) expect(listener).to_not receive(:pause) Guard.pause(:unpaused) end end context "with invalid parameter" do it "raises an ArgumentError" do expect { Guard.pause(:invalid) } .to raise_error(ArgumentError, "invalid mode: :invalid") end end end context "when already paused" do let(:listener) { instance_double("Listen::Listener") } before do allow(::Guard::Notifier).to receive(:connect) allow(Guard).to receive(:listener).and_return(listener) allow(listener).to receive(:paused?) { true } end [:toggle, nil, :unpaused].each do |mode| context "with #{mode.inspect}" do before do allow(listener).to receive(:start) end it "unpauses" do expect(listener).to receive(:start) Guard.pause(mode) end it "shows a message" do expected = /File event handling has been resumed/ expect(Guard::UI).to receive(:info).with(expected) Guard.pause(mode) end end end context "with :paused" do it "does nothing" do expect(listener).to_not receive(:start) expect(listener).to_not receive(:pause) Guard.pause(:paused) end end context "with invalid parameter" do it "raises an ArgumentError" do expect { Guard.pause(:invalid) } .to raise_error(ArgumentError, "invalid mode: :invalid") end end end end describe ".show" do let(:dsl_describer) { instance_double("Guard::DslDescriber") } before do allow(Guard::DslDescriber).to receive(:new).with(no_args) .and_return(dsl_describer) end it "shows list of plugins" do expect(dsl_describer).to receive(:show) Guard.show end end end guard-2.18.1/spec/lib/guard/commands/000077500000000000000000000000001450001123100172735ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/commands/all_spec.rb000066400000000000000000000045471450001123100214140ustar00rootroot00000000000000# frozen_string_literal: true require "guard/plugin" require "guard/commands/all" require "guard/internals/session" require "guard/internals/state" RSpec.describe Guard::Commands::All do let(:foo_group) { instance_double(Guard::Group) } let(:bar_guard) { instance_double(Guard::Plugin) } let(:output) { instance_double(Pry::Output) } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } class FakePry < Pry::Command def self.output; end end before do allow(session).to receive(:convert_scope).with(given_scope) .and_return(converted_scope) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("all") do |&block| FakePry.instance_eval(&block) end described_class.import end context "without scope" do let(:given_scope) { [] } let(:converted_scope) { [{ groups: [], plugins: [] }, []] } it "runs the :run_all action" do expect(Guard).to receive(:async_queue_add) .with([:guard_run_all, groups: [], plugins: []]) FakePry.process end end context "with a valid Guard group scope" do let(:given_scope) { ["foo"] } let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] } it "runs the :run_all action with the given scope" do expect(Guard).to receive(:async_queue_add) .with([:guard_run_all, groups: [foo_group], plugins: []]) FakePry.process("foo") end end context "with a valid Guard plugin scope" do let(:given_scope) { ["bar"] } let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] } it "runs the :run_all action with the given scope" do expect(Guard).to receive(:async_queue_add) .with([:guard_run_all, plugins: [bar_guard], groups: []]) FakePry.process("bar") end end context "with an invalid scope" do let(:given_scope) { ["baz"] } let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] } it "does not run the action" do expect(output).to receive(:puts).with("Unknown scopes: baz") expect(Guard).to_not receive(:async_queue_add) FakePry.process("baz") end end end guard-2.18.1/spec/lib/guard/commands/change_spec.rb000066400000000000000000000022761450001123100220660ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/change" RSpec.describe Guard::Commands::Change do let(:output) { instance_double(Pry::Output) } class FakePry < Pry::Command def self.output; end end before do allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("change") do |&block| FakePry.instance_eval(&block) end described_class.import end context "with a file" do it "runs the :run_on_changes action with the given file" do expect(::Guard).to receive(:async_queue_add) .with(modified: ["foo"], added: [], removed: []) FakePry.process("foo") end end context "with multiple files" do it "runs the :run_on_changes action with the given files" do expect(::Guard).to receive(:async_queue_add) .with(modified: %w[foo bar baz], added: [], removed: []) FakePry.process("foo", "bar", "baz") end end context "without a file" do it "does not run the :run_on_changes action" do expect(::Guard).to_not receive(:async_queue_add) expect(output).to receive(:puts).with("Please specify a file.") FakePry.process end end end guard-2.18.1/spec/lib/guard/commands/notification_spec.rb000066400000000000000000000011151450001123100233160ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/notification" RSpec.describe Guard::Commands::Notification do let(:output) { instance_double(Pry::Output) } class FakePry < Pry::Command def self.output; end end before do allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command) .with("notification") do |&block| FakePry.instance_eval(&block) end described_class.import end it "toggles the Guard notifier" do expect(::Guard::Notifier).to receive(:toggle) FakePry.process end end guard-2.18.1/spec/lib/guard/commands/pause_spec.rb000066400000000000000000000010161450001123100217450ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/pause" RSpec.describe Guard::Commands::Pause do class FakePry < Pry::Command def self.output; end end before do allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("pause") do |&block| FakePry.instance_eval(&block) end described_class.import end it "tells Guard to pause" do expect(::Guard).to receive(:async_queue_add).with([:guard_pause]) FakePry.process end end guard-2.18.1/spec/lib/guard/commands/reload_spec.rb000066400000000000000000000045521450001123100221060ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/reload" require "guard/internals/session" require "guard/internals/state" RSpec.describe Guard::Commands::Reload do let(:output) { instance_double(Pry::Output) } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } let(:foo_group) { instance_double(Guard::Group) } let(:bar_guard) { instance_double(Guard::Plugin) } class FakePry < Pry::Command def self.output; end end before do allow(session).to receive(:convert_scope).with(given_scope) .and_return(converted_scope) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("reload") do |&block| FakePry.instance_eval(&block) end described_class.import end context "without scope" do let(:given_scope) { [] } let(:converted_scope) { [{ groups: [], plugins: [] }, []] } it "triggers the :reload action" do expect(Guard).to receive(:async_queue_add) .with([:guard_reload, { groups: [], plugins: [] }]) FakePry.process end end context "with a valid Guard group scope" do let(:given_scope) { ["foo"] } let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] } it "triggers the :reload action with the given scope" do expect(Guard).to receive(:async_queue_add) .with([:guard_reload, { groups: [foo_group], plugins: [] }]) FakePry.process("foo") end end context "with a valid Guard plugin scope" do let(:given_scope) { ["bar"] } let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] } it "triggers the :reload action with the given scope" do expect(Guard).to receive(:async_queue_add) .with([:guard_reload, { plugins: [bar_guard], groups: [] }]) FakePry.process("bar") end end context "with an invalid scope" do let(:given_scope) { ["baz"] } let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] } it "does not trigger the action" do allow(output).to receive(:puts).with("Unknown scopes: baz") expect(Guard).to_not receive(:async_queue_add) FakePry.process("baz") end end end guard-2.18.1/spec/lib/guard/commands/scope_spec.rb000066400000000000000000000050131450001123100217420ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/scope" require "guard/internals/session" require "guard/internals/state" RSpec.describe Guard::Commands::Scope do let(:output) { instance_double(Pry::Output) } let(:state) { instance_double("Guard::Internals::State") } let(:scope) { instance_double("Guard::Internals::Scope") } let(:session) { instance_double("Guard::Internals::Session") } let(:foo_group) { instance_double(Guard::Group) } let(:bar_guard) { instance_double(Guard::PluginUtil) } class FakePry < Pry::Command def self.output; end end before do allow(session).to receive(:convert_scope).with(given_scope) .and_return(converted_scope) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("scope") do |&block| FakePry.instance_eval(&block) end allow(state).to receive(:scope).and_return(scope) allow(Guard).to receive(:state).and_return(state) described_class.import end context "without scope" do let(:given_scope) { [] } let(:converted_scope) { [{ groups: [], plugins: [] }, []] } it "does not call :scope= and shows usage" do expect(output).to receive(:puts).with("Usage: scope ") expect(scope).to_not receive(:from_interactor) FakePry.process end end context "with a valid Guard group scope" do let(:given_scope) { ["foo"] } let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] } it "sets up the scope with the given scope" do expect(scope).to receive(:from_interactor) .with(groups: [foo_group], plugins: []) FakePry.process("foo") end end context "with a valid Guard plugin scope" do let(:given_scope) { ["bar"] } let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] } it "runs the :scope= action with the given scope" do expect(scope).to receive(:from_interactor) .with(plugins: [bar_guard], groups: []) FakePry.process("bar") end end context "with an invalid scope" do let(:given_scope) { ["baz"] } let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] } it "does not change the scope and shows unknown scopes" do expect(output).to receive(:puts).with("Unknown scopes: baz") expect(scope).to_not receive(:from_interactor) FakePry.process("baz") end end end guard-2.18.1/spec/lib/guard/commands/show_spec.rb000066400000000000000000000012021450001123100216050ustar00rootroot00000000000000# frozen_string_literal: true require "guard/commands/show" # TODO: we only need the async queue require "guard" RSpec.describe Guard::Commands::Show do let(:output) { instance_double(Pry::Output) } class FakePry < Pry::Command def self.output; end end before do allow(FakePry).to receive(:output).and_return(output) allow(Pry::Commands).to receive(:create_command).with("show") do |&block| FakePry.instance_eval(&block) end described_class.import end it "tells Guard to output DSL description" do expect(::Guard).to receive(:async_queue_add).with([:guard_show]) FakePry.process end end guard-2.18.1/spec/lib/guard/config_spec.rb000066400000000000000000000011421450001123100202740ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" RSpec.describe Guard::Config, exclude_stubs: [:Nenv] do it { is_expected.to respond_to(:strict?) } it { is_expected.to respond_to(:silence_deprecations?) } describe ".strict?" do before do allow(subject).to receive(:strict?).and_return(result) end context "when GUARD_STRICT is set to a 'true' value" do let(:result) { true } it { is_expected.to be_strict } end context "when GUARD_STRICT is set to a 'false' value" do let(:result) { false } it { is_expected.to_not be_strict } end end end guard-2.18.1/spec/lib/guard/deprecated/000077500000000000000000000000001450001123100175725ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/deprecated/dsl_spec.rb000066400000000000000000000031331450001123100217130ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" unless Guard::Config.new.strict? # Require listen now, so the requiring below doesn't use File methods require "listen" require "guard/deprecated/dsl" require "guard/ui" require "guard/config" RSpec.describe Guard::Deprecated::Dsl do subject do module TestModule; end.tap { |mod| described_class.add_deprecated(mod) } end describe ".evaluate_guardfile" do before { stub_user_guard_rb } before { stub_guardfile(" ") } before { stub_user_guardfile } before { stub_user_project_guardfile } let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } before do # TODO: this is a workaround for a bad require loop allow_any_instance_of(Guard::Config).to receive(:strict?) .and_return(false) require "guard/guardfile/evaluator" allow(Guard::Guardfile::Evaluator).to receive(:new) .and_return(evaluator) allow(evaluator).to receive(:evaluate_guardfile) allow(Guard::UI).to receive(:deprecation) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Dsl::ClassMethods::EVALUATE_GUARDFILE) subject.evaluate_guardfile end it "delegates to Guard::Guardfile::Generator" do expect(Guard::Guardfile::Evaluator).to receive(:new) .with(foo: "bar") { evaluator } expect(evaluator).to receive(:evaluate_guardfile) subject.evaluate_guardfile(foo: "bar") end end end end guard-2.18.1/spec/lib/guard/deprecated/evaluator_spec.rb000066400000000000000000000024331450001123100231350ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" unless Guard::Config.new.strict? require "guard/deprecated/evaluator" require "guard/internals/state" RSpec.describe Guard::Deprecated::Evaluator do subject do class TestClass def evaluate; end end described_class.add_deprecated(TestClass) TestClass.new end let(:state) { instance_double("Guard::Internals::State") } before do allow(Guard::UI).to receive(:deprecation) allow(Guard).to receive(:state).and_return(state) end describe "#evaluate_guardfile" do before do allow(subject).to receive(:evaluate) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Evaluator::EVALUATE_GUARDFILE) subject.evaluate_guardfile end it "calls the recommended method" do expect(subject).to receive(:evaluate) subject.evaluate_guardfile end end describe "#reevaluate_guardfile" do it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Evaluator::REEVALUATE_GUARDFILE) subject.reevaluate_guardfile end end end end guard-2.18.1/spec/lib/guard/deprecated/guard_spec.rb000066400000000000000000000300601450001123100222320ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" unless Guard::Config.new.strict? # require guard, to avoid circular require require "guard" # require "guard/deprecated/guard" require "guard/config" RSpec.describe Guard::Deprecated::Guard do let(:session) { instance_double("Guard::Internals::Session") } let(:state) { instance_double("Guard::Internals::State") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:scope) { instance_double("Guard::Internals::Scope") } subject do module TestModule def self.listener; end def self._pluginless_guardfile? false end end TestModule.tap { |mod| described_class.add_deprecated(mod) } end before do allow(session).to receive(:evaluator_options).and_return({}) allow(state).to receive(:scope).and_return(scope) allow(state).to receive(:session).and_return(session) allow(session).to receive(:plugins).and_return(plugins) allow(session).to receive(:groups).and_return(groups) allow(::Guard).to receive(:state).and_return(state) allow(Guard::UI).to receive(:deprecation) allow(plugins).to receive(:all) allow(groups).to receive(:all) end describe ".guards" do before do end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::GUARDS) subject.guards end it "delegates to Plugins" do expect(plugins).to receive(:all).with(group: "backend") subject.guards(group: "backend") end end describe ".add_guard" do before { allow(plugins).to receive(:add).with("rspec", {}) } it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::ADD_GUARD) subject.add_guard("rspec") end it "delegates to Guard.plugins" do expect(subject).to receive(:add_plugin).with("rspec", group: "backend") subject.add_guard("rspec", group: "backend") end end describe ".get_guard_class" do let(:plugin_util) do instance_double("Guard::PluginUtil", plugin_class: true) end before do allow(Guard::PluginUtil).to receive(:new).with("rspec") .and_return(plugin_util) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::GET_GUARD_CLASS) subject.get_guard_class("rspec") end it "delegates to Guard::PluginUtil" do expect(plugin_util).to receive(:plugin_class) .with(fail_gracefully: false) subject.get_guard_class("rspec") end describe ":fail_gracefully" do it "pass it to get_guard_class" do expect(plugin_util).to receive(:plugin_class) .with(fail_gracefully: true) subject.get_guard_class("rspec", true) end end end describe ".locate_guard" do let(:plugin_util) do instance_double("Guard::PluginUtil", plugin_location: true) end before do allow(Guard::PluginUtil).to receive(:new) { plugin_util } end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::LOCATE_GUARD) subject.locate_guard("rspec") end it "delegates to Guard::PluginUtil" do expect(Guard::PluginUtil).to receive(:new).with("rspec") { plugin_util } expect(plugin_util).to receive(:plugin_location) subject.locate_guard("rspec") end end describe ".guard_gem_names" do before { allow(Guard::PluginUtil).to receive(:plugin_names) } it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::GUARD_GEM_NAMES) subject.guard_gem_names end it "delegates to Guard::PluginUtil" do expect(Guard::PluginUtil).to receive(:plugin_names) subject.guard_gem_names end end describe ".running" do it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::RUNNING) subject.running end end describe ".lock" do it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::LOCK) subject.lock end end describe ".listener=" do it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::LISTENER_ASSIGN) subject.listener = 123 end it "provides and alternative implementation" do subject.listener = 123 end end describe "reset_evaluator" do it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::RESET_EVALUATOR) subject.reset_evaluator({}) end end describe "evaluator" do before do allow(Guard::Guardfile::Evaluator).to receive(:new) .and_return(double("evaluator")) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::EVALUATOR) subject.evaluator end end describe "evaluate_guardfile" do let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } before do allow(::Guard::Guardfile::Evaluator).to receive(:new) .and_return(evaluator) allow(evaluator).to receive(:evaluate) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::EVALUATOR) subject.evaluate_guardfile end it "evaluates the guardfile" do expect(evaluator).to receive(:evaluate) subject.evaluate_guardfile end end describe "options" do before do allow(session).to receive(:clearing?) allow(session).to receive(:debug?) allow(session).to receive(:watchdirs) allow(session).to receive(:notify_options).and_return({}) allow(session).to receive(:interactor_name) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::OPTIONS) subject.options end describe ":clear" do before do allow(session).to receive(:clearing?).and_return(clearing) end context "when being set to true" do let(:clearing) { true } it "sets the clearing option accordingly" do expect(session).to receive(:clearing).with(true) subject.options[:clear] = true end end context "when being set to false" do let(:clearing) { false } it "sets the clearing option accordingly" do expect(session).to receive(:clearing).with(false) subject.options[:clear] = false end end context "when being read" do context "when not set" do let(:clearing) { false } it "provides an alternative implementation" do expect(subject.options).to include(clear: false) end end context "when set" do let(:clearing) { true } it "provides an alternative implementation" do expect(subject.options).to include(clear: true) end end end end # TODO: other options? end describe ".add_group" do before do allow(groups).to receive(:add) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::ADD_GROUP) subject.add_group(:foo) end it "adds a group" do group = instance_double("Guard::Group") expect(groups).to receive(:add).with(:foo, bar: 3).and_return(group) expect(subject.add_group(:foo, bar: 3)).to eq(group) end end describe ".add_plugin" do before do allow(plugins).to receive(:add) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::ADD_PLUGIN) subject.add_plugin(:foo) end it "adds a plugin" do plugin = instance_double("Guard::Plugin") expect(plugins).to receive(:add).with(:foo, bar: 3).and_return(plugin) expect(subject.add_plugin(:foo, bar: 3)).to be(plugin) end end describe ".group" do let(:array) { instance_double(Array) } before do allow(groups).to receive(:all).with(:foo).and_return(array) allow(array).to receive(:first) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::GROUP) subject.group(:foo) end it "provides a similar implementation" do group = instance_double("Guard::Group") expect(array).to receive(:first).and_return(group) expect(subject.group(:foo)).to be(group) end end describe ".plugin" do let(:array) { instance_double(Array) } let(:plugin) { instance_double("Guard::Plugin") } before do allow(plugins).to receive(:all).with(:foo).and_return(array) allow(array).to receive(:first).and_return(plugin) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::PLUGIN) subject.plugin(:foo) end it "provides a similar implementation" do expect(subject.plugin(:foo)).to be(plugin) end end describe ".groups" do let(:array) { instance_double(Array) } before do allow(groups).to receive(:all).with(:foo).and_return(array) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::GROUPS) subject.groups(:foo) end it "provides a similar implementation" do expect(subject.groups(:foo)).to be(array) end end describe ".plugins" do let(:array) { instance_double(Array) } before do allow(plugins).to receive(:all).with(:foo).and_return(array) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::PLUGINS) subject.plugins(:foo) end it "provides a similar implementation" do expect(subject.plugins(:foo)).to be(array) end end describe ".scope" do let(:hash) { instance_double(Hash) } before do allow(scope).to receive(:to_hash).and_return(hash) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::SCOPE) subject.scope end it "provides a similar implementation" do expect(subject.scope).to be(hash) end end describe ".scope=" do before do allow(scope).to receive(:from_interactor).with(foo: :bar) end it "show deprecation warning" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guard::ClassMethods::SCOPE_ASSIGN) subject.scope = { foo: :bar } end it "provides a similar implementation" do expect(scope).to receive(:from_interactor).with(foo: :bar) subject.scope = { foo: :bar } end end end end guard-2.18.1/spec/lib/guard/deprecated/guardfile_spec.rb000066400000000000000000000051621450001123100230770ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" unless Guard::Config.new.strict? require "guard/deprecated/guardfile" RSpec.describe Guard::Deprecated::Guardfile do subject do module TestModule; end.tap { |mod| described_class.add_deprecated(mod) } end let(:generator) { instance_double("Guard::Guardfile::Generator") } before do allow(Guard::UI).to receive(:deprecation) end describe ".create_guardfile" do before do allow(File).to receive(:exist?).with("Guardfile").and_return(false) template = Guard::Guardfile::Generator::GUARDFILE_TEMPLATE allow(FileUtils).to receive(:cp).with(template, "Guardfile") allow(Guard::Guardfile::Generator).to receive(:new) .and_return(generator) allow(generator).to receive(:create_guardfile) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guardfile::ClassMethods::CREATE_GUARDFILE) subject.create_guardfile end it "delegates to Guard::Guardfile::Generator" do expect(Guard::Guardfile::Generator).to receive(:new) .with(foo: "bar") { generator } expect(generator).to receive(:create_guardfile) subject.create_guardfile(foo: "bar") end end describe ".initialize_template" do before do expect(Guard::Guardfile::Generator).to receive(:new) do generator end allow(generator).to receive(:initialize_template) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Guardfile::ClassMethods::INITIALIZE_TEMPLATE) subject.initialize_template("rspec") end it "delegates to Guard::Guardfile::Generator" do expect(generator).to receive(:initialize_template).with("rspec") subject.initialize_template("rspec") end end describe ".initialize_all_templates" do before do expect(Guard::Guardfile::Generator).to receive(:new) do generator end allow(generator).to receive(:initialize_all_templates) end it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(described_class::ClassMethods::INITIALIZE_ALL_TEMPLATES) subject.initialize_all_templates end it "delegates to Guard::Guardfile::Generator" do expect(generator).to receive(:initialize_all_templates) subject.initialize_all_templates end end end end guard-2.18.1/spec/lib/guard/deprecated/watcher_spec.rb000066400000000000000000000030541450001123100225700ustar00rootroot00000000000000# frozen_string_literal: true require "guard/config" unless Guard::Config.new.strict? require "guard/deprecated/watcher" require "guard/guardfile/evaluator" RSpec.describe Guard::Deprecated::Watcher do let(:session) { instance_double("Guard::Internals::Session") } subject do module TestModule; end.tap { |mod| described_class.add_deprecated(mod) } end let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } let(:options) { { guardfile: "foo" } } let(:state) { instance_double("Guard::Internals::State") } before do allow(session).to receive(:evaluator_options).and_return(options) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(evaluator).to receive(:guardfile_path) .and_return(File.expand_path("foo")) allow(::Guard::Guardfile::Evaluator).to receive(:new).with(options) .and_return(evaluator) allow(Guard::UI).to receive(:deprecation) end describe ".match_guardfile?" do it "displays a deprecation warning to the user" do expect(Guard::UI).to receive(:deprecation) .with(Guard::Deprecated::Watcher::ClassMethods::MATCH_GUARDFILE) files = %w[foo bar] subject.match_guardfile?(files) end it "matches against current guardfile" do expect(subject.match_guardfile?(%w[foo bar])).to be(true) expect(subject.match_guardfile?(%w[bar])).to be(false) end end end end guard-2.18.1/spec/lib/guard/dsl_describer_spec.rb000066400000000000000000000123241450001123100216370ustar00rootroot00000000000000# frozen_string_literal: true require "guard/plugin" require "guard/dsl_describer" require "formatador" RSpec.describe Guard::DslDescriber do let(:interactor) { instance_double(Guard::Interactor) } let(:env) { double("ENV") } let(:session) { instance_double("Guard::Internals::Session") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:state) { instance_double("Guard::Internals::State") } before do allow(session).to receive(:groups).and_return(groups) allow(session).to receive(:plugins).and_return(plugins) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(env).to receive(:[]).with("GUARD_NOTIFY_PID") allow(env).to receive(:[]).with("GUARD_NOTIFY") allow(env).to receive(:[]).with("GUARD_NOTIFIERS") allow(env).to receive(:[]=).with("GUARD_NOTIFIERS", anything) allow(Guard::Notifier).to receive(:turn_on) stub_const "Guard::Test", class_double("Guard::Plugin") stub_const "Guard::Another", class_double("Guard::Plugin") # TODO: Reenable rubocop and refactor to +'' one 2.2 compatibility is dropped @output = String.new # Strip escape sequences allow(STDOUT).to receive(:tty?).and_return(false) # Capture formatador output Thread.current[:formatador] = Formatador.new allow(Thread.current[:formatador]).to receive(:print) do |msg| @output << msg end end describe "#list" do let(:result) do <<-OUTPUT +---------+-----------+ | Plugin | Guardfile | +---------+-----------+ | Another | ✔ | | Even | ✘ | | More | ✘ | | Test | ✔ | +---------+-----------+ OUTPUT end let(:another) { instance_double("Guard::Plugin", title: "Another") } let(:test) { instance_double("Guard::Plugin", title: "Test") } before do allow(plugins).to receive(:all).with("another").and_return([another]) allow(plugins).to receive(:all).with("test").and_return([test]) allow(plugins).to receive(:all).with("even").and_return([]) allow(plugins).to receive(:all).with("more").and_return([]) allow(Guard::PluginUtil).to receive(:plugin_names) do %w[test another even more] end end it "lists the available Guards declared as strings or symbols" do subject.list expect(@output).to eq result end end describe ".show" do let(:result) do <<-OUTPUT +---------+---------+--------+-------+ | Group | Plugin | Option | Value | +---------+---------+--------+-------+ | Default | Test | a | :b | | | | c | :d | +---------+---------+--------+-------+ | A | Test | x | 1 | | | | y | 2 | +---------+---------+--------+-------+ | B | Another | | | +---------+---------+--------+-------+ OUTPUT end before do allow(groups).to receive(:all).and_return [ instance_double("Guard::Group", name: :default, title: "Default"), instance_double("Guard::Group", name: :a, title: "A"), instance_double("Guard::Group", name: :b, title: "B") ] allow(plugins).to receive(:all).with(group: :default) do options = { a: :b, c: :d } [instance_double("Guard::Plugin", title: "Test", options: options)] end allow(plugins).to receive(:all).with(group: :a) do options = { x: 1, y: 2 } [instance_double("Guard::Plugin", title: "Test", options: options)] end allow(plugins).to receive(:all).with(group: :b).and_return [ instance_double("Guard::Plugin", title: "Another", options: []) ] end it "shows the Guards and their options" do subject.show expect(@output).to eq result end end describe ".notifiers" do let(:result) do <<-OUTPUT +----------------+-----------+------+--------+-------+ | Name | Available | Used | Option | Value | +----------------+-----------+------+--------+-------+ | gntp | ✔ | ✔ | sticky | true | +----------------+-----------+------+--------+-------+ | terminal_title | ✘ | ✘ | | | +----------------+-----------+------+--------+-------+ OUTPUT end before do allow(Guard::Notifier).to receive(:supported).and_return( gntp: ::Notiffany::Notifier::GNTP, terminal_title: ::Notiffany::Notifier::TerminalTitle ) allow(Guard::Notifier).to receive(:connect).once allow(Guard::Notifier).to receive(:detected) .and_return([{ name: :gntp, options: { sticky: true } }]) allow(Guard::Notifier).to receive(:disconnect).once end it "properly connects and disconnects" do expect(Guard::Notifier).to receive(:connect).once.ordered expect(::Guard::Notifier).to receive(:detected).once.ordered.and_return [ { name: :gntp, options: { sticky: true } } ] expect(Guard::Notifier).to receive(:disconnect).once.ordered subject.notifiers end it "shows the notifiers and their options" do subject.notifiers expect(@output).to eq result end end end guard-2.18.1/spec/lib/guard/dsl_reader_spec.rb000066400000000000000000000031241450001123100211350ustar00rootroot00000000000000# frozen_string_literal: true require "guard/dsl_reader" RSpec.describe Guard::DslReader, exclude_stubs: [Guard::Dsl] do methods = %w[ initialize guard notification interactor group watch callback ignore ignore! logger scope directories clearing ].map(&:to_sym) methods.each do |meth| describe "\##{meth} signature" do it "matches base signature" do expected = Guard::Dsl.instance_method(meth).arity expect(subject.method(meth).arity).to eq(expected) end end end describe "guard" do context "when it is a String" do let(:name) { "foo" } it "works without errors" do expect { subject.guard(name, bar: :baz) }.to_not raise_error end it "reports the name as a String" do subject.guard("foo", bar: :baz) expect(subject.plugin_names).to eq(%w[foo]) end end context "when it is a Symbol" do let(:name) { :foo } it "works without errors" do expect { subject.guard(name, bar: :baz) }.to_not raise_error end it "reports the name as a String" do subject.guard(name, bar: :baz) expect(subject.plugin_names).to eq(%w[foo]) end end end describe "plugin_names" do it "returns encountered names" do subject.guard("foo", bar: :baz) subject.guard("bar", bar: :baz) subject.guard("baz", bar: :baz) expect(subject.plugin_names).to eq(%w[foo bar baz]) end end describe "notification" do it "handles arguments without errors" do expect { subject.notification(:off) }.to_not raise_error end end end guard-2.18.1/spec/lib/guard/dsl_spec.rb000066400000000000000000000460471450001123100176260ustar00rootroot00000000000000# frozen_string_literal: true require "guard/plugin" require "guard/dsl" RSpec.describe Guard::Dsl do let(:ui_config) { instance_double("Guard::UI::Config") } let(:guardfile_evaluator) { instance_double(Guard::Guardfile::Evaluator) } let(:interactor) { instance_double(Guard::Interactor) } let(:listener) { instance_double("Listen::Listener") } let(:session) { instance_double("Guard::Internals::Session") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:state) { instance_double("Guard::Internals::State") } let(:scope) { instance_double("Guard::Internals::Scope") } let(:evaluator) do proc do |contents| Guard::Dsl.new.evaluate(contents, "", 1) end end before do stub_user_guard_rb stub_const "Guard::Foo", instance_double(Guard::Plugin) stub_const "Guard::Bar", instance_double(Guard::Plugin) stub_const "Guard::Baz", instance_double(Guard::Plugin) allow(Guard::Notifier).to receive(:turn_on) allow(Guard::Interactor).to receive(:new).and_return(interactor) allow(state).to receive(:scope).and_return(scope) allow(session).to receive(:plugins).and_return(plugins) allow(session).to receive(:groups).and_return(groups) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) # For backtrace cleanup allow(ENV).to receive(:[]).with("GEM_HOME").and_call_original allow(ENV).to receive(:[]).with("GEM_PATH").and_call_original allow(Guard::UI::Config).to receive(:new).and_return(ui_config) end describe "#ignore" do context "with ignore regexps" do let(:contents) { "ignore %r{^foo}, /bar/" } it "adds ignored regexps to the listener" do expect(session).to receive(:guardfile_ignore=).with([/^foo/, /bar/]) evaluator.call(contents) end end context "with multiple ignore calls" do let(:contents) { "ignore(/foo/); ignore(/bar/)" } it "adds all ignored regexps to the listener" do expect(session).to receive(:guardfile_ignore=).with([/foo/]).once expect(session).to receive(:guardfile_ignore=).with([/bar/]).once evaluator.call(contents) end end end describe "#ignore!" do context "when ignoring only foo* and *bar*" do let(:contents) { "ignore! %r{^foo}, /bar/" } it "replaces listener regexps" do expect(session).to receive(:guardfile_ignore_bang=) .with([[/^foo/, /bar/]]) evaluator.call(contents) end end context "when ignoring *.txt and *.zip and ignoring! only foo*" do let(:contents) { "ignore! %r{.txt$}, /.*\\.zip/\n ignore! %r{^foo}" } it "replaces listener ignores, but keeps ignore! ignores" do allow(session).to receive(:guardfile_ignore_bang=) .with([[/.txt$/, /.*\.zip/]]) expect(session).to receive(:guardfile_ignore_bang=) .with([[/.txt$/, /.*\.zip/], [/^foo/]]) evaluator.call(contents) end end end # TODO: remove this hack (after deprecating filter) def method_for(klass, meth) klass.instance_method(meth) end # TODO: deprecated #filter describe "#filter alias method" do subject { method_for(described_class, :filter) } it { is_expected.to eq(method_for(described_class, :ignore)) } end # TODO: deprecated #filter describe "#filter! alias method" do subject { method_for(described_class, :filter!) } it { is_expected.to eq(method_for(described_class, :ignore!)) } end describe "#notification" do context "when notification" do let(:contents) { "notification :growl" } it "adds a notification to the notifier" do expect(session).to receive(:guardfile_notification=).with(growl: {}) evaluator.call(contents) end end context "with multiple notifications" do let(:contents) do "notification :growl\nnotification :ruby_gntp, host: '192.168.1.5'" end it "adds multiple notifiers" do expect(session).to receive(:guardfile_notification=).with(growl: {}) expect(session).to receive(:guardfile_notification=).with( ruby_gntp: { host: "192.168.1.5" } ) evaluator.call(contents) end end end describe "#interactor" do context "with interactor :off" do let(:contents) { "interactor :off" } it "disables the interactions with :off" do expect(Guard::Interactor).to receive(:enabled=).with(false) evaluator.call(contents) end end context "with interactor options" do let(:contents) { "interactor option1: 'a', option2: 123" } it "passes the options to the interactor" do expect(Guard::Interactor).to receive(:options=) .with(option1: "a", option2: 123) evaluator.call(contents) end end end describe "#group" do context "no plugins in group" do let(:contents) { "group :w" } it "displays an error" do expect(::Guard::UI).to receive(:error) .with("No Guard plugins found in the group 'w',"\ " please add at least one.") evaluator.call(contents) end end context "group named :all" do let(:contents) { "group :all" } it "raises an error" do expect { evaluator.call(contents) } .to raise_error( Guard::Dsl::Error, /'all' is not an allowed group name!/ ) end end context 'group named "all"' do let(:contents) { "group 'all'" } it "raises an error" do expect { evaluator.call(contents) } .to raise_error( Guard::Dsl::Error, /'all' is not an allowed group name!/ ) end end context "with a valid guardfile" do let(:contents) { valid_guardfile_string } it "evaluates all groups" do expect(groups).to receive(:add).with(:w, {}) expect(groups).to receive(:add).with(:y, {}) expect(groups).to receive(:add).with(:x, halt_on_fail: true) expect(plugins).to receive(:add) .with(:pow, watchers: [], callbacks: [], group: :default) expect(plugins).to receive(:add) .with(:test, watchers: [], callbacks: [], group: :w) expect(plugins).to receive(:add) .with(:rspec, watchers: [], callbacks: [], group: :x).twice expect(plugins).to receive(:add) .with(:less, watchers: [], callbacks: [], group: :y) expect(session).to receive(:guardfile_notification=).with(growl: {}) evaluator.call(contents) end end context "with multiple names" do let(:contents) { "group :foo, :bar do; end" } it "adds all given groups" do expect(groups).to receive(:add).with(:foo, {}) expect(groups).to receive(:add).with(:bar, {}) evaluator.call(contents) end end end describe "#guard" do context "with single-quoted name" do let(:contents) { "guard 'test'" } it "loads a guard specified as a quoted string from the DSL" do expect(plugins).to receive(:add) .with("test", watchers: [], callbacks: [], group: :default) evaluator.call(contents) end end context "with double-quoted name" do let(:contents) { 'guard "test"' } it "loads a guard specified as a double quoted string from the DSL" do expect(plugins).to receive(:add) .with("test", watchers: [], callbacks: [], group: :default) evaluator.call(contents) end end context "with symbol for name" do let(:contents) { "guard :test" } it "loads a guard specified as a symbol from the DSL" do expect(plugins).to receive(:add) .with(:test, watchers: [], callbacks: [], group: :default) evaluator.call(contents) end end context "with name as symbol in parens" do let(:contents) { "guard(:test)" } it "adds the plugin" do expect(plugins).to receive(:add) .with(:test, watchers: [], callbacks: [], group: :default) evaluator.call(contents) end end context "with options" do let(:contents) { "guard 'test', opt_a: 1, opt_b: 'fancy'" } it "passes options to plugin" do options = { watchers: [], callbacks: [], opt_a: 1, opt_b: "fancy", group: :default } expect(plugins).to receive(:add).with("test", options) evaluator.call(contents) end end context "with groups" do let(:contents) { "group :foo do; group :bar do; guard :test; end; end" } it "adds plugin with group info" do expect(groups).to receive(:add).with(:foo, {}) expect(groups).to receive(:add).with(:bar, {}) expect(plugins).to receive(:add) .with(:test, watchers: [], callbacks: [], group: :bar) evaluator.call(contents) end end context "with plugins in custom and default groups" do let(:contents) do "group :foo do; group :bar do; guard :test; end; end; guard :rspec" end it "assigns plugins to correct groups" do expect(groups).to receive(:add).with(:foo, {}) expect(groups).to receive(:add).with(:bar, {}) expect(plugins).to receive(:add) .with(:test, watchers: [], callbacks: [], group: :bar) expect(plugins).to receive(:add) .with(:rspec, watchers: [], callbacks: [], group: :default) evaluator.call(contents) end end end describe "#watch" do # TODO: this is testing too much context "with watchers" do let(:watcher_a) do instance_double("Guard::Watcher", pattern: "a", action: proc { "b" }) end let(:watcher_c) do instance_double("Guard::Watcher", pattern: "c", action: nil) end let(:contents) do ' guard :dummy do watch(\'a\') { \'b\' } watch(\'c\') end' end it "should receive watchers when specified" do call_params = { watchers: [anything, anything], callbacks: [], group: :default } expect(plugins).to receive(:add) .with(:dummy, call_params) do |_, options| expect(options[:watchers].size).to eq 2 expect(options[:watchers][0].pattern).to eq "a" expect(options[:watchers][0].action.call).to eq proc { "b" }.call expect(options[:watchers][1].pattern).to eq "c" expect(options[:watchers][1].action).to be_nil end allow(Guard::Watcher).to receive(:new).with("a", anything) .and_return(watcher_a) allow(Guard::Watcher).to receive(:new).with("c", nil) .and_return(watcher_c) evaluator.call(contents) end end context "with watch in main scope" do let(:contents) { "watch('a')" } let(:watcher) do instance_double("Guard::Watcher", pattern: "a", action: nil) end it "should create an implicit no-op guard when outside a guard block" do plugin_options = { watchers: [anything], callbacks: [], group: :default } expect(plugins).to receive(:add) .with(:plugin, plugin_options) do |_, options| expect(options[:watchers].size).to eq 1 expect(options[:watchers][0].pattern).to eq "a" expect(options[:watchers][0].action).to be_nil end allow(Guard::Watcher).to receive(:new).with("a", nil) .and_return(watcher) evaluator.call(contents) end end end describe "#callback" do context "with " do let(:contents) do ' guard :rspec do callback(:start_end) do |plugin, event, args| "#{plugin.title} executed \'#{event}\' hook with #{args}!" end callback(MyCustomCallback, [:start_begin, :run_all_begin]) end' end it "creates callbacks for the guard" do class MyCustomCallback def self.call(_plugin, _event, _args) # do nothing end end params = { watchers: [], callbacks: [anything, anything], group: :default } expect(plugins).to receive(:add).with(:rspec, params) do |_, opt| # TODO: this whole block is too verbose, tests too many things at # once and needs refactoring expect(opt[:callbacks].size).to eq 2 callback0 = opt[:callbacks][0] expect(callback0[:events]).to eq :start_end plugin = instance_double("Guard::Plugin", title: "RSpec") result = callback0[:listener].call(plugin, :start_end, "foo") expect(result).to eq "RSpec executed 'start_end' hook"\ " with foo!" callback1 = opt[:callbacks][1] expect(callback1[:events]).to eq %i[start_begin run_all_begin] expect(callback1[:listener]).to eq MyCustomCallback end evaluator.call(contents) end end context "without a guard block" do let(:contents) do ' callback(:start_end) do |plugin, event, args| "#{plugin} executed \'#{event}\' hook with #{args}!" end callback(MyCustomCallback, [:start_begin, :run_all_begin])' end it "fails" do expect { evaluator.call(contents) }.to raise_error(/guard block/i) end end end describe "#logger" do before do allow(Guard::UI).to receive(:options).and_return({}) allow(Guard::UI).to receive(:options=) end describe "options" do let(:contents) { "" } before do evaluator.call(contents) end subject { Guard::UI } context "with logger level :error" do let(:contents) { "logger level: :error" } it { is_expected.to have_received(:options=).with(level: :error) } end context "with logger level 'error'" do let(:contents) { "logger level: 'error'" } it { is_expected.to have_received(:options=).with(level: :error) } end context "with logger template" do let(:contents) { "logger template: ':message - :severity'" } it do is_expected.to have_received(:options=) .with(template: ":message - :severity") end end context "with a logger time format" do let(:contents) { "logger time_format: '%Y'" } it do is_expected.to have_received(:options=).with(time_format: "%Y") end end context "with a logger only filter from a symbol" do let(:contents) { "logger only: :cucumber" } it { is_expected.to have_received(:options=).with(only: /cucumber/i) } end context "with logger only filter from a string" do let(:contents) { "logger only: 'jasmine'" } it { is_expected.to have_received(:options=).with(only: /jasmine/i) } end context "with logger only filter from an array of symbols and string" do let(:contents) { "logger only: [:rspec, 'cucumber']" } it do is_expected.to have_received(:options=).with(only: /rspec|cucumber/i) end end context "with logger except filter from a symbol" do let(:contents) { "logger except: :jasmine" } it { is_expected.to have_received(:options=).with(except: /jasmine/i) } end context "with logger except filter from a string" do let(:contents) { "logger except: 'jasmine'" } it { is_expected.to have_received(:options=).with(except: /jasmine/i) } end context "with logger except filter from array of symbols and string" do let(:contents) { "logger except: [:rspec, 'cucumber', :jasmine]" } it do is_expected.to have_received(:options=) .with(except: /rspec|cucumber|jasmine/i) end end end context "with invalid options" do context "for the log level" do let(:contents) { "logger level: :baz" } it "shows a warning" do expect(Guard::UI).to receive(:warning) .with "Invalid log level `baz` ignored."\ " Please use either :debug, :info, :warn or :error." evaluator.call(contents) end it "does not set the invalid value" do expect(Guard::UI).to receive(:options=).with({}) evaluator.call(contents) end end context "when having both the :only and :except options" do let(:contents) { "logger only: :jasmine, except: :rspec" } it "shows a warning" do expect(Guard::UI).to receive(:warning) .with "You cannot specify the logger options"\ " :only and :except at the same time." evaluator.call(contents) end it "removes the options" do expect(Guard::UI).to receive(:options=).with({}) evaluator.call(contents) end end end end describe "#scope" do context "with any parameters" do let(:contents) { "scope plugins: [:foo, :bar]" } it "sets the guardfile's default scope" do expect(session).to receive(:guardfile_scope).with(plugins: %i[foo bar]) evaluator.call(contents) end end end describe "#directories" do context "with valid directories" do let(:contents) { "directories %w(foo bar)" } before do allow(Dir).to receive(:exist?).with("foo").and_return(true) allow(Dir).to receive(:exist?).with("bar").and_return(true) end it "sets the watchdirs to given values" do expect(session).to receive(:watchdirs=).with(%w[foo bar]) evaluator.call(contents) end end context "with no parameters" do let(:contents) { "directories []" } it "sets the watchdirs to empty" do expect(session).to receive(:watchdirs=).with([]) evaluator.call(contents) end end context "with non-existing directory" do let(:contents) { "directories ['foo']" } before do allow(Dir).to receive(:exist?).with("foo").and_return(false) end it "fails with an error" do expect(session).to_not receive(:watchdirs=) expect do evaluator.call(contents) end.to raise_error(Guard::Dsl::Error, /Directory "foo" does not exist!/) end end end describe "#clear" do context "with clear :off" do let(:contents) { "clearing :off" } it "disables clearing the screen after every task" do expect(session).to receive(:clearing).with(false) evaluator.call(contents) end end context "with clear :on" do let(:contents) { "clearing :on" } it "enabled clearing the screen after every task" do expect(session).to receive(:clearing).with(true) evaluator.call(contents) end end end private def valid_guardfile_string ' notification :growl guard :pow group :w do guard :test end group :x, halt_on_fail: true do guard :rspec guard :rspec end group :y do guard :less end ' end end guard-2.18.1/spec/lib/guard/group_spec.rb000066400000000000000000000014061450001123100201660ustar00rootroot00000000000000# frozen_string_literal: true require "guard/group" RSpec.describe Guard::Group do subject { described_class.new(name, options) } let(:name) { :foo } let(:options) { {} } describe "#name" do specify { expect(subject.name).to eq :foo } context "when initialized from a string" do let(:name) { "foo" } specify { expect(subject.name).to eq :foo } end end describe "#options" do context "when provided" do let(:options) { { halt_on_fail: true } } specify { expect(subject.options).to eq options } end end describe "#title" do specify { expect(subject.title).to eq "Foo" } end describe "#to_s" do specify do expect(subject.to_s).to eq "#" end end end guard-2.18.1/spec/lib/guard/guardfile/000077500000000000000000000000001450001123100174345ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/guardfile/evaluator_spec.rb000066400000000000000000000154621450001123100230050ustar00rootroot00000000000000# frozen_string_literal: true require "guard/guardfile/evaluator" # TODO: shouldn't be necessary require "guard" RSpec.describe Guard::Guardfile::Evaluator do let(:options) { {} } subject { described_class.new(options) } let!(:local_guardfile) { Pathname("Guardfile").to_s } let!(:home_guardfile) { Pathname("~/.Guardfile").expand_path.to_s } let!(:home_config) { Pathname("~/.guard.rb").expand_path.to_s } let(:valid_guardfile_string) { "group :foo; do guard :bar; end; end; " } let(:dsl) { instance_double("Guard::Dsl") } let(:rel_guardfile) do Pathname("../relative_path_to_Guardfile").expand_path.to_s end before do allow(Guard::Interactor).to receive(:new).with(false) allow(Guard::Dsl).to receive(:new).and_return(dsl) allow(dsl).to receive(:instance_eval) stub_pathname end describe ".evaluate" do describe "error cases" do context "with an invalid Guardfile" do let(:options) { { contents: "guard :foo Bad Guardfile" } } it "displays an error message and raises original exception" do stub_user_guard_rb allow(dsl).to receive(:evaluate) .and_raise(Guard::Dsl::Error, "Invalid Guardfile, original error is:") expect { subject.evaluate }.to raise_error(Guard::Dsl::Error) end end context "with no Guardfile at all" do it "displays an error message and exits" do stub_guardfile_rb stub_guardfile stub_user_guardfile stub_user_project_guardfile expect { subject.evaluate } .to raise_error(Guard::Guardfile::Evaluator::NoGuardfileError) end end context "with Guardfile as guardfile.rb" do it "evalutates guardfile.rb" do stub_guardfile_rb("guard :bar") allow(dsl).to receive(:evaluate).with("guard :bar", "", 1) end end context "with a problem reading a Guardfile" do let(:path) { File.expand_path("Guardfile") } before do stub_user_project_guardfile stub_guardfile_rb stub_guardfile(" ") do fail Errno::EACCES.new("permission error") end end it "displays an error message and exits" do expect(Guard::UI).to receive(:error).with(/^Error reading file/) expect { subject.evaluate }.to raise_error(SystemExit) end end context "with empty Guardfile content" do let(:options) { { contents: "" } } it "displays an error message about no plugins" do stub_user_guard_rb stub_guardfile(" ") allow(dsl).to receive(:evaluate).with("", "", 1) expect { subject.evaluate } .to raise_error(Guard::Guardfile::Evaluator::NoPluginsError) end end context "when provided :contents is nil" do before do # Anything stub_guardfile("guard :foo") stub_guardfile_rb stub_user_guard_rb stub_user_project_guardfile stub_user_guardfile end it "does not raise error and skip it" do allow(dsl).to receive(:evaluate).with("guard :foo", anything, 1) expect(Guard::UI).to_not receive(:error) expect do described_class.new(contents: nil).evaluate end.to_not raise_error end end context "with a non-existing Guardfile given" do let(:non_existing_path) { "/non/existing/path/to/Guardfile" } let(:options) { { guardfile: non_existing_path } } before do stub_file(non_existing_path) end it "raises error" do expect { subject.evaluate } .to raise_error(Guard::Guardfile::Evaluator::NoCustomGuardfile) end end end describe "selection of the Guardfile data contents" do context "with a valid :contents option" do before do stub_user_guard_rb allow(dsl).to receive(:evaluate) end context "with inline content and other Guardfiles available" do let(:inline_code) { "guard :foo" } let(:options) do { contents: inline_code, guardfile: "/abc/Guardfile" } end before do stub_file("/abc/Guardfile", "guard :bar") stub_guardfile_rb("guard :baz") stub_guardfile("guard :baz") stub_user_guardfile("guard :buz") end it "gives ultimate precedence to inline content" do expect(dsl).to receive(:evaluate).with(inline_code, "", 1) subject.evaluate end end end context "with the :guardfile option" do let(:options) { { guardfile: "../relative_path_to_Guardfile" } } before do stub_file(File.expand_path("../relative_path_to_Guardfile"), valid_guardfile_string) allow(dsl).to receive(:evaluate) .with(valid_guardfile_string, anything, 1) end end end end describe "#inline?" do before do allow(dsl).to receive(:evaluate) stub_guardfile("guard :bar") stub_guardfile_rb stub_user_guard_rb subject.evaluate end context "when guardfile_contents is provided" do let(:options) { { guardfile_contents: "guard :foo" } } it { is_expected.to be_inline } end context "when contents is provided" do let(:options) { { contents: "guard :foo" } } it { is_expected.to be_inline } end context "when no content is provided" do let(:options) { {} } it { is_expected.to_not be_inline } end end describe ".guardfile_include?" do subject do evaluator = described_class.new(options) evaluator.evaluate evaluator end let(:dsl_reader) { instance_double(Guard::DslReader) } before do allow(dsl).to receive(:evaluate) allow(Guard::DslReader).to receive(:new).and_return(dsl_reader) allow(dsl_reader).to receive(:evaluate) stub_user_guard_rb end context "when plugin is present" do let(:options) { { contents: 'guard "test" {watch("c")}' } } it "returns true" do allow(dsl_reader) .to receive(:evaluate).with('guard "test" {watch("c")}', "", 1) allow(dsl_reader).to receive(:plugin_names).and_return(["test"]) expect(subject).to be_guardfile_include("test") end end context "when plugin is not present" do let(:options) { { contents: 'guard "other" {watch("c")}' } } it "returns false" do allow(dsl_reader) .to receive(:evaluate).with('guard "test" {watch("c")}', "", 1) allow(dsl_reader).to receive(:plugin_names).and_return(["other"]) expect(subject).to_not be_guardfile_include("test") end end end end guard-2.18.1/spec/lib/guard/guardfile/generator_spec.rb000066400000000000000000000102211450001123100227550ustar00rootroot00000000000000# frozen_string_literal: true require "guard/guardfile/generator" RSpec.describe Guard::Guardfile::Generator do let(:plugin_util) { instance_double("Guard::PluginUtil") } let(:guardfile_generator) { described_class.new } before do stub_pathname end it "has a valid Guardfile template" do allow(File).to receive(:exist?) .with(described_class::GUARDFILE_TEMPLATE).and_call_original expect(File.exist?(described_class::GUARDFILE_TEMPLATE)).to be_truthy end describe "#create_guardfile" do context "with an existing Guardfile" do before do stub_guardfile("foo") end it "does not copy the Guardfile template or notify the user" do expect(::Guard::UI).to_not receive(:info) expect(FileUtils).to_not receive(:cp) begin subject.create_guardfile rescue SystemExit end end it "does not display information" do expect(::Guard::UI).to_not receive(:info) begin subject.create_guardfile rescue SystemExit end end it "displays an error message" do expect(::Guard::UI).to receive(:error) .with("Guardfile already exists at Guardfile") begin subject.create_guardfile rescue SystemExit end end it "aborts" do expect { subject.create_guardfile }.to raise_error(SystemExit) end end context "without an existing Guardfile" do before do stub_guardfile allow(FileUtils).to receive(:cp) end it "does not display any kind of error or abort" do expect(::Guard::UI).to_not receive(:error) expect(described_class).to_not receive(:abort) described_class.new.create_guardfile end it "copies the Guardfile template and notifies the user" do expect(::Guard::UI).to receive(:info) expect(FileUtils).to receive(:cp) described_class.new.create_guardfile end end end describe "#initialize_template" do before do @guardfile = stub_guardfile end context "with an installed Guard implementation" do before do expect(Guard::PluginUtil).to receive(:new) { plugin_util } expect(plugin_util).to receive(:plugin_class) do double("Guard::Foo").as_null_object end end it "initializes the Guard" do expect(plugin_util).to receive(:add_to_guardfile) described_class.new.initialize_template("foo") end end context "with a user defined template" do let(:template) { File.join(described_class::HOME_TEMPLATES, "/bar") } let(:template_content) { "Template content" } before do stub_file("bar") stub_file(File.expand_path("~/.guard/templates/bar"), "Template content") end it "copies the Guardfile template and initializes the Guard" do expect(@guardfile).to receive(:binwrite) .with("\n#{template_content}\n", open_args: ["a"]) expect(plugin_util).to receive(:plugin_class).with(fail_gracefully: true) expect(Guard::PluginUtil).to receive(:new).with("bar") .and_return(plugin_util) described_class.new.initialize_template("bar") end end context "when the passed guard can't be found" do before do expect(::Guard::PluginUtil).to receive(:new) { plugin_util } allow(plugin_util).to receive(:plugin_class) { nil } stub_file("foo") stub_file(File.expand_path("~/.guard/templates/foo")) end it "notifies the user about the problem" do expect { described_class.new.initialize_template("foo") } .to raise_error(Guard::Guardfile::Generator::Error) end end end describe "#initialize_all_templates" do let(:plugins) { %w[rspec spork phpunit] } before do expect(::Guard::PluginUtil).to receive(:plugin_names) { plugins } end it "calls Guard.initialize_template on all installed plugins" do plugins.each do |g| expect(subject).to receive(:initialize_template).with(g) end subject.initialize_all_templates end end end guard-2.18.1/spec/lib/guard/interactor_spec.rb000066400000000000000000000104711450001123100212060ustar00rootroot00000000000000# frozen_string_literal: true require "guard/interactor" # TODO: this shouldn't be necessary require "guard/jobs/pry_wrapper" require "guard/jobs/sleep" RSpec.describe Guard::Interactor do let!(:pry_interactor) { instance_double("Guard::Jobs::PryWrapper") } let!(:sleep_interactor) { instance_double("Guard::Jobs::Sleep") } let(:pry_class) { class_double("Guard::Jobs::PryWrapper") } let(:sleep_class) { class_double("Guard::Jobs::Sleep") } before do stub_const("Guard::Jobs::PryWrapper", pry_class) stub_const("Guard::Jobs::Sleep", sleep_class) allow(Guard::Jobs::PryWrapper).to receive(:new).and_return(pry_interactor) allow(Guard::Jobs::Sleep).to receive(:new).and_return(sleep_interactor) @interactor_enabled = described_class.enabled? described_class.enabled = nil end after { described_class.enabled = @interactor_enabled } describe ".enabled & .enabled=" do it "returns true by default" do expect(described_class).to be_enabled end context "interactor not enabled" do before { described_class.enabled = false } it "returns false" do expect(described_class).to_not be_enabled end end end describe ".options & .options=" do before { described_class.options = nil } it "returns {} by default" do expect(described_class.options).to eq({}) end context "options set to { foo: :bar }" do before { described_class.options = { foo: :bar } } it "returns { foo: :bar }" do expect(described_class.options).to eq(foo: :bar) end end end context "when enabled" do before { described_class.enabled = true } describe "#foreground" do it "starts Pry" do expect(pry_interactor).to receive(:foreground) subject.foreground end end describe "#background" do it "hides Pry" do expect(pry_interactor).to receive(:background) subject.background end end describe "#handle_interrupt" do it "interrupts Pry" do expect(pry_interactor).to receive(:handle_interrupt) subject.handle_interrupt end end end context "when disabled" do before { described_class.enabled = false } describe "#foreground" do it "sleeps" do expect(sleep_interactor).to receive(:foreground) subject.foreground end end describe "#background" do it "wakes up from sleep" do expect(sleep_interactor).to receive(:background) subject.background end end describe "#handle_interrupt" do it "interrupts sleep" do expect(sleep_interactor).to receive(:handle_interrupt) subject.handle_interrupt end end end describe "job selection" do subject do Guard::Interactor.new(no_interactions) Guard::Interactor end before do Guard::Interactor.enabled = dsl_enabled end context "when enabled from the DSL" do let(:dsl_enabled) { true } context "when enabled from the commandline" do let(:no_interactions) { false } it "uses only pry" do expect(pry_class).to receive(:new) expect(sleep_class).to_not receive(:new) subject end it { is_expected.to be_enabled } end context "when disabled from the commandline" do let(:no_interactions) { true } it "uses only sleeper" do expect(pry_class).to_not receive(:new) expect(sleep_class).to receive(:new) subject end # TODO: this is both a useless case and incorrect value it { is_expected.to be_enabled } end end context "when disabled from the DSL" do let(:dsl_enabled) { false } context "when enabled from the commandline" do let(:no_interactions) { false } it "uses only sleeper" do expect(pry_class).to_not receive(:new) expect(sleep_class).to receive(:new) subject end it { is_expected.to_not be_enabled } end context "when disabled from the commandline" do let(:no_interactions) { true } it "uses only sleeper" do expect(pry_class).to_not receive(:new) expect(sleep_class).to receive(:new) subject end it { is_expected.to_not be_enabled } end end end end guard-2.18.1/spec/lib/guard/internals/000077500000000000000000000000001450001123100174715ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/internals/debugging_spec.rb000066400000000000000000000056001450001123100227640ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/debugging" RSpec.describe Guard::Internals::Debugging do let(:null) { IO::NULL } let(:ui) { class_double(::Guard::UI) } let(:tracing) { class_spy(::Guard::Internals::Tracing) } before do stub_const("::Guard::Internals::Tracing", tracing) stub_const("::Guard::UI", ui) allow(ui).to receive(:debug) allow(ui).to receive(:level=) allow(Thread).to receive(:abort_on_exception=) end after do described_class.send(:_reset) end describe "#start" do it "traces Kernel.system" do expect(tracing).to receive(:trace).with(Kernel, :system) do |*_, &block| expect(ui).to receive(:debug).with("Command execution: foo") block.call "foo" end described_class.start end it "traces Kernel.`" do expect(tracing).to receive(:trace).with(Kernel, :`) do |*_, &block| expect(ui).to receive(:debug).with("Command execution: foo") block.call("foo") end described_class.start end it "traces Open3.popen3" do expect(tracing).to receive(:trace).with(Open3, :popen3) do |*_, &block| expect(ui).to receive(:debug).with("Command execution: foo") block.call("foo") end described_class.start end it "traces Kernel.spawn" do expect(tracing).to receive(:trace).with(Kernel, :spawn) do |*_, &block| expect(ui).to receive(:debug).with("Command execution: foo") block.call("foo") end described_class.start end context "when not started" do before { described_class.start } it "sets logger to debug" do expect(ui).to have_received(:level=).with(Logger::DEBUG) end it "makes threads abort on exceptions" do expect(Thread).to have_received(:abort_on_exception=).with(true) end end context "when already started" do before do allow(tracing).to receive(:trace) described_class.start end it "does not set log level" do expect(ui).to_not receive(:level=) described_class.start end end end describe "#stop" do context "when already started" do before do described_class.start described_class.stop end it "sets logger level to info" do expect(ui).to have_received(:level=).with(Logger::INFO) end it "untraces Kernel.system" do expect(tracing).to have_received(:untrace).with(Kernel, :system) end it "untraces Kernel.`" do expect(tracing).to have_received(:untrace).with(Kernel, :`) end it "untraces Open3.popen3" do expect(tracing).to have_received(:untrace).with(Kernel, :popen3) end end context "when not started" do it "does not set logger level" do described_class.stop expect(ui).to_not have_received(:level=) end end end end guard-2.18.1/spec/lib/guard/internals/groups_spec.rb000066400000000000000000000075671450001123100223660ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/groups" RSpec.describe Guard::Internals::Groups do describe "#all" do let(:common) { instance_double("Guard::Group", name: :common) } let(:default) { instance_double("Guard::Group", name: :default) } before do allow(Guard::Group).to receive(:new).with(:common).and_return(common) allow(Guard::Group).to receive(:new).with(:default).and_return(default) end context "with only default groups" do it "initializes the groups" do expect(subject.all.map(&:name)).to eq %i[common default] end end context "with existing groups" do let(:frontend) { instance_double("Guard::Group", name: :frontend) } let(:backend) { instance_double("Guard::Group", name: :backend) } before do allow(Guard::Group).to receive(:new).with(:frontend, {}) .and_return(frontend) allow(Guard::Group).to receive(:new).with(:backend, {}) .and_return(backend) subject.add(:frontend) subject.add(:backend) end context "with no arguments" do let(:args) { [] } it "returns all groups" do expect(subject.all(*args)).to eq [common, default, frontend, backend] end end context "with a string argument" do it "returns an array of groups if plugins are found" do expect(subject.all("backend")).to eq [backend] end end context "with a symbol argument matching a group" do it "returns an array of groups if plugins are found" do expect(subject.all(:backend)).to eq [backend] end end context "with a symbol argument not matching a group" do it "returns an empty array when no group is found" do expect(subject.all(:foo)).to be_empty end end context "with a regexp argument matching a group" do it "returns an array of groups" do expect(subject.all(/^back/)).to eq [backend] end end context "with a regexp argument not matching a group" do it "returns an empty array when no group is found" do expect(subject.all(/back$/)).to be_empty end end end end # TOOD: test adding with options describe "#add" do let(:common) { instance_double("Guard::Group", name: :common) } let(:default) { instance_double("Guard::Group", name: :default) } before do allow(Guard::Group).to receive(:new).with(:common).and_return(common) allow(Guard::Group).to receive(:new).with(:default).and_return(default) end context "with existing groups" do let(:frontend) { instance_double("Guard::Group", name: :frontend) } let(:backend) { instance_double("Guard::Group", name: :backend) } before do allow(Guard::Group).to receive(:new).with("frontend", {}) .and_return(frontend) subject.add("frontend") end it "add the given group" do subject.add("frontend") expect(subject.all).to match_array([common, default, frontend]) end it "add the given group with options" do subject.add("frontend", foo: :bar) expect(subject.all).to match_array([common, default, frontend]) end # TODO: what if group is added multiple times with different options? context "with an existing group" do before { subject.add("frontend") } it "does not add duplicate groups when name is a string" do subject.add("frontend") expect(subject.all).to match_array([common, default, frontend]) end it "does not add duplicate groups when name is a symbol" do subject.add(:frontend) expect(subject.all).to match_array([common, default, frontend]) end end end end end guard-2.18.1/spec/lib/guard/internals/plugins_spec.rb000066400000000000000000000106631450001123100225170ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/plugins" RSpec.describe Guard::Internals::Plugins do def stub_plugin(name, group) instance_double("Guard::Plugin", name: name, group: group) end # TODO: all this is crazy let(:frontend) { instance_double("Guard::Group", name: :frontend) } let(:backend) { instance_double("Guard::Group", name: :backend) } let(:foo_bar_frontend) { stub_plugin("foobar", frontend) } let(:foo_baz_frontend) { stub_plugin("foobaz", frontend) } let(:foo_bar_backend) { stub_plugin("foobar", backend) } let(:foo_baz_backend) { stub_plugin("foobaz", backend) } let(:pu_foobar) { instance_double("Guard::PluginUtil") } let(:pu_foobaz) { instance_double("Guard::PluginUtil") } before do allow(Guard::PluginUtil).to receive(:new).with("foobar") .and_return(pu_foobar) allow(Guard::PluginUtil).to receive(:new).with("foobaz") .and_return(pu_foobaz) allow(pu_foobar).to receive(:initialize_plugin).with(group: "frontend") .and_return(foo_bar_frontend) allow(pu_foobaz).to receive(:initialize_plugin).with(group: "frontend") .and_return(foo_baz_frontend) allow(pu_foobar).to receive(:initialize_plugin).with(group: "backend") .and_return(foo_bar_backend) allow(pu_foobaz).to receive(:initialize_plugin).with(group: "backend") .and_return(foo_baz_backend) end describe "#all" do before do subject.add("foobar", group: "frontend") subject.add("foobaz", group: "frontend") subject.add("foobar", group: "backend") subject.add("foobaz", group: "backend") end context "with no arguments" do let(:args) { [] } it "returns all plugins" do expect(subject.all(*args)).to eq [ foo_bar_frontend, foo_baz_frontend, foo_bar_backend, foo_baz_backend ] end end context "find a plugin by as string" do it "returns an array of plugins if plugins are found" do expect(subject.all("foo-bar")) .to match_array([foo_bar_backend, foo_bar_frontend]) end end context "find a plugin by as symbol" do it "returns an array of plugins if plugins are found" do expect(subject.all(:'foo-bar')) .to match_array([foo_bar_backend, foo_bar_frontend]) end it "returns an empty array when no plugin is found" do expect(subject.all("foo-foo")).to be_empty end end context "find plugins matching a regexp" do it "returns an array of plugins if plugins are found" do expect(subject.all(/^foobar/)) .to match_array([foo_bar_backend, foo_bar_frontend]) end it "returns an empty array when no plugin is found" do expect(subject.all(/foo$/)).to be_empty end end context "find plugins by their group as a string" do it "returns an array of plugins if plugins are found" do expect(subject.all(group: "backend")) .to eq [foo_bar_backend, foo_baz_backend] end end context "find plugins by their group as a symbol" do it "returns an array of plugins if plugins are found" do expect(subject.all(group: :frontend)) .to eq [foo_bar_frontend, foo_baz_frontend] end it "returns an empty array when no plugin is found" do expect(subject.all(group: :unknown)).to be_empty end end context "find plugins by their group & name" do it "returns an array of plugins if plugins are found" do expect(subject.all(group: "backend", name: "foo-bar")) .to eq [foo_bar_backend] end it "returns an empty array when no plugin is found" do expect(subject.all(group: :unknown, name: :'foo-baz')) .to be_empty end end end describe "#remove" do before do subject.add("foobar", group: "frontend") subject.add("foobaz", group: "frontend") subject.add("foobar", group: "backend") subject.add("foobaz", group: "backend") end it "removes given plugin" do subject.remove(foo_bar_frontend) expect(subject.all).to match_array [ foo_baz_frontend, foo_bar_backend, foo_baz_backend ] end end end guard-2.18.1/spec/lib/guard/internals/scope_spec.rb000066400000000000000000000063011450001123100221410ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/scope" RSpec.describe Guard::Internals::Scope do let(:session) { instance_double("Guard::Internals::Session") } let(:state) { instance_double("Guard::Internals::State") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:foo_plugin) { instance_double("Guard::Plugin", name: :foo) } let(:bar_plugin) { instance_double("Guard::Plugin", name: :bar) } let(:baz_plugin) { instance_double("Guard::Plugin", name: :baz) } let(:foo_group) { instance_double("Guard::Group", name: :foo) } let(:bar_group) { instance_double("Guard::Group", name: :bar) } let(:baz_group) { instance_double("Guard::Group", name: :baz) } before do allow(groups).to receive(:all).with("foo").and_return([foo_group]) allow(groups).to receive(:all).with("bar").and_return([bar_group]) allow(groups).to receive(:all).with("baz").and_return([baz_group]) allow(groups).to receive(:all).with(:baz).and_return([baz_group]) allow(plugins).to receive(:all).with("foo").and_return([foo_plugin]) allow(plugins).to receive(:all).with("bar").and_return([bar_plugin]) allow(plugins).to receive(:all).with("baz").and_return([baz_plugin]) allow(plugins).to receive(:all).with(:baz).and_return([baz_plugin]) allow(session).to receive(:cmdline_plugins).and_return([]) allow(session).to receive(:cmdline_groups).and_return([]) allow(session).to receive(:groups).and_return(groups) allow(session).to receive(:plugins).and_return(plugins) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(session).to receive(:guardfile_plugin_scope).and_return([]) allow(session).to receive(:guardfile_group_scope).and_return([]) end # TODO: move to Session? describe "#to_hash" do %i[group plugin].each do |scope| describe scope.inspect do let(:hash) do subject.to_hash[:"#{scope}s"].map(&:name).map(&:to_s) end # NOTE: interactor returns objects context "when set from interactor" do before do stub_obj = send("baz_#{scope}") subject.from_interactor(:"#{scope}s" => stub_obj) end it "uses interactor scope" do expect(hash).to contain_exactly("baz") end end context "when not set in interactor" do context "when set in commandline" do before do allow(session).to receive(:"cmdline_#{scope}s") .and_return(%w[baz]) end it "uses commandline scope" do expect(hash).to contain_exactly("baz") end end context "when not set in commandline" do context "when set in Guardfile" do before do allow(session).to receive(:"guardfile_#{scope}_scope") .and_return(%w[baz]) end it "uses guardfile scope" do expect(hash).to contain_exactly("baz") end end end end end end end describe "#titles" do pending end end guard-2.18.1/spec/lib/guard/internals/session_spec.rb000066400000000000000000000212101450001123100225070ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/session" RSpec.describe Guard::Internals::Session do let(:options) { {} } subject { described_class.new(options) } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Plugins") } before do allow(Guard::Internals::Plugins).to receive(:new).and_return(plugins) allow(Guard::Internals::Groups).to receive(:new).and_return(groups) end describe "#initialize" do describe "#listener_args" do subject { described_class.new(options).listener_args } context "with a single watchdir" do let(:options) { { watchdir: ["/usr"] } } let(:dir) { Gem.win_platform? ? "C:/usr" : "/usr" } it { is_expected.to eq [:to, dir, {}] } end context "with multiple watchdirs" do let(:options) { { watchdir: ["/usr", "/bin"] } } let(:dir1) { Gem.win_platform? ? "C:/usr" : "/usr" } let(:dir2) { Gem.win_platform? ? "C:/bin" : "/bin" } it { is_expected.to eq [:to, dir1, dir2, {}] } end context "with force_polling option" do let(:options) { { force_polling: true } } it { is_expected.to eq [:to, Dir.pwd, force_polling: true] } end context "with latency option" do let(:options) { { latency: 1.5 } } it { is_expected.to eq [:to, Dir.pwd, latency: 1.5] } end end context "with the plugin option" do let(:options) do { plugin: %w[cucumber jasmine], guardfile_contents: "guard :jasmine do; end; "\ "guard :cucumber do; end; guard :coffeescript do; end" } end let(:jasmine) { instance_double("Guard::Plugin") } let(:cucumber) { instance_double("Guard::Plugin") } let(:coffeescript) { instance_double("Guard::Plugin") } before do stub_const "Guard::Jasmine", jasmine stub_const "Guard::Cucumber", cucumber stub_const "Guard::CoffeeScript", coffeescript end it "initializes the plugin scope" do allow(plugins).to receive(:add).with("cucumber", {}) .and_return(cucumber) allow(plugins).to receive(:add).with("jasmine", {}) .and_return(jasmine) expect(subject.cmdline_plugins).to match_array(%w[cucumber jasmine]) end end context "with the group option" do let(:options) do { group: %w[backend frontend], guardfile_contents: "group :backend do; end; "\ "group :frontend do; end; group :excluded do; end" } end before do g3 = instance_double("Guard::Group", name: :backend, options: {}) g4 = instance_double("Guard::Group", name: :frontend, options: {}) allow(Guard::Group).to receive(:new).with("backend", {}).and_return(g3) allow(Guard::Group).to receive(:new).with("frontend", {}).and_return(g4) end it "initializes the group scope" do expect(subject.cmdline_groups).to match_array(%w[backend frontend]) end end end describe "#clearing" do context "when not set" do context "when clearing is not set from commandline" do it { is_expected.to_not be_clearing } end context "when clearing is set from commandline" do let(:options) { { clear: false } } it { is_expected.to_not be_clearing } end end context "when set from guardfile" do context "when set to :on" do before { subject.clearing(true) } it { is_expected.to be_clearing } end context "when set to :off" do before { subject.clearing(false) } it { is_expected.to_not be_clearing } end end end describe "#guardfile_ignore=" do context "when set from guardfile" do before { subject.guardfile_ignore = [/foo/] } specify { expect(subject.guardfile_ignore).to eq([/foo/]) } end context "when set multiple times from guardfile" do before do subject.guardfile_ignore = [/foo/] subject.guardfile_ignore = [/bar/] end specify { expect(subject.guardfile_ignore).to eq([/foo/, /bar/]) } end context "when unset" do specify { expect(subject.guardfile_ignore).to eq([]) } end end describe "#guardfile_ignore_bang=" do context "when set from guardfile" do before { subject.guardfile_ignore_bang = [/foo/] } specify { expect(subject.guardfile_ignore_bang).to eq([/foo/]) } end context "when unset" do specify { expect(subject.guardfile_ignore_bang).to eq([]) } end end describe "#guardfile_scope" do before do subject.guardfile_scope(scope) end context "with a groups scope" do let(:scope) { { groups: [:foo] } } it "sets the groups" do expect(subject.guardfile_group_scope).to eq([:foo]) end end context "with a group scope" do let(:scope) { { group: [:foo] } } it "sets the groups" do expect(subject.guardfile_group_scope).to eq([:foo]) end end context "with a plugin scope" do let(:scope) { { plugin: [:foo] } } it "sets the plugins" do expect(subject.guardfile_plugin_scope).to eq([:foo]) end end context "with a plugins scope" do let(:scope) { { plugins: [:foo] } } it "sets the plugins" do expect(subject.guardfile_plugin_scope).to eq([:foo]) end end end describe ".convert_scope" do let(:foo) { instance_double("Guard::Plugin", name: "foo") } let(:bar) { instance_double("Guard::Plugin", name: "bar") } let(:backend) { instance_double("Guard::Group", name: "backend") } let(:frontend) { instance_double("Guard::Group", name: "frontend") } before do stub_const "Guard::Foo", class_double("Guard::Plugin") stub_const "Guard::Bar", class_double("Guard::Plugin") allow(plugins).to receive(:all).with("backend").and_return([]) allow(plugins).to receive(:all).with("frontend").and_return([]) allow(plugins).to receive(:all).with("foo").and_return([foo]) allow(plugins).to receive(:all).with("bar").and_return([bar]) allow(plugins).to receive(:all).with("unknown").and_return([]) allow(plugins).to receive(:all).with("scope").and_return([]) allow(groups).to receive(:all).with("backend").and_return([backend]) allow(groups).to receive(:all).with("frontend").and_return([frontend]) allow(groups).to receive(:all).with("unknown").and_return([]) allow(groups).to receive(:all).with("scope").and_return([]) end it "returns a group scope" do scopes, = subject.convert_scope %w[backend] expect(scopes).to eq(groups: [backend], plugins: []) scopes, = subject.convert_scope %w[frontend] expect(scopes).to eq(groups: [frontend], plugins: []) end it "returns a plugin scope" do scopes, = subject.convert_scope %w[foo] expect(scopes).to eq(plugins: [foo], groups: []) scopes, = subject.convert_scope %w[bar] expect(scopes).to eq(plugins: [bar], groups: []) end it "returns multiple group scopes" do scopes, = subject.convert_scope %w[backend frontend] expected = { groups: [backend, frontend], plugins: [] } expect(scopes).to eq(expected) end it "returns multiple plugin scopes" do scopes, = subject.convert_scope %w[foo bar] expect(scopes).to eq(plugins: [foo, bar], groups: []) end it "returns a plugin and group scope" do scopes, = subject.convert_scope %w[foo backend] expect(scopes).to eq(plugins: [foo], groups: [backend]) end it "returns the unkown scopes" do _, unknown = subject.convert_scope %w[unknown scope] expect(unknown).to eq %w[unknown scope] end end describe "#guardfile_notification=" do context "when set from guardfile" do before do subject.guardfile_notification = { foo: { bar: :baz } } end specify do expect(subject.notify_options).to eq( notify: true, notifiers: { foo: { bar: :baz } } ) end end context "when set multiple times from guardfile" do before do subject.guardfile_notification = { foo: { param: 1 } } subject.guardfile_notification = { bar: { param: 2 } } end it "merges results" do expect(subject.notify_options).to eq( notify: true, notifiers: { foo: { param: 1 }, bar: { param: 2 } } ) end end context "when unset" do specify do expect(subject.notify_options).to eq(notify: true, notifiers: {}) end end end end guard-2.18.1/spec/lib/guard/internals/state_spec.rb000066400000000000000000000027201450001123100221510ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/state" RSpec.describe Guard::Internals::State do let(:options) { {} } subject { described_class.new(options) } let(:scope) { instance_double("Guard::Internals::Scope") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:session) { instance_double("Guard::Internals::Session") } before do allow(Guard::Internals::Session).to receive(:new).and_return(session) allow(Guard::Internals::Scope).to receive(:new).and_return(scope) allow(session).to receive(:debug?).and_return(false) allow(session).to receive(:plugins).and_return(plugins) allow(session).to receive(:groups).and_return(groups) end describe "#initialize" do describe "debugging" do let(:options) { { debug: debug } } before do allow(session).to receive(:debug?).and_return(debug) expect(Guard::Internals::Session).to receive(:new).with(debug: debug) end context "when debug is set to true" do let(:debug) { true } it "sets up debugging" do expect(Guard::Internals::Debugging).to receive(:start) subject end end context "when debug is set to false" do let(:debug) { false } it "does not set up debugging" do expect(Guard::Internals::Debugging).to_not receive(:start) subject end end end end end guard-2.18.1/spec/lib/guard/internals/tracing_spec.rb000066400000000000000000000056511450001123100224660ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/tracing" RSpec.describe Guard::Internals::Tracing do let(:null) { IO::NULL } # NOTE: Calling system() is different from calling Kernel.system() # # We can capture system() calls by stubbing Kernel.system, but to capture # Kernel.system() calls, we need to stub the module's metaclass methods. # # Stubbing just Kernel.system isn't "deep" enough, but not only that, # we don't want to stub here, we want to TEST the stubbing # describe "Module method tracing" do let(:result) { Kernel.send(meth, *args) } subject { result } let(:callback) { double("callback", call: true) } # Since we can't stub the C code in Ruby, only "right" way to test this is: # actually call a real command and compare the output before { allow(Kernel).to receive(meth).and_call_original } context "when tracing" do before do described_class.trace(Kernel, meth) { |*args| callback.call(*args) } subject end after { described_class.untrace(Kernel, meth) } context "with no command arguments" do let(:args) { ["echo >#{null}"] } context "when #system" do let(:meth) { "system" } it { is_expected.to eq(true) } it "outputs command" do expect(callback).to have_received(:call).with("echo >#{null}") end end context "when backticks" do let(:meth) { :` } it { is_expected.to eq("") } it "outputs command" do expect(callback).to have_received(:call).with("echo >#{null}") end end end context "with command arguments" do let(:args) { %w[true 123] } context "when #system" do let(:meth) { "system" } it { is_expected.to eq(true) } it "outputs command arguments" do expect(callback).to have_received(:call).with("true", "123") end end end end context "when not tracing" do before { subject } context "with no command arguments" do let(:args) { ["echo test > #{null}"] } context "when #system" do let(:meth) { :system } it { is_expected.to eq(true) } it "does not output anything" do expect(callback).to_not have_received(:call) end end context "when backticks" do let(:meth) { :` } it { is_expected.to eq("") } it "does not output anything" do expect(callback).to_not have_received(:call) end end end context "with command arguments" do let(:args) { %w[true 123] } context "when #system" do let(:meth) { :system } it { is_expected.to eq(true) } it "does not output anything" do expect(callback).to_not have_received(:call) end end end end end end guard-2.18.1/spec/lib/guard/internals/traps_spec.rb000066400000000000000000000016121450001123100221610ustar00rootroot00000000000000# frozen_string_literal: true require "guard/internals/traps" RSpec.describe Guard::Internals::Traps do describe ".handle" do let(:signal_class) { class_double(Signal) } before do stub_const("Signal", signal_class) end context "with a supported signal name" do let(:signal) { "USR1" } it "sets up a handler" do allow(Signal).to receive(:list).and_return("USR1" => 10) allow(Signal).to receive(:trap).with(signal) do |_, &block| block.call end expect { |b| described_class.handle(signal, &b) }.to yield_control end end context "with an unsupported signal name" do let(:signal) { "ABCD" } it "does not set a handler" do allow(Signal).to receive(:list).and_return("KILL" => 9) expect(Signal).to_not receive(:trap) described_class.handle(signal) end end end end guard-2.18.1/spec/lib/guard/jobs/000077500000000000000000000000001450001123100164275ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/jobs/pry_wrapper_spec.rb000066400000000000000000000153101450001123100223400ustar00rootroot00000000000000# frozen_string_literal: true require "guard/jobs/pry_wrapper" RSpec.describe Guard::Jobs::PryWrapper do subject { described_class.new({}) } let(:listener) { instance_double("Listen::Listener") } let(:pry_hooks) { double("pry_hooks", add_hook: true) } let(:pry_config) do double("pry_config", "history_file=" => true, command_prefix: true, "prompt=" => true, "should_load_rc=" => true, "should_load_local_rc=" => true, hooks: pry_hooks) end let(:pry_history) { double("pry_history") } let(:pry_commands) do double("pry_commands", alias_command: true, create_command: true, command: true, block_command: true) end let(:terminal_settings) { instance_double("Guard::Jobs::TerminalSettings") } let(:session) { instance_double("Guard::Internals::Session") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:state) { instance_double("Guard::Internals::State") } let(:scope) { instance_double("Guard::Internals::Scope") } before do allow(Guard).to receive(:listener).and_return(listener) allow(Pry).to receive(:config).and_return(pry_config) allow(Pry).to receive(:commands).and_return(pry_commands) allow(Shellany::Sheller).to receive(:run).with("hash", "stty") { false } allow(groups).to receive(:all).and_return([]) allow(session).to receive(:groups).and_return(groups) allow(plugins).to receive(:all).and_return([]) allow(session).to receive(:plugins).and_return(plugins) allow(state).to receive(:session).and_return(session) allow(state).to receive(:scope).and_return(scope) allow(Guard).to receive(:state).and_return(state) allow(Guard::Commands::All).to receive(:import) allow(Guard::Commands::Change).to receive(:import) allow(Guard::Commands::Reload).to receive(:import) allow(Guard::Commands::Pause).to receive(:import) allow(Guard::Commands::Notification).to receive(:import) allow(Guard::Commands::Show).to receive(:import) allow(Guard::Commands::Scope).to receive(:import) allow(Guard::Jobs::TerminalSettings).to receive(:new) .and_return(terminal_settings) allow(terminal_settings).to receive(:configurable?).and_return(false) allow(terminal_settings).to receive(:save) allow(terminal_settings).to receive(:restore) end describe "#_setup" do context "Guard is using Pry >= 0.13" do it "calls Pry.config.history_file=" do expect(pry_config).to receive(:history_file=) subject end end context "Guard is using Pry < 0.13" do let(:pry_config) do double("pry_config", "history" => true, command_prefix: true, "prompt=" => true, "should_load_rc=" => true, "should_load_local_rc=" => true, hooks: pry_hooks) end it "calls Pry.config.history.file=" do expect(pry_config).to receive(:history).and_return(pry_history) expect(pry_history).to receive(:file=) subject end end end describe "#foreground" do before do allow(Pry).to receive(:start) do # sleep for a long time (anything > 0.6) sleep 2 end end after do subject.background end it "waits for Pry thread to finish" do was_alive = false Thread.new do sleep 0.1 was_alive = subject.send(:thread).alive? subject.background end subject.foreground # blocks expect(was_alive).to be end it "prevents the Pry thread from being killed too quickly" do start = Time.now.to_f Thread.new do sleep 0.1 subject.background end subject.foreground # blocks killed_moment = Time.now.to_f expect(killed_moment - start).to be > 0.5 end it "return :stopped when brought into background" do Thread.new do sleep 0.1 subject.background end expect(subject.foreground).to be(:stopped) end end describe "#background" do before do allow(Pry).to receive(:start) do # 0.5 is enough for Pry, so we use 0.4 sleep 0.4 end end it "kills the Pry thread" do subject.foreground sleep 1 # give Pry 0.5 sec to boot subject.background sleep 0.25 # to let Pry get killed asynchronously expect(subject.send(:thread)).to be_nil end end describe "#_prompt(ending_char)" do let(:prompt) { subject.send(:_prompt, ">") } before do allow(Shellany::Sheller).to receive(:run).with("hash", "stty") { false } allow(scope).to receive(:titles).and_return(["all"]) allow(listener).to receive(:paused?).and_return(false) allow(Pry).to receive(:view_clip).and_return("main") end context "Guard is using Pry >= 0.13" do let(:pry) { double("Pry", input_ring: []) } let(:pry_prompt) { double } it "calls Pry::Prompt.new" do expect(Pry::Prompt).to receive(:is_a?).with(Class).and_return(true) expect(Pry::Prompt).to receive(:new).with("Guard", "Guard Pry prompt", an_instance_of(Array)).and_return(pry_prompt) expect(pry_config).to receive(:prompt=).with(pry_prompt) subject end context "Guard is not paused" do it "displays 'guard'" do expect(prompt.call(double, 0, pry)) .to eq "[0] guard(main)> " end end context "Guard is paused" do before do allow(listener).to receive(:paused?).and_return(true) end it "displays 'pause'" do expect(prompt.call(double, 0, pry)) .to eq "[0] pause(main)> " end end context "with a groups scope" do before do allow(scope).to receive(:titles).and_return(%w[Backend Frontend]) end it "displays the group scope title in the prompt" do expect(prompt.call(double, 0, pry)) .to eq "[0] Backend,Frontend guard(main)> " end end context "with a plugins scope" do before do allow(scope).to receive(:titles).and_return(%w[RSpec Ronn]) end it "displays the group scope title in the prompt" do result = prompt.call(double, 0, pry) expect(result).to eq "[0] RSpec,Ronn guard(main)> " end end end context "Guard is using Pry < 0.13" do let(:pry) { double("Pry", input_array: []) } it "does not call Pry::Prompt.new" do expect(Pry::Prompt).to receive(:is_a?).with(Class).and_return(false) expect(pry_config).to receive(:prompt=).with(an_instance_of(Array)) subject end it "displays 'guard'" do expect(prompt.call(double, 0, pry)) .to eq "[0] guard(main)> " end end end end guard-2.18.1/spec/lib/guard/jobs/sleep_spec.rb000066400000000000000000000023771450001123100211070ustar00rootroot00000000000000# frozen_string_literal: true require "guard/jobs/sleep" RSpec.describe Guard::Jobs::Sleep do subject { described_class.new({}) } describe "#foreground" do it "sleeps" do status = "unknown" Thread.new do sleep 0.1 status = Thread.main.status subject.background end subject.foreground expect(status).to eq("sleep") end it "returns :stopped when put to background" do Thread.new do sleep 0.1 subject.background end expect(subject.foreground).to eq(:stopped) end end describe "#background" do it "wakes up main thread" do status = "unknown" Thread.new do sleep 0.1 # give enough time for foreground to put main thread to sleep subject.background sleep 0.1 # cause test to fail every time (without busy loop below) status = Thread.main.status Thread.main.wakeup # to get "red" in TDD without hanging end subject.foreground # go to sleep # Keep main thread busy until above thread has a chance to get status begin value = 0 Timeout.timeout(0.1) { loop { value += 1 } } rescue Timeout::Error end expect(status).to eq("run") end end end guard-2.18.1/spec/lib/guard/notifier_spec.rb000066400000000000000000000036751450001123100206630ustar00rootroot00000000000000# frozen_string_literal: true require "guard/notifier" RSpec.describe Guard::Notifier do subject { described_class } let(:notifier) { instance_double("Notiffany::Notifier") } before do allow(Notiffany::Notifier).to receive(:new).and_return(notifier) end after do Guard::Notifier.instance_variable_set(:@notifier, nil) end describe "toggle_notification" do before do allow(notifier).to receive(:enabled?).and_return(true) end context "with available notifiers" do context "when currently on" do before do allow(notifier).to receive(:active?).and_return(true) subject.connect end it "suspends notifications" do expect(notifier).to receive(:turn_off) subject.toggle end end context "when currently off" do before do subject.connect allow(notifier).to receive(:active?).and_return(false) end it "resumes notifications" do expect(notifier).to receive(:turn_on) subject.toggle end end end end describe ".notify" do before do subject.connect allow(notifier).to receive(:notify) end context "with no options" do it "notifies" do expect(notifier).to receive(:notify).with("A", {}) subject.notify("A") end end context "with multiple parameters" do it "notifies" do expect(notifier).to receive(:notify) .with("A", priority: 2, image: :failed) subject.notify("A", priority: 2, image: :failed) end end context "with a runtime error" do before do allow(notifier).to receive(:notify).and_raise(RuntimeError, "an error") end it "shows an error" do expect(Guard::UI).to receive(:error) .with(/Notification failed for .+: an error/) subject.notify("A", priority: 2, image: :failed) end end end end guard-2.18.1/spec/lib/guard/options_spec.rb000066400000000000000000000015101450001123100205210ustar00rootroot00000000000000# frozen_string_literal: true require "guard/options" RSpec.describe Guard::Options do describe ".initialize" do it "handles nil options" do expect { described_class.new(nil) }.to_not raise_error end it "has indifferent access" do options = described_class.new({ foo: "bar" }, "foo2" => "baz") expect(options[:foo]).to eq "bar" expect(options["foo"]).to eq "bar" expect(options[:foo2]).to eq "baz" expect(options["foo2"]).to eq "baz" end it "can be passed defaults" do options = described_class.new({}, foo: "bar") expect(options[:foo]).to eq "bar" end it "merges the sensible defaults to the given options" do options = described_class.new({ plugin: ["rspec"] }, plugin: ["test"]) expect(options[:plugin]).to eq ["rspec"] end end end guard-2.18.1/spec/lib/guard/plugin_spec.rb000066400000000000000000000145001450001123100203270ustar00rootroot00000000000000# frozen_string_literal: true require "guard/plugin" require "guard/watcher" RSpec.describe Guard::Plugin do let(:default) { instance_double("Guard::Group") } let(:test) { instance_double("Guard::Group") } let(:session) { instance_double("Guard::Internals::Session") } let(:groups) { instance_double("Guard::Internals::Groups") } let(:state) { instance_double("Guard::Internals::State") } before do allow(groups).to receive(:add).with(:default).and_return(default) allow(groups).to receive(:add).with(:test).and_return(test) allow(session).to receive(:groups).and_return(groups) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) end # TODO: this should already be done in spec_helper! after do klass = described_class klass.instance_variables.each do |var| klass.instance_variable_set(var, nil) end end describe "#initialize" do it "assigns the defined watchers" do watchers = [double("foo")] expect(Guard::Plugin.new(watchers: watchers).watchers).to eq watchers end it "assigns the defined options" do options = { a: 1, b: 2 } expect(Guard::Plugin.new(options).options).to eq options end context "with a group in the options" do it "assigns the given group" do expect(Guard::Plugin.new(group: :test).group).to eq test end end context "without a group in the options" do it "assigns a default group" do allow(groups).to receive(:add).with(:default).and_return(default) expect(Guard::Plugin.new.group).to eq default end end context "with a callback" do it "adds the callback" do block = instance_double(Proc) events = %i[start_begin start_end] callbacks = [{ events: events, listener: block }] Guard::Plugin.new(callbacks: callbacks) expect(Guard::Plugin.callbacks.first[0][0].callbacks).to eq(callbacks) end end end context "with a plugin instance" do subject do module Guard class DuMmy < Guard::Plugin end end Guard::DuMmy end after do Guard.send(:remove_const, :DuMmy) end describe ".non_namespaced_classname" do it "remove the Guard:: namespace" do expect(subject.non_namespaced_classname).to eq "DuMmy" end end describe ".non_namespaced_name" do it "remove the Guard:: namespace and downcase" do expect(subject.non_namespaced_name).to eq "dummy" end end describe ".template" do before do allow(File).to receive(:read) end it "reads the default template" do expect(File).to receive(:read) .with("/guard-dummy/lib/guard/dummy/templates/Guardfile") { true } subject.template("/guard-dummy") end end describe "#name" do it "outputs the short plugin name" do expect(subject.new.name).to eq "dummy" end end describe "#title" do it "outputs the plugin title" do expect(subject.new.title).to eq "DuMmy" end end describe "#to_s" do let(:default) { instance_double("Guard::Group", name: :default) } it "output the short plugin name" do expect(subject.new.to_s) .to match(/#/) end end end let(:listener) { instance_double(Proc, call: nil) } describe ".add_callback" do let(:foo) { double("foo plugin") } it "can add a run_on_modifications callback" do described_class.add_callback( listener, foo, :run_on_modifications_begin ) result = described_class.callbacks[[foo, :run_on_modifications_begin]] expect(result).to include(listener) end it "can add multiple callbacks" do described_class.add_callback(listener, foo, %i[event1 event2]) result = described_class.callbacks[[foo, :event1]] expect(result).to include(listener) result = described_class.callbacks[[foo, :event2]] expect(result).to include(listener) end end describe ".notify" do let(:foo) { double("foo plugin") } let(:bar) { double("bar plugin") } before do described_class.add_callback(listener, foo, :start_begin) end it "sends :call to the given Guard class's start_begin callback" do expect(listener).to receive(:call).with(foo, :start_begin, "args") described_class.notify(foo, :start_begin, "args") end it "sends :call to the given Guard class's start_begin callback" do expect(listener).to receive(:call).with(foo, :start_begin, "args") described_class.notify(foo, :start_begin, "args") end it "runs only the given callbacks" do listener2 = double("listener2") described_class.add_callback(listener2, foo, :start_end) expect(listener2).to_not receive(:call).with(foo, :start_end) described_class.notify(foo, :start_begin) end it "runs callbacks only for the guard given" do described_class.add_callback(listener, bar, :start_begin) expect(listener).to_not receive(:call).with(bar, :start_begin) described_class.notify(foo, :start_begin) end end describe "#hook" do let(:foo) { double("foo plugin") } before do described_class.add_callback(listener, foo, :start_begin) end it "notifies the hooks" do class Foo < described_class def run_all hook :begin hook :end end end foo = Foo.new expect(described_class).to receive(:notify).with(foo, :run_all_begin) expect(described_class).to receive(:notify).with(foo, :run_all_end) foo.run_all end it "passes the hooks name" do class Foo < described_class def start hook "my_hook" end end foo = Foo.new expect(described_class).to receive(:notify).with(foo, :my_hook) foo.start end it "accepts extra arguments" do class Foo < described_class def stop hook :begin, "args" hook "special_sauce", "first_arg", "second_arg" end end foo = Foo.new expect(described_class).to receive(:notify) .with(foo, :stop_begin, "args") expect(described_class).to receive(:notify) .with(foo, :special_sauce, "first_arg", "second_arg") foo.stop end end end guard-2.18.1/spec/lib/guard/plugin_util_spec.rb000066400000000000000000000241341450001123100213700ustar00rootroot00000000000000# frozen_string_literal: true require "guard/plugin_util" require "guard/guardfile/evaluator" RSpec.describe Guard::PluginUtil do let(:guard_rspec_class) { class_double("Guard::Plugin") } let(:guard_rspec) { instance_double("Guard::Plugin") } let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } let(:session) { instance_double("Guard::Internals::Session") } let(:state) { instance_double("Guard::Internals::State") } before do allow(session).to receive(:evaluator_options).and_return({}) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) end describe ".plugin_names" do before do spec = Gem::Specification gems = [ instance_double(spec, name: "guard-myplugin"), instance_double(spec, name: "gem1", full_gem_path: "/gem1"), instance_double(spec, name: "gem2", full_gem_path: "/gem2"), instance_double(spec, name: "guard-compat") ] allow(File).to receive(:exist?) .with("/gem1/lib/guard/gem1.rb") { false } allow(File).to receive(:exist?) .with("/gem2/lib/guard/gem2.rb") { true } gem = class_double(Gem::Specification) stub_const("Gem::Specification", gem) expect(Gem::Specification).to receive(:find_all) { gems } allow(Gem::Specification).to receive(:unresolved_deps) { [] } end it "returns the list of guard gems" do expect(described_class.plugin_names).to include("myplugin") end it "returns the list of embedded guard gems" do expect(described_class.plugin_names).to include("gem2") end it "ignores guard-compat" do expect(described_class.plugin_names).to_not include("compat") end end describe "#initialize" do it "accepts a name without guard-" do expect(described_class.new("rspec").name).to eq "rspec" end it "accepts a name with guard-" do expect(described_class.new("guard-rspec").name).to eq "rspec" end end describe "#initialize_plugin" do let(:plugin_util) { described_class.new("rspec") } before do allow_any_instance_of(described_class) .to receive(:plugin_class) .and_return(guard_rspec_class) end context "with a plugin inheriting from Guard::Plugin" do before do expect(guard_rspec_class).to receive(:ancestors) { [::Guard::Plugin] } end it "instantiate the plugin using the new API" do options = { watchers: ["watcher"], group: "foo" } expect(guard_rspec_class).to receive(:new).with(options) { guard_rspec } expect(plugin_util.initialize_plugin(options)).to eq guard_rspec end end end describe "#plugin_location" do subject { described_class.new("rspec") } it "returns the path of a Guard gem" do expect(Gem::Specification).to receive(:find_by_name) .with("guard-rspec") { double(full_gem_path: "gems/guard-rspec") } expect(subject.plugin_location).to eq "gems/guard-rspec" end end describe "#plugin_class" do after do # TODO: use RSpec's stub const consts = %i[Classname DashedClassName UnderscoreClassName VSpec Inline] consts.each do |const| begin Guard.send(:remove_const, const) rescue NameError end end end it "reports an error if the class is not found" do expect(::Guard::UI).to receive(:error).with(/Could not load/) expect(::Guard::UI).to receive(:error).with(/Error is: cannot load/) expect(::Guard::UI).to receive(:error).with(/plugin_util.rb/) plugin = described_class.new("notAGuardClass") allow(plugin).to receive(:require).with("guard/notaguardclass") .and_raise(LoadError, "cannot load such file --") plugin.plugin_class end context "with a nested Guard class" do it "resolves the Guard class from string" do plugin = described_class.new("classname") expect(plugin).to receive(:require) do |classname| expect(classname).to eq "guard/classname" module Guard class Classname end end end expect(plugin.plugin_class).to eq Guard::Classname end it "resolves the Guard class from symbol" do plugin = described_class.new(:classname) expect(plugin).to receive(:require) do |classname| expect(classname).to eq "guard/classname" module Guard class Classname end end end expect(plugin.plugin_class).to eq Guard::Classname end end context "with a name with dashes" do it "returns the Guard class" do plugin = described_class.new("dashed-class-name") expect(plugin).to receive(:require) do |classname| expect(classname).to eq "guard/dashed-class-name" module Guard class DashedClassName end end end expect(plugin.plugin_class).to eq Guard::DashedClassName end end context "with a name with underscores" do it "returns the Guard class" do plugin = described_class.new("underscore_class_name") expect(plugin).to receive(:require) do |classname| expect(classname).to eq "guard/underscore_class_name" module Guard class UnderscoreClassName end end end expect(plugin.plugin_class).to eq Guard::UnderscoreClassName end end context "with a name like VSpec" do it "returns the Guard class" do plugin = described_class.new("vspec") mod = nil allow(plugin).to receive(:require) do |classname| expect(classname).to eq "guard/vspec" module Guard class VSpec end end mod = Guard::VSpec end expect(plugin.plugin_class).to eq mod expect(mod).to be end end context "with an inline Guard class" do subject { described_class.new("inline") } let(:plugin_class) { class_double("Guard::Plugin") } it "returns the Guard class" do allow(Guard).to receive(:constants).and_return([:Inline]) allow(Guard).to receive(:const_get).with(:Inline) .and_return(plugin_class) expect(subject).to_not receive(:require) expect(subject.plugin_class).to eq plugin_class end end context "when set to fail gracefully" do options = { fail_gracefully: true } subject { described_class.new("notAGuardClass") } it "does not print error messages on fail" do expect(::Guard::UI).to_not receive(:error) plugin = subject allow(plugin).to receive(:require).and_raise(LoadError) expect(subject.plugin_class(options)).to be_nil end end end describe "#add_to_guardfile" do before do allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) allow(evaluator).to receive(:evaluate) end context "when the Guard is already in the Guardfile" do before do allow(evaluator).to receive(:guardfile_include?) { true } end it "shows an info message" do expect(::Guard::UI).to receive(:info) .with "Guardfile already includes myguard guard" described_class.new("myguard").add_to_guardfile end end context "when Guardfile is empty" do let(:plugin_util) { described_class.new("myguard") } let(:plugin_class) { class_double("Guard::Plugin") } let(:location) { "/Users/me/projects/guard-myguard" } let(:gem_spec) { instance_double("Gem::Specification") } let(:io) { StringIO.new } before do allow(evaluator).to receive(:evaluate) .and_raise(Guard::Guardfile::Evaluator::NoPluginsError) allow(gem_spec).to receive(:full_gem_path).and_return(location) allow(evaluator).to receive(:guardfile_include?) { false } allow(Guard).to receive(:constants).and_return([:MyGuard]) allow(Guard).to receive(:const_get).with(:MyGuard) .and_return(plugin_class) allow(Gem::Specification).to receive(:find_by_name) .with("guard-myguard").and_return(gem_spec) allow(plugin_class).to receive(:template).with(location) .and_return("Template content") allow(File).to receive(:read).with("Guardfile") { "Guardfile content" } allow(File).to receive(:open).with("Guardfile", "wb").and_yield io end it "appends the template to the Guardfile" do plugin_util.add_to_guardfile expect(io.string).to eq "Guardfile content\n\nTemplate content\n" end end context "when the Guard is not in the Guardfile" do let(:plugin_util) { described_class.new("myguard") } let(:plugin_class) { class_double("Guard::Plugin") } let(:location) { "/Users/me/projects/guard-myguard" } let(:gem_spec) { instance_double("Gem::Specification") } let(:io) { StringIO.new } before do allow(gem_spec).to receive(:full_gem_path).and_return(location) allow(evaluator).to receive(:guardfile_include?) { false } allow(Guard).to receive(:constants).and_return([:MyGuard]) allow(Guard).to receive(:const_get).with(:MyGuard) .and_return(plugin_class) allow(Gem::Specification).to receive(:find_by_name) .with("guard-myguard").and_return(gem_spec) allow(plugin_class).to receive(:template).with(location) .and_return("Template content") allow(File).to receive(:read).with("Guardfile") { "Guardfile content" } allow(File).to receive(:open).with("Guardfile", "wb").and_yield io end it "appends the template to the Guardfile" do plugin_util.add_to_guardfile expect(io.string).to eq "Guardfile content\n\nTemplate content\n" end end end end guard-2.18.1/spec/lib/guard/runner_spec.rb000066400000000000000000000325061450001123100203500ustar00rootroot00000000000000# frozen_string_literal: true require "guard/runner" require "guard/plugin" RSpec.describe Guard::Runner do let(:ui_config) { instance_double("Guard::UI::Config") } let(:backend_group) do instance_double("Guard::Group", options: {}, name: :backend) end let(:frontend_group) do instance_double("Guard::Group", options: {}, name: :frontend) end let(:foo_plugin) { double("foo", group: backend_group, hook: nil) } let(:bar_plugin) { double("bar", group: frontend_group, hook: nil) } let(:baz_plugin) { double("baz", group: frontend_group, hook: nil) } let(:scope) { instance_double("Guard::Internals::Scope") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:state) { instance_double("Guard::Internals::State") } let(:session) { instance_double("Guard::Internals::Session") } before do allow(session).to receive(:plugins).and_return(plugins) allow(state).to receive(:session).and_return(session) allow(state).to receive(:scope).and_return(scope) allow(Guard).to receive(:state).and_return(state) allow(Guard::UI::Config).to receive(:new).and_return(ui_config) end before do Guard::UI.options = nil end after do Guard::UI.reset_logger Guard::UI.options = nil end describe "#run" do before do allow(scope).to receive(:grouped_plugins).with({}) .and_return([[nil, [foo_plugin, bar_plugin, baz_plugin]]]) allow(ui_config).to receive(:with_progname).and_yield end it "executes supervised task on all registered plugins implementing it" do [foo_plugin, bar_plugin].each do |plugin| expect(plugin).to receive(:my_hard_task) end subject.run(:my_hard_task) end it "marks an action as unit of work" do expect(Lumberjack).to receive(:unit_of_work) subject.run(:my_task) end context "with interrupted task" do before do allow(foo_plugin).to receive(:failing).and_raise(Interrupt) # allow(Guard).to receive(:plugins).and_return([foo_plugin]) end it "catches the thrown symbol" do expect { subject.run(:failing) }.to_not throw_symbol(:task_has_failed) end end context "with a scope" do let(:scope_hash) { { plugin: :bar } } it "executes the supervised task on the specified plugin only" do expect(scope).to receive(:grouped_plugins).with(scope_hash) .and_return([[nil, [bar_plugin]]]) expect(bar_plugin).to receive(:my_task) expect(foo_plugin).to_not receive(:my_task) expect(baz_plugin).to_not receive(:my_task) subject.run(:my_task, scope_hash) end end context "with no scope" do let(:scope_hash) { nil } it "executes the supervised task using current scope" do expect(bar_plugin).to receive(:my_task) expect(foo_plugin).to receive(:my_task) expect(baz_plugin).to receive(:my_task) subject.run(:my_task, scope_hash) end end end describe "#run_on_changes" do let(:changes) { [[], [], []] } let(:watcher_module) { Guard::Watcher } before do allow(watcher_module).to receive(:match_files) { [] } allow(Guard::UI).to receive(:clear) allow(foo_plugin).to receive(:regular_without_arg) { fail "not stubbed" } allow(foo_plugin).to receive(:regular_with_arg) { fail "not stubbed" } allow(foo_plugin).to receive(:failing) { fail "not stubbed" } # TODO: runner shouldn't have to know about these allow(foo_plugin).to receive(:run_on_modifications) { fail "not stubbed" } allow(foo_plugin).to receive(:run_on_change) { fail "not stubbed" } allow(foo_plugin).to receive(:run_on_additions) { fail "not stubbed" } allow(foo_plugin).to receive(:run_on_removals) { fail "not stubbed" } allow(foo_plugin).to receive(:run_on_deletion) { fail "not stubbed" } allow(foo_plugin).to receive(:my_task) allow(bar_plugin).to receive(:my_task) allow(baz_plugin).to receive(:my_task) allow(foo_plugin).to receive(:name).and_return("Foo") allow(scope).to receive(:grouped_plugins) do |args| fail "stub me (#{args.inspect})!" end # disable reevaluator allow(scope).to receive(:grouped_plugins).with(group: :common) .and_return([[nil, []]]) # foo in default group allow(scope).to receive(:grouped_plugins).with(group: :default) .and_return([[nil, [foo_plugin]]]) allow(scope).to receive(:grouped_plugins).with(no_args) .and_return([[nil, [foo_plugin]]]) allow(ui_config).to receive(:with_progname).and_yield end it "always calls UI.clearable" do expect(Guard::UI).to receive(:clearable) expect(scope).to receive(:grouped_plugins).with(no_args) .and_return([[nil, [foo_plugin]]]) subject.run_on_changes(*changes) end context "when clearable" do it "clear UI" do expect(Guard::UI).to receive(:clear) expect(scope).to receive(:grouped_plugins).with(no_args) .and_return([[nil, [foo_plugin]]]) subject.run_on_changes(*changes) end end context "with no changes" do it "does not run any task" do %w[ run_on_modifications run_on_change run_on_additions run_on_removals run_on_deletion ].each do |task| expect(foo_plugin).to_not receive(task.to_sym) end subject.run_on_changes(*changes) end end context "with modified files but modified paths is empty" do let(:modified) { %w[file.txt image.png] } before do changes[0] = modified expect(watcher_module).to receive(:match_files).once .with(foo_plugin, modified).and_return([]) # stub so respond_to? works end it "does not call run anything" do expect(foo_plugin).to_not receive(:run_on_modifications) subject.run_on_changes(*changes) end end context "with modified paths" do let(:modified) { %w[file.txt image.png] } before do changes[0] = modified expect(watcher_module).to receive(:match_files) .with(foo_plugin, modified).and_return(modified) end it "executes the :run_first_task_found task" do expect(foo_plugin).to receive(:run_on_modifications).with(modified) {} subject.run_on_changes(*changes) end end context "with added files but added paths is empty" do let(:added) { %w[file.txt image.png] } before do changes[0] = added expect(watcher_module).to receive(:match_files).once .with(foo_plugin, added).and_return([]) end it "does not call run anything" do expect(foo_plugin).to_not receive(:run_on_additions) subject.run_on_changes(*changes) end end context "with added paths" do let(:added) { %w[file.txt image.png] } before do changes[1] = added expect(watcher_module).to receive(:match_files) .with(foo_plugin, added).and_return(added) end it "executes the :run_on_additions task" do expect(foo_plugin).to receive(:run_on_additions).with(added) {} subject.run_on_changes(*changes) end end context "with non-matching removed paths" do let(:removed) { %w[file.txt image.png] } before do changes[2] = removed expect(watcher_module).to receive(:match_files).once .with(foo_plugin, removed) { [] } # stub so respond_to? works allow(foo_plugin).to receive(:run_on_removals) end it "does not call tasks" do expect(foo_plugin).to_not receive(:run_on_removals) subject.run_on_changes(*changes) end end context "with matching removed paths" do let(:removed) { %w[file.txt image.png] } before do changes[2] = removed expect(watcher_module).to receive(:match_files) .with(foo_plugin, removed) { removed } end it "executes the :run_on_removals task" do expect(foo_plugin).to receive(:run_on_removals).with(removed) {} subject.run_on_changes(*changes) end end end describe "#_supervise" do before do allow(ui_config).to receive(:with_progname).and_yield end it "executes the task on the passed guard" do expect(foo_plugin).to receive(:my_task) subject.send(:_supervise, foo_plugin, :my_task) end context "with a task that succeeds" do context "without any arguments" do before do allow(foo_plugin).to receive(:regular_without_arg) { true } end it "does not remove the Guard" do expect(plugins).to_not receive(:remove) subject.send(:_supervise, foo_plugin, :regular_without_arg) end it "returns the result of the task" do result = subject.send(:_supervise, foo_plugin, :regular_without_arg) expect(result).to be_truthy end it "calls :begin and :end hooks" do expect(foo_plugin).to receive(:hook) .with("regular_without_arg_begin") expect(foo_plugin).to receive(:hook) .with("regular_without_arg_end", true) subject.send(:_supervise, foo_plugin, :regular_without_arg) end it "passes the result of the supervised method to the :end hook" do expect(foo_plugin).to receive(:hook) .with("regular_without_arg_begin") expect(foo_plugin).to receive(:hook) .with("regular_without_arg_end", true) subject.send(:_supervise, foo_plugin, :regular_without_arg) end end context "with arguments" do before do allow(foo_plugin).to receive(:regular_with_arg) .with("given_path") { "I'm a success" } end it "does not remove the Guard" do expect(plugins).to_not receive(:remove) subject.send( :_supervise, foo_plugin, :regular_with_arg, "given_path" ) end it "returns the result of the task" do result = subject.send( :_supervise, foo_plugin, :regular_with_arg, "given_path" ) expect(result).to eq "I'm a success" end end end context "with a task that throws :task_has_failed" do before do allow(foo_plugin).to receive(:failing) { throw :task_has_failed } end context "in a group" do context "with halt_on_fail: true" do before { backend_group.options[:halt_on_fail] = true } it "throws :task_has_failed" do expect do subject.send(:_supervise, foo_plugin, :failing) end.to throw_symbol(:task_has_failed) end end context "with halt_on_fail: false" do before { backend_group.options[:halt_on_fail] = false } it "catches :task_has_failed" do expect do subject.send(:_supervise, foo_plugin, :failing) end.to_not throw_symbol(:task_has_failed) end end end end context "with a task that raises an exception" do before do allow(foo_plugin).to receive(:failing) { fail "I break your system" } allow(plugins).to receive(:remove).with(foo_plugin) end it "removes the Guard" do expect(plugins).to receive(:remove).with(foo_plugin) {} subject.send(:_supervise, foo_plugin, :failing) end it "display an error to the user" do expect(::Guard::UI).to receive :error expect(::Guard::UI).to receive :info subject.send(:_supervise, foo_plugin, :failing) end it "returns the exception" do failing_result = subject.send(:_supervise, foo_plugin, :failing) expect(failing_result).to be_kind_of(Exception) expect(failing_result.message).to eq "I break your system" end it "calls the default begin hook but not the default end hook" do expect(foo_plugin).to receive(:hook).with("failing_begin") expect(foo_plugin).to_not receive(:hook).with("failing_end") subject.send(:_supervise, foo_plugin, :failing) end end end describe ".stopping_symbol_for" do let(:guard_plugin) { instance_double("Guard::Plugin") } let(:group) { instance_double("Guard::Group", title: "Foo") } before do allow(guard_plugin).to receive(:group).and_return(group) end context "for a group with :halt_on_fail" do before do allow(group).to receive(:options).and_return(halt_on_fail: true) end it "returns :no_catch" do symbol = described_class.stopping_symbol_for(guard_plugin) expect(symbol).to eq :no_catch end end context "for a group without :halt_on_fail" do before do allow(group).to receive(:options).and_return(halt_on_fail: false) end it "returns :task_has_failed" do symbol = described_class.stopping_symbol_for(guard_plugin) expect(symbol).to eq :task_has_failed end end end end guard-2.18.1/spec/lib/guard/terminal_spec.rb000066400000000000000000000035501450001123100206470ustar00rootroot00000000000000# frozen_string_literal: true require "guard/terminal" RSpec.describe Guard::Terminal do subject { described_class } it { is_expected.to respond_to(:clear) } let(:sheller) { class_double("Shellany::Sheller") } before do stub_const("Shellany::Sheller", sheller) end describe ".clear" do context "when on UNIX" do before { allow(Gem).to receive(:win_platform?).and_return(false) } context "when the clear command exists" do let(:result) { [0, "\e[H\e[2J", ""] } it "clears the screen using 'clear'" do expect(sheller).to receive(:system).with("printf '\33c\e[3J';") .and_return(result) ::Guard::Terminal.clear end end context "when the clear command fails" do let(:result) { [nil, nil, 'Guard failed to run "clear;"'] } before do allow(sheller).to receive(:system).with("printf '\33c\e[3J';") .and_return(result) end it "fails" do expect { ::Guard::Terminal.clear } .to raise_error(Errno::ENOENT, /Guard failed to run "clear;"/) end end end context "when on Windows" do before { allow(Gem).to receive(:win_platform?).and_return(true) } it "clears the screen" do result = [0, "\f", ""] expect(sheller).to receive(:system).with("cls").and_return(result) ::Guard::Terminal.clear end context "when the clear command fails" do let(:result) { [nil, nil, 'Guard failed to run "cls"'] } before do allow(sheller).to receive(:system).with("cls").and_return(result) end it "fails" do expect { ::Guard::Terminal.clear } .to raise_error(Errno::ENOENT, /Guard failed to run "cls"/) end end end end end guard-2.18.1/spec/lib/guard/ui/000077500000000000000000000000001450001123100161075ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/ui/config_spec.rb000066400000000000000000000036041450001123100207160ustar00rootroot00000000000000# frozen_string_literal: true require "guard/ui/config" RSpec.describe Guard::UI::Config do describe "#device" do context "when not set" do context "when accessed as a method" do it "returns $stderr" do expect(subject.device).to be($stderr) end end context "when accessed as a string" do it "returns $stderr" do expect(subject["device"]).to be($stderr) end end context "when accessed as a symbol" do it "returns $stderr" do expect(subject[:device]).to be($stderr) end end end end describe "#logger_config" do let(:options) { {} } subject { described_class.new(options) } let(:logger_config) { instance_double("Guard::UI::Logger::Config") } before do allow(Guard::UI::Logger::Config).to receive(:new) .and_return(logger_config) end context "with defaults" do it "provides a logger config" do expect(subject.logger_config).to be(logger_config) end end context "with deprecated options set" do context "when set using a string" do subject { described_class.new('time_format': "foo") } it "passes deprecated options to logger" do expect(Guard::UI::Logger::Config).to receive(:new) .with(time_format: "foo") subject end it "provides a logger config" do expect(subject.logger_config).to be(logger_config) end end context "when set using a symbol" do let(:options) { { time_format: "foo" } } it "passes deprecated options to logger" do expect(Guard::UI::Logger::Config).to receive(:new) .with(time_format: "foo") subject end it "provides a logger config" do expect(subject.logger_config).to be(logger_config) end end end end end guard-2.18.1/spec/lib/guard/ui/logger_spec.rb000066400000000000000000000010031450001123100207170ustar00rootroot00000000000000# frozen_string_literal: true require "guard/ui/logger" RSpec.describe Guard::UI::Logger::Config do describe "defaults" do it "flushes device by default" do expect(subject[:flush_seconds]).to eq(0) end end describe "#level=" do context "with a valid value" do before do subject.level = Logger::WARN end it "stores the level" do expect(subject[:level]).to eq(Logger::WARN) expect(subject["level"]).to eq(Logger::WARN) end end end end guard-2.18.1/spec/lib/guard/ui_spec.rb000066400000000000000000000247141450001123100174560ustar00rootroot00000000000000# frozen_string_literal: true require "guard/notifier" # NOTE: this is here so that no UI does not require anything, # since it could be activated by guard plugins during development # (it they are tested by other guard plugins) # # TODO: regardless, the dependency on Guard.state should be removed # require "guard/ui" require "guard/internals/session" RSpec.describe Guard::UI do let(:interactor) { instance_double("Guard::Interactor") } let(:logger) { instance_double("Lumberjack::Logger") } let(:config) { instance_double("Guard::UI::Config") } let(:logger_config) { instance_double("Guard::UI::Logger::Config") } let(:terminal) { class_double("Guard::Terminal") } let(:session) { instance_double("Guard::Internals::Session") } let(:state) { instance_double("Guard::Internals::State") } let(:scope) { instance_double("Guard::Internals::Scope") } before do allow(state).to receive(:scope).and_return(scope) allow(state).to receive(:session).and_return(session) allow(Guard).to receive(:state).and_return(state) stub_const("Guard::Terminal", terminal) allow(Guard::Notifier).to receive(:turn_on) {} allow(Lumberjack::Logger).to receive(:new).and_return(logger) allow(Guard::UI::Config).to receive(:new).and_return(config) allow(Guard::UI::Logger::Config).to receive(:new).and_return(logger_config) # The spec helper stubs all UI classes, so other specs doesn't have # to explicit take care of it. We unstub and move the stubs one layer # down just for this spec. allow(Guard::UI).to receive(:info).and_call_original allow(Guard::UI).to receive(:warning).and_call_original allow(Guard::UI).to receive(:error).and_call_original allow(Guard::UI).to receive(:deprecation).and_call_original allow(Guard::UI).to receive(:debug).and_call_original allow(logger).to receive(:info) allow(logger).to receive(:warn) allow(logger).to receive(:error) allow(logger).to receive(:debug) allow(config).to receive(:device) allow(config).to receive(:only) allow(config).to receive(:except) allow(config).to receive(:logger_config).and_return(logger_config) allow($stderr).to receive(:print) end before do Guard::UI.options = nil end after do Guard::UI.reset_logger Guard::UI.options = nil end describe ".logger" do before do allow(config).to receive(:device).and_return(device) end context "with no logger set yet" do let(:device) { "foo.log" } it "returns the logger instance" do expect(Guard::UI.logger).to be(logger) end it "sets the logger device" do expect(Lumberjack::Logger).to receive(:new) .with(device, logger_config) Guard::UI.logger end end end describe ".level=" do before do allow(logger).to receive(:level=) allow(logger_config).to receive(:level=) end context "when logger is set up" do before { Guard::UI.logger } it "sets the logger's level" do level = Logger::WARN expect(logger).to receive(:level=).with(level) Guard::UI.level = level end it "sets the logger's config level" do level = Logger::WARN expect(logger_config).to receive(:level=).with(level) Guard::UI.level = level end end context "when logger is not set up yet" do before { Guard::UI.reset_logger } it "sets the logger's config level" do level = Logger::WARN expect(logger_config).to receive(:level=).with(level) Guard::UI.level = level end it "does not autocreate the logger" do level = Logger::WARN expect(logger).to_not receive(:level=).with(level) Guard::UI.level = level end end end describe ".options=" do let(:new_config) { instance_double("Guard::UI::Config") } before do allow(Guard::UI::Config).to receive(:new).with(hi: :ho) .and_return(new_config) allow(new_config).to receive(:[]).with(:hi).and_return(:ho) end it "sets the logger options" do Guard::UI.options = { hi: :ho } expect(Guard::UI.options[:hi]).to eq :ho end end shared_examples_for "a logger method" do it "resets the line with the :reset option" do expect(Guard::UI).to receive :reset_line Guard::UI.send(ui_method, input, reset: true) end it "logs the message with the given severity" do expect(logger).to receive(severity).with(output) Guard::UI.send(ui_method, input) end context "with the :only option" do before { allow(config).to receive(:only).and_return(/A/) } it "allows logging matching messages" do expect(logger).to receive(severity).with(output) Guard::UI.send(ui_method, input, plugin: "A") end it "prevents logging other messages" do expect(logger).to_not receive(severity) Guard::UI.send(ui_method, input, plugin: "B") end end context "with the :except option" do before { allow(config).to receive(:except).and_return(/A/) } it "prevents logging matching messages" do expect(logger).to_not receive(severity) Guard::UI.send(ui_method, input, plugin: "A") end it "allows logging other messages" do expect(logger).to receive(severity).with(output) Guard::UI.send(ui_method, input, plugin: "B") end end end describe ".info" do it_behaves_like "a logger method" do let(:ui_method) { :info } let(:severity) { :info } let(:input) { "Info" } let(:output) { "Info" } end end describe ".warning" do it_behaves_like "a logger method" do let(:ui_method) { :warning } let(:severity) { :warn } let(:input) { "Warning" } let(:output) { "\e[0;33mWarning\e[0m" } end end describe ".error" do it_behaves_like "a logger method" do let(:ui_method) { :error } let(:severity) { :error } let(:input) { "Error" } let(:output) { "\e[0;31mError\e[0m" } end end describe ".deprecation" do before do allow(ENV).to receive(:[]).with("GUARD_GEM_SILENCE_DEPRECATIONS") .and_return(value) end context "with GUARD_GEM_SILENCE_DEPRECATIONS set to 1" do let(:value) { "1" } it "silences deprecations" do expect(Guard::UI.logger).to_not receive(:warn) Guard::UI.deprecation "Deprecator message" end end context "with GUARD_GEM_SILENCE_DEPRECATIONS unset" do let(:value) { nil } it_behaves_like "a logger method" do let(:ui_method) { :deprecation } let(:severity) { :warn } let(:input) { "Deprecated" } let(:output) do /^\e\[0;33mDeprecated\nDeprecation backtrace: .*\e\[0m$/m end end end end describe ".debug" do it_behaves_like "a logger method" do let(:ui_method) { :debug } let(:severity) { :debug } let(:input) { "Debug" } let(:output) { "\e[0;33mDebug\e[0m" } end end describe ".clear" do context "with UI set up and ready" do before do allow(session).to receive(:clear?).and_return(false) Guard::UI.reset_and_clear end context "when clear option is disabled" do it "does not clear the output" do expect(terminal).to_not receive(:clear) Guard::UI.clear end end context "when clear option is enabled" do before do allow(session).to receive(:clear?).and_return(true) end context "when the screen is marked as needing clearing" do before { Guard::UI.clearable } it "clears the output" do expect(terminal).to receive(:clear) Guard::UI.clear end it "clears the output only once" do expect(terminal).to receive(:clear).once Guard::UI.clear Guard::UI.clear end context "when the command fails" do before do allow(terminal).to receive(:clear) .and_raise(Errno::ENOENT, "failed to run command") end it "shows a warning" do expect(logger).to receive(:warn) do |arg| expect(arg).to match(/failed to run command/) end Guard::UI.clear end end end context "when the screen has just been cleared" do before { Guard::UI.clear } it "does not clear" do expect(terminal).to_not receive(:clear) Guard::UI.clear end context "when forced" do let(:opts) { { force: true } } it "clears the outputs if forced" do expect(terminal).to receive(:clear) Guard::UI.clear(opts) end end end end end end describe ".action_with_scopes" do let(:rspec) { double("Rspec", title: "Rspec") } let(:jasmine) { double("Jasmine", title: "Jasmine") } let(:group) { instance_double("Guard::Group", title: "Frontend") } context "with a plugins scope" do it "shows the plugin scoped action" do allow(scope).to receive(:titles).with(plugins: [rspec, jasmine]) .and_return(%w[Rspec Jasmine]) expect(Guard::UI).to receive(:info).with("Reload Rspec, Jasmine") Guard::UI.action_with_scopes("Reload", plugins: [rspec, jasmine]) end end context "with a groups scope" do it "shows the group scoped action" do allow(scope).to receive(:titles).with(groups: [group]) .and_return(%w[Frontend]) expect(Guard::UI).to receive(:info).with("Reload Frontend") Guard::UI.action_with_scopes("Reload", groups: [group]) end end context "without a scope" do context "with a global plugin scope" do it "shows the global plugin scoped action" do allow(scope).to receive(:titles).and_return(%w[Rspec Jasmine]) expect(Guard::UI).to receive(:info).with("Reload Rspec, Jasmine") Guard::UI.action_with_scopes("Reload", {}) end end context "with a global group scope" do it "shows the global group scoped action" do allow(scope).to receive(:titles).and_return(%w[Frontend]) expect(Guard::UI).to receive(:info).with("Reload Frontend") Guard::UI.action_with_scopes("Reload", {}) end end end end end guard-2.18.1/spec/lib/guard/watcher/000077500000000000000000000000001450001123100171275ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/watcher/pattern/000077500000000000000000000000001450001123100206045ustar00rootroot00000000000000guard-2.18.1/spec/lib/guard/watcher/pattern/deprecated_regexp_spec.rb000066400000000000000000000015351450001123100256210ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern/deprecated_regexp" RSpec.describe Guard::Watcher::Pattern::DeprecatedRegexp do describe ".deprecated?" do specify { expect(described_class.new("^spec_helper.rb")).to be_deprecated } specify { expect(described_class.new("spec_helper.rb$")).to be_deprecated } end describe "Matcher returned by .convert" do let(:matcher) { Guard::Watcher::Pattern::Matcher } before { allow(matcher).to receive(:new) } { "^foo.rb" => /^foo.rb/, "foo.rb$" => /foo.rb$/, 'foo\.rb' => /foo\.rb/, ".*rb" => /.*rb/ }.each do |pattern, regexp| context "with #{pattern}" do it "creates a Matcher with #{regexp}" do expect(matcher).to receive(:new).with(regexp) described_class.convert(pattern) end end end end end guard-2.18.1/spec/lib/guard/watcher/pattern/match_result_spec.rb000066400000000000000000000024211450001123100246340ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern/match_result" RSpec.describe Guard::Watcher::Pattern::MatchResult do let(:match_result) { double("match_data") } let(:original_value) { "foo/bar.rb" } subject { described_class.new(match_result, original_value) } describe "#initialize" do context "with valid arguments" do it "does not fail" do expect { subject }.to_not raise_error end end end describe "#[]" do context "with a valid match" do let(:match_result) { double("match_data", to_a: %w[foo bar baz]) } context "when asked for the non-first item" do let(:index) { 1 } it "returns the value at given index" do expect(subject[index]).to eq("bar") end end context "when asked for the first item" do let(:index) { 0 } it "returns the full original value" do expect(subject[index]).to eq("foo/bar.rb") end end context "when asked for a name match via a symbol" do let(:index) { :foo } before do allow(match_result).to receive(:[]).with(:foo).and_return("baz") end it "returns the value by name" do expect(subject[index]).to eq("baz") end end end end end guard-2.18.1/spec/lib/guard/watcher/pattern/matcher_spec.rb000066400000000000000000000054241450001123100235730ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern/matcher" RSpec.describe Guard::Watcher::Pattern::Matcher do subject { described_class.new(obj) } describe "#match" do let(:expected) { double("match_result") } context "when constructed with valid matcher object" do let(:obj) { double("matcher") } context "when matched against a Pathname" do before do allow(obj).to receive(:match).and_return(expected) end let(:filename) { Pathname("foo.rb") } it "returns the match result" do expect(subject.match(filename)).to be(expected) end it "passes the Pathname to the matcher" do allow(obj).to receive(:match).with(filename) subject.match(filename) end end context "when matched against a String" do before do allow(obj).to receive(:match).and_return(expected) end let(:filename) { "foo.rb" } it "returns the match result" do expect(subject.match(filename)).to be(expected) end it "passes a Pathname to the matcher" do allow(obj).to receive(:match).with(Pathname(filename)) subject.match(filename) end end end end describe "#==" do it "returns true for equal matchers" do expect(described_class.new(/spec_helper\.rb/)) .to eq(described_class.new(/spec_helper\.rb/)) end it "returns false for unequal matchers" do expect(described_class.new(/spec_helper\.rb/)) .not_to eq(described_class.new(/spec_helper\.r/)) end end describe "integration" do describe "#match result" do subject { described_class.new(obj).match(filename) } context "when constructed with valid regexp" do let(:obj) { /foo.rb$/ } context "when matched file is a string" do context "when filename matches" do let(:filename) { "foo.rb" } specify { expect(subject.to_a).to eq(["foo.rb"]) } end context "when filename does not match" do let(:filename) { "bar.rb" } specify { expect(subject).to be_nil } end end context "when matched file is an unclean Pathname" do context "when filename matches" do let(:filename) { Pathname("./foo.rb") } specify { expect(subject.to_a).to eq(["foo.rb"]) } end context "when filename does not match" do let(:filename) { Pathname("./bar.rb") } specify { expect(subject).to be_nil } end end context "when matched file contains a $" do let(:filename) { Pathname("lib$/foo.rb") } specify { expect(subject.to_a).to eq(["foo.rb"]) } end end end end end guard-2.18.1/spec/lib/guard/watcher/pattern/pathname_path_spec.rb000066400000000000000000000022001450001123100247460ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern/pathname_path" RSpec.describe Guard::Watcher::Pattern::PathnamePath do subject { described_class.new(path) } describe "#match result" do subject { described_class.new(path).match(filename) } context "when constructed with an unclean Pathname" do let(:path) { Pathname("./foo.rb") } context "when matched file is a string" do context "when filename matches" do let(:filename) { "foo.rb" } specify { expect(subject).to eq([Pathname("foo.rb")]) } end context "when filename does not match" do let(:filename) { "bar.rb" } specify { expect(subject).to be_nil } end end context "when matched file is an unclean Pathname" do context "when filename matches" do let(:filename) { Pathname("./foo.rb") } specify { expect(subject).to eq([Pathname("foo.rb")]) } end context "when filename does not match" do let(:filename) { Pathname("./bar.rb") } specify { expect(subject).to be_nil } end end end end end guard-2.18.1/spec/lib/guard/watcher/pattern/simple_path_spec.rb000066400000000000000000000021371450001123100244530ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern/simple_path" RSpec.describe Guard::Watcher::Pattern::SimplePath do subject { described_class.new(path) } describe "#match result" do context "when constructed with filename string" do let(:path) { "foo.rb" } context "when matched file is a string" do context "when filename matches" do let(:filename) { "foo.rb" } specify { expect(subject.match(filename)).to eq(["foo.rb"]) } end context "when filename does not match" do let(:filename) { "bar.rb" } specify { expect(subject.match(filename)).to be_nil } end end context "when matched file is an unclean Pathname" do context "when filename matches" do let(:filename) { Pathname("./foo.rb") } specify { expect(subject.match(filename)).to eq(["foo.rb"]) } end context "when filename does not match" do let(:filename) { Pathname("./bar.rb") } specify { expect(subject.match(filename)).to be_nil } end end end end end guard-2.18.1/spec/lib/guard/watcher/pattern_spec.rb000066400000000000000000000021171450001123100221440ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher/pattern" RSpec.describe Guard::Watcher::Pattern do describe ".create" do subject { described_class.create(pattern) } context "when a string is given" do let(:pattern) { "foo.rb" } it { is_expected.to be_a(described_class::SimplePath) } end context "when a Pathname is given" do let(:pattern) { Pathname("foo.rb") } it { is_expected.to be_a(described_class::PathnamePath) } end context "when an regexp string is given" do let(:pattern) { "^foo.*$" } it { is_expected.to be_a(described_class::Matcher) } it "shows a warning" do expect(described_class::DeprecatedRegexp) .to receive(:show_deprecation).with(pattern) subject end end context "when a regexp is given" do let(:pattern) { /foo\.rb/ } it { is_expected.to be_a(described_class::Matcher) } end context "when a custom matcher" do let(:pattern) { Class.new { def match; end } } it { is_expected.to be_a(described_class::Matcher) } end end end guard-2.18.1/spec/lib/guard/watcher_spec.rb000066400000000000000000000271401450001123100204720ustar00rootroot00000000000000# frozen_string_literal: true require "guard/watcher" # TODO: shouldn't be needed require "guard/guardfile/evaluator" RSpec.describe Guard::Watcher do let(:args) { [] } subject { described_class.new(*args) } describe "#initialize" do context "with no arguments" do let(:args) { [] } it "raises an error" do expect { subject }.to raise_error(ArgumentError) end end context "with a pattern parameter" do let(:pattern) { ["spec_helper.rb"] } let(:args) { [pattern] } it "creates a matcher" do expect(described_class::Pattern).to receive(:create).with(pattern) subject end end end describe "#action" do it "sets the action to nothing by default" do expect(described_class.new(/spec_helper\.rb/).action).to be_nil end it "sets the action to the supplied block" do action = ->(m) { "spec/#{m[1]}_spec.rb" } expect(described_class.new(%r{^lib/(.*).rb}, action).action).to eq action end end describe "#==" do it "returns true for equal watchers" do expect(described_class.new(/spec_helper\.rb/)) .to eq(described_class.new(/spec_helper\.rb/)) end it "returns false for unequal watchers" do expect(described_class.new(/spec_helper\.rb/)) .not_to eq(described_class.new(/spec_helper\.r/)) end end describe ".match_files" do let(:plugin) { instance_double("Guard::Plugin", options: {}) } def matched(files) described_class.match_files(plugin, files) end context "without a watcher action" do before do allow(plugin).to receive(:watchers) .and_return([described_class.new(pattern)]) end context "with a regex pattern" do let(:pattern) { /.*_spec\.rb/ } it "returns the paths that matches the regex" do expect(matched(%w[foo_spec.rb foo.rb])).to eq %w[foo_spec.rb] end end context "with a string pattern" do let(:pattern) { "foo_spec.rb" } it "returns the path that matches the string" do expect(matched(%w[foo_spec.rb foo.rb])).to eq ["foo_spec.rb"] end end end context "with a watcher action without parameter" do context "for a watcher that matches file strings" do before do klass = described_class allow(plugin).to receive(:watchers).and_return( [ klass.new("spec_helper.rb", -> { "spec" }), klass.new("addition.rb", -> { 1 + 1 }), klass.new("hash.rb", -> { Hash[:foo, "bar"] }), klass.new("array.rb", -> { %w[foo bar] }), klass.new("blank.rb", -> { "" }), klass.new(/^uptime\.rb/, -> { "" }) ] ) end it "returns a single file specified within the action" do expect(matched(%w[spec_helper.rb])).to eq ["spec"] end it "returns multiple files specified within the action" do expect(matched(%w[hash.rb])).to eq %w[foo bar] end it "combines files from results of different actions" do expect(matched(%w[spec_helper.rb array.rb])).to eq %w[spec foo bar] end context "when action returns non-string or array of non-strings" do it "returns nothing" do expect(matched(%w[addition.rb])).to eq [] end end it "returns nothing if the action response is empty" do expect(matched(%w[blank.rb])).to eq [] end it "returns nothing if the action returns nothing" do expect(matched(%w[uptime.rb])).to eq [] end end context "for a watcher that matches information objects" do before do allow(plugin).to receive(:options).and_return(any_return: true) klass = described_class allow(plugin).to receive(:watchers).and_return( [ klass.new("spec_helper.rb", -> { "spec" }), klass.new("addition.rb", -> { 1 + 1 }), klass.new("hash.rb", -> { Hash[:foo, "bar"] }), klass.new("array.rb", -> { %w[foo bar] }), klass.new("blank.rb", -> { "" }), klass.new(/^uptime\.rb/, -> { "" }) ] ) end it "returns a single file specified within the action" do expect(matched(%w[spec_helper.rb]).class).to be Array expect(matched(%w[spec_helper.rb])).to_not be_empty end it "returns multiple files specified within the action" do expect(matched(%w[hash.rb])).to eq [{ foo: "bar" }] end it "combines the results of different actions" do expect(matched(%w[spec_helper.rb array.rb])) .to eq ["spec", %w[foo bar]] end it "returns the evaluated addition argument in an array" do expect(matched(%w[addition.rb]).class).to be(Array) expect(matched(%w[addition.rb])[0]).to eq 2 end it "returns nothing if the action response is empty string" do expect(matched(%w[blank.rb])).to eq [""] end it "returns nothing if the action returns empty string" do expect(matched(%w[uptime.rb])).to eq [""] end end end context "with a watcher action that takes a parameter" do context "for a watcher that matches file strings" do before do klass = described_class allow(plugin).to receive(:watchers).and_return [ klass.new(%r{lib/(.*)\.rb}, ->(m) { "spec/#{m[1]}_spec.rb" }), klass.new(/addition(.*)\.rb/, ->(_m) { 1 + 1 }), klass.new("hash.rb", ->(_m) { Hash[:foo, "bar"] }), klass.new(/array(.*)\.rb/, ->(_m) { %w[foo bar] }), klass.new(/blank(.*)\.rb/, ->(_m) { "" }), klass.new(/^uptime\.rb/, -> { "" }) ] end it "returns a substituted single file specified within the action" do expect(matched(%w[lib/foo.rb])).to eq ["spec/foo_spec.rb"] end it "returns multiple files specified within the action" do expect(matched(%w[hash.rb])).to eq %w[foo bar] end it "combines results of different actions" do expect(matched(%w[lib/foo.rb array.rb])) .to eq %w[spec/foo_spec.rb foo bar] end it "returns nothing if action returns non-string or non-string array" do expect(matched(%w[addition.rb])).to eq [] end it "returns nothing if the action response is empty" do expect(matched(%w[blank.rb])).to eq [] end it "returns nothing if the action returns nothing" do expect(matched(%w[uptime.rb])).to eq [] end end context "for a watcher that matches information objects" do before do allow(plugin).to receive(:options).and_return(any_return: true) kl = described_class allow(plugin).to receive(:watchers).and_return( [ kl.new(%r{lib/(.*)\.rb}, ->(m) { "spec/#{m[1]}_spec.rb" }), kl.new(/addition(.*)\.rb/, ->(m) { (1 + 1).to_s + m[0] }), kl.new("hash.rb", ->(m) { { foo: "bar", file_name: m[0] } }), kl.new(/array(.*)\.rb/, ->(m) { ["foo", "bar", m[0]] }), kl.new(/blank(.*)\.rb/, ->(_m) { "" }), kl.new(/^uptime\.rb/, -> { "" }) ] ) end it "returns a substituted single file specified within the action" do expect(matched(%w[lib/foo.rb])).to eq %w[spec/foo_spec.rb] end it "returns a hash specified within the action" do expect(matched(%w[hash.rb])).to eq [ { foo: "bar", file_name: "hash.rb" } ] end it "combinines results of different actions" do expect(matched(%w[lib/foo.rb array.rb])) .to eq ["spec/foo_spec.rb", %w[foo bar array.rb]] end it "returns the evaluated addition argument + the path" do expect(matched(%w[addition.rb])).to eq ["2addition.rb"] end it "returns nothing if the action response is empty string" do expect(matched(%w[blank.rb])).to eq [""] end it "returns nothing if the action returns is IO::NULL" do expect(matched(%w[uptime.rb])).to eq [""] end end end context "with an exception that is raised" do before do allow(plugin).to receive(:watchers).and_return( [described_class.new("evil.rb", -> { fail "EVIL" })] ) end it "displays the error and backtrace" do expect(Guard::UI).to receive(:error) do |msg| expect(msg).to include("Problem with watch action!") expect(msg).to include("EVIL") end described_class.match_files(plugin, ["evil.rb"]) end end context "for ambiguous watchers" do before do expect(plugin).to receive(:watchers).and_return [ described_class.new("awesome_helper.rb", -> {}), described_class.new(/.+some_helper.rb/, -> { "foo.rb" }), described_class.new(/.+_helper.rb/, -> { "bar.rb" }) ] end context "when the :first_match option is turned off" do before do allow(plugin).to receive(:options).and_return(first_match: false) end it "returns multiple files by combining the results of the watchers" do expect(described_class.match_files( plugin, ["awesome_helper.rb"] )).to eq(["foo.rb", "bar.rb"]) end end context "when the :first_match option is turned on" do before do plugin.options[:first_match] = true end it "returns only the files from the first watcher" do expect(described_class.match_files( plugin, ["awesome_helper.rb"] )).to eq(["foo.rb"]) end end end end describe "#match" do subject { described_class.new(pattern).match(file) } let(:matcher) { instance_double(described_class::Pattern::Matcher) } let(:match) { instance_double(described_class::Pattern::MatchResult) } before do allow(described_class::Pattern).to receive(:create).with(pattern) .and_return(matcher) allow(matcher).to receive(:match).with(pattern) .and_return(match_data) allow(described_class::Pattern::MatchResult).to receive(:new) .with(match_data, file).and_return(match) end context "with a valid pattern" do let(:pattern) { "foo.rb" } context "with a valid file name to match" do let(:file) { "foo.rb" } context "when matching is successful" do let(:match_data) { double("match data", to_a: ["foo"]) } it "returns the match result" do expect(subject).to be(match) end end context "when matching is not successful" do let(:match_data) { nil } it "returns nil" do expect(subject).to be_nil end end end end end describe "integration" do describe "#match" do subject { described_class.new(pattern) } context "with a named regexp pattern" do let(:pattern) { /(?.*)_spec\.rb/ } context "with a watcher that matches a file" do specify do expect(subject.match("bar_spec.rb")[0]).to eq("bar_spec.rb") expect(subject.match("bar_spec.rb")[1]).to eq("bar") end it "provides the match by name" do expect(subject.match("bar_spec.rb")[:foo]).to eq("bar") end end end end end end guard-2.18.1/spec/lib/guard_spec.rb000066400000000000000000000175571450001123100170500ustar00rootroot00000000000000# frozen_string_literal: true require "guard" RSpec.describe Guard do # Initialize before Guard::Interactor const is stubbed let!(:interactor) { instance_double("Guard::Interactor") } let(:guardfile) { File.expand_path("Guardfile") } let(:traps) { Guard::Internals::Traps } let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") } let(:plugins) { instance_double("Guard::Internals::Plugins") } let(:scope) { instance_double("Guard::Internals::Scope") } let(:session) { instance_double("Guard::Internals::Session") } let(:state) { instance_double("Guard::Internals::State") } let(:queue) { instance_double("Guard::Internals::Queue") } before do allow(Guard::Interactor).to receive(:new).and_return(interactor) allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) allow(session).to receive(:debug?).and_return(false) allow(session).to receive(:plugins).and_return(plugins) allow(state).to receive(:session).and_return(session) allow(Guard::Internals::Session).to receive(:new).and_return(session) allow(Guard::Internals::Scope).to receive(:new).and_return(scope) allow(Guard::Internals::Queue).to receive(:new).and_return(queue) end # TODO: setup has too many responsibilities describe ".setup" do subject { Guard.setup(options) } let(:options) { { my_opts: true, guardfile: guardfile } } let(:listener) { instance_double("Listen::Listener") } before do allow(Listen).to receive(:to).with(Dir.pwd, {}) { listener } stub_guardfile(" ") stub_user_guard_rb g1 = instance_double("Guard::Group", name: :common, options: {}) g2 = instance_double("Guard::Group", name: :default, options: {}) allow(Guard::Group).to receive(:new).with(:common).and_return(g1) allow(Guard::Group).to receive(:new).with(:default).and_return(g2) allow(evaluator).to receive(:inline?).and_return(false) allow(evaluator).to receive(:custom?).and_return(false) allow(evaluator).to receive(:evaluate) allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) allow(Guard::Notifier).to receive(:connect) allow(Guard::UI).to receive(:reset_and_clear) allow(plugins).to receive(:all).and_return([]) allow(session).to receive(:listener_args).and_return([:to, Dir.pwd, {}]) allow(session).to receive(:evaluator_options).and_return({}) allow(session).to receive(:cmdline_groups).and_return({}) allow(session).to receive(:cmdline_plugins).and_return({}) allow(session).to receive(:notify_options).and_return(notify: true) allow(session).to receive(:interactor_name).and_return(:foo) allow(session).to receive(:guardfile_ignore).and_return([]) allow(session).to receive(:guardfile_ignore_bang).and_return([]) allow(listener).to receive(:ignore) allow(listener).to receive(:ignore!) allow(Guard::Internals::State).to receive(:new).and_return(state) end it "returns itself for chaining" do expect(subject).to be Guard end it "initializes the listener" do allow(Listen).to receive(:to) .with("/foo", latency: 2, wait_for_delay: 1).and_return(listener) allow(session).to receive(:listener_args).and_return( [:to, "/foo", { latency: 2, wait_for_delay: 1 }] ) subject end it "initializes the interactor" do expect(Guard::Interactor).to receive(:new).with(false) subject end context "trapping signals" do before do allow(traps).to receive(:handle) end it "sets up USR1 trap for pausing" do expect(traps).to receive(:handle).with("USR1") { |_, &b| b.call } expect(Guard).to receive(:async_queue_add) .with(%i[guard_pause paused]) subject end it "sets up USR2 trap for unpausing" do expect(traps).to receive(:handle).with("USR2") { |_, &b| b.call } expect(Guard).to receive(:async_queue_add) .with(%i[guard_pause unpaused]) subject end it "sets up INT trap for cancelling or quitting interactor" do expect(traps).to receive(:handle).with("INT") { |_, &b| b.call } expect(interactor).to receive(:handle_interrupt) subject end end it "evaluates the Guardfile" do expect(evaluator).to receive(:evaluate) allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator) subject end describe "listener" do subject { listener } context "with ignores 'ignore(/foo/)' and 'ignore!(/bar/)'" do before do allow(evaluator).to receive(:evaluate) do allow(session).to receive(:guardfile_ignore).and_return([/foo/]) allow(session).to receive(:guardfile_ignore_bang) .and_return([/bar/]) end Guard.setup(options) end it { is_expected.to have_received(:ignore).with([/foo/]) } it { is_expected.to have_received(:ignore!).with([/bar/]) } end context "without ignores" do before { Guard.setup(options) } it { is_expected.to_not have_received(:ignore) } it { is_expected.to_not have_received(:ignore!) } end end it "displays an error message when no guard are defined in Guardfile" do expect(Guard::UI).to receive(:error) .with("No plugins found in Guardfile, please add at least one.") subject end it "connects to the notifier" do expect(Guard::Notifier).to receive(:connect).with(notify: true) subject end context "with the group option" do let(:options) { { group: %w[frontend backend] } } it "passes options to session" do expect(Guard::Internals::State).to receive(:new).with(options) subject end end context "with the plugin option" do let(:options) { { plugin: %w[cucumber jasmine] } } it "passes options to session" do expect(Guard::Internals::State).to receive(:new).with(options) subject end end describe ".interactor" do subject { Guard::Interactor } before do expect(session).to receive(:interactor_name).and_return(type) Guard.setup(options) end context "with interactions enabled" do let(:type) { :pry_wrapper } let(:options) { { no_interactions: false } } it { is_expected.to have_received(:new).with(false) } end context "with interactions disabled" do let(:type) { :sleep } let(:options) { { no_interactions: true } } it { is_expected.to have_received(:new).with(true) } end end describe "UI" do subject { Guard::UI } context "when clearing is configured" do before { Guard.setup(options) } it { is_expected.to have_received(:reset_and_clear) } end end end describe "._relative_pathname" do subject { Guard.send(:_relative_pathname, raw_path) } let(:pwd) { Pathname("/project") } before { allow(Pathname).to receive(:pwd).and_return(pwd) } context "with file in project directory" do let(:raw_path) { "/project/foo" } it { is_expected.to eq(Pathname("foo")) } end context "with file within project" do let(:raw_path) { "/project/spec/models/foo_spec.rb" } it { is_expected.to eq(Pathname("spec/models/foo_spec.rb")) } end context "with file in parent directory" do let(:raw_path) { "/foo" } it { is_expected.to eq(Pathname("../foo")) } end context "with file on another drive (e.g. Windows)" do let(:raw_path) { "d:/project/foo" } let(:pathname) { instance_double(Pathname) } before do allow_any_instance_of(Pathname).to receive(:relative_path_from) .with(pwd).and_raise(ArgumentError) end it { is_expected.to eq(Pathname.new("d:/project/foo")) } end end describe "#relevant_changes?" do pending end end guard-2.18.1/spec/spec_helper.rb000066400000000000000000000252141450001123100164440ustar00rootroot00000000000000# frozen_string_literal: true # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause # this file to always be loaded, without a need to explicitly require it in any # files. # # Given that it is always loaded, you are encouraged to keep this file as # light-weight as possible. Requiring heavyweight dependencies from this file # will add to the boot time of your test suite on EVERY test run, even for an # individual file that may not need all of that loaded. Instead, consider making # a separate helper file that requires the additional dependencies and performs # the additional setup, and require it from the spec files that actually need # it. # # The `.rspec` file also contains a few flags that are not defaults but that # users commonly want. # require "fileutils" require "simplecov" SimpleCov.start do add_filter "/spec" end ENV["GUARD_SPECS_RUNNING"] = "1" path = "#{File.expand_path(__dir__)}/support/**/*.rb" Dir[path].each { |f| require f } # TODO: these shouldn't be necessary with proper specs def stub_pathname allow(Pathname).to receive(:new).with(anything) do |*args, &_block| caller.each { |l| puts l } abort "stub me! (Pathname.new(#{args.map(&:inspect) * ', '}))" end end def stub_guardfile(contents = nil, &block) stub_file("Guardfile", contents, &block) end def stub_guardfile_rb(contents = nil, &block) stub_file("guardfile.rb", contents, &block) end def stub_user_guardfile(contents = nil, &block) stub_file("~/.Guardfile", contents, &block) end def stub_user_guard_rb(contents = nil, &block) stub_file("~/.guard.rb", contents, &block) end def stub_user_project_guardfile(contents = nil, &block) stub_file(".Guardfile", contents, &block) end def stub_mod(mod, excluded) mod.constants.each do |klass_name| klass = mod.const_get(klass_name) if klass.is_a?(Class) unless klass == described_class unless excluded.include?(klass) inst = instance_double(klass) allow(klass).to receive(:new).and_return(inst) # TODO: use object_double? class_double(klass.to_s) .as_stubbed_const(transfer_nested_constants: true) end end elsif klass.is_a?(Module) stub_mod(klass, excluded) end end end def stub_file(path, contents = nil, &block) exists = !contents.nil? pathname = instance_double(Pathname) allow(Pathname).to receive(:new).with(path).and_return(pathname) allow(pathname).to receive(:to_s).and_return(path) allow(pathname).to receive(:expand_path).and_return(pathname) allow(pathname).to receive(:exist?).and_return(exists) if exists if block.nil? allow(pathname).to receive(:read).and_return(contents) else allow(pathname).to receive(:read) do yield end end else allow(pathname).to receive(:read) do fail Errno::ENOENT end end pathname end # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. mocks.verify_partial_doubles = true end # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. # These two settings work together to allow you to limit a spec run # to individual examples or groups you care about by tagging them with # `:focus` metadata. When nothing is tagged with `:focus`, all examples # get run. # config.filter_run :focus config.filter_run focus: ENV["CI"] != "true" config.run_all_when_everything_filtered = true # Limits the available syntax to the non-monkey patched syntax that is # recommended. # # For more details, see: # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. # config.warnings = true # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). # # This is set in .rspec file # config.default_formatter = "doc" end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. # config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed config.raise_errors_for_deprecations! config.mock_with :rspec do |mocks| mocks.verify_doubled_constant_names = true mocks.verify_partial_doubles = true end config.before(:each) do |example| stub_const("FileUtils", class_double(FileUtils)) excluded = [] excluded += Array(example.metadata[:exclude_stubs]) excluded << Guard::Config if Guard.constants.include?(:Config) excluded << Guard::Options if Guard.constants.include?(:Options) excluded << Guard::Jobs::Base if Guard.constants.include?(:Jobs) excluded << Guard::DuMmy if Guard.constants.include?(:DuMmy) if Guard.constants.include?(:Notifier) if Guard::Notifier.constants.include?(:NotServer) excluded << Guard::Notifier::NotServer end if Guard::Notifier.constants.include?(:FooBar) excluded << Guard::Notifier::FooBar end if Guard::Notifier.constants.include?(:Base) excluded << Guard::Notifier::Base end end modules = [Guard] modules << Listen if Object.const_defined?(:Listen) modules << Shellany if Object.const_defined?(:Shellany) modules << Notiffany if Object.const_defined?(:Notiffany) modules.each do |mod| stub_mod(mod, excluded) end allow(ENV).to receive(:[]=) do |*args| abort "stub me: ENV[#{args.first}]= #{args.map(&:inspect)[1..-1] * ','}!" end allow(ENV).to receive(:[]) do |*args| abort "stub me: ENV[#{args.first}]!" end allow(ENV).to receive(:key?) do |*args| fail "stub me: ENV.key?(#{args.first})!" end # NOTE: call original, so we can run tests depending on this variable allow(ENV).to receive(:[]).with("GUARD_STRICT").and_call_original # FIXME: instead, properly stub PluginUtil in the evaluator specs! # and remove this! allow(ENV).to receive(:[]).with("SPEC_OPTS").and_call_original # FIXME: properly stub out Pry instead of this! allow(ENV).to receive(:[]).with("ANSICON").and_call_original allow(ENV).to receive(:[]).with("TERM").and_call_original # Needed for debugging allow(ENV).to receive(:[]).with("DISABLE_PRY").and_call_original allow(ENV).to receive(:[]).with("PRYRC").and_call_original allow(ENV).to receive(:[]).with("PAGER").and_call_original # Workarounds for Cli inheriting from Thor allow(ENV).to receive(:[]).with("ANSICON").and_call_original allow(ENV).to receive(:[]).with("THOR_SHELL").and_call_original allow(ENV).to receive(:[]).with("GEM_SKIP").and_call_original %w[read write exist?].each do |meth| allow(File).to receive(meth.to_sym).with(anything) do |*args, &_block| abort "stub me! (File.#{meth}(#{args.map(&:inspect).join(', ')}))" end end %w[read write binwrite binread].each do |meth| allow(IO).to receive(meth.to_sym).with(anything) do |*args, &_block| abort "stub me! (IO.#{meth}(#{args.map(&:inspect).join(', ')}))" end end %w[exist?].each do |meth| allow_any_instance_of(Pathname) .to receive(meth.to_sym) do |*args, &_block| obj = args.first formatted_args = args[1..-1].map(&:inspect).join(", ") abort "stub me! (#{obj.inspect}##{meth}(#{formatted_args}))" end end allow(Dir).to receive(:exist?).with(anything) do |*args, &_block| abort "stub me! (Dir#exist?(#{args.map(&:inspect) * ', '}))" end if Guard.const_defined?("UI") && Guard::UI.respond_to?(:info) # Stub all UI methods, so no visible output appears for the UI class allow(Guard::UI).to receive(:info) allow(Guard::UI).to receive(:warning) allow(Guard::UI).to receive(:error) allow(Guard::UI).to receive(:debug) allow(Guard::UI).to receive(:deprecation) end allow(Kernel).to receive(:system) do |*args| fail "stub for Kernel.system() called with: #{args.inspect}" end # TODO: use metadata to stub out all used classes if Guard.const_defined?("Sheller") unless example.metadata[:sheller_specs] allow(Guard::Sheller).to receive(:run) do |*args| fail "stub for Sheller.run() called with: #{args.inspect}" end end end end config.after(:each) do # Reset everything (Guard.constants + [Guard]).each do |klass| klass.instance_variables.each do |var| klass.instance_variable_set(var, nil) end end end end guard-2.18.1/spec/support/000077500000000000000000000000001450001123100153365ustar00rootroot00000000000000guard-2.18.1/spec/support/gems_helper.rb000066400000000000000000000004211450001123100201520ustar00rootroot00000000000000# frozen_string_literal: true def growl_installed? require "growl" true rescue LoadError false end def libnotify_installed? require "libnotify" true rescue LoadError false end def rbnotifu_installed? require "rb-notifu" true rescue LoadError false end guard-2.18.1/spec/support/platform_helper.rb000066400000000000000000000003311450001123100210430ustar00rootroot00000000000000# frozen_string_literal: true def mac? RbConfig::CONFIG["target_os"] =~ /darwin/i end def linux? RbConfig::CONFIG["target_os"] =~ /linux/i end def windows? RbConfig::CONFIG["target_os"] =~ /mswin|mingw/i end guard-2.18.1/vendor/000077500000000000000000000000001450001123100141655ustar00rootroot00000000000000guard-2.18.1/vendor/hound/000077500000000000000000000000001450001123100153025ustar00rootroot00000000000000guard-2.18.1/vendor/hound/config/000077500000000000000000000000001450001123100165475ustar00rootroot00000000000000guard-2.18.1/vendor/hound/config/style_guides/000077500000000000000000000000001450001123100212475ustar00rootroot00000000000000guard-2.18.1/vendor/hound/config/style_guides/ruby.yml000066400000000000000000000216501450001123100227570ustar00rootroot00000000000000AllCops: Exclude: - "vendor/**/*" - "db/schema.rb" UseCache: false Style/CollectionMethods: Description: Preferred collection methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size Enabled: true PreferredMethods: collect: map collect!: map! find: detect find_all: select reduce: inject Layout/DotPosition: Description: Checks the position of the dot in multi-line method calls. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains Enabled: true EnforcedStyle: leading SupportedStyles: - leading - trailing Naming/FileName: Description: Use snake_case for source file names. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files Enabled: false Exclude: [] Style/GuardClause: Description: Check for conditionals that can be replaced with guard clauses StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals Enabled: false MinBodyLength: 1 Style/IfUnlessModifier: Description: Favor modifier if/unless usage when you have a single-line body. StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier Enabled: false Style/OptionHash: Description: Don't use option hashes when you can use keyword arguments. Enabled: false Style/PercentLiteralDelimiters: Description: Use `%`-literal delimiters consistently StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces Enabled: false PreferredDelimiters: "%": "()" "%i": "()" "%q": "()" "%Q": "()" "%r": "{}" "%s": "()" "%w": "()" "%W": "()" "%x": "()" Naming/PredicateName: Description: Check the names of predicate methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark Enabled: true NamePrefix: - is_ - has_ - have_ NamePrefixBlacklist: - is_ Exclude: - spec/**/* Style/RaiseArgs: Description: Checks the arguments passed to raise/fail. StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages Enabled: false EnforcedStyle: exploded SupportedStyles: - compact - exploded Style/SignalException: Description: Checks for proper usage of fail and raise. StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method Enabled: false EnforcedStyle: semantic SupportedStyles: - only_raise - only_fail - semantic Style/SingleLineBlockParams: Description: Enforces the names of some block params. StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks Enabled: false Methods: - reduce: - a - e - inject: - a - e Style/SingleLineMethods: Description: Avoid single-line methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods Enabled: false AllowIfMethodIsEmpty: true Style/StringLiterals: Description: Checks if uses of quotes match the configured preference. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals Enabled: true EnforcedStyle: double_quotes SupportedStyles: - single_quotes - double_quotes Style/StringLiteralsInInterpolation: Description: Checks if uses of quotes inside expressions in interpolated strings match the configured preference. Enabled: true EnforcedStyle: single_quotes SupportedStyles: - single_quotes - double_quotes Style/TrailingCommaInArguments: Description: 'Checks for trailing comma in argument lists.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' Enabled: false EnforcedStyleForMultiline: no_comma # SupportedStyles: # - comma # - consistent_comma # - no_comma Style/TrailingCommaInHashLiteral: Description: 'Checks for trailing comma in hash literals.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' Enabled: false EnforcedStyleForMultiline: no_comma # SupportedStyles: # - comma # - consistent_comma # - no_comma Style/TrailingCommaInArrayLiteral: Description: 'Checks for trailing comma in array literals.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' Enabled: false EnforcedStyleForMultiline: no_comma # SupportedStyles: # - comma # - consistent_comma # - no_comma Metrics/AbcSize: Description: A calculated magnitude based on number of assignments, branches, and conditions. Enabled: false Max: 15 Metrics/ClassLength: Description: Avoid classes longer than 100 lines of code. Enabled: false CountComments: false Max: 100 Metrics/ModuleLength: CountComments: false Max: 100 Description: Avoid modules longer than 100 lines of code. Enabled: false Metrics/BlockLength: Exclude: - spec/**/*.rb Metrics/CyclomaticComplexity: Description: A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Enabled: false Max: 6 Metrics/LineLength: Enabled: true Max: 125 # To make it possible to copy or click on URIs in the code, we allow lines # containing a URI to be longer than Max. AllowHeredoc: true AllowURI: true URISchemes: - http - https # The IgnoreCopDirectives option causes the LineLength rule to ignore cop # directives like '# rubocop: enable ...' when calculating a line's length. IgnoreCopDirectives: false # The IgnoredPatterns option is a list of !ruby/regexp and/or string # elements. Strings will be converted to Regexp objects. A line that matches # any regular expression listed in this option will be ignored by LineLength. IgnoredPatterns: [] Metrics/MethodLength: Description: Avoid methods longer than 10 lines of code. StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods Enabled: false CountComments: false Max: 10 Metrics/ParameterLists: Description: Avoid parameter lists longer than three or four parameters. StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params Enabled: false Max: 5 CountKeywordArgs: true Metrics/PerceivedComplexity: Description: A complexity metric geared towards measuring complexity for a human reader. Enabled: false Max: 7 Lint/AssignmentInCondition: Description: Don't use assignment in conditions. StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition Enabled: false AllowSafeAssignment: true Style/InlineComment: Description: Avoid inline comments. Enabled: false Naming/AccessorMethodName: Description: Check the naming of accessor methods for get_/set_. Enabled: false Style/Alias: Description: Use alias_method instead of alias. StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method Enabled: false Style/Documentation: Description: Document classes and non-namespace modules. Enabled: false Style/DoubleNegation: Description: Checks for uses of double negation (!!). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang Enabled: false Style/EachWithObject: Description: Prefer `each_with_object` over `inject` or `reduce`. Enabled: false Style/EmptyLiteral: Description: Prefer literals to Array.new/Hash.new/String.new. StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash Enabled: false Style/ModuleFunction: Description: Checks for usage of `extend self` in modules. StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function Enabled: false Style/OneLineConditional: Description: Favor the ternary operator(?:) over if/then/else/end constructs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator Enabled: false Style/PerlBackrefs: Description: Avoid Perl-style regex back references. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers Enabled: false Style/Send: Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send` may overlap with existing methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-public-send Enabled: false Style/SpecialGlobalVars: Description: Avoid Perl-style global variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms Enabled: false Style/VariableInterpolation: Description: Don't interpolate global, instance and class variables directly in strings. StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate Enabled: false Style/WhenThen: Description: Use when x then ... for one-line cases. StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases Enabled: false Lint/EachWithObjectArgument: Description: Check for immutable argument given to each_with_object. Enabled: true Lint/HandleExceptions: Description: Don't suppress exception. StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions Enabled: false Lint/LiteralAsCondition: Description: Checks of literals used in conditions. Enabled: false Lint/LiteralInInterpolation: Description: Checks for literals used in interpolation. Enabled: false