jarvis-cli 0.0.1 → 0.0.2

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 (45) hide show
  1. checksums.yaml +5 -13
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +6 -0
  4. data/README.md +69 -5
  5. data/bin/jarvis +9 -0
  6. data/features/fact_service.feature +7 -0
  7. data/features/step_definitions/fact_service_steps.rb +7 -0
  8. data/features/step_definitions/server_steps.rb +3 -0
  9. data/features/support/env.rb +38 -0
  10. data/features/support/hooks.rb +1 -0
  11. data/features/support/vcr_cassettes/Fact_Service/Jarvis_responds_with_a_random_fact.yml +52 -0
  12. data/jarvis-cli.gemspec +8 -0
  13. data/lib/jarvis/cli.rb +25 -0
  14. data/lib/jarvis/core_ext/symbol.rb +7 -0
  15. data/lib/jarvis/core_ext.rb +1 -0
  16. data/lib/jarvis/interpreter.rb +32 -0
  17. data/lib/jarvis/server.rb +19 -0
  18. data/lib/jarvis/service.rb +73 -6
  19. data/lib/jarvis/services/dice.rb +25 -0
  20. data/lib/jarvis/services/eightball.rb +38 -0
  21. data/lib/jarvis/services/fact.rb +7 -0
  22. data/lib/jarvis/services/null_service.rb +2 -0
  23. data/lib/jarvis/services.rb +4 -0
  24. data/lib/jarvis/slack/message.rb +17 -0
  25. data/lib/jarvis/slack.rb +1 -0
  26. data/lib/jarvis/test_support/test_support.rb +20 -0
  27. data/lib/jarvis/version.rb +1 -1
  28. data/lib/jarvis.rb +19 -2
  29. data/spec/jarvis/interpreter_spec.rb +61 -0
  30. data/spec/jarvis/jarvis_spec.rb +6 -4
  31. data/spec/jarvis/server_spec.rb +11 -2
  32. data/spec/jarvis/service_spec.rb +93 -14
  33. data/spec/services/dice_spec.rb +22 -0
  34. data/spec/services/eightball_spec.rb +9 -0
  35. data/spec/services/fact_spec.rb +7 -0
  36. data/spec/services/null_service_spec.rb +3 -0
  37. data/spec/spec_helper.rb +53 -0
  38. data/spec/support/fixtures/vcr_cassettes/fact.yml +57 -0
  39. data/spec/support/shared_contexts/server.rb +9 -0
  40. data/spec/support/shared_contexts/service.rb +10 -0
  41. data/templates/project/Gemfile +3 -0
  42. data/templates/project/app/server.rb +2 -0
  43. data/templates/project/app/services/.gitkeep +0 -0
  44. data/templates/project/config.ru +4 -0
  45. metadata +177 -20
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Jarvis::Interpreter do
4
+ def message(params)
5
+ Slack::Message.new(slack_outgoing_message(params))
6
+ end
7
+
8
+ describe "Defaults to NullService" do
9
+ before { Jarvis.register_services :test_service }
10
+ subject { described_class.new(message text: "success kid") }
11
+ it { expect(subject.determine_service).to eq NullService }
12
+ end
13
+
14
+ describe "Routes Services based on the registered services phrases" do
15
+ before { service.phrases = "success kid" }
16
+ before { Jarvis.register_services :test_service }
17
+ context "A Match" do
18
+ subject { described_class.new(message text: "success kid") }
19
+ it { expect(subject.determine_service).to eq service }
20
+ end
21
+ context "No Match" do
22
+ subject { described_class.new(message text: "blah") }
23
+ it { expect(subject.determine_service).to eq NullService }
24
+ end
25
+ context "Generated regex is case insensitive" do
26
+ subject { described_class.new(message text: "SUCCESS KID") }
27
+ it { expect(subject.determine_service).to eq service}
28
+ end
29
+ context "Multiple phrases route correctly" do
30
+ before(:each) { other_service.phrases = "overly attached bot", "hello world" }
31
+ before { Jarvis.register_services :other_test_service }
32
+ it { expect(described_class.new(message text: "hello world").determine_service).to eq other_service }
33
+ it { expect(described_class.new(message text: "overly attached bot").determine_service).to eq other_service }
34
+ end
35
+ end
36
+
37
+ describe "Routes services based on the registered interpreter pattern" do
38
+ before { service.interpreter_pattern = /success kid/i }
39
+ before { Jarvis.register_services :test_service }
40
+ context "A Match" do
41
+ subject { described_class.new(message text: "success kid") }
42
+ it { expect(subject.determine_service).to eq service }
43
+ end
44
+ context "No Match" do
45
+ subject { described_class.new(message text: "blah") }
46
+ it { expect(subject.determine_service).to eq NullService }
47
+ end
48
+ end
49
+
50
+ describe "Routes to the correct service" do
51
+ let(:correct_service) { service }
52
+ let(:incorrect_service) { other_service }
53
+ before { correct_service.interpreter_pattern = /success kid/i }
54
+ before { incorrect_service.interpreter_pattern = /overly attached chatbot/i }
55
+ before { Jarvis.register_services :test_service, :other_test_service }
56
+ context "Routes to Test Service" do
57
+ subject { described_class.new(message text: "success kid hello, world") }
58
+ it { expect(subject.determine_service).to eq correct_service }
59
+ end
60
+ end
61
+ end
@@ -4,10 +4,12 @@ RSpec.describe Jarvis do
4
4
 
