pgit 0.0.4 → 1.0.0

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