dimelo_ccp_api 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +15 -0
  5. data/CHANGELOG.md +29 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +59 -0
  9. data/Rakefile +23 -0
  10. data/VERSION +1 -0
  11. data/dimelo_ccp_api.gemspec +26 -0
  12. data/examples/dimelo_api_test +34 -0
  13. data/gemfiles/Gemfile.activesupport-3.2.x +5 -0
  14. data/gemfiles/Gemfile.activesupport-4.0.x +5 -0
  15. data/gemfiles/Gemfile.activesupport-4.1.x +5 -0
  16. data/gemfiles/Gemfile.activesupport-head +5 -0
  17. data/lib/dimelo/ccp/api/basic_object.rb +17 -0
  18. data/lib/dimelo/ccp/api/client.rb +46 -0
  19. data/lib/dimelo/ccp/api/common/openable.rb +23 -0
  20. data/lib/dimelo/ccp/api/common/publishable.rb +23 -0
  21. data/lib/dimelo/ccp/api/common/starrable.rb +23 -0
  22. data/lib/dimelo/ccp/api/connection.rb +86 -0
  23. data/lib/dimelo/ccp/api/error.rb +71 -0
  24. data/lib/dimelo/ccp/api/lazzy_collection.rb +114 -0
  25. data/lib/dimelo/ccp/api/model/answer.rb +39 -0
  26. data/lib/dimelo/ccp/api/model/attachment.rb +44 -0
  27. data/lib/dimelo/ccp/api/model/category.rb +12 -0
  28. data/lib/dimelo/ccp/api/model/category_group.rb +12 -0
  29. data/lib/dimelo/ccp/api/model/feedback.rb +20 -0
  30. data/lib/dimelo/ccp/api/model/feedback_comment.rb +31 -0
  31. data/lib/dimelo/ccp/api/model/membership.rb +12 -0
  32. data/lib/dimelo/ccp/api/model/private_message.rb +8 -0
  33. data/lib/dimelo/ccp/api/model/question.rb +20 -0
  34. data/lib/dimelo/ccp/api/model/role.rb +8 -0
  35. data/lib/dimelo/ccp/api/model/user.rb +37 -0
  36. data/lib/dimelo/ccp/api/model/webhook.rb +9 -0
  37. data/lib/dimelo/ccp/api/model.rb +209 -0
  38. data/lib/dimelo/ccp/api/version.rb +7 -0
  39. data/lib/dimelo/ccp/api.rb +62 -0
  40. data/lib/dimelo_ccp_api.rb +1 -0
  41. data/spec/examples/openable_examples.rb +37 -0
  42. data/spec/examples/starrable_example.rb +57 -0
  43. data/spec/fixtures/files/logo.jpg +0 -0
  44. data/spec/lib/dimelo/ccp/api/client_spec.rb +111 -0
  45. data/spec/lib/dimelo/ccp/api/connection_spec.rb +129 -0
  46. data/spec/lib/dimelo/ccp/api/model/attachment_spec.rb +26 -0
  47. data/spec/lib/dimelo/ccp/api/model/feedback_spec.rb +10 -0
  48. data/spec/lib/dimelo/ccp/api/model/question_spec.rb +10 -0
  49. data/spec/lib/dimelo/ccp/api/model_spec.rb +162 -0
  50. data/spec/spec_helper.rb +7 -0
  51. metadata +174 -0
