gradesfirst 0.2.1 → 0.3.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.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +1 -2
  3. data/Gemfile.lock +2 -6
  4. data/gradesfirst.gemspec +2 -2
  5. data/lib/gradesfirst.rb +1 -0
  6. data/lib/gradesfirst/cli.rb +7 -29
  7. data/lib/gradesfirst/cli_helper.rb +33 -0
  8. data/lib/gradesfirst/command.rb +7 -0
  9. data/lib/gradesfirst/commit_message_command.rb +1 -1
  10. data/lib/gradesfirst/task_add_command.rb +51 -0
  11. data/lib/gradesfirst/task_cli.rb +48 -0
  12. data/lib/gradesfirst/task_command.rb +38 -30
  13. data/lib/gradesfirst/task_delete_command.rb +64 -0
  14. data/lib/gradesfirst/task_list_command.rb +40 -0
  15. data/lib/gradesfirst/task_move_command.rb +64 -0
  16. data/lib/gradesfirst/task_toggle_command.rb +65 -0
  17. data/lib/http_magic.rb +2 -258
  18. data/lib/http_magic/api.rb +233 -0
  19. data/lib/http_magic/request.rb +93 -0
  20. data/lib/http_magic/uri.rb +74 -0
  21. data/lib/pivotal_tracker.rb +1 -1
  22. data/test/branch_command_test.rb +8 -21
  23. data/test/cli_test.rb +51 -34
  24. data/test/command_test.rb +5 -7
  25. data/test/commit_message_command_test.rb +37 -45
  26. data/test/fixtures/task.json +10 -0
  27. data/test/fixtures/task_added.txt +6 -0
  28. data/test/fixtures/task_deleted.txt +6 -0
  29. data/test/fixtures/task_moved.txt +6 -0
  30. data/test/fixtures/task_toggled.txt +6 -0
  31. data/test/fixtures/tasks.txt +2 -2
  32. data/test/http_magic/{get_test.rb → api/get_test.rb} +1 -1
  33. data/test/http_magic/api/post_test.rb +34 -0
  34. data/test/http_magic/request_test.rb +63 -0
  35. data/test/http_magic/uri_test.rb +28 -55
  36. data/test/support/pivotal_test_helper.rb +110 -0
  37. data/test/support/request_expectation.rb +33 -0
  38. data/test/task_add_command_test.rb +54 -0
  39. data/test/task_delete_command_test.rb +57 -0
  40. data/test/task_list_command_test.rb +18 -0
  41. data/test/task_move_command_test.rb +66 -0
  42. data/test/task_toggle_command_test.rb +58 -0
  43. data/test/test_helper.rb +35 -19
  44. metadata +29 -7
  45. data/test/pivotal_tracker_test.rb +0 -46
  46. data/test/task_command_test.rb +0 -52
