pgit 0.0.4 → 1.0.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.agignore +3 -0
  3. data/.travis.yml +1 -0
  4. data/README.markdown +32 -9
  5. data/Rakefile +12 -0
  6. data/bin/pgit +167 -25
  7. data/lib/pgit.rb +40 -10
  8. data/lib/pgit/bilateral/handle_back.rb +22 -0
  9. data/lib/pgit/bilateral/handle_choose_story.rb +31 -0
  10. data/lib/pgit/bilateral/story.rb +44 -0
  11. data/lib/pgit/command.rb +83 -0
  12. data/lib/pgit/command/add.rb +36 -0
  13. data/lib/pgit/command/application.rb +21 -0
  14. data/lib/pgit/command/edit.rb +36 -0
  15. data/lib/pgit/command/remove.rb +36 -0
  16. data/lib/pgit/command/run.rb +32 -0
  17. data/lib/pgit/command/show.rb +53 -0
  18. data/lib/pgit/configuration.rb +27 -3
  19. data/lib/pgit/current_project.rb +9 -45
  20. data/lib/pgit/current_project/validator.rb +2 -1
  21. data/lib/pgit/error/external.rb +11 -0
  22. data/lib/pgit/error/user.rb +12 -0
  23. data/lib/pgit/helpers/heredoc.rb +17 -0
  24. data/lib/pgit/helpers/query_methods.rb +63 -0
  25. data/lib/pgit/helpers/string_extensions.rb +29 -0
  26. data/lib/pgit/installer/bash_auto_completion.rb +57 -0
  27. data/lib/pgit/pivotal/collection_request.rb +21 -0
  28. data/lib/pgit/pivotal/individual_request.rb +47 -0
  29. data/lib/pgit/pivotal/iteration.rb +6 -0
  30. data/lib/pgit/pivotal/iterations.rb +15 -0
  31. data/lib/pgit/pivotal/project.rb +6 -0
  32. data/lib/pgit/pivotal/projects.rb +20 -0
  33. data/lib/pgit/pivotal/query.rb +8 -0
  34. data/lib/pgit/pivotal/request.rb +33 -0
  35. data/lib/pgit/pivotal/request/query.rb +25 -0
  36. data/lib/pgit/pivotal/story.rb +38 -0
  37. data/lib/pgit/pivotal_request_validator.rb +1 -1
  38. data/lib/pgit/project.rb +78 -0
  39. data/lib/pgit/project/add.rb +28 -0
  40. data/lib/pgit/project/application.rb +21 -0
  41. data/lib/pgit/project/interactive_adder.rb +41 -0
  42. data/lib/pgit/project/remove.rb +41 -0
  43. data/lib/pgit/project/reuse_api_token_adder.rb +48 -0
  44. data/lib/pgit/response_handler.rb +16 -0
  45. data/lib/pgit/root.rb +5 -0
  46. data/lib/pgit/status.rb +16 -0
  47. data/lib/pgit/story_branch/application.rb +3 -3
  48. data/lib/pgit/{name_parser.rb → story_branch/name_parser.rb} +0 -0
  49. data/lib/pgit/story_branch/story_id_parser.rb +11 -0
  50. data/lib/pgit/validators/project_validator.rb +20 -0
  51. data/lib/pgit/version.rb +1 -1
  52. data/lib/pivotal +0 -0
  53. data/pgit.gemspec +5 -0
  54. data/spec/fixtures/iterations +1 -0
  55. data/spec/pgit/bilateral/handle_back_spec.rb +29 -0
  56. data/spec/pgit/bilateral/handle_choose_story_spec.rb +17 -0
  57. data/spec/pgit/bilateral/story_spec.rb +178 -0
  58. data/spec/pgit/command/add_spec.rb +68 -0
  59. data/spec/pgit/command/application_spec.rb +110 -0
  60. data/spec/pgit/command/edit_spec.rb +61 -0
  61. data/spec/pgit/command/remove_spec.rb +76 -0
  62. data/spec/pgit/command/run_spec.rb +49 -0
  63. data/spec/pgit/command/show_spec.rb +95 -0
  64. data/spec/pgit/command_spec.rb +299 -0
  65. data/spec/pgit/configuration_spec.rb +121 -18
  66. data/spec/pgit/current_project/validator_spec.rb +2 -1
  67. data/spec/pgit/current_project_spec.rb +20 -71
  68. data/spec/pgit/{external_error_spec.rb → error/external_spec.rb} +3 -3
  69. data/spec/pgit/error/user_spec.rb +17 -0
  70. data/spec/pgit/helpers/heredoc_spec.rb +33 -0
  71. data/spec/pgit/helpers/query_methods_spec.rb +24 -0
  72. data/spec/pgit/helpers/string_extensions_spec.rb +49 -0
  73. data/spec/pgit/installer/bash_auto_completion_spec.rb +134 -0
  74. data/spec/pgit/pivotal/individual_request_spec.rb +32 -0
  75. data/spec/pgit/pivotal/iteration_spec.rb +19 -0
  76. data/spec/pgit/pivotal/iterations_spec.rb +37 -0
  77. data/spec/pgit/pivotal/project_spec.rb +9 -0
  78. data/spec/pgit/pivotal/projects_spec.rb +48 -0
  79. data/spec/pgit/pivotal/request/query_spec.rb +24 -0
  80. data/spec/pgit/pivotal/story_spec.rb +113 -0
  81. data/spec/pgit/pivotal_request_validator_spec.rb +3 -3
  82. data/spec/pgit/project/add_spec.rb +52 -0
  83. data/spec/pgit/project/application_spec.rb +69 -0
  84. data/spec/pgit/project/interactive_adder_spec.rb +45 -0
  85. data/spec/pgit/project/remove_spec.rb +86 -0
  86. data/spec/pgit/project/reuse_api_token_adder_spec.rb +41 -0
  87. data/spec/pgit/project_spec.rb +513 -0
  88. data/spec/pgit/status_spec.rb +40 -0
  89. data/spec/pgit/story_branch/application_spec.rb +5 -8
  90. data/spec/pgit/story_branch/name_parser_spec.rb +3 -3
  91. data/spec/pgit/story_branch/story_id_parser_spec.rb +17 -0
  92. data/spec/pgit/validators/project_validator_spec.rb +39 -0
  93. metadata +146 -21
  94. data/lib/pgit/configuration/layout_error.rb +0 -9
  95. data/lib/pgit/configuration/missing_attributes_error.rb +0 -10
  96. data/lib/pgit/configuration/not_found_error.rb +0 -10
  97. data/lib/pgit/configuration/project_missing_error.rb +0 -10
  98. data/lib/pgit/configuration/validator.rb +0 -41
  99. data/lib/pgit/current_project/no_paths_match_working_dir_error.rb +0 -10
  100. data/lib/pgit/external_error.rb +0 -9
  101. data/lib/pgit/installer/configuration.rb +0 -34
  102. data/lib/pgit/story.rb +0 -44
  103. data/spec/pgit/configuration/missing_attributes_error_spec.rb +0 -30
  104. data/spec/pgit/configuration/not_found_error_spec.rb +0 -17
  105. data/spec/pgit/configuration/project_missing_error_spec.rb +0 -30
  106. data/spec/pgit/configuration/validator_spec.rb +0 -79
  107. data/spec/pgit/current_project/no_paths_match_working_dir_error_spec.rb +0 -17
  108. data/spec/pgit/installer/configuration_spec.rb +0 -162
  109. data/spec/pgit/story_spec.rb +0 -35
