Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Add a Guard::API module that Guard plugins should include #873

Open
wants to merge 66 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
add7310
Add a Guard::API module that Guard plugins should include
rymai Jun 17, 2017
672ed89
Continue the work on the new Guard::API
rymai Mar 3, 2018
1814234
Continue work on using Guard::API
rymai Oct 9, 2017
cef6aa5
Fix more specs
rymai Mar 4, 2018
52c64a5
Settings: Update rubocop and settings
edthamm Aug 17, 2018
e0eefa7
Auto correct: Style/StringLiterals in spec
edthamm Aug 17, 2018
b680c6c
Auto correct: Style/FrozenStringLiteralComment
edthamm Aug 17, 2018
87a677a
Correct: Violations in gemfile
edthamm Aug 17, 2018
cf64428
Fix: Unfreeze string literal in failing test
edthamm Aug 17, 2018
6f3ef4b
Auto correct: Layout/DotPosition
edthamm Aug 17, 2018
e417c40
Auto correct: Style/PercentLiteralDelimiters in spec
edthamm Aug 17, 2018
fe4e7aa
Auto correct: Layout/EmptyLineAfterMagicComment in spec
edthamm Aug 17, 2018
b80411a
Auto correct: Style/SymbolArray in spec
edthamm Aug 17, 2018
fb32fe9
Auto correct: Layout/MultilineMethodCallIndentation in spec
edthamm Aug 17, 2018
4942fc1
Settings: Set Metrics/LineLength:Max to 125
edthamm Aug 17, 2018
8358c62
Auto correct: Style/TrailingCommaIn.*Literal in spec
edthamm Aug 17, 2018
f33d6c4
Settings: Adapt quote and dot settings
edthamm Aug 17, 2018
98933f8
Auto correct: Style/ExpandPathArguments in spec
edthamm Aug 17, 2018
3d8571a
Auto correct: Layout/AlignArray in spec
edthamm Aug 17, 2018
c7b3fd3
Auto correct: Style/Encoding in spec
edthamm Aug 17, 2018
3a98cc0
Correct: Forgotten double-quotes in Gemfile
edthamm Aug 17, 2018
6b466f6
Auto correct: Lint/UnneededSplatExpansion in spec
edthamm Aug 17, 2018
8c93ed9
Fix: Explicitly set rubocop configuration file
edthamm Aug 17, 2018
5069a11
Auto correct: Style/EmptyMethod in spec
edthamm Aug 17, 2018
b095597
Correct: the remaining 3 violations
edthamm Aug 17, 2018
5230884
Settings: Get rid of warnings.
edthamm Aug 17, 2018
49d6ffa
Fix: Resupport version 2.2.9
edthamm Aug 17, 2018
fed30f1
Fix: Increase aruba timeout for Jruby on CI
edthamm Aug 17, 2018
8c2ab9e
Settings: Enrich Metrics/LineLength config
edthamm Aug 19, 2018
8114666
Fix: Resupport 2.2.9 in rubocop
edthamm Aug 19, 2018
215f891
Settings: Set Style/StringLiterals to double_quotes
edthamm Aug 21, 2018
4a77fde
Auto correct: Style/StringLiterals
edthamm Aug 21, 2018
a804658
Trigger Hound
edthamm Sep 18, 2018
b972242
Auto correct: Style/StringLiterals in top level dir
edthamm Sep 18, 2018
e6aab49
Merge pull request #911 from edthamm/feat/styling-spec
rymai Sep 24, 2018
1e12d8d
add simplecov filter for a spec folder
Mifrill Oct 15, 2018
1baa713
Pry 0.12.0 Deprecation warnings
Nov 8, 2018
0ba79a6
Use Gem Version in comparison
rymai Nov 9, 2018
a47aed1
Merge pull request #916 from chadmetcalf/pry_deprecation
rymai Nov 9, 2018
68dc4bb
Bump to 2.15.0
rymai Nov 14, 2018
8df6a45
Merge pull request #918 from guard/bump-to-2.15.0
rymai Nov 14, 2018
929ffdf
Add a "Reviewed by Hound" badge
salbertson Nov 17, 2018
a3104cb
Merge pull request #919 from salbertson/patch-1
rymai Nov 19, 2018
7ed2884
Update Ruby versions tested in .travis.yml
rymai Feb 6, 2019
568e12a
Fix YARD annotation
olleolleolle Apr 1, 2019
8dc0c4f
Fix YARD annotation
olleolleolle Apr 1, 2019
ebe4b12
Merge pull request #925 from olleolleolle/patch-1
rymai Apr 4, 2019
ca71795
CI: Use 2.4.6, 2.5.5, 2.6.2...
olleolleolle Apr 4, 2019
586224d
Merge pull request #926 from olleolleolle/patch-1
rymai Apr 4, 2019
ba67757
README: Drop outdated badge for Gemnasium
olleolleolle Apr 4, 2019
fbae74a
Merge pull request #927 from olleolleolle/patch-1
rymai Apr 4, 2019
f469f51
Stub File.read instead of IO.read in spec.
jackorp Feb 4, 2019
0363572
Properly stub Pathname and their methods in specs
rymai Apr 4, 2019
435ffc3
Merge pull request #928 from guard/jackorp-fix-spec-helper
rymai Apr 4, 2019
98a4868
Cleanup guard process anytime
pocke Apr 5, 2019
f004880
Add spec for cleanup process
pocke Apr 13, 2019
c9888b4
Merge pull request #930 from pocke/cleanup-process
rymai Apr 17, 2019
ea80783
Update Ruby versions and fix a few small things in the README
rymai Apr 17, 2019
7bc081a
Merge pull request #914 from Mifrill/simplecov
rymai Apr 17, 2019
516b062
Add a Guard::API module that Guard plugins should include
rymai Jun 17, 2017
ec27de8
Continue the work on the new Guard::API
rymai Mar 3, 2018
486ef0d
Continue work on using Guard::API
rymai Oct 9, 2017
d6a4ec6
Fix more specs
rymai Mar 4, 2018
a033cb7
Not sure what I'm doing
rymai Nov 14, 2018
1d993a6
Fix some missed conflicts
rymai Apr 17, 2019
2866275
Merge branch 'guard-api' of github.com:guard/guard into guard-api
rymai Apr 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
272 changes: 272 additions & 0 deletions lib/guard/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# frozen_string_literal: true

