1 # Contains Twitter4R Model API.
4 # Mixin module for model classes. Includes generic class methods like
7 # To create a new model that includes this mixin's features simply:
9 # include Twitter::ModelMixin
12 # This mixin module automatically includes <tt>Twitter::ClassUtilMixin</tt>
15 # The contract for models to use this mixin correctly is that the class
16 # including this mixin must provide an class method named <tt>attributes</tt>
17 # that will return an Array of attribute symbols that will be checked
18 # in #eql? override method. The following would be sufficient:
19 # def self.attributes; @@ATTRIBUTES; end
20 module ModelMixin #:nodoc:
21 def self.included(base) #:nodoc:
22 base.send(:include, Twitter::ClassUtilMixin)
23 base.send(:include, InstanceMethods)
24 base.extend(ClassMethods)
27 # Class methods defined for <tt>Twitter::ModelMixin</tt> module.
28 module ClassMethods #:nodoc:
29 # Unmarshal object singular or plural array of model objects
30 # from JSON serialization. Currently JSON is only supported
31 # since this is all <tt>Twitter4R</tt> needs.
33 input = JSON.parse(raw)
34 def unmarshal_model(hash)
37 return unmarshal_model(input) if input.is_a?(Hash) # singular case
40 model = unmarshal_model(hash) if hash.is_a?(Hash)
42 end if input.is_a?(Array)
47 # Instance methods defined for <tt>Twitter::ModelMixin</tt> module.
48 module InstanceMethods #:nodoc:
50 # Equality method override of Object#eql? default.
52 # Relies on the class using this mixin to provide a <tt>attributes</tt>
53 # class method that will return an Array of attributes to check are
54 # equivalent in this #eql? override.
56 # It is by design that the #eql? method will raise a NoMethodError
57 # if no <tt>attributes</tt> class method exists, to alert you that
58 # you must provide it for a meaningful result from this #eql? override.
59 # Otherwise this will return a meaningless result.
61 attrs = self.class.attributes
63 return false unless self.send(att).eql?(other.send(att))
68 # Returns integer representation of model object instance.
71 # status = Twitter::Status.new(:id => 234343)
72 # status.to_i #=> 234343
77 # Returns string representation of model object instance.
80 # status = Twitter::Status.new(:text => 'my status message')
81 # status.to_s #=> 'my status message'
83 # If a model class doesn't have a @text attribute defined
84 # the default Object#to_s will be returned as the result.
86 self.respond_to?(:text) ? @text : super.to_s
89 # Returns hash representation of model object instance.
92 # u = Twitter::User.new(:id => 2342342, :screen_name => 'tony_blair_is_the_devil')
93 # u.to_hash #=> {:id => 2342342, :screen_name => 'tony_blair_is_the_devil'}
95 # This method also requires that the class method <tt>attributes</tt> be
96 # defined to return an Array of attributes for the class.
98 attrs = self.class.attributes
101 value = self.send(att)
102 value = value.to_hash if value.respond_to?(:to_hash)
103 result[att] = value if value
108 # "Blesses" model object.
110 # Should be overridden by model class if special behavior is expected
112 # Expected to return blessed object (usually <tt>self</tt>)
114 self.basic_bless(client)
118 # Basic "blessing" of model object
119 def basic_bless(client)
126 module AuthenticatedUserMixin
127 def self.included(base)
128 base.send(:include, InstanceMethods)
131 module InstanceMethods
132 # Returns an Array of user objects that represents the authenticated
133 # user's friends on Twitter.
134 def followers(options = {})
135 @client.my(:followers, options)
138 # Adds given user as a friend. Returns user object as given by
139 # <tt>Twitter</tt> REST server response.
141 # For <tt>user</tt> argument you may pass in the unique integer
142 # user ID, screen name or Twitter::User object representation.
144 @client.friend(:add, user)
147 # Removes given user as a friend. Returns user object as given by
148 # <tt>Twitter</tt> REST server response.
150 # For <tt>user</tt> argument you may pass in the unique integer
151 # user ID, screen name or Twitter::User object representation.
153 @client.friend(:remove, user)
158 # Represents a <tt>Twitter</tt> user
161 @@ATTRIBUTES = [:id, :name, :description, :location, :screen_name, :url,
162 :protected, :profile_image_url, :profile_background_color,
163 :profile_text_color, :profile_link_color, :profile_sidebar_fill_color,
164 :profile_sidebar_border_color, :profile_background_image_url,
165 :profile_background_tile, :utc_offset, :time_zone,
166 :following, :notifications, :favourites_count, :followers_count,
167 :friends_count, :statuses_count, :created_at, ]
168 attr_accessor *@@ATTRIBUTES
171 # Used as factory method callback
172 def attributes; @@ATTRIBUTES; end
174 # Returns user model object with given <tt>id</tt> using the configuration
175 # and credentials of the <tt>client</tt> object passed in.
177 # You can pass in either the user's unique integer ID or the user's
184 # Override of ModelMixin#bless method.
186 # Adds #followers instance method when user object represents
187 # authenticated user. Otherwise just do basic bless.
189 # This permits applications using <tt>Twitter4R</tt> to write
190 # Rubyish code like this:
191 # followers = user.followers if user.is_me?
193 # followers = user.followers if user.respond_to?(:followers)
196 self.instance_eval(%{
197 self.class.send(:include, Twitter::AuthenticatedUserMixin)
198 }) if self.is_me? and not self.respond_to?(:followers)
202 # Returns whether this <tt>Twitter::User</tt> model object
203 # represents the authenticated user of the <tt>client</tt>
206 # TODO: Determine whether we should cache this or not?
207 # Might be dangerous to do so, but do we want to support
208 # the edge case where this would cause a problem? i.e.
209 # changing authenticated user after initial use of
211 # TBD: To cache or not to cache. That is the question!
212 # Since this is an implementation detail we can leave this for
213 # subsequent 0.2.x releases. It doesn't have to be decided before
215 @screen_name == @client.instance_eval("@login")
218 # Returns an Array of user objects that represents the authenticated
219 # user's friends on Twitter.
221 @client.user(@id, :friends)
225 # Represents a status posted to <tt>Twitter</tt> by a <tt>Twitter</tt> user.
228 @@ATTRIBUTES = [:id, :text, :source, :truncated, :created_at, :user,
229 :favorited, :in_reply_to_status_id, :in_reply_to_user_id,
230 :in_reply_to_screen_name]
231 attr_accessor *@@ATTRIBUTES
234 # Used as factory method callback
235 def attributes; @@ATTRIBUTES; end
237 # Returns status model object with given <tt>status</tt> using the
238 # configuration and credentials of the <tt>client</tt> object passed in.
240 client.status(:get, id)
243 # Creates a new status for the authenticated user of the given
244 # <tt>client</tt> context.
246 # You MUST include a valid/authenticated <tt>client</tt> context
247 # in the given <tt>params</tt> argument.
250 # status = Twitter::Status.create(
251 # :text => 'I am shopping for flip flops',
254 # An <tt>ArgumentError</tt> will be raised if no valid client context
255 # is given in the <tt>params</tt> Hash. For example,
256 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
257 # The above line of code will raise an <tt>ArgumentError</tt>.
259 # The same is true when you do not provide a <tt>:text</tt> key-value
260 # pair in the <tt>params</tt> argument given.
262 # The Twitter::Status object returned after the status successfully
263 # updates on the Twitter server side is returned from this method.
265 client, text = params[:client], params[:text]
266 raise ArgumentError, 'Valid client context must be provided' unless client.is_a?(Twitter::Client)
267 raise ArgumentError, 'Must provide text for the status to update' unless text.is_a?(String)
268 client.status(:post, text)
273 !!@in_reply_to_status_id
277 client.status(:reply, :status => status, :in_reply_to_status_id => @id)
281 # Constructor callback
283 @user = User.new(@user) if @user.is_a?(Hash)
284 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
288 # Represents a direct message on <tt>Twitter</tt> between <tt>Twitter</tt> users.
291 @@ATTRIBUTES = [:id, :recipient, :sender, :text, :created_at]
292 attr_accessor *@@ATTRIBUTES
295 # Used as factory method callback
296 def attributes; @@ATTRIBUTES; end
298 # Raises <tt>NotImplementedError</tt> because currently
299 # <tt>Twitter</tt> doesn't provide a facility to retrieve
300 # one message by unique ID.
302 raise NotImplementedError, 'Twitter has yet to implement a REST API for this. This is not a Twitter4R library limitation.'
305 # Creates a new direct message from the authenticated user of the
306 # given <tt>client</tt> context.
308 # You MUST include a valid/authenticated <tt>client</tt> context
309 # in the given <tt>params</tt> argument.
312 # status = Twitter::Message.create(
313 # :text => 'I am shopping for flip flops',
314 # :receipient => 'anotherlogin',
317 # An <tt>ArgumentError</tt> will be raised if no valid client context
318 # is given in the <tt>params</tt> Hash. For example,
319 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
320 # The above line of code will raise an <tt>ArgumentError</tt>.
322 # The same is true when you do not provide any of the following
323 # key-value pairs in the <tt>params</tt> argument given:
324 # * <tt>text</tt> - the String that will be the message text to send to <tt>user</tt>
325 # * <tt>recipient</tt> - the user ID, screen_name or Twitter::User object representation of the recipient of the direct message
327 # The Twitter::Message object returned after the direct message is
328 # successfully sent on the Twitter server side is returned from
331 client, text, recipient = params[:client], params[:text], params[:recipient]
332 raise ArgumentError, 'Valid client context must be given' unless client.is_a?(Twitter::Client)
333 raise ArgumentError, 'Message text must be supplied to send direct message' unless text.is_a?(String)
334 raise ArgumentError, 'Recipient user must be specified to send direct message' unless [Twitter::User, Integer, String].member?(recipient.class)
335 client.message(:post, text, recipient)
340 # Constructor callback
342 @sender = User.new(@sender) if @sender.is_a?(Hash)
343 @recipient = User.new(@recipient) if @recipient.is_a?(Hash)
344 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
348 # RateLimitStatus provides information about how many requests you have left
349 # and when you can resume more requests if your remaining_hits count is zero.
350 class RateLimitStatus
352 @@ATTRIBUTES = [:remaining_hits, :hourly_limit, :reset_time_in_seconds, :reset_time]
353 attr_accessor *@@ATTRIBUTES
356 # Used as factory method callback
357 def attributes; @@ATTRIBUTES; end