5
5
  describe "Service Registry" do
6
6
  it "Allows you to register a Jarvis::Service subclass" do
7
- class MyRegisteredService < Jarvis::Service
8
- end
9
- Jarvis.register(MyRegisteredService)
10
- expect(Jarvis.services).to eq [MyRegisteredService]
7
+ Jarvis.register_services(:test_service)
8
+ expect(Jarvis.services).to eq [:test_service]
9
+ end
10
+ it "Works with multiple classes" do
11
+ Jarvis.register_services :test_service, :other_test_service
12
+ expect(Jarvis.services).to eq [:test_service, :other_test_service]
11
13
  end
12
14
  end
13
15
 
@@ -2,10 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe Jarvis::Server do
4
4
  let(:parsed_response) { JSON.parse(last_response.body) }
5
-
5
+ let(:message) { parsed_response["text"] }
6
6
  describe "get /" do
7
7
  before { get "/" }
8
8
  it { expect(last_response).to be_ok }
9
- it { expect(parsed_response["text"]).to eq "Hello, I'm Jarvis" }
9
+ it { expect(message).to eq "Hello, I'm Jarvis" }
10
+ end
11
+
12
+ describe "unfit environment" do
13
+ let(:slack_message) { slack_outgoing_message(text: "Jarvis, success kid blah blah blah") }
14
+ before { service.environment :blah }
15
+ before { service.phrases = "success kid" }
16
+ before { Jarvis.register_services :test_service }
17
+ before { post "/jarvis", slack_message }
18
+ it { expect(message).to eq "I'm really sorry, but that sevice needs to be configured" }
10
19
  end
11
20
  end
@@ -1,33 +1,112 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Jarvis::Service do
4
-
4
+ let(:message) { double("Slack::Message") }
5
5
  describe "Validate Environment Variables are Present" do
6
-
7
- class MyService < Jarvis::Service
8
- required_environment "SERVICE_TOKEN"
9
- end
10
-
11
- subject { MyService.new }
12
-
6
+ before { service.environment :service_token }
7
+ subject { service.new message }
13
8
  context "Required Environment not set up" do
14
9
  it { expect { subject.validate_environment }.to raise_exception(Jarvis::UnfitEnvironmentException) }
15
10
  end
16
11
 
17
12
  context "Environment Correctly Set Up" do
18
- before { ENV["SERVICE_TOKEN"] = "token" }
13
+ before { stub_env("SERVICE_TOKEN" => "token") }
14
+ it { expect { subject.validate_environment }.to_not raise_exception }
15
+ end
16
+
17
+ context "tries lowercase environment variable if uppercase isn't present" do
18
+ before { service.environment :service_token }
19
+ before { stub_env("SERVICE_TOKEN" => nil, "service_token" => "token")}
20
+ subject { service.new message }
19
21
  it { expect { subject.validate_environment }.to_not raise_exception }
20
22
  end
21
23
 
22
24
  context "Multiple Required Variables, Some not present" do
23
- class MyOtherService < Jarvis::Service
24
- required_environment "SERVICE_TOKEN", "SERVICE_SECRET"
25
- end
26
- subject { MyOtherService.new }
27
- before { ENV["SERVICE_TOKEN"] = "token" }
25
+ before { service.environment :service_token, :service_secret }
26
+ before { stub_env("SERVICE_TOKEN" => "token", "SERVICE_SECRET" => nil, "service_secret" => nil) }
28
27
  it { expect { subject.validate_environment }.to raise_exception(Jarvis::UnfitEnvironmentException) }
29
28
  end
