guard 1.4.0 → 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -677
  3. data/LICENSE +4 -2
  4. data/README.md +91 -753
  5. data/bin/_guard-core +11 -0
  6. data/bin/guard +108 -3
  7. data/lib/guard/aruba_adapter.rb +59 -0
  8. data/lib/guard/cli/environments/bundler.rb +22 -0
  9. data/lib/guard/cli/environments/evaluate_only.rb +35 -0
  10. data/lib/guard/cli/environments/valid.rb +69 -0
  11. data/lib/guard/cli.rb +129 -128
  12. data/lib/guard/commander.rb +104 -0
  13. data/lib/guard/commands/all.rb +37 -0
  14. data/lib/guard/commands/change.rb +31 -0
  15. data/lib/guard/commands/notification.rb +26 -0
  16. data/lib/guard/commands/pause.rb +29 -0
  17. data/lib/guard/commands/reload.rb +36 -0
  18. data/lib/guard/commands/scope.rb +38 -0
  19. data/lib/guard/commands/show.rb +24 -0
  20. data/lib/guard/config.rb +18 -0
  21. data/lib/guard/deprecated/dsl.rb +45 -0
  22. data/lib/guard/deprecated/evaluator.rb +39 -0
  23. data/lib/guard/deprecated/guard.rb +328 -0
  24. data/lib/guard/deprecated/guardfile.rb +84 -0
  25. data/lib/guard/deprecated/watcher.rb +27 -0
  26. data/lib/guard/dsl.rb +332 -363
  27. data/lib/guard/dsl_describer.rb +132 -122
  28. data/lib/guard/dsl_reader.rb +51 -0
  29. data/lib/guard/group.rb +34 -14
  30. data/lib/guard/guardfile/evaluator.rb +232 -0
  31. data/lib/guard/guardfile/generator.rb +128 -0
  32. data/lib/guard/guardfile.rb +24 -60
  33. data/lib/guard/interactor.rb +31 -255
  34. data/lib/guard/internals/debugging.rb +68 -0
  35. data/lib/guard/internals/groups.rb +40 -0
  36. data/lib/guard/internals/helpers.rb +13 -0
  37. data/lib/guard/internals/plugins.rb +53 -0
  38. data/lib/guard/internals/queue.rb +51 -0
  39. data/lib/guard/internals/scope.rb +121 -0
  40. data/lib/guard/internals/session.rb +180 -0
  41. data/lib/guard/internals/state.rb +25 -0
  42. data/lib/guard/internals/tracing.rb +33 -0
  43. data/lib/guard/internals/traps.rb +10 -0
  44. data/lib/guard/jobs/base.rb +21 -0
  45. data/lib/guard/jobs/pry_wrapper.rb +336 -0
  46. data/lib/guard/jobs/sleep.rb +26 -0
  47. data/lib/guard/notifier.rb +46 -212
  48. data/lib/guard/options.rb +22 -0
  49. data/lib/guard/plugin.rb +303 -0
  50. data/lib/guard/plugin_util.rb +191 -0
  51. data/lib/guard/rake_task.rb +42 -0
  52. data/lib/guard/runner.rb +80 -140
  53. data/lib/guard/templates/Guardfile +14 -0
  54. data/lib/guard/terminal.rb +13 -0
  55. data/lib/guard/ui/colors.rb +56 -0
  56. data/lib/guard/ui/config.rb +70 -0
  57. data/lib/guard/ui/logger.rb +30 -0
  58. data/lib/guard/ui.rb +163 -128
  59. data/lib/guard/version.rb +1 -2
  60. data/lib/guard/watcher/pattern/deprecated_regexp.rb +45 -0
  61. data/lib/guard/watcher/pattern/match_result.rb +18 -0
  62. data/lib/guard/watcher/pattern/matcher.rb +33 -0
  63. data/lib/guard/watcher/pattern/pathname_path.rb +15 -0
  64. data/lib/guard/watcher/pattern/simple_path.rb +23 -0
  65. data/lib/guard/watcher/pattern.rb +24 -0
  66. data/lib/guard/watcher.rb +52 -95
  67. data/lib/guard.rb +108 -376
  68. data/lib/tasks/releaser.rb +116 -0
  69. data/man/guard.1 +12 -9
  70. data/man/guard.1.html +18 -12
  71. metadata +148 -77
  72. data/images/guard.png +0 -0
  73. data/lib/guard/guard.rb +0 -156
  74. data/lib/guard/hook.rb +0 -120
  75. data/lib/guard/interactors/coolline.rb +0 -64
  76. data/lib/guard/interactors/helpers/completion.rb +0 -32
  77. data/lib/guard/interactors/helpers/terminal.rb +0 -46
  78. data/lib/guard/interactors/readline.rb +0 -94
  79. data/lib/guard/interactors/simple.rb +0 -19
  80. data/lib/guard/notifiers/emacs.rb +0 -69
  81. data/lib/guard/notifiers/gntp.rb +0 -118
  82. data/lib/guard/notifiers/growl.rb +0 -99
  83. data/lib/guard/notifiers/growl_notify.rb +0 -92
  84. data/lib/guard/notifiers/libnotify.rb +0 -96
  85. data/lib/guard/notifiers/notifysend.rb +0 -84
  86. data/lib/guard/notifiers/rb_notifu.rb +0 -102
  87. data/lib/guard/notifiers/terminal_notifier.rb +0 -66
  88. data/lib/guard/notifiers/tmux.rb +0 -69
  89. data/lib/guard/version.rbc +0 -130
