ego 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8250ad3de4494ebfced808df3a5cb09de9707a38
4
- data.tar.gz: 333930e6ddc866ec268016395b38061bc26aa9ab
3
+ metadata.gz: 0cb81d8a1088b04177d530c9e48535cf5aac9836
4
+ data.tar.gz: e73d083cb652574fda45ae050434c5317afa9690
5
5
  SHA512:
6
- metadata.gz: de4f362f46c4347e659330f418524a4cc3ce3f3ee0f1bdd8adcd97a63fb8257b6b61f9a34e31e9a339ba0725efc02f4faa590ef6ef1edb5f56dfd5415cc77d43
7
- data.tar.gz: df7fadaf3e23e57fec7634623da1a2a56135ff6c9ec8e3b5b501fd87168d5aac8dc7057f3523a2aab4991772e5c77adfc1bddd580f8ccfb17ee6a0dfe14fe00a
6
+ metadata.gz: e0804236f4b6316062857aeb92ab2376c7b10150b5e83588f1a57f9c9ea57ec7ded723872c25d3a557fc40eb9d0bb96656323086c722fe1fffe270f6c1fb7d4c
7
+ data.tar.gz: d618ba73954abebffd2eaac1b1ee71f275729b41ea12b926cc199cf525d523bb83656393216ad822ace7c925c2c980c6c0c5e77a59eaae3dab276c8e964b8a70
@@ -0,0 +1,20 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+
6
+ Metrics/BlockLength:
7
+ Exclude:
8
+ - '*.gemspec'
9
+ - 'spec/**/*_spec.rb'
10
+ - 'lib/ego/options.rb'
11
+ - 'lib/ego/plugins/*.rb'
12
+
13
+ Style/Semicolon:
14
+ Exclude:
15
+ - 'spec/**/*_spec.rb'
16
+
17
+ Style/SingleLineMethods:
18
+ Exclude:
19
+ - 'spec/**/*_spec.rb'
20
+
@@ -0,0 +1,36 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-02-05 09:02:49 -0500 using RuboCop version 0.52.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Metrics/AbcSize:
11
+ Max: 27
12
+
13
+ # Offense count: 1
14
+ Metrics/CyclomaticComplexity:
15
+ Max: 9
16
+
17
+ # Offense count: 3
18
+ # Configuration parameters: CountComments.
19
+ Metrics/MethodLength:
20
+ Max: 31
21
+
22
+ # Offense count: 4
23
+ Style/ClassVars:
24
+ Exclude:
25
+ - 'lib/ego/plugin.rb'
26
+
27
+ # Offense count: 1
28
+ Style/Documentation:
29
+ Exclude:
30
+ - 'lib/ego.rb'
31
+
32
+ # Offense count: 32
33
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
34
+ # URISchemes: http, https
35
+ Metrics/LineLength:
36
+ Max: 160
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in ego.gemspec
data/Guardfile CHANGED
@@ -1,5 +1,7 @@
1
- guard :rspec, cmd: "bundle exec rspec" do
2
- require "guard/rspec/dsl"
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: 'bundle exec rspec' do
4
+ require 'guard/rspec/dsl'
3
5
  dsl = Guard::RSpec::Dsl.new(self)
4
6
 
5
7
  # RSpec files
@@ -12,3 +14,8 @@ guard :rspec, cmd: "bundle exec rspec" do
12
14
  ruby = dsl.ruby
13
15
  dsl.watch_spec_files_for(ruby.lib_files)
14
16
  end
17
+
18
+ guard :rubocop do
19
+ watch(/.+\.rb$/)
20
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
21
+ end
data/Rakefile CHANGED
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
3
6
 
4
7
  RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
5
9
 
6
- task :default => :spec
10
+ task default: :spec
data/bin/ego CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative '../lib/ego/runner'
3
5
 
4
6
  runner = Ego::Runner.new(ARGV)
