ego 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +20 -0
  3. data/.rubocop_todo.yml +36 -0
  4. data/Gemfile +2 -0
  5. data/Guardfile +9 -2
  6. data/Rakefile +5 -1
  7. data/bin/ego +2 -0
  8. data/ego.gemspec +22 -17
  9. data/lib/ego.rb +3 -1
  10. data/lib/ego/capability.rb +2 -0
  11. data/lib/ego/filesystem.rb +2 -0
  12. data/lib/ego/handler.rb +8 -2
  13. data/lib/ego/options.rb +13 -8
  14. data/lib/ego/plugin.rb +3 -1
  15. data/lib/ego/plugin_helper.rb +18 -16
  16. data/lib/ego/plugins/capabilities.rb +9 -3
  17. data/lib/ego/plugins/fallback.rb +9 -7
  18. data/lib/ego/plugins/robot_io.rb +2 -0
  19. data/lib/ego/plugins/social.rb +3 -1
  20. data/lib/ego/plugins/status.rb +2 -0
  21. data/lib/ego/plugins/system.rb +4 -2
  22. data/lib/ego/printer.rb +9 -5
  23. data/lib/ego/robot.rb +9 -6
  24. data/lib/ego/robot_error.rb +2 -0
  25. data/lib/ego/runner.rb +12 -7
  26. data/lib/ego/version.rb +3 -1
  27. data/spec/ego/acceptance/help_spec.rb +9 -0
  28. data/spec/ego/acceptance/no_args_spec.rb +9 -0
  29. data/spec/ego/acceptance/shell_spec.rb +11 -0
  30. data/spec/ego/acceptance/simple_query_spec.rb +9 -0
  31. data/spec/ego/acceptance/template_spec.rb +43 -0
  32. data/spec/ego/acceptance/usage_error_spec.rb +17 -0
  33. data/spec/ego/acceptance/version_spec.rb +9 -0
  34. data/spec/ego/capability_spec.rb +2 -0
  35. data/spec/ego/handler_spec.rb +18 -6
  36. data/spec/ego/options_spec.rb +8 -1
  37. data/spec/ego/plugin_helper_spec.rb +4 -2
  38. data/spec/ego/plugin_spec.rb +5 -4
  39. data/spec/ego/plugins/capabilities_spec.rb +4 -4
  40. data/spec/ego/plugins/fallback_spec.rb +16 -6
  41. data/spec/ego/plugins/robot_io_spec.rb +3 -3
  42. data/spec/ego/plugins/social_spec.rb +3 -3
  43. data/spec/ego/plugins/status_spec.rb +3 -3
  44. data/spec/ego/plugins/system_spec.rb +8 -8
  45. data/spec/ego/printer_spec.rb +86 -86
  46. data/spec/ego/robot_error_spec.rb +2 -0
  47. data/spec/ego/robot_spec.rb +24 -22
  48. data/spec/ego/runner_spec.rb +12 -0
  49. data/spec/spec_helper.rb +5 -48
  50. data/spec/support/aruba.rb +3 -0
  51. data/spec/support/plugin_helper.rb +64 -0
  52. metadata +71 -9
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'socialize'
3
5
 
@@ -13,7 +15,7 @@ Ego.plugin do |robot|
13
15
  'Hi.',
14
16
  'Hey.',
15
17
  'Ciao.',
16
- 'Hej.',
18
+ 'Hej.'
17
19
  ].sample
18
20
  end
19
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'report robot status'
3
5
 
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'execute system commands'
3
5
 
4
6
  robot.provide :system do |*args|
5
7
  debug 'Running system with arguments %s.', args
6
8
 
7
- unless Kernel.system(*args)
9
+ unless Kernel.system(*args) # rubocop:disable Style/IfUnlessModifier
8
10
  alert 'Sorry, there was a problem running %s.', args.first
9
11
  end
10
12
  end
@@ -13,7 +15,7 @@ Ego.plugin do |robot|
13
15
 
