troo 0.0.11 → 0.0.12

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +3 -0
  3. data/Gemfile.lock +4 -1
  4. data/README.md +7 -7
  5. data/Rakefile +2 -2
  6. data/config/en.yml +2 -0
  7. data/features/support/env.rb +0 -4
  8. data/lib/troo.rb +14 -26
  9. data/lib/troo/api/client.rb +28 -6
  10. data/lib/troo/api/endpoints.rb +42 -30
  11. data/lib/troo/api/response.rb +5 -0
  12. data/lib/troo/cli/main.rb +3 -0
  13. data/lib/troo/cli/wizard.rb +71 -0
  14. data/lib/troo/configuration.rb +29 -15
  15. data/lib/troo/debug.rb +9 -0
  16. data/lib/troo/decorators/resource.rb +8 -4
  17. data/lib/troo/launcher.rb +22 -12
  18. data/lib/troo/models/board.rb +2 -2
  19. data/lib/troo/models/card.rb +2 -2
  20. data/lib/troo/models/member.rb +1 -1
  21. data/lib/troo/models/refresh.rb +2 -2
  22. data/lib/troo/remote/list.rb +1 -1
  23. data/lib/troo/remote/member.rb +1 -1
  24. data/lib/troo/remote/persistence/board.rb +6 -10
  25. data/lib/troo/remote/persistence/card.rb +6 -10
  26. data/lib/troo/remote/persistence/comment.rb +6 -10
  27. data/lib/troo/remote/persistence/list.rb +6 -10
  28. data/lib/troo/remote/persistence/move_card.rb +6 -10
  29. data/lib/troo/troo.rb +3 -2
  30. data/lib/troo/version.rb +1 -1
  31. data/test/lib/troo/api/endpoints_test.rb +12 -22
  32. data/test/lib/troo/cli/commands/status_test.rb +1 -1
  33. data/test/lib/troo/cli/wizard_test.rb +44 -0
  34. data/test/lib/troo/configuration_test.rb +39 -0
  35. data/test/lib/troo/decorators/resource_test.rb +2 -1
  36. data/test/lib/troo/models/member_test.rb +1 -1
  37. data/test/support/fake_trello/fake_response.rb +1 -2
  38. data/test/support/fake_trello/server.rb +22 -14
  39. data/test/test_helper.rb +0 -5
  40. data/troo.gemspec +1 -0
  41. metadata +20 -6
  42. data/config/trello_api.yml +0 -19
  43. data/lib/troo/decorators/member.rb +0 -12
  44. data/test/lib/troo/decorators/member_test.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e68baa9983b5fa453d4feceb5542febfa90bb445
4
- data.tar.gz: 3b3a8343f7f8b1d80ac3ee811faa29fdaefde523
3
+ metadata.gz: 7f4539888216b2fee0e90cc5a1cffd97a20e8d6d
4
+ data.tar.gz: e35fc3a74a984d5f3eb0a76f4331d69fe85f5f78
5
5
  SHA512:
6
- metadata.gz: 58654c3f6350bd6e016a44b94191263577cb655835882926896c553c7b8c10e79eb8b387130b71b7ced574533a0a8f9dfc47b9158263616836a67b2f5f3c8f9f
7
- data.tar.gz: f3d8a493bba1fcc134496dd3f437b7ba613ba978802cd45fd74786d46adedfdaae4e1ea579148eaec983b20a8864ecc42485c32016db149e07bc40b224951259
6
+ metadata.gz: a90ddbf943db77a1544cf98590ba2d4fe2eb491edcfe99cbf254a2f792e88cfa6e7150bf9a71f230e5355e2d140f7a58ed19ede803a7d9db51cd06ca36bcf4db
7
+ data.tar.gz: 94686af4d0318e6fea58e6392ea4475dcb060a2ea00cd81af47168dca8fe5c227351f8053a72b9bdf0f0e019be16d64c9cf988e2c6bc9d33589ec2afedc6ec08
data/.simplecov ADDED
@@ -0,0 +1,3 @@
1
+ SimpleCov.start do
2
+ add_filter '/test/'
3
+ end
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- troo (0.0.11)
4
+ troo (0.0.12)
5
5
  addressable
