From 6fb7b3fffdb953e07348e87f6a3af23f603e996f Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 17 Oct 2011 21:59:37 -0700 Subject: [PATCH] Implement lazy attribute setting Hat-tip: @dkubb and @phiggins --- lib/twitter/base.rb | 18 +++++++++------ lib/twitter/configuration.rb | 15 +++++++------ lib/twitter/creatable.rb | 7 +++--- lib/twitter/cursor.rb | 16 ++++++++------ lib/twitter/direct_message.rb | 14 +++++++----- lib/twitter/language.rb | 2 +- lib/twitter/list.rb | 14 +++++++----- lib/twitter/metadata.rb | 2 +- lib/twitter/photo.rb | 16 ++++++++------ lib/twitter/place.rb | 19 +++++++++------- lib/twitter/point.rb | 8 +++---- lib/twitter/polygon.rb | 4 ++-- lib/twitter/rate_limit_status.rb | 10 +++++---- lib/twitter/relationship.rb | 9 ++++---- lib/twitter/saved_search.rb | 4 ++-- lib/twitter/settings.rb | 12 +++++----- lib/twitter/size.rb | 4 ++-- lib/twitter/status.rb | 38 +++++++++++++++++--------------- lib/twitter/suggestion.rb | 14 +++++++----- lib/twitter/trend.rb | 4 ++-- lib/twitter/user.rb | 16 ++++++++------ spec/twitter/base_spec.rb | 7 ------ 22 files changed, 137 insertions(+), 116 deletions(-) diff --git a/lib/twitter/base.rb b/lib/twitter/base.rb index a142ecb32..677f89553 100644 --- a/lib/twitter/base.rb +++ b/lib/twitter/base.rb @@ -1,18 +1,22 @@ module Twitter class Base - def initialize(hash={}) - hash.each do |key, value| - instance_variable_set(:"@#{key}", value) + def self.lazy_attr_reader(*attributes) + attributes.each do |attribute| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{attribute} + @#{attribute} ||= @attributes[#{attribute.to_s.inspect}] + end + RUBY end end - def [](method) - self.__send__(method.to_sym) + def initialize(attributes = {}) + @attributes = attributes.dup end - def to_hash - Hash[instance_variables.map{|ivar| [ivar[1..-1].to_sym, instance_variable_get(ivar)]}] + def [](method) + self.__send__(method.to_sym) end end diff --git a/lib/twitter/configuration.rb b/lib/twitter/configuration.rb index e5e176eab..768d8784b 100644 --- a/lib/twitter/configuration.rb +++ b/lib/twitter/configuration.rb @@ -4,15 +4,16 @@ module Twitter class Configuration < Twitter::Base - attr_reader :characters_reserved_per_media, :max_media_per_upload, - :non_username_paths, :photo_size_limit, :photo_sizes, :short_url_length, - :short_url_length_https + attr_reader :photo_sizes + lazy_attr_reader :characters_reserved_per_media, :max_media_per_upload, + :non_username_paths, :photo_size_limit, :short_url_length, :short_url_length_https - def initialize(configuration={}) - @photo_sizes = configuration.delete('photo_sizes').each_with_object({}) do |(key, value), object| + def initialize(attributes={}) + attributes = attributes.dup + @photo_sizes = attributes.delete('photo_sizes').each_with_object({}) do |(key, value), object| object[key] = Twitter::Size.new(value) - end unless configuration['photo_sizes'].nil? - super(configuration) + end unless attributes['photo_sizes'].nil? + super(attributes) end end diff --git a/lib/twitter/creatable.rb b/lib/twitter/creatable.rb index 748f04e1e..9ea1d9a94 100644 --- a/lib/twitter/creatable.rb +++ b/lib/twitter/creatable.rb @@ -4,9 +4,10 @@ module Twitter module Creatable attr_reader :created_at - def initialize(hash={}) - @created_at = Time.parse(hash.delete('created_at')) unless hash['created_at'].nil? - super(hash) + def initialize(attributes={}) + attributes = attributes.dup + @created_at = Time.parse(attributes.delete('created_at')) unless attributes['created_at'].nil? + super(attributes) end end diff --git a/lib/twitter/cursor.rb b/lib/twitter/cursor.rb index 1d0830100..7a9839ffd 100644 --- a/lib/twitter/cursor.rb +++ b/lib/twitter/cursor.rb @@ -3,31 +3,33 @@ module Twitter class Cursor < Twitter::Base - attr_reader :collection, :next_cursor, :previous_cursor + attr_reader :collection + lazy_attr_reader :next_cursor, :previous_cursor alias :next :next_cursor alias :previous :previous_cursor - def initialize(cursor, method, klass=nil) - @collection = cursor.delete(method.to_s).map do |item| + def initialize(attributes, method, klass=nil) + attributes = attributes.dup + @collection = attributes.delete(method.to_s).map do |item| if klass klass.new(item) else item end - end unless cursor[method.to_s].nil? + end unless attributes[method.to_s].nil? singleton_class.class_eval do alias_method method.to_sym, :collection end - super(cursor) + super(attributes) end def first? - @previous_cursor.zero? + previous_cursor.zero? end alias :first :first? def last? - @next_cursor.zero? + next_cursor.zero? end alias :last :last? diff --git a/lib/twitter/direct_message.rb b/lib/twitter/direct_message.rb index e0b31fda9..bc4fb19db 100644 --- a/lib/twitter/direct_message.rb +++ b/lib/twitter/direct_message.rb @@ -5,16 +5,18 @@ module Twitter class DirectMessage < Twitter::Base include Twitter::Creatable - attr_reader :id, :recipient, :sender, :text + attr_reader :recipient, :sender + lazy_attr_reader :id, :text - def initialize(direct_message={}) - @recipient = Twitter::User.new(direct_message.delete('recipient')) unless direct_message['recipient'].nil? - @sender = Twitter::User.new(direct_message.delete('sender')) unless direct_message['sender'].nil? - super(direct_message) + def initialize(attributes={}) + attributes = attributes.dup + @recipient = Twitter::User.new(attributes.delete('recipient')) unless attributes['recipient'].nil? + @sender = Twitter::User.new(attributes.delete('sender')) unless attributes['sender'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/language.rb b/lib/twitter/language.rb index 439d0bcfa..97c40ca9c 100644 --- a/lib/twitter/language.rb +++ b/lib/twitter/language.rb @@ -2,6 +2,6 @@ module Twitter class Language < Twitter::Base - attr_reader :code, :name, :status + lazy_attr_reader :code, :name, :status end end diff --git a/lib/twitter/list.rb b/lib/twitter/list.rb index 31264db77..35134bca3 100644 --- a/lib/twitter/list.rb +++ b/lib/twitter/list.rb @@ -3,17 +3,19 @@ module Twitter class List < Twitter::Base - attr_reader :description, :following, :full_name, :id, :member_count, - :mode, :name, :slug, :subscriber_count, :uri, :user + attr_reader :user + lazy_attr_reader :description, :following, :full_name, :id, :member_count, + :mode, :name, :slug, :subscriber_count, :uri alias :following? :following - def initialize(list={}) - @user = Twitter::User.new(list.delete('user')) unless list['user'].nil? - super(list) + def initialize(attributes={}) + attributes = attributes.dup + @user = Twitter::User.new(attributes.delete('user')) unless attributes['user'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/metadata.rb b/lib/twitter/metadata.rb index eee61309f..272cdb079 100644 --- a/lib/twitter/metadata.rb +++ b/lib/twitter/metadata.rb @@ -2,6 +2,6 @@ module Twitter class Metadata < Twitter::Base - attr_reader :result_type + lazy_attr_reader :result_type end end diff --git a/lib/twitter/photo.rb b/lib/twitter/photo.rb index e20159a6f..30ccc7614 100644 --- a/lib/twitter/photo.rb +++ b/lib/twitter/photo.rb @@ -4,18 +4,20 @@ module Twitter class Photo < Twitter::Base - attr_reader :display_url, :expanded_url, :id, :indices, :media_url, - :media_url_https, :sizes, :url + attr_reader :sizes + lazy_attr_reader :display_url, :expanded_url, :id, :indices, :media_url, + :media_url_https, :url - def initialize(photo={}) - @sizes = photo.delete('sizes').each_with_object({}) do |(key, value), object| + def initialize(attributes={}) + attributes = attributes.dup + @sizes = attributes['sizes'].each_with_object({}) do |(key, value), object| object[key] = Twitter::Size.new(value) - end unless photo['sizes'].nil? - super(photo) + end unless attributes['sizes'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/place.rb b/lib/twitter/place.rb index 111dce581..216881ef9 100644 --- a/lib/twitter/place.rb +++ b/lib/twitter/place.rb @@ -3,18 +3,21 @@ module Twitter class Place < Twitter::Base - attr_reader :attributes, :bounding_box, :country, :country_code, - :full_name, :id, :name, :place_type, :parent_id, :url, :woeid + attr_reader :bounding_box, :country_code, :parent_id, :place_type + lazy_attr_reader :attributes, :country, :full_name, :id, :name, :url, + :woeid - def initialize(place={}) - @bounding_box = Twitter::GeoFactory.new(place.delete('bounding_box')) unless place['bounding_box'].nil? - @country_code = place.delete('country_code') || place.delete('countryCode') - @place_type = place.delete('place_type') || place['placeType'] && place['placeType'].delete('name') - super(place) + def initialize(attributes={}) + attributes = attributes.dup + @bounding_box = Twitter::GeoFactory.new(attributes['bounding_box']) unless attributes['bounding_box'].nil? + @country_code = attributes['country_code'] || attributes['countryCode'] + @parent_id = attributes['parentid'] + @place_type = attributes['place_type'] || attributes['placeType'] && attributes['placeType']['name'] + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/point.rb b/lib/twitter/point.rb index 950ccea30..9f19d953e 100644 --- a/lib/twitter/point.rb +++ b/lib/twitter/point.rb @@ -2,19 +2,19 @@ module Twitter class Point < Twitter::Base - attr_reader :coordinates + lazy_attr_reader :coordinates def ==(other) - super || (other.class == self.class && other.instance_variable_get('@coordinates'.to_sym) == @coordinates) + super || (other.class == self.class && other.coordinates == self.coordinates) end def latitude - @coordinates[0] + coordinates[0] end alias :lat :latitude def longitude - @coordinates[1] + coordinates[1] end alias :long :longitude alias :lng :longitude diff --git a/lib/twitter/polygon.rb b/lib/twitter/polygon.rb index 44bb956fa..9565605bd 100644 --- a/lib/twitter/polygon.rb +++ b/lib/twitter/polygon.rb @@ -2,10 +2,10 @@ module Twitter class Polygon < Twitter::Base - attr_reader :coordinates + lazy_attr_reader :coordinates def ==(other) - super || (other.class == self.class && other.instance_variable_get('@coordinates'.to_sym) == @coordinates) + super || (other.class == self.class && other.coordinates == self.coordinates) end end diff --git a/lib/twitter/rate_limit_status.rb b/lib/twitter/rate_limit_status.rb index b2036359d..c471ee2fc 100644 --- a/lib/twitter/rate_limit_status.rb +++ b/lib/twitter/rate_limit_status.rb @@ -2,11 +2,13 @@ module Twitter class RateLimitStatus < Twitter::Base - attr_reader :hourly_limit, :remaining_hits, :reset_time, :reset_time_in_seconds + attr_reader :reset_time + lazy_attr_reader :hourly_limit, :remaining_hits, :reset_time_in_seconds - def initialize(rate_limit_status={}) - @reset_time = Time.parse(rate_limit_status.delete('reset_time')) unless rate_limit_status['reset_time'].nil? - super(rate_limit_status) + def initialize(attributes={}) + attributes = attributes.dup + @reset_time = Time.parse(attributes['reset_time']) unless attributes['reset_time'].nil? + super(attributes) end end diff --git a/lib/twitter/relationship.rb b/lib/twitter/relationship.rb index 7413ced50..a16d89501 100644 --- a/lib/twitter/relationship.rb +++ b/lib/twitter/relationship.rb @@ -5,10 +5,11 @@ module Twitter class Relationship < Twitter::Base attr_reader :source, :target - def initialize(relationship={}) - @source = Twitter::User.new(relationship.delete('source')) unless relationship['source'].nil? - @target = Twitter::User.new(relationship.delete('target')) unless relationship['target'].nil? - super(relationship) + def initialize(attributes={}) + attributes = attributes.dup + @source = Twitter::User.new(attributes.delete('source')) unless attributes['source'].nil? + @target = Twitter::User.new(attributes.delete('target')) unless attributes['target'].nil? + super(attributes) end end diff --git a/lib/twitter/saved_search.rb b/lib/twitter/saved_search.rb index 733004e8c..58c7fd801 100644 --- a/lib/twitter/saved_search.rb +++ b/lib/twitter/saved_search.rb @@ -4,10 +4,10 @@ module Twitter class SavedSearch < Twitter::Base include Twitter::Creatable - attr_reader :id, :name, :position, :query + lazy_attr_reader :id, :name, :position, :query def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/settings.rb b/lib/twitter/settings.rb index 9267bf1bb..1477fc31b 100644 --- a/lib/twitter/settings.rb +++ b/lib/twitter/settings.rb @@ -3,14 +3,16 @@ module Twitter class Settings < Twitter::Base - attr_reader :always_use_https, :discoverable_by_email, :geo_enabled, + attr_reader :trend_location + lazy_attr_reader :always_use_https, :discoverable_by_email, :geo_enabled, :language, :protected, :screen_name, :show_all_inline_media, :sleep_time, - :time_zone, :trend_location + :time_zone alias :protected? :protected - def initialize(settings={}) - @trend_location = Twitter::Place.new(settings.delete('trend_location').first) unless settings['trend_location'].nil? || settings['trend_location'].empty? - super(settings) + def initialize(attributes={}) + attributes = attributes.dup + @trend_location = Twitter::Place.new(attributes.delete('trend_location').first) unless attributes['trend_location'].nil? || attributes['trend_location'].empty? + super(attributes) end end diff --git a/lib/twitter/size.rb b/lib/twitter/size.rb index e155a65c0..1e47417ca 100644 --- a/lib/twitter/size.rb +++ b/lib/twitter/size.rb @@ -2,12 +2,12 @@ module Twitter class Size < Twitter::Base - attr_reader :h, :resize, :w + lazy_attr_reader :h, :resize, :w alias :height :h alias :width :w def ==(other) - super || (other.class == self.class && other.instance_variable_get('@h'.to_sym) == @h && other.instance_variable_get('@w'.to_sym) == @w) + super || (other.class == self.class && other.h == self.h && other.w == self.w) end end diff --git a/lib/twitter/status.rb b/lib/twitter/status.rb index 8a1e8ba74..1f1c238a0 100644 --- a/lib/twitter/status.rb +++ b/lib/twitter/status.rb @@ -10,32 +10,34 @@ module Twitter class Status < Twitter::Base include Twitter::Creatable - attr_reader :favorited, :from_user, :from_user_id, :geo, :hashtags, :id, - :in_reply_to_screen_name, :in_reply_to_status_id, :in_reply_to_user_id, - :iso_language_code, :metadata, :profile_image_url, :media, :place, - :retweet_count, :retweeted, :source, :text, :to_user, :to_user_id, - :truncated, :urls, :user, :user_mentions + attr_reader :geo, :hashtags, :media, :metadata, :place, :urls, :user, + :user_mentions + lazy_attr_reader :favorited, :from_user, :from_user_id, :id, + :in_reply_to_screen_name, :in_reply_to_attributes_id, :in_reply_to_user_id, + :iso_language_code, :profile_image_url, :retweet_count, :retweeted, + :source, :text, :to_user, :to_user_id, :truncated alias :favorited? :favorited alias :mentions :user_mentions alias :retweeted? :retweeted alias :truncated? :truncated - def initialize(status={}) - @geo = Twitter::GeoFactory.new(status.delete('geo')) unless status['geo'].nil? - @hashtags = Twitter::Extractor.extract_hashtags(status['text']) unless status['text'].nil? - @media = status['entities'].delete('media').map do |medium| - Twitter::MediaFactory.new(medium) - end unless status['entities'].nil? || status['entities']['media'].nil? - @metadata = Twitter::Metadata.new(status.delete('metadata')) unless status['metadata'].nil? - @place = Twitter::Place.new(status.delete('place')) unless status['place'].nil? - @urls = Twitter::Extractor.extract_urls(status['text']) unless status['text'].nil? - @user = Twitter::User.new(status.delete('user').merge('status' => self.to_hash.delete('user'))) unless status['user'].nil? - @user_mentions = Twitter::Extractor.extract_mentioned_screen_names(status['text']) unless status['text'].nil? - super(status) + def initialize(attributes={}) + attributes = attributes.dup + @geo = Twitter::GeoFactory.new(attributes.delete('geo')) unless attributes['geo'].nil? + @hashtags = Twitter::Extractor.extract_hashtags(attributes['text']) unless attributes['text'].nil? + @media = attributes['entities'].delete('media').map do |media| + Twitter::MediaFactory.new(media) + end unless attributes['entities'].nil? || attributes['entities']['media'].nil? + @metadata = Twitter::Metadata.new(attributes.delete('metadata')) unless attributes['metadata'].nil? + @place = Twitter::Place.new(attributes.delete('place')) unless attributes['place'].nil? + @urls = Twitter::Extractor.extract_urls(attributes['text']) unless attributes['text'].nil? + @user = Twitter::User.new(attributes.delete('user')) unless attributes['user'].nil? + @user_mentions = Twitter::Extractor.extract_mentioned_screen_names(attributes['text']) unless attributes['text'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/lib/twitter/suggestion.rb b/lib/twitter/suggestion.rb index ee1acca48..a8d74900c 100644 --- a/lib/twitter/suggestion.rb +++ b/lib/twitter/suggestion.rb @@ -3,17 +3,19 @@ module Twitter class Suggestion < Twitter::Base - attr_reader :name, :size, :slug, :users + attr_reader :users + lazy_attr_reader :name, :size, :slug - def initialize(suggestion={}) - @users = suggestion.delete('users').map do |user| + def initialize(attributes={}) + attributes = attributes.dup + @users = attributes.delete('users').map do |user| Twitter::User.new(user) - end unless suggestion['users'].nil? - super(suggestion) + end unless attributes['users'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@slug'.to_sym) == @slug) + super || (other.class == self.class && other.slug == self.slug) end end diff --git a/lib/twitter/trend.rb b/lib/twitter/trend.rb index 12595853b..9bacac484 100644 --- a/lib/twitter/trend.rb +++ b/lib/twitter/trend.rb @@ -2,10 +2,10 @@ module Twitter class Trend < Twitter::Base - attr_reader :events, :name, :promoted_content, :query, :url + lazy_attr_reader :events, :name, :promoted_content, :query, :url def ==(other) - super || (other.class == self.class && other.instance_variable_get('@name'.to_sym) == @name) + super || (other.class == self.class && other.name == self.name) end end diff --git a/lib/twitter/user.rb b/lib/twitter/user.rb index df400a876..d37f67a46 100644 --- a/lib/twitter/user.rb +++ b/lib/twitter/user.rb @@ -7,7 +7,8 @@ module Twitter class User < Twitter::Base include Twitter::Authenticatable include Twitter::Creatable - attr_reader :all_replies, :blocking, :can_dm, :connections, + attr_reader :status + lazy_attr_reader :all_replies, :blocking, :can_dm, :connections, :contributors_enabled, :default_profile, :default_profile_image, :description, :favourites_count, :follow_request_sent, :followed_by, :followers_count, :following, :friends_count, :geo_enabled, :id, @@ -17,8 +18,8 @@ class User < Twitter::Base :profile_background_tile, :profile_image_url, :profile_image_url_https, :profile_link_color, :profile_sidebar_border_color, :profile_sidebar_fill_color, :profile_text_color, - :profile_use_background_image, :protected, :screen_name, :status, - :statuses_count, :time_zone, :url, :utc_offset, :verified, :want_retweets + :profile_use_background_image, :protected, :screen_name, :statuses_count, + :time_zone, :url, :utc_offset, :verified, :want_retweets alias :all_replies? :all_replies alias :blocking? :blocking alias :can_dm? :can_dm @@ -49,13 +50,14 @@ class User < Twitter::Base alias :verified? :verified alias :want_retweets? :want_retweets - def initialize(user={}) - @status = Twitter::Status.new(user.delete('status').merge('user' => self.to_hash.delete('status'))) unless user['status'].nil? - super(user) + def initialize(attributes={}) + attributes = attributes.dup + @status = Twitter::Status.new(attributes.delete('status')) unless attributes['status'].nil? + super(attributes) end def ==(other) - super || (other.class == self.class && other.instance_variable_get('@id'.to_sym) == @id) + super || (other.class == self.class && other.id == self.id) end end diff --git a/spec/twitter/base_spec.rb b/spec/twitter/base_spec.rb index 495620ca6..548f83042 100644 --- a/spec/twitter/base_spec.rb +++ b/spec/twitter/base_spec.rb @@ -15,11 +15,4 @@ end end - describe "#to_hash" do - it "should return a hash" do - @base.to_hash.should be_a Hash - @base.to_hash[:id].should == 1 - end - end - end