require "guard/internals/groups"

module Guard
module API
TEMPLATE_FORMAT = "%s/lib/guard/%s/templates/Guardfile".freeze

require "guard/ui"

def self.included(base)
base.extend(ClassMethods)
base.class_eval do
attr_accessor :group, :watchers, :callbacks, :options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you add documentation.

end
end

module ClassMethods
# Get all callbacks registered for all Guard plugins present in the
# Guardfile.
#
def callbacks
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Ruby will complain about this as it wasn't defined prior. Unfortunately, I think you need to do something like this: https://github.com/ioquatix/utopia/blob/9239e84edfeaf7177f74ae9408c4e16468faa505/lib/utopia/controller/actions.rb#L126-L131

Check by running tests with warnings turned on.

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<Symbol>] events the events to register
#
def 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 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 reset_callbacks!
@callbacks = nil
end

# 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 non_namespaced_classname
to_s.sub(/\AGuard::/, "").sub(/::Plugin\z/, "")
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 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 template(plugin_location)
File.read(format(TEMPLATE_FORMAT, plugin_location, non_namespaced_name))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do something better than this? Rather than putting templates in lib/..., which is a tiny bit odd since it's not code - why not put it in templates/ of root of gem?

end
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(1..1).first[/`([^']*)'/, 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

# 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
#
def start; end

# 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
#
def stop; end

# 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
#
def reload; end

# 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
#
def run_all; end

# Default behaviour on file(s) changes that the Guard plugin watches.
#
# @param [Array<String>] paths the changes files or paths
# @raise [:task_has_failed] when run_on_changes has failed
# @return [Object] the task result
#
def run_on_changes(paths); end

# Called on file(s) additions that the Guard plugin watches.
#
# @param [Array<String>] paths the changes files or paths
# @raise [:task_has_failed] when run_on_additions has failed
# @return [Object] the task result
#
def run_on_additions(paths); end

# Called on file(s) modifications that the Guard plugin watches.
#
# @param [Array<String>] paths the changes files or paths
# @raise [:task_has_failed] when run_on_modifications has failed
# @return [Object] the task result
#
def run_on_modifications(paths); end

# Called on file(s) removals that the Guard plugin watches.
#
# @param [Array<String>] paths the changes files or paths
# @raise [:task_has_failed] when run_on_removals has failed
# @return [Object] the task result
#
def run_on_removals(paths); end

# 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
# #=> "#<Guard::RSpec @name=rspec @group=#<Guard::Group @name=default
# @options={}> @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<Guard::Watcher>] 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(opts = {})
group_name = opts.delete(:group) { :default }
@group = Guard.state.session.groups.add(group_name)
@watchers = opts.delete(:watchers) { [] }
@callbacks = opts.delete(:callbacks) { [] }
@options = opts
_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
4 changes: 2 additions & 2 deletions lib/guard/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CLI < Thor
method_option :latency,
type: :numeric,
aliases: "-l",
banner: 'Overwrite Listen\'s default latency'
banner: "Overwrite Listen's default latency"

method_option :force_polling,
type: :boolean,
Expand All @@ -87,7 +87,7 @@ class CLI < Thor
method_option :wait_for_delay,
type: :numeric,
aliases: "-y",
banner: 'Overwrite Listen\'s default wait_for_delay'
banner: "Overwrite Listen's default wait_for_delay"

method_option :listen_on,
type: :string,
Expand Down
2 changes: 0 additions & 2 deletions lib/guard/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
require "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
Expand Down
1 change: 1 addition & 0 deletions lib/guard/interactor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def initialize(no_interaction = false)

job_klass = interactive? ? Jobs::PryWrapper : Jobs::Sleep
@idle_job = job_klass.new(self.class.options)
@enabled = nil
end

def interactive?
Expand Down
1 change: 1 addition & 0 deletions lib/guard/internals/plugins.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "guard/plugin_util"
require "guard/group"
require "guard/api"
require "guard/plugin"

module Guard
Expand Down
2 changes: 0 additions & 2 deletions lib/guard/internals/scope.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require "guard"

module Guard
# @private api
module Internals
Expand Down