6
6
  curses (= 1.0.1)
7
7
  dispel
8
8
  json
9
+ launchy
9
10
  oauth
10
11
  ohm (= 1.3.2)
11
12
  ohm-contrib (= 1.2)
@@ -76,6 +77,8 @@ GEM
76
77
  minitest (>= 3.0)
77
78
  ice_nine (0.11.0)
78
79
  json (1.8.1)
80
+ launchy (2.4.2)
81
+ addressable (~> 2.3)
79
82
  listen (2.7.1)
80
83
  celluloid (>= 0.15.2)
81
84
  celluloid-io (>= 0.15.0)
data/README.md CHANGED
@@ -29,6 +29,12 @@ Or install it yourself as:
29
29
 
30
30
  You will need user authentication tokens to access your Trello account.
31
31
 
32
+ ### Setup Wizard
33
+
34
+ Simply run `troo wizard` and follow the on screen instructions.
35
+
36
+ ### Manually
37
+
32
38
  1) Sign in to Trello in the normal way.
33
39
 
34
40
  2) Create your developer key at Trello:
@@ -89,12 +95,6 @@ You will need user authentication tokens to access your Trello account.
89
95
 
90
96
  troo version
91
97
 
92
- ## Todo
93
-
94
- - Fix multitude of sins.
95
- - Write cucumber integration tests.
96
- - Convert dates/times to Time.iso8601().
97
-
98
98
  ## Testing
99
99
 
100
100
  I've put a simple fake server together so that I'm not hitting the Trello API continuously. You can activate and use this too.
@@ -115,7 +115,7 @@ I've put a simple fake server together so that I'm not hitting the Trello API co
115
115
  4) Run `server.rb`:
116
116
 
117
117
  cd ./test/support/fake_trello
118
- ruby ./server.rb
118
+ ./server.rb
119
119
 
120
120
  5) Have fun.
121
121
 
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ if File.exist?(Dir.home + '/.trooconf')
19
19
 
20
20
  Rake::Task['cucumber'].execute
21
21
  else
22
- warn "\nConfiguration cannot be found, please run 'troo " \
23
- "init' or './bin/troo init' first.\n"
22
+ warn "\nConfiguration cannot be found, please run `troo " \
23
+ "init` or `./bin/troo init` first.\n"
24
24
  exit(1)
25
25
  end
data/config/en.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  en:
2
+ configuration:
3
+ not_found: 'Configuration cannot be found, please run `troo init` or `./bin/troo init` first.'
2
4
  cli:
3
5
  add:
4
6
  board: 'Add a new board with <name>; prompts if <name> not provided.'
@@ -7,10 +7,6 @@ require 'vcr'
7
7
  require 'webmock/cucumber'
8
8
  require 'mocha/api'
9
9
 
10
- SimpleCov.start do
11
- add_filter '/test/'
12
- end
13
-
14
10
  require_relative '../../lib/troo.rb'
15
11
 
16
12
  class CucumberError < StandardError; end
data/lib/troo.rb CHANGED
@@ -5,50 +5,38 @@ require_relative 'troo/version'
5
5
 
6
6
  module Troo
7
7
  ConfigurationNotFound = Class.new(StandardError)
8
- InvalidAccessToken = Class.new(StandardError)
8
+ ConfigurationAborted = Class.new(StandardError)
9
+ ExpiredAccessToken = Class.new(StandardError)
9
10
  EndpointNotFound = Class.new(StandardError)
10
11
 
11
- # @param []
12
12
  # @param [String]
13
- # @return []
13
+ # @param [String]
14
+ # @return [Troo::Configuration]
14
15
  def self.configuration(file = Dir.home + '/.trooconf', env = 'default')
15
16
  unless File.exist?(file)
