gradesfirst 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }