temjin 0.0.1

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.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # temjin
2
+
3
+ cli tool for talking to trello
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+ require 'rake/notes/rake_task'
4
+ require 'rubocop/rake_task'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'test'
8
+ t.test_files = ['test/main.rb']
9
+ t.verbose = true
10
+ t.warning = false
11
+ end
12
+
13
+ RuboCop::RakeTask.new
14
+
15
+ desc "Runs Rubocop style checker with xml output for Jenkins"
16
+ task 'rubocop:jenkins' do
17
+ system("bundle exec rubocop \
18
+ --require rubocop/formatter/checkstyle_formatter \
19
+ --format Robocop::Formatter::CheckstyleFormatter \
20
+ --no-color \
21
+ --out rubocop.xml")
22
+ end
23
+
24
+ task :default => %i[test rubocop]
25
+
26
+ CLEAN << 'coverage'
data/authors.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ - Adam Price
data/bin/temjin ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'temjin'
4
+ begin
5
+ Temjin::MainCommand.run
6
+ rescue StandardError => e
7
+ puts e.message
8
+ exit 1
9
+ end
@@ -0,0 +1,26 @@
1
+ module Temjin
2
+ class CardCommand
3
+ class AddCommand < Temjin::TrelloAPICommand
4
+ parameter 'BOARD', 'board name'
5
+ parameter 'LIST', 'list name'
6
+ parameter '[CARD_NAME]', 'name of card to add'
7
+
8
+ option '--desc', 'DESC', 'card description'
9
+
10
+ def execute
11
+ # TODO: should also be included in the command setup
12
+ user = find_user(config.username)
13
+
14
+ list_id = find_list(user, board, list).id
15
+
16
+ create_options = {}
17
+ create_options[:list_id] = list_id
18
+ create_options[:name] = card_name if card_name
19
+ create_options[:desc] = desc if desc
20
+
21
+ new_card = Trello::Card.create(create_options)
22
+ Formatador.display_line("[[yellow]#{new_card.id}[/]] added")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Temjin
2
+ class CardCommand
3
+ class ListCommand < Temjin::TrelloAPICommand
4
+ # TODO: eventually make BOARD and LIST optional, but for now we need them
5
+ parameter 'BOARD', 'trello board'
6
+ parameter 'LIST', 'trello list'
7
+
8
+ def execute
9
+ # TODO: should also be included in the command setup
10
+ user = find_user(config.username)
11
+
12
+ cards = find_list(user, board, list).cards
13
+
14
+ cards = cards.map do |c|
15
+ {:id => c.id,
16
+ :name => c.name,
17
+ :desc => c.desc}
18
+ end
19
+
20
+ cards.each do |card|
21
+ Formatador.display_line("[yellow]#{card[:id]}[/] - #{card[:name]}")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ module Temjin
2
+ class CardCommand
3
+ class ShowCommand < Temjin::TrelloAPICommand
4
+ parameter 'CARD_ID', 'card ID'
5
+
6
+ def execute
7
+ card = find_card(card_id)
8
+ list = find_list_by_id(card.list_id)
9
+ board = find_board(card.board_id)
10
+ members = card.member_ids.map { |member| find_user(member).username }
11
+ labels = card.labels.map { |label| label.name }
12
+
13
+ Formatador.display_line("# [bold]#{card.name}[/]")
14
+ Formatador.indent do
15
+ Formatador.display_line("-> #{list.name} --> #{board.name}")
16
+ end
17
+ Formatador.display_line("[[yellow]#{card.id}[/]] #{card.short_url} #{card.attachments.empty? ? '' : '+attachments '}- #{card.closed? ? '[_red_]closed[/]' : '[green]open[/]'}")
18
+ Formatador.display_line(members.map { |m| "[cyan]@#{m}[/]" }.join(', ')) unless members.empty?
19
+ Formatador.display_line("{ #{labels.map { |l| "[purple]#{l}[/]" }.join(', ')} }") unless labels.empty?
20
+ Formatador.display_line("due on [red]#{card.due.getlocal.ctime}[/]") unless card.due.nil?
21
+ unless card.checklists.empty?
22
+ Formatador.display_line("---")
23
+ card.checklists.each do |checklist|
24
+ Formatador.display_line(checklist.name)
25
+ Formatador.indent do
26
+ checklist.check_items.each do |checklist_item|
27
+ checkbox = if checklist_item['state'].match?(/^complete$/)
28
+ "\u2713".force_encoding('utf-8')
29
+ else
30
+ ' '
31
+ end
32
+ Formatador.display_line("[#{checkbox}] #{checklist_item['name']}")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ unless card.desc.empty?
38
+ Formatador.display_line("---")
39
+ Formatador.display_line(card.desc) # FIXME: what happens if the desc is multiple lines?
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ module Temjin
2
+ class CardCommand
3
+ class UpdateCommand < Temjin::TrelloAPICommand
4
+ parameter 'CARD_ID', 'card ID'
5
+
6
+ option '--name', 'NAME', 'card name'
7
+ option '--desc', 'DESC', 'card description'
8
+
9
+ def execute
10
+ card = find_card(card_id)
11
+
12
+ card.name = name if name
13
+ card.desc = desc if desc
14
+
15
+ card.save
16
+ Formatador.display_line("[[yellow]#{card.id}[/]] updated")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Temjin
2
+ class CardCommand < Clamp::Command; end
3
+ end
@@ -0,0 +1,46 @@
1
+ module Temjin
2
+ class Command < Clamp::Command
3
+ def initialize(invocation_path, context = {})
4
+ @config = context[:config] if context.key?(:config)
5
+ super(invocation_path, context)
6
+ end
7
+
8
+ def config
9
+ @config ||= Config.new
10
+ end
11
+ end
12
+
13
+ class TrelloAPICommand < Temjin::Command
14
+ def initialize(invocation_path, context = {})
15
+ super(invocation_path, context)
16
+ trello_connection
17
+ end
18
+
19
+ def trello_connection
20
+ Trello.configure do |c|
21
+ c.developer_public_key = config.key
22
+ c.member_token = config.token
23
+ end
24
+ end
25
+
26
+ def find_board(board_id)
27
+ Trello::Board.find(board_id)
28
+ end
29
+
30
+ def find_list(user, board_name, list_name)
31
+ user.boards.detect { |b| b.name.match(board_name) }.lists.detect { |l| l.name.match(list_name) }
32
+ end
33
+
34
+ def find_list_by_id(list_id)
35
+ Trello::List.find(list_id)
36
+ end
37
+
38
+ def find_user(username)
39
+ Trello::Member.find(username)
40
+ end
41
+
42
+ def find_card(card_id)
43
+ Trello::Card.find(card_id)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ module Temjin
2
+ class Config
3
+ attr_accessor :username, :key, :token
4
+
5
+ attr_reader :file_path
6
+
7
+ def initialize(options = {})
8
+ options = {:path => File.join(ENV['HOME'], '.config', 'temjin.yml'),
9
+ :create => false}.merge(options.select { |k, _| %i[path create].include?(k) })
10
+
11
+ @file_path = options[:path]
12
+
13
+ create_or_verify_file(options[:create])
14
+
15
+ config = YAML.load_file(@file_path) || {}
16
+ @username = config.dig('username')
17
+ @key = config.dig('key')
18
+ @token = config.dig('token')
19
+
20
+ verify_format unless options[:create]
21
+ end
22
+
23
+ def create_or_verify_file(create_bool = false)
24
+ if create_bool
25
+ FileUtils.touch(file_path)
26
+ else
27
+ fail Temjin::ConfigurationNotFoundError unless File.exist?(file_path)
28
+ end
29
+ end
30
+
31
+ def verify_format
32
+ malformed = username.nil? || key.nil? || token.nil?
33
+ fail Temjin::ConfigurationNotFoundError if malformed
34
+ end
35
+
36
+ def save!
37
+ File.open(file_path, 'w') do |f|
38
+ f.write({'username' => username, 'key' => key, 'token' => token}.to_yaml)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module Temjin
2
+ class ConfigCommand
3
+ class InitCommand < Temjin::Command
4
+ def config # override
5
+ @config ||= Config.new(:create => true)
6
+ end
7
+
8
+ def prompt(message, default = nil)
9
+ print(message)
10
+ result = $stdin.gets.chomp
11
+ result.empty? ? default : result
12
+ end
13
+
14
+ def execute
15
+ # TODO: use Formatador for all output
16
+ puts 'Go to https://trello.com/app-key to get your key and token.'
17
+ puts
18
+
19
+ config.username = prompt('username: ')
20
+ config.key = prompt('key: ')
21
+ config.token = prompt('token: ')
22
+
23
+ config.save!
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Temjin
2
+ class ConfigCommand
3
+ class ShowCommand < Temjin::Command
4
+ def execute
5
+ # TODO: use formatador for all output
6
+ puts "key: #{config.key}"
7
+ puts "token: #{config.token}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Temjin
2
+ class ConfigCommand < Clamp::Command; end
3
+ end
@@ -0,0 +1,8 @@
1
+ module Temjin
2
+ class ConfigurationNotFoundError < StandardError
3
+ def initialize(message = nil)
4
+ message ||= "Configuration not found. Please run `temjin config init`."
5
+ super(message)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Temjin
2
+ class ListCommand < Clamp::Command; end
3
+ end
@@ -0,0 +1,8 @@
1
+ module Temjin
2
+ class MainCommand < Clamp::Command
3
+ option ['-v', '--version'], :flag, 'display version' do
4
+ puts Temjin.version
5
+ exit
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Temjin
2
+ def self.version
3
+ @version ||= Gem::Version.new('0.0.1')
4
+ end
5
+ end
data/lib/temjin.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'formatador'
2
+ require 'clamp'
3
+ require 'trello' # ruby-trello
4
+
5
+ # beta feature
6
+ Clamp.allow_options_after_parameters = true
7
+
8
+ require 'temjin/configuration_not_found_error'
9
+ require 'temjin/config'
10
+ require 'temjin/command'
11
+
12
+ require 'temjin/config_command'
13
+ require 'temjin/config_command/show_command'
14
+ require 'temjin/config_command/init_command'
15
+ require 'temjin/card_command'
16
+ require 'temjin/card_command/list_command'
17
+ require 'temjin/card_command/show_command'
18
+ require 'temjin/card_command/add_command'
19
+ require 'temjin/card_command/update_command'
20
+ require 'temjin/list_command'
21
+ require 'temjin/main_command'
22
+
23
+ # cli structure
24
+ module Temjin
25
+ ConfigCommand.subcommand("show", "display current configuration", ConfigCommand::ShowCommand)
26
+ ConfigCommand.subcommand("init", "setup configuration", ConfigCommand::InitCommand)
27
+ MainCommand.subcommand("config", "temjin configuration", ConfigCommand)
28
+
29
+ CardCommand.subcommand("list", "list cards using optional filters", CardCommand::ListCommand)
30
+ CardCommand.subcommand("show", "display a card's information", CardCommand::ShowCommand)
31
+ CardCommand.subcommand("add", "add a new card", CardCommand::AddCommand)
32
+ CardCommand.subcommand("update", "update a card", CardCommand::UpdateCommand)
33
+ MainCommand.subcommand("card", "trello cards", CardCommand)
34
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ username: bigboss
3
+ key: 0123456789abcdef0123456789abcdef
4
+ token: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
@@ -0,0 +1,26 @@
1
+ module Temjin
2
+ module Test
3
+ module CaptureOutput
4
+ def capture_output
5
+ original_stdout = $stdout
6
+ $stdout = fake_stdout = StringIO.new
7
+
8
+ original_stderr = $stderr
9
+ $stderr = fake_stderr = StringIO.new
10
+
11
+ original_stdin = $stdin
12
+ $stdin = fake_stdin = StringIO.new
13
+
14
+ begin
15
+ yield(fake_stdin)
16
+ ensure
17
+ $stdout = original_stdout
18
+ $stderr = original_stderr
19
+ $stdin = original_stdin
20
+ end
21
+
22
+ [fake_stdout.string, fake_stderr.string]
23
+ end
24
+ end
25
+ end
26
+ end
data/test/main.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'mocha/mini_test'
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter '/test/'
6
+ end
7
+
8
+ require 'minitest/autorun'
9
+ require 'mocha/setup'
10
+
11
+ require 'temjin'
12
+
13
+ require 'webmock'
14
+ include WebMock::API
15
+ WebMock.enable!
16
+ WebMock.disable_net_connect!
17
+
18
+ module Temjin
19
+ module Test
20
+ HOME_DIR = File.dirname(__FILE__)
21
+ end
22
+ end
23
+
24
+ require './test/helpers/capture_output.rb'
25
+
26
+ Dir['./test/**/*_test.rb'].each do |f|
27
+ require f
28
+ end
@@ -0,0 +1,98 @@
1
+ module Temjin
2
+ module Test
3
+ module CardCommand
4
+ class AddCommandTest < MiniTest::Test
5
+ include CaptureOutput
6
+
7
+ def setup
8
+ @username = 'solid_snake'
9
+ @key = '0123456789abcdef0123456789abcdef'
10
+ @token = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
11
+
12
+ @board_name = 'fake_board'
13
+ @list_name = 'fake_list'
14
+ @id = '0123456789abcdef0123456789abcdef'
15
+ @card_name = "remind everyone that i'm no hero"
16
+ @card_desc = "uttered it a million times in metal gear solid 4"
17
+ end
18
+
19
+ def teardown; end
20
+
21
+ def test_execute_no_options
22
+ config = mock()
23
+ config.expects(:username).returns(@username)
24
+ config.expects(:key).returns(@key)
25
+ config.expects(:token).returns(@token)
26
+
27
+ list = mock()
28
+ list.expects(:id).returns(@id)
29
+
30
+ add_command = Temjin::CardCommand::AddCommand.new('temjin card add', :config => config)
31
+ add_command.expects(:find_user).with(@username).returns(@username)
32
+ add_command.expects(:find_list).with(@username, @board_name, @list_name).returns(list)
33
+
34
+ added_card = mock()
35
+ added_card.expects(:id).returns(@id)
36
+
37
+ create_options = {:list_id => @id}
38
+
39
+ Trello::Card.expects(:create).with(create_options).returns(added_card)
40
+
41
+ capture_output do
42
+ add_command.run([@board_name, @list_name])
43
+ end
44
+ end
45
+
46
+ def test_execute_with_name
47
+ config = mock()
48
+ config.expects(:username).returns(@username)
49
+ config.expects(:key).returns(@key)
50
+ config.expects(:token).returns(@token)
51
+
52
+ list = mock()
53
+ list.expects(:id).returns(@id)
54
+
55
+ add_command = Temjin::CardCommand::AddCommand.new('temjin card add', :config => config)
56
+ add_command.expects(:find_user).with(@username).returns(@username)
57
+ add_command.expects(:find_list).with(@username, @board_name, @list_name).returns(list)
58
+
59
+ added_card = mock()
60
+ added_card.expects(:id).returns(@id)
61
+
62
+ create_options = {:list_id => @id, :name => @card_name}
63
+
64
+ Trello::Card.expects(:create).with(create_options).returns(added_card)
65
+
66
+ capture_output do
67
+ add_command.run([@board_name, @list_name, @card_name])
68
+ end
69
+ end
70
+
71
+ def test_execute_with_name_and_desc
72
+ config = mock()
73
+ config.expects(:username).returns(@username)
74
+ config.expects(:key).returns(@key)
75
+ config.expects(:token).returns(@token)
76
+
77
+ list = mock()
78
+ list.expects(:id).returns(@id)
79
+
80
+ add_command = Temjin::CardCommand::AddCommand.new('temjin card add', :config => config)
81
+ add_command.expects(:find_user).with(@username).returns(@username)
82
+ add_command.expects(:find_list).with(@username, @board_name, @list_name).returns(list)
83
+
84
+ added_card = mock()
85
+ added_card.expects(:id).returns(@id)
86
+
87
+ create_options = {:list_id => @id, :name => @card_name, :desc => @card_desc}
88
+
89
+ Trello::Card.expects(:create).with(create_options).returns(added_card)
90
+
91
+ capture_output do
92
+ add_command.run([@board_name, @list_name, @card_name, '--desc', @card_desc])
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,47 @@
1
+ module Temjin
2
+ module Test
3
+ module CardCommand
4
+ class ListCommandTest < MiniTest::Test
5
+ include CaptureOutput
6
+
7
+ def setup
8
+ @username = 'solid_snake'
9
+ @key = '0123456789abcdef0123456789abcdef'
10
+ @token = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
11
+
12
+ @board_name = 'fake_board'
13
+ @list_name = 'fake_list'
14
+ @id = '0123456789abcdef0123456789abcdef'
15
+ @card_name = "remind everyone that i'm no hero"
16
+ @card_desc = "uttered it a million times in metal gear solid 4"
17
+ end
18
+
19
+ def teardown; end
20
+
21
+ def test_run
22
+ config = mock('config')
23
+ config.expects(:username).returns(@username)
24
+ config.expects(:key).returns(@key)
25
+ config.expects(:token).returns(@token)
26
+
27
+ card = mock('card')
28
+ card.expects(:id).times(3).returns(@id)
29
+ card.expects(:name).times(3).returns(@card_name)
30
+ card.expects(:desc).times(3).returns(@card_desc)
31
+ card_list = [card, card, card]
32
+
33
+ list = mock('list')
34
+ list.expects(:cards).returns(card_list)
35
+
36
+ list_command = Temjin::CardCommand::ListCommand.new('temjin card list', :config => config)
37
+ list_command.expects(:find_user).with(@username).returns(@username)
38
+ list_command.expects(:find_list).with(@username, @board_name, @list_name).returns(list)
39
+
40
+ capture_output do
41
+ list_command.run([@board_name, @list_name])
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ module Temjin
2
+ module Test
3
+ module CardCommand
4
+ class ShowCommandTest < MiniTest::Test
5
+ include CaptureOutput
6
+
7
+ def setup
8
+ @username = 'solid_snake'
9
+ @key = '0123456789abcdef0123456789abcdef'
10
+ @token = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
11
+
12
+ @board_name = 'fake_board'
13
+ @list_name = 'fake_list'
14
+ @label = 'fake_label'
15
+ @id = '0123456789abcdef0123456789abcdef'
16
+ @card_name = "remind everyone that i'm no hero"
17
+ @card_desc = "uttered it a million times in metal gear solid 4"
18
+ end
19
+
20
+ def teardown; end
21
+
22
+ def test_run
23
+ config = mock('config')
24
+ config.expects(:key).returns(@key)
25
+ config.expects(:token).returns(@token)
26
+
27
+ label = mock('label')
28
+ label.expects(:name).times(2).returns(@label)
29
+
30
+ card = mock('card')
31
+ card.expects(:list_id).returns(@id)
32
+ card.expects(:board_id).returns(@id)
33
+ card.expects(:member_ids).returns([@id, @id])
34
+ card.expects(:labels).returns([label, label])
35
+ card.expects(:name).returns(@card_name)
36
+ card.expects(:id).returns(@id)
37
+ card.expects(:short_url).returns('http://trello.com/fancyshorturl')
38
+ card.expects(:attachments).returns([])
39
+ card.expects(:closed?).returns(false)
40
+ card.expects(:due).times(2).returns(Time.now + (60 * 60 * 24))
41
+ card.expects(:checklists).returns([])
42
+ card.expects(:desc).times(2).returns(@card_desc)
43
+
44
+ user = mock('user')
45
+ user.expects(:username).times(2).returns(@username)
46
+
47
+ list = mock('list')
48
+ list.expects(:name).returns(@list_name)
49
+
50
+ board = mock('board')
51
+ board.expects(:name).returns(@board_name)
52
+
53
+ show_command = Temjin::CardCommand::ShowCommand.new('temjin card show', :config => config)
54
+ show_command.expects(:find_card).with(@id).returns(card)
55
+ show_command.expects(:find_list_by_id).with(@id).returns(list)
56
+ show_command.expects(:find_user).with(@id).times(2).returns(user)
57
+ show_command.expects(:find_board).with(@id).returns(board)
58
+
59
+ capture_output do
60
+ show_command.run([@id])
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end