@@ -0,0 +1,53 @@
1
+ require "guard/plugin_util"
2
+ require "guard/group"
3
+ require "guard/plugin"
4
+
5
+ module Guard
6
+ # @private api
7
+ module Internals
8
+ class Plugins
9
+ def initialize
10
+ @plugins = []
11
+ end
12
+
13
+ def all(filter = nil)
14
+ return @plugins if filter.nil?
15
+ matcher = matcher_for(filter)
16
+ @plugins.select { |plugin| matcher.call(plugin) }
17
+ end
18
+
19
+ def remove(plugin)
20
+ @plugins.delete(plugin)
21
+ end
22
+
23
+ # TODO: should it allow duplicates? (probably yes because of different
24
+ # configs or groups)
25
+ def add(name, options)
26
+ @plugins << PluginUtil.new(name).initialize_plugin(options)
27
+ end
28
+
29
+ private
30
+
31
+ def matcher_for(filter)
32
+ case filter
33
+ when String, Symbol
34
+ shortname = filter.to_s.downcase.delete("-")
35
+ ->(plugin) { plugin.name == shortname }
36
+ when Regexp
37
+ ->(plugin) { plugin.name =~ filter }
38
+ when Hash
39
+ lambda do |plugin|
40
+ filter.all? do |k, v|
41
+ case k
42
+ when :name
43
+ plugin.name == v.to_s.downcase.delete("-")
44
+ when :group
45
+ plugin.group.name == v.to_sym
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,51 @@
1
+ module Guard
2
+ module Internals
3
+ class Queue
4
+ def initialize(commander)
5
+ @commander = commander
6
+ @queue = ::Queue.new
7
+ end
8
+
9
+ # Process the change queue, running tasks within the main Guard thread
10
+ def process
11
+ actions = []
12
+ changes = { modified: [], added: [], removed: [] }
13
+
14
+ while pending?
15
+ if (item = @queue.pop).first.is_a?(Symbol)
16
+ actions << item
17
+ else
18
+ item.each { |key, value| changes[key] += value }
19
+ end
20
+ end
21
+
22
+ _run_actions(actions)
23
+ return if changes.values.all?(&:empty?)
24
+ Runner.new.run_on_changes(*changes.values)
25
+ end
26
+
27
+ def pending?
28
+ !@queue.empty?
29
+ end
30
+
31
+ def <<(changes)
32
+ @queue << changes
33
+ end
34
+
35
+ private
36
+
37
+ def _run_actions(actions)
38
+ actions.each do |action_args|
39
+ args = action_args.dup
40
+ namespaced_action = args.shift
41
+ action = namespaced_action.to_s.sub(/^guard_/, "")
42
+ if @commander.respond_to?(action)
43
+ @commander.send(action, *args)
44
+ else
45
+ fail "Unknown action: #{action.inspect}"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,121 @@
1
+ require "guard"
2
+
3
+ module Guard
4
+ # @private api
5
+ module Internals
6
+ class Scope
7
+ def initialize
8
+ @interactor_plugin_scope = []
9
+ @interactor_group_scope = []
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ plugins: _hashify_scope(:plugin),
15
+ groups: _hashify_scope(:group)
16
+ }.dup.freeze
17
+ end
18
+
19
+ # TODO: refactor
20
+ def grouped_plugins(scope = { plugins: [], groups: [] })
21
+ items = nil
22
+ plugins = _find_non_empty_scope(:plugins, scope)
23
+ if plugins
24
+ items = Array(plugins).map { |plugin| _instantiate(:plugin, plugin) }
25
+ end
26
+
27
+ unless items
28
+ # TODO: no coverage here!!
29
+ found = _find_non_empty_scope(:groups, scope)
30
+ found ||= Guard.state.session.groups.all
31
+ groups = Array(found).map { |group| _instantiate(:group, group) }
32
+ if groups.any? { |g| g.name == :common }
33
+ items = groups
34
+ else
35
+ items = ([_instantiate(:group, :common)] + Array(found)).compact
36
+ end
37
+ end
38
+
39
+ items.map do |plugin_or_group|
40
+ group = nil
41
+ plugins = [plugin_or_group]
42
+ if plugin_or_group.is_a?(Group)
43
+ # TODO: no coverage here!
44
+ group = plugin_or_group
45
+ plugins = Guard.state.session.plugins.all(group: group.name)
46
+ end
47
+ [group, plugins]
48
+ end
49
+ end
50
+
51
+ def from_interactor(scope)
52
+ @interactor_plugin_scope = Array(scope[:plugins])
53
+ @interactor_group_scope = Array(scope[:groups])
54
+ end
55
+
56
+ def titles(scope = nil)
57
+ hash = scope || to_hash
58
+ plugins = hash[:plugins]
59
+ groups = hash[:groups]
60
+ return plugins.map(&:title) unless plugins.nil? || plugins.empty?
61
+ return hash[:groups].map(&:title) unless groups.nil? || groups.empty?
62
+ ["all"]
63
+ end
64
+
65
+ private
66
+
67
+ # TODO: move to session
68
+ def _scope_names(new_scope, name)
69
+ items = Array(new_scope[:"#{name}s"] || new_scope[name]) if items.empty?
70
+
71
+ # Convert objects to names
72
+ items.map { |p| p.respond_to?(:name) ? p.name : p }
73
+ end
74
+
75
+ # TODO: let the Plugins and Groups classes handle this?
76
+ # TODO: why even instantiate?? just to check if it exists?
77
+ def _hashify_scope(type)
78
+ # TODO: get cmdline passed to initialize above?
79
+ cmdline = Array(Guard.state.session.send("cmdline_#{type}s"))
80
+ guardfile = Guard.state.session.send(:"guardfile_#{type}_scope")
81
+ interactor = instance_variable_get(:"@interactor_#{type}_scope")
82
+
83
+ # TODO: session should decide whether to use cmdline or guardfile -
84
+ # since it has access to both variables
85
+ items = [interactor, cmdline, guardfile].detect do |source|
86
+ !source.empty?
87
+ end
88
+
89
+ # TODO: not tested when groups/plugins given don't exist
90
+
91
+ # TODO: should already be instantiated
92
+ Array(items).map do |obj|
93
+ if obj.respond_to?(:name)
94
+ obj
95
+ else
96
+ name = obj
97
+ (type == :group ? _groups : _plugins).all(name).first
98
+ end
99
+ end.compact
100
+ end
101
+
102
+ def _instantiate(meth, obj)
103
+ # TODO: no coverage
104
+ return obj unless obj.is_a?(Symbol) || obj.is_a?(String)
105
+ Guard.state.session.send("#{meth}s".to_sym).all(obj).first
106
+ end
107
+
108
+ def _find_non_empty_scope(type, local_scope)
109
+ [Array(local_scope[type]), to_hash[type]].map(&:compact).detect(&:any?)
110
+ end
111
+
112
+ def _groups
113
+ Guard.state.session.groups
114
+ end
115
+
116
+ def _plugins
117
+ Guard.state.session.plugins
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,180 @@
1
+ require "guard/internals/plugins"
2
+ require "guard/internals/groups"
3
+
4
+ require "guard/options"
5
+
6
+ module Guard
7
+ # @private api
8
+ module Internals
9
+ # TODO: split into a commandline class and session (plugins, groups)
10
+ # TODO: swap session and metadata
11
+ class Session
12
+ attr_reader :plugins
13
+ attr_reader :groups
14
+
15
+ DEFAULT_OPTIONS = {
16
+ clear: false,
17
+ debug: false,
18
+ no_bundler_warning: false,
19
+
20
+ # User defined scopes
21
+ group: [],
22
+ plugin: [],
23
+
24
+ # Notifier
25
+ notify: true,
26
+
27
+ # Interactor
28
+ no_interactions: false,
29
+
30
+ # Guardfile options:
31
+ # guardfile_contents
32
+ guardfile: nil,
33
+
34
+ # Listener options
35
+ # TODO: rename to watchdirs?
36
+ watchdir: Dir.pwd,
37
+ latency: nil,
38
+ force_polling: false,
39
+ wait_for_delay: nil,
40
+ listen_on: nil
41
+ }
42
+
43
+ def cmdline_groups
44
+ @cmdline_groups.dup.freeze
45
+ end
46
+
47
+ def cmdline_plugins
48
+ @cmdline_plugins.dup.freeze
49
+ end
50
+
51
+ def initialize(new_options)
52
+ @options = Options.new(new_options, DEFAULT_OPTIONS)
53
+
54
+ @plugins = Plugins.new
55
+ @groups = Groups.new
56
+
57
+ @cmdline_groups = @options[:group]
58
+ @cmdline_plugins = @options[:plugin]
59
+
60
+ @clear = @options[:clear]
61
+ @debug = @options[:debug]
62
+ @watchdirs = Array(@options[:watchdir])
63
+ @notify = @options[:notify]
64
+ @interactor_name = @options[:no_interactions] ? :sleep : :pry_wrapper
65
+
66
+ @guardfile_plugin_scope = []
67
+ @guardfile_group_scope = []
68
+ @guardfile_ignore = []
69
+ @guardfile_ignore_bang = []
70
+
71
+ @guardfile_notifier_options = {}
72
+ end
73
+
74
+ def guardfile_scope(scope)
75
+ opts = scope.dup
76
+
77
+ groups = Array(opts.delete(:groups))
78
+ group = Array(opts.delete(:group))
79
+ @guardfile_group_scope = Array(groups) + Array(group)
80
+
81
+ plugins = Array(opts.delete(:plugins))
82
+ plugin = Array(opts.delete(:plugin))
83
+ @guardfile_plugin_scope = Array(plugins) + Array(plugin)
84
+
85
+ fail "Unknown options: #{opts.inspect}" unless opts.empty?
86
+ end
87
+
88
+ # TODO: create a EvaluatorResult class?
89
+ attr_reader :guardfile_group_scope
90
+ attr_reader :guardfile_plugin_scope
91
+ attr_accessor :guardfile_ignore_bang
92
+
93
+ attr_reader :guardfile_ignore
94
+ def guardfile_ignore=(ignores)
95
+ @guardfile_ignore += Array(ignores).flatten
96
+ end
97
+
98
+ def clearing(on)
99
+ @clear = on
100
+ end
101
+
102
+ def clearing?
103
+ @clear
104
+ end
105
+
106
+ alias :clear? :clearing?
107
+
108
+ def debug?
109
+ @debug
110
+ end
111
+
112
+ def watchdirs
113
+ @watchdirs_from_guardfile ||= nil
114
+ @watchdirs_from_guardfile || @watchdirs
115
+ end
116
+
117
+ # set by Dsl with :directories() command
118
+ def watchdirs=(dirs)
119
+ dirs = [Dir.pwd] if dirs.empty?
120
+ @watchdirs_from_guardfile = dirs.map { |dir| File.expand_path dir }
121
+ end
122
+
123
+ def listener_args
124
+ if @options[:listen_on]
125
+ [:on, @options[:listen_on]]
126
+ else
127
+ listener_options = {}
128
+ [:latency, :force_polling, :wait_for_delay].each do |option|
129
+ listener_options[option] = @options[option] if @options[option]
130
+ end
131
+ expanded_watchdirs = watchdirs.map { |dir| File.expand_path dir }
132
+ [:to, *expanded_watchdirs, listener_options]
133
+ end
134
+ end
135
+
136
+ def evaluator_options
137
+ opts = { guardfile: @options[:guardfile] }
138
+ # TODO: deprecate :guardfile_contents
139
+ if @options[:guardfile_contents]
140
+ opts[:contents] = @options[:guardfile_contents]
141
+ end
142
+ opts
143
+ end
144
+
145
+ def notify_options
146
+ names = @guardfile_notifier_options.keys
147
+ return { notify: false } if names.include?(:off)
148
+
149
+ {
150
+ notify: @options[:notify],
151
+ notifiers: @guardfile_notifier_options
152
+ }
153
+ end
154
+
155
+ def guardfile_notification=(config)
156
+ @guardfile_notifier_options.merge!(config)
157
+ end
158
+
159
+ attr_reader :interactor_name
160
+
161
+ # TODO: call this from within action, not within interactor command
162
+ def convert_scope(entries)
163
+ scopes = { plugins: [], groups: [] }
164
+ unknown = []
165
+
166
+ entries.each do |entry|
167
+ if plugin = plugins.all(entry).first
168
+ scopes[:plugins] << plugin
169
+ elsif group = groups.all(entry).first
170
+ scopes[:groups] << group
171
+ else
172
+ unknown << entry
173
+ end
174
+ end
175
+
176
+ [scopes, unknown]
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,25 @@
1
+ require "guard/group"
2
+
3
+ require "guard/plugin_util"
4
+ require "guard/internals/session"
5
+ require "guard/internals/scope"
6
+ require "guard/runner"
7
+
8
+ module Guard
9
+ module Internals
10
+ class State
11
+ # Minimal setup for non-interactive commands (list, init, show, etc.)
12
+ def initialize(cmdline_opts)
13
+ @session = Session.new(cmdline_opts)
14
+
15
+ @scope = Scope.new
16
+
17
+ # NOTE: must be set before anything calls Guard::UI.debug
18
+ Debugging.start if session.debug?
19
+ end
20
+
21
+ attr_reader :scope
22
+ attr_reader :session
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ module Guard
2
+ module Internals
3
+ module Tracing
4
+ def self.trace(mod, meth)
5
+ meta = (class << mod; self; end)
6
+ original_meth = "original_#{meth}".to_sym
7
+
8
+ if mod.respond_to?(original_meth)
9
+ fail "ALREADY TRACED: #{mod}.#{meth}"
10
+ end
11
+
12
+ meta.send(:alias_method, original_meth, meth)
13
+ meta.send(:define_method, meth) do |*args, &block|
14
+ yield(*args) if block_given?
15
+ mod.send original_meth, *args, &block
16
+ end
17
+ end
18
+
19
+ def self.untrace(mod, meth)
20
+ meta = (class << mod; self; end)
21
+ original_meth = "original_#{meth}".to_sym
22
+
23
+ unless mod.respond_to?(original_meth)
24
+ fail "NOT TRACED: #{mod}.#{meth} (no method: #{original_meth})"
25
+ end
26
+
27
+ meta.send(:remove_method, meth)
28
+ meta.send(:alias_method, meth, original_meth)
29
+ meta.send(:undef_method, original_meth)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module Guard
2
+ module Internals
3
+ module Traps
4
+ def self.handle(signal, &block)
5
+ return unless Signal.list.key?(signal)
6
+ Signal.trap(signal, &block)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Guard
2
+ module Jobs
3
+ class Base
4
+ def initialize(_options)
5
+ end
6
+
7
+ # @return [Symbol] :stopped once job is finished
8
+ # @return [Symbol] :exit to tell Guard to terminate
9
+ def foreground
10
+ end
11
+
12
+ def background
13
+ end
14
+
15
+ # Signal handler calls this, so avoid actually doing
16
+ # anything other than signaling threads
17
+ def handle_interrupt
18
+ end
19
+ end
20
+ end
21
+ end