jira-slurper 1.3.1 → 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6ad181f2dca8798ddadff89769d39844986dd03
4
- data.tar.gz: d797ec588af063f63d4bcee2065b5cf833fb0cb0
3
+ metadata.gz: 8fe9b95e6287faca051d81a53b31a96dc83bcc97
4
+ data.tar.gz: be88e9ef68afb38a263592a8305d2264762b47e2
5
5
  SHA512:
6
- metadata.gz: 82041d79a329dfd4db59771b6e35c1571c8bbf45a0dd814ab466cf7847d4c40d078e114eb0cc04a0ba44c9c806966544773840c87cb612cbef1e2912de957961
7
- data.tar.gz: 9c02687fb906ffde1115e8d3f00437f02fe14fca41d5c3bbadc563bbf13b5d2797e3102ef9ffc3086f9c65ab4689307ecbb16b94a36d952799d810dc00a88d93
6
+ metadata.gz: 19d143ebdf7583731ba5d8bdfb1e566bc23f535ffb1793e6d9f0bbe8ddfda09e314314812d86b77cb35a9a0c08c7aa53c2b8910e470b07a3c78d7d2cb6da205b
7
+ data.tar.gz: a755f9a35edfd10c66b3b8702222278c88656b7c869a714a528b2bb3f72201b8dd6352a6328dc8d3cb84e72cc9b6eeabe6f41ef0519857b89386f4ec4ad853b7
data/README.rdoc CHANGED
@@ -1,12 +1,12 @@
1
- = slurper gem
1
+ = jira-slurper gem
2
2
 
3
- Slurper allows you to quickly compose your stories in a text file and import them into Pivotal Tracker.
3
+ Slurper allows you to quickly compose your stories in a text file and import them into Jira or Pivotal Tracker.
4
4
 
