gradesfirst 0.2.0
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.
- 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: []
|