@@ -1,35 +1,40 @@
1
- # coding: utf-8
1
+
2
+ # frozen_string_literal: true
3
+
2
4
  lib = File.expand_path('../lib', __FILE__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'ego/version'
5
7
 
6
8
  Gem::Specification.new do |spec|
7
- spec.name = "ego"
9
+ spec.name = 'ego'
8
10
  spec.version = Ego::VERSION
9
- spec.authors = ["Noah Frederick"]
10
- spec.email = ["acc.rubygems@noahfrederick.com"]
11
- spec.summary = %q{An extensible personal command-line assistant}
12
- spec.description = <<-EOF
11
+ spec.authors = ['Noah Frederick']
12
+ spec.email = ['acc.rubygems@noahfrederick.com']
13
+ spec.summary = 'An extensible personal command-line assistant'
14
+ spec.description = <<-DESC
13
15
  Ego is a personal command-line assistant that provides a flexible, natural
14
16
  language interface (sort of) for interacting with other programs. Think of
15
17
  it as a single-user IRC bot that can be extended with handlers for various
16
18
  natural-language queries.
17
- EOF
18
- spec.homepage = "https://github.com/noahfrederick/ego"
19
- spec.license = "GPL-3.0+"
19
+ DESC
20
+ spec.homepage = 'https://github.com/noahfrederick/ego'
21
+ spec.license = 'GPL-3.0+'
20
22
 
21
23
  spec.files = `git ls-files -z`.split("\x0")
22
24
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
25
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
25
27
 
26
- spec.required_ruby_version = "~> 2.3"
28
+ spec.required_ruby_version = '~> 2.3'
27
29
 
28
- spec.add_development_dependency "bundler", "~> 1.6"
29
- spec.add_development_dependency "rake"
30
- spec.add_development_dependency "rspec", "~> 3.4"
31
- spec.add_development_dependency "guard-rspec"
30
+ spec.add_development_dependency 'aruba', '~> 1.0.0.pre.alpha.2'
31
+ spec.add_development_dependency 'bundler', '~> 1.6'
32
+ spec.add_development_dependency 'guard-rspec'
33
+ spec.add_development_dependency 'guard-rubocop'
34
+ spec.add_development_dependency 'rake'
35
+ spec.add_development_dependency 'rspec', '~> 3.7'
36
+ spec.add_development_dependency 'rubocop', '~> 0.52.1'
32
37
 
33
- spec.add_runtime_dependency "colorize", "~> 0.7"
34
- spec.add_runtime_dependency "hooks", "~> 0.4"
38
+ spec.add_runtime_dependency 'colorize', '~> 0.7'
39
+ spec.add_runtime_dependency 'hooks', '~> 0.4'
35
40
  end
data/lib/ego.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'ego/filesystem'
2
4
  require_relative 'ego/robot'
3
5
  require_relative 'ego/plugin'
@@ -12,7 +14,7 @@ module Ego
12
14
  # automatically at runtime. Each plug-in goes in it's own file with an `.rb`
13
15
  # extension (e.g., `~/.config/ego/plugins/my_plugin.rb`).
14
16
  #
15
- # Be carefulego will execute any Ruby scripts in this directory
17
+ # Be careful---ego will execute any Ruby scripts in this directory
16
18
  # indiscriminately.
17
19
  #
18
20
  # @example Create and register a new plug-in
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'plugin'
2
4
 
3
5
  module Ego
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ego
2
4
  # Provides utility methods for getting configuration, data, and cache paths.
3
5
  module Filesystem
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'robot_error'
2
4
 
3
5
  module Ego
@@ -58,10 +60,14 @@ module Ego
58
60
  # @return [false] if condition doesn't match
59
61
  # @return [Array] parameters to pass to the action
60
62
  def handle(query)
61
- return false unless result = @condition.call(query)
63
+ return false unless (result = @condition.call(query))
62
64
 
63
65
  @action.parameters.each_with_object([]) do |param, arr|
64
- arr << result[param.pop]
66
+ begin
67
+ arr << result[param.pop]
68
+ rescue IndexError
69
+ arr << nil # Match group isn't defined.
70
+ end
65
71
  end
66
72
  end
67
73
 
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  module Ego
4
6
  # Parse command-line options and set defaults.
5
7
  class Options
6
-
7
8
  attr_reader :mode,
8
9
  :plugins,
9
10
  :robot_name,
@@ -18,7 +19,7 @@ module Ego
18
19
  @plugins = true
19
20
  @verbose = false
20
21
  parse(argv)
21
- @query = argv.join(" ")
22
+ @query = argv.join(' ')
22
23
  end
23
24
 
24
25
  private
@@ -33,28 +34,32 @@ module Ego
33
34
  @robot_name = opts.program_name.capitalize
34
35
  opts.banner = "Usage: #{opts.program_name} [ options ] query..."
35
36
 
36
- opts.on("-n", "--no-plugins", "Skip loading user plug-ins") do
37
+ opts.on('-n', '--no-plugins', 'Skip loading user plug-ins') do
37
38
  @plugins = false
38
39
  end
39
40
 
40
- opts.on("-s", "--shell", "Start in REPL-mode") do
41
+ opts.on('-s', '--shell', 'Start in REPL-mode') do
41
42
  @mode = :shell
42
43
  end
43
44
 
44
- opts.on("-v", "--version", "Print version number") do
45
+ opts.on('-t', '--template', 'Create a new plug-in') do
46
+ @mode = :template
47
+ end
48
+
49
+ opts.on('-v', '--version', 'Print version number') do
45
50
  @mode = :version
46
51
  end
47
52
 
48
- opts.on("-V", "--verbose", "Include debugging info in output") do
53
+ opts.on('-V', '--verbose', 'Include debugging info in output') do
49
54
  @verbose = true
50
55
  end
51
56
 
52
- opts.on("-h", "--help", "Show this message") do
57
+ opts.on('-h', '--help', 'Show this message') do
53
58
  @mode = :help
54
59
  end
55
60
 
56
61
  begin
57
- argv = ["-h"] if argv.empty?
62
+ argv = ['-h'] if argv.empty?
58
63
  opts.parse!(argv)
59
64
  rescue OptionParser::ParseError => e
60
65
  @usage_error = e.message
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ego
2
4
  # A plug-in extends Ego with new handlers and other functionality, through a
3
5
  # domain-specific language (DSL).
@@ -47,7 +49,7 @@ module Ego
47
49
  # @param obj [Object] the object to decorate
48
50
  # @return [Object] the decorated object
49
51
  def self.decorate(obj)
50
- @@plugins.each do |name, plugin|
52
+ @@plugins.each_value do |plugin|
51
53
  @@context = plugin
52
54
  plugin.body.call(obj)
53
55
  @@context = nil
@@ -1,4 +1,6 @@
1
- require 'ego/filesystem'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'filesystem'
2
4
 
3
5
  module Ego
4
6
  # The PluginHelper assists the user in writing extensions by generating
@@ -8,9 +10,9 @@ module Ego
8
10
  class PluginHelper
9
11
  # @param query [String] example user query
10
12
  # @param program_name [String] the executable name
11
- def initialize(query:, program_name:)
12
- @query = query
13
- @program_name = program_name
13
+ def initialize(query: nil, program_name: nil)
14
+ @query = query || 'My new plugin'
15
+ @program_name = program_name || 'ego'
14
16
  end
15
17
 
16
18
  # Derive a slug from the user query.
@@ -18,13 +20,13 @@ module Ego
18
20
  # @return [String] slug
19
21
  def slug
20
22
  @slug ||= @query
21
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
22
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
23
- .tr('\'', '')
24
- .gsub(/\W+/, '_')
25
- .gsub(/__+/, '_')
26
- .sub(/_$/, '')
27
- .downcase
23
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
24
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
25
+ .tr('\'', '')
26
+ .gsub(/\W+/, '_')
27
+ .gsub(/__+/, '_')
28
+ .sub(/_$/, '')
29
+ .downcase
28
30
  end
29
31
 
30
32
  # Derive a plug-in path from the user query.
@@ -32,7 +34,7 @@ module Ego
32
34
  # @return [String] plug-in path
33
35
  def path
34
36
  @path ||= Filesystem.config("plugins/#{slug}.rb")
35
- .sub(/^#{ENV['HOME']}/, '~')
37
+ .sub(/^#{ENV['HOME']}/, '~')
36
38
  end
37
39
 
38
40
  # Provide a hint for initializing a new plug-in.
@@ -40,19 +42,19 @@ module Ego
40
42
  # @return [String] hint text
41
43
  def hint
42
44
  require 'shellwords'
43
- @hint ||= <<~EOF
45
+ @hint ||= <<~HINT
44
46
  I don't understand "#{@query}".
45
47
 
46
48
  If you would like to add this capability, start by running:
47
49
  #{@program_name} #{@query.shellescape} > #{path}
48
- EOF
50
+ HINT
49
51
  end
50
52
 
51
53
  # Provide a template for initializing a new plug-in.
52
54
  #
53
55
  # @return [String] template contents
54
56
  def template
55
- @template ||= <<~EOF
57
+ @template ||= <<~TEMPLATE
56
58
  Ego.plugin do |robot|
57
59
  robot.can 'do something new'
58
60
 
@@ -60,7 +62,7 @@ module Ego
60
62
  alert 'Not implemented yet. Go ahead and edit #{path}.'
61
63
  end
62
64
  end
63
- EOF
65
+ TEMPLATE
64
66
  end
65
67
  end
66
68
  end
@@ -1,17 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'list capabilities'
3
5
 
4
6
  robot.on(
5
7
  /^(show me|show|tell me|list)\s+(handlers|what you can do|what (?:you are|you're) able to do|what you do|what (?:queries )?you (?:can )?understand)$/i => 5,
6
8
  /^what (?:can you|are you able to|do you) (?:do|handle|understand)\??$/i => 5,
7
- /^help/i => 2,
9
+ /^help/i => 2
8
10
  ) do
9
11
  say 'I can...'
10
12
 
11
13
  @capabilities.each do |cap|
12
14
  builtin = cap.plugin.builtin ? '*' : ''
13
- plugin = sprintf('(%s%s)', cap.plugin.name, builtin).magenta
14
- printf("- %s %s\n", cap.to_s, plugin)
15
+ plugin = format('(%<name>s%<builtin>s)',
16
+ name: cap.plugin.name,
17
+ builtin: builtin).magenta
18
+ printf("- %<desc>s %<plugin>s\n",
19
+ desc: cap.to_s,
20
+ plugin: plugin)
15
21
  end
16
22
  end
17
23
 
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'help you write plug-ins'
3
5
 
4
6
  robot.on_unhandled_query do |query|
5
- require 'ego/plugin_helper'
7
+ require_relative '../plugin_helper'
6
8
 
7
9
  helper = Ego::PluginHelper.new(
8
10
  query: query,
9
11
  program_name: options.usage.program_name
10
12
  )
11
13
 
12
- if $stdout.isatty
13
- alert helper.hint
14
- end
14
+ alert helper.hint if $stdout.isatty
15
+
16
+ puts helper.template if verbose? || !$stdout.isatty
17
+ end
15
18
 
16
- if verbose? || !$stdout.isatty
17
- puts helper.template
18
- end
19
+ robot.on(/^$/) do
20
+ say %w[Yes? Hello? ...?].sample
19
21
  end
20
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Ego.plugin do |robot|
2
4
  robot.can 'output text to the terminal'
3
5