@@ -0,0 +1,78 @@
1
+ require 'pgit'
2
+
3
+ module PGit
4
+ class Project < PGit::Pivotal::IndividualRequest
5
+ include ActiveModel::Validations
6
+ include PGit::Helpers::QueryMethods
7
+ extend PGit::Helpers::QueryMethods
8
+
9
+ validates_with PGit::Validators::ProjectValidator
10
+
11
+ attr_writer :api_token, :id, :path
12
+ attr_reader :path, :api_token, :id, :configuration, :kind
13
+ attr_query :id, :path, :api_token
14
+
15
+ def initialize(configuration=:no_config_given,
16
+ proj={},
17
+ &block)
18
+ yield self if block_given?
19
+ @query_hash = proj
20
+ @configuration = configuration
21
+ @cmds = proj.fetch('commands') { Array.new }
22
+ set_default_queries
23
+ end
24
+
25
+ def commands=(some_commands)
26
+ @cmds = some_commands
27
+ end
28
+
29
+ def commands
30
+ build_commands(@cmds)
31
+ end
32
+
33
+ def to_hash
34
+ {
35
+ "path" => path,
36
+ "api_token" => api_token,
37
+ "id" => id,
38
+ "commands" => commands.map {|cmd| cmd.to_hash}
39
+ }
40
+ end
41
+
42
+ def save!
43
+ ensure_given_queries
44
+ raise PGit::Error::User, errors.full_messages.first unless valid?
45
+
46
+ remove_old_copy { configuration.projects = configuration.projects << self }
47
+ end
48
+
49
+ def remove!
50
+ remove_old_copy
51
+ end
52
+
53
+ def exists?
54
+ configuration.projects.find {|p| p.path == path}
55
+ end
56
+
57
+ def sublink
58
+ "projects/#{id}"
59
+ end
60
+ private
61
+
62
+ def remove_old_copy
63
+ configuration.projects = configuration.projects.reject {|p| p.path == path}
64
+ yield if block_given?
65
+ configuration.save!
66
+ end
67
+
68
+ def build_commands(cmds)
69
+ cmds.map do |cmd|
70
+ if cmd.respond_to?(:name) && cmd.respond_to?(:steps)
71
+ cmd
72
+ else
73
+ cmd.map { |k,v| PGit::Command.new(k, v, self) }.first
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ require 'pgit'
2
+
3
+ #TODO: Find a way to make execute! just be a call on children tasks
4
+ module PGit
5
+ class Project
6
+ class Add
7
+ extend Forwardable
8
+
9
+ attr_reader :adder
10
+ def initialize(app)
11
+ @app = app
12
+ raise PGit::Error::User, 'Project path already exists. See `pgit proj update --help.`' if app.exists?
13
+
14
+ @reuse_adder = PGit::Project::ReuseApiTokenAdder.new(app.project, app.projects)
15
+ end
16
+
17
+ def execute!
18
+ @reuse_adder.execute!
19
+ @adder = PGit::Project::InteractiveAdder.new(@reuse_adder.project)
20
+
21
+ adder.execute!
22
+ adder.save!
23
+
24
+ puts "Successfully added the project!"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ require 'forwardable'
2
+
3
+ module PGit
4
+ class Project
5
+ class Application
6
+ extend Forwardable
7
+ def_delegators :@project, :exists?, :save!
8
+ def_delegators :@config, :projects
9
+ attr_reader :project
10
+
11
+ def initialize(global_opts, opts, args)
12
+ @config = PGit::Configuration.new
13
+ @project = PGit::Project.new(@config) do |p|
14
+ p.path = opts["path"]
15
+ p.api_token = opts["api_token"]
16
+ p.id = opts["id"]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'pgit'
2
+ require 'forwardable'
3
+
4
+ module PGit
5
+ class Project
6
+ class InteractiveAdder
7
+ extend Forwardable
8
+
9
+ def_delegators :@project, :save!
10
+ attr_reader :project
11
+
12
+ def initialize(project)
13
+ @project = project
14
+ end
15
+
16
+ def execute!
17
+ if project.api_token == :no_api_token_given
18
+ puts "What's the project api_token?"
19
+ project.api_token = STDIN.gets.chomp
20
+ end
21
+
22
+ get_projects
23
+ end
24
+
25
+ def get_projects
26
+ projects = PGit::Pivotal::Projects.new(api_token: project.api_token).get!
27
+ question = Interactive::Question.new do |q|
28
+ q.question = "Which project do you want to associate with the working directory?"
29
+ q.options = [projects]
30
+ q.columns = [:index, :name]
31
+ end
32
+
33
+ question.ask do |response|
34
+ if response.whole_number?
35
+ project.id = projects[response.to_i].id
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require 'interactive'
2
+ module PGit
3
+ class Project
4
+ class Remove
5
+ include Interactive
6
+
7
+ attr_reader :project, :projects
8
+ def initialize(app)
9
+ @project = app.project
10
+ @projects = app.projects
11
+
12
+ raise PGit::Error::User, "#{@project.path} is not in the configuration file." unless @project.exists?
13
+ end
14
+
15
+ def execute!
16
+ confirm.ask_and_wait_for_valid_response do |confirm_response|
17
+ if confirm_response.yes?
18
+ puts "Removing #{path} from the configuration file..."
19
+ project.remove!
20
+ puts "Removed."
21
+ elsif confirm_response.no?
22
+ puts "Cancelling..."
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def confirm
30
+ Question.new do |c|
31
+ c.question = "Are you sure you want to remove #{path} from the configuration file?"
32
+ c.options = [:yes, :no]
33
+ end
34
+ end
35
+
36
+ def path
37
+ @project.path
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ require 'pgit'
2
+
3
+ module PGit
4
+ class Project
5
+ class ReuseApiTokenAdder
6
+ include Interactive
7
+
8
+ attr_reader :projects, :project
9
+ def initialize(project_to_add_to, projects_in_config)
10
+ @project = project_to_add_to
11
+ @projects = projects_in_config
12
+ end
13
+
14
+ def execute!
15
+ return false if @projects.empty?
16
+
17
+ reuse_question.ask_and_wait_for_valid_response do |reuse_response|
18
+ if reuse_response.yes?
19
+ which_to_reuse_question.ask_and_wait_for_valid_response do |which_response|
20
+ if which_response.whole_number?
21
+ project.api_token = projects[which_response.to_i].api_token
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def reuse_question
31
+ Question.new do |q|
32
+ q.question = "Do you want to reuse an api token?"
33
+ q.options = [:yes, :no]
34
+ end
35
+ end
36
+
37
+ def which_to_reuse_question
38
+ Question.new do |q|
39
+ q.question = "Which one?"
40
+ q.options = [projects.map do |proj|
41
+ OpenStruct.new(api_token: proj.api_token, path: proj.path)
42
+ end, :cancel]
43
+ q.columns = [:index, :api_token, :path]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ module PGit
2
+ class ResponseHandler
3
+ attr_reader :question
4
+ def execute!
5
+ if response_can_be_handled?
6
+ @question.ask do |response|
7
+ @response_handlers.each {|handler| handler.new(options(response)).execute! }
8
+ end
9
+ end
10
+ end
11
+
12
+ def response_can_be_handled?
13
+ true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module PGit
2
+ def self.root
3
+ File.expand_path '../../..', __FILE__
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class PGit::Status
2
+ def initialize(global_options, options, args)
3
+ end
4
+
5
+ def execute!
6
+ current_branch = PGit::CurrentBranch.new
7
+ raise PGit::Error::User, 'The current branch is not associated with a story. Does not have a story id.' unless current_branch.story_id
8
+ story = PGit::Pivotal::Story.new(current_branch.story_id)
9
+ story_hash = story.get!
10
+ table = Terminal::Table.new do |t|
11
+ t.rows = story_hash.to_a
12
+ end
13
+
14
+ puts table
15
+ end
16
+ end
@@ -3,9 +3,9 @@ module PGit
3
3
  class Application
