jira-cards 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/GemFile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "bundler", "~> 1.0.0"
5
+ gem "jeweler", "~> 1.5.2"
6
+ gem "rake"
7
+ gem "rspec"
8
+ gem "ruby-debug", "< 0.10.4"
9
+ end
10
+
11
+ gem 'soap4r'
12
+ gem 'jira4r-jh', "> 0.3.0"
13
+ gem 'pdfkit'
14
+ gem 'clint'
@@ -0,0 +1,39 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ clint (0.2.7)
5
+ columnize (0.3.2)
6
+ git (1.2.5)
7
+ httpclient (2.1.6)
8
+ jeweler (1.5.2)
9
+ bundler (~> 1.0.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ jira4r-jh (0.4.0)
13
+ soap4r
14
+ soap4r
15
+ linecache (0.43)
16
+ pdfkit (0.5.0)
17
+ rake (0.8.7)
18
+ rspec (1.3.0)
19
+ ruby-debug (0.10.3)
20
+ columnize (>= 0.1)
21
+ ruby-debug-base (~> 0.10.3.0)
22
+ ruby-debug-base (0.10.3)
23
+ linecache (>= 0.3)
24
+ soap4r (1.5.8)
25
+ httpclient (>= 2.1.1)
26
+
27
+ PLATFORMS
28
+ x86-mingw32
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.0.0)
32
+ clint
33
+ jeweler (~> 1.5.2)
34
+ jira4r-jh (> 0.3.0)
35
+ pdfkit
36
+ rake
37
+ rspec
38
+ ruby-debug (< 0.10.4)
39
+ soap4r
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 James Hollingworth
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ h1. jira-cards
2
+
3
+ A command line tool for generating scrum cards from jira. It allows you to customise the styling of the scrum cards using an erb template.
4
+
5
+ h1. Installation
6
+
7
+ You are going to need to
8
+
9
+ * install the jira-cards gem <pre>gem install jira-cards</pre>
10
+ * "Enable the jira RPC-Plugin":http://confluence.atlassian.com/display/JIRA/JIRA+XML-RPC+Overview
11
+ * Install "wkhtmltopdf":http://code.google.com/p/wkhtmltopdf/
12
+
13
+ h1. Configuration
14
+
15
+ When you first run the tool it will ask you:
16
+
17
+ * The url to your jira instance e.g. jira.atlassian.com
18
+ * A username & password for accessing jira. Whatever user you use must be an admin
19
+ * Path to the wkhtmltopdf e.g. C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe
20
+ * If you want to use the default templates
21
+ ** If no, the path to the template erb & css file
22
+
23
+ These values are then written to a config.yml file. If you run the command it will tell you where the config file is if you want to change it.
24
+
25
+
26
+ h1. Commands
27
+
28
+ The two commands are issues and iteration. For both commands you need to specify the output flag which is the path where you want the output pdf file to go to.
29
+
30
+ h2. Issues
31
+
32
+ You want this command if you want to print specific issues. The only flag is --keys which are the key's of the issues you want. e.g.
33
+
34
+ <pre>
35
+ jira-cards issues --keys "foo-1, foo-4, foo-10" --output c:\temp\test.pdf
36
+ </pre>
37
+
38
+ You can also specify ranges (a la "ruby ranges":http://www.ruby-doc.org/core/classes/Range.html) of keys e.g.
39
+
40
+ <pre>
41
+ jira-cards issues --keys foo-4..foo-10 --output c:\temp\test.pdf
42
+ </pre>
43
+
44
+ h2. Iteration
45
+
46
+ You want this if you want to get all of the cards for an iteration (or version in jira-speak). The flags are:
47
+
48
+ * --project
49
+ * --version
50
+
51
+ <pre>
52
+ jira-cards iteration --project "Some project" --version "Iteration 30" --output c:\temp\test.pdf
53
+ </pre>
54
+
55
+ h1. Templates
56
+
57
+ The scrum cards are generated from an "erb":http://ruby-doc.org/stdlib/libdoc/erb/rdoc/ html template which is then converted to pdf using "pdfkit":https://github.com/jdpace/PDFKit. If you want to use your own template you can specify the path to your erb template & css file in the initial config.
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+ require 'spec/rake/spectask'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "jira-cards"
17
+ gem.homepage = "http://github.com/jhollingworth/jira-cards"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{A command line tool for generating scrum cards from jira}
20
+ gem.description = %Q{You can get individual issues or all issues for an iteration. see --h for more info}
21
+ gem.email = "jamiehollingworth@gmail.com"
22
+ gem.authors = ["James Hollingworth"]
23
+ end
24
+ Jeweler::RubygemsDotOrgTasks.new
25
+
26
+
27
+ namespace :specs do
28
+
29
+ desc "Run all specs"
30
+ Spec::Rake::SpecTask.new('all') do |t|
31
+ t.spec_files = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'rubygems'
5
+ require 'application'
@@ -0,0 +1,45 @@
1
+ require 'commands'
2
+ require 'clint'
3
+
4
+ c = Clint.new
5
+
6
+ c.usage do
7
+ $stderr.puts "\nA command line tool for generating jira scrum cards (for more help [--h|--help])\n\n"
8
+ end
9
+
10
+ c.help do
11
+ $stderr.puts "config: shows the current jira configuration\n\n\t" +
12
+ "[--r|--reset] if this flag is specified, it will reset the configuration\n\n"
13
+
14
+ $stderr.puts "issues: print cards for specific issues\n\n\t" +
15
+ "[--k|--keys] issue keys. they can be csv, e.g. \"foo-1, foo-2\" or a range foo-2..foo-10\n\t" +
16
+ "[--o|--output] output path for pdf e.g. c:\\foo\\temp.pdf\n\n"
17
+
18
+ $stderr.puts "iteration: print cards for an iteration\n\n\t" +
19
+ "[--v|--version] the version you want to print e.g. \"Iteration 30\"\n\t" +
20
+ "[--p|--project] the project e.g. \"Some project\"\n\t" +
21
+ "[--o|--output] output path for pdf e.g. c:\\foo\\temp.pdf\n\n"
22
+ end
23
+
24
+ c.options :help => false, :h => :help
25
+ c.parse ARGV
26
+
27
+ if c.options[:help]
28
+ c.help
29
+ exit 1
30
+ end
31
+
32
+ c.subcommand Commands do |subcommand|
33
+ if subcommand == :config
34
+ c.options :reset => true, :r => :reset
35
+ end
36
+
37
+ if subcommand == :issues
38
+ c.options :keys => String, :k => :keys, :output => String, :o => :output
39
+ end
40
+
41
+ if subcommand == :iteration
42
+ c.options :project => String, :p => :project, :version => String, :v => :version, :output => String, :o => :output
43
+ end
44
+ c.parse
45
+ end
@@ -0,0 +1,64 @@
1
+ require 'jql_generator'
2
+ require 'jira'
3
+ require 'html_renderer'
4
+ require 'pdf_renderer'
5
+ require 'configuration'
6
+
7
+ class Commands
8
+
9
+ class << self
10
+
11
+ def config(options = {})
12
+ if options[:reset] == false
13
+ Configuration.reset!
14
+ else
15
+ Configuration.print
16
+ end
17
+ end
18
+
19
+ def issues(options = {})
20
+
21
+ validate_flags(options, :keys, :output)
22
+ print_issues("issues", options)
23
+ end
24
+
25
+ def iteration(options = {})
26
+ validate_flags(options, :project, :version, :output)
27
+
28
+ print_issues("iteration", options)
29
+ end
30
+
31
+ private
32
+
33
+ def validate_flags(options, *flags)
34
+ error = false
35
+ flags.each do |flag|
36
+ if(options[flag].nil? || options[flag] == "")
37
+ $stderr.puts "--#{flag.to_s} is required"
38
+ error = true
39
+ end
40
+ end
41
+
42
+ exit 1 if error
43
+ end
44
+
45
+ def print_issues(command, options)
46
+ config = Configuration.new
47
+ jira = Jira.new(config)
48
+ jql = JqlGenerator.generate(command, options)
49
+
50
+ html_renderer = HtmlRenderer.new(config)
51
+ pdf_renderer = PdfRenderer.new(config)
52
+ issues = jira.query(jql)
53
+
54
+ if issues.length == 0
55
+ puts "No issues found"
56
+ return
57
+ end
58
+
59
+ html = html_renderer.render(issues)
60
+ pdf_renderer.render(html, options[:output])
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,165 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require File.dirname(__FILE__) + '/meta'
5
+
6
+ class Configuration
7
+ include Meta
8
+ def initialize()
9
+ create_config if false == File.exists?(Configuration.config_path)
10
+
11
+ YAML.load_file(Configuration.config_path).each do |key, value|
12
+ create_method(key) {
13
+ value
14
+ }
15
+ end
16
+ end
17
+
18
+ class << self
19
+
20
+ def reset!()
21
+ File.delete(config_path) if File.exists?(config_path)
22
+ Configuration.new
23
+ end
24
+
25
+ def config_path()
26
+ File.dirname(__FILE__) + '/config.yml'
27
+ end
28
+
29
+ def print()
30
+ if !File.exists?(config_path)
31
+ puts "jira-cards has not been configured yet"
32
+ exit 1
33
+ end
34
+ YAML.load_file(Configuration.config_path).each do |key, value|
35
+ puts "#{key}: #{value}"
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def create_config
43
+ puts "Hi, we need to configure jira-cards. Can you answer some questions please?"
44
+
45
+ config = {
46
+ "jira_url" => jira_url,
47
+ "username" => username,
48
+ "password" => password,
49
+ "wkhtmltopdf_path" => wkhtmltopdf_path,
50
+ "template_erb" => template_erb,
51
+ "template_stylesheet" => template_stylesheet
52
+ }
53
+
54
+ File.open(Configuration.config_path, "w") do |f|
55
+ f.write(config.to_yaml)
56
+ end
57
+ end
58
+
59
+ def yaml(hash)
60
+ method = hash.respond_to?(:ya2yaml) ? :ya2yaml : :to_yaml
61
+ string = hash.deep_stringify_keys.send(method)
62
+ string.gsub("!ruby/symbol ", ":").sub("---","").split("\n").map(&:rstrip).join("\n").strip
63
+ end
64
+
65
+ def jira_url()
66
+ url = ask "What's the url to your jira instance? e.g. jira.atlassian.com"
67
+ invalid = true
68
+ while invalid
69
+ wsdl_url = "#{url}/rpc/soap/jirasoapservice-v2"
70
+ if url.match(/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix).nil?
71
+ url = ask "The url #{url} is invalid. Please try again. e.g. jira.atlassian.com"
72
+ else
73
+ case Net::HTTP.get_response(url, '/rpc/soap/jirasoapservice-v2')
74
+ when Net::HTTPSuccess then
75
+ invalid = false
76
+ when Net::HTTPServiceUnavailable then
77
+ url = ask "The jira api is disabled. Please enable it, see http://confluence.atlassian.com/display/JIRA/JIRA+XML-RPC+Overview"
78
+ when Net::HTTPNotFound then
79
+ url = ask "Could not find #{wsdl_url}. Please check the url is correct"
80
+ else
81
+ url = ask "There was a problem getting the jira wsdl (#{wsdl_url}). Please check the url is correct"
82
+ end
83
+ end
84
+ end
85
+ url
86
+ end
87
+
88
+ def username()
89
+ get_non_empty_value("What is your username for jira (must be an admin)", "username")
90
+ end
91
+
92
+ def get_non_empty_value(question, type)
93
+ value = ask question
94
+ invalid = true
95
+ while invalid
96
+ if value == ""
97
+ value = ask "Please specify the #{type}"
98
+ else
99
+ invalid = false
100
+ end
101
+ end
102
+ value
103
+ end
104
+
105
+ def password()
106
+ get_non_empty_value("What is the password?", "password")
107
+ end
108
+
109
+ def wkhtmltopdf_path()
110
+ get_file_path(
111
+ "What is the path to wkhtmltopdf? e.g. C:\\\\Program Files\\\\wkhtmltopdf\\\\wkhtmltopdf.exe. " +
112
+ "If you don't have it installed, please install from http://code.google.com/p/wkhtmltopdf/",
113
+ "wkhtmltopdf path"
114
+ )
115
+ end
116
+
117
+ def get_file_path(question, type)
118
+ path = get_non_empty_value(question, type)
119
+
120
+ invalid = true
121
+ while invalid
122
+ if !File.exists?(path)
123
+ path = ask("The file #{path} does not exist")
124
+ else
125
+ invalid = false
126
+ end
127
+ end
128
+
129
+ path
130
+ end
131
+
132
+ def template_erb()
133
+ invalid = true
134
+ while(invalid)
135
+ @use_default = (ask "Do you want to use the default template? (y/n)").downcase
136
+ if @use_default == "y" || @use_default == "n"
137
+ invalid = false
138
+ @use_default = @use_default == "y"
139
+ end
140
+ end
141
+
142
+ if @use_default
143
+ clean_path("default_template.erb")
144
+ else
145
+ get_file_path("What is the path to the template? e.g. c:\\\\foo\\\\template.erb", "template path")
146
+ end
147
+ end
148
+
149
+ def template_stylesheet()
150
+ if @use_default
151
+ clean_path("default_template.css")
152
+ else
153
+ get_file_path("What is the path to the template stylesheet? e.g. c:\\\\foo\\\\template.css", "template stylesheet")
154
+ end
155
+ end
156
+
157
+ def clean_path(path)
158
+ File.expand_path(File.dirname(__FILE__) + "/" + path).gsub('/', '\\\\\\')
159
+ end
160
+
161
+ def ask(question)
162
+ puts question
163
+ STDIN.gets.strip
164
+ end
165
+ end
@@ -0,0 +1,51 @@
1
+ html
2
+ {
3
+ font-family:georgia;
4
+ }
5
+
6
+ td
7
+ {
8
+ width:50%;
9
+ height: 453px;
10
+ border-bottom:1px dashed #CCC;
11
+ padding:20px;
12
+ text-align: top;
13
+ }
14
+
15
+ h1
16
+ {
17
+ font-size:33px;
18
+ margin:0;
19
+ border:0;
20
+ }
21
+
22
+ .type
23
+ {
24
+ font-size:25px;
25
+ }
26
+
27
+ td.left
28
+ {
29
+ border-right:1px dashed #CCC;
30
+ }
31
+
32
+ .third-row td
33
+ {
34
+ border-bottom:none;
35
+ }
36
+
37
+ dl
38
+ {
39
+ font-size:20px;
40
+ }
41
+
42
+ dt
43
+ {
44
+ float:left;
45
+ color:#9E999B;
46
+ }
47
+
48
+ dd
49
+ {
50
+ padding-left:130px;
51
+ }
@@ -0,0 +1,44 @@
1
+ <html>
2
+ <body>
3
+ <table width="100%" height="100%" cellspacing="0" cellpadding="0">
4
+ <% row = 0 %>
5
+
6
+ <tr class="row-<%= row %>">
7
+
8
+ <% @issues.each do |i| %>
9
+
10
+ <td valign="top" class="<%= (@issues.index(i) % 2 == 0) ? "left" : "right" %>">
11
+
12
+ <h1><%= i.key %>: <%= i.summary %></h1>
13
+
14
+ <div class="type <%= i.issue_type.downcase %>"><%= i.issue_type %></div>
15
+
16
+ <dl>
17
+ <dt>Points</dt><dd><%= i.points %></dd>
18
+ <dt>Priority</dt><dd><%= i.priority %></dd>
19
+ <dt>Story Owner</dt><dd><%= i.assignee.capitalize %></dd>
20
+ </dl>
21
+ </td>
22
+
23
+ <% if @issues.index(i) % 2 != 0 %>
24
+
25
+ <% row +=1 %>
26
+
27
+ </tr>
28
+
29
+ <% if row % 3 == 0 %>
30
+
31
+ </table>
32
+
33
+ <table style="margin-top:16px;" width="100%" height="100%" cellspacing="0" cellpadding="0">
34
+
35
+ <% end %>
36
+
37
+ <tr class="row-<%= row %> <%= ((row + 1) % 3 == 0)? "third-row" : "" %>">
38
+
39
+ <% end %>
40
+
41
+ <% end %>
42
+ </table>
43
+ </body>
44
+ </html>
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,26 @@
1
+ require 'erb'
2
+
3
+ class HtmlRenderer
4
+
5
+ def initialize(config)
6
+ @template = config.template_erb
7
+ end
8
+
9
+ def render(issues)
10
+ rhtml = ERB.new(IO.read(@template))
11
+ model = Model.new(issues)
12
+ rhtml.result(model.get_binding)
13
+ end
14
+
15
+ private
16
+
17
+ class Model
18
+ def initialize(issues)
19
+ @issues = issues
20
+ end
21
+
22
+ def get_binding()
23
+ binding
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/meta'
2
+
3
+ class Issue
4
+ include Meta
5
+ attr_reader :issue_type
6
+
7
+ def initialize(issue, custom_fields, types)
8
+ @issue = issue
9
+ @issue_type = get_type(types)
10
+ custom_fields.each do |id, name|
11
+ create_method(name) {
12
+ custom_field(id)
13
+ }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def method_missing(m, *args, &block)
20
+ @issue.send m
21
+ end
22
+
23
+ def get_type(types)
24
+ types.each do |id, name|
25
+ return name if @issue.type == id
26
+ end
27
+ nil
28
+ end
29
+
30
+ def custom_field(id)
31
+ @issue.customFieldValues.each do |c|
32
+ return c.values if c.customfieldId == id
33
+ end
34
+ nil
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ require 'jira4r/jira4r'
2
+ require File.dirname(__FILE__) + '/issue'
3
+ gem 'soap4r'
4
+
5
+ class Jira
6
+ def initialize(config)
7
+ @jira = Jira4R::JiraTool.new(2, "http://#{config.jira_url}")
8
+ @jira.login(config.username, config.password)
9
+
10
+ @types = {}
11
+ @custom_fields = {}
12
+
13
+ @jira.getIssueTypes().each {|t| @types[t.id] = t.name }
14
+ @jira.getCustomFields().each { |f| @custom_fields[f.id] = f.name.downcase.gsub(/ /, '_') }
15
+ end
16
+
17
+ def query(jql)
18
+ @jira.getIssuesFromJqlSearch(jql, 10000).collect do |issue|
19
+ issue = Issue.new(issue, @custom_fields, @types)
20
+ puts "Found #{issue.key} (#{issue.issue_type})"
21
+ issue
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ Dir[File.dirname(__FILE__) + '/jql_generators/**/*_generator.rb'].each do |generator|
2
+ require generator
3
+ end
4
+
5
+ class JqlGenerator
6
+ def self.generate(command, options)
7
+ Kernel.const_get("#{command.capitalize}JqlGenerator").new.generate(options)
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ class IssuesJqlGenerator
2
+ def generate(options)
3
+
4
+ keys = options[:keys].split(',').collect { |k| k.strip }.collect do |key|
5
+ #if it's a specific key
6
+ if !key.match(/(.*)\-(\d+)\.\.(.*)\-(\d+)/).nil?
7
+ raise "The beginning and end ranges (#{$1}, #{$2}) do not match" if $1 != $3
8
+ ($2.to_i..$4.to_i).collect { |id| "#{$1}-#{id}"}
9
+ elsif !key.match(/(.*\-\d+)/).nil?
10
+ $1
11
+ else
12
+ "Unknown key #{key}"
13
+ end
14
+ end
15
+
16
+ keys.flatten.collect { |key| "key = \"#{key}\"" } * " or "
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ class IterationJqlGenerator
2
+ def generate(options)
3
+ "project = \"#{options[:project]}\" AND fixVersion = \"#{options[:version]}\""
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Meta
2
+ def create_method( name, &block )
3
+ self.class.send( :define_method, name.to_sym, &block )
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'pdfkit'
2
+
3
+ class PdfRenderer
4
+
5
+ def initialize(config)
6
+ @stylesheet = config.template_stylesheet
7
+ @config = config
8
+ end
9
+
10
+ def render(html, output_file_path)
11
+ PDFKit.configure do |c|
12
+ c.wkhtmltopdf = @config.wkhtmltopdf_path
13
+ end
14
+
15
+ kit = PDFKit.new(html, :page_size => 'Letter', :margin_left => 0, :margin_right => 0, :margin_bottom => 0, :margin_top => 0)
16
+ kit.stylesheets << @stylesheet
17
+ kit.to_file output_file_path
18
+
19
+ puts "Printed scrum cards to #{output_file_path}"
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ require 'html_renderer'
2
+ require 'mock'
3
+
4
+ describe "When I render the issues" do
5
+ before(:all) do
6
+ @issues = [
7
+ Mock.new({
8
+ :issue_type => "Story", :key => "PY-1"
9
+ })
10
+ ]
11
+ @renderer = HtmlRenderer.new(Mock.new({
12
+ :template_erb => File.dirname(__FILE__) + '/test.erb'
13
+ }))
14
+ end
15
+
16
+ before(:each) do
17
+ @html = @renderer.render(@issues)
18
+ end
19
+
20
+ it "should render the issues using the default template" do
21
+ @html.should == "Type:Story\nKey:PY-1"
22
+ end
23
+
24
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'issues_jql_generator'
3
+
4
+ describe "Generating issue jql" do
5
+
6
+ before(:all) do
7
+ @generator = IssuesJqlGenerator.new
8
+ end
9
+
10
+ before(:each) do
11
+ @jql = @generator.generate({:keys => @keys})
12
+ end
13
+
14
+ describe "When the issue keys are separated by commas" do
15
+ before(:all) do
16
+ @keys = "py-1, py-3"
17
+ end
18
+
19
+ it "should be able to split the keys" do
20
+ @jql.should == "key = \"py-1\" or key = \"py-3\""
21
+ end
22
+ end
23
+
24
+ describe "When the issue keys are a range" do
25
+ before(:all) do
26
+ @keys = "py-40..py-42"
27
+ end
28
+
29
+ it "should be able to work out the range" do
30
+ @jql.should == "key = \"py-40\" or key = \"py-41\" or key = \"py-42\""
31
+ end
32
+ end
33
+
34
+ describe "When the issues keys are ranges separated by commas" do
35
+ before(:all) do
36
+ @keys = "py-1, py-4..py-6, py-14"
37
+ end
38
+
39
+ it "should be able to split the keys and work out the range" do
40
+ @jql.should == "key = \"py-1\" or key = \"py-4\" or key = \"py-5\" or key = \"py-6\" or key = \"py-14\""
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ class Mock
2
+ def initialize(properties)
3
+ @properties = properties
4
+ end
5
+
6
+ def method_missing(m, *args, &block)
7
+ @properties[m]
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'pdf_renderer'
3
+
4
+ describe "When I render some html to a pdf" do
5
+ before(:all) do
6
+ @html = "<html><body><h1>Hello world</h1></body></html>"
7
+ @renderer = PdfRenderer.new(Mock.new({
8
+ :template_stylesheet => File.dirname(__FILE__) + '/test.css',
9
+ :wkhtmltopdf_path => "C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe"
10
+ }))
11
+
12
+ @output_path = File.dirname(__FILE__) + '/test.pdf'
13
+ end
14
+
15
+ before(:each) do
16
+ delete_file(@output_path)
17
+ @renderer.render(@html, @output_path)
18
+ end
19
+
20
+ it "should create the pdf" do
21
+ File.exists?(@output_path).should == true
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ SPEC_ROOT = File.dirname(__FILE__)
2
+
3
+ $:.unshift(SPEC_ROOT)
4
+ $:.unshift(File.join(SPEC_ROOT, '..', 'lib'))
5
+ $:.unshift(File.join(SPEC_ROOT, '..', 'lib', 'jql_generators'))
6
+
7
+ require 'rubygems'
8
+ require 'ftools'
9
+
10
+ def delete_file(path)
11
+ (0..20).each do
12
+ begin
13
+ File.delete(path) if File.exists?(path)
14
+ return
15
+ rescue
16
+ end
17
+ end
18
+
19
+ puts "Failed to delete #{path}"
20
+ end
@@ -0,0 +1,4 @@
1
+ h1
2
+ {
3
+ color: red;
4
+ }
@@ -0,0 +1,2 @@
1
+ Type:<%= @issues[0].issue_type %>
2
+ Key:<%= @issues[0].key %>
metadata ADDED
@@ -0,0 +1,232 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jira-cards
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - James Hollingworth
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-02 00:00:00 +00:00
19
+ default_executable: jira-cards
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ prerelease: false
33
+ name: soap4r
34
+ requirement: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ type: :runtime
37
+ version_requirements: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">"
41
+ - !ruby/object:Gem::Version
42
+ hash: 19
43
+ segments:
44
+ - 0
45
+ - 3
46
+ - 0
47
+ version: 0.3.0
48
+ prerelease: false
49
+ name: jira4r-jh
50
+ requirement: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ type: :runtime
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ prerelease: false
63
+ name: pdfkit
64
+ requirement: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ type: :runtime
67
+ version_requirements: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ prerelease: false
77
+ name: clint
78
+ requirement: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ type: :development
81
+ version_requirements: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ hash: 23
87
+ segments:
88
+ - 1
89
+ - 0
90
+ - 0
91
+ version: 1.0.0
92
+ prerelease: false
93
+ name: bundler
94
+ requirement: *id005
95
+ - !ruby/object:Gem::Dependency
96
+ type: :development
97
+ version_requirements: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ hash: 7
103
+ segments:
104
+ - 1
105
+ - 5
106
+ - 2
107
+ version: 1.5.2
108
+ prerelease: false
109
+ name: jeweler
110
+ requirement: *id006
111
+ - !ruby/object:Gem::Dependency
112
+ type: :development
113
+ version_requirements: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ prerelease: false
123
+ name: rake
124
+ requirement: *id007
125
+ - !ruby/object:Gem::Dependency
126
+ type: :development
127
+ version_requirements: &id008 !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ hash: 3
133
+ segments:
134
+ - 0
135
+ version: "0"
136
+ prerelease: false
137
+ name: rspec
138
+ requirement: *id008
139
+ - !ruby/object:Gem::Dependency
140
+ type: :development
141
+ version_requirements: &id009 !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - <
145
+ - !ruby/object:Gem::Version
146
+ hash: 63
147
+ segments:
148
+ - 0
149
+ - 10
150
+ - 4
151
+ version: 0.10.4
152
+ prerelease: false
153
+ name: ruby-debug
154
+ requirement: *id009
155
+ description: You can get individual issues or all issues for an iteration. see --h for more info
156
+ email: jamiehollingworth@gmail.com
157
+ executables:
158
+ - jira-cards
159
+ extensions: []
160
+
161
+ extra_rdoc_files:
162
+ - LICENSE.txt
163
+ - README.textile
164
+ files:
165
+ - GemFile
166
+ - Gemfile.lock
167
+ - LICENSE.txt
168
+ - README.textile
169
+ - RakeFile
170
+ - VERSION
171
+ - bin/jira-cards
172
+ - lib/application.rb
173
+ - lib/commands.rb
174
+ - lib/configuration.rb
175
+ - lib/default_template.css
176
+ - lib/default_template.erb
177
+ - lib/flags.rb
178
+ - lib/html_renderer.rb
179
+ - lib/issue.rb
180
+ - lib/jira.rb
181
+ - lib/jql_generator.rb
182
+ - lib/jql_generators/issues_jql_generator.rb
183
+ - lib/jql_generators/iteration_jql_generator.rb
184
+ - lib/meta.rb
185
+ - lib/pdf_renderer.rb
186
+ - spec/html_renderer_spec.rb
187
+ - spec/issue_jql_generator_spec.rb
188
+ - spec/mock.rb
189
+ - spec/pdf_renderer_spec.rb
190
+ - spec/spec_helper.rb
191
+ - spec/test.css
192
+ - spec/test.erb
193
+ has_rdoc: true
194
+ homepage: http://github.com/jhollingworth/jira-cards
195
+ licenses:
196
+ - MIT
197
+ post_install_message:
198
+ rdoc_options: []
199
+
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ none: false
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ hash: 3
208
+ segments:
209
+ - 0
210
+ version: "0"
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ none: false
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ hash: 3
217
+ segments:
218
+ - 0
219
+ version: "0"
220
+ requirements: []
221
+
222
+ rubyforge_project:
223
+ rubygems_version: 1.3.7
224
+ signing_key:
225
+ specification_version: 3
226
+ summary: A command line tool for generating scrum cards from jira
227
+ test_files:
228
+ - spec/html_renderer_spec.rb
229
+ - spec/issue_jql_generator_spec.rb
230
+ - spec/mock.rb
231
+ - spec/pdf_renderer_spec.rb
232
+ - spec/spec_helper.rb