16
- warn "\nConfiguration cannot be found, please run 'troo " \
17
- "init' or './bin/troo init' first.\n"
18
- file = configuration_path + '/trooconf.yml'
17
+ warn "\nConfiguration cannot be found, please run `troo " \
18
+ "init` or `./bin/troo init` first.\n"
19
+ file = root_path + '/config/trooconf.yml'
19
20
  end
20
21
 
21
22
  @configuration ||= Troo::Configuration.load(file, env)
22
23
  end
23
24
 
24
- # @param [String]
25
- # @return []
26
- def self.endpoints(version = 'version_1')
27
- @endpoints ||= Troo::API::Endpoints
28
- .load(configuration_path + '/trello_api.yml', version)
29
- end
30
-
31
- # @return []
25
+ # @return [TrueClass]
32
26
  def self.logger
33
27
  @logger ||= Logger
34
- .new(log_path + '/troo.log').tap do |log|
28
+ .new(root_path + '/logs/troo.log').tap do |log|
35
29
  log.formatter = proc do |mode, time, prog, msg|
36
- "#{time.iso8601} #{mode}:\n#{msg}\n"
30
+ "\n" + Esc.green + "#{time.iso8601}:" + Esc.reset + " #{msg}\n"
37
31
  end
38
32
  end
39
33
  end
40
34
 
35
+ def self.root_path
36
+ File.expand_path('../..', __FILE__)
37
+ end
38
+
41
39
  # RestClient.log = log_dir + '/restclient.log'
42
40
 
43
41
  Database.connect(configuration)
44
-
45
- private
46
-
47
- def self.configuration_path
48
- File.dirname(__FILE__) + '/../config'
49
- end
50
-
51
- def self.log_path
52
- File.dirname(__FILE__) + '/../logs'
53
- end
54
42
  end
@@ -40,15 +40,25 @@ module Troo
40
40
  end
41
41
 
42
42
  def empty_response?
43
- parsed_response.empty?
43
+ return log(true) if parsed_response.empty?
44
+ false
44
45
  end
45
46
 
46
47
  def error_response?
47
- response.is_a?(ErrorResponse)
48
+ return log(true) if response.is_a?(ErrorResponse)
49
+ false
48
50
  end
49
51
 
50
52
  def parsed_response
51
- @parsed ||= Yajl::Parser.parse(response.body)
53
+ @parsed ||= Yajl::Parser.parse(response_body)
54
+ end
55
+
56
+ def response_body
57
+ data = response.body
58
+ File.open(filename, 'w') do |file_handle|
59
+ file_handle.write(data)
60
+ end
61
+ data
52
62
  end
53
63
 
54
64
  def response
@@ -69,7 +79,7 @@ module Troo
69
79
  end
70
80
 
71
81
  def urn
72
- Troo.endpoints.interpolate!(endpoint, interpolation)
82
+ Troo::API::Endpoints.interpolate(endpoint, interpolation)
73
83
  end
74
84
 
75
85
  def missing_parameters?
@@ -80,8 +90,9 @@ module Troo
80
90
  Troo.configuration.allow_remote
81
91
  end
82
92
 
83
- def log
84
- Troo.logger.debug(formatted_messages) if log?
93
+ def log(retval = nil)
94
+ Troo.logger.debug("\n" + formatted_messages) if log?
95
+ retval
85
96
  end
86
97
 
87
98
  def log?
@@ -115,6 +126,17 @@ module Troo
115
126
  'Response' => response.body
116
127
  }
117
128
  end
129
+
130
+ def filename
131
+ filename = [Troo.root_path + '/tmp/', endpoint.to_s]
132
+ filename << '_' << external_id if external_id
133
+ filename << '.json'
134
+ filename.join
135
+ end
136
+
137
+ def external_id
138
+ interpolation[:external_id]
139
+ end
118
140
  end
119
141
  end
120
142
  end
@@ -1,45 +1,57 @@
1
1
  module Troo
2
2
  module API
3
3
  class Endpoints