4
4
  def initialize(global_options, options, arguments)
5
5
  if story_id = options[:start]
6
- config_yaml = PGit::Configuration.new.to_yaml
7
- current_project = PGit::CurrentProject.new(config_yaml)
8
- story = PGit::Story.get(story_id, current_project)
6
+ story = PGit::Pivotal::Story.new(story_id)
7
+ story.get!
8
+
9
9
  name_parser = PGit::StoryBranch::NameParser.new(story)
10
10
  story_branch = PGit::StoryBranch.new(name_parser)
11
11
 
@@ -0,0 +1,11 @@
1
+ class PGit::StoryBranch::StoryIdParser
2
+ def initialize(branch_name)
3
+ @branch_name = branch_name
4
+ end
5
+
6
+ def parse
7
+ raise PGit::Error::User, "The current branch is not associated with a story. Does not have a story id." unless @branch_name.match(/\d+$/)
8
+
9
+ @branch_name.match(/\d+$/)[0]
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ require 'pgit'
2
+
3
+ module PGit
4
+ module Validators
5
+ class ProjectValidator < ActiveModel::Validator
6
+ def validate(project)
7
+ project.get!
8
+
9
+ unless project.respond_to?(:kind)
10
+ project.errors[:curl] << "is not able to do the request. Please check your internet connection."
11
+ end
12
+ #
13
+ if project.respond_to?(:kind) && project.kind == 'error'
14
+ project.errors[:base] << "Project api_token or id is not valid."
15
+ puts project.errors.full_messages
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module Pgit
2
- VERSION = '0.0.4'
2
+ VERSION = '1.0.0'
3
3
  end
