pra 0.1.1

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.
@@ -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