redmine_cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/lib/assets/messages/en.yml +5 -2
- data/lib/assets/templates/en/id_and_name_list.erb +16 -0
- data/lib/assets/templates/en/issue/show.erb +8 -0
- data/lib/redmine_cli/exceptions.rb +1 -0
- data/lib/redmine_cli/helpers/input.rb +2 -27
- data/lib/redmine_cli/helpers/issue/update.rb +3 -6
- data/lib/redmine_cli/helpers/monkey_patching.rb +6 -0
- data/lib/redmine_cli/input_parser.rb +71 -0
- data/lib/redmine_cli/subcommands/issue.rb +5 -2
- data/lib/redmine_cli/subcommands/project.rb +36 -0
- data/lib/redmine_cli/version.rb +1 -1
- data/lib/redmine_cli.rb +4 -0
- data/redmine_cli.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32498eec5c1276546c816822dde138b72a826d22
|
4
|
+
data.tar.gz: 50336d81f2a26124ea63d2b2ed48d7fcdd0bb38d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 248f44ffd06f6d859a850d2026c94ab4f9bb2b431679e3dbc3c6452f9b7de2a9c08946d33d0bf8cde8e1802c6618840e63d27fe9e009061db94d8b5c3e3f3079
|
7
|
+
data.tar.gz: ae9920568649936d6eeaa43cc448cd61b23f7d1c05064a7867191b60cf124001a4e1c8d1f290c9a345bc84c80c2f9c9b362cb4cffa89871287c205b58520cae2
|
data/README.md
CHANGED
@@ -22,6 +22,19 @@ Why? 'cause web-browser + mouse sucks
|
|
22
22
|
redmine user find 123
|
23
23
|
redmine user find pupkin@yet.another.mail.com
|
24
24
|
|
25
|
+
redmine project list
|
26
|
+
redmine project members 1
|
27
|
+
redmine project members trololo
|
28
|
+
|
29
|
+
Don't forget about console aliases!
|
30
|
+
I use something like this:
|
31
|
+
|
32
|
+
# alias i='redmine issue'
|
33
|
+
# by the way, Thor can guess your commands so:
|
34
|
+
|
35
|
+
$ i s 123
|
36
|
+
$ i u --assign 'vasya pupkin'
|
37
|
+
|
25
38
|
|
26
39
|
## Contributing
|
27
40
|
|
data/lib/assets/messages/en.yml
CHANGED
@@ -4,6 +4,7 @@ en:
|
|
4
4
|
issue: 'Works with issues. See "redmine issue help"'
|
5
5
|
conf: 'Works with config file. See "redmine conf help"'
|
6
6
|
user: 'Works with users. See "redmine user help"'
|
7
|
+
project: 'Works with projects. See "redmine project help"'
|
7
8
|
issue:
|
8
9
|
list: 'Lists issues of user. By default it is you'
|
9
10
|
show: 'More info about issue'
|
@@ -14,7 +15,7 @@ en:
|
|
14
15
|
limit: 'Amount of comments (last journals).'
|
15
16
|
update:
|
16
17
|
done: 'Readiness percentage'
|
17
|
-
assign: 'ID of user issue will be assigned to'
|
18
|
+
assign: 'ID of name part of user issue will be assigned to'
|
18
19
|
time: 'Add time entry. Formats: HH:MM; M (minutes); H.h (hours in float)'
|
19
20
|
status: 'Change status of issue. Search by status name substring'
|
20
21
|
comment: 'Leave comment. It will open your text editor.'
|
@@ -23,7 +24,9 @@ en:
|
|
23
24
|
init: 'Asks you few question to make your config file useful'
|
24
25
|
user:
|
25
26
|
find: 'Finds users by id|email|name. Without arguments it will show your info'
|
26
|
-
|
27
|
+
project:
|
28
|
+
list: 'Just lists project name with IDs'
|
29
|
+
members: 'Lists members of given project'
|
27
30
|
commands:
|
28
31
|
conf:
|
29
32
|
init:
|
@@ -0,0 +1,16 @@
|
|
1
|
+
% if list.size.zero?
|
2
|
+
Empty list
|
3
|
+
% end
|
4
|
+
%
|
5
|
+
%
|
6
|
+
% head_id = '(id)'
|
7
|
+
% max_id_len = list.map(&:id).map(&:size).max || 0
|
8
|
+
% max_id_len = [max_id_len, head_id.size].max
|
9
|
+
%
|
10
|
+
% head_name = '(name)'
|
11
|
+
%
|
12
|
+
<%= head_id.yellow %> | <%= head_name.yellow %>
|
13
|
+
|
14
|
+
% list.each do |object|
|
15
|
+
<%= object.id.rjust(max_id_len) %> - <%= object.name %><%= "\n" %>
|
16
|
+
% end
|
@@ -32,6 +32,14 @@
|
|
32
32
|
* <%= issue_id == issue.id ? 'this' : issue_id %> <%= relation_type %> <%= issue_to_id == issue.id ? 'this' : issue_to_id %>
|
33
33
|
% end
|
34
34
|
|
35
|
+
% end
|
36
|
+
%
|
37
|
+
% if issue.attachments?
|
38
|
+
<%= 'Attachments:'.yellow %>
|
39
|
+
|
40
|
+
% issue.attachments.each do |a|
|
41
|
+
<%= '*'.yellow %> <%= a.filename %> - <%= a.content_url %>
|
42
|
+
% end
|
35
43
|
% end
|
36
44
|
%
|
37
45
|
% if issue.description? && !issue.description.empty?
|
@@ -12,16 +12,10 @@ module RedmineCLI
|
|
12
12
|
include Helpers::Output
|
13
13
|
|
14
14
|
def ask_for_user(message, params = {})
|
15
|
-
params[:limited_to] =
|
16
|
-
begin
|
17
|
-
@ask_for_user_cache = RedmineRest::Models::User.find(input) # save record
|
18
|
-
rescue
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
end
|
15
|
+
params[:limited_to] = ->(input) { @cached_user = RedmineRest::Models::User.find_by_id(input) }
|
22
16
|
|
23
17
|
ask(message, params)
|
24
|
-
@
|
18
|
+
@cached_user
|
25
19
|
end
|
26
20
|
|
27
21
|
def ask_from_text_editor(welcome_message = '')
|
@@ -58,25 +52,6 @@ module RedmineCLI
|
|
58
52
|
object_list[input.to_i]
|
59
53
|
end
|
60
54
|
|
61
|
-
#
|
62
|
-
# Parses time from user's input.
|
63
|
-
# Formats: HH:MM; M; H.h
|
64
|
-
#
|
65
|
-
# @param input [String]
|
66
|
-
#
|
67
|
-
def parse_time(input)
|
68
|
-
fail(BadInputTime) unless input =~ /^\d+[\:\.]?\d*/
|
69
|
-
|
70
|
-
if input.include?(':')
|
71
|
-
h, m = input.split(':').map(&:to_i)
|
72
|
-
(60 * h + m) / 60.0
|
73
|
-
elsif input.include?('.')
|
74
|
-
input.to_f
|
75
|
-
else
|
76
|
-
input.to_i
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
55
|
#
|
81
56
|
# #ask with :limited_to set to url regexp.
|
82
57
|
# TODO: make good regexp
|
@@ -32,12 +32,9 @@ module RedmineCLI
|
|
32
32
|
|
33
33
|
def update_assigned_to(issue)
|
34
34
|
return unless options[:assign]
|
35
|
+
issue.assigned_to_id = InputParser.parse_user(options[:assign], project: issue.project).id
|
35
36
|
|
36
|
-
|
37
|
-
Models::User.find(options[:assign])
|
38
|
-
issue.assigned_to_id = options[:assign]
|
39
|
-
|
40
|
-
rescue ActiveResource::ResourceNotFound
|
37
|
+
rescue UserNotFound
|
41
38
|
@errors.push "Assigned: #{m(:not_found)}"
|
42
39
|
end
|
43
40
|
|
@@ -70,7 +67,7 @@ module RedmineCLI
|
|
70
67
|
def add_time_entry_to_issue(issue)
|
71
68
|
return unless options[:time]
|
72
69
|
|
73
|
-
hours = parse_time(options[:time])
|
70
|
+
hours = InputParser.parse_time(options[:time])
|
74
71
|
entry = Models::TimeEntry.create issue_id: issue.id,
|
75
72
|
hours: hours
|
76
73
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module RedmineCLI
|
2
|
+
#
|
3
|
+
# class with some methods for user input processing
|
4
|
+
#
|
5
|
+
class InputParser
|
6
|
+
extend Helpers::Input
|
7
|
+
|
8
|
+
#
|
9
|
+
# Processes string and tries to find project
|
10
|
+
#
|
11
|
+
def self.parse_project(value)
|
12
|
+
by_id = RedmineRest::Models::Project.find_by_id(value) if value.numeric?
|
13
|
+
return by_id if by_id
|
14
|
+
|
15
|
+
found_projects = RedmineRest::Models::Project.all.filter_by_name_substring(value)
|
16
|
+
case found_projects.size
|
17
|
+
when 0 then fail(ProjectNotFound)
|
18
|
+
when 1 then found_projects.first
|
19
|
+
else ask_for_object(found_projects)
|
20
|
+
end
|
21
|
+
|
22
|
+
rescue ActiveResource::ResourceNotFound, ActiveResource::ForbiddenAccess
|
23
|
+
raise ProjectNotFound
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Processes string and tries to find user
|
28
|
+
#
|
29
|
+
# @return [RedmineRest::Models::User]
|
30
|
+
# @raise [UserNotFound]
|
31
|
+
#
|
32
|
+
def self.parse_user(value, project: nil)
|
33
|
+
return RedmineRest::Models::User.find(value) if value.numeric? || value == 'current'
|
34
|
+
fail UserNotFound unless project
|
35
|
+
|
36
|
+
user_from_project(project, value)
|
37
|
+
|
38
|
+
rescue ActiveResource::ResourceNotFound
|
39
|
+
raise UserNotFound
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.user_from_project(project, name_substring = '')
|
43
|
+
found_members = project.members.filter_by_name_substring(name_substring)
|
44
|
+
|
45
|
+
case found_members.size
|
46
|
+
when 0 then fail(UserNotFound)
|
47
|
+
when 1 then found_members.first
|
48
|
+
else ask_for_object(found_members)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Parses time from user's input.
|
54
|
+
# Formats: HH:MM; M; H.h
|
55
|
+
#
|
56
|
+
# @param input [String]
|
57
|
+
#
|
58
|
+
def self.parse_time(input)
|
59
|
+
fail(BadInputTime) unless input =~ /^\d+[\:\.]?\d*/
|
60
|
+
|
61
|
+
if input.include?(':')
|
62
|
+
h, m = input.split(':').map(&:to_i)
|
63
|
+
(60 * h + m) / 60.0
|
64
|
+
elsif input.include?('.')
|
65
|
+
input.to_f
|
66
|
+
else
|
67
|
+
input.to_i
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -16,7 +16,10 @@ module RedmineCLI
|
|
16
16
|
def list(id = 'current')
|
17
17
|
fail('new config') if Config.new?
|
18
18
|
|
19
|
-
|
19
|
+
user = InputParser.parse_user(id)
|
20
|
+
puts erb('issue/list', issues: user.issues)
|
21
|
+
rescue UserNotFound
|
22
|
+
puts "User #{m(:not_found)}"
|
20
23
|
end
|
21
24
|
|
22
25
|
desc 'show <id>', m('desc.issue.show')
|
@@ -40,7 +43,7 @@ module RedmineCLI
|
|
40
43
|
#
|
41
44
|
desc 'update <id>', m('desc.issue.update')
|
42
45
|
option :done, type: :numeric, aliases: '-d', desc: m('desc.issue.options.update.done')
|
43
|
-
option :assign, type: :
|
46
|
+
option :assign, type: :string, aliases: '-a', desc: m('desc.issue.options.update.assign')
|
44
47
|
option :time, type: :string, aliases: '-t', desc: m('desc.issue.options.update.time')
|
45
48
|
option :status, type: :string, aliases: '-s', desc: m('desc.issue.options.update.status')
|
46
49
|
option :comment, type: :boolean, aliases: '-c', desc: m('desc.issue.options.update.comment')
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'redmine_rest'
|
3
|
+
module RedmineCLI
|
4
|
+
module Subcommands
|
5
|
+
#
|
6
|
+
# Methods for working with projects
|
7
|
+
#
|
8
|
+
class Project < Thor
|
9
|
+
extend Helpers::Output
|
10
|
+
|
11
|
+
include RedmineRest
|
12
|
+
include Helpers::Output
|
13
|
+
include Helpers::Input
|
14
|
+
|
15
|
+
map 'users' => 'members'
|
16
|
+
|
17
|
+
desc 'list', m('desc.project.list')
|
18
|
+
def list
|
19
|
+
puts erb('id_and_name_list', list: Models::Project.all)
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'members <id | name part>', m('desc.project.members')
|
23
|
+
def members(project)
|
24
|
+
project = InputParser.parse_project(project)
|
25
|
+
|
26
|
+
users = project.members
|
27
|
+
.map(&:reload)
|
28
|
+
.sort { |a, b| a.id.to_i <=> b.id.to_i }
|
29
|
+
|
30
|
+
puts erb('user/find', users: users)
|
31
|
+
rescue ProjectNotFound
|
32
|
+
puts "Project #{m(:not_found)}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/redmine_cli/version.rb
CHANGED
data/lib/redmine_cli.rb
CHANGED
@@ -10,6 +10,7 @@ Dir[File.expand_path('../redmine_cli/helpers/**/*.rb', __FILE__)].each { |f| req
|
|
10
10
|
require 'redmine_cli/version'
|
11
11
|
require 'redmine_cli/exceptions'
|
12
12
|
require 'redmine_cli/config'
|
13
|
+
require 'redmine_cli/input_parser'
|
13
14
|
I18n.locale = RedmineCLI::Config['locale']
|
14
15
|
|
15
16
|
require 'redmine_cli/template_renderer'
|
@@ -43,5 +44,8 @@ module RedmineCLI
|
|
43
44
|
|
44
45
|
desc 'user ...', m('desc.client.user')
|
45
46
|
subcommand 'user', Subcommands::User
|
47
|
+
|
48
|
+
desc 'project ...', m('desc.client.project')
|
49
|
+
subcommand 'project', Subcommands::Project
|
46
50
|
end
|
47
51
|
end
|
data/redmine_cli.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
|
28
28
|
spec.add_dependency 'thor', '~> 0.19'
|
29
29
|
spec.add_dependency 'i18n', '~> 0.7'
|
30
|
-
spec.add_dependency 'redmine_rest', '0.
|
30
|
+
spec.add_dependency 'redmine_rest', '0.7.0'
|
31
31
|
spec.add_dependency 'non_config', '0.1.2'
|
32
32
|
spec.add_dependency 'colorize', '~> 0.7'
|
33
33
|
spec.add_dependency 'unicode', '~> 0.4'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redmine_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitriy Non
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
103
|
+
version: 0.7.0
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.7.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: non_config
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- exe/redmine
|
172
172
|
- lib/assets/default_config.yml
|
173
173
|
- lib/assets/messages/en.yml
|
174
|
+
- lib/assets/templates/en/id_and_name_list.erb
|
174
175
|
- lib/assets/templates/en/issue/list.erb
|
175
176
|
- lib/assets/templates/en/issue/show.erb
|
176
177
|
- lib/assets/templates/en/user/find.erb
|
@@ -182,8 +183,10 @@ files:
|
|
182
183
|
- lib/redmine_cli/helpers/issue/update.rb
|
183
184
|
- lib/redmine_cli/helpers/monkey_patching.rb
|
184
185
|
- lib/redmine_cli/helpers/output.rb
|
186
|
+
- lib/redmine_cli/input_parser.rb
|
185
187
|
- lib/redmine_cli/subcommands/conf.rb
|
186
188
|
- lib/redmine_cli/subcommands/issue.rb
|
189
|
+
- lib/redmine_cli/subcommands/project.rb
|
187
190
|
- lib/redmine_cli/subcommands/user.rb
|
188
191
|
- lib/redmine_cli/template_renderer.rb
|
189
192
|
- lib/redmine_cli/version.rb
|