gradesfirst 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +45 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +1 -0
- data/Rakefile +8 -0
- data/bin/gf +12 -0
- data/gradesfirst.gemspec +27 -0
- data/lib/gradesfirst.rb +2 -0
- data/lib/gradesfirst/branch_command.rb +59 -0
- data/lib/gradesfirst/cli.rb +58 -0
- data/lib/gradesfirst/command.rb +22 -0
- data/lib/gradesfirst/commit_message_command.rb +37 -0
- data/lib/gradesfirst/task_command.rb +47 -0
- data/lib/http_magic.rb +260 -0
- data/lib/pivotal_tracker.rb +16 -0
- data/test/branch_command_test.rb +32 -0
- data/test/cli_test.rb +53 -0
- data/test/command_test.rb +14 -0
- data/test/commit_message_command_test.rb +24 -0
- data/test/fixtures/commit_message.txt +4 -0
- data/test/fixtures/gf_branch.txt +3 -0
- data/test/fixtures/git_branch.txt +3 -0
- data/test/fixtures/projects.json +66 -0
- data/test/fixtures/story_bar.json +17 -0
- data/test/fixtures/story_foo.json +17 -0
- data/test/fixtures/tasks.json +22 -0
- data/test/fixtures/tasks.txt +4 -0
- data/test/http_magic/get_test.rb +68 -0
- data/test/http_magic/uri_test.rb +76 -0
- data/test/pivotal_tracker_test.rb +46 -0
- data/test/task_command_test.rb +52 -0
- data/test/test_helper.rb +29 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.3.5)
|
5
|
+
coderay (1.1.0)
|
6
|
+
columnize (0.3.6)
|
7
|
+
crack (0.4.1)
|
8
|
+
safe_yaml (~> 0.9.0)
|
9
|
+
debugger (1.6.3)
|
10
|
+
columnize (>= 0.3.1)
|
11
|
+
debugger-linecache (~> 1.2.0)
|
12
|
+
debugger-ruby_core_source (~> 1.2.4)
|
13
|
+
debugger-linecache (1.2.0)
|
14
|
+
debugger-ruby_core_source (1.2.4)
|
15
|
+
metaclass (0.0.1)
|
16
|
+
method_source (0.8.2)
|
17
|
+
minitest (4.7.5)
|
18
|
+
mocha (0.14.0)
|
19
|
+
metaclass (~> 0.0.1)
|
20
|
+
pry (0.9.12.4)
|
21
|
+
coderay (~> 1.0)
|
22
|
+
method_source (~> 0.8)
|
23
|
+
slop (~> 3.4)
|
24
|
+
pry-debugger (0.2.2)
|
25
|
+
debugger (~> 1.3)
|
26
|
+
pry (~> 0.9.10)
|
27
|
+
rake (10.0.2)
|
28
|
+
safe_yaml (0.9.7)
|
29
|
+
slop (3.4.7)
|
30
|
+
thor (0.14.6)
|
31
|
+
webmock (1.16.0)
|
32
|
+
addressable (>= 2.2.7)
|
33
|
+
crack (>= 0.3.2)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
minitest (~> 4.7.0)
|
40
|
+
mocha
|
41
|
+
pry
|
42
|
+
pry-debugger
|
43
|
+
rake
|
44
|
+
thor (~> 0.14.6)
|
45
|
+
webmock (~> 1.16.0)
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 GradesFirst
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/Rakefile
ADDED
data/bin/gf
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
if File.exists?(File.join(File.expand_path('../..', __FILE__), '.git'))
|
4
|
+
lib_path = File.expand_path('../../lib', __FILE__)
|
5
|
+
$LOAD_PATH.unshift(lib_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'gradesfirst'
|
10
|
+
require 'gradesfirst/cli'
|
11
|
+
|
12
|
+
GradesFirst::CLI.start
|
data/gradesfirst.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.platform = Gem::Platform::RUBY
|
3
|
+
s.name = "gradesfirst"
|
4
|
+
s.version = "0.2.0"
|
5
|
+
s.summary = "GradesFirst command line utility for developers."
|
6
|
+
s.description = "This utility will help manage the various tasks developers need to do on their workstation such as database updates."
|
7
|
+
s.license = "MIT"
|
8
|
+
|
9
|
+
s.required_ruby_version = ">= 1.8.7"
|
10
|
+
s.required_rubygems_version = ">= 1.3.6"
|
11
|
+
|
12
|
+
s.author = "GradesFirst"
|
13
|
+
s.email = "tech@gradesfirst.com"
|
14
|
+
s.homepage = "http://www.gradesfirst.com"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
18
|
+
|
19
|
+
s.bindir = "bin"
|
20
|
+
s.executables = ["gf"]
|
21
|
+
s.default_executable = "gf"
|
22
|
+
|
23
|
+
s.require_paths = ['lib']
|
24
|
+
|
25
|
+
s.add_dependency('thor', '~> 0.14.6')
|
26
|
+
s.add_development_dependency('minitest', '~> 4.7.0')
|
27
|
+
end
|
data/lib/gradesfirst.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'gradesfirst/command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implemenation of a Thor command for enhancing git branch to include
|
6
|
+
# PivotalTracker information such as the story state, name and url.
|
7
|
+
class BranchCommand < GradesFirst::Command
|
8
|
+
|
9
|
+
# Description of the gf branch Thor command that will be used in the
|
10
|
+
# commandline help.
|
11
|
+
def self.description
|
12
|
+
'List git branches with related PivotalTracker story information.'
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@enhanced_branches = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Performs the gf branch Thor command.
|
20
|
+
def execute
|
21
|
+
git_response = git_branch
|
22
|
+
git_response.each_line do |branch|
|
23
|
+
@enhanced_branches << enhanced_branch(branch.rstrip)
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Output response of the gf branch Thor command that contains the
|
29
|
+
# PivotalTracker enhanced branch names.
|
30
|
+
def response
|
31
|
+
@enhanced_branches.join("\n") + "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def enhanced_branch(branch)
|
37
|
+
story = find_story_by_branch(branch)
|
38
|
+
if story
|
39
|
+
status = story['current_state'].capitalize
|
40
|
+
"#{branch} [#{status}] #{story['name']} (#{story['url']})"
|
41
|
+
else
|
42
|
+
branch
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_story_by_branch(branch)
|
47
|
+
if story_id(branch)
|
48
|
+
PivotalTracker.stories[story_id(branch)].get
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def git_branch
|
55
|
+
`git branch`
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
require 'gradesfirst/command'
|
4
|
+
require 'gradesfirst/branch_command'
|
5
|
+
require 'gradesfirst/commit_message_command'
|
6
|
+
require 'gradesfirst/task_command'
|
7
|
+
|
8
|
+
require 'pivotal_tracker'
|
9
|
+
|
10
|
+
module GradesFirst
|
11
|
+
class CLI < Thor
|
12
|
+
|
13
|
+
desc 'branch', GradesFirst::BranchCommand.description
|
14
|
+
def branch
|
15
|
+
set_pivotal_tracker_api_token
|
16
|
+
execute(GradesFirst::BranchCommand.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'commit-message', GradesFirst::CommitMessageCommand.description
|
20
|
+
def commit_message
|
21
|
+
set_pivotal_tracker_api_token
|
22
|
+
execute(GradesFirst::CommitMessageCommand.new)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'task', GradesFirst::TaskCommand.description
|
26
|
+
def task
|
27
|
+
set_pivotal_tracker_api_token
|
28
|
+
execute(GradesFirst::TaskCommand.new)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def execute(command)
|
34
|
+
command.execute
|
35
|
+
say command.response
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_pivotal_tracker_api_token
|
39
|
+
file_name = "#{Dir.home}/.pivotal_tracker_api_key"
|
40
|
+
if File.exists?(file_name)
|
41
|
+
api_token = File.read(file_name)
|
42
|
+
else
|
43
|
+
api_token = ask('Enter your PivotalTracker API token:')
|
44
|
+
File.open(file_name, 'w') do |file|
|
45
|
+
file.write(api_token)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
PivotalTracker.api_token = api_token
|
50
|
+
end
|
51
|
+
|
52
|
+
Signal.trap('SIGINT') do
|
53
|
+
print "\n"
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GradesFirst
|
2
|
+
|
3
|
+
# Base class for all command objects.
|
4
|
+
class Command
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# Retrieves the current git branch.
|
9
|
+
def current_branch
|
10
|
+
`git rev-parse --abbrev-ref HEAD`
|
11
|
+
end
|
12
|
+
|
13
|
+
# Extracts a PivotalTracker story id from a branch if one is present.
|
14
|
+
def story_id(branch = current_branch)
|
15
|
+
if branch =~ /[0-9]+$/
|
16
|
+
branch.match(/[0-9]+$/)[0]
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'gradesfirst/command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implementation of a Thor command for creating a git commit message that
|
6
|
+
# already includes the information from the related PivotalTracker story
|
7
|
+
# associated with the current branch.
|
8
|
+
class CommitMessageCommand < GradesFirst::Command
|
9
|
+
|
10
|
+
# Description of the gf commit-message Thor command that well be usewd in
|
11
|
+
# the commandline help.
|
12
|
+
def self.description
|
13
|
+
'Generate a git commit message in the standard format.'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Performs the gf commit-message Thor command.
|
17
|
+
def execute
|
18
|
+
@story = PivotalTracker.stories[story_id].get
|
19
|
+
end
|
20
|
+
|
21
|
+
# Output response of the gf commit-message Thor command in the standard
|
22
|
+
# format.
|
23
|
+
def response
|
24
|
+
if @story.nil?
|
25
|
+
''
|
26
|
+
else
|
27
|
+
message = [
|
28
|
+
"[\##{@story['id']}]",
|
29
|
+
"",
|
30
|
+
@story['name'],
|
31
|
+
@story['url']
|
32
|
+
]
|
33
|
+
message.join("\n") + "\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'gradesfirst/command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implementation of a Thor command for managing tasks related to a story.
|
6
|
+
class TaskCommand < GradesFirst::Command
|
7
|
+
|
8
|
+
# Description of the gf branch Thor command that will be used in the
|
9
|
+
# command line help.
|
10
|
+
def self.description
|
11
|
+
'List the tasks related to a PivotalTracker story.'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Performs the gf tasks Thor command.
|
15
|
+
def execute
|
16
|
+
@story = PivotalTracker.stories[story_id].get
|
17
|
+
if @story
|
18
|
+
project_id = @story['project_id']
|
19
|
+
@tasks = PivotalTracker.projects[project_id].stories[story_id].tasks.get
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Generates the comand line output response. The output of the task command
|
24
|
+
# is a list of the tasks associated with the PivotalTracker story associated
|
25
|
+
# with the current branch.
|
26
|
+
def response
|
27
|
+
if @tasks.nil?
|
28
|
+
error_message
|
29
|
+
else
|
30
|
+
task_list = @tasks.map do |t|
|
31
|
+
" [#{t["complete"] ? 'X' : ' '}] #{t["description"]}"
|
32
|
+
end
|
33
|
+
story_line + task_list.join("\n") + "\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def error_message
|
40
|
+
'Tasks cannot be retrieved for this branch.'
|
41
|
+
end
|
42
|
+
|
43
|
+
def story_line
|
44
|
+
" #{@story['name']} (#{@story['url']})\n\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/http_magic.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# A magical class that interacts with HTTP resources with a minimal amount of
|
5
|
+
# configuration.
|
6
|
+
#
|
7
|
+
# Assuming an api where the url http://www.example.com/foo/99 responds with
|
8
|
+
# the following.
|
9
|
+
#
|
10
|
+
# Header:
|
11
|
+
#
|
12
|
+
# Content-Type: application/json
|
13
|
+
#
|
14
|
+
# Body:
|
15
|
+
#
|
16
|
+
# {
|
17
|
+
# "name": "Foo Bar"
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# == Example
|
21
|
+
#
|
22
|
+
# class ExampleApi < HttpMagic
|
23
|
+
# url 'www.example.com'
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# ExampleApi.foo[99].get['name']
|
27
|
+
# => "Foo Bar"
|
28
|
+
#
|
29
|
+
class HttpMagic < BasicObject
|
30
|
+
# Makes the new method private so that instances of this class and it's
|
31
|
+
# children can only be instantiated through the class method method_missing.
|
32
|
+
# This is needed to enforce the intended usage of HttpMagic where the class
|
33
|
+
# is configured to represent an http location. Once it is configured, the
|
34
|
+
# location is interacted with dynamically with chained methods that correspond
|
35
|
+
# with parts of URNs found at the location.
|
36
|
+
#
|
37
|
+
# == Example
|
38
|
+
#
|
39
|
+
# class ExampleApi < HttpMagic
|
40
|
+
# url 'www.example.com'
|
41
|
+
# namespace 'api/v1'
|
42
|
+
# headers({'X-AuthToken' => 'token'})
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# ExampleApi.new.uri
|
46
|
+
# => "http://www.example.com/api/v1/new"
|
47
|
+
#
|
48
|
+
# ExampleApi.foo[99].uri
|
49
|
+
# => "http://www.example.com/api/v1/foo/99"
|
50
|
+
class << self
|
51
|
+
private :new
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sets or returns the request headers for an HttpMagic based class. The
|
55
|
+
# headers will be passed along to each resource request being made.
|
56
|
+
#
|
57
|
+
# == Example
|
58
|
+
#
|
59
|
+
# class ExampleApi < HttpMagic
|
60
|
+
# url 'www.example.com'
|
61
|
+
# namespace 'api/v1'
|
62
|
+
# headers({'X-AuthToken' => 'token'})
|
63
|
+
# end
|
64
|
+
def self.headers(value = :not_provided)
|
65
|
+
unless value == :not_provided
|
66
|
+
@headers = value
|
67
|
+
end
|
68
|
+
@headers
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets or returns the namespace for an HttpMagic based class. The namespace
|
72
|
+
# will be prefixed to the urn for each request made to the url.
|
73
|
+
#
|
74
|
+
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
75
|
+
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
76
|
+
#
|
77
|
+
# Header:
|
78
|
+
#
|
79
|
+
# Content-Type: application/json
|
80
|
+
#
|
81
|
+
# Body:
|
82
|
+
#
|
83
|
+
# {
|
84
|
+
# "name": "Foo Bar"
|
85
|
+
# }
|
86
|
+
#
|
87
|
+
# == Example
|
88
|
+
#
|
89
|
+
# class ExampleApi < HttpMagic
|
90
|
+
# url 'www.example.com'
|
91
|
+
# namespace 'api/v1'
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# ExampleApi.foo[99].get['name']
|
95
|
+
# => "Foo Bar"
|
96
|
+
def self.namespace(value = :not_provided)
|
97
|
+
unless value == :not_provided
|
98
|
+
@namespace = value
|
99
|
+
end
|
100
|
+
@namespace
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets or returns the uniform resource locator for the HTTP resource.
|
104
|
+
#
|
105
|
+
# == Example
|
106
|
+
#
|
107
|
+
# class ExampleApi < HttpMagic
|
108
|
+
# url 'www.example.com'
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# ExampleApi.url
|
112
|
+
# => 'www.example.com'
|
113
|
+
def self.url(value = :not_provided)
|
114
|
+
unless value == :not_provided
|
115
|
+
@url = value
|
116
|
+
end
|
117
|
+
@url
|
118
|
+
end
|
119
|
+
|
120
|
+
# Class scoped method_missing that starts the magic of creating urns
|
121
|
+
# through meta programming by instantiating an instance of the class
|
122
|
+
# and delegating the first part of the urn to that instance. This method
|
123
|
+
# is an implementation of the Factory Method design pattern.
|
124
|
+
def self.method_missing(name, *args, &block)
|
125
|
+
new(@url, @namespace, @headers).__send__(name, *args)
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize(url, namespace, headers)
|
129
|
+
@url = url
|
130
|
+
@namespace = namespace
|
131
|
+
@headers = headers
|
132
|
+
|
133
|
+
@urn_parts = []
|
134
|
+
end
|
135
|
+
|
136
|
+
# Resource index reference intended to allow for the use of numbers in a urn
|
137
|
+
# such as the following 'foo/99' being referenced by ExampleApi.foo[99]. It
|
138
|
+
# can also be used to allow for HttpMagic methods to be specified for a urn
|
139
|
+
# such as 'foo/get' being referenced by ExampleApi.foo[:get]. Finally, it can
|
140
|
+
# be used for urn parts that are not valid Ruby methods such as 'foo/%5B%5D'
|
141
|
+
# being referenced by ExampleApi.foo["%5B%5D"].
|
142
|
+
#
|
143
|
+
# * part - a part of a urn such that 'foo' and 'bar' would be parts of the urn
|
144
|
+
# 'foo/bar'.
|
145
|
+
#
|
146
|
+
# Returns a reference to its instance so that urn parts can be chained
|
147
|
+
# together.
|
148
|
+
def [](part)
|
149
|
+
@urn_parts << part.to_s
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# Gets a resource from the URI and returns it based on its content type. JSON
|
154
|
+
# content will be parsed and returned as equivalent Ruby objects. All other
|
155
|
+
# content types will be returned as text.
|
156
|
+
#
|
157
|
+
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
158
|
+
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
159
|
+
#
|
160
|
+
# Header:
|
161
|
+
#
|
162
|
+
# Content-Type: application/json
|
163
|
+
#
|
164
|
+
# Body:
|
165
|
+
#
|
166
|
+
# {
|
167
|
+
# "name": "Foo Bar"
|
168
|
+
# }
|
169
|
+
#
|
170
|
+
# == Example
|
171
|
+
#
|
172
|
+
# class ExampleApi < HttpMagic
|
173
|
+
# url 'www.example.com'
|
174
|
+
# namespace 'api/v1'
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# ExampleApi.foo[99].get
|
178
|
+
# => { "name" => "Foo Bar" }
|
179
|
+
def get
|
180
|
+
http = ::Net::HTTP.new(url, 443)
|
181
|
+
http.use_ssl = true
|
182
|
+
|
183
|
+
response = http.request_get(urn, @headers || {})
|
184
|
+
if response && response.is_a?(::Net::HTTPSuccess)
|
185
|
+
if response.content_type == 'application/json'
|
186
|
+
::JSON.parse(response.body)
|
187
|
+
else
|
188
|
+
response.body
|
189
|
+
end
|
190
|
+
else
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Uniform resource identifier for a specified request.
|
196
|
+
#
|
197
|
+
# == Example
|
198
|
+
#
|
199
|
+
# class ExampleApi < HttpMagic
|
200
|
+
# url 'www.example.com'
|
201
|
+
# namespace 'api/v1'
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# ExampleApi.foo[99].uri
|
205
|
+
# => "www.example.com/api/v1/foo/99"
|
206
|
+
def uri
|
207
|
+
"#{url}#{urn}"
|
208
|
+
end
|
209
|
+
|
210
|
+
# Uniform resource locator for all the resources.
|
211
|
+
#
|
212
|
+
# == Example
|
213
|
+
#
|
214
|
+
# class ExampleApi < HttpMagic
|
215
|
+
# url 'www.example.com'
|
216
|
+
# namespace 'api/v1'
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# ExampleApi.foo[99].url
|
220
|
+
# => "www.example.com"
|
221
|
+
def url
|
222
|
+
@url
|
223
|
+
end
|
224
|
+
|
225
|
+
# Uniform resource name for a resource.
|
226
|
+
#
|
227
|
+
# == Example
|
228
|
+
#
|
229
|
+
# class ExampleApi < HttpMagic
|
230
|
+
# url 'www.example.com'
|
231
|
+
# namespace 'api/v1'
|
232
|
+
# end
|
233
|
+
#
|
234
|
+
# ExampleApi.foo[99].urn
|
235
|
+
# => "api/v1/foo/99"
|
236
|
+
def urn
|
237
|
+
resource_name = [@namespace, @urn_parts].flatten.compact.join('/')
|
238
|
+
"/#{resource_name}"
|
239
|
+
end
|
240
|
+
|
241
|
+
# Instance scoped method_missing that accumulates the URN parts used in
|
242
|
+
# forming the URI. It returns a reference to the current instance so that
|
243
|
+
# method and [] based parts can be chained together to from the URN. In the
|
244
|
+
# case of ExampleApi.foo[99] the 'foo' method is accumlated as a URN part
|
245
|
+
# that will form the resulting URI.
|
246
|
+
#
|
247
|
+
# == Example
|
248
|
+
#
|
249
|
+
# class ExampleApi < HttpMagic
|
250
|
+
# url 'www.example.com'
|
251
|
+
# namespace 'api/v1'
|
252
|
+
# end
|
253
|
+
#
|
254
|
+
# ExampleApi.foo[99].uri
|
255
|
+
# => "www.example.com/api/v1/foo/99"
|
256
|
+
def method_missing(part, *args, &block)
|
257
|
+
@urn_parts << part.to_s
|
258
|
+
self
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'http_magic'
|
2
|
+
|
3
|
+
class PivotalTracker < HttpMagic
|
4
|
+
url 'www.pivotaltracker.com'
|
5
|
+
namespace 'services/v5'
|
6
|
+
|
7
|
+
# Specifies the PivotalTracker API token that is needed to authenticate a user
|
8
|
+
# to access the api.
|
9
|
+
#
|
10
|
+
# == Example
|
11
|
+
#
|
12
|
+
# ExampleApi.api_token = 'token'
|
13
|
+
def self.api_token=(value)
|
14
|
+
headers({ 'X-TrackerToken' => value })
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'gradesfirst/branch_command'
|
3
|
+
|
4
|
+
describe GradesFirst::BranchCommand do
|
5
|
+
before do
|
6
|
+
stub_pivotal_request(
|
7
|
+
:get,
|
8
|
+
'stories/29384793',
|
9
|
+
body: fixture_file('story_bar.json')
|
10
|
+
)
|
11
|
+
|
12
|
+
stub_pivotal_request(
|
13
|
+
:get,
|
14
|
+
'stories/57348714',
|
15
|
+
body: fixture_file('story_foo.json')
|
16
|
+
)
|
17
|
+
|
18
|
+
PivotalTracker.api_token = 'test_token'
|
19
|
+
git_branch_output = fixture_file('git_branch.txt')
|
20
|
+
@command = GradesFirst::BranchCommand.new
|
21
|
+
@command.stub :git_branch, git_branch_output do
|
22
|
+
@command.execute
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#response' do
|
27
|
+
it 'should respond with Pivotal enhancements' do
|
28
|
+
assert_equal fixture_file('gf_branch.txt'), @command.response
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/test/cli_test.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'gradesfirst/cli'
|
3
|
+
|
4
|
+
describe GradesFirst::CLI do
|
5
|
+
def command_not_found(command)
|
6
|
+
"Could not find task \"#{command}\".\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
def executed_command_expectations(command)
|
10
|
+
command.any_instance.expects(:execute)
|
11
|
+
command.any_instance.expects(:response).returns('')
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'branch command' do
|
15
|
+
before do
|
16
|
+
executed_command_expectations(GradesFirst::BranchCommand)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should exist' do
|
20
|
+
output = capture_io do
|
21
|
+
GradesFirst::CLI.start %w{ branch }
|
22
|
+
end
|
23
|
+
refute_includes output, command_not_found('branch')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'commit-message command' do
|
28
|
+
before do
|
29
|
+
executed_command_expectations(GradesFirst::CommitMessageCommand)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should exist' do
|
33
|
+
output = capture_io do
|
34
|
+
GradesFirst::CLI.start %w{ commit-message }
|
35
|
+
end
|
36
|
+
refute_includes output, command_not_found('commit-message')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'task command' do
|
41
|
+
before do
|
42
|
+
executed_command_expectations(GradesFirst::TaskCommand)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should exist' do
|
46
|
+
output = capture_io do
|
47
|
+
GradesFirst::CLI.start %w{ task }
|
48
|
+
end
|
49
|
+
refute_includes output, command_not_found('task')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'gradesfirst/command'
|
3
|
+
|
4
|
+
describe GradesFirst::Command do
|
5
|
+
describe '#story_id' do
|
6
|
+
it 'should pull the story id from the branch' do
|
7
|
+
assert '123', GradesFirst::Command.new.send(:story_id, 'abc/123')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return nil if not story id' do
|
11
|
+
assert_nil GradesFirst::Command.new.send(:story_id, 'abc')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'gradesfirst/commit_message_command'
|
3
|
+
|
4
|
+
describe GradesFirst::CommitMessageCommand do
|
5
|
+
before do
|
6
|
+
stub_pivotal_request(
|
7
|
+
:get,
|
8
|
+
'stories/29384793',
|
9
|
+
body: fixture_file('story_bar.json')
|
10
|
+
)
|
11
|
+
|
12
|
+
PivotalTracker.api_token = 'test_token'
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#response' do
|
16
|
+
it 'should repond with formatted commit message' do
|
17
|
+
command = GradesFirst::CommitMessageCommand.new
|
18
|
+
command.stub :current_branch, 'test/29384793' do
|
19
|
+
command.execute
|
20
|
+
assert_equal fixture_file('commit_message.txt'), command.response
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"atom_enabled": false,
|
4
|
+
"version": 91975,
|
5
|
+
"name": "GradesFirst",
|
6
|
+
"velocity_averaged_over": 3,
|
7
|
+
"kind": "project",
|
8
|
+
"created_at": "2009-03-13T21:43:45Z",
|
9
|
+
"has_google_domain": false,
|
10
|
+
"point_scale_is_custom": false,
|
11
|
+
"current_iteration_number": 123,
|
12
|
+
"start_date": "2012-11-12",
|
13
|
+
"number_of_done_iterations_to_show": 4,
|
14
|
+
"updated_at": "2013-11-21T05:39:11Z",
|
15
|
+
"enable_incoming_emails": true,
|
16
|
+
"initial_velocity": 10,
|
17
|
+
"enable_following": true,
|
18
|
+
"account_id": 277647,
|
19
|
+
"start_time": "2009-03-16T05:00:00Z",
|
20
|
+
"enable_tasks": true,
|
21
|
+
"enable_planned_mode": true,
|
22
|
+
"week_start_day": "Monday",
|
23
|
+
"iteration_length": 2,
|
24
|
+
"time_zone": {
|
25
|
+
"kind": "time_zone",
|
26
|
+
"olson_name": "America/Chicago",
|
27
|
+
"offset": "-06:00"
|
28
|
+
},
|
29
|
+
"id": 10688,
|
30
|
+
"bugs_and_chores_are_estimatable": true,
|
31
|
+
"public": false,
|
32
|
+
"point_scale": "0,1,2,3,5,8"
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"atom_enabled": false,
|
36
|
+
"version": 91975,
|
37
|
+
"name": "Technical",
|
38
|
+
"velocity_averaged_over": 3,
|
39
|
+
"kind": "project",
|
40
|
+
"created_at": "2009-03-13T21:43:45Z",
|
41
|
+
"has_google_domain": false,
|
42
|
+
"point_scale_is_custom": false,
|
43
|
+
"current_iteration_number": 123,
|
44
|
+
"start_date": "2012-11-12",
|
45
|
+
"number_of_done_iterations_to_show": 4,
|
46
|
+
"updated_at": "2013-11-21T05:39:11Z",
|
47
|
+
"enable_incoming_emails": true,
|
48
|
+
"initial_velocity": 10,
|
49
|
+
"enable_following": true,
|
50
|
+
"account_id": 277647,
|
51
|
+
"start_time": "2009-03-16T05:00:00Z",
|
52
|
+
"enable_tasks": true,
|
53
|
+
"enable_planned_mode": true,
|
54
|
+
"week_start_day": "Monday",
|
55
|
+
"iteration_length": 2,
|
56
|
+
"time_zone": {
|
57
|
+
"kind": "time_zone",
|
58
|
+
"olson_name": "America/Chicago",
|
59
|
+
"offset": "-06:00"
|
60
|
+
},
|
61
|
+
"id": 10687,
|
62
|
+
"bugs_and_chores_are_estimatable": true,
|
63
|
+
"public": false,
|
64
|
+
"point_scale": "0,1,2,3,5,8"
|
65
|
+
}
|
66
|
+
]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"name": "Bring me the passengers",
|
3
|
+
"kind": "story",
|
4
|
+
"description": "ignore the droids",
|
5
|
+
"created_at": "2013-11-12T12:00:00Z",
|
6
|
+
"id": 29384793,
|
7
|
+
"labels":
|
8
|
+
[
|
9
|
+
],
|
10
|
+
"estimate": 2,
|
11
|
+
"project_id": 10687,
|
12
|
+
"current_state": "started",
|
13
|
+
"url": "http://www.pivotaltracker.com/story/show/29384793",
|
14
|
+
"story_type": "feature",
|
15
|
+
"requested_by_id": 101,
|
16
|
+
"updated_at": "2013-11-12T12:00:00Z"
|
17
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"name": "This is not the droid",
|
3
|
+
"kind": "story",
|
4
|
+
"description": "you are looking for.",
|
5
|
+
"created_at": "2013-11-12T12:00:00Z",
|
6
|
+
"id": 57348714,
|
7
|
+
"labels":
|
8
|
+
[
|
9
|
+
],
|
10
|
+
"estimate": 2,
|
11
|
+
"project_id": 99,
|
12
|
+
"current_state": "started",
|
13
|
+
"url": "http://www.pivotaltracker.com/story/show/57348714",
|
14
|
+
"story_type": "feature",
|
15
|
+
"requested_by_id": 101,
|
16
|
+
"updated_at": "2013-11-12T12:00:00Z"
|
17
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"kind": "task",
|
4
|
+
"description": "Port 0",
|
5
|
+
"created_at": "2013-12-03T12:00:00Z",
|
6
|
+
"position": 1,
|
7
|
+
"id": 5,
|
8
|
+
"complete": true,
|
9
|
+
"story_id": 558,
|
10
|
+
"updated_at": "2013-12-03T12:00:00Z"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"kind": "task",
|
14
|
+
"description": "Port 90",
|
15
|
+
"created_at": "2013-12-03T12:00:00Z",
|
16
|
+
"position": 2,
|
17
|
+
"id": 6,
|
18
|
+
"complete": false,
|
19
|
+
"story_id": 558,
|
20
|
+
"updated_at": "2013-12-03T12:00:00Z"
|
21
|
+
}
|
22
|
+
]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'http_magic'
|
3
|
+
|
4
|
+
class HttpMagicTest < HttpMagic
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'HttpMagic#get' do
|
8
|
+
before do
|
9
|
+
@url = HttpMagicTest.url 'www.example.com'
|
10
|
+
HttpMagicTest.namespace nil
|
11
|
+
HttpMagicTest.headers nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return the root response' do
|
15
|
+
stub_request(:get, "https://#{@url}:443").
|
16
|
+
to_return(body: 'I am root!!')
|
17
|
+
|
18
|
+
assert_equal(
|
19
|
+
'I am root!!',
|
20
|
+
HttpMagicTest.get
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return the named resource' do
|
25
|
+
stub_request(:get, "https://#{@url}:443/bar").
|
26
|
+
to_return(body: 'A bear walked into a bar...')
|
27
|
+
|
28
|
+
assert_equal(
|
29
|
+
'A bear walked into a bar...',
|
30
|
+
HttpMagicTest.bar.get
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return the named resource' do
|
35
|
+
stub_request(:get, "https://#{@url}:443/bar/84").
|
36
|
+
to_return(body: 'Where Everybody Knows Your Name')
|
37
|
+
|
38
|
+
assert_equal(
|
39
|
+
'Where Everybody Knows Your Name',
|
40
|
+
HttpMagicTest.bar[84].get
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should serialize JSON objects' do
|
45
|
+
stub_request(:get, "https://#{@url}:443/bar/99").
|
46
|
+
to_return(
|
47
|
+
headers: { 'Content-Type' => 'application/json' },
|
48
|
+
body: fixture_file('projects.json')
|
49
|
+
)
|
50
|
+
|
51
|
+
assert_equal(
|
52
|
+
'GradesFirst',
|
53
|
+
HttpMagicTest.bar[99].get.first['name']
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should provide specified headers' do
|
58
|
+
stub_request(:get, "https://#{@url}:443/bar/84").
|
59
|
+
with(headers: { 'X-AuthToken' => 'test_token' }).
|
60
|
+
to_return(body: 'Where Everybody Knows Your Name')
|
61
|
+
|
62
|
+
HttpMagicTest.headers({ 'X-AuthToken' => 'test_token' })
|
63
|
+
assert_equal(
|
64
|
+
'Where Everybody Knows Your Name',
|
65
|
+
HttpMagicTest.bar[84].get
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'http_magic'
|
3
|
+
|
4
|
+
class HttpMagicTest < HttpMagic
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'HttpMagic#uri' do
|
8
|
+
before do
|
9
|
+
@url = HttpMagicTest.url 'www.example.com'
|
10
|
+
HttpMagicTest.namespace nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should make a good root uri' do
|
14
|
+
assert_equal(
|
15
|
+
"#{@url}/",
|
16
|
+
HttpMagicTest.uri
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should make a uri that begins with new' do
|
21
|
+
assert_equal(
|
22
|
+
"#{@url}/new",
|
23
|
+
HttpMagicTest.new.uri
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should make a good single part uri' do
|
28
|
+
assert_equal(
|
29
|
+
"#{@url}/foos",
|
30
|
+
HttpMagicTest.foos.uri
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should make a good uri with an index' do
|
35
|
+
assert_equal(
|
36
|
+
"#{@url}/foos/99",
|
37
|
+
HttpMagicTest.foos[99].uri
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should make a good nested uri' do
|
42
|
+
assert_equal(
|
43
|
+
"#{@url}/foos/99/bars",
|
44
|
+
HttpMagicTest.foos[99].bars.uri
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should make a uri including a keyword at the beginning' do
|
49
|
+
assert_equal(
|
50
|
+
"#{@url}/get",
|
51
|
+
HttpMagicTest[:get].uri
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should make a uri including a keyword in the middle' do
|
56
|
+
assert_equal(
|
57
|
+
"#{@url}/foo/get/bar",
|
58
|
+
HttpMagicTest.foo[:get].bar.uri
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should make a uri including a keyword at the end' do
|
63
|
+
assert_equal(
|
64
|
+
"#{@url}/foo/bar/get",
|
65
|
+
HttpMagicTest.foo.bar[:get].uri
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should make a namespaced uri' do
|
70
|
+
HttpMagicTest.namespace 'api/v5'
|
71
|
+
assert_equal(
|
72
|
+
"#{@url}/api/v5/foo/bar/get",
|
73
|
+
HttpMagicTest.foo.bar[:get].uri
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'pivotal_tracker'
|
3
|
+
|
4
|
+
describe 'PivotalTracker' do
|
5
|
+
it 'should make a good root uri' do
|
6
|
+
assert_equal(
|
7
|
+
'www.pivotaltracker.com/services/v5',
|
8
|
+
PivotalTracker.uri
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should make a good project list uri' do
|
13
|
+
assert_equal(
|
14
|
+
'www.pivotaltracker.com/services/v5/projects',
|
15
|
+
PivotalTracker.projects.uri
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should make a good project uri' do
|
20
|
+
assert_equal(
|
21
|
+
'www.pivotaltracker.com/services/v5/projects/99',
|
22
|
+
PivotalTracker.projects[99].uri
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should make a good iterations uri' do
|
27
|
+
assert_equal(
|
28
|
+
'www.pivotaltracker.com/services/v5/projects/99/iterations',
|
29
|
+
PivotalTracker.projects[99].iterations.uri
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return a pivotal project' do
|
34
|
+
stub_pivotal_request(
|
35
|
+
:get,
|
36
|
+
'projects/99',
|
37
|
+
body: fixture_file('projects.json')
|
38
|
+
)
|
39
|
+
|
40
|
+
PivotalTracker.api_token = 'test_token'
|
41
|
+
assert_equal(
|
42
|
+
'GradesFirst',
|
43
|
+
PivotalTracker.projects[99].get.first['name']
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'gradesfirst/task_command'
|
3
|
+
|
4
|
+
describe GradesFirst::TaskCommand do
|
5
|
+
before do
|
6
|
+
stub_pivotal_request(
|
7
|
+
:get,
|
8
|
+
'stories/29384799',
|
9
|
+
status: 404
|
10
|
+
)
|
11
|
+
|
12
|
+
stub_pivotal_request(
|
13
|
+
:get,
|
14
|
+
'stories/29384793',
|
15
|
+
body: fixture_file('story_bar.json')
|
16
|
+
)
|
17
|
+
|
18
|
+
stub_pivotal_request(
|
19
|
+
:get,
|
20
|
+
'projects/10687/stories/29384793/tasks',
|
21
|
+
body: fixture_file('tasks.json')
|
22
|
+
)
|
23
|
+
|
24
|
+
PivotalTracker.api_token = 'test_token'
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#response with a valid story' do
|
28
|
+
before do
|
29
|
+
@command = GradesFirst::TaskCommand.new
|
30
|
+
@command.stub :current_branch, '29384793' do
|
31
|
+
@command.execute
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should respond with a task list' do
|
36
|
+
assert_equal fixture_file('tasks.txt'), @command.response
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#response with an invalid story' do
|
41
|
+
before do
|
42
|
+
@command = GradesFirst::TaskCommand.new
|
43
|
+
@command.stub :current_branch, '29384799' do
|
44
|
+
@command.execute
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should respond with error message' do
|
49
|
+
assert_equal @command.send(:error_message), @command.response
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'minitest' if RUBY_VERSION > '1.9'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'webmock/minitest'
|
7
|
+
require 'mocha/setup'
|
8
|
+
require 'pry'
|
9
|
+
|
10
|
+
# Retreive the contents of fixture files to be used in tests. It is easier
|
11
|
+
# to view, modify and visualize text used in tests when it is stored in files.
|
12
|
+
# Also, it is much easier to reuse that text.
|
13
|
+
def fixture_file(file_name)
|
14
|
+
File.open(File.dirname(__FILE__) + '/fixtures/' + file_name, 'rb').read
|
15
|
+
end
|
16
|
+
|
17
|
+
# Simplify and DRY up stubbing out of PivotalTracker requests.
|
18
|
+
def stub_pivotal_request(method, urn, options)
|
19
|
+
default_options = {
|
20
|
+
headers: { 'Content-Type' => 'application/json' },
|
21
|
+
body: '',
|
22
|
+
status: 200
|
23
|
+
}
|
24
|
+
|
25
|
+
uri = "https://www.pivotaltracker.com:443/services/v5/#{urn}"
|
26
|
+
stub_request(method, uri).
|
27
|
+
with(headers: { 'X-TrackerToken' => 'test_token' }).
|
28
|
+
to_return(default_options.merge(options))
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gradesfirst
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- GradesFirst
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.14.6
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.14.6
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 4.7.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 4.7.0
|
46
|
+
description: This utility will help manage the various tasks developers need to do
|
47
|
+
on their workstation such as database updates.
|
48
|
+
email: tech@gradesfirst.com
|
49
|
+
executables:
|
50
|
+
- gf
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- Gemfile.lock
|
57
|
+
- MIT-LICENSE
|
58
|
+
- README.rdoc
|
59
|
+
- Rakefile
|
60
|
+
- bin/gf
|
61
|
+
- gradesfirst.gemspec
|
62
|
+
- lib/gradesfirst.rb
|
63
|
+
- lib/gradesfirst/branch_command.rb
|
64
|
+
- lib/gradesfirst/cli.rb
|
65
|
+
- lib/gradesfirst/command.rb
|
66
|
+
- lib/gradesfirst/commit_message_command.rb
|
67
|
+
- lib/gradesfirst/task_command.rb
|
68
|
+
- lib/http_magic.rb
|
69
|
+
- lib/pivotal_tracker.rb
|
70
|
+
- test/branch_command_test.rb
|
71
|
+
- test/cli_test.rb
|
72
|
+
- test/command_test.rb
|
73
|
+
- test/commit_message_command_test.rb
|
74
|
+
- test/fixtures/commit_message.txt
|
75
|
+
- test/fixtures/gf_branch.txt
|
76
|
+
- test/fixtures/git_branch.txt
|
77
|
+
- test/fixtures/projects.json
|
78
|
+
- test/fixtures/story_bar.json
|
79
|
+
- test/fixtures/story_foo.json
|
80
|
+
- test/fixtures/tasks.json
|
81
|
+
- test/fixtures/tasks.txt
|
82
|
+
- test/http_magic/get_test.rb
|
83
|
+
- test/http_magic/uri_test.rb
|
84
|
+
- test/pivotal_tracker_test.rb
|
85
|
+
- test/task_command_test.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
homepage: http://www.gradesfirst.com
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.8.7
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 1.3.6
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.24
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: GradesFirst command line utility for developers.
|
112
|
+
test_files: []
|