@@ -0,0 +1,93 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ module HttpMagic
5
+ # Encapsulating class to hold all of the infrastructure to make the actual
6
+ # requests to the api. It receives at a minimum a HttpMagic::Uri object which
7
+ # manages the url building.
8
+ #
9
+ # == Example
10
+ #
11
+ # uri_object = HttpMagic::Uri.new('http://example.com')
12
+ #
13
+ # request = Request.new(uri_object)
14
+ #
15
+ # request.get
16
+ # => { 'name' => 'Foo Bar' }
17
+ class Request
18
+ def initialize(uri, options = {})
19
+ @uri = uri
20
+ @data = options[:data]
21
+ @options = {
22
+ headers: options[:headers] || {}
23
+ }
24
+ end
25
+
26
+ def delete
27
+ parse_response http.delete(@uri.urn, @options[:headers])
28
+ end
29
+
30
+ # Makes a GET request to the url provided by the Uri object and returns the
31
+ # resulting JSON data as a Ruby hash.
32
+ #
33
+ # == Example
34
+ #
35
+ # uri_object = HttpMagic::Uri.new('http://example.com')
36
+ #
37
+ # request = Request.new(uri_object)
38
+ #
39
+ # request.get
40
+ # => { 'name' => 'Foo Bar' }
41
+ def get
42
+ parse_response http.request_get(@uri.urn, @options[:headers])
43
+ end
44
+
45
+ # Makes a POST request to the url provided by the Uri object and returns the
46
+ # resulting JSON data as a Ruby hash. If data was provided as an optional
47
+ # initialization parameter, then that is also POSTed.
48
+ #
49
+ # == Example
50
+ #
51
+ # uri_object = HttpMagic::Uri.new('http://example.com')
52
+ #
53
+ # request = Request.new(uri_object, data: { name: 'New Foo' })
54
+ #
55
+ # request.post
56
+ def post
57
+ if !@data.empty?
58
+ @options[:headers].merge!( 'Content-Type' => 'application/json' )
59
+ end
60
+
61
+ parse_response http.request_post(@uri.urn, @data.to_json, @options[:headers])
62
+ end
63
+
64
+ def put
65
+ if !@data.empty?
66
+ @options[:headers].merge!( 'Content-Type' => 'application/json' )
67
+ end
68
+
69
+ parse_response http.request_put(@uri.urn, @data.to_json, @options[:headers])
70
+ end
71
+
72
+ private
73
+
74
+ def http
75
+ return @http unless @http.nil?
76
+ @http = Net::HTTP.new(@uri.domain, 443)
77
+ @http.use_ssl = true
78
+ @http
79
+ end
80
+
81
+ def parse_response(response)
82
+ if response && response.is_a?(Net::HTTPSuccess)
83
+ if response.body && response.content_type == 'application/json'
84
+ JSON.parse(response.body)
85
+ else
86
+ response.body.to_s
87
+ end
88
+ else
89
+ nil
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,74 @@
1
+
2
+ module HttpMagic
3
+ # Helper class that holds the parts of the URI and provides methods to put
4
+ # them together.
5
+ #
6
+ # == Example
7
+ #
8
+ # uri = HttpMagic::Uri.new('example.com')
9
+ # uri.build
10
+ # => "https://example.com/"
11
+ #
12
+ # uri.namespace = 'api/v2'
13
+ # uri.build
14
+ # => "https://example.com/api/v2"
15
+ #
16
+ # uri.parts = ['path']
17
+ # uri.build
18
+ # => "https://example.com/api/v2/path"
19
+ #
20
+ class Uri
21
+ attr_accessor :namespace, :parts
22
+
23
+ attr_reader :domain
24
+
25
+ def initialize(domain)
26
+ @domain = domain
27
+ @parts = []
28
+ end
29
+
30
+ # Builds a full uniform resource identifier.
31
+ #
32
+ # == Example
33
+ #
34
+ # uri = HttpMagic::Uri.new('example.com')
35
+ # uri.namespace = 'api/v1'
36
+ # uri.parts = %w(foo bar)
37
+ #
38
+ # uri.urn
39
+ # => "https://example.com/api/v1/foo/bar"
40
+ def build
41
+ "#{url}#{urn}"
42
+ end
43
+
44
+ # Uniform resource locator based on @domain value.
45
+ #
46
+ # == Example
47
+ #
48
+ # uri = HttpMagic::Uri.new('example.com')
49
+ #
50
+ # uri.url
51
+ # => "https://example.com/"
52
+ #
53
+ # uri.url(false)
54
+ # => "https://example.com"
55
+ def url
56
+ "https://#{@domain}"
57
+ end
58
+
59
+ # Uniform resource name for a resource.
60
+ #
61
+ # == Example
62
+ #
63
+ # uri = HttpMagic::Uri.new('example.com')
64
+ # uri.namespace = 'api/v1'
65
+ # uri.parts = %w(foo bar)
66
+ #
67
+ # uri.urn
68
+ # => "/api/v1/foo/bar"
69
+ def urn
70
+ resource_name = [@namespace, @parts].flatten.compact.join('/')
71
+ "/#{resource_name}"
72
+ end
73
+ end
74
+ end
@@ -1,6 +1,6 @@
1
1
  require 'http_magic'
2
2
 
3
- class PivotalTracker < HttpMagic
3
+ class PivotalTracker < HttpMagic::Api
4
4
  url 'www.pivotaltracker.com'
5
5
  namespace 'services/v5'
6
6
 
@@ -1,32 +1,19 @@
1
1
  require 'test_helper'
2
+ require 'pivotal_tracker'
2
3
  require 'gradesfirst/branch_command'
4
+ require 'pivotal_tracker'
3
5
 
4
6
  describe GradesFirst::BranchCommand do
5
7
  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