File without changes
@@ -20,7 +20,12 @@ spec = Gem::Specification.new do |s|
20
20
  s.add_development_dependency('rspec', '~> 3.1')
21
21
  s.add_development_dependency('coveralls', '~> 0.7')
22
22
  s.add_development_dependency('simplecov', '~> 0.9')
23
+ s.add_development_dependency('pry-byebug', '~> 3.0.1')
24
+ s.add_development_dependency('byebug', '~> 3.5.1')
23
25
  s.add_runtime_dependency('gli','2.12.2')
26
+ s.add_runtime_dependency('activemodel','~> 4.2.0')
27
+ s.add_runtime_dependency('rainbow', '~> 2.0.0')
28
+ s.add_runtime_dependency('interactive', '~> 0.6.0')
24
29
  s.license = 'MIT'
25
30
  s.requirements << 'At least one project that uses Pivotal Tracker and Git'
26
31
  s.requirements << 'cURL, at least 7.35.0'
@@ -0,0 +1 @@
1
+ [{"kind":"iteration","number":15,"project_id":1228944,"team_strength":1,"stories":[{"kind":"story","id":90501214,"project_id":1228944,"name":"fix bug where calling project.get! defines methods on ALL projects. Use define_singleton_method.","story_type":"bug","current_state":"accepted","accepted_at":"2015-03-17T22:08:23Z","requested_by_id":1121520,"owned_by_id":1121520,"owner_ids":[1121520],"labels":[],"created_at":"2015-03-17T13:02:11Z","updated_at":"2015-03-17T22:12:28Z","url":"https://www.pivotaltracker.com/story/show/90501214"},{"kind":"story","id":90285028,"project_id":1228944,"name":"pgit proj asks if you want to reuse an api_token","description":"`Do you want to reuse an api_token? [y/n]`\n - y\n - `Which one? (1/2/c)\n - 1. /some/path: token\n - 2. /some/other/path: another_token\n ","story_type":"feature","current_state":"accepted","estimate":1,"accepted_at":"2015-03-18T01:11:47Z","requested_by_id":1121520,"owner_ids":[],"labels":[{"kind":"label","id":10378716,"project_id":1228944,"name":"project","created_at":"2015-01-05T11:42:35Z","updated_at":"2015-01-05T11:42:35Z"}],"created_at":"2015-03-13T14:29:42Z","updated_at":"2015-03-18T02:18:32Z","url":"https://www.pivotaltracker.com/story/show/90285028"},{"kind":"story","id":90566322,"project_id":1228944,"name":"pgit install no longer attempts to save an example config file in favor of using pgit proj add","story_type":"chore","current_state":"accepted","accepted_at":"2015-03-18T01:11:48Z","requested_by_id":1121520,"owned_by_id":1121520,"owner_ids":[1121520],"labels":[{"kind":"label","id":10378716,"project_id":1228944,"name":"project","created_at":"2015-01-05T11:42:35Z","updated_at":"2015-01-05T11:42:35Z"}],"created_at":"2015-03-18T01:07:29Z","updated_at":"2015-03-18T02:18:41Z","url":"https://www.pivotaltracker.com/story/show/90566322"},{"kind":"story","id":85418516,"project_id":1228944,"name":"pgit project rm","description":"Which project do you want to remove? [0/1/q]\n0 -- /Developer/blah\n1 -- /Developer/blah/blah\nq -- quit\n\nAre you sure you want to remove \"/Developer/blah\"? [y/c/q]\n\nIf user answers yes, proceed, \"Removing /Developer/blah as a PGit project.\"\nif user answers cancel, go back to first question\nIf user answers quit, then say you're quitting and quit.","story_type":"feature","current_state":"accepted","estimate":1,"accepted_at":"2015-03-20T00:22:35Z","requested_by_id":1121520,"owned_by_id":1121520,"owner_ids":[1121520],"labels":[{"kind":"label","id":10378716,"project_id":1228944,"name":"project","created_at":"2015-01-05T11:42:35Z","updated_at":"2015-01-05T11:42:35Z"}],"created_at":"2015-01-05T11:26:32Z","updated_at":"2015-03-20T00:22:34Z","url":"https://www.pivotaltracker.com/story/show/85418516"},{"kind":"story","id":90748872,"project_id":1228944,"name":"pgit story should display list of stories for the current and backlog iterations.","description":"`pgit story`, by default, will display a list of stories for the current iteration. It will ask, which story are you interested in? [0/1/2/3/4/5/c]\n 0 -- [feature-type] [points] [title] [assignment]\n 1 -- [feature-type] [points] [title] [assignment]\nIt should display information about the story, color coded.\n\n\n","story_type":"feature","current_state":"started","estimate":2,"requested_by_id":1121520,"owned_by_id":1121520,"owner_ids":[1121520],"labels":[{"kind":"label","id":10420512,"project_id":1228944,"name":"story","created_at":"2015-01-08T12:25:39Z","updated_at":"2015-01-08T12:25:39Z"}],"created_at":"2015-03-20T00:56:29Z","updated_at":"2015-03-22T12:35:20Z","url":"https://www.pivotaltracker.com/story/show/90748872"}],"start":"2015-03-16T04:00:00Z","finish":"2015-03-23T04:00:00Z"},{"kind":"iteration","number":16,"project_id":1228944,"team_strength":1,"stories":[],"start":"2015-03-23T04:00:00Z","finish":"2015-03-30T04:00:00Z"},{"kind":"iteration","number":17,"project_id":1228944,"team_strength":1,"stories":[{"kind":"story","id":90833342,"project_id":1228944,"name":"make getting project ids easier when adding a project","description":"```{\"kind\":\"me\",\"id\":1121520,\"name\":\"Edderic\",\"initials\":\"ED\",\"username\":\"edderic\",\"time_zone\":{\"kind\":\"time_zone\",\"olson_name\":\"America/Los_Angeles\",\"offset\":\"-07:00\"},\"api_token\":\"302ebab4f92dcc77dbee9d18bf09e576\",\"has_google_identity\":true,\"projects\":[{\"kind\":\"membership_summary\",\"id\":3492008,\"project_id\":915432,\"project_name\":\"ideabind\",\"project_color\":\"155f8b\",\"role\":\"owner\",\"last_viewed_at\":\"2015-02-10T01:26:35Z\"},{\"kind\":\"membership_summary\",\"id\":3570108,\"project_id\":932970,\"project_name\":\"palace\",\"project_color\":\"ee61f0\",\"role\":\"owner\",\"last_viewed_at\":\"2013-12-04T19:48:46Z\"},{\"kind\":\"membership_summary\",\"id\":3593330,\"project_id\":938010,\"project_name\":\"Ojibss\",\"project_color\":\"b800bb\",\"role\":\"owner\",\"last_viewed_at\":\"2013-10-28T15:25:44Z\"},{\"kind\":\"membership_summary\",\"id\":3662734,\"project_id\":953934,\"project_name\":\"QuidPlays\",\"project_color\":\"00a3d6\",\"role\":\"owner\",\"last_viewed_at\":\"2013-11-11T10:14:36Z\"},{\"kind\":\"membership_summary\",\"id\":3699234,\"project_id\":961764,\"project_name\":\"Granuvolver\",\"project_color\":\"d82b00\",\"role\":\"owner\",\"last_viewed_at\":\"2014-01-15T19:37:38Z\"},{\"kind\":\"membership_summary\",\"id\":3801016,\"project_id\":985420,\"project_name\":\"Adjust Brightness Script\",\"project_color\":\"ffc100\",\"role\":\"owner\",\"last_viewed_at\":\"2014-01-05T02:54:46Z\"},{\"kind\":\"membership_summary\",\"id\":4064208,\"project_id\":1041282,\"project_name\":\"Training\",\"project_color\":\"818182\",\"role\":\"owner\",\"last_viewed_at\":\"2014-03-20T00:57:48Z\"},{\"kind\":\"membership_summary\",\"id\":4434754,\"project_id\":1120660,\"project_name\":\"Cooking Assistant\",\"project_color\":\"e46642\",\"role\":\"owner\",\"last_viewed_at\":\"2014-11-13T00:34:00Z\"},{\"kind\":\"membership_summary\",\"id\":4948554,\"project_id\":1228944,\"project_name\":\"PGit\",\"project_color\":\"8100ea\",\"role\":\"owner\",\"last_viewed_at\":\"2015-03-21T14:25:10Z\"},{\"kind\":\"membership_summary\",\"id\":5271772,\"project_id\":1300676,\"project_name\":\"Interactive\",\"project_color\":\"555555\",\"role\":\"owner\",\"last_viewed_at\":\"2015-03-21T13:15:55Z\"}],\"email\":\"edderic@gmail.com\",\"receives_in_app_notifications\":true,\"created_at\":\"2013-09-18T19:08:50Z\",\"updated_at\":\"2015-03-21T15:04:26Z\"}```","story_type":"feature","current_state":"unstarted","estimate":2,"requested_by_id":1121520,"owner_ids":[],"labels":[{"kind":"label","id":10378716,"project_id":1228944,"name":"project","created_at":"2015-01-05T11:42:35Z","updated_at":"2015-01-05T11:42:35Z"}],"created_at":"2015-03-21T15:34:55Z","updated_at":"2015-03-21T15:42:18Z","url":"https://www.pivotaltracker.com/story/show/90833342"}],"start":"2015-03-30T04:00:00Z","finish":"2015-04-06T04:00:00Z"},{"kind":"iteration","number":18,"project_id":1228944,"team_strength":1,"stories":[{"kind":"story","id":86168300,"project_id":1228944,"name":"pgit project mv --src=source_path --dest=destination_path","story_type":"feature","current_state":"unstarted","estimate":1,"requested_by_id":1121520,"owner_ids":[],"labels":[{"kind":"label","id":10378716,"project_id":1228944,"name":"project","created_at":"2015-01-05T11:42:35Z","updated_at":"2015-01-05T11:42:35Z"}],"created_at":"2015-01-15T00:18:57Z","updated_at":"2015-01-15T00:24:26Z","url":"https://www.pivotaltracker.com/story/show/86168300"}],"start":"2015-04-06T04:00:00Z","finish":"2015-04-13T04:00:00Z"},{"kind":"iteration","number":19,"project_id":1228944,"team_strength":1,"stories":[{"kind":"story","id":86169384,"project_id":1228944,"name":"pgit project show","story_type":"feature","current_state":"unstarted","estimate":1,"requested_by_id":1121520,"owner_ids":[],"labels":[],"created_at":"2015-01-15T00:29:39Z","updated_at":"2015-01-15T00:30:39Z","url":"https://www.pivotaltracker.com/story/show/86169384"},{"kind":"story","id":86366000,"project_id":1228944,"name":"Move models into their own folder","story_type":"chore","current_state":"unstarted","requested_by_id":1121520,"owner_ids":[],"labels":[],"created_at":"2015-01-17T14:11:49Z","updated_at":"2015-02-01T22:57:23Z","url":"https://www.pivotaltracker.com/story/show/86366000"},{"kind":"story","id":86365990,"project_id":1228944,"name":"Move Command-Line classes to their own folder","story_type":"chore","current_state":"unstarted","requested_by_id":1121520,"owner_ids":[],"labels":[],"created_at":"2015-01-17T14:10:57Z","updated_at":"2015-02-01T22:57:25Z","url":"https://www.pivotaltracker.com/story/show/86365990"}],"start":"2015-04-13T04:00:00Z","finish":"2015-04-20T04:00:00Z"}]
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe PGit::Bilateral::HandleBack do
4
+ describe '#execute!' do
5
+ it 'should call #reask on the parent_question' do
6
+ stories = double('stories')
7
+ question = instance_double('Interactive::Question', reask!: nil)
8
+ response = instance_double('Interactive::Response', back?: true)
9
+ options = {response: response, parent_question: question, stories: stories}
10
+
11
+ back_handler = PGit::Bilateral::HandleBack.new(options)
12
+ back_handler.execute!
13
+
14
+ expect(question).to have_received(:reask!)
15
+ end
16
+
17
+ it 'should not call #reask on the parent question if response is not back' do
18
+ stories = double('stories')
19
+ question = instance_double('Interactive::Question', reask!: nil)
20
+ response = instance_double('Interactive::Response', back?: false)
21
+ options = {response: response, parent_question: question, stories: stories}
22
+
23
+ back_handler = PGit::Bilateral::HandleBack.new(options)
24
+ back_handler.execute!
25
+
26
+ expect(question).not_to have_received(:reask!)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe PGit::Bilateral::HandleChooseStory do
4
+ it 'should create the branch' do
5
+ some_story = double('PGit::Story')
6
+ chosen_story = double('PGit::Story')
7
+ options = {response: '1', stories: [some_story, chosen_story]}
8
+ story_branch = double('PGit::StoryBranch', start: true)
9
+
10
+ name_parser = double('PGit::StoryBranch::NameParser')
11
+ allow(PGit::StoryBranch::NameParser).to receive(:new).with(chosen_story).and_return(name_parser)
12
+ allow(PGit::StoryBranch).to receive(:new).with(name_parser).and_return(story_branch)
13
+ handle_choose_story = PGit::Bilateral::HandleChooseStory.new(options)
14
+
15
+ expect(story_branch).to have_received(:start)
16
+ end
17
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe PGit::Bilateral::Story do
4
+ describe 'scope is bad scope' do
5
+ it 'should raise an error' do
6
+ project = instance_double('PGit::Project', id: 12345, api_token: 'someapitoken')
7
+ options = { scope: 'blah' }
8
+ query = {scope: :current}
9
+ story_1 = instance_double('PGit::Story',
10
+ story_type: 'chore',
11
+ estimate: 2,
12
+ name: 'some story',
13
+ current_state: 'unstarted')
14
+ story_2 = instance_double('PGit::Story',
15
+ story_type: 'feature',
16
+ estimate: 3,
17
+ name: 'some other story',
18
+ current_state: 'unstarted')
19
+ stories = [story_1, story_2]
20
+ iteration_1 = double('PGit::Pivotal::Iteration', stories: stories)
21
+ iterations = [iteration_1]
22
+ iterations_obj = double('PGit::Pivotal::Iterations', get!: iterations)
23
+ allow(PGit::Pivotal::Iterations).
24
+ to receive(:new).with(query).and_return(iterations_obj)
25
+ config = double('config', :question= => nil, :options= => nil, :columns= => nil)
26
+ question = instance_double('Interactive::Question', ask: nil)
27
+ allow(Interactive::Question).to receive(:new).and_yield(config).and_return(question)
28
+ expect{ PGit::Bilateral::Story.new(options) }.to raise_error(PGit::Error::User, 'Invalid options. See `pgit iteration -h` for valid options.')
29
+ end
30
+ end
31
+
32
+ describe '#execute!' do
33
+ describe 'scope is done' do
34
+ it 'should show the done ones' do
35
+ project = instance_double('PGit::Project', id: 12345, api_token: 'someapitoken')
36
+ options = { scope: :done }
37
+ query = {scope: :done}
38
+ story_1 = instance_double('PGit::Story',
39
+ story_type: 'chore',
40
+ estimate: 2,
41
+ name: 'some story',
42
+ current_state: 'accepted')
43
+ story_2 = instance_double('PGit::Story',
44
+ story_type: 'feature',
45
+ estimate: 3,
46
+ name: 'some other story',
47
+ current_state: 'accepted')
48
+ stories = [story_1, story_2]
49
+ iteration_1 = double('PGit::Pivotal::Iteration', stories: stories)
50
+ iterations = [iteration_1]
51
+ iterations_obj = double('PGit::Pivotal::Iterations', get!: iterations)
52
+ allow(PGit::Pivotal::Iterations).
53
+ to receive(:new).with(query).and_return(iterations_obj)
54
+ config = double('config', :question= => nil, :options= => nil, :columns= => nil)
55
+ question = instance_double('Interactive::Question', ask: nil)
56
+ allow(Interactive::Question).to receive(:new).and_yield(config).and_return(question)
57
+ interactive_story = PGit::Bilateral::Story.new(options)
58
+ interactive_story.execute!
59
+
60
+ expect(config).to have_received(:question=).with("Which story do you want to branch-ify?")
61
+ expect(config).to have_received(:options=).with([stories, :back])
62
+ expect(config).to have_received(:columns=).with([:index, :story_type, :estimate, :name, :current_state])
63
+ expect(question).to have_received(:ask)
64
+ end
65
+ end
66
+
67
+ describe 'scope is backlog' do
68
+ it 'should show the backlog stories' do
69
+ project = instance_double('PGit::Project', id: 12345, api_token: 'someapitoken')
70
+ options = { scope: :backlog }
71
+ query = {scope: :backlog}
72
+ story_1 = instance_double('PGit::Story',
73
+ story_type: 'chore',
74
+ estimate: 2,
75
+ name: 'some story',
76
+ current_state: 'unstarted')
77
+ story_2 = instance_double('PGit::Story',
78
+ story_type: 'feature',
79
+ estimate: 3,
80
+ name: 'some other story',
81
+ current_state: 'unstarted')
82
+ stories = [story_1, story_2]
83
+ iteration_1 = double('PGit::Pivotal::Iteration', stories: stories)
84
+ iterations = [iteration_1]
85
+ iterations_obj = double('PGit::Pivotal::Iterations', get!: iterations)
86
+ allow(PGit::Pivotal::Iterations).
87
+ to receive(:new).with(query).and_return(iterations_obj)
88
+ config = double('config', :question= => nil, :options= => nil, :columns= => nil)
89
+ question = instance_double('Interactive::Question', ask: nil)
90
+ response = instance_double('Interactive::Response', valid?: true)
91
+ allow(question).to receive(:ask).and_yield(response)
92
+ allow(Interactive::Question).to receive(:new).and_yield(config).and_return(question)
93
+ handle_choose_story = instance_double('PGit::Bilateral::HandleChooseStory',
94
+ execute!: nil)
95
+ interactive_story = PGit::Bilateral::Story.new(options)
96
+ response_options = {response: response, stories: stories, parent_question: interactive_story.question}
97
+ allow(PGit::Bilateral::HandleChooseStory).to receive(:new).with(response_options).
98
+ and_return(handle_choose_story)
99
+ interactive_story.execute!
100
+
101
+ expect(config).to have_received(:question=).with("Which story do you want to branch-ify?")
102
+ expect(config).to have_received(:options=).with([stories, :back])
103
+ expect(config).to have_received(:columns=).with([:index, :story_type, :estimate, :name, :current_state])
104
+ expect(question).to have_received(:ask)
105
+ expect(handle_choose_story).to have_received(:execute!)
106
+ end
107
+ end
108
+
109
+ describe 'scope is current' do
110
+ it 'should show a list of stories for the current iteration' do
111
+ project = instance_double('PGit::Project', id: 12345, api_token: 'someapitoken')
112
+ options = { scope: :current }
113
+ query = {scope: :current}
114
+ story_1 = instance_double('PGit::Story',
115
+ story_type: 'chore',
116
+ estimate: 2,
117
+ name: 'some story',
118
+ current_state: 'unstarted')
119
+ story_2 = instance_double('PGit::Story',
120
+ story_type: 'feature',
121
+ estimate: 3,
122
+ name: 'some other story',
123
+ current_state: 'unstarted')
124
+ stories = [story_1, story_2]
125
+ iteration_1 = double('PGit::Pivotal::Iteration', stories: stories)
126
+ iterations = [iteration_1]
127
+ iterations_obj = double('PGit::Pivotal::Iterations', get!: iterations)
128
+ allow(PGit::Pivotal::Iterations).
129
+ to receive(:new).with(query).and_return(iterations_obj)
130
+ config = double('config', :question= => nil, :options= => nil, :columns= => nil)
131
+ question = instance_double('Interactive::Question', ask: nil)
132
+ allow(Interactive::Question).to receive(:new).and_yield(config).and_return(question)
133
+ interactive_story = PGit::Bilateral::Story.new(options)
134
+ interactive_story.execute!
135
+
136
+ expect(config).to have_received(:question=).with("Which story do you want to branch-ify?")
137
+ expect(config).to have_received(:options=).with([stories, :back])
138
+ expect(config).to have_received(:columns=).with([:index, :story_type, :estimate, :name, :current_state])
139
+ expect(question).to have_received(:ask)
140
+ end
141
+ end
142
+
143
+ describe 'scope is current_backlog' do
144
+ it 'show a list of stories for the current and backlog iteration' do
145
+ project = instance_double('PGit::Project', id: 12345, api_token: 'someapitoken')
146
+
147
+ options = { scope: :current_backlog }
148
+ query = {scope: :current_backlog}
149
+ story_1 = instance_double('PGit::Story',
150
+ story_type: 'chore',
151
+ estimate: 2,
152
+ name: 'some story',
153
+ current_state: 'unstarted')
154
+ story_2 = instance_double('PGit::Story',
155
+ story_type: 'feature',
156
+ estimate: 3,
157
+ name: 'some other story',
158
+ current_state: 'unstarted')
159
+ stories = [story_1, story_2]
160
+ iteration_1 = double('PGit::Pivotal::Iteration', stories: stories)
161
+ iterations = [iteration_1]
162
+ iterations_obj = double('PGit::Pivotal::Iterations', get!: iterations)
163
+ allow(PGit::Pivotal::Iterations).
164
+ to receive(:new).with(query).and_return(iterations_obj)
165
+ config = double('config', :question= => nil, :options= => nil, :columns= => nil)
166
+ question = instance_double('Interactive::Question', ask: nil)
167
+ allow(Interactive::Question).to receive(:new).and_yield(config).and_return(question)
168
+ interactive_story = PGit::Bilateral::Story.new(options)
169
+ interactive_story.execute!
170
+
171
+ expect(config).to have_received(:question=).with("Which story do you want to branch-ify?")
172
+ expect(config).to have_received(:options=).with([stories, :back])
173
+ expect(config).to have_received(:columns=).with([:index, :story_type, :estimate, :name, :current_state])
174
+ expect(question).to have_received(:ask)
175
+ end
176
+ end
177
+ end
178
+ end