taskmapper-bcx 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76364ca93d18edf8820a34fbb399c801b7baecc1
4
+ data.tar.gz: 43a7fa1e2daa97823e37ff1a341cd7f516938eeb
5
+ SHA512:
6
+ metadata.gz: 59a4f1ce9de5e0f4ad3269de65b8657503704a441197474f7e7396f7622de4dd3dba0c855f4f73bb8592854d73108db46858c6577194eff66f3c96c1625f1f8b
7
+ data.tar.gz: 24ef5d1916a8229e4ecb402aecad85331fd1efab7f178313eba5d06e5faeaa9d7ed9988c1b562124d53bf5c0e34f3ea407a04ecd59892016a7ad55a3403e5d8c
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andrew Stewart
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,104 @@
1
+ # taskmapper-bcx
2
+
3
+ This is the [TaskMapper][] adapter for interaction with [Basecamp Next][]
4
+
5
+ ## Usage
6
+
7
+ Initialize the taskmapper-bcx instance using your username, password, and
8
+ account ID:
9
+
10
+ ```ruby
11
+ bcx = TaskMapper.new(
12
+ :bcx,
13
+ :account_id => "123456789",
14
+ :username => "YOUR_USERNAME",
15
+ :username => "YOUR_PASSWORD",
16
+ )
17
+ ```
18
+
19
+ ## Finding Projects
20
+
21
+ You can find your own projects by using:
22
+
23
+ ```ruby
24
+ projects = bcx.projects # will return all projects
25
+ projects = bcx.projects ["project_id", "another_project_id"]
26
+ project = bcx.projects.find :first, "project_id"
27
+ projects = bcx.projects.find :all, ["project_id", "another_project_id"]
28
+ ```
29
+
30
+ ## Finding Tickets
31
+
32
+ ```ruby
33
+ tickets = project.tickets # All open tickets
34
+ tickets = project.tickets :all, :status => 'closed' # all closed tickets
35
+ ticket = project.ticket 981234
36
+ ```
37
+
38
+ ## Opening A Ticket
39
+
40
+ ```ruby
41
+ ticket = project.ticket!(
42
+ :description => "Content of the new ticket."
43
+ )
44
+ ```
45
+
46
+ ## Closing Tickets
47
+
48
+ ```ruby
49
+ ticket.close
50
+ ```
51
+
52
+ ## Reopening Tickets
53
+
54
+ ```ruby
55
+ ticket.reopen
56
+ ```
57
+
58
+ ## Updating Tickets
59
+
60
+ ```ruby
61
+ ticket.description = "New description"
62
+ ticket.save
63
+ ```
64
+
65
+ ## Finding Comments
66
+
67
+ ```ruby
68
+ ticket.comments # all comments for a ticket
69
+ ticket.comments 90210
70
+ ```
71
+
72
+ ## Creating a Comment
73
+
74
+ ```ruby
75
+ ticket.comment! :body => "New Comment!"
76
+ ```
77
+
78
+ ## Dependencies
79
+
80
+ - rubygems
81
+ - [taskmapper][]
82
+ - [httparty][]
83
+
84
+ ## Contributing
85
+
86
+ The main way you can contribute is with some code! Here's how:
87
+
88
+ - Fork `taskmapper-bcx`
89
+ - Create a topic branch: git checkout -b my_awesome_feature
90
+ - Push to your branch - git push origin my_awesome_feature
91
+ - Create a Pull Request from your branch
92
+ - That's it!
93
+
94
+ We use RSpec for testing. Please include tests with your pull request. A simple
95
+ `bundle exec rake` will run the suite. Also, please try to TomDoc your methods,
96
+ it makes it easier to see what the code does and makes it easier for future
97
+ contributors to get started.
98
+
99
+ (c) 2013 The Hybrid Group
100
+
101
+
102
+ [taskmapper]: http://ticketrb.com
103
+ [basecamp next]: https://basecamp.com
104
+ [httparty]: https://github.com/jnunemaker/httparty
@@ -0,0 +1,7 @@
1
+ require "bundler"
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => :spec
5
+
6
+ Bundler::GemHelper.install_tasks
7
+ RSpec::Core::RakeTask.new :spec
@@ -0,0 +1,26 @@
1
+ require 'httparty'
2
+
3
+ module TaskMapper::Provider
4
+ module Bcx
5
+ class API
6
+ include HTTParty
7
+
8
+ attr_reader :account_id, :username, :password
9
+
10
+ headers "Content-Type" => "Content-Type: application/json; charset=utf-8"
11
+
12
+ def initialize(account_id, username, password)
13
+ @username = username
14
+ @password = password
15
+ @account_id = account_id
16
+ self.class.base_uri "https://basecamp.com/#{account_id}/api/v1"
17
+ self.class.basic_auth username, password
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'provider/api/auth'
24
+ require 'provider/api/projects'
25
+ require 'provider/api/todos'
26
+ require 'provider/api/comments'
@@ -0,0 +1,12 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class API
4
+ def authenticated?
5
+ @auth ||= begin
6
+ me = self.class.get "/people/me.json"
7
+ me.is_a?(Hash) && !me['id'].nil?
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class API
4
+ def create_comment(attributes)
5
+ project = attributes.delete(:project_id)
6
+ todo = attributes.delete(:ticket_id)
7
+ body = { "content" => attributes[:body] }
8
+
9
+ url = "/projects#{project}/todos/#{todo}/comments.json"
10
+ self.class.post url, :body => body.to_json
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class API
4
+ def projects
5
+ self.class.get "/projects.json"
6
+ end
7
+
8
+ def project(id)
9
+ self.class.get "/projects/#{id}.json"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class API
4
+ def todos(project_id)
5
+ ids = get_todolist_ids project_id
6
+ todo_ids = ids.collect do |id|
7
+ get_todo_ids_from_list project_id, id
8
+ end.flatten
9
+
10
+ todos = []
11
+ todo_ids.each do |id|
12
+ todo = self.class.get "/projects/#{project_id}/todos/#{id}.json"
13
+ todos << Hash[todo]
14
+ end
15
+ todos.flatten
16
+ end
17
+
18
+ def todo(project_id, todo_id)
19
+ self.class.get "/projects/#{project_id}/todos/#{todo_id}.json"
20
+ end
21
+
22
+ def create_todo(attributes)
23
+ project = attributes.delete(:project_id)
24
+ todolist = attributes.delete(:todolist_id)
25
+ due_at = attributes.delete(:due_at)
26
+
27
+ body = { "content" => attributes[:description] }
28
+ body.merge({ "due_at" => due_at.utc.iso8601 }) if due_at
29
+
30
+ url = "/projects/#{project}/todolists/#{todolist}/todos.json"
31
+ self.class.post url, :body => body.to_json
32
+ end
33
+
34
+ def update_todo(todo)
35
+ body = {
36
+ :content => todo['content'],
37
+ :due_at => todo['due_at'],
38
+ :completed => todo['completed']
39
+ }
40
+
41
+ url = "/projects/#{todo['project_id']}/todos/#{todo['id']}.json"
42
+ self.class.put url, :body => body.to_json
43
+ end
44
+
45
+ private
46
+ def get_todolist_ids(project_id)
47
+ lists = self.class.get "/projects/#{project_id}/todolists.json"
48
+ lists.collect { |t| t['id'] }
49
+ end
50
+
51
+ def get_todo_ids_from_list(project_id, list)
52
+ list = self.class.get "/projects/#{project_id}/todolists/#{list}.json"
53
+ return [] if !list.is_a?(Hash) || list["todos"].nil?
54
+ ids = []
55
+ ids << list["todos"]["remaining"].map { |t| t['id'] }
56
+ ids << list["todos"]["completed"].map { |t| t['id'] }
57
+ ids.flatten
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,44 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ include TaskMapper::Provider::Base
4
+
5
+ class << self
6
+ attr_accessor :username, :password, :account_id, :api
7
+
8
+ def new(auth = {})
9
+ TaskMapper.new(:bcx, auth)
10
+ end
11
+ end
12
+
13
+ def provider
14
+ TaskMapper::Provider::Bcx
15
+ end
16
+
17
+ def authorize(auth = {})
18
+ @authenticator ||= TaskMapper::Authenticator.new(auth)
19
+
20
+ unless auth[:username] && auth[:password]
21
+ message = "Please provide a username and password."
22
+ raise TaskMapper::Exception.new message
23
+ end
24
+
25
+ unless auth[:account_id]
26
+ message = "Please provide a Basecamp account_id"
27
+ raise TaskMapper::Exception.new message
28
+ end
29
+
30
+ provider.username = auth[:username]
31
+ provider.password = auth[:password]
32
+ provider.account_id = auth[:account_id]
33
+ configure auth
34
+ end
35
+
36
+ def configure(auth)
37
+ provider.api = API.new auth[:account_id], auth[:username], auth[:password]
38
+ end
39
+
40
+ def valid?
41
+ provider.api.authenticated?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,78 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class Comment < TaskMapper::Provider::Base::Comment
4
+ def initialize(*object)
5
+ object = object.first if object.is_a?(Array)
6
+ super object if object.is_a?(Hash)
7
+ end
8
+
9
+ def body
10
+ self[:content]
11
+ end
12
+
13
+ def updated_at
14
+ begin
15
+ Time.parse(self[:updated_at])
16
+ rescue
17
+ self[:updated_at]
18
+ end
19
+ end
20
+
21
+ def created_at
22
+ begin
23
+ Time.parse(self[:created_at])
24
+ rescue
25
+ self[:created_at]
26
+ end
27
+ end
28
+
29
+ class << self
30
+ def find_by_attributes(project_id, ticket_id, attributes = {})
31
+ search_by_attribute(self.find_all(project_id, ticket_id), attributes)
32
+ end
33
+
34
+ def find_by_id(project_id, ticket_id, comment_id)
35
+ ticket = Ticket.find_by_id project_id, ticket_id
36
+ if comment = ticket[:comments].find { |c| c[:id] == comment_id }
37
+ comment = comment.merge({
38
+ :ticket_id => ticket_id,
39
+ :project_id => project_id
40
+ })
41
+ return self.new comment
42
+ else
43
+ return false
44
+ end
45
+ end
46
+
47
+ def find_all(project_id, ticket_id)
48
+ ticket = Ticket.find_by_id project_id, ticket_id
49
+ ticket[:comments].collect do |comment|
50
+ comment = comment.merge({
51
+ :ticket_id => ticket_id,
52
+ :project_id => project_id
53
+ })
54
+ self.new comment
55
+ end
56
+ end
57
+
58
+ def create(attributes)
59
+ project = attributes[:project_id]
60
+ ticket = attributes[:ticket_id]
61
+
62
+ comment = api.create_comment attributes
63
+ comment = comment.merge({
64
+ :project_id => project,
65
+ :ticket_id => ticket
66
+ })
67
+
68
+ self.new comment
69
+ end
70
+
71
+ private
72
+ def api
73
+ TaskMapper::Provider::Bcx.api
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,48 @@
1
+ module TaskMapper::Provider
2
+ module Bcx
3
+ class Project < TaskMapper::Provider::Base::Project
4
+ def initialize(*object)
5
+ object = object.first if object.is_a?(Array)
6
+ super object if object.is_a?(Hash)
7
+ end
8
+
9
+ def updated_at
10
+ begin
11
+ Time.parse(self[:updated_at])
12
+ rescue
13
+ self[:updated_at]
14
+ end
15
+ end
16
+
17
+ def created_at
18
+ begin
19
+ Time.parse(self[:created_at])
20
+ rescue
21
+ self[:created_at]
22
+ end
23
+ end
24
+
25
+ class << self
26
+ def find_by_attributes(attributes = {})
27
+ search_by_attribute(self.find_all, attributes)
28
+ end
29
+
30
+ def find_all
31
+ projects = api.projects
32
+ projects = projects.select { |project| project.is_a?(Hash) }
33
+ projects.collect { |project| self.new project }
34
+ end
35
+
36
+ def find_by_id(id)
37
+ project = api.project(id)
38
+ self.new Hash[project]
39
+ end
40
+
41
+ private
42
+ def api
43
+ TaskMapper::Provider::Bcx.api
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end