14
16
  robot.on(
15
17
  /^what(?:'?s| is) my (?:user|login)? ?name/i => 5,
16
- /^who am I(?: logged in as)?/i => 5,
18
+ /^who am I(?: logged in as)?/i => 5
17
19
  ) do
18
20
  say 'You are currently logged in as:'
19
21
  system 'who'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'colorize'
2
4
 
3
5
  module Ego
@@ -16,7 +18,7 @@ module Ego
16
18
  # @param *replacements [Object, ...] `printf`-style replacements
17
19
  # @return [nil]
18
20
  def say(message, *replacements)
19
- puts sprintf(message, *replacements).bold
21
+ puts format(message, *replacements).bold
20
22
  end
21
23
 
22
24
  # Write stylized message to `$stdout` indicating an emote.
@@ -45,7 +47,7 @@ module Ego
45
47
  # @param *replacements [Object, ...] `printf`-style replacements
46
48
  # @return [nil]
47
49
  def alert(message, *replacements)
48
- errs sprintf(message, *replacements).light_red
50
+ errs format(message, *replacements).light_red
49
51
  end
50
52
 
51
53
  # Write stylized message to `$stderr` indicating a debugging message.
@@ -62,14 +64,16 @@ module Ego
62
64
  # @param *replacements [Object, ...] `printf`-style replacements
63
65
  # @return [nil]
64
66
  def debug(message, *replacements)
65
- errs sprintf(message, *replacements) if verbose?
67
+ errs format(message, *replacements) if verbose?
66
68
  end
67
69
 
68
70
  # Whether to print debugging messages. Can be overridden by classes that
69
71
  # include `Printer`.
70
72
  #
71
73
  # @return [false] should print debugging messages?
72
- def verbose?; false; end
74
+ def verbose?
75
+ false
76
+ end
73
77
 
74
78
  module_function
75
79
 
@@ -88,7 +92,7 @@ module Ego
88
92
  # @param *message [Object, ...] message(s) to write
89
93
  # @return [nil]
90
94
  def errs(*message)
91
- $stderr.puts(*message)
95
+ warn(*message)
92
96
  end
93
97
  end
94
98
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'capability'
2
4
  require_relative 'handler'
3
5
  require_relative 'robot_error'
@@ -17,7 +19,7 @@ module Ego
17
19
 
18
20
  attr_reader :name, :options, :capabilities
19
21
 
20
- alias_method :provide, :define_singleton_method
22
+ alias provide define_singleton_method
21
23
 
22
24
  define_hooks :on_ready, :on_shutdown
23
25
  define_hooks :before_handle_query, :after_handle_query, :on_unhandled_query
@@ -75,6 +77,7 @@ module Ego
75
77
  # The robot will execute the given block (the "action") when the given
76
78
  # pattern (the "condition") matches. Conditions are assigned priorities,
77
79
  # which determined in what order conditions are checked against the query.
80
+ # A higher number is treated as higher priority.
78
81
  #
79
82
  # @example Add a handler to the robot instance
80
83
  # robot.on(/^pattern/) do
@@ -100,14 +103,14 @@ module Ego
100
103
  # # ...
101
104
  # end
102
105
  #
103
- # @param condition [Proc, #match] the condition that triggers the supplied action
104
- # @param priority [Integer] the handler priority (higher number = higher priority)
106
+ # @param condition [Proc, #match] the condition that triggers the action
107
+ # @param priority [Integer] the handler priority
105
108
  # @param action [Proc] the block to be executed when condition is met
106
109
  # @return [void]
107
110
  #
108
111
  # @see Handler#initialize
109
112
  def on(condition, priority = 5, &action)
110
- unless action
113
+ unless action # rubocop:disable Style/IfUnlessModifier
111
114
  raise RobotError, "Hook requires an action: robot.on #{condition.inspect}"
112
115
  end
113
116
 
@@ -152,7 +155,7 @@ module Ego
152
155
  run_hook :before_handle_query, query
153
156
 
154
157
  first_handler_for(query) do |handler, params|
155
- return run_action(handler.action, params).tap do |result|
158
+ return run_action(handler.action, params).tap do |_result|
156
159
  run_hook :after_handle_query, query, handler
157
160
  end
158
161
  end
@@ -170,7 +173,7 @@ module Ego
170
173
  # @return [Handler] the first matching handler
171
174
  def first_handler_for(query)
172
175
  @handlers.sort.reverse_each do |handler|
173
- if params = handler.handle(query)
176
+ if (params = handler.handle(query))
174
177
  yield(handler, params) if block_given?
175
178
  return handler
176
179
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ego
2
4
  # Error type raised by robot plug-in DSL.
3
5
  #
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../ego'
2
4
  require_relative 'options'
5
+ require_relative 'plugin_helper'
3
6
 
4
7
  module Ego
5
8
  # The Runner class, given an array of arguments, initializes the required
@@ -8,7 +11,7 @@ module Ego
8
11
  # Prompt to display in shell-mode
9
12
  PROMPT = 'ego, '.green.freeze
10
13
  # Pattern that triggers shell-mode to exit
11
- QUIT = /^q(uit)?|exit|(good)?bye$/.freeze
14
+ QUIT = /^q(uit)?|exit|(good)?bye$/
12
15
 
13
16
  # Takes an array of arguments and parses them into options:
14
17
  #
@@ -27,15 +30,19 @@ module Ego
27
30
  def run
28
31
  case @options.mode
29
32
  when :help
30
- if @options.usage_error
31
- Printer.errs @options.usage_error, "\n"
32
- end
33
+ Printer.errs @options.usage_error, "\n" if @options.usage_error
33
34
 
34
35
  Printer.puts @options.usage
35
36
 
36
37
  exit(-1) if @options.usage_error
37
38
  when :version
38
39
  Printer.puts "ego v#{Ego::VERSION}"
40
+ when :template
41
+ helper = PluginHelper.new(
42
+ query: (@options.query unless @options.query.empty?),
43
+ program_name: @options.usage.program_name
44
+ )
45
+ Printer.puts helper.template
39
46
  when :shell
40
47
  start_shell(robot_factory)
41
48
  else
@@ -54,9 +61,7 @@ module Ego
54
61
  def robot_factory
55
62
  Plugin.load Filesystem.builtin_plugins
56
63
 
57
- if @options.plugins
58
- Plugin.load Filesystem.user_plugins
59
- end
64
+ Plugin.load Filesystem.user_plugins if @options.plugins
60
65
 
61
66
  Plugin.decorate(Robot.new(@options)).ready
62
67
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ego
2
4
  # Gem version
3
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
4
6
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego --help', type: :aruba do
4
+ before(:each) { run_command('bin/ego --help') }
5
+
6
+ it 'prints usage help' do
7
+ expect(last_command_started).to have_output an_output_string_matching('^Usage: ego')
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego', type: :aruba do
4
+ before(:each) { run_command('bin/ego') }
5
+
6
+ it 'prints usage help' do
7
+ expect(last_command_started).to have_output an_output_string_matching('^Usage: ego')
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: false
2
+
3
+ RSpec.describe 'bin/ego --shell', type: :aruba do
4
+ before(:each) { run_command('bin/ego --shell') }
5
+
6
+ it 'responds' do
7
+ type('echo xxx')
8
+ type('exit')
9
+ expect(last_command_started).to have_output an_output_string_including('xxx')
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego hello', type: :aruba do
4
+ before(:each) { run_command('bin/ego hello') }
5
+
6
+ it 'responds with a greeting' do
7
+ expect(last_command_started).to have_output an_output_string_matching('^[[:upper:]].+\.$')
8
+ end
9
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego --template', type: :aruba do
4
+ context 'without a query' do
5
+ before(:each) { run_command('bin/ego --template') }
6
+
7
+ it 'prints the plug-in template with default query' do
8
+ # rubocop:disable Layout/EmptyLinesAroundArguments
9
+ expect(last_command_started).to have_output an_output_string_being_eq(
10
+ <<~TEMPLATE
11
+ Ego.plugin do |robot|
12
+ robot.can 'do something new'
13
+
14
+ robot.on(/^My new plugin$/i) do |params|
15
+ alert 'Not implemented yet. Go ahead and edit ~/.config/ego/plugins/my_new_plugin.rb.'
16
+ end
17
+ end
18
+ TEMPLATE
19
+ )
20
+ # rubocop:enable Layout/EmptyLinesAroundArguments
21
+ end
22
+ end
23
+
24
+ context 'with a query' do
25
+ before(:each) { run_command('bin/ego --template help me out') }
26
+
27
+ it 'prints the plug-in template with supplied query' do
28
+ # rubocop:disable Layout/EmptyLinesAroundArguments
29
+ expect(last_command_started).to have_output an_output_string_being_eq(
30
+ <<~TEMPLATE
31
+ Ego.plugin do |robot|
32
+ robot.can 'do something new'
33
+
34
+ robot.on(/^help me out$/i) do |params|
35
+ alert 'Not implemented yet. Go ahead and edit ~/.config/ego/plugins/help_me_out.rb.'
36
+ end
37
+ end
38
+ TEMPLATE
39
+ )
40
+ # rubocop:enable Layout/EmptyLinesAroundArguments
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego --invalid-flag', type: :aruba do
4
+ before(:each) { run_command('bin/ego --invalid-flag', fail_on_error: false) }
5
+
6
+ it 'exits with a non-zero status' do
7
+ expect(last_command_started).not_to be_successfully_executed
8
+ end
9
+
10
+ it 'prints usage error' do
11
+ expect(last_command_started).to have_output an_output_string_matching('^invalid option:')
12
+ end
13
+
14
+ it 'prints usage help' do
15
+ expect(last_command_started).to have_output an_output_string_matching('^Usage: ego')
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'bin/ego --version', type: :aruba do
4
+ before(:each) { run_command('bin/ego --version') }
5
+
6
+ it 'prints version message' do
7
+ expect(last_command_started).to have_output an_output_string_matching('^ego v\d+\.\d+\.\d+')
8
+ end
9
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ego/capability'
2
4
 
3
5
  RSpec.describe Ego::Capability do
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ego/handler'
2
4
 
3
5
  RSpec.describe Ego::Handler do
4
- let(:condition) { ->(q) { {p: 'bar', q: 'baz'} if q == 'foo' } }
6
+ let(:condition) { ->(q) { { p: 'bar', q: 'baz' } if q == 'foo' } }
5
7
  let(:regexp) { /^baz/i }
6
8
  let(:action) { ->(p) { puts p } }
7
9
  let(:priority) { 2 }
@@ -68,13 +70,23 @@ RSpec.describe Ego::Handler do
68
70
  end
69
71
 
70
72
  it 'respects the order of action arguments' do
71
- subject = described_class.new(condition, ->(q, p) { }, priority)
72
- expect(subject.handle('foo')).to eq(['baz', 'bar'])
73
+ subject = described_class.new(condition, ->(q, p) {}, priority)
74
+ expect(subject.handle('foo')).to eq(%w[baz bar])
75
+ end
76
+
77
+ context 'when the condition returns a hash' do
78
+ it 'gracefully handles extra action arguments' do
79
+ subject = described_class.new(condition, ->(p, q, r) {}, priority)
80
+ expect { subject.handle('foo') }.not_to raise_error
81
+ end
73
82
  end
74
83
 
75
- it 'gracefully handles extra action arguments' do
76
- subject = described_class.new(condition, ->(p, q, r) { }, priority)
77
- expect { subject.handle('foo') }.not_to raise_error
84
+ context 'when the condition returns a match object' do
85
+ it 'gracefully handles extra action arguments' do
86
+ condition = /(?<p>foo|bar)/i
87
+ subject = described_class.new(condition, ->(p, q) {}, priority)
88
+ expect { subject.handle('foo') }.not_to raise_error
89
+ end
78
90
  end
79
91
  end
80
92
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ego/options'
2
4
 
3
5
  RSpec.describe Ego::Options do
@@ -11,6 +13,11 @@ RSpec.describe Ego::Options do
11
13
  expect(opts.mode).to eq(:shell)
12
14
  end
13
15
 
16
+ it 'can be set to template-mode' do
17
+ opts = described_class.new(['-t'])
18
+ expect(opts.mode).to eq(:template)
19
+ end
20
+
14
21
  it 'can be set to version-mode' do
15
22
  opts = described_class.new(['-v'])
16
23
  expect(opts.mode).to eq(:version)
@@ -47,7 +54,7 @@ RSpec.describe Ego::Options do
47
54
  end
48
55
 
49
56
  it 'sets the query to remaining args join with spaces' do
50
- opts = described_class.new(['foo', 'bar'])
57
+ opts = described_class.new(%w[foo bar])
51
58
  expect(opts.query).to eq('foo bar')
52
59
  end
53
60
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ego/plugin_helper'
2
4
 
3
5
  RSpec.describe Ego::PluginHelper do
@@ -13,11 +15,11 @@ RSpec.describe Ego::PluginHelper do
13
15
 
14
16
  describe '#path' do
15
17
  it 'returns a path to the plugins directory' do
16
- expect(subject.path).to match(/\/plugins\//)
18
+ expect(subject.path).to match(%r{/plugins/})
17
19
  end
18
20
 
19
21
  it 'uses a tilde for the home directory' do
20
- expect(subject.path).to match(/^~\//)
22
+ expect(subject.path).to match(%r{^~/})
21
23
  end
22
24
 
23
25
  it 'names the file after the slug' do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ego/plugin'
2
4
 
3
5
  RSpec.describe Ego::Plugin do
@@ -30,7 +32,6 @@ RSpec.describe Ego::Plugin do
30
32
  end
31
33
  end
32
34
 
33
-
34
35
  describe '.decorate' do
35
36
  let(:obj) do
36
37
  Class.new { attr_accessor :context, :a, :b }.new
@@ -62,10 +63,10 @@ RSpec.describe Ego::Plugin do
62
63
  end
63
64
 
64
65
  it 'calls each plugin body passing the obj' do
65
- expect(obj).to receive_messages({
66
+ expect(obj).to receive_messages(
66
67
  :a= => 'foo',
67
- :b= => 'bar',
68
- })
68
+ :b= => 'bar'
69
+ )
69
70
  described_class.decorate(obj)
70
71
  end
71
72