@@ -0,0 +1,39 @@
1
+ module Dimelo::CCP
2
+ class Answer < Dimelo::CCP::API::Model
3
+ include ::Dimelo::CCP::API::Common::Publishable
4
+
5
+ path 'questions/%{question_id}/answers/%{id}'
6
+
7
+ attributes :id, :body, :body_format, :flow_state, :user_id, :permalink,
8
+ :attachments_count, :comments_count,
9
+ :created_at, :updated_at, :question_id, :ipaddr, :question_flow_state
10
+
11
+ submit_attributes :body, :body_format, :user_id, :question_id
12
+
13
+ belongs_to :user
14
+ belongs_to :question
15
+
16
+ has_many :answer_attachments
17
+
18
+ def admin_stamp
19
+ path = "#{compute_path(attributes)}/admin_stamp"
20
+ response = client.transport(:post, path)
21
+ self.attributes = Dimelo::CCP::API.decode_json(response)
22
+ errors.empty?
23
+ end
24
+
25
+ def author_stamp
26
+ path = "#{compute_path(attributes)}/author_stamp"
27
+ response = client.transport(:post, path)
28
+ self.attributes = Dimelo::CCP::API.decode_json(response)
29
+ errors.empty?
30
+ end
31
+
32
+ def unstamp
33
+ path = "#{compute_path(attributes)}/stamp"
34
+ response = client.transport(:delete, path)
35
+ self.attributes = Dimelo::CCP::API.decode_json(response)
36
+ errors.empty?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ module Dimelo::CCP
2
+ class Attachment < Dimelo::CCP::API::Model
3
+ attributes :id, :file, :original, :question_id
4
+ end
5
+
6
+ class AnswerAttachment < Attachment
7
+ path 'questions/%{question_id}/answers/%{answer_id}/attachments/%{id}'
8
+
9
+ attributes :id, :file, :original, :answer_id, :question_id
10
+
11
+ belongs_to :answer
12
+
13
+ submit_attributes :file, :question_id, :answer_id
14
+ end
15
+
16
+ class FeedbackCommentAttachment < Attachment
17
+ path 'feedbacks/%{feedback_id}/comments/%{comment_id}/attachments/%{id}'
18
+
19
+ attributes :id, :file, :original, :feedback_id, :comment_id
20
+ belongs_to :feedback_comment
21
+
22
+ submit_attributes :file, :question_id, :answer_id
23
+ end
24
+
25
+ class QuestionAttachment < Attachment
26
+ path 'questions/%{question_id}/attachments/%{id}'
27
+
28
+ attributes :id, :file, :original, :question_id
29
+
30
+ belongs_to :question
31
+
32
+ submit_attributes :file, :question_id
33
+ end
34
+
35
+ class FeedbackAttachment < Attachment
36
+ path 'feedbacks/%{feedback_id}/attachments/%{id}'
37
+
38
+ attributes :id, :file, :original, :feedback_id
39
+
40
+ belongs_to :question
41
+
42
+ submit_attributes :file, :feedback_id
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module Dimelo::CCP
2
+ class Category < Dimelo::CCP::API::Model
3
+ path 'category_groups/%{category_group_id}/categories/%{id}'
4
+
5
+ attributes :id, :category_group_id, :name, :description, :questions_count, :permalink, :picture
6
+ alias :to_s :name
7
+
8
+ belongs_to :category_group
9
+ has_many :questions
10
+
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Dimelo::CCP
2
+ class CategoryGroup < Dimelo::CCP::API::Model
3
+
4
+ attributes :id, :name, :item_name, :multiple, :optional
5
+
6
+ alias_method :multiple?, :multiple
7
+ alias_method :optional?, :optional
8
+
9
+ has_many :categories
10
+
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Dimelo::CCP
2
+ class Feedback < Dimelo::CCP::API::Model
3
+ include ::Dimelo::CCP::API::Common::Openable
4
+ include ::Dimelo::CCP::API::Common::Publishable
5
+ include ::Dimelo::CCP::API::Common::Starrable
6
+
7
+ attributes :id, :title, :body, :body_format, :flow_state, :score, :user_id,
8
+ :category_id, :category_ids, :category_names, :status_id, :star, :starred_at,
9
+ :closed, :attachments_count, :comments_count, :positive_votes_count, :negative_votes_count,
10
+ :permalink, :ipaddr, :created_at, :updated_at
11
+
12
+ submit_attributes :title, :body, :body_format, :category_ids, :user_id
13
+
14
+ belongs_to :user
15
+ belongs_to :category
16
+ has_many :feedback_comments
17
+ alias :comments :feedback_comments
18
+
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Dimelo::CCP
2
+ class FeedbackComment < Dimelo::CCP::API::Model
3
+ include ::Dimelo::CCP::API::Common::Publishable
4
+
5
+ STATUS_COMMENT_PATH = 'feedbacks/%{feedback_id}/status_comments/%{id}'
6
+ COMMENT_PATH = 'feedbacks/%{feedback_id}/comments/%{id}'
7
+
8
+ path COMMENT_PATH
9
+
10
+ attributes :id, :feedback_id, :body, :body_format, :flow_state, :user_id, :type, :status_id, :attachments_count, :created_at, :updated_at, :permalink, :ipaddr
11
+ submit_attributes :body, :body_format, :user_id, :feedback_id, :status_id
12
+
13
+ belongs_to :user
14
+ belongs_to :feedback
15
+
16
+ def create
17
+ self.class.path(STATUS_COMMENT_PATH) if status_id
18
+ super
19
+ ensure
20
+ self.class.path(COMMENT_PATH) if status_id
21
+ end
22
+
23
+ def update
24
+ self.class.path(STATUS_COMMENT_PATH) if status_id
25
+ super
26
+ ensure
27
+ self.class.path(COMMENT_PATH) if status_id
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ module Dimelo::CCP
2
+ class Membership < Dimelo::CCP::API::Model
3
+
4
+ path 'users/%{user_id}/memberships/%{id}'
5
+
6
+ attributes :id, :user_id, :role, :domain, :domain_application_id
7
+ submit_attributes :user_id, :role, :domain
8
+
9
+ belongs_to :user
10
+
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Dimelo::CCP
2
+ class PrivateMessage < Dimelo::CCP::API::Model
3
+
4
+ attributes :id, :title, :body, :html_body, :raw_body, :from_user_id, :to_user_id, :parent_id, :root_id, :created_at, :updated_at
5
+ submit_attributes :title, :body, :from_user_id, :to_user_id, :parent_id
6
+
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module Dimelo::CCP
2
+ class Question < Dimelo::CCP::API::Model
3
+ include ::Dimelo::CCP::API::Common::Openable
4
+ include ::Dimelo::CCP::API::Common::Publishable
5
+ include ::Dimelo::CCP::API::Common::Starrable
6
+
7
+ attributes :id, :title, :body, :body_format, :flow_state, :score, :user_id,
8
+ :ipaddr, :category_id, :category_ids, :category_names, :starred_at,
9
+ :answers_count, :attachments_count, :usefulnesses_yes_count, :usefulnesses_no_count,
10
+ :star, :closed, :permalink, :created_at, :updated_at
11
+
12
+ submit_attributes :title, :body, :body_format, :category_ids, :user_id
13
+
14
+ belongs_to :user
15
+ belongs_to :category
16
+ has_many :answers
17
+ has_many :question_attachments
18
+
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ module Dimelo::CCP
2
+ class Role < Dimelo::CCP::API::Model
3
+
4
+ attributes :id, :label, :team
5
+ attributes :view_site, :edit_customization, :view_admin, :edit_grids, :send_private_message, :view_analytics, :view_identities, :view_advanced_admin, :configure_extensions, :configure_mobile
6
+
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module Dimelo::CCP
2
+ class User < Dimelo::CCP::API::Model
3
+ CUSTOM_FIELD_COUNT = 10
4
+ CUSTOM_FIELD_ATTRIBUTES = (1..CUSTOM_FIELD_COUNT).map { |i| "custom_field_#{i}".to_sym }.freeze
5
+
6
+ attributes :id, :firstname, :lastname, :signature, :email, :private_message, :type, :username, :flow_state, :about, :avatar, :created_at, :updated_at, :blocked, :team
7
+ attributes *CUSTOM_FIELD_ATTRIBUTES
8
+
9
+ submit_attributes :type, :firstname, :lastname, :email, :username, :avatar_url, :about
10
+
11
+ has_many :memberships
12
+ has_many :questions
13
+ has_many :answers
14
+ has_many :feedbacks
15
+
16
+ def avatar_url(size='normal')
17
+ avatar.try(:[], size).try(:[], 'url')
18
+ end
19
+
20
+ # Blocks the specified user
21
+ def block
22
+ path = "#{compute_path(attributes)}/block"
23
+ response = client.transport(:post, path)
24
+ self.attributes = Dimelo::CCP::API.decode_json(response)
25
+ errors.empty?
26
+ end
27
+
28
+ # Unblocks the specified user
29
+ def unblock
30
+ path = "#{compute_path(attributes)}/unblock"
31
+ response = client.transport(:post, path)
32
+ self.attributes = Dimelo::CCP::API.decode_json(response)
33
+ errors.empty?
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ module Dimelo::CCP
2
+ class Webhook < Dimelo::CCP::API::Model
3
+
4
+ attributes :id, :endpoint_enabled, :endpoint_url, :preprod_settings, :verify_token
5
+ attributes :answer__destroyed, :question__destroyed, :identity__destroyed, :comment__destroyed, :feedback__destroyed, :status_comment__destroyed
6
+
7
+ submit_attributes :id, :endpoint_enabled, :endpoint_url, :preprod_settings, :verify_token, :answer__destroyed, :question__destroyed, :identity__destroyed, :comment__destroyed, :feedback__destroyed, :status_comment__destroyed
8
+ end
9
+ end
@@ -0,0 +1,209 @@
1
+ require 'active_model'
2
+
3
+ module Dimelo::CCP
4
+ module API
5
+ class Model
6
+ extend ActiveModel::Translation
7
+ extend ActiveModel::Naming
8
+ include ActiveModel::Validations
9
+
10
+ class << self
11
+
12
+ def path(*args)
13
+ @path = args.first if args.any?
14
+ @path ||= "#{name.demodulize.pluralize.underscore}/%{id}"
15
+ end
16
+
17
+ def attribute(arg)
18
+ @attributes ||= []
19
+ @attributes << arg
20
+ attr_reader(arg)
21
+ class_eval "
22
+ def #{arg}=(val)
23
+ @tracked_attributes << :#{arg}
24
+ @#{arg} = val
25
+ end"
26
+ end
27
+
28
+ def attributes(*args)
29
+ args.each do |arg|
30
+ attribute arg
31
+ end
32
+ @attributes
33
+ end
34
+
35
+ def submit_attributes(*args)
36
+ @submit_attributes = args if args.any?
37
+ @submit_attributes ||= []
38
+ end
39
+
40
+ def has_many(association, options={})
41
+ foreign_class = options[:class_name] || "#{name.gsub(/(\w+)$/, '')}#{association.to_s.singularize.camelize}"
42
+ foreign_reference = self.name.demodulize.underscore
43
+ foreign_key = "#{foreign_reference}_id"
44
+
45
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
46
+
47
+ def #{association}(client=nil)
48
+ # FIXME: clear cache if client change, or something like that
49
+ client ||= @client
50
+ @#{association} ||= #{foreign_class}.find({:#{foreign_key} => self.id}, client).each do |instance|
51
+ instance.#{foreign_reference} = self
52
+ end
53
+ end
54
+
55
+ def #{association}=(items)
56
+ @#{association} = items.each{ |i| i.#{foreign_reference} = self }
57
+ end
58
+
59
+ EOS
60
+
61
+ end
62
+
63
+ def belongs_to(association, options={})
64
+ attr_writer association
65
+ foreign_class = options[:class_name] || "#{name.gsub(/(\w+)$/, '')}#{association.to_s.camelize}"
66
+ foreign_reference = self.name.demodulize.underscore
67
+ foreign_key = "#{association}_id"
68
+
69
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
70
+
71
+ def #{association}(client=nil)
72
+ client ||= @client
73
+ @#{association} ||= #{foreign_class}.find(self.#{foreign_key}, client)
74
+ end
75
+
76
+ EOS
77
+ end
78
+
79
+ def find(*args)
80
+ client = args.pop
81
+ criterias = args.pop
82
+ criterias = {:id => criterias} unless criterias.is_a?(Hash)
83
+ Dimelo::CCP::API::LazzyCollection.new(criterias) do |criterias|
84
+ parse(client.transport(:get, compute_path(criterias), criterias), client)
85
+ end
86
+ end
87
+
88
+ def parse(document, client=nil)
89
+ object = Dimelo::CCP::API.decode_json(document)
90
+ object.is_a?(Array) ? object.map{ |i| new(i, client) } : new(object, client)
91
+ end
92
+
93
+ # Inspired by https://github.com/svenfuchs/i18n/blob/master/lib/i18n/core_ext/string/interpolate.rb
94
+ INTERPOLATION_PATTERN = /%\{(\w+)\}/ # matches placeholders like "%{foo}"
95
+ def compute_path(criterias={})
96
+ path.gsub(INTERPOLATION_PATTERN) do |match|
97
+ criterias.delete($1.to_sym) || ''
98
+ end.chomp '/'
99
+ end
100
+
101
+ end
102
+
103
+ attr_accessor :client
104
+ attr_reader :errors, :tracked_attributes
105
+
106
+ delegate :compute_path, :to => 'self.class'
107
+
108
+ def initialize(hash={}, client=nil)
109
+ @errors = ActiveModel::Errors.new(self)
110
+ @tracked_attributes = []
111
+ self.client = client
112
+ hash.each do |k,v|
113
+ self.send("#{k}=", v) if self.respond_to?("#{k}=")
114
+ end
115
+ end
116
+
117
+ def errors=(errs)
118
+ @errors = ActiveModel::Errors.new(self).tap do |errors|
119
+ errs.each do |error|
120
+ errors.add(error['attribute'], error['type'])
121
+ end
122
+ end
123
+ end
124
+
125
+ def attributes
126
+ Hash[self.class.attributes.map{ |key| [key, self.send(key)] }]
127
+ end
128
+
129
+ def attributes=(hash)
130
+ hash.each do |k, value|
131
+ if self.respond_to? "#{k}="
132
+ self.send("#{k}=", value)
133
+ else
134
+ warn("Unknown field or method #{k} for object #{self.inspect}")
135
+ end
136
+ end
137
+ end
138
+
139
+ def tracked_submit_attributes
140
+ @tracked_attributes & self.class.submit_attributes
141
+ end
142
+
143
+ def submit_attributes
144
+ Hash[tracked_submit_attributes.map{ |key| [key, self.send(key)] }]
145
+ end
146
+
147
+ def new_record?
148
+ id.blank?
149
+ end
150
+
151
+ def save
152
+ if new_record?
153
+ create
154
+ else
155
+ update
156
+ end
157
+ end
158
+
159
+ def create
160
+ attrs = submit_attributes
161
+ path = compute_path(attrs)
162
+ response = client.transport(:post, path, attrs)
163
+ self.attributes = Dimelo::CCP::API.decode_json(response)
164
+ id.present?
165
+ end
166
+
167
+ def update
168
+ attrs = submit_attributes
169
+ path = compute_path(attributes)
170
+ response = client.transport(:put, path, attrs)
171
+ self.attributes = Dimelo::CCP::API.decode_json(response)
172
+ errors.empty?
173
+ end
174
+
175
+ def valid?
176
+ true
177
+ end
178
+
179
+ def destroy
180
+ client.transport(:delete, compute_path(self.attributes))
181
+ freeze
182
+ end
183
+
184
+ def reload
185
+ raise ArgumentError.new("You cannot fetch models without a populated id attribute") if id.nil?
186
+ other = self.class.find(id, client)
187
+ self.attributes.merge!(other.attributes) if other
188
+ self
189
+ end
190
+
191
+ def ==(other)
192
+ other.is_a?(self.class) and self.id == other.id and self.id.present?
193
+ end
194
+
195
+ def merge!(other)
196
+ self.attributes = other.attributes
197
+ end
198
+
199
+ def to_json
200
+ Dimelo::CCP::API.encode_json(self.attributes)
201
+ end
202
+
203
+ def warn(message)
204
+ defined?(Rails) ? Rails.logger.warn(message) : STDERR.puts(message)
205
+ end
206
+
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,7 @@
1
+ module Dimelo
2
+ module CCP
3
+ module API
4
+ VERSION = File.read(File.join(File.dirname(__FILE__),'..', '..', '..', '..','VERSION') ).strip
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,62 @@
1
+ require 'active_support'
2
+ require 'active_support/json'
3
+ require 'active_support/core_ext'
4
+ require 'faraday'
5
+ require 'uri'
6
+
7
+ module Dimelo
8
+ module CCP
9
+
10
+ autoload :Answer, 'dimelo/ccp/api/model/answer'
11
+ autoload :Attachment, 'dimelo/ccp/api/model/attachment'
12
+ autoload :Category, 'dimelo/ccp/api/model/category'
13
+ autoload :CategoryGroup, 'dimelo/ccp/api/model/category_group'
14
+ autoload :Feedback, 'dimelo/ccp/api/model/feedback'
15
+ autoload :FeedbackComment,'dimelo/ccp/api/model/feedback_comment'
16
+ autoload :Membership, 'dimelo/ccp/api/model/membership'
17
+ autoload :PrivateMessage, 'dimelo/ccp/api/model/private_message'
18
+ autoload :Question, 'dimelo/ccp/api/model/question'
19
+ autoload :Role, 'dimelo/ccp/api/model/role'
20
+ autoload :User, 'dimelo/ccp/api/model/user'
21
+ autoload :Webhook, 'dimelo/ccp/api/model/webhook'
22
+
23
+ # Attachments
24
+ autoload :AnswerAttachment, 'dimelo/ccp/api/model/attachment'
25
+ autoload :CommentAttachment, 'dimelo/ccp/api/model/attachment'
26
+ autoload :FeedbackAttachment, 'dimelo/ccp/api/model/attachment'
27
+ autoload :FeedbackCommentAttachment, 'dimelo/ccp/api/model/attachment'
28
+ autoload :QuestionAttachment, 'dimelo/ccp/api/model/attachment'
29
+
30
+ module API
31
+
32
+ autoload :VERSION, 'dimelo/ccp/api/version'
33
+ autoload :Client, 'dimelo/ccp/api/client'
34
+ autoload :Connection, 'dimelo/ccp/api/connection'
35
+ autoload :Model, 'dimelo/ccp/api/model'
36
+ autoload :BasicObject, 'dimelo/ccp/api/basic_object'
37
+ autoload :LazzyCollection, 'dimelo/ccp/api/lazzy_collection'
38
+
39
+ require 'dimelo/ccp/api/error'
40
+
41
+ class << self
42
+
43
+ def decode_json(document)
44
+ ActiveSupport::JSON.decode(document)
45
+ end
46
+
47
+ def encode_json(object)
48
+ ActiveSupport::JSON.encode(object)
49
+ end
50
+
51
+ end
52
+
53
+ module Common
54
+
55
+ autoload :Openable, 'dimelo/ccp/api/common/openable'
56
+ autoload :Publishable, 'dimelo/ccp/api/common/publishable'
57
+ autoload :Starrable, 'dimelo/ccp/api/common/starrable'
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1 @@
1
+ require 'dimelo/ccp/api'
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'common model actions' do
4
+
5
+ before :each do
6
+ @response = {'title' => 'new title'}.to_json
7
+ @client = double
8
+ allow(@client).to receive(:transport) { @response }
9
+ @content = described_class.new({'id' => 123, 'title' => 'old title'}, @client)
10
+ allow(@content).to receive(:errors) { [] }
11
+ end
12
+
13
+ %w(open close publish unpublish).each do |action|
14
+ describe action do
15
+
16
+ it 'sends a put to the appropriate uri' do
17
+ path = @content.compute_path(@content.attributes) + "/#{action}"
18
+ expect(@client).to receive(:transport).with(:put, path).and_return(@response)
19
+ @content.send(action)
20
+ end
21
+
22
+ it 'updates the object with the new attributes' do
23
+ expect {
24
+ @content.send(action)
25
+ }.to change { @content.title }.from('old title').to('new title')
26
+ end
27
+
28
+ it 'returns a boolean indicating any errors' do
29
+ expect {
30
+ allow(@content).to receive(:errors) { ['uh-oh'] }
31
+ }.to change { @content.send(action) }.from(true).to(false)
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'starrable object' do
4
+
5
+ before :each do
6
+ @response = {'title' => 'new title'}.to_json
7
+ @client = double
8
+ allow(@client).to receive(:transport) { @response }
9
+ @content = described_class.new({'id' => 123, 'title' => 'old title'}, @client)
10
+ allow(@content).to receive(:errors) { [] }
11
+ end
12
+
13
+ describe '#star!' do
14
+
15
+ it 'sends a put to the appropriate uri' do
16
+ path = @content.compute_path(@content.attributes) + "/star"
17
+ expect(@client).to receive(:transport).with(:put, path).and_return(@response)
18
+ @content.star!
19
+ end
20
+
21
+ it 'updates the object with the new attributes' do
22
+ expect {
23
+ @content.star!
24
+ }.to change { @content.title }.from('old title').to('new title')
25
+ end
26
+
27
+ it 'returns a boolean indicating any errors' do
28
+ expect {
29
+ allow(@content).to receive(:errors) { ['uh-oh'] }
30
+ }.to change { @content.star! }.from(true).to(false)
31
+ end
32
+
33
+ end
34
+
35
+ describe '#unstar!' do
36
+
37
+ it 'sends a put to the appropriate uri' do
38
+ path = @content.compute_path(@content.attributes) + "/unstar"
39
+ expect(@client).to receive(:transport).with(:put, path).and_return(@response)
40
+ @content.unstar!
41
+ end
42
+
43
+ it 'updates the object with the new attributes' do
44
+ expect {
45
+ @content.unstar!
46
+ }.to change { @content.title }.from('old title').to('new title')
47
+ end
48
+
49
+ it 'returns a boolean indicating any errors' do
50
+ expect {
51
+ allow(@content).to receive(:errors) { ['uh-oh'] }
52
+ }.to change { @content.unstar! }.from(true).to(false)
53
+ end
54
+
55
+ end
56
+
57
+ end
Binary file