4
- include Virtus.value_object
5
-
6
- values do
7
- attribute :board_by_id
8
- attribute :boards_all
9
- attribute :card_by_id
10
- attribute :cards_by_board_id
11
- attribute :cards_by_list_id
12
- attribute :comments_by_board_id
13
- attribute :comments_by_card_id
14
- attribute :comments_by_list_id
15
- attribute :create_board
16
- attribute :create_card
17
- attribute :create_comment
18
- attribute :create_list
19
- attribute :list_by_id
20
- attribute :lists_by_board_id
21
- attribute :member_by_id
22
- attribute :members_by_board_id
23
- attribute :move_card_board
24
- attribute :move_card_list
25
- end
26
-
27
4
  class << self
28
- # @param [String]
29
- # @param [String]
30
- # @return [Troo::API::Endpoints]
31
- def load(file, version)
32
- new(YAML.load_file(file)[version])
5
+ # @param [Symbol]
6
+ # @param [Hash]
7
+ # @return [String, EndpointNotFound]
8
+ def interpolate(endpoint, value = {})
9
+ new(endpoint, value).interpolate
33
10
  end
34
11
  end
35
12
 
13
+ # @param [Symbol]
14
+ # @param [Hash]
15
+ # @return [Troo::API::Endpoints]
16
+ def initialize(endpoint, value = {})
17
+ @endpoint, @value = endpoint, value
18
+ end
19
+
36
20
  # @param [Symbol]
37
21
  # @param [Hash]
38
22
  # @return [String, EndpointNotFound]
39
- def interpolate!(endpoint, value = {})
40
- return send(endpoint) % value if respond_to?(endpoint)
23
+ def interpolate
24
+ endpoints.fetch(endpoint) % value
25
+ rescue KeyError
41
26
  fail EndpointNotFound
42
27
  end
28
+
29
+ private
30
+
31
+ attr_reader :endpoint, :value
32
+
33
+ def endpoints
34
+ {
35
+ boards_all: '/members/me/boards',
36
+ board_by_id: '/boards/%{external_id}',
37
+ card_by_id: '/cards/%{external_id}',
38
+ list_by_id: '/lists/%{external_id}',
39
+ member_by_id: '/members/%{external_id}',
40
+ cards_by_board_id: '/boards/%{external_id}/cards',
41
+ cards_by_list_id: '/lists/%{external_id}/cards',
42
+ comments_by_board_id: '/boards/%{external_id}/actions',
43
+ comments_by_card_id: '/cards/%{external_id}/actions',
44
+ comments_by_list_id: '/lists/%{external_id}/actions',
45
+ lists_by_board_id: '/boards/%{external_id}/lists',
46
+ members_by_board_id: '/boards/%{external_id}/members',
47
+ create_board: '/boards',
48
+ create_card: '/cards',
49
+ create_comment: '/cards/%{external_id}/actions/comments',
50
+ create_list: '/lists',
51
+ move_card_list: '/cards/%{external_id}/idList',
52
+ move_card_board: '/cards/%{external_id}/idBoard',
53
+ }
54
+ end
43
55
  end
44
56
  end
45
57
  end
@@ -18,11 +18,16 @@ module Troo
18
18
 
19
19
  # @return [Response, ErrorResponse]
20
20
  def build
21
+ raise ExpiredAccessToken if expired?
21
22
  ok? ? Response.new(attributes) : ErrorResponse.new(attributes)
22
23
  end
23
24
 
24
25
  private
25
26
 
27
+ def expired?
28
+ code == '401'
29
+ end
30
+
26
31
  def ok?
27
32
  code == '200'
28
33
  end
data/lib/troo/cli/main.rb CHANGED
@@ -63,6 +63,9 @@ module Troo
63
63
  'Refresh all local data or board, list or card with <id>.'
64
64
  subcommand :refresh, CLI::Refresh
65
65
 
66
+ desc 'wizard', 'Become the wizard.'
67
+ subcommand :wizard, CLI::Wizard
68
+
66
69
  # @param [String]
67
70
  # @param [String]
68
71
  # @param [String]
