troo 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
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