dschn-twitter 0.3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/History.txt +106 -0
  2. data/License.txt +19 -0
  3. data/Manifest.txt +71 -0
  4. data/README.txt +84 -0
  5. data/Rakefile +4 -0
  6. data/bin/twitter +15 -0
  7. data/config/hoe.rb +74 -0
  8. data/config/requirements.rb +17 -0
  9. data/examples/blocks.rb +15 -0
  10. data/examples/direct_messages.rb +28 -0
  11. data/examples/favorites.rb +20 -0
  12. data/examples/friends_followers.rb +25 -0
  13. data/examples/friendships.rb +13 -0
  14. data/examples/identica_timeline.rb +7 -0
  15. data/examples/location.rb +8 -0
  16. data/examples/posting.rb +9 -0
  17. data/examples/replies.rb +26 -0
  18. data/examples/search.rb +17 -0
  19. data/examples/sent_messages.rb +26 -0
  20. data/examples/timeline.rb +33 -0
  21. data/examples/twitter.rb +27 -0
  22. data/examples/verify_credentials.rb +13 -0
  23. data/lib/twitter.rb +21 -0
  24. data/lib/twitter/base.rb +260 -0
  25. data/lib/twitter/cli.rb +328 -0
  26. data/lib/twitter/cli/config.rb +9 -0
  27. data/lib/twitter/cli/helpers.rb +97 -0
  28. data/lib/twitter/cli/migrations/20080722194500_create_accounts.rb +13 -0
  29. data/lib/twitter/cli/migrations/20080722194508_create_tweets.rb +16 -0
  30. data/lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb +9 -0
  31. data/lib/twitter/cli/migrations/20080722214606_create_configurations.rb +13 -0
  32. data/lib/twitter/cli/models/account.rb +33 -0
  33. data/lib/twitter/cli/models/configuration.rb +13 -0
  34. data/lib/twitter/cli/models/tweet.rb +20 -0
  35. data/lib/twitter/direct_message.rb +22 -0
  36. data/lib/twitter/easy_class_maker.rb +43 -0
  37. data/lib/twitter/rate_limit_status.rb +19 -0
  38. data/lib/twitter/search.rb +94 -0
  39. data/lib/twitter/status.rb +22 -0
  40. data/lib/twitter/user.rb +37 -0
  41. data/lib/twitter/version.rb +9 -0
  42. data/script/destroy +14 -0
  43. data/script/generate +14 -0
  44. data/script/txt2html +74 -0
  45. data/setup.rb +1585 -0
  46. data/spec/base_spec.rb +109 -0
  47. data/spec/cli/helper_spec.rb +35 -0
  48. data/spec/direct_message_spec.rb +35 -0
  49. data/spec/fixtures/followers.xml +706 -0
  50. data/spec/fixtures/friends.xml +609 -0
  51. data/spec/fixtures/friends_for.xml +584 -0
  52. data/spec/fixtures/friends_lite.xml +192 -0
  53. data/spec/fixtures/friends_timeline.xml +66 -0
  54. data/spec/fixtures/public_timeline.xml +148 -0
  55. data/spec/fixtures/rate_limit_status.xml +7 -0
  56. data/spec/fixtures/search_results.json +1 -0
  57. data/spec/fixtures/status.xml +25 -0
  58. data/spec/fixtures/user.xml +38 -0
  59. data/spec/fixtures/user_timeline.xml +465 -0
  60. data/spec/search_spec.rb +89 -0
  61. data/spec/spec.opts +1 -0
  62. data/spec/spec_helper.rb +12 -0
  63. data/spec/status_spec.rb +40 -0
  64. data/spec/user_spec.rb +42 -0
  65. data/tasks/deployment.rake +50 -0
  66. data/tasks/environment.rake +7 -0
  67. data/tasks/website.rake +17 -0
  68. data/twitter.gemspec +49 -0
  69. data/website/css/common.css +47 -0
  70. data/website/images/terminal_output.png +0 -0
  71. data/website/index.html +156 -0
  72. metadata +180 -0