8
+ expect_requests stub_bar_story_request, stub_foo_story_request do
9
+ git_branch_output = fixture_file('git_branch.txt')
10
+ @command = GradesFirst::BranchCommand.new
11
+ @command.stub :git_branch, git_branch_output, &call_execute
23
12
  end
24
13
  end
25
14
 
26
- describe '#response' do
27
- it 'should respond with Pivotal enhancements' do
28
- assert_equal fixture_file('gf_branch.txt'), @command.response
29
- end
15
+ specify '#response responds with Pivotal enhancements' do
16
+ assert_equal fixture_file('gf_branch.txt'), @command.response
30
17
  end
31
18
 
32
19
  end
data/test/cli_test.rb CHANGED
@@ -2,52 +2,69 @@ require 'test_helper'
2
2
  require 'gradesfirst/cli'
3
3
 
4
4
  describe GradesFirst::CLI do
5
- def command_not_found(command)
6
- "Could not find task \"#{command}\".\n"
7
- end
8
5
 
9
- def executed_command_expectations(command)
10
- command.any_instance.expects(:execute)
11
- command.any_instance.expects(:response).returns('')
12
- end
6
+ def assert_command_hooked_up(shell_command, klass)
7
+ assert_includes klass.instance_methods, :execute
8
+ assert_includes klass.instance_methods, :response
13
9
 
14
- describe 'branch command' do
15
- before do
16
- executed_command_expectations(GradesFirst::BranchCommand)
10
+ execute_arity = klass.instance_method(:execute).arity
11
+ if execute_arity < 0
12
+ execute_arity += 1
13
+ execute_arity = execute_arity.abs
17
14
  end
15
+ expected_execute_parameters = Array.new(execute_arity, Object)
16
+
17
+ expected_output = 'Expected Response'
18
+ command_instance = Minitest::Mock.new.
19
+ expect(:execute, nil, expected_execute_parameters).
20
+ expect(:response, expected_output)
18
21
 
19
- it 'should exist' do
20
- output = capture_io do
21
- GradesFirst::CLI.start %w{ branch }
22
+ stdout, stderr = capture_io do
23
+ klass.stub :new, command_instance do
24
+ GradesFirst::CLI.start Shellwords.split(shell_command)
22
25
  end
23
- refute_includes output, command_not_found('branch')
24
26
  end
27
+
28
+ assert_equal '', stderr, "Error reported to standard error by CLI command \"#{shell_command}\""
29
+ assert_match expected_output, stdout, "Incorrect output from CLI command \"#{shell_command}\""
30
+ command_instance.verify
25
31
  end
26
32
 
27
- describe 'commit-message command' do
28
- before do
29
- executed_command_expectations(GradesFirst::CommitMessageCommand)
30
- end
33
+ specify 'branch command is hooked up' do
34
+ assert_command_hooked_up 'branch', GradesFirst::BranchCommand
35
+ end
31
36
 
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
37
+ specify 'commit-message command is hooked up' do
38
+ assert_command_hooked_up 'commit-message', GradesFirst::CommitMessageCommand
38
39
  end
39
40
 
40
- describe 'task command' do
41
- before do
42
- executed_command_expectations(GradesFirst::TaskCommand)
43
- end
41
+ # Waiting on fix for subcommands with default command.
42
+ # https://github.com/erikhuda/thor/pull/374
43
+ #specify 'task command is hooked up to default list subcommand' do
44
+ # assert_command_hooked_up 'task', GradesFirst::TaskListCommand
45
+ #end
44
46
 
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
47
+ specify 'task add command is hooked up' do
48
+ assert_command_hooked_up(
49
+ 'task add "This is a task."',
50
+ GradesFirst::TaskAddCommand
51
+ )
52
+ end
53
+
54
+ specify 'task delete command is hooked up' do
55
+ assert_command_hooked_up('task delete 1', GradesFirst::TaskDeleteCommand)
56
+ end
57
+
58
+ specify 'task list command is hooked up' do
59
+ assert_command_hooked_up 'task list', GradesFirst::TaskListCommand
60
+ end
61
+
62
+ specify 'task move command is hooked up' do
63
+ assert_command_hooked_up('task move 5 1', GradesFirst::TaskMoveCommand)
64
+ end
65
+
66
+ specify 'task toggle command is hooked up' do
67
+ assert_command_hooked_up('task toggle 1', GradesFirst::TaskToggleCommand)
51
68
  end
