ruby-trello 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ruby Trello API
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/jeremytregunna/ruby-trello.png)](http://travis-ci.org/jeremytregunna/ruby-trello)
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
4
 
5
5
  This library implements the [Trello](http://www.trello.com/) [API](http://trello.com/api).
6
6
 
@@ -13,7 +13,8 @@ Seriously, [check it out](http://www.trello.com/).
13
13
  # gem install ruby-trello
14
14
  ```
15
15
 
16
- Full Disclosure: This library is not complete, but it does function enough to be useful.
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).
17
18
 
18
19
  ## Special thanks
19
20
 
data/lib/trello/action.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  module Trello
2
2
  # Action represents some event that occurred. For instance, when a card is created.
3
3
  class Action < BasicData
4
- register_attributes :id, :type, :data, :date, :member_creator_id
4
+ register_attributes :id, :type, :data, :date, :member_creator_id,
5
+ :readonly => [ :id, :type, :data, :date, :member_creator_id ]
5
6
  validates_presence_of :id, :type, :date, :member_creator_id
6
7
 
7
8
  class << self
@@ -19,7 +20,7 @@ module Trello
19
20
  attributes[:id] = fields['id']
20
21
  attributes[:type] = fields['type']
21
22
  attributes[:data] = fields['data']
22
- attributes[:date] = fields['date']
23
+ attributes[:date] = Time.iso8601(fields['date'])
23
24
  attributes[:member_creator_id] = fields['idMemberCreator']
24
25
  self
25
26
  end
@@ -40,8 +41,6 @@ module Trello
40
41
  end
41
42
 
42
43
  # Returns the member who created the action.
43
- def member_creator
44
- Member.find(member_creator_id)
45
- end
44
+ one :member_creator, :via => Member, :using => :member_creator_id
46
45
  end
47
46
  end
@@ -0,0 +1,11 @@
1
+ module Trello
2
+ class Association
3
+ attr_reader :owner, :target, :options, :proxy
4
+
5
+ def initialize(owner, target)
6
+ @owner = owner
7
+ @target = target
8
+ @options = {}
9
+ end
10
+ end
11
+ 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
@@ -1,4 +1,5 @@
1
1
  require 'securerandom'
2
+ require "oauth"
2
3
 
3
4
  module Trello
4
5
  module Authorization
@@ -32,9 +33,28 @@ module Trello
32
33
 
33
34
  OAuthCredential = Struct.new "OAuthCredential", :key, :secret
34
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.
35
55
  class OAuthPolicy
36
56
  class << self
37
- attr_accessor :consumer_credential, :token
57
+ attr_accessor :consumer_credential, :token, :return_url, :callback
38
58
 
39
59
  def authorize(request)
40
60
  unless consumer_credential
@@ -42,26 +62,40 @@ module Trello
42
62
  fail "The consumer_credential has not been supplied."
43
63
  end
44
64
 
45
- request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
46
- request
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
47
74
  end
48
75
 
49
76
  private
50
77
 
51
- def get_auth_header(url, verb)
52
- require "oauth"
53
-
54
- self.token ||= OAuthCredential.new
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
55
88
 
56
- consumer = OAuth::Consumer.new(
89
+ def consumer(options = {})
90
+ @consumer ||= OAuth::Consumer.new(
57
91
  consumer_credential.key,
58
92
  consumer_credential.secret,
59
- {
60
- :scheme => :header,
61
- :scope => 'read,write,account',
62
- :http_method => verb
63
- }
93
+ consumer_params(options)
64
94
  )
95
+ end
96
+
97
+ def get_auth_header(url, verb, options = {})
98
+ self.token ||= OAuththCredential.new
65
99
 
66
100
  request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
67
101
 
@@ -13,6 +13,9 @@ module Trello
13
13
  end
14
14
 
15
15
  def self.register_attributes(*names)
16
+ options = { :readonly => [] }
17
+ options.merge!(names.pop) if names.last.kind_of? Hash
18
+
16
19
  # Defines the attribute getter and setters.
17
20
  class_eval do
18
21
  define_method :attributes do
@@ -22,16 +25,43 @@ module Trello
22
25
  names.each do |key|
23
26
  define_method(:"#{key}") { @attributes[key] }
24
27
 
25
- define_method :"#{key}=" do |val|
26
- send(:"#{key}_will_change!") unless val == @attributes[key]
27
- @attributes[key] = val
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
28
33
  end
29
34
  end
35
+
30
36
  define_attribute_methods names
31
37
  end
32
38
  end
33
39
 
34
- register_attributes :id
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 ]
35
65
 
36
66
  def initialize(fields = {})
37
67
  update_fields(fields)
data/lib/trello/board.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Trello
2
-
3
2
  class Board < BasicData
4
- register_attributes :id, :name, :description, :url, :organization_id
3
+ register_attributes :id, :name, :description, :closed, :url, :organization_id, :prefs,
4
+ :readonly => [ :id, :url, :organization_id, :prefs ]
5
5
  validates_presence_of :id, :name
6
+ validates_length_of :name, :in => 1..16384
7
+ validates_length_of :description, :maximum => 16384
6
8
 
7
9
  include HasActions
8
10
 
@@ -49,49 +51,41 @@ module Trello
49
51
  attributes[:closed] = fields['closed'] if fields.has_key?('closed')
50
52
  attributes[:url] = fields['url'] if fields['url']
51
53
  attributes[:organization_id] = fields['idOrganization'] if fields['idOrganization']
52
-
54
+ attributes[:prefs] = fields['prefs'] || {}
53
55
  self
54
56
  end
55
57
 
56
58
  def closed?
57
- @attributes[:closed]
59
+ attributes[:closed]
60
+ end
61
+
62
+ def has_lists?
63
+ lists.size > 0
58
64
  end
59
65
 
60
66
  # Return all the cards on this board.
61
67
  #
62
- # The options hash may have a filter key which can have its value set as any
68
+ # This method, when called, can take a hash table with a filter key containing any
63
69
  # of the following values:
64
70
  # :filter => [ :none, :open, :closed, :all ] # default :open
65
- def cards(options = { :filter => :open })
66
- Client.get("/boards/#{id}/cards").json_into(Card)
67
- end
68
-
69
- def has_lists?
70
- lists.size > 0
71
- end
71
+ many :cards, :filter => :open
72
72
 
73
73
  # Returns all the lists on this board.
74
74
  #
75
- # The options hash may have a filter key which can have its value set as any
75
+ # This method, when called, can take a hash table with a filter key containing any
76
76
  # of the following values:
77
77
  # :filter => [ :none, :open, :closed, :all ] # default :open
78
- def lists(options = { :filter => :open })
79
- Client.get("/boards/#{id}/lists", options).json_into(List)
80
- end
78
+ many :lists, :filter => :open
81
79
 
82
80
  # Returns an array of members who are associated with this board.
83
81
  #
84
- # The options hash may have a filter key which can have its value set as any
82
+ # This method, when called, can take a hash table with a filter key containing any
85
83
  # of the following values:
86
84
  # :filter => [ :none, :normal, :owners, :all ] # default :all
87
- def members(options = { :filter => :all })
88
- Client.get("/boards/#{id}/members", options).json_into(Member)
89
- end
85
+ many :members, :filter => :all
90
86
 
91
87
  # Returns a reference to the organization this board belongs to.
92
- def organization
93
- Organization.find(organization_id)
94
- end
88
+ one :organization, :using => :organization_id
95
89
 
96
90
  # :nodoc:
97
91
  def request_prefix
data/lib/trello/card.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  module Trello
2
2
  # A Card is a container that can house checklists and comments; it resides inside a List.
3
3
  class Card < BasicData
4
- register_attributes :id, :short_id, :name, :description, :closed, :url, :board_id, :member_ids, :list_id
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 ]
5
6
  validates_presence_of :id, :name, :list_id
7
+ validates_length_of :name, :in => 1..16384
8
+ validates_length_of :description, :in => 0..16384
6
9
 
7
10
  include HasActions
8
11
 
@@ -29,6 +32,7 @@ module Trello
29
32
  attributes[:short_id] = fields['idShort']
30
33
  attributes[:name] = fields['name']
31
34
  attributes[:description] = fields['desc']
35
+ attributes[:due] = Time.iso8601(fields['due']) rescue nil
32
36
  attributes[:closed] = fields['closed']
33
37
  attributes[:url] = fields['url']
34
38
  attributes[:board_id] = fields['idBoard']
@@ -38,29 +42,24 @@ module Trello
38
42
  end
39
43
 
40
44
  # Returns a reference to the board this card is part of.
41
- def board
42
- Board.find(board_id)
43
- end
45
+ one :board, :using => :board_id
44
46
 
45
47
  # Returns a list of checklists associated with the card.
46
48
  #
47
49
  # The options hash may have a filter key which can have its value set as any
48
50
  # of the following values:
49
51
  # :filter => [ :none, :all ] # default :all
50
- def checklists(options = { :filter => :all })
51
- Client.get("/cards/#{id}/checklists", options).json_into(Checklist)
52
- end
52
+ many :checklists, :filter => :all
53
53
 
54
54
  # Returns a reference to the list this card is currently in.
55
- def list
56
- List.find(list_id)
57
- end
55
+ one :list, :using => :list_id
58
56
 
59
57
  # Returns a list of members who are assigned to this card.
60
58
  def members
61
- member_ids.map do |member_id|
59
+ members = member_ids.map do |member_id|
62
60
  Client.get("/members/#{member_id}").json_into(Member)
63
61
  end
62
+ MultiAssociation.new(self, members).proxy
64
63
  end
65
64
 
66
65
  # Saves a record.
@@ -83,14 +82,15 @@ module Trello
83
82
  @previously_changed = changes
84
83
  @changed_attributes.clear
85
84
 
86
- Client.put("/cards/#{@id}", {
85
+ Client.put("/cards/#{id}", {
87
86
  :name => name,
88
87
  :desc => description,
88
+ :due => due && due.utc.iso8601,
89
89
  :closed => closed,
90
90
  :idList => list_id,
91
91
  :idBoard => board_id,
92
92
  :idMembers => member_ids
93
- }).json_into(self)
93
+ })
94
94
  end
95
95
 
96
96
  # Is the record valid?
@@ -112,13 +112,19 @@ module Trello
112
112
 
113
113
  # Add a label
114
114
  def add_label(colour)
115
- return logger.warn "The label colour '#{colour}' does not exist." unless %w{green yellow orange red purple blue}.include? colour
115
+ unless %w{green yellow orange red purple blue}.include? colour
116
+ errors.add(:label, "colour '#{colour}' does not exist")
117
+ return Trello.logger.warn "The label colour '#{colour}' does not exist."
118
+ end
116
119
  Client.post("/cards/#{id}/labels", { :value => colour })
117
120
  end
118
121
 
119
122
  # Remove a label
120
123
  def remove_label(colour)
121
- return logger.warn "The label colour '#{colour}' does not exist." unless %w{green yellow orange red purple blue}.include? colour
124
+ unless %w{green yellow orange red purple blue}.include? colour
125
+ errors.add(:label, "colour '#{colour}' does not exist")
126
+ return Trello.logger.warn "The label colour '#{colour}' does not exist." unless %w{green yellow orange red purple blue}.include? colour
127
+ end
122
128
  Client.delete("/cards/#{id}/labels/#{colour}")
123
129
  end
124
130
 
@@ -1,8 +1,10 @@
1
1
  module Trello
2
2
  # A Checklist holds items which are like a "task" list. Checklists are linked to a card.
3
3
  class Checklist < BasicData
4
- register_attributes :id, :name, :description, :closed, :url, :check_items, :board_id, :list_id, :member_ids
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 ]
5
6
  validates_presence_of :id, :board_id, :list_id
7
+ validates_length_of :name, :in => 1..16384
6
8
 
7
9
  class << self
8
10
  # Locate a specific checklist by its id.
@@ -42,13 +44,13 @@ module Trello
42
44
  return update! if id
43
45
 
44
46
  Client.post("/checklists", {
45
- :name => @name,
46
- :idBoard => @board_id
47
+ :name => name,
48
+ :idBoard => board_id
47
49
  }).json_into(self)
48
50
  end
49
51
 
50
52
  def update!
51
- Client.put("/checklists", { :name => @name }).json_into(self)
53
+ Client.put("/checklists", { :name => name }).json_into(self)
52
54
  end
53
55
 
54
56
  # Return a list of items on the checklist.
@@ -59,20 +61,17 @@ module Trello
59
61
  end
60
62
 
61
63
  # Return a reference to the board the checklist is on.
62
- def board
63
- Board.find(board_id)
64
- end
64
+ one :board, :using => :board_id
65
65
 
66
66
  # Return a reference to the list the checklist is on.
67
- def list
68
- List.find(list_id)
69
- end
67
+ one :list, :using => :list_id
70
68
 
71
69
  # Return a list of members active in this checklist.
72
70
  def members
73
- member_ids.map do |member_id|
71
+ members = member_ids.map do |member_id|
74
72
  Member.find(member_id)
75
73
  end
74
+ MultiAssociation.new(self, members).proxy
76
75
  end
77
76
 
78
77
  # Add an item to the checklist
data/lib/trello/client.rb CHANGED
@@ -30,6 +30,8 @@ module Trello
30
30
  request = Request.new name, uri, {}, body
31
31
  response = TInternet.execute AuthPolicy.authorize(request)
32
32
 
33
+ return '' unless response
34
+
33
35
  if response.code.to_i == 401 && response.body =~ /expired token/
34
36
  Trello.logger.error("[401 #{name.to_s.upcase} #{uri}]: Your access token has expired.")
35
37
  raise InvalidAccessToken, response.body
@@ -2,7 +2,8 @@ module Trello
2
2
  module HasActions
3
3
  # Returns a list of the actions associated with this object.
4
4
  def actions(options = {})
5
- Client.get("#{request_prefix}/actions", { :filter => :all }.merge(options)).json_into(Action)
5
+ actions = Client.get("#{request_prefix}/actions", { :filter => :all }.merge(options)).json_into(Action)
6
+ MultiAssociation.new(self, actions).proxy
6
7
  end
7
8
  end
8
9
  end
data/lib/trello/item.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Trello
2
2
  # An Item is a basic task that can be checked off and marked as completed.
3
3
  class Item < BasicData
4
- register_attributes :id, :name, :type
4
+ register_attributes :id, :name, :type, :readonly => [ :id, :name, :type ]
5
5
  validates_presence_of :id, :type
6
6
 
7
7
  # Updates the fields of an item.
@@ -1,7 +1,7 @@
1
1
  module Trello
2
2
  # Represents the state of an item.
3
3
  class ItemState < BasicData
4
- register_attributes :id, :state, :item_id
4
+ register_attributes :id, :state, :item_id, :readonly => [ :id, :state, :item_id ]
5
5
  validates_presence_of :id, :item_id
6
6
 
7
7
  # Update the fields of an item state.
data/lib/trello/list.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  module Trello
2
2
  # A List is a container which holds cards. Lists are items on a board.
3
3
  class List < BasicData
4
- register_attributes :id, :name, :closed, :board_id
4
+ register_attributes :id, :name, :closed, :board_id, :readonly => [ :id, :board_id ]
5
5
  validates_presence_of :id, :name, :board_id
6
+ validates_length_of :name, :in => 1..16384
6
7
 
7
8
  include HasActions
8
9
 
@@ -34,16 +35,16 @@ module Trello
34
35
  return update! if id
35
36
 
36
37
  Client.post("/lists", {
37
- :name => @name,
38
- :closed => @closed || false,
39
- :idBoard => @board_id
38
+ :name => name,
39
+ :closed => closed || false,
40
+ :idBoard => board_id
40
41
  }).json_into(self)
41
42
  end
42
43
 
43
44
  def update!
44
45
  Client.put("/lists", {
45
- :name => @name,
46
- :closed => @closed
46
+ :name => name,
47
+ :closed => closed
47
48
  }).json_into(self)
48
49
  end
49
50
 
@@ -53,18 +54,14 @@ module Trello
53
54
  end
54
55
 
55
56
  # Return the board the list is connected to.
56
- def board
57
- Board.find(board_id)
58
- end
57
+ one :board, :using => :board_id
59
58
 
60
59
  # Returns all the cards on this list.
61
60
  #
62
61
  # The options hash may have a filter key which can have its value set as any
63
62
  # of the following values:
64
63
  # :filter => [ :none, :open, :closed, :all ] # default :open
65
- def cards(options = { :filter => :open })
66
- Client.get("/lists/#{id}/cards", options).json_into(Card)
67
- end
64
+ many :cards, :filter => :open
68
65
 
69
66
  # :nodoc:
70
67
  def request_prefix
data/lib/trello/member.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Trello
2
2
  # A Member is a user of the Trello service.
3
3
  class Member < BasicData
4
- register_attributes :id, :username, :full_name, :avatar_id, :bio, :url
4
+ register_attributes :id, :username, :full_name, :avatar_id, :bio, :url, :readonly => [ :id, :username, :avatar_id, :url ]
5
5
  validates_presence_of :id, :username
6
+ validates_length_of :full_name, :minimum => 4
7
+ validates_length_of :bio, :maximum => 16384
6
8
 
7
9
  include HasActions
8
10
 
@@ -41,46 +43,51 @@ module Trello
41
43
 
42
44
  # Returns a list of the boards a member is a part of.
43
45
  #
44
- # The options hash may have a filter key which can have its value set as any
46
+ # This method, when called, can take a hash table with a filter key containing any
45
47
  # of the following values:
46
48
  # :filter => [ :none, :members, :organization, :public, :open, :closed, :all ] # default: :all
47
- def boards(options = { :filter => :all })
48
- Client.get("/members/#{username}/boards", options).json_into(Board)
49
- end
49
+ # i.e.,
50
+ # me.boards(:filter => :closed) # retrieves all closed boards
51
+ many :boards, :filter => :all
50
52
 
51
53
  # Returns a list of cards the member is assigned to.
52
54
  #
53
- # The options hash may have a filter key which can have its value set as any
55
+ # This method, when called, can take a hash table with a filter key containing any
54
56
  # of the following values:
55
57
  # :filter => [ :none, :open, :closed, :all ] # default :open
56
- def cards(options = { :filter => :open })
57
- Client.get("/members/#{username}/cards", options).json_into(Card)
58
- end
58
+ # i.e.,
59
+ # me.cards(:filter => :closed) # retrieves all closed cards
60
+ many :cards, :filter => :open
59
61
 
60
62
  # Returns a list of the organizations this member is a part of.
61
63
  #
62
- # The options hash may have a filter key which can have its value set as any
64
+ # This method, when called, can take a hash table with a filter key containing any
63
65
  # of the following values:
64
66
  # :filter => [ :none, :members, :public, :all ] # default: all
65
- def organizations(options = { :filter => :all })
66
- Client.get("/members/#{username}/organizations", options).json_into(Organization)
67
- end
67
+ # i.e.,
68
+ # me.organizations(:filter => :public) # retrieves all public organizations
69
+ many :organizations, :filter => :all
68
70
 
69
71
  # Returns a list of notifications for the user
70
- def notifications
71
- Client.get("/members/#{username}/notifications").json_into(Notification)
72
- end
72
+ many :notifications
73
73
 
74
74
  def save
75
75
  @previously_changed = changes
76
76
  @changed_attributes.clear
77
77
 
78
- # TODO: updating attributes.
78
+ return update! if id
79
+ end
80
+
81
+ def update!
82
+ Client.put(request_prefix, {
83
+ :displayName => full_name,
84
+ :bio => bio
85
+ }).json_into(self)
79
86
  end
80
87
 
81
88
  # :nodoc:
82
89
  def request_prefix
83
- "/members/#{username}"
90
+ "/members/#{id}"
84
91
  end
85
92
  end
86
93
  end
@@ -0,0 +1,10 @@
1
+ module Trello
2
+ class MultiAssociation < Association
3
+ delegate :count, :to => :target
4
+
5
+ def initialize(owner, target = [])
6
+ super
7
+ @proxy = AssociationProxy.new(self)
8
+ end
9
+ end
10
+ end