@@ -0,0 +1,71 @@
1
+ module Troo
2
+ module CLI
3
+ class Wizard < ThorFixes
4
+ package_name 'wizard'
5
+ default_task :start
6
+
7
+ # @return [String]
8
+ desc 'start', 'Run the configuration wizard to get started.'
9
+ def start
10
+ say Troo::Formatter.wordwrap(welcome_message)
11
+
12
+ proceed?
13
+ step_one!
14
+
15
+ say Troo::Formatter.wordwrap(step_one_message)
16
+
17
+ proceed?
18
+ step_two!
19
+
20
+ say 'All done! Run `troo` for further commands.'
21
+ end
22
+
23
+ private
24
+
25
+ def connect_url(api_key)
26
+ "https://trello.com/1/connect?key=#{api_key}" \
27
+ "&name=troo&response_type=token&scope=read,write"
28
+ end
29
+
30
+ def generate_url
31
+ 'https://trello.com/1/appKey/generate'
32
+ end
33
+
34
+ def config
35
+ @config ||= Troo::Configuration.new
36
+ end
37
+
38
+ def proceed?
39
+ raise ConfigurationAborted unless yes?(user_input('Proceed?'))
40
+ end
41
+
42
+ def step_one!
43
+ Launchy.open(generate_url)
44
+ config.api_key = ask(user_input('Key:'))
45
+ config.api_token = ask(user_input('Secret:'))
46
+ end
47
+
48
+ def step_two!
49
+ Launchy.open(connect_url(config.api_key))
50
+ config.api_oauth_token = ask(user_input('Token:'))
51
+ config.save(Dir.home + '/.trooconf', 'default')
52
+ end
53
+
54
+ def welcome_message
55
+ "Welcome to Troo. This wizard will help you configure your " \
56
+ "API credentials for Trello. First, you'll need to sign in " \
57
+ "to Trello in the normal way. Copy the API key and paste " \
58
+ "when prompted, then do the same for the secret.\n"
59
+ end
60
+
61
+ def step_one_message
62
+ "Thank you. You must now allow this application to use " \
63
+ "Trello account. Copy and paste the token when prompted.\n"
64
+ end
65
+
66
+ def user_input(value)
67
+ [Esc.yellow, value, Esc.reset].join
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,33 +1,47 @@
1
1
  module Troo
2
2
  class Configuration
3
- include Virtus.value_object
3
+ include Virtus.model
4
4
 
5
- values do
6
- attribute :name
7
- attribute :api_url
8
- attribute :api_key
9
- attribute :api_token
10
- attribute :api_oauth_token
11
- attribute :api_oauth_token_secret
12
- attribute :database
13
- attribute :allow_remote, Boolean, default: true
14
- attribute :logs, Boolean, default: false
15
- end
5
+ attribute :api_key
6
+ attribute :api_token
7
+ attribute :api_oauth_token
8
+ attribute :api_oauth_token_secret
9
+ attribute :allow_remote, Boolean, default: true
10
+ attribute :logs, Boolean, default: false
11
+ attribute :name, String, default: 'My Default Configuration'
12
+ attribute :api_url, String, default: 'https://api.trello.com/1'
13
+ attribute :database, Integer, default: 1
16
14
 
17
15
  class << self
18
- # @param []
19
16
  # @param [String]
20
- # @return []
17
+ # @param [String]
18
+ # @return [Troo::Configuration]
21
19
  def load(file, env)
22
20
  new(YAML.load_file(file)[env])
23
21
  end
24
22
  end
25
23
 
26
- # @return []
24
+ # @param [String]
25
+ # @param [String]
26
+ # @return [TrueClass, FalseClass]
27
+ def save(file, env)
28
+ return true if File.open(file, 'w') do |file|
29
+ file.write configuration_yaml(env)
30
+ end
31
+ false
32
+ end
33
+
34
+ # @return [String]
27
35
  def view
28
36
  attributes.map do |label, value|
29
37
  Preference.view(label: label, value: value)
30
38
  end.join("\n")
31
39
  end
40
+
41
+ private
42
+
43
+ def configuration_yaml(env = 'default')
44
+ { env => self.attributes }.to_yaml
45
+ end
32
46
  end
33
47
  end