52
69
 
53
70
  end
data/test/command_test.rb CHANGED
@@ -2,13 +2,11 @@ require 'test_helper'
2
2
  require 'gradesfirst/command'
3
3
 
4
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
5
+ specify '#story_id pulls the story id from the branch' do
6
+ assert '123', GradesFirst::Command.new.send(:story_id, 'abc/123')
7
+ end
9
8
 
10
- it 'should return nil if not story id' do
11
- assert_nil GradesFirst::Command.new.send(:story_id, 'abc')
12
- end
9
+ specify '#story_id returns nil if not story id' do
10
+ assert_nil GradesFirst::Command.new.send(:story_id, 'abc')
13
11
  end
14
12
  end
@@ -1,62 +1,54 @@
1
1
  require 'test_helper'
2
+ require 'pivotal_tracker'
2
3
  require 'gradesfirst/commit_message_command'
4
+ require 'pivotal_tracker'
3
5
 
4
6
  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
7
  describe '#response' do
16
- it 'should repond with formatted commit message' do
8
+ it 'responds with formatted commit message' do
17
9
  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
10
+ expect_requests stub_bar_story_request do
11
+ command.stub :current_branch, "test/#{bar_story_id}", &call_execute
21
12
  end
13
+ assert_equal fixture_file('commit_message.txt'), command.response
22
14
  end
23
15
  end
24
- end
25
16
 
26
- describe 'GradesFirst::CommitMessageCommand#constrain_line_length' do
27
- def test_string
28
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vitae.'
29
- end
17
+ describe '#constrain_line_length' do
18
+ def test_string
19
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vitae.'
20
+ end
30
21
 
31
- def test_string_ending_with_a_letter
32
- 'Lorem ipsum dolor sit amet'
33
- end
22
+ def test_string_ending_with_a_letter
23
+ 'Lorem ipsum dolor sit amet'
24
+ end
34
25
 
35
- def test_string_with_line_feeds_at_30
36
- "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.\nFusce vitae."
37
- end
26
+ def test_string_with_line_feeds_at_30
27
+ "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.\nFusce vitae."
28
+ end
38
29
 
39
- it 'should add new lines at natural breaks when length is exceeded' do
40
- command = GradesFirst::CommitMessageCommand.new
41
- assert_equal(
42
- test_string_with_line_feeds_at_30,
43
- command.send(:constrain_line_length, test_string, 30)
44
- )
45
- end
30
+ it 'adds new lines at natural breaks when length is exceeded' do
31
+ command = GradesFirst::CommitMessageCommand.new
32
+ assert_equal(
33
+ test_string_with_line_feeds_at_30,
34
+ command.send(:constrain_line_length, test_string, 30)
35
+ )
36
+ end
46
37
 
47
- it 'should do nothing when length is not exceeded' do
48
- command = GradesFirst::CommitMessageCommand.new
49
- assert_equal(
50
- test_string,
51
- command.send(:constrain_line_length, test_string, 80)
52
- )
53
- end
38
+ it 'does nothing when length is not exceeded' do
39
+ command = GradesFirst::CommitMessageCommand.new
40
+ assert_equal(
41
+ test_string,
42
+ command.send(:constrain_line_length, test_string, 80)
43
+ )
44
+ end
54
45
 
55
- it 'should not wrap when ending with a letter' do
56
- command = GradesFirst::CommitMessageCommand.new
57
- assert_equal(
58
- test_string_ending_with_a_letter,
59
- command.send(:constrain_line_length, test_string_ending_with_a_letter, 80)
60
- )
46
+ it 'does not wrap when ending with a letter' do
47
+ command = GradesFirst::CommitMessageCommand.new
48
+ assert_equal(
49
+ test_string_ending_with_a_letter,
50
+ command.send(:constrain_line_length, test_string_ending_with_a_letter, 80)
51
+ )
52
+ end
61
53
  end
62
54
  end
@@ -0,0 +1,10 @@
1
+ {
2
+ "updated_at": "2014-01-14T12:00:00Z",
3
+ "description": "New task.",
4
+ "complete": false,
5
+ "kind": "task",
6
+ "id": 5100,
7
+ "created_at": "2014-01-14T12:00:00Z",
8
+ "position": 3,
9
+ "story_id": 558
10
+ }