ruby-trello-wgibbs 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +37 -0
- data/lib/trello/action.rb +46 -0
- data/lib/trello/association.rb +11 -0
- data/lib/trello/association_proxy.rb +42 -0
- data/lib/trello/authorization.rb +114 -0
- data/lib/trello/basic_data.rb +84 -0
- data/lib/trello/board.rb +95 -0
- data/lib/trello/card.rb +162 -0
- data/lib/trello/checklist.rb +82 -0
- data/lib/trello/client.rb +49 -0
- data/lib/trello/has_actions.rb +9 -0
- data/lib/trello/item.rb +18 -0
- data/lib/trello/item_state.rb +23 -0
- data/lib/trello/label.rb +19 -0
- data/lib/trello/list.rb +71 -0
- data/lib/trello/member.rb +93 -0
- data/lib/trello/multi_association.rb +10 -0
- data/lib/trello/net.rb +37 -0
- data/lib/trello/notification.rb +48 -0
- data/lib/trello/organization.rb +47 -0
- data/lib/trello/string.rb +36 -0
- data/lib/trello/token.rb +24 -0
- data/lib/trello.rb +83 -0
- data/spec/action_spec.rb +71 -0
- data/spec/basic_auth_policy_spec.rb +56 -0
- data/spec/board_spec.rb +196 -0
- data/spec/card_spec.rb +213 -0
- data/spec/checklist_spec.rb +50 -0
- data/spec/client_spec.rb +131 -0
- data/spec/integration/how_to_authorize_spec.rb +53 -0
- data/spec/integration/how_to_use_boards_spec.rb +48 -0
- data/spec/integration/integration_test.rb +40 -0
- data/spec/item_spec.rb +27 -0
- data/spec/item_state_spec.rb +0 -0
- data/spec/list_spec.rb +50 -0
- data/spec/member_spec.rb +92 -0
- data/spec/notification_spec.rb +83 -0
- data/spec/oauth_policy_spec.rb +93 -0
- data/spec/organization_spec.rb +26 -0
- data/spec/spec_helper.rb +244 -0
- data/spec/string_spec.rb +50 -0
- data/spec/token_spec.rb +33 -0
- metadata +220 -0
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Ruby Trello API
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/jeremytregunna/ruby-trello.png)](http://travis-ci.org/jeremytregunna/ruby-trello) [![Dependency Status](https://gemnasium.com/jeremytregunna/ruby-trello.png)](https://gemnasium.com/jeremytregunna/ruby-trello.png)
|
4
|
+
|
5
|
+
This library implements the [Trello](http://www.trello.com/) [API](http://trello.com/api).
|
6
|
+
|
7
|
+
Trello is an awesome tool for organization. Not just aimed at developers, but everybody.
|
8
|
+
Seriously, [check it out](http://www.trello.com/).
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
```
|
13
|
+
# gem install ruby-trello
|
14
|
+
```
|
15
|
+
|
16
|
+
Full Disclosure: This library is mostly complete, if you do find anything missing or not functioning as you expect it
|
17
|
+
to, please [let us know](https://trello.com/card/spot-a-bug-report-it/4f092b2ee23cb6fe6d1aaabd/17).
|
18
|
+
|
19
|
+
## Special thanks
|
20
|
+
|
21
|
+
A special thanks goes out to [Ben Biddington](https://github.com/ben-biddington) who has contributed a significant amount
|
22
|
+
of refactoring and functionality to be deserving of a beer and this special thanks.
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
Several ways you can contribute. Documentation, code, tests, feature requests, bug reports.
|
27
|
+
|
28
|
+
We develop ruby-trello using [Trello itself](https://trello.com/board/ruby-trello/4f092b2ee23cb6fe6d1aaabd).
|
29
|
+
|
30
|
+
Pick up an editor, fix a test! (If you want to, of course.) Send a pull
|
31
|
+
request, and I'll look at it. I only ask a few things:
|
32
|
+
|
33
|
+
1. Feature branches please!
|
34
|
+
2. Adding or refactoring existing features, ensure there are tests.
|
35
|
+
|
36
|
+
Also, don't be afraid to send a pull request if your changes aren't done. Just
|
37
|
+
let me know.
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Trello
|
2
|
+
# Action represents some event that occurred. For instance, when a card is created.
|
3
|
+
class Action < BasicData
|
4
|
+
register_attributes :id, :type, :data, :date, :member_creator_id,
|
5
|
+
:readonly => [ :id, :type, :data, :date, :member_creator_id ]
|
6
|
+
validates_presence_of :id, :type, :date, :member_creator_id
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Locate a specific action and return a new Action object.
|
10
|
+
def find(id)
|
11
|
+
super(:actions, id)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Update the attributes of an action
|
16
|
+
#
|
17
|
+
# Supply a hash of string keyed data retrieved from the Trello API representing
|
18
|
+
# an Action.
|
19
|
+
def update_fields(fields)
|
20
|
+
attributes[:id] = fields['id']
|
21
|
+
attributes[:type] = fields['type']
|
22
|
+
attributes[:data] = fields['data']
|
23
|
+
attributes[:date] = Time.iso8601(fields['date'])
|
24
|
+
attributes[:member_creator_id] = fields['idMemberCreator']
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the board this action occurred on.
|
29
|
+
def board
|
30
|
+
Client.get("/actions/#{id}/board").json_into(Board)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the card the action occurred on.
|
34
|
+
def card
|
35
|
+
Client.get("/actions/#{id}/card").json_into(Card)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the list the action occurred on.
|
39
|
+
def list
|
40
|
+
Client.get("/actions/#{id}/list").json_into(List)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the member who created the action.
|
44
|
+
one :member_creator, :via => Member, :using => :member_creator_id
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module Trello
|
4
|
+
class AssociationProxy
|
5
|
+
alias :proxy_extend :extend
|
6
|
+
|
7
|
+
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
|
8
|
+
|
9
|
+
delegate :owner, :target, :to => :@association
|
10
|
+
delegate :count, :to => :@association
|
11
|
+
|
12
|
+
def initialize(association)
|
13
|
+
@association = association
|
14
|
+
Array(association.options[:extend]).each { |ext| proxy_extend(ext) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def proxy_assocation
|
18
|
+
@association
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(method, *args, &block)
|
22
|
+
if target.respond_to? method
|
23
|
+
target.send(method, *args, &block)
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ===(other)
|
30
|
+
other === target
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ary
|
34
|
+
proxy_assocation.dup
|
35
|
+
end
|
36
|
+
alias_method :to_a, :to_ary
|
37
|
+
|
38
|
+
def <<(*records)
|
39
|
+
proxy_assocation.concat(records) && self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require "oauth"
|
3
|
+
|
4
|
+
module Trello
|
5
|
+
module Authorization
|
6
|
+
|
7
|
+
AuthPolicy = Class.new
|
8
|
+
|
9
|
+
class BasicAuthPolicy
|
10
|
+
class << self
|
11
|
+
attr_accessor :developer_public_key, :member_token
|
12
|
+
|
13
|
+
def authorize(request)
|
14
|
+
the_uri = Addressable::URI.parse(request.uri)
|
15
|
+
existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
|
16
|
+
new_values = { :key => @developer_public_key, :token => @member_token }
|
17
|
+
the_uri.query_values = new_values.merge existing_values
|
18
|
+
|
19
|
+
Request.new request.verb, the_uri, request.headers
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Clock
|
25
|
+
def self.timestamp; Time.now.to_i; end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Nonce
|
29
|
+
def self.next
|
30
|
+
SecureRandom.hex()
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
OAuthCredential = Struct.new "OAuthCredential", :key, :secret
|
35
|
+
|
36
|
+
# Handles the OAuth connectivity to Trello.
|
37
|
+
#
|
38
|
+
# For 2-legged OAuth, do the following:
|
39
|
+
#
|
40
|
+
# OAuthPolicy.consumer_credential = OAuthCredential.new "public_key", "secret"
|
41
|
+
# OAuthPolicy.token = OAuthCredential.new "token_key", nil
|
42
|
+
#
|
43
|
+
# For 3-legged OAuth, do the following:
|
44
|
+
#
|
45
|
+
# OAuthPolicy.consumer_credential = OAuthCredential.new "public_key", "secret"
|
46
|
+
# OAuthPolicy.return_url = "http://your.site.com/path/to/receive/post"
|
47
|
+
# OAuthPolicy.callback = Proc.new do |request_token|
|
48
|
+
# DB.save(request_token.key, request_token.secret)
|
49
|
+
# redirect_to request_token.authorize_url
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Then, recreate the request token given the request token key and secret you saved earlier,
|
53
|
+
# and the consumer, and pass that RequestToken instance the #get_access_token method, and
|
54
|
+
# store that in OAuthPolicy.token as a OAuthCredential.
|
55
|
+
class OAuthPolicy
|
56
|
+
class << self
|
57
|
+
attr_accessor :consumer_credential, :token, :return_url, :callback
|
58
|
+
|
59
|
+
def authorize(request)
|
60
|
+
unless consumer_credential
|
61
|
+
Trello.logger.error "The consumer_credential has not been supplied."
|
62
|
+
fail "The consumer_credential has not been supplied."
|
63
|
+
end
|
64
|
+
|
65
|
+
if token
|
66
|
+
request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
|
67
|
+
request
|
68
|
+
else
|
69
|
+
consumer(:return_url => return_url, :callback_method => :postMessage)
|
70
|
+
request_token = consumer.get_request_token
|
71
|
+
callback.call request_token
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def consumer_params(params = {})
|
79
|
+
{
|
80
|
+
:scheme => :header,
|
81
|
+
:scope => 'read,write,account',
|
82
|
+
:http_method => :get,
|
83
|
+
:request_token_path => "https://trello.com/1/OAuthGetRequestToken",
|
84
|
+
:authorize_path => "https://trello.com/1/OAuthAuthorizeToken",
|
85
|
+
:access_token_path => "https://trello.com/1/OAuthGetAccessToken"
|
86
|
+
}.merge!(params)
|
87
|
+
end
|
88
|
+
|
89
|
+
def consumer(options = {})
|
90
|
+
@consumer ||= OAuth::Consumer.new(
|
91
|
+
consumer_credential.key,
|
92
|
+
consumer_credential.secret,
|
93
|
+
consumer_params(options)
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_auth_header(url, verb, options = {})
|
98
|
+
self.token ||= OAuththCredential.new
|
99
|
+
|
100
|
+
request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
|
101
|
+
|
102
|
+
consumer.options[:signature_method] = 'HMAC-SHA1'
|
103
|
+
consumer.options[:nonce] = Nonce.next
|
104
|
+
consumer.options[:timestamp] = Clock.timestamp
|
105
|
+
consumer.options[:uri] = url
|
106
|
+
|
107
|
+
consumer.sign!(request, OAuth::Token.new(token.key, token.secret))
|
108
|
+
|
109
|
+
request['authorization']
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'trello/string'
|
2
|
+
|
3
|
+
module Trello
|
4
|
+
class BasicData
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include ActiveModel::Dirty
|
7
|
+
include ActiveModel::Serializers::JSON
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def find(path, id)
|
11
|
+
Client.get("/#{path}/#{id}").json_into(self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.register_attributes(*names)
|
16
|
+
options = { :readonly => [] }
|
17
|
+
options.merge!(names.pop) if names.last.kind_of? Hash
|
18
|
+
|
19
|
+
# Defines the attribute getter and setters.
|
20
|
+
class_eval do
|
21
|
+
define_method :attributes do
|
22
|
+
@attributes ||= names.inject({}) { |hash,k| hash.merge(k.to_sym => nil) }
|
23
|
+
end
|
24
|
+
|
25
|
+
names.each do |key|
|
26
|
+
define_method(:"#{key}") { @attributes[key] }
|
27
|
+
|
28
|
+
unless options[:readonly].include?(key.to_sym)
|
29
|
+
define_method :"#{key}=" do |val|
|
30
|
+
send(:"#{key}_will_change!") unless val == @attributes[key]
|
31
|
+
@attributes[key] = val
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
define_attribute_methods names
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.one(name, opts = {})
|
41
|
+
class_eval do
|
42
|
+
define_method(:"#{name}") do |*args|
|
43
|
+
options = opts.dup
|
44
|
+
klass = options.delete(:via) || Trello.const_get(name.to_s.camelize)
|
45
|
+
ident = options.delete(:using) || :id
|
46
|
+
klass.find(self.send(ident))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.many(name, opts = {})
|
52
|
+
class_eval do
|
53
|
+
define_method(:"#{name}") do |*args|
|
54
|
+
options = opts.dup
|
55
|
+
resource = options.delete(:in) || self.class.to_s.split("::").last.downcase.pluralize
|
56
|
+
klass = options.delete(:via) || Trello.const_get(name.to_s.singularize.camelize)
|
57
|
+
params = options.merge(args[0] || {})
|
58
|
+
resources = Client.get("/#{resource}/#{id}/#{name}", options).json_into(klass)
|
59
|
+
MultiAssociation.new(self, resources).proxy
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
register_attributes :id, :readonly => [ :id ]
|
65
|
+
|
66
|
+
def initialize(fields = {})
|
67
|
+
update_fields(fields)
|
68
|
+
end
|
69
|
+
|
70
|
+
def update_fields(fields)
|
71
|
+
raise NotImplementedError, "#{self.class} does not implement update_fields."
|
72
|
+
end
|
73
|
+
|
74
|
+
# Refresh the contents of our object.
|
75
|
+
def refresh!
|
76
|
+
self.class.find(id)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Two objects are equal if their _id_ methods are equal.
|
80
|
+
def ==(other)
|
81
|
+
id == other.id
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/trello/board.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Trello
|
2
|
+
class Board < BasicData
|
3
|
+
register_attributes :id, :name, :description, :closed, :url, :organization_id, :prefs,
|
4
|
+
:readonly => [ :id, :url, :organization_id, :prefs ]
|
5
|
+
validates_presence_of :id, :name
|
6
|
+
validates_length_of :name, :in => 1..16384
|
7
|
+
validates_length_of :description, :maximum => 16384
|
8
|
+
|
9
|
+
include HasActions
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Finds a board.
|
13
|
+
def find(id)
|
14
|
+
super(:boards, id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(fields)
|
18
|
+
new('name' => fields[:name],
|
19
|
+
'desc' => fields[:description],
|
20
|
+
'closed' => fields[:closed] || false).save
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def save
|
25
|
+
return update! if id
|
26
|
+
|
27
|
+
fields = { :name => name }
|
28
|
+
fields.merge!(:desc => description) if description
|
29
|
+
fields.merge!(:idOrganization => organization_id) if organization_id
|
30
|
+
|
31
|
+
Client.post("/boards", fields).json_into(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def update!
|
35
|
+
fail "Cannot save new instance." unless self.id
|
36
|
+
|
37
|
+
@previously_changed = changes
|
38
|
+
@changed_attributes.clear
|
39
|
+
|
40
|
+
Client.put("/boards/#{self.id}/", {
|
41
|
+
:name => @name,
|
42
|
+
:description => @description,
|
43
|
+
:closed => @closed
|
44
|
+
}).json_into(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_fields(fields)
|
48
|
+
attributes[:id] = fields['id'] if fields['id']
|
49
|
+
attributes[:name] = fields['name'] if fields['name']
|
50
|
+
attributes[:description] = fields['desc'] if fields['desc']
|
51
|
+
attributes[:closed] = fields['closed'] if fields.has_key?('closed')
|
52
|
+
attributes[:url] = fields['url'] if fields['url']
|
53
|
+
attributes[:organization_id] = fields['idOrganization'] if fields['idOrganization']
|
54
|
+
attributes[:prefs] = fields['prefs'] || {}
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def closed?
|
59
|
+
attributes[:closed]
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_lists?
|
63
|
+
lists.size > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return all the cards on this board.
|
67
|
+
#
|
68
|
+
# This method, when called, can take a hash table with a filter key containing any
|
69
|
+
# of the following values:
|
70
|
+
# :filter => [ :none, :open, :closed, :all ] # default :open
|
71
|
+
many :cards, :filter => :open
|
72
|
+
|
73
|
+
# Returns all the lists on this board.
|
74
|
+
#
|
75
|
+
# This method, when called, can take a hash table with a filter key containing any
|
76
|
+
# of the following values:
|
77
|
+
# :filter => [ :none, :open, :closed, :all ] # default :open
|
78
|
+
many :lists, :filter => :open
|
79
|
+
|
80
|
+
# Returns an array of members who are associated with this board.
|
81
|
+
#
|
82
|
+
# This method, when called, can take a hash table with a filter key containing any
|
83
|
+
# of the following values:
|
84
|
+
# :filter => [ :none, :normal, :owners, :all ] # default :all
|
85
|
+
many :members, :filter => :all
|
86
|
+
|
87
|
+
# Returns a reference to the organization this board belongs to.
|
88
|
+
one :organization, :using => :organization_id
|
89
|
+
|
90
|
+
# :nodoc:
|
91
|
+
def request_prefix
|
92
|
+
"/boards/#{id}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/trello/card.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module Trello
|
2
|
+
# A Card is a container that can house checklists and comments; it resides inside a List.
|
3
|
+
class Card < BasicData
|
4
|
+
register_attributes :id, :short_id, :name, :description, :due, :closed, :url, :board_id, :member_ids, :list_id,
|
5
|
+
:readonly => [ :id, :short_id, :url, :board_id, :member_ids ]
|
6
|
+
validates_presence_of :id, :name, :list_id
|
7
|
+
validates_length_of :name, :in => 1..16384
|
8
|
+
validates_length_of :description, :in => 0..16384
|
9
|
+
|
10
|
+
include HasActions
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Find a specific card by its id.
|
14
|
+
def find(id)
|
15
|
+
super(:cards, id)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create a new card and save it on Trello.
|
19
|
+
def create(options)
|
20
|
+
new('name' => options[:name],
|
21
|
+
'idList' => options[:list_id],
|
22
|
+
'desc' => options[:description]).save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Update the fields of a card.
|
27
|
+
#
|
28
|
+
# Supply a hash of string keyed data retrieved from the Trello API representing
|
29
|
+
# a card.
|
30
|
+
def update_fields(fields)
|
31
|
+
attributes[:id] = fields['id']
|
32
|
+
attributes[:short_id] = fields['idShort']
|
33
|
+
attributes[:name] = fields['name']
|
34
|
+
attributes[:description] = fields['desc']
|
35
|
+
attributes[:due] = Time.iso8601(fields['due']) rescue nil
|
36
|
+
attributes[:closed] = fields['closed']
|
37
|
+
attributes[:url] = fields['url']
|
38
|
+
attributes[:board_id] = fields['idBoard']
|
39
|
+
attributes[:member_ids] = fields['idMembers']
|
40
|
+
attributes[:list_id] = fields['idList']
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a reference to the board this card is part of.
|
45
|
+
one :board, :using => :board_id
|
46
|
+
|
47
|
+
# Returns a list of checklists associated with the card.
|
48
|
+
#
|
49
|
+
# The options hash may have a filter key which can have its value set as any
|
50
|
+
# of the following values:
|
51
|
+
# :filter => [ :none, :all ] # default :all
|
52
|
+
many :checklists, :filter => :all
|
53
|
+
|
54
|
+
# Returns a reference to the list this card is currently in.
|
55
|
+
one :list, :using => :list_id
|
56
|
+
|
57
|
+
# Returns a list of members who are assigned to this card.
|
58
|
+
def members
|
59
|
+
members = member_ids.map do |member_id|
|
60
|
+
Client.get("/members/#{member_id}").json_into(Member)
|
61
|
+
end
|
62
|
+
MultiAssociation.new(self, members).proxy
|
63
|
+
end
|
64
|
+
|
65
|
+
# Saves a record.
|
66
|
+
def save
|
67
|
+
# If we have an id, just update our fields.
|
68
|
+
return update! if id
|
69
|
+
|
70
|
+
Client.post("/cards", {
|
71
|
+
:name => name,
|
72
|
+
:desc => description,
|
73
|
+
:idList => list_id
|
74
|
+
}).json_into(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Update an existing record.
|
78
|
+
# Warning, this updates all fields using values already in memory. If
|
79
|
+
# an external resource has updated these fields, you should refresh!
|
80
|
+
# this object before making your changes, and before updating the record.
|
81
|
+
def update!
|
82
|
+
@previously_changed = changes
|
83
|
+
@changed_attributes.clear
|
84
|
+
|
85
|
+
Client.put("/cards/#{id}", {
|
86
|
+
:name => name,
|
87
|
+
:desc => description,
|
88
|
+
:due => due && due.utc.iso8601,
|
89
|
+
:closed => closed,
|
90
|
+
:idList => list_id,
|
91
|
+
:idBoard => board_id,
|
92
|
+
:idMembers => member_ids
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is the record valid?
|
97
|
+
def valid?
|
98
|
+
name && list_id
|
99
|
+
end
|
100
|
+
|
101
|
+
# Add a comment with the supplied text.
|
102
|
+
def add_comment(text)
|
103
|
+
Client.post("/cards/#{id}/actions/comments", :text => text)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add a checklist to this card
|
107
|
+
def add_checklist(checklist)
|
108
|
+
Client.post("/cards/#{id}/checklists", {
|
109
|
+
:value => checklist.id
|
110
|
+
})
|
111
|
+
end
|
112
|
+
|
113
|
+
# Add a member to this card
|
114
|
+
def add_member(member)
|
115
|
+
Client.post("/cards/#{id}/members", {
|
116
|
+
:value => member.id
|
117
|
+
})
|
118
|
+
end
|
119
|
+
|
120
|
+
# Remove a member from this card
|
121
|
+
def remove_member(member)
|
122
|
+
Client.delete("/cards/#{id}/members/#{member.id}")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Move this card to the given list
|
126
|
+
def move_to_list(list)
|
127
|
+
Client.put("/cards/#{id}/idList", {
|
128
|
+
:value => list.id
|
129
|
+
})
|
130
|
+
end
|
131
|
+
|
132
|
+
# Retrieve a list of labels
|
133
|
+
def labels
|
134
|
+
labels = Client.get("/cards/#{id}/labels").json_into(Label)
|
135
|
+
MultiAssociation.new(self, labels).proxy
|
136
|
+
end
|
137
|
+
|
138
|
+
# Add a label
|
139
|
+
def add_label(colour)
|
140
|
+
unless %w{green yellow orange red purple blue}.include? colour
|
141
|
+
errors.add(:label, "colour '#{colour}' does not exist")
|
142
|
+
return Trello.logger.warn "The label colour '#{colour}' does not exist."
|
143
|
+
end
|
144
|
+
Client.post("/cards/#{id}/labels", { :value => colour })
|
145
|
+
end
|
146
|
+
|
147
|
+
# Remove a label
|
148
|
+
def remove_label(colour)
|
149
|
+
unless %w{green yellow orange red purple blue}.include? colour
|
150
|
+
errors.add(:label, "colour '#{colour}' does not exist")
|
151
|
+
return Trello.logger.warn "The label colour '#{colour}' does not exist." unless %w{green yellow orange red purple blue}.include? colour
|
152
|
+
end
|
153
|
+
Client.delete("/cards/#{id}/labels/#{colour}")
|
154
|
+
end
|
155
|
+
|
156
|
+
# :nodoc:
|
157
|
+
def request_prefix
|
158
|
+
"/cards/#{id}"
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Trello
|
2
|
+
# A Checklist holds items which are like a "task" list. Checklists are linked to a card.
|
3
|
+
class Checklist < BasicData
|
4
|
+
register_attributes :id, :name, :description, :closed, :url, :check_items, :board_id, :list_id, :member_ids,
|
5
|
+
:readonly => [ :id, :description, :closed, :url, :check_items, :board_id, :list_id, :member_ids ]
|
6
|
+
validates_presence_of :id, :board_id, :list_id
|
7
|
+
validates_length_of :name, :in => 1..16384
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Locate a specific checklist by its id.
|
11
|
+
def find(id)
|
12
|
+
super(:checklists, id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(options)
|
16
|
+
new('name' => options[:name],
|
17
|
+
'idBoard' => options[:board_id]).save
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Update the fields of a checklist.
|
22
|
+
#
|
23
|
+
# Supply a hash of string keyed data retrieved from the Trello API representing
|
24
|
+
# a checklist.
|
25
|
+
def update_fields(fields)
|
26
|
+
attributes[:id] = fields['id']
|
27
|
+
attributes[:name] = fields['name']
|
28
|
+
attributes[:description] = fields['desc']
|
29
|
+
attributes[:closed] = fields['closed']
|
30
|
+
attributes[:url] = fields['url']
|
31
|
+
attributes[:check_items] = fields['checkItems']
|
32
|
+
attributes[:board_id] = fields['idBoard']
|
33
|
+
attributes[:member_ids] = fields['idMembers']
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Check if the checklist is currently active.
|
38
|
+
def closed?
|
39
|
+
closed
|
40
|
+
end
|
41
|
+
|
42
|
+
# Save a record.
|
43
|
+
def save
|
44
|
+
return update! if id
|
45
|
+
|
46
|
+
Client.post("/checklists", {
|
47
|
+
:name => name,
|
48
|
+
:idBoard => board_id
|
49
|
+
}).json_into(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def update!
|
53
|
+
Client.put("/checklists/#{id}", { :name => name }).json_into(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return a list of items on the checklist.
|
57
|
+
def items
|
58
|
+
check_items.map do |item_fields|
|
59
|
+
Item.new(item_fields)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return a reference to the board the checklist is on.
|
64
|
+
one :board, :using => :board_id
|
65
|
+
|
66
|
+
# Return a reference to the list the checklist is on.
|
67
|
+
one :list, :using => :list_id
|
68
|
+
|
69
|
+
# Return a list of members active in this checklist.
|
70
|
+
def members
|
71
|
+
members = member_ids.map do |member_id|
|
72
|
+
Member.find(member_id)
|
73
|
+
end
|
74
|
+
MultiAssociation.new(self, members).proxy
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add an item to the checklist
|
78
|
+
def add_item(name)
|
79
|
+
Client.post("/checklists/#{id}/checkItems", { :name => name })
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|