-
Notifications
You must be signed in to change notification settings - Fork 481
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
add7310
672ed89
1814234
cef6aa5
52c64a5
e0eefa7
b680c6c
87a677a
cf64428
6f3ef4b
e417c40
fe4e7aa
b80411a
fb32fe9
4942fc1
8358c62
f33d6c4
98933f8
3d8571a
c7b3fd3
3a98cc0
6b466f6
8c93ed9
5069a11
b095597
5230884
49d6ffa
fed30f1
8c2ab9e
8114666
215f891
4a77fde
a804658
b972242
e6aab49
1e12d8d
1baa713
0ba79a6
a47aed1
68dc4bb
8df6a45
929ffdf
a3104cb
7ed2884
568e12a
8dc0c4f
ebe4b12
ca71795
586224d
ba67757
fbae74a
f469f51
0363572
435ffc3
98a4868
f004880
c9888b4
ea80783
7bc081a
516b062
ec27de8
486ef0d
d6a4ec6
a033cb7
1d993a6
2866275
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
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] = [] } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we do something better than this? Rather than putting templates in |
||
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 |
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
require "guard" | ||
|
||
module Guard | ||
# @private api | ||
module Internals | ||
|
There was a problem hiding this comment.
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.