pra 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ module Pra
2
+ class WindowSystem
3
+ class PureVirtualMethodNotImplemented < RuntimeError; end
4
+
5
+ # This method is a pure virtual method and is intended for the inheriting
6
+ # class to implement it in order to handle all the initialization and
7
+ # setup of the windowing system the inheriting class is implementing.
8
+ def setup
9
+ raise PureVirtualMethodNotImplemented, "the 'setup' method needs to be implemented."
10
+ end
11
+
12
+ # This method is a pure virtual method and is called by the system
13
+ # right before the fetching of the pull requests is started. It is simply
14
+ # intended to be a notification to the window system so that it can notify
15
+ # the user in whatever is an appropriate fashion that it is in the process
16
+ # of fetching the pull requests.
17
+ def fetching_pull_requests
18
+ raise PureVirtualMethodNotImplemented, "the 'fetching_pull_requests' method needs to be implemented."
19
+ end
20
+
21
+ # This method is a pure virtual method and is intendend for the inheriting
22
+ # class to implement it to handle refreshing the pull requests within in
23
+ # the window system. This method is called evertime the pull request
24
+ # fetching system fetches pull requests.
25
+ def refresh_pull_requests(pull_requests)
26
+ raise PureVirtualMethodNotImplemented, "the 'refresh_pull_requests' method needs to be implemented."
27
+ end
28
+
29
+ # This method is a pure virtual method and is intended for the inherting
30
+ # class to implement it in order to initiate the main run loop of the
31
+ # windowing system the inheriting class is implementing. It can also be
32
+ # used to include any tear down that needs to happen when the run loop
33
+ # exits.
34
+ def run_loop
35
+ raise PureVirtualMethodNotImplemented, "the 'run_loop' method needs to be implemented."
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ require 'pra/curses_window_system'
2
+
3
+ module Pra
4
+ module WindowSystemFactory
5
+ class UnknownWindowSystemId < RuntimeError; end
6
+
7
+ def self.build(window_system_id)
8
+ case window_system_id
9
+ when 'curses'
10
+ return Pra::CursesWindowSystem.new
11
+ else
12
+ raise Pra::WindowSystemFactory::UnknownWindowSystemId, ".build() doesn't know about a windows system identified by '#{window_system_id}'"
13
+ end
14
+ end
15
+ end
16
+ end
data/lib/pra.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "pra/version"
2
+
3
+ module Pra
4
+ # Your code goes here...
5
+ end
data/pra.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pra/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pra"
8
+ spec.version = Pra::VERSION
9
+ spec.authors = ["Andrew De Ponte"]
10
+ spec.email = ["cyphactor@gmail.com"]
11
+ spec.description = %q{Command Line utility to make you aware of open pull-requests across systems at all times.}
12
+ spec.summary = %q{CLI tool that shows open pull-requests across systems.}
13
+ spec.homepage = "http://github.com/reachlocal/pra"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec", "~> 2.14.1"
24
+
25
+ spec.add_dependency "rest-client", "~> 1.6.7"
26
+ spec.add_dependency "launchy", "~> 2.3.0"
27
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "../../../lib/pra/app"
2
+
3
+ describe Pra::App do
4
+ describe "#run" do
5
+ it "builds the window system" do
6
+ subject.stub(:spawn_pull_request_fetcher)
7
+ window_system_double = double('window system', setup: nil, run_loop: nil)
8
+ expect(Pra::WindowSystemFactory).to receive(:build).with('curses').and_return(window_system_double)
9
+ subject.run
10
+ end
11
+
12
+ it "sets up the window system" do
13
+ subject.stub(:spawn_pull_request_fetcher)
14
+ window_system_double = double('window system', run_loop: nil)
15
+ Pra::WindowSystemFactory.stub(:build).and_return(window_system_double)
16
+ expect(window_system_double).to receive(:setup)
17
+ subject.run
18
+ end
19
+
20
+ it "spawns the pull request fetcher thread" do
21
+ window_system_double = double('window system', setup: nil, run_loop: nil)
22
+ Pra::WindowSystemFactory.stub(:build).and_return(window_system_double)
23
+ expect(subject).to receive(:spawn_pull_request_fetcher)
24
+ subject.run
25
+ end
26
+
27
+ it "starts the window system run loop" do
28
+ subject.stub(:spawn_pull_request_fetcher)
29
+ window_system_double = double('window system', setup: nil, refresh_pull_requests: nil)
30
+ Pra::WindowSystemFactory.stub(:build).and_return(window_system_double)
31
+ expect(window_system_double).to receive(:run_loop)
32
+ subject.run
33
+ end
34
+ end
35
+
36
+ describe "#fetch_and_refresh_pull_requests" do
37
+ it "notifies the window system it is starting to fetch pull requests" do
38
+ Kernel.stub(:sleep)
39
+ Pra::PullRequestService.stub(:fetch_pull_requests)
40
+ window_system_double = double('window system', refresh_pull_requests: nil)
41
+ subject.instance_variable_set(:@window_system, window_system_double)
42
+ expect(window_system_double).to receive(:fetching_pull_requests)
43
+ subject.fetch_and_refresh_pull_requests
44
+ end
45
+
46
+ it "fetches the pull requests from all of the sources" do
47
+ Kernel.stub(:sleep)
48
+ window_system_double = double('window system', refresh_pull_requests: nil, fetching_pull_requests: nil)
49
+ subject.instance_variable_set(:@window_system, window_system_double)
50
+ expect(Pra::PullRequestService).to receive(:fetch_pull_requests)
51
+ subject.fetch_and_refresh_pull_requests
52
+ end
53
+
54
+ it "tells the window system to refresh pull requests" do
55
+ Kernel.stub(:sleep)
56
+ pull_requests = double('fetched pull requests')
57
+ Pra::PullRequestService.stub(:fetch_pull_requests).and_return(pull_requests)
58
+ window_system_double = double('window system', fetching_pull_requests: nil)
59
+ subject.instance_variable_set(:@window_system, window_system_double)
60
+ expect(window_system_double).to receive(:refresh_pull_requests).with(pull_requests)
61
+ subject.fetch_and_refresh_pull_requests
62
+ end
63
+
64
+ it "sleeps for the polling frequency" do
65
+ window_system_double = double('window system', refresh_pull_requests: nil, fetching_pull_requests: nil)
66
+ subject.instance_variable_set(:@window_system, window_system_double)
67
+ Pra::PullRequestService.stub(:fetch_pull_requests)
68
+ expect(Kernel).to receive(:sleep)
69
+ subject.fetch_and_refresh_pull_requests
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,128 @@
1
+ require_relative "../../../lib/pra/config"
2
+
3
+ describe Pra::Config do
4
+ describe "#initialize" do
5
+ it "assigns the provide default config hash" do
6
+ config_hash = { some: "hash" }
7
+ config = Pra::Config.new(config_hash)
8
+ config.instance_variable_get(:@initial_config).should eq(config_hash)
9
+ end
10
+ end
11
+
12
+ describe ".load_config" do
13
+ subject { Pra::Config }
14
+
15
+ it "parses the config file" do
16
+ subject.should_receive(:parse_config_file)
17
+ subject.load_config
18
+ end
19
+
20
+ it "constructs an instance of the config from the parsed config" do
21
+ parsed_config = double('parsed config file')
22
+ subject.stub(:parse_config_file).and_return(parsed_config)
23
+ subject.should_receive(:new).with(parsed_config)
24
+ subject.load_config
25
+ end
26
+
27
+ it "returns the instance of the config object" do
28
+ subject.stub(:parse_config_file)
29
+ config = double('config')
30
+ subject.stub(:new).and_return(config)
31
+ subject.load_config.should eq(config)
32
+ end
33
+ end
34
+
35
+ describe ".parse_config_file" do
36
+ subject { Pra::Config }
37
+
38
+ it "reads the users config" do
39
+ subject.stub(:json_parse)
40
+ subject.should_receive(:read_config_file)
41
+ subject.parse_config_file
42
+ end
43
+
44
+ it "json parses the config contents" do
45
+ config_contents = double('config contents')
46
+ subject.stub(:read_config_file).and_return(config_contents)
47
+ subject.should_receive(:json_parse).with(config_contents)
48
+ subject.parse_config_file
49
+ end
50
+ end
51
+
52
+ describe ".read_config_file" do
53
+ subject { Pra::Config }
54
+
55
+ it "opens the file" do
56
+ config_path = double('config path')
57
+ subject.stub(:config_path).and_return(config_path)
58
+ File.should_receive(:open).with(config_path, "r").and_return(double('config file').as_null_object)
59
+ subject.read_config_file
60
+ end
61
+
62
+ it "reads the files contents" do
63
+ subject.stub(:config_path)
64
+ file = double('config file').as_null_object
65
+ File.stub(:open).and_return(file)
66
+ file.should_receive(:read)
67
+ subject.read_config_file
68
+ end
69
+
70
+ it "closes the file" do
71
+ subject.stub(:config_path)
72
+ file = double('config file', read: nil)
73
+ File.stub(:open).and_return(file)
74
+ file.should_receive(:close)
75
+ subject.read_config_file
76
+ end
77
+
78
+ it "returns the file contents" do
79
+ subject.stub(:config_path)
80
+ file = double('config file', close: nil)
81
+ File.stub(:open).and_return(file)
82
+ file.stub(:read).and_return('some file contents')
83
+ subject.read_config_file.should eq('some file contents')
84
+ end
85
+ end
86
+
87
+ describe ".config_path" do
88
+ subject { Pra::Config }
89
+
90
+ it "returns the joined users home directory and .pra.json to create the path" do
91
+ subject.stub(:users_home_directory).and_return('/home/someuser')
92
+ subject.config_path.should eq('/home/someuser/.pra.json')
93
+ end
94
+ end
95
+
96
+ describe ".users_home_directory" do
97
+ subject { Pra::Config }
98
+
99
+ it "returns the current users home directory" do
100
+ ENV['HOME'] = '/home/someuser'
101
+ subject.users_home_directory.should eq('/home/someuser')
102
+ end
103
+ end
104
+
105
+ describe ".json_parse" do
106
+ subject { Pra::Config }
107
+
108
+ it "parses the given content as json" do
109
+ content = double('some json content')
110
+ JSON.should_receive(:parse).with(content)
111
+ subject.json_parse(content)
112
+ end
113
+
114
+ it "returns the parsed result" do
115
+ parsed_json = double('the parsed json')
116
+ JSON.stub(:parse).and_return(parsed_json)
117
+ subject.json_parse(double).should eq(parsed_json)
118
+ end
119
+ end
120
+
121
+ describe "#pull_sources" do
122
+ it "returns the pull sources value out of the config" do
123
+ pull_source_configs = double('pull source configs')
124
+ subject.instance_variable_set(:@initial_config, { "pull_sources" => pull_source_configs })
125
+ subject.pull_sources.should eq(pull_source_configs)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "../../../lib/pra/curses_window_system"
2
+
3
+ describe Pra::CursesWindowSystem do
4
+ end
@@ -0,0 +1,128 @@
1
+ require_relative "../../../lib/pra/github_pull_source"
2
+
3
+ describe Pra::GithubPullSource do
4
+ describe "#pull_requests" do
5
+ it "gets all the repositories" do
6
+ subject.should_receive(:repositories).and_return([])
7
+ subject.pull_requests
8
+ end
9
+
10
+ it "gets the pull requests for each repository" do
11
+ config = {
12
+ "protocol" => "https",
13
+ "host" => "my.github.instance",
14
+ "username" => "foo",
15
+ "password" => "bar",
16
+ "repositories" => [
17
+ { "owner" => "reachlocal", "repository" => "snapdragon" }
18
+ ]
19
+ }
20
+ pull_source = Pra::GithubPullSource.new(config)
21
+ pull_source.should_receive(:get_repo_pull_requests).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return([])
22
+ pull_source.pull_requests
23
+ end
24
+
25
+ it "returns the collection of all of the pull requests for the configured repos" do
26
+ config = {
27
+ "protocol" => "https",
28
+ "host" => "my.github.instance",
29
+ "username" => "foo",
30
+ "password" => "bar",
31
+ "repositories" => [
32
+ { "owner" => "reachlocal", "repository" => "snapdragon" },
33
+ { "owner" => "realpractice", "repository" => "rliapi" }
34
+ ]
35
+ }
36
+ pull_request_one = double('pull request one')
37
+ pull_request_two = double('pull request two')
38
+ pull_source = Pra::GithubPullSource.new(config)
39
+ pull_source.stub(:get_repo_pull_requests).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return([pull_request_one])
40
+ pull_source.stub(:get_repo_pull_requests).with({ "owner" => "realpractice", "repository" => "rliapi" }).and_return([pull_request_two])
41
+ pull_source.pull_requests.should eq([pull_request_one, pull_request_two])
42
+ end
43
+ end
44
+
45
+ describe "#repositories" do
46
+ it "returns the repositories segment of the config" do
47
+ config = {
48
+ "protocol" => "https",
49
+ "host" => "my.github.instance",
50
+ "username" => "foo",
51
+ "password" => "bar",
52
+ "repositories" => [
53
+ { "owner" => "reachlocal", "repository" => "snapdragon" }
54
+ ]
55
+ }
56
+ pull_source = Pra::GithubPullSource.new(config)
57
+ pull_source.repositories.should eq([{ "owner" => "reachlocal", "repository" => "snapdragon" }])
58
+ end
59
+ end
60
+
61
+ describe "#get_repo_pull_requests" do
62
+ it "requests the pull requests for the given repo" do
63
+ config = {
64
+ "protocol" => "https",
65
+ "host" => "my.github.instance",
66
+ "username" => "foo",
67
+ "password" => "bar",
68
+ "repositories" => [
69
+ { "owner" => "reachlocal", "repository" => "snapdragon" }
70
+ ]
71
+ }
72
+ pull_source = Pra::GithubPullSource.new(config)
73
+ the_resource = double
74
+ pull_source.stub(:rest_api_pull_request_resource).with({ "owner" => "reachlocal", "repository" => "snapdragon" }).and_return(the_resource)
75
+ the_resource.should_receive(:get).and_return('[]')
76
+ pull_source.get_repo_pull_requests({ "owner" => "reachlocal", "repository" => "snapdragon" })
77
+ end
78
+ end
79
+
80
+ describe "#rest_api_pull_request_url" do
81
+ let(:config) do
82
+ {
83
+ "protocol" => "https",
84
+ "host" => "my.github.instance",
85
+ "username" => "foo",
86
+ "password" => "bar",
87
+ "repositories" => [
88
+ { "owner" => "reachlocal", "repository" => "snapdragon" }
89
+ ]
90
+ }
91
+ end
92
+
93
+ it "returns the pull request url compiled from the config options" do
94
+ pull_source = Pra::GithubPullSource.new(config)
95
+ pull_source.rest_api_pull_request_url({ "owner" => "reachlocal", "repository" => "snapdragon" }).should eq("https://my.github.instance/repos/reachlocal/snapdragon/pulls")
96
+ end
97
+ end
98
+
99
+ describe "#rest_api_pull_request_resource" do
100
+ let(:config) do
101
+ {
102
+ "protocol" => "https",
103
+ "host" => "my.github.instance",
104
+ "username" => "foo",
105
+ "password" => "bar",
106
+ "repositories" => [
107
+ { "owner" => "reachlocal", "repository" => "snapdragon" }
108
+ ]
109
+ }
110
+ end
111
+
112
+ let(:repo_config) { {"owner" => "reachlocal", "repository" => "snapdragon"} }
113
+
114
+ subject { Pra::GithubPullSource.new(config) }
115
+
116
+ it "gets the repository url compiled from the config options" do
117
+ subject.should_receive(:rest_api_pull_request_url).with(repo_config)
118
+ subject.rest_api_pull_request_resource(repo_config)
119
+ end
120
+
121
+ it "builds a restclient resource using the pull request url and user credentials" do
122
+ url = "https://my.github.instance/repos/reachlocal/snapdragon/pulls"
123
+ subject.stub(:rest_api_pull_request_url).and_return(url)
124
+ RestClient::Resource.should_receive(:new).with(url, {user: "foo", password: "bar", content_type: :json, accept: :json})
125
+ subject.rest_api_pull_request_resource(repo_config)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,78 @@
1
+ require_relative '../../../lib/pra/pull_request_service'
2
+
3
+ describe Pra::PullRequestService do
4
+ describe ".fetch_pull_requests" do
5
+ it "gets all the pull-request sources" do
6
+ subject.should_receive(:pull_sources).and_return([])
7
+ subject.fetch_pull_requests
8
+ end
9
+
10
+ it "gets the pull requests from each pull-request source" do
11
+ pull_source_one = double('pull source one')
12
+ pull_source_two = double('pull source two')
13
+ subject.stub(:pull_sources).and_return([pull_source_one, pull_source_two])
14
+ pull_source_one.should_receive(:pull_requests).and_return([])
15
+ pull_source_two.should_receive(:pull_requests).and_return([])
16
+ subject.fetch_pull_requests
17
+ end
18
+
19
+ it "returns an array of all the pull-requests from each of the sources" do
20
+ pull_request_one = double('pull request one')
21
+ pull_request_two = double('pull request two')
22
+ pull_source_one = double('pull source one', :pull_requests => [pull_request_one])
23
+ pull_source_two = double('pull source two', :pull_requests => [pull_request_two])
24
+ subject.stub(:pull_sources).and_return([pull_source_one, pull_source_two])
25
+ subject.fetch_pull_requests.should eq([pull_request_one, pull_request_two])
26
+ end
27
+ end
28
+
29
+ describe "#pull_sources" do
30
+ it "gets the users config" do
31
+ subject.stub(:map_config_to_pull_sources)
32
+ Pra::Config.should_receive(:load_config)
33
+ subject.pull_sources
34
+ end
35
+
36
+ it "maps the pull-request sources from the config to PullSource objects" do
37
+ config = double('users config')
38
+ Pra::Config.stub(:load_config).and_return(config)
39
+ subject.should_receive(:map_config_to_pull_sources).with(config)
40
+ subject.pull_sources
41
+ end
42
+
43
+ it "returns the mapped pull-request sources" do
44
+ Pra::Config.stub(:load_config)
45
+ sources = double('pull sources')
46
+ subject.stub(:map_config_to_pull_sources).and_return(sources)
47
+ subject.pull_sources.should eq(sources)
48
+ end
49
+ end
50
+
51
+ describe "#map_config_to_pull_sources" do
52
+ it "gets the pull sources from the config" do
53
+ config = double('config')
54
+ config.should_receive(:pull_sources).and_return([])
55
+ subject.map_config_to_pull_sources(config)
56
+ end
57
+
58
+ it "creates a PullSource based object for each configured pull source" do
59
+ pull_source_config_one = double('pull source config one')
60
+ pull_source_config_two = double('pull source config two')
61
+ config = double('config', pull_sources: [pull_source_config_one, pull_source_config_two])
62
+ Pra::PullSourceFactory.should_receive(:build_pull_source).with(pull_source_config_one)
63
+ Pra::PullSourceFactory.should_receive(:build_pull_source).with(pull_source_config_two)
64
+ subject.map_config_to_pull_sources(config)
65
+ end
66
+
67
+ it "returns an array of the constructed PullSource based objects" do
68
+ pull_source_one = double('pull source one')
69
+ pull_source_two = double('pull source two')
70
+ pull_source_config_one = double('pull source config one')
71
+ pull_source_config_two = double('pull source config two')
72
+ config = double('config', pull_sources: [pull_source_config_one, pull_source_config_two])
73
+ Pra::PullSourceFactory.stub(:build_pull_source).with(pull_source_config_one).and_return(pull_source_one)
74
+ Pra::PullSourceFactory.stub(:build_pull_source).with(pull_source_config_two).and_return(pull_source_two)
75
+ subject.map_config_to_pull_sources(config).should eq([pull_source_one, pull_source_two])
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "../../../lib/pra/pull_request"
2
+
3
+ describe Pra::PullRequest do
4
+ end
@@ -0,0 +1,40 @@
1
+ require_relative "../../../lib/pra/pull_source_factory"
2
+
3
+ describe Pra::PullSourceFactory do
4
+ describe ".build_pull_source" do
5
+ it "maps the pull source type to class" do
6
+ pull_source_type_specific_configs = double('pull source type specific configs')
7
+ pull_source_config = { "type" => "stash", "config" => pull_source_type_specific_configs }
8
+ subject.should_receive(:map_type_to_klass).with("stash").and_return(Pra::StashPullSource)
9
+ subject.build_pull_source(pull_source_config)
10
+ end
11
+
12
+ it "constructs the mapped PullSource based object using the given pull source type specific config" do
13
+ pull_source_type_specific_configs = double('pull source type specific configs')
14
+ pull_source_config = { "type" => "stash", "config" => pull_source_type_specific_configs }
15
+ Pra::StashPullSource.should_receive(:new).with(pull_source_type_specific_configs)
16
+ subject.build_pull_source(pull_source_config)
17
+ end
18
+
19
+ it "returns the instance of the previously constructed PullSource based object" do
20
+ pull_source = double('the constructed pull source')
21
+ pull_source_config = { "type" => "stash", "config" => {} }
22
+ Pra::StashPullSource.should_receive(:new).and_return(pull_source)
23
+ subject.build_pull_source(pull_source_config).should eq(pull_source)
24
+ end
25
+ end
26
+
27
+ describe ".map_type_to_klass" do
28
+ context "when given type is 'stash'" do
29
+ it "returns the StashPullSource class" do
30
+ subject.map_type_to_klass("stash").should eq(Pra::StashPullSource)
31
+ end
32
+ end
33
+
34
+ context "When given type is 'github'" do
35
+ it "returns the GithubPullSource class" do
36
+ subject.map_type_to_klass("github").should eq(Pra::GithubPullSource)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "../../../lib/pra/pull_source"
2
+
3
+ describe Pra::PullSource do
4
+ describe "#initialize" do
5
+ it "assigns the given config hash to an instance variable" do
6
+ config = double('config hash')
7
+ pull_source = Pra::PullSource.new(config)
8
+ pull_source.instance_variable_get(:@config).should eq(config)
9
+ end
10
+ end
11
+
12
+ describe "#pull_requests" do
13
+ it "raises an exception forcing inheriting classes to implement this method" do
14
+ expect { subject.pull_requests }.to raise_error(Pra::PullSource::NotImplemented)
15
+ end
16
+ end
17
+ end