@@ -0,0 +1,16 @@
1
+ class CreateTweets < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tweets do |t|
4
+ t.datetime :occurred_at
5
+ t.boolean :truncated, :favorited, :user_protected, :default => false
6
+ t.integer :twitter_id, :user_id, :in_reply_to_status_id, :in_reply_to_user_id, :user_followers_count
7
+ t.text :body
8
+ t.string :source, :user_name, :user_screen_name, :user_location, :user_description, :user_profile_image_url, :user_url
9
+ t.timestamps
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :tweets
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ class AddAccountIdToTweets < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :tweets, :account_id, :integer
4
+ end
5
+
6
+ def self.down
7
+ remove_column :tweets, :account_id
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ class CreateConfigurations < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :configurations do |t|
4
+ t.string :key
5
+ t.text :data
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :accounts
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ class Account < ActiveRecord::Base
2
+ named_scope :current, :conditions => {:current => true}
3
+
4
+ has_many :tweets, :dependent => :destroy
5
+
6
+ def self.add(hash)
7
+ username = hash.delete(:username)
8
+ account = find_or_initialize_by_username(username)
9
+ account.attributes = hash
10
+ account.save
11
+ set_current(account) if new_active_needed?
12
+ end
13
+
14
+ def self.active
15
+ current.first
16
+ end
17
+
18
+ def self.set_current(account_or_id)
19
+ account = account_or_id.is_a?(Account) ? account_or_id : find(account_or_id)
20
+ account.update_attribute :current, true
21
+ Account.update_all "current = 0", "id != #{account.id}"
22
+ account
23
+ end
24
+
25
+ def self.new_active_needed?
26
+ self.current.count == 0 && self.count > 0
27
+ end
28
+
29
+ def to_s
30
+ "#{current? ? '*' : ' '} #{username}"
31
+ end
32
+ alias to_str to_s
33
+ end
@@ -0,0 +1,13 @@
1
+ class Configuration < ActiveRecord::Base
2
+ serialize :data
3
+
4
+ def self.[](key)
5
+ key = find_by_key(key.to_s)
6
+ key.nil? ? nil : key.data
7
+ end
8
+
9
+ def self.[]=(key, data)
10
+ c = find_or_create_by_key(key.to_s)
11
+ c.update_attribute :data, data
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ class Tweet < ActiveRecord::Base
2
+ belongs_to :account
3
+
4
+ def self.create_from_tweet(account, s)
5
+ tweet = account.tweets.find_or_initialize_by_twitter_id(s.id)
6
+ tweet.body = s.text
7
+ tweet.occurred_at = s.created_at
8
+
9
+ %w[truncated favorited in_reply_to_status_id in_reply_to_user_id source].each do |m|
10
+ tweet.send("#{m}=", s.send(m))
11
+ end
12
+
13
+ %w[id followers_count name screen_name location description
14
+ profile_image_url url protected].each do |m|
15
+ tweet.send("user_#{m}=", s.user.send(m))
16
+ end
17
+ tweet.save!
18
+ tweet
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module Twitter
2
+ class DirectMessage
3
+ include EasyClassMaker
4
+
5
+ attributes :id, :text, :sender_id, :recipient_id, :created_at, :sender_screen_name, :recipient_screen_name
6
+
7
+ class << self
8
+ # Creates a new status from a piece of xml
9
+ def new_from_xml(xml)
10
+ DirectMessage.new do |d|
11
+ d.id = (xml).at('id').innerHTML
12
+ d.text = (xml).get_elements_by_tag_name('text').innerHTML
13
+ d.sender_id = (xml).at('sender_id').innerHTML
14
+ d.recipient_id = (xml).at('recipient_id').innerHTML
15
+ d.created_at = (xml).at('created_at').innerHTML
16
+ d.sender_screen_name = (xml).at('sender_screen_name').innerHTML
17
+ d.recipient_screen_name = (xml).at('recipient_screen_name').innerHTML
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ # This is pretty much just a macro for creating a class that allows
2
+ # using a block to initialize stuff and to define getters and setters
3
+ # really quickly.
4
+ module Twitter
5
+ module EasyClassMaker
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ # creates the attributes class variable and creates each attribute's accessor methods
13
+ def attributes(*attrs)
14
+ @@attributes = attrs
15
+ @@attributes.each { |a| attr_accessor a }
16
+ end
17
+
18
+ # read method for attributes class variable
19
+ def self.attributes; @@attributes end
20
+ end
21
+
22
+ # allows for any class that includes this to use a block to initialize
23
+ # variables instead of assigning each one seperately
24
+ #
25
+ # Example:
26
+ #
27
+ # instead of...
28
+ #
29
+ # s = Status.new
30
+ # s.foo = 'thing'
31
+ # s.bar = 'another thing'
32
+ #
33
+ # you can ...
34
+ #
35
+ # Status.new do |s|
36
+ # s.foo = 'thing'
37
+ # s.bar = 'another thing'
38
+ # end
39
+ def initialize
40
+ yield self if block_given?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ module Twitter
2
+ class RateLimitStatus
3
+ include EasyClassMaker
4
+
5
+ attributes :reset_time_in_seconds, :reset_time, :remaining_hits, :hourly_limit
6
+
7
+ class << self
8
+ # Creates a new rate limi status from a piece of xml
9
+ def new_from_xml(xml)
10
+ RateLimitStatus.new do |s|
11
+ s.reset_time_in_seconds = xml.at('reset-time-in-seconds').inner_html.to_i
12
+ s.reset_time = Time.parse xml.at('reset-time').inner_html
13
+ s.remaining_hits = xml.at('remaining-hits').inner_html.to_i
14
+ s.hourly_limit = xml.at('hourly-limit').inner_html.to_i
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,94 @@
1
+ gem 'httparty'
2
+ require 'httparty'
3
+
4
+ module Twitter
5
+ class Search
6
+ include HTTParty
7
+ include Enumerable
8
+ base_uri 'search.twitter.com'
9
+
10
+ attr_reader :result, :query
11
+
12
+ def initialize(q=nil)
13
+ clear
14
+ containing(q) unless q.blank?
15
+ end
16
+
17
+ def from(user)
18
+ @query[:q] << "from:#{user}"
19
+ self
20
+ end
21
+
22
+ def to(user)
23
+ @query[:q] << "to:#{user}"
24
+ self
25
+ end
26
+
27
+ def referencing(user)
28
+ @query[:q] << "@#{user}"
29
+ self
30
+ end
31
+ alias :references :referencing
32
+ alias :ref :referencing
33
+
34
+ def containing(word)
35
+ @query[:q] << "#{word}"
36
+ self
37
+ end
38
+ alias :contains :containing
39
+
40
+ # adds filtering based on hash tag ie: #twitter
41
+ def hashed(tag)
42
+ @query[:q] << "##{tag}"
43
+ self
44
+ end
45
+
46
+ # lang must be ISO 639-1 code ie: en, fr, de, ja, etc.
47
+ #
48
+ # when I tried en it limited my results a lot and took
49
+ # out several tweets that were english so i'd avoid
50
+ # this unless you really want it
51
+ def lang(lang)
52
+ @query[:lang] = lang
53
+ self
54
+ end
55
+
56
+ # Limits the number of results per page
57
+ def per_page(num)
58
+ @query[:rpp] = num
59
+ self
60
+ end
61
+
62
+ # Only searches tweets since a given id.
63
+ # Recommended to use this when possible.
64
+ def since(since_id)
65
+ @query[:since_id] = since_id
66
+ self
67
+ end
68
+
69
+ # Search tweets by longitude, latitude and a given range.
70
+ # Ranges like 25km and 50mi work.
71
+ def geocode(long, lat, range)
72
+ @query[:geocode] = [long, lat, range].join(',')
73
+ self
74
+ end
75
+
76
+ # Clears all the query filters to make a new search
77
+ def clear
78
+ @query = {}
79
+ @query[:q] = []
80
+ self
81
+ end
82
+
83
+ # If you want to get results do something other than iterate over them.
84
+ def fetch
85
+ @query[:q] = @query[:q].join(' ')
86
+ self.class.get('/search.json', {:query => @query})
87
+ end
88
+
89
+ def each
90
+ @result = fetch()
91
+ @result['results'].each { |r| yield r }
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,22 @@
1
+ module Twitter
2
+ class Status
3
+ include EasyClassMaker
4
+
5
+ attributes :created_at, :id, :text, :user, :source, :truncated, :in_reply_to_status_id, :in_reply_to_user_id, :favorited
6
+
7
+ # Creates a new status from a piece of xml
8
+ def self.new_from_xml(xml)
9
+ s = new
10
+ s.id = (xml).at('id').innerHTML
11
+ s.created_at = (xml).at('created_at').innerHTML
12
+ s.text = (xml).get_elements_by_tag_name('text').innerHTML
13
+ s.source = (xml).at('source').innerHTML
14
+ s.truncated = (xml).at('truncated').innerHTML == 'false' ? false : true
15
+ s.favorited = (xml).at('favorited').innerHTML == 'false' ? false : true
16
+ s.in_reply_to_status_id = (xml).at('in_reply_to_status_id').innerHTML
17
+ s.in_reply_to_user_id = (xml).at('in_reply_to_user_id').innerHTML
18
+ s.user = User.new_from_xml(xml.at('user')) if (xml).at('user')
19
+ s
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module Twitter
2
+ class User
3
+ include EasyClassMaker
4
+
5
+ attributes :id, :name, :screen_name, :status, :location, :description, :url,
6
+ :profile_image_url, :profile_background_color, :profile_text_color, :profile_link_color,
7
+ :profile_sidebar_fill_color, :profile_sidebar_border_color, :friends_count, :followers_count,
8
+ :favourites_count, :statuses_count, :utc_offset , :protected
9
+
10
+ # Creates a new user from a piece of xml
11
+ def self.new_from_xml(xml)
12
+ u = new
13
+ u.id = (xml).at('id').innerHTML
14
+ u.name = (xml).at('name').innerHTML
15
+ u.screen_name = (xml).at('screen_name').innerHTML
16
+ u.location = (xml).at('location').innerHTML
17
+ u.description = (xml).at('description').innerHTML
18
+ u.url = (xml).at('url').innerHTML
19
+ u.profile_image_url = (xml).at('profile_image_url').innerHTML
20
+
21
+ # optional, not always present
22
+ u.profile_background_color = (xml).at('profile_background_color').innerHTML if (xml).at('profile_background_color')
23
+ u.profile_text_color = (xml).at('profile_text_color').innerHTML if (xml).at('profile_text_color')
24
+ u.profile_link_color = (xml).at('profile_link_color').innerHTML if (xml).at('profile_link_color')
25
+ u.profile_sidebar_fill_color = (xml).at('profile_sidebar_fill_color').innerHTML if (xml).at('profile_sidebar_fill_color')
26
+ u.profile_sidebar_border_color = (xml).at('profile_sidebar_border_color').innerHTML if (xml).at('profile_sidebar_border_color')
27
+ u.friends_count = (xml).at('friends_count').innerHTML if (xml).at('friends_count')
28
+ u.followers_count = (xml).at('followers_count').innerHTML if (xml).at('followers_count')
29
+ u.favourites_count = (xml).at('favourites_count').innerHTML if (xml).at('favourites_count')
30
+ u.statuses_count = (xml).at('statuses_count').innerHTML if (xml).at('statuses_count')
31
+ u.utc_offset = (xml).at('utc_offset').innerHTML if (xml).at('utc_offset')
32
+ u.protected = (xml).at('protected').innerHTML == 'false' ? false : true if (xml).at('protected')
33
+ u.status = Status.new_from_xml(xml.at('status')) if (xml).at('status')
34
+ u
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ module Twitter #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 3
5
+ TINY = 7
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/twitter/version.rb'
15
+
16
+ version = Twitter.git::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/twitter'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)