29
+ end
30
+
31
+ describe "Expose Pattern For the Service to be Interpreted" do
32
+ before { service.interpreter_pattern = /are you up/i }
33
+ it { expect(service.interpreter_pattern).to eq /are you up/i }
34
+ end
35
+
36
+ describe "Allow the Developer to make a list of phrases to be concatenated into the interpreter pattern" do
37
+ context "Accepts a single phrase" do
38
+ before { service.phrases = "success kid" }
39
+ it { expect(service.interpreter_pattern ).to eq /success kid/i }
40
+ end
41
+
42
+ context "Multiple Phrases" do
43
+ before { service.phrases = "success kid", "hello world" }
44
+ it { expect(service.interpreter_pattern).to eq /success kid|hello world/i }
45
+
46
+ end
47
+ end
48
+
49
+ describe "Environment Accessor" do
50
+ context "Uppercase" do
51
+ before { service.environment :service_token }
52
+ before { stub_env("SERVICE_TOKEN" => "token") }
53
+ subject { service.new message }
54
+ it { expect(subject.service_token).to eq "token"}
55
+ end
56
+ context "Lowercase" do
57
+ before { service.environment :service_token }
58
+ before { stub_env("SERVICE_TOKEN" => nil, "service_token" => "token") }
59
+ subject { service.new message }
60
+ it { expect(subject.service_token).to eq "token"}
61
+ end
62
+ end
63
+
64
+ describe "invoke_with" do
65
+ describe "defaults to run" do
66
+ before do
67
+ class TestService < Jarvis::Service
68
+ def run
69
+ "Hello World"
70
+ end
71
+ end
72
+ end
73
+ it { expect(service.new(message).invoke).to eq "Hello World" }
74
+ end
75
+
76
+ describe "Can be overridden with #invoke_with" do
77
+ before do
78
+ class OtherTestService < Jarvis::Service
79
+ invoke_with :call_me_maybe
80
+ def call_me_maybe
81
+ "This is crazy"
82
+ end
83
+ end
84
+ end
85
+ it { expect(other_service.new(message).invoke).to eq "This is crazy" }
86
+ end
87
+
88
+ end
89
+
90
+ describe "before_invoke" do
91
+ before { class TestService < Jarvis::Service
92
+ before_invoke :puts_hello
93
+ def puts_hello
94
+ puts "Hello"
95
+ end
96
+ end }
97
+
98
+ it { expect { service.new(message).invoke }.to output("Hello\n").to_stdout }
99
+ end
100
+
101
+ describe "after_invoke" do
102
+ before { class TestService < Jarvis::Service
103
+ after_invoke :puts_hello
104
+ def puts_hello
105
+ puts "Hello"
106
+ end
107
+ end }
30
108
 
109
+ it { expect { service.new(message).invoke }.to output("Hello\n").to_stdout }
31
110
  end
32
111
 
33
112
  end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Dice do
4
+ context "d6" do
5
+ let(:message) { Slack::Message.new slack_outgoing_message(text: "Jarvis, roll 1d6") }
6
+ subject { described_class.new(message) }
7
+ before { allow(subject).to receive(:roll) { 5 }}
8
+ it { expect(subject.invoke).to eq "You got a 5" }
9
+ end
10
+ context "2d6" do
11
+ let(:message) { Slack::Message.new slack_outgoing_message(text: "Jarvis, roll 2d6") }
12
+ subject { described_class.new(message) }
13
+ before { allow(subject).to receive(:roll).and_return(1, 6) }
14
+ it { expect(subject.invoke).to eq "You got a 7" }
15
+ end
16
+ context "5d20" do
17
+ let(:message) { Slack::Message.new slack_outgoing_message(text: "Jarvis, roll 5d20") }
18
+ subject { described_class.new(message) }
19
+ before { allow(subject).to receive(:roll).and_return(1, 12, 20, 3, 4) }
20
+ it { expect(subject.invoke).to eq "You got a 40" }
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Eightball do
4
+
5
+ let(:message) { Slack::Message.new slack_outgoing_message(text: "Jarvis, will I ship this gem today?")}
6
+ subject { described_class.new(message) }
7
+ before { allow_any_instance_of(Array).to receive(:sample) { "Outlook not so good" } }
8
+ specify { expect(subject.invoke).to eq "Outlook not so good" }
9
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Fact, vcr: { cassette_name: 'fact' } do
4
+ let(:message) { Slack::Message.new(slack_outgoing_message(text: "Jarvis, fact")) }
5
+ subject { described_class.new message }
6
+ it { expect(subject.invoke).to eq "182 is the carat of the Star of Bombay cabochon-cut star sapphire originating from Sri Lanka." }
7
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.describe NullService do
2
+
3
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,10 @@ require 'rspec'
3
3
  require 'rack/test'
