ruby-trello-wgibbs 0.4.3
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 +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
|
+
[](http://travis-ci.org/jeremytregunna/ruby-trello) [](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
|