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)
158 # Represents a location in Twitter
162 @@ATTRIBUTES = [:name, :woeid, :country, :url, :countryCode, :parentid, :placeType]
163 attr_accessor(*@@ATTRIBUTES)
166 def attributes; @@ATTRIBUTES; end
169 # Alias to +countryCode+ for those wanting to use consistent naming
170 # convention for attribute
175 # Alias to +parentid+ for those wanting to use consistent naming
176 # convention for attribute
181 # Alias to +placeType+ for those wanting to use consistent naming
182 # convention for attribute
187 # Convenience method to output meaningful representation to STDOUT as per
190 "#{name} / #{woeid} / #{countryCode}\n#{url}\n"
196 @placeType = ::Twitter::PlaceType.new(:name => @placeType["name"],
197 :code => @placeType["code"]) if @placeType.is_a?(Hash)
201 # Represents a type of a place.
205 @@ATTRIBUTES = [:name, :code]
206 attr_accessor(*@@ATTRIBUTES)
209 def attributes; @@ATTRIBUTES; end
213 # Represents a sorted, dated and typed list of trends.
215 # To find out when this +Trendline+ was created query the +as_of+ attribute.
216 # To find out what type +Trendline+ is use the +type+ attribute.
217 # You can iterator over the trends in the +Trendline+ with +each+ or by
218 # index, whichever you prefer.
224 @@ATTRIBUTES = [:as_of, :type]
225 attr_accessor(*@@ATTRIBUTES)
228 def attributes; @@ATTRIBUTES; end
231 # Spaceship operator definition needed by Comparable mixin
234 self.type === other.type && self.as_of <=> other.as_of
237 # each definition needed by Enumerable mixin for first, ...
244 # index operator definition needed to iterate over trends
245 # in the +::Twitter::Trendline+ object using for or otherwise
251 attr_accessor(:trends)
252 # Constructor callback
254 @trends = @trends.collect do |trend|
255 ::Twitter::Trend.new(trend) if trend.is_a?(Hash)
256 end if @trends.is_a?(Array)
262 @@ATTRIBUTES = [:name, :url]
263 attr_accessor(*@@ATTRIBUTES)
266 def attributes; @@ATTRIBUTES; end
270 # Represents a <tt>Twitter</tt> user
273 @@ATTRIBUTES = [:id, :name, :description, :location, :screen_name, :url,
274 :protected, :profile_image_url, :profile_background_color,
275 :profile_text_color, :profile_link_color, :profile_sidebar_fill_color,
276 :profile_sidebar_border_color, :profile_background_image_url,
277 :profile_background_tile, :utc_offset, :time_zone,
278 :following, :notifications, :favourites_count, :followers_count,
279 :friends_count, :statuses_count, :created_at ]
280 attr_accessor(*@@ATTRIBUTES)
283 # Used as factory method callback
284 def attributes; @@ATTRIBUTES; end
286 # Returns user model object with given <tt>id</tt> using the configuration
287 # and credentials of the <tt>client</tt> object passed in.
289 # You can pass in either the user's unique integer ID or the user's
296 # Override of ModelMixin#bless method.
298 # Adds #followers instance method when user object represents
299 # authenticated user. Otherwise just do basic bless.
301 # This permits applications using <tt>Twitter4R</tt> to write
302 # Rubyish code like this:
303 # followers = user.followers if user.is_me?
305 # followers = user.followers if user.respond_to?(:followers)
308 self.instance_eval(%{
309 self.class.send(:include, Twitter::AuthenticatedUserMixin)
310 }) if self.is_me? and not self.respond_to?(:followers)
314 # Returns whether this <tt>Twitter::User</tt> model object
315 # represents the authenticated user of the <tt>client</tt>
318 # TODO: Determine whether we should cache this or not?
319 # Might be dangerous to do so, but do we want to support
320 # the edge case where this would cause a problem? i.e.
321 # changing authenticated user after initial use of
323 # TBD: To cache or not to cache. That is the question!
324 # Since this is an implementation detail we can leave this for
325 # subsequent 0.2.x releases. It doesn't have to be decided before
327 @screen_name == @client.instance_eval("@login")
330 # Returns an Array of user objects that represents the authenticated
331 # user's friends on Twitter.
333 @client.user(@id, :friends)
337 # Represents a status posted to <tt>Twitter</tt> by a <tt>Twitter</tt> user.
340 @@ATTRIBUTES = [:id, :id_str, :text, :source, :truncated, :created_at, :user,
341 :from_user, :to_user, :favorited, :in_reply_to_status_id,
342 :in_reply_to_user_id, :in_reply_to_screen_name, :geo]
343 attr_accessor(*@@ATTRIBUTES)
346 # Used as factory method callback
347 def attributes; @@ATTRIBUTES; end
349 # Returns status model object with given <tt>status</tt> using the
350 # configuration and credentials of the <tt>client</tt> object passed in.
352 client.status(:get, id)
355 # Creates a new status for the authenticated user of the given
356 # <tt>client</tt> context.
358 # You MUST include a valid/authenticated <tt>client</tt> context
359 # in the given <tt>params</tt> argument.
362 # status = Twitter::Status.create(
363 # :text => 'I am shopping for flip flops',
366 # An <tt>ArgumentError</tt> will be raised if no valid client context
367 # is given in the <tt>params</tt> Hash. For example,
368 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
369 # The above line of code will raise an <tt>ArgumentError</tt>.
371 # The same is true when you do not provide a <tt>:text</tt> key-value
372 # pair in the <tt>params</tt> argument given.
374 # The Twitter::Status object returned after the status successfully
375 # updates on the Twitter server side is returned from this method.
377 client, text = params[:client], params[:text]
378 raise ArgumentError, 'Valid client context must be provided' unless client.is_a?(Twitter::Client)
379 raise ArgumentError, 'Must provide text for the status to update' unless text.is_a?(String)
380 client.status(:post, text)
385 !!@in_reply_to_status_id
388 # Convenience method to allow client developers to not have to worry about
389 # setting the +in_reply_to_status_id+ attribute or prefixing the status
390 # text with the +screen_name+ being replied to.
392 status_reply = "@#{user.screen_name} #{reply}"
393 client.status(:reply, :status => status_reply,
394 :in_reply_to_status_id => @id)
398 # Constructor callback
400 @user = User.new(@user) if @user.is_a?(Hash)
401 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
405 # Represents a direct message on <tt>Twitter</tt> between <tt>Twitter</tt> users.
408 @@ATTRIBUTES = [:id, :recipient, :sender, :text, :geo, :created_at]
409 attr_accessor(*@@ATTRIBUTES)
412 # Used as factory method callback
413 def attributes; @@ATTRIBUTES; end
415 # Raises <tt>NotImplementedError</tt> because currently
416 # <tt>Twitter</tt> doesn't provide a facility to retrieve
417 # one message by unique ID.
419 raise NotImplementedError, 'Twitter has yet to implement a REST API for this. This is not a Twitter4R library limitation.'
422 # Creates a new direct message from the authenticated user of the
423 # given <tt>client</tt> context.
425 # You MUST include a valid/authenticated <tt>client</tt> context
426 # in the given <tt>params</tt> argument.
429 # status = Twitter::Message.create(
430 # :text => 'I am shopping for flip flops',
431 # :recipient => 'anotherlogin',
434 # An <tt>ArgumentError</tt> will be raised if no valid client context
435 # is given in the <tt>params</tt> Hash. For example,
436 # status = Twitter::Status.create(:text => 'I am shopping for flip flops')
437 # The above line of code will raise an <tt>ArgumentError</tt>.
439 # The same is true when you do not provide any of the following
440 # key-value pairs in the <tt>params</tt> argument given:
441 # * <tt>text</tt> - the String that will be the message text to send to <tt>user</tt>
442 # * <tt>recipient</tt> - the user ID, screen_name or Twitter::User object representation of the recipient of the direct message
444 # The Twitter::Message object returned after the direct message is
445 # successfully sent on the Twitter server side is returned from
448 client, text, recipient = params[:client], params[:text], params[:recipient]
449 raise ArgumentError, 'Valid client context must be given' unless client.is_a?(Twitter::Client)
450 raise ArgumentError, 'Message text must be supplied to send direct message' unless text.is_a?(String)
451 raise ArgumentError, 'Recipient user must be specified to send direct message' unless [Twitter::User, Integer, String].member?(recipient.class)
452 client.message(:post, text, recipient)
457 # Constructor callback
459 @sender = User.new(@sender) if @sender.is_a?(Hash)
460 @recipient = User.new(@recipient) if @recipient.is_a?(Hash)
461 @created_at = Time.parse(@created_at) if @created_at.is_a?(String)
465 # RateLimitStatus provides information about how many requests you have left
466 # and when you can resume more requests if your remaining_hits count is zero.
467 class RateLimitStatus
469 @@ATTRIBUTES = [:remaining_hits, :hourly_limit, :reset_time_in_seconds, :reset_time]
470 attr_accessor(*@@ATTRIBUTES)
473 # Used as factory method callback
474 def attributes; @@ATTRIBUTES; end