4
4
  require 'json'
5
5
  require 'jarvis'
6
+ require 'byebug'
7
+ require 'vcr'
8
+ require_relative './support/shared_contexts/service'
9
+ require_relative './support/shared_contexts/server'
6
10
 
7
11
  include Rack::Test::Methods
8
12
 
@@ -10,6 +14,55 @@ def app
10
14
  Jarvis::Server
11
15
  end
12
16
 
17
+ def stub_env(hash)
18
+ hash.each do |key, value|
19
+ allow(ENV).to receive(:[]).with(key) { value }
20
+ end
21
+ end
22
+
23
+ def slack_outgoing_message(options={text:"Jarvis, what's going on?"})
24
+ {
25
+ "team_id" => options[:team_id] || "T0001",
26
+ "channel_id" => options[:channel_id] || "BLAH",
27
+ "channel_name" => options[:channel_name] || "test",
28
+ "timestamp" => options[:timestamp] || "1355517523.000005",
29
+ "user_id" => options[:user_id] || "U2147483697",
30
+ "user_name" => options[:user_name] || "Steve",
31
+ "text" => options[:text],
32
+ "trigger_word" => options[:trigger_word] || "Jarvis"
33
+ }
34
+ end
35
+
13
36
  RSpec.configure do |config|
14
37
  config.expose_dsl_globally = false
38
+ config.around(:each) do |example|
39
+ class TestService < Jarvis::Service
40
+ def run
41
+
42
+ end
43
+ end
44
+ class OtherTestService < Jarvis::Service
45
+ def run
46
+
47
+ end
48
+ end
49
+ class TestServer < Jarvis::Server
50
+ end
51
+
52
+ example.run
53
+
54
+ Jarvis.clear_services
55
+
56
+ Object.send :remove_const, :TestServer
57
+ Object.send :remove_const, :TestService
58
+ Object.send :remove_const, :OtherTestService
59
+ end
60
+ config.include ServerContext
61
+ config.include ServiceContext
62
+ end
63
+
64
+ VCR.configure do |config|
65
+ config.cassette_library_dir = "spec/support/fixtures/vcr_cassettes"
66
+ config.configure_rspec_metadata!
67
+ config.hook_into :webmock
15
68
  end
@@ -0,0 +1,57 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://numbersapi.com/random
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Server:
22
+ - nginx/1.4.6 (Ubuntu)
23
+ Date:
24
+ - Fri, 20 Feb 2015 05:01:41 GMT
25
+ Content-Type:
26
+ - text/plain; charset="UTF-8"; charset=utf-8
27
+ Content-Length:
28
+ - '93'
29
+ X-Powered-By:
30
+ - Express
31
+ Access-Control-Allow-Origin:
32
+ - "*"
33
+ Access-Control-Allow-Headers:
34
+ - X-Requested-With
35
+ X-Numbers-Api-Number:
36
+ - '182'
37
+ X-Numbers-Api-Type:
38
+ - trivia
39
+ Pragma:
40
+ - no-cache
41
+ Cache-Control:
42
+ - no-cache
43
+ Expires:
44
+ - '0'
45
+ Proxy-Connection:
46
+ - Keep-Alive
47
+ Connection:
48
+ - Keep-Alive
49
+ Age:
50
+ - '0'
51
+ body:
52
+ encoding: UTF-8
53
+ string: 182 is the carat of the Star of Bombay cabochon-cut star sapphire originating
54
+ from Sri Lanka.
55
+ http_version:
56
+ recorded_at: Fri, 20 Feb 2015 05:01:27 GMT
57
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,9 @@
1
+ RSpec.shared_context "server" do
2
+ let(:service) { TestServer }
3
+ end
4
+
5
+ module ServerContext
6
+ def self.included(base)
7
+ base.include_context "server"
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ RSpec.shared_context "service" do
2
+ let(:service) { TestService }
3
+ let(:other_service) { OtherTestService }
4
+ end
5
+
6
+ module ServiceContext
7
+ def self.included(base)
8
+ base.include_context "service"
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'jarvis-cli', require: 'jarvis', path: "~/code/jarvis-cli"
@@ -0,0 +1,2 @@
1
+ class Server < Jarvis::Server
2
+ end
File without changes
@@ -0,0 +1,4 @@
1
+ $: << File.expand_path('../', __FILE__)
2
+ require 'jarvis'
3
+ require './app/server.rb'
4
+ run Server