5
5
  Works great with slurper.vim! (http://github.com/adamlowe/vim-slurper)
6
6
 
7
7
  == Install
8
8
 
9
- gem install slurper
9
+ gem install jira-slurper
10
10
 
11
11
  == Config
12
12
 
data/bin/slurp CHANGED
@@ -41,10 +41,10 @@ end.parse!
41
41
 
42
42
  story_file = ARGV.empty? ? "stories.slurper" : ARGV[0]
43
43
 
44
- handlers = [Jira.new, Pivotal.new]
44
+ handlers = [Jira::Handler.new, Pivotal.new]
45
45
 
46
- raise "Stories file not found: '#{story_file}'" if not File::exists? story_file
47
- raise "Config file not found: '#{options[:config_file]}'" if not File::exists? options[:config_file]
46
+ raise "Stories file not found: '#{story_file}'" unless File::exists? story_file
47
+ raise "Config file not found: '#{options[:config_file]}'" unless File::exists? options[:config_file]
48
48
 
49
49
  if options[:dump]
50
50
  Slurper.dump(options[:config_file], options[:filter], handlers, options[:reverse])
@@ -0,0 +1,108 @@
1
+ module Jira
2
+ module Inflectors
3
+
4
+ class BaseInflector
5
+ def initialize(value)
6
+ @value = value
7
+ end
8
+
9
+ def value
10
+ @value
11
+ end
12
+ end
13
+
14
+ class StringInflector < BaseInflector
15
+ def initialize(value)
16
+ super(value.to_s)
17
+ end
18
+
19
+ def value
20
+ super.strip
21
+ end
22
+ end
23
+
24
+ class NumberFieldInflector < BaseInflector
25
+ def value
26
+ @value.to_f
27
+ end
28
+ end
29
+
30
+ class LabelInflector < StringInflector
31
+ def value
32
+ super.gsub(' ', '_')
33
+ end
34
+ end
35
+
36
+ class IndexedValueInflector < BaseInflector
37
+ def initialize(value, key=nil)
38
+ super(value)
39
+ if key.nil?
40
+ key = :id if value.is_a?(Fixnum) or (value.is_a?(String) && value[/^[0-9]+$/])
41
+ end
42
+ @key = key || :value
43
+ end
44
+
45
+ def value
46
+ {@key.to_sym => super}
47
+ end
48
+ end
49
+
50
+ class CompositeInflector < BaseInflector
51
+ def initialize(*inflectors)
52
+ @inflectors = inflectors
53
+ end
54
+
55
+ def value
56
+ @inflectors.map(&:value)
57
+ end
58
+ end
59
+
60
+ def self.factory(type, value)
61
+ case type.to_s
62
+ when 'FreeTextField', 'TextField', 'URLField'
63
+ StringInflector.new(value)
64
+ when 'GroupPicker', 'UserPicker', 'SingleVersionPicker', 'SingleSelect'
65
+ IndexedValueInflector.new(value, :name)
66
+ when 'MultiUserPicker', 'MultiGroupPicker', 'MultiSelect'
67
+ inner = ensure_list(value) do |value|
68
+ value.map do |v|
69
+ begin
70
+ factory(type.sub('Multi', ''), v)
71
+ rescue
72
+ factory(type.sub('Multi', 'Single'), v)
73
+ end
74
+ end
75
+ end
76
+ CompositeInflector.new(*inner)
77
+ when 'Labels'
78
+ ensure_list(value) do |value|
79
+ CompositeInflector.new(*(value.map { |v| LabelInflector.new(v) }))
80
+ end
81
+ when 'SelectList'
82
+ IndexedValueInflector.new(value, :value)
83
+ when 'ProjectPicker'
84
+ IndexedValueInflector.new(value, :key)
85
+ else
86
+ inflector = "#{type}Inflector".to_sym
87
+ unless const_defined?(inflector)
88
+ raise "Unrecognized field type \"#{type}\""
89
+ end
90
+ const_get(inflector).new(value)
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def self.ensure_list(value)
97
+ value = case value
98
+ when String
99
+ value.split(',').map(&:strip)
100
+ when Enumerable
101
+ value
102
+ else
103
+ raise('Expected enumerable value')
104
+ end
105
+ yield value if block_given?
106
+ end
107
+ end
108
+ end
data/lib/jira.rb CHANGED
@@ -2,112 +2,162 @@ require 'faraday'
2
2
  require 'configliere'
3
3
  require 'base64'
4
4
  require 'json'
5
+ require 'jira/inflectors'
5
6
 
6
- # Jira handler
7
- class Jira
7
+ module Jira
8
+ class Handler
8
9
 
9
- def initialize(jira_mapper = nil, jira_api = nil)
10
- @mapper = jira_mapper || JiraStoryMapper.new
11
- @api = jira_api || JiraApi.new
12
- end
10
+ def initialize(jira_mapper = nil, jira_api = nil)
11
+ @mapper = jira_mapper || StoryMapper.new
12
+ @api = jira_api || ApiConsumer.new
13
+ end
13
14
 
14
- def supports?(config)
15
- config.fetch('tracker', '').downcase == 'jira'
16
- end
15
+ def supports?(config)
16
+ config.fetch('tracker', '').downcase == 'jira'
17
+ end
17
18
 
18
- def configure!(config)
19
- settings = Configliere::Param.new
19
+ def configure!(config)
20
+ settings = Configliere::Param.new
20
21
 
21
- settings.define :username, :required => true, :env_var => 'JIRA_USERNAME'
22
- settings.define :password, :required => true, :env_var => 'JIRA_PASSWORD'
23
- settings.define :url, :required => true
24
- settings.define :project, :required => true
22
+ settings.define :username, :required => true, :env_var => 'JIRA_USERNAME'
23
+ settings.define :password, :required => true, :env_var => 'JIRA_PASSWORD'
24
+ settings.define :url, :required => true
25
+ settings.define :project, :required => true
26
+ settings.define :mappings, :required => false
25
27
 
26
- settings.defaults api_version: 'latest', default_labels: ''
28
+ settings.defaults api_version: 'latest', default_labels: '', mappings: {}
27
29
 
28
- settings.use(config)
29
- settings.resolve!
30
+ settings.use(config)
31
+ settings.resolve!
30
32
 
31
- @config = settings
32
- end
33
+ @mapper.configure!(settings)
34
+ @config = settings
35
+ end
36
+
37
+ def handle(yaml_story)
38
+ @api.configure!(@config[:url], @config[:username], @config[:password], @config[:api_version])
33
39
 
34
- def handle(yaml_story)
35
- @api.configure!(@config[:url], @config[:username], @config[:password], @config[:api_version])
40
+ yaml_story['project'] = @config[:project]
41
+ yaml_story['reporter'] = @config[:username] unless yaml_story.key?('reporter')
36
42
 
37
- issue = @mapper.map(yaml_story, @config[:project])
43
+ issue = @mapper.map(yaml_story)
38
44
 
39
- success, response_body = @api.create_issue(issue)
40
- response = JSON.parse response_body
45
+ success, response_body = @api.create_issue(issue)
46
+ response = JSON.parse response_body
41
47
 
42
- if success
43
- message = "Issue key = #{response['key']}, #{@config[:url]}/browse/#{response['key']}"
44
- else
45
- message = JSON.pretty_generate response
48
+ if success
49
+ message = "Issue key = #{response['key']}, #{@config[:url]}/browse/#{response['key']}"
50
+ else
51
+ message = JSON.pretty_generate response
52
+ end
53
+
54
+ yield success, message
46
55
  end
47
56
 
48
- yield success, message
49
57
  end
50
58
 
51
- end
59
+ class ApiConsumer
52
60
 
53
- class JiraApi
61
+ def configure!(url, username, password, api_version = 'latest')
62
+ @url = url
63
+ @username = username
64
+ @password = password
65
+ @api_version = api_version
66
+ end
54
67
 
55
- def configure!(url, username, password, api_version = 'latest')
56
- @url = url
57
- @username = username
58
- @password = password
59
- @api_version = api_version
60
- end
68
+ def create_issue(jira_story)
69
+ response = post(url, jira_story.to_json, headers)
70
+ return response.success?, response.body
71
+ end
61
72
 
62
- def create_issue(jira_story)
63
- response = post(url, jira_story.to_json, headers)
64
- return response.success?, response.body
65
- end
73
+ protected
66
74
 
67
- protected
75
+ def headers
76
+ {'Content-Type' => 'application/json', 'Authorization' => auth_header}
77
+ end
68
78
 
69
- def headers
70
- {'Content-Type' => 'application/json', 'Authorization' => auth_header}
71
- end
79
+ def post(issues_url, json_data, headers)
80
+
81
+ conn = Faraday.new do |faraday|
82
+ faraday.headers = headers
83
+ faraday.adapter Faraday.default_adapter
84
+ end
72
85
 
73
- def post(issues_url, json_data, headers)
86
+ conn.post do |req|
87
+ req.url issues_url
88
+ req.body = json_data
89
+ end
74
90
 
75
- conn = Faraday.new do |faraday|
76
- faraday.headers = headers
77
- faraday.adapter Faraday.default_adapter
78
91
  end
79
92
 
80
- conn.post do |req|
81
- req.url issues_url
82
- req.body = json_data
93
+ def url
94
+ "#{@url}/rest/api/#{@api_version}/issue/"
83
95
  end
84
96
 
85
- end
97
+ def auth_header
98
+ login = Base64.encode64 @username + ':' + @password
99
+ "Basic #{login}"
100
+ end
86
101
 
87
- def url
88
- "#{@url}/rest/api/#{@api_version}/issue/"
89
102
  end
90
103
 
91
- def auth_header
92
- login = Base64.encode64 @username + ':' + @password
93
- "Basic #{login}"
94
- end
104
+ class StoryMapper
95
105
 
96
- end
106
+ def initialize
107
+ @map = default_mapping
108
+ end
97
109
 
98
- class JiraStoryMapper
110
+ def configure!(config)
111
+ config[:mappings].each do |mapping|
112
+ field, map = mapping
113
+ @map[field] = map.to_hash
114
+ end
115
+ end
99
116
 
100
- def map(story, project)
101
- hash = {}
102
- hash[:project] = {:key => project}
103
- hash[:summary] = story.name
104
- hash[:description] = story.description
105
- hash[:issuetype] = {:name => story.story_type}
117
+ def map(story)
118
+ fields = story.to_hash.inject({}) do |hash, (key, value)|
119
+ hash.merge!(do_map(key, value))
120
+ hash
121
+ end
122
+ {:fields => fields}
123
+ end
106
124
 
107
- if story.labels
108
- hash[:labels] = story.labels.map {|label| label.gsub(' ', '_')}
125
+ private
126
+
127
+ def default_mapping
128
+ {
129
+ :project => {
130
+ :type => 'ProjectPicker'
131
+ },
132
+ :name => {
133
+ :field_name => :summary,
134
+ :type => 'TextField'
135
+ },
136
+ :description => {
137
+ :type => 'FreeTextField'
138
+ },
139
+ :story_type => {
140
+ :field_name => :issuetype,
141
+ :type => 'SingleSelect'
142
+ },
143
+ :labels => {
144
+ :type => 'Labels'
145
+ },
146
+ :reporter => {
147
+ :type => 'SingleSelect'
148
+ },
149
+ :assignee => {
150
+ :type => 'SingleSelect'
151
+ }
152
+ }
109
153
  end
110
154
 
111
- { :fields => hash }
155
+ def do_map(key, value)
156
+ map = @map[key.to_sym].tap do |m|
157
+ raise "I don't know how to map '#{key}' to request. Please add it to your mappings config." if m.nil?
158
+ end
159
+
160
+ {map.fetch(:field_name, key).to_sym => Inflectors.factory(map.fetch(:type), value).value}
161
+ end
112
162
  end
113
163
  end
data/lib/slurper.rb CHANGED
@@ -2,6 +2,9 @@ require 'yaml'
2
2
  require 'story'
3
3
 
4
4
  class Slurper
5
+
6
+ GLOBAL_STORY_ATTRIBUTES = ['project']
7
+
5
8
  attr_accessor :story_file, :stories, :handlers
6
9
 
7
10
  def self.dump(config_file, filter, handlers, reverse)
@@ -14,7 +17,7 @@ class Slurper
14
17
  def self.slurp(story_file, config_file, handlers, reverse)
15
18
  config = self.load_config(config_file)
16
19
 
17
- stories = self.load_stories(story_file, config)
20
+ stories = self.load_stories(story_file, config.select {|k, _| GLOBAL_STORY_ATTRIBUTES.include?(k)})
18
21
  stories.reverse! unless reverse
19
22
 
20
23
  slurper = new(config, stories)
@@ -63,7 +66,7 @@ class Slurper
63
66
 
64
67
  begin
65
68
  handler.handle(story) do |status, message|
66
- if status then
69
+ if status
67
70
  puts "Success: #{message}"
68
71
  else
69
72
  puts "Failed: #{message}"
data/lib/story.rb CHANGED
@@ -2,47 +2,75 @@
2
2
  # It's only concern is to store and format yaml data from stories
3
3
  class YamlStory
4
4
 
5
- attr_reader :name, :story_type, :requested_by
6
-
7
5
  def initialize(attributes = {}, defaults = {})
8
- # delete empty values (otherwise the default_proc bellow won't be called)
9
- attributes.delete_if { |key, val| val == '' }
10
-
11
- attributes.default_proc = proc do |hash, key|
12
- defaults[key]
13
- end
14
-
15
- @name = attributes['name']
16
- @description = attributes['description']
17
- @story_type = attributes['story_type']
18
- @labels = attributes['labels']
19
- @requested_by = attributes['requested_by']
6
+ # delete empty values
7
+ attributes.delete_if do |key, val|
8
+ val == '' && defaults.key?(key)
9
+ end
10
+ @attributes = defaults.merge(attributes)
20
11
  end
21
12
 
22
13
  def labels
23
- return [] if not @labels
24
- @labels.split(',').map { |label| label.strip }
14
+ (@attributes['labels'] || '').split(',').map(&:strip)
25
15
  end
26
16
 
27
17
  def description
28
- return nil if @description == nil || @description == ''
18
+ description = @attributes['description'] || ''
19
+ if description == ''
20
+ nil
21
+ else
22
+ description.gsub(' ', '').gsub(" \n", "\n")
23
+ end
24
+ end
25
+
26
+ def key?(key)
27
+ @attributes.key?(key.to_s)
28
+ end
29
+
30
+ def [](key)
31
+ raise "Attribute #{key} not defined" unless @attributes.key?(key.to_s)
32
+
33
+ if respond_to?(key.to_sym)
34
+ send(key.to_sym)
35
+ else
36
+ @attributes[key.to_s] == '' ? nil : @attributes[key.to_s]
37
+ end
38
+ end
39
+
40
+ def []=(key, value)
41
+ @attributes[key.to_s] = value
42
+ end
43
+
44
+ def method_missing(symbol)
45
+ self[symbol]
46
+ end
29
47
 
30
- @description.gsub(' ', '').gsub(" \n", "\n")
48
+ def to_hash
49
+ @attributes.keys.inject({}) do |hash, key|
50
+ hash[key] = self[key]
51
+ hash
52
+ end
31
53
  end
32
54
 
33
55
  def yaml_description
34
- return '' if @description == nil || @description == ''
35
- return @description.gsub(/^/, ' ')
56
+ description = @attributes['description']
57
+ if description.nil? || description.empty?
58
+ ''
59
+ else
60
+ description.gsub(/^/, ' ')
61
+ end
36
62
  end
37
63
 
38
64
  def to_yaml
39
- return "==
65
+ <<-yaml
66
+ ==
40
67
  story_type: #{story_type}
41
- name: \"#{name.gsub('"', "'")}\"
68
+ name: "#{name.gsub('"', "'")}"
42
69
  description:
43
70
  #{yaml_description}
44
71
  labels: #{labels.join(', ')}
45
- "
72
+ ==
73
+ yaml
46
74
  end
47
75
 
48
- end
76
+ end
data/spec/jira_spec.rb CHANGED
@@ -3,37 +3,72 @@ require 'slurper'
3
3
  require 'story'
4
4
  require 'jira'
5
5
 
6
- describe JiraStoryMapper do
7
- before do
8
- @story = YamlStory.new('name' => 'Story name', 'story_type' => 'Feature',
9
- 'description' => 'Story desc', 'labels' => 'a, b')
10
- @mapper = JiraStoryMapper.new
11
- end
12
-
13
- it 'should take the project key from config' do
14
- result = @mapper.map(@story, 'AB')
15
- result[:fields][:project][:key].should == 'AB'
6
+ describe Jira::StoryMapper do
7
+ let(:story) do
8
+ YamlStory.new('project' => 'Test', 'name' => 'Story name', 'story_type' => 'Feature',
9
+ 'description' => 'Story desc', 'labels' => 'a, b',
10
+ 'reporter' => 'User', 'assignee' => 'A guy')
16
11
  end
12
+ let(:mapper) { described_class.new }
13
+
14
+ describe '#map' do
15
+ subject { mapper.map(story) }
16
+
17
+ context 'default mappings' do
18
+ it do
19
+ should == {
20
+ :fields => {
21
+ project: {key: 'Test'},
22
+ summary: 'Story name',
23
+ issuetype: {name: 'Feature'},
24
+ description: 'Story desc',
25
+ labels: %w(a b),
26
+ reporter: {name: 'User'},
27
+ assignee: {name: 'A guy'},
28
+ }
29
+ }
30
+ end
31
+ end
17
32
 
18
- it 'should set the issuetype field with the story_type' do
19
- result = @mapper.map(@story, 'AB')
20
- result[:fields][:issuetype][:name].should == 'Feature'
21
- end
33
+ context 'extra mappings' do
34
+ before do
35
+ mapper.configure!(:mappings => {
36
+ test: {:type => 'TextField'},
37
+ something: {:field_name => 'Cici', :type => 'TextField'}
38
+ })
39
+ story.tap do |s|
40
+ s['test'] = 'Hello'
41
+ s['something'] = 'World!'
42
+ end
43
+ end
22
44
 
23
- it 'should split and trim labels' do
24
- result = @mapper.map(@story, 'AB')
25
- result[:fields][:labels].should == ['a', 'b']
45
+ it do
46
+ should == {
47
+ :fields => {
48
+ project: {key: 'Test'},
49
+ summary: 'Story name',
50
+ issuetype: {name: 'Feature'},
51
+ description: 'Story desc',
52
+ labels: %w(a b),
53
+ reporter: {name: 'User'},
54
+ assignee: {name: 'A guy'},
55
+ test: 'Hello',
56
+ Cici: 'World!'
57
+ }
58
+ }
59
+ end
60
+ end
26
61
  end
27
62
  end
28
63
 
29
- describe JiraApi do
64
+ describe Jira::ApiConsumer do
30
65
  it 'should post with correctly configured the url' do
31
66
  good_response = double(success?: true, body: '{"id": 1, "key": "TEST-1", "self":"http://localhost:8090/rest/api/2/issue/1"}')
32
67
 
33
- api = JiraApi.new
68
+ api = described_class.new
34
69
  api.configure!('http://server', 'user', 'pass', '2')
35
70
 
36
- api.should_receive(:post).with('http://server/rest/api/2/issue/', 'json', kind_of(Hash)).and_return good_response
71
+ expect(api).to receive(:post).with('http://server/rest/api/2/issue/', 'json', kind_of(Hash)).and_return good_response
37
72
 
38
73
  api.create_issue(double(to_json: 'json'))
39
74
 
@@ -43,33 +78,32 @@ describe JiraApi do
43
78
  bad_response = double(success?: false, status: 401)
44
79
  issue = double(to_json: 'json')
45
80
 
46
- api = JiraApi.new
81
+ api = described_class.new
47
82
  api.configure!('http://server', 'user', 'pass', '2')
48
- api.should_receive(:post).and_return(bad_response)
49
83
 
84
+ expect(api).to receive(:post).and_return(bad_response)
50
85
  expect { api.create_issue(issue) }.to raise_error Exception
51
-
52
86
  end
53
87
 
54
88
  end
55
89
 
56
- describe Jira do
90
+ describe Jira::Handler do
57
91
 
58
92
  before do
59
- @jira = Jira.new
93
+ @jira = described_class.new
60
94
  end
61
95
 
62
96
  context '#supports' do
63
97
  it 'should support the jira config if tracker is given' do
64
- @jira.supports?('tracker' => 'jira').should == true
98
+ expect(@jira.supports?('tracker' => 'jira')).to be true
65
99
  end
66
100
 
67
101
  it 'should not support if tracker is missing' do
68
- @jira.supports?({}).should == false
102
+ expect(@jira.supports?({})).to be false
69
103
  end
70
104
 
71
105
  it 'should not support if tracker is anything else' do
72
- @jira.supports?('tracker' => 'pivotal').should == false
106
+ expect(@jira.supports?('tracker' => 'pivotal')).to be false
73
107
  end
74
108
  end
75
109
 
@@ -83,23 +117,23 @@ describe Jira do
83
117
  context 'good config provided' do
84
118
  before do
85
119
  required = [:username, :password, :project, :url]
86
- @raw_config = Hash[required.map {|v| [v, 'value']} ]
120
+ @raw_config = Hash[required.map { |v| [v, 'value'] }]
87
121
  end
88
122
 
89
123
  it 'should pass if the required fields are present' do
90
124
  config = @jira.configure! @raw_config
91
- config.username.should == 'value'
125
+ expect(config[:username]).to eq 'value'
92
126
  end
93
127
 
94
128
  it 'should return the default api version' do
95
129
  config = @jira.configure! @raw_config
96
- config[:api_version].should == 'latest'
130
+ expect(config[:api_version]).to eq 'latest'
97
131
  end
98
132
 
99
133
  it 'should overwrite the default api_version' do
100
134
  @raw_config[:api_version] = '2'
101
135
  config = @jira.configure! @raw_config
102
- config[:api_version].should == '2'
136
+ expect(config[:api_version]).to eq '2'
103
137
  end
104
138
  end
105
139
  end
@@ -118,16 +152,16 @@ describe Jira do
118
152
 
119
153
  it 'should create a json issue using the api' do
120
154
  issue = double(to_json: 'json')
121
- mapper = double(map: issue)
155
+ mapper = double(map: issue, configure!: true)
122
156
 
123
157
  api = double()
124
- api.should_receive(:configure!).with('http://host', 'user', 'pass', 'latest')
125
- api.should_receive(:create_issue).with(issue).and_return([true, '{"id": 1, "key": "TEST-1", "self":"http://localhost:8090/rest/api/2/issue/1"}'])
158
+ expect(api).to receive(:configure!).with('http://host', 'user', 'pass', 'latest')
159
+ expect(api).to receive(:create_issue).with(issue).and_return([true, '{"id": 1, "key": "TEST-1", "self":"http://localhost:8090/rest/api/2/issue/1"}'])
126
160
 
127
- handler = Jira.new(mapper, api)
161
+ handler = described_class.new(mapper, api)
128
162
  handler.configure! @config
129
163
  handler.handle @story do |status, response|
130
- expect(status).to be_true
164
+ expect(status).to be true
131
165
  expect(response).to include('TEST-1')
132
166
  end
133
167
  end
data/spec/pivotal_spec.rb CHANGED
@@ -10,48 +10,12 @@ describe Pivotal do
10
10
 
11
11
  describe '#supports?' do
12
12
  it 'should support pivotal if project_id exists' do
13
- @tracker.supports?({'project_id' => 12345}).should be_true
13
+ expect(@tracker.supports?({'project_id' => 12345})).to be true
14
14
  end
15
15
 
16
16
  it 'should support pivotal if specified' do
17
- @tracker.supports?({'tracker' => 'pivotal'}).should be_true
17
+ expect(@tracker.supports?({'tracker' => 'pivotal'})).to be true
18
18
  end
19
19
  end
20
20
 
21
21
  end
22
-
23
- #describe Story do
24
- #
25
- # before do
26
- # fixture = File.join(File.dirname(__FILE__), "fixtures", "slurper_config_pivotal.yml")
27
- # Story.configure(YAML.load_file fixture)
28
- # end
29
- #
30
- # context "#prepare" do
31
- #
32
- # it "uses http by default" do
33
- # Story.scheme.should == "http"
34
- # Story.config['ssl'].should be_nil
35
- # end
36
- #
37
- # it "uses https if set in the config" do
38
- # Story.configure({"ssl" => true})
39
- #
40
- # Story.config['ssl'].should be_true
41
- # Story.scheme.should == "https"
42
- # Story.ssl_options[:verify_mode].should == 1
43
- #
44
- # # Not sure what this next line is testing
45
- # File.open(File.expand_path('lib/cacert.pem')).readlines.find_all{ |l| l.starts_with?("Equifax") }.count.should == 4
46
- # end
47
- #
48
- # it "sets the api token from config" do
49
- # Story.headers.should == {"X-TrackerToken"=>"123abc123abc123abc123abc"}
50
- # end
51
- # end
52
- #
53
- #
54
- #
55
- #
56
- #
57
- #end
data/spec/slurper_spec.rb CHANGED
@@ -4,7 +4,7 @@ require 'slurper'
4
4
  describe Slurper do
5
5
 
6
6
  context "#create_stories" do
7
- let(:stories) { [YamlStory.new(:name => 'A story')] }
7
+ let(:stories) { [YamlStory.new('name' => 'A story')] }
8
8
  let(:config) { {:tracker => 'jira'} }
9
9
  let(:slurper) { Slurper.new(config, stories) }
10
10
  let(:handler) { double('handler') }
@@ -19,37 +19,37 @@ describe Slurper do
19
19
  end
20
20
 
21
21
  it "should detect the handler by checking the config" do
22
- handler.should_receive(:supports?).with(config).and_return(true)
23
- handler.should_receive(:configure!).and_return(true)
24
- handler.should_receive(:handle)
22
+ expect(handler).to receive(:supports?).with(config).and_return(true)
23
+ expect(handler).to receive(:configure!).and_return(true)
24
+ expect(handler).to receive(:handle)
25
25
 
26
26
  slurper.create_stories
27
27
  end
28
28
 
29
29
  it "should call configure on the handler" do
30
- handler.should_receive(:supports?).with(config).and_return(true)
31
- handler.should_receive(:configure!).with(config)
30
+ expect(handler).to receive(:supports?).with(config).and_return(true)
31
+ expect(handler).to receive(:configure!).with(config)
32
32
 
33
33
  slurper.create_stories
34
34
  end
35
35
 
36
36
  it "should delegate to the proper handler" do
37
37
  jira = double
38
- jira.should_receive(:supports?).with(config).and_return(false)
38
+ expect(jira).to receive(:supports?).with(config).and_return(false)
39
39
 
40
40
  pivotal = double
41
- pivotal.should_receive(:supports?).with(config).and_return(true)
42
- pivotal.should_receive(:configure!).and_return(true)
43
- pivotal.should_receive(:handle).with(stories[0])
41
+ expect(pivotal).to receive(:supports?).with(config).and_return(true)
42
+ expect(pivotal).to receive(:configure!).and_return(true)
43
+ expect(pivotal).to receive(:handle).with(stories[0])
44
44
 
45
45
  slurper.handlers = [jira, pivotal]
46
46
  slurper.create_stories
47
47
  end
48
48
 
49
49
  it "should fail gracefully on handler exceptions" do
50
- handler.stub(:supports?).and_return(true)
51
- handler.should_receive(:configure!).and_return(true)
52
- handler.stub(:handle).and_raise('An error')
50
+ allow(handler).to receive(:supports?).and_return(true)
51
+ expect(handler).to receive(:configure!).and_return(true)
52
+ allow(handler).to receive(:handle).and_raise('An error')
53
53
 
54
54
  expect { slurper.create_stories }.not_to raise_error
55
55
  end
@@ -61,9 +61,9 @@ describe Slurper do
61
61
  config_file = File.join(File.dirname(__FILE__), "fixtures", "slurper_config_pivotal.yml")
62
62
 
63
63
  handler = double
64
- handler.stub(:supports?).and_return true
65
- handler.stub(:configure!).and_return true
66
- handler.should_receive(:handle).once()
64
+ allow(handler).to receive(:supports?).and_return true
65
+ allow(handler).to receive(:configure!).and_return true
66
+ expect(handler).to receive(:handle).once
67
67
 
68
68
  Slurper.slurp(story_file, config_file, [handler], false)
69
69
  end
@@ -80,7 +80,7 @@ describe Slurper do
80
80
  let(:config) { {'requested_by' => 'John Doe'} }
81
81
 
82
82
  it 'should load default values' do
83
- story.requested_by.should == 'John Doe'
83
+ expect(story.requested_by).to eq 'John Doe'
84
84
  end
85
85
  end
86
86
 
@@ -88,7 +88,7 @@ describe Slurper do
88
88
  let(:story_file) { 'whitespacey_story.slurper' }
89
89
 
90
90
  it "strips whitespace from the name" do
91
- story.name.should == "Profit"
91
+ expect(story.name).to eq "Profit"
92
92
  end
93
93
  end
94
94
 
@@ -96,15 +96,15 @@ describe Slurper do
96
96
  let(:story_file) { 'full_story.slurper' }
97
97
 
98
98
  it "parses the name correctly" do
99
- story.name.should == 'Profit'
99
+ expect(story.name).to eq 'Profit'
100
100
  end
101
101
 
102
102
  it "parses the label correctly" do
103
- story.labels.should == ['money','power','fame']
103
+ expect(story.labels).to eq ['money','power','fame']
104
104
  end
105
105
 
106
106
  it "parses the story type correctly" do
107
- story.story_type.should == 'feature'
107
+ expect(story.story_type).to eq 'feature'
108
108
  end
109
109
  end
110
110
 
@@ -113,7 +113,7 @@ describe Slurper do
113
113
  let(:story_file) { 'name_only.slurper' }
114
114
 
115
115
  it "should parse the name correctly" do
116
- story.name.should == "Profit"
116
+ expect(story.name).to eq "Profit"
117
117
  end
118
118
 
119
119
  end
@@ -123,11 +123,11 @@ describe Slurper do
123
123
  let(:story_file) { 'empty_attributes.slurper' }
124
124
 
125
125
  it "should not set any name" do
126
- story.name.should be_nil
126
+ expect(story.name).to be_nil
127
127
  end
128
128
 
129
129
  it "should not set any labels" do
130
- story.labels.should == []
130
+ expect(story.labels).to eq []
131
131
  end
132
132
  end
133
133
  end
data/spec/story_spec.rb CHANGED
@@ -6,28 +6,27 @@ describe YamlStory do
6
6
  it "should return a default value if it's set" do
7
7
  story = YamlStory.new({}, 'description' => 'Fill description', 'requested_by' => 'John Doe')
8
8
 
9
- story.description.should == 'Fill description'
10
- story.requested_by.should == 'John Doe'
9
+ expect(story.description).to eq 'Fill description'
10
+ expect(story.requested_by).to eq 'John Doe'
11
11
  end
12
12
  end
13
13
 
14
14
  context "#requested_by" do
15
15
  it "should return nil when value is blank" do
16
16
  story = YamlStory.new('requested_by' => '')
17
- story.requested_by.should be_nil
17
+ expect(story.requested_by).to be_nil
18
18
  end
19
-
20
19
  end
21
20
 
22
21
  context "#description" do
23
22
  it "when the description is blank" do
24
23
  story = YamlStory.new('description' => '', 'name' => 'test')
25
- story.description.should be_nil
24
+ expect(story.description).to be_nil
26
25
  end
27
26
 
28
27
  it "when there is no description given" do
29
28
  story = YamlStory.new
30
- story.description.should be_nil
29
+ expect(story.description).to be_nil
31
30
  end
32
31
 
33
32
  it "when it contains quotes" do
@@ -35,7 +34,7 @@ describe YamlStory do
35
34
  I have a "quote"
36
35
  STRING
37
36
  story = YamlStory.new('description' => desc)
38
- story.description.should == "I have a \"quote\"\n"
37
+ expect(story.description).to eq "I have a \"quote\"\n"
39
38
  end
40
39
 
41
40
  it "when it is full of whitespace" do
@@ -45,7 +44,7 @@ describe YamlStory do
45
44
  I want to click a thingy
46
45
  STRING
47
46
  story = YamlStory.new('description' => desc)
48
- story.description.should == "In order to do something\nAs a role\nI want to click a thingy\n"
47
+ expect(story.description).to eq "In order to do something\nAs a role\nI want to click a thingy\n"
49
48
  end
50
49
 
51
50
  it "when it contains acceptance criteria" do
@@ -59,24 +58,76 @@ describe YamlStory do
59
58
  - don't forget the other thing
60
59
  STRING
61
60
  story = YamlStory.new('description' => desc)
62
- story.description.should == "In order to do something\nAs a role\nI want to click a thingy\n\nAcceptance:\n- do the thing\n- don't forget the other thing\n"
61
+ expect(story.description).to eq "In order to do something\nAs a role\nI want to click a thingy\n\nAcceptance:\n- do the thing\n- don't forget the other thing\n"
63
62
  end
64
63
  end
65
64
 
66
65
  context 'default attributes' do
67
66
  it 'uses the default if not given one' do
68
67
  story = YamlStory.new({}, {'requested_by' => 'Mr. Client'})
69
- story.requested_by.should == 'Mr. Client'
68
+ expect(story.requested_by).to eq 'Mr. Client'
70
69
  end
71
70
 
72
71
  it 'uses the default if given a blank requested_by' do
73
72
  story = YamlStory.new({'requested_by' => ''}, {'requested_by' => 'Mr. Client'})
74
- story.requested_by.should == 'Mr. Client'
73
+ expect(story.requested_by).to eq 'Mr. Client'
75
74
  end
76
75
 
77
76
  it 'uses the name given in the story file if there is one' do
78
77
  story = YamlStory.new({'requested_by' => 'Mr. Stakeholder'}, {'requested_by' => 'Mr. Client'})
79
- story.requested_by.should == 'Mr. Stakeholder'
78
+ expect(story.requested_by).to eq 'Mr. Stakeholder'
79
+ end
80
+ end
81
+
82
+ context '#key?' do
83
+ subject { YamlStory.new({'name' => 'Test', 'description' => 'Desc', 'requested_by' => 'Mr. Client'}).key?(key) }
84
+
85
+ context 'with existing key' do
86
+ let(:key) { 'name' }
87
+ it { should be true }
88
+ end
89
+
90
+ context 'with other key' do
91
+ let(:key) { 'missing' }
92
+ it { should be false }
93
+ end
94
+ end
95
+
96
+ context '#[]' do
97
+ subject { YamlStory.new({'name' => 'Test', 'description' => 'Desc', 'requested_by' => 'Mr. Client'}) }
98
+
99
+ it 'return defined attributes' do
100
+ expect(subject['name']).to eq 'Test'
101
+ expect(subject['description']).to eq 'Desc'
102
+ expect(subject['requested_by']).to eq 'Mr. Client'
103
+ end
104
+
105
+ it 'raise exception for undefined attributes' do
106
+ expect { subject['something'] }.to raise_exception
107
+ end
108
+ end
109
+
110
+ context 'attributes accessed as methods' do
111
+ subject { YamlStory.new({'name' => 'Test', 'description' => 'Desc', 'requested_by' => 'Mr. Client'}) }
112
+
113
+ it 'return defined attributes' do
114
+ expect(subject.name).to eq 'Test'
115
+ expect(subject.description).to eq 'Desc'
116
+ expect(subject.requested_by).to eq 'Mr. Client'
117
+ end
118
+
119
+ it 'raise exception for undefined attributes' do
120
+ expect { subject.somethig }.to raise_exception
121
+ end
122
+ end
123
+
124
+ context '#to_hash' do
125
+ let(:attrs) do
126
+ {'name' => 'Test', 'description' => 'Desc', 'requested_by' => 'Mr. Client'}
80
127
  end
128
+
129
+ subject { YamlStory.new(attrs).to_hash }
130
+
131
+ it { should == attrs }
81
132
  end
82
- end
133
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jira-slurper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wes Gibbs
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2014-07-07 00:00:00.000000000 Z
15
+ date: 2014-08-05 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: faraday
@@ -62,14 +62,28 @@ dependencies:
62
62
  requirements:
63
63
  - - ~>
64
64
  - !ruby/object:Gem::Version
65
- version: 2.14.0
65
+ version: 3.0.0
66
66
  type: :development
67
67
  prerelease: false
68
68
  version_requirements: !ruby/object:Gem::Requirement
69
69
  requirements:
70
70
  - - ~>
71
71
  - !ruby/object:Gem::Version
72
- version: 2.14.0
72
+ version: 3.0.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: rspec-its
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 1.0.1
80
+ type: :development
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ version: 1.0.1
73
87
  description: "\n Slurps stories from the given file (stories.slurper by default)
74
88
  and creates Pivotal Tracker stories from them. Useful during story carding sessions
75
89
  when you want to capture a number of stories quickly without clicking your way through
@@ -85,6 +99,7 @@ files:
85
99
  - bin/slurp
86
100
  - lib/cacert.pem
87
101
  - lib/jira.rb
102
+ - lib/jira/inflectors.rb
88
103
  - lib/pivotal.rb
89
104
  - lib/slurper.rb
90
105
  - lib/story.rb