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) if raw.is_a?(String)
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)
163 @@ATTRIBUTES = [:as_of, :type]
164 attr_accessor(*@@ATTRIBUTES)
167 def attributes; @@ATTRIBUTES; end
170 # Spaceship operator definition needed by Comparable mixin
173 self.type === other.type && self.as_of <=> other.as_of
176 # each definition needed by Enumerable mixin for first, ...
183 # index operator definition needed to iterate over trends
184 # in the +::Twitter::Trendline+ object using for or otherwise
190 attr_accessor(:trends)
191 # Constructor callback
193 @trends = @trends.collect do |trend|
194 ::Twitter::Trend.new(trend) if trend.is_a?(Hash)
195 end if @trends.is_a?(Array)
201 @@ATTRIBUTES = [:name, :url]
202 attr_accessor(*@@ATTRIBUTES)
205 def attributes; @@ATTRIBUTES; end
209 # Represents a <tt>Twitter</tt> user
212 @@ATTRIBUTES = [:id, :name, :description, :location, :screen_name, :url,
213 :protected, :profile_image_url, :profile_background_color,
214 :profile_text_color, :profile_link_color, :profile_sidebar_fill_color,
215 :profile_sidebar_border_color, :profile_background_image_url,
216 :profile_background_tile, :utc_offset, :time_zone,
217 :following, :notifications, :favourites_count, :followers_count,
218 :friends_count, :statuses_count, :created_at ]
219 attr_accessor(*@@ATTRIBUTES)
222 # Used as factory method callback
223 def attributes; @@ATTRIBUTES; end
225 # Returns user model object with given <tt>id</tt> using the configuration
226 # and credentials of the <tt>client</tt> object passed in.
228 # You can pass in either the user's unique integer ID or the user's
235 # Override of ModelMixin#bless method.
237 # Adds #followers instance method when user object represents
238 # authenticated user. Otherwise just do basic bless.
240 # This permits applications using <tt>Twitter4R</tt> to write
241 # Rubyish code like this:
242 # followers = user.followers if user.is_me?
244 # followers = user.followers if user.respond_to?(:followers)
247 self.instance_eval(%{
248 self.class.send(:include, Twitter::AuthenticatedUserMixin)
249 }) if self.is_me? and not self.respond_to?(:followers)
253 # Returns whether this <tt>Twitter::User</tt> model object
254 # represents the authenticated user of the <tt>client</tt>
257 # TODO: Determine whether we should cache this or not?
258 # Might be dangerous to do so, but do we want to support
259 # the edge case where this would cause a problem? i.e.
260 # changing authenticated user after initial use of
262 # TBD: To cache or not to cache. That is the question!
263 # Since this is an implementation detail we can leave this for
264 # subsequent 0.2.x releases. It doesn't have to be decided before
266 @screen_name == @client.instance_eval("@login")
269 # Returns an Array of user objects that represents the authenticated
270 # user's friends on Twitter.
272 @client.user(@id, :friends)
276 # Represents a status posted to <tt>Twitter</tt> by a <tt>Twitter</tt> user.
279 @@ATTRIBUTES = [:id, :id_str, :text, :source, :truncated, :created_at, :user,
280 :from_user, :to_user, :favorited, :in_reply_to_status_id,
281 :in_reply_to_user_id, :in_reply_to_screen_name, :geo]
282 attr_accessor(*@@ATTRIBUTES)
285 # Used as factory method callback
286 def attributes; @@ATTRIBUTES; end
288 # Returns status model object with given <tt>status</tt> using the
289 # configuration and credentials of the <tt>client</tt> object passed in.
291 client.status(:get, id)
294 # Creates a new status for the authenticated user of the given
295 # <tt>client</tt> context.
297 # You MUST include a valid/authenticated <tt>client</tt> context
298 # in the given <tt>params</tt> argument.
301 # status = Twitter::Status.create(
302 # :text => 'I am shopping for flip flops',
305 # An <tt>ArgumentError</tt> will be raised if no valid client context
306 # is given in the <tt>params</tt> Hash. For example,
307 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
308 # The above line of code will raise an <tt>ArgumentError</tt>.
310 # The same is true when you do not provide a <tt>:text</tt> key-value
311 # pair in the <tt>params</tt> argument given.
313 # The Twitter::Status object returned after the status successfully
314 # updates on the Twitter server side is returned from this method.
316 client, text = params[:client], params[:text]
317 raise ArgumentError, 'Valid client context must be provided' unless client.is_a?(Twitter::Client)
318 raise ArgumentError, 'Must provide text for the status to update' unless text.is_a?(String)
319 client.status(:post, text)
324 !!@in_reply_to_status_id
328 client.status(:reply, :status => status, :in_reply_to_status_id => @id)
332 # Constructor callback
334 @user = User.new(@user) if @user.is_a?(Hash)
335 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
339 # Represents a direct message on <tt>Twitter</tt> between <tt>Twitter</tt> users.
342 @@ATTRIBUTES = [:id, :recipient, :sender, :text, :geo, :created_at]
343 attr_accessor(*@@ATTRIBUTES)
346 # Used as factory method callback
347 def attributes; @@ATTRIBUTES; end
349 # Raises <tt>NotImplementedError</tt> because currently
350 # <tt>Twitter</tt> doesn't provide a facility to retrieve
351 # one message by unique ID.
353 raise NotImplementedError, 'Twitter has yet to implement a REST API for this. This is not a Twitter4R library limitation.'
356 # Creates a new direct message from the authenticated user of the
357 # given <tt>client</tt> context.
359 # You MUST include a valid/authenticated <tt>client</tt> context
360 # in the given <tt>params</tt> argument.
363 # status = Twitter::Message.create(
364 # :text => 'I am shopping for flip flops',
365 # :recipient => 'anotherlogin',
368 # An <tt>ArgumentError</tt> will be raised if no valid client context
369 # is given in the <tt>params</tt> Hash. For example,
370 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
371 # The above line of code will raise an <tt>ArgumentError</tt>.
373 # The same is true when you do not provide any of the following
374 # key-value pairs in the <tt>params</tt> argument given:
375 # * <tt>text</tt> - the String that will be the message text to send to <tt>user</tt>
376 # * <tt>recipient</tt> - the user ID, screen_name or Twitter::User object representation of the recipient of the direct message
378 # The Twitter::Message object returned after the direct message is
379 # successfully sent on the Twitter server side is returned from
382 client, text, recipient = params[:client], params[:text], params[:recipient]
383 raise ArgumentError, 'Valid client context must be given' unless client.is_a?(Twitter::Client)
384 raise ArgumentError, 'Message text must be supplied to send direct message' unless text.is_a?(String)
385 raise ArgumentError, 'Recipient user must be specified to send direct message' unless [Twitter::User, Integer, String].member?(recipient.class)
386 client.message(:post, text, recipient)
391 # Constructor callback
393 @sender = User.new(@sender) if @sender.is_a?(Hash)
394 @recipient = User.new(@recipient) if @recipient.is_a?(Hash)
395 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
399 # RateLimitStatus provides information about how many requests you have left
400 # and when you can resume more requests if your remaining_hits count is zero.
401 class RateLimitStatus
403 @@ATTRIBUTES = [:remaining_hits, :hourly_limit, :reset_time_in_seconds, :reset_time]
404 attr_accessor(*@@ATTRIBUTES)
407 # Used as factory method callback
408 def attributes; @@ATTRIBUTES; end