twitter_backup 0.1.0

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in twitter_backup.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 il.zoff
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ # TwitterBackup
2
+
3
+ This gem will download your tweets from Twitter and save them to sqlite3 database and plaintext (yaml) archive file.
4
+
5
+ ## Installation
6
+
7
+ $ gem install twitter_backup
8
+
9
+ ## Usage
10
+
11
+ First of all, you need to create an app at https://dev.twitter.com/apps
12
+ Your app will be assigned a 'consumer key'/'consumer secret' pair and you as a user will be assigned an 'access token'/'acces token secret' OAuth pair for that application.
13
+ Without these credentials you won't be able to use TwitterBackup.
14
+
15
+ Now, all you have to do is to run
16
+
17
+ $ twitter_backup
18
+
19
+ If you want to know, what's happening during your backup process, use verbose mode with `-v`.
20
+
21
+ $ twitter_backup -h
22
+ -v, --verbose Enable verbose mode
23
+ -f, --force Try to download tweets, even if it seems useless
24
+ -s, --seed Try to download tweets older than the oldest one you have
25
+ -c, --config Config file. Default: ~/.config/twitter_backup/config.yml
26
+ -h, --help Display this help message.
27
+
28
+ ## TODO
29
+
30
+ - Automate adding this script to cron?
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ # require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib twitter_backup]))
5
+ require "twitter_backup"
6
+ require "twitter_backup/ui"
7
+
8
+ include TwitterBackup
9
+
10
+ if TBConfig.passed_opts.help?
11
+ TBConfig.passed_opts.help
12
+ exit 0
13
+ end
14
+
15
+ begin
16
+
17
+ say ".... checking existance of credentials" if TBConfig.passed_opts.verbose?
18
+ UI.ask_credentials if TBConfig.credentials_missing?
19
+
20
+ UI.define_path_to_backup unless TBConfig.path_to_backup_defined?
21
+
22
+ TBConfig.configure
23
+
24
+ UI.greet_user
25
+
26
+ Tweet.update_tweets
27
+ Tweet.update_tweets(:from => :earliest) if TBConfig.passed_opts.seed?
28
+
29
+ Tweet.dump_to_backup_file
30
+
31
+ rescue Error::MissingCredentials => error
32
+ UI.missing_credentials_exit
33
+
34
+ rescue Twitter::Error::Unauthorized => error
35
+ TBConfig.empty_credentials
36
+ UI.wrong_credentials_exit
37
+
38
+ rescue Twitter::Error::TooManyRequests => error
39
+ UI.too_many_requests_exit
40
+
41
+ rescue Error::InvalidBackupFile => error
42
+ UI.failed_backup_path_exit( error.message )
43
+
44
+ rescue Error::InvalidPath => error
45
+ say %[<%= color("#{error.message}", :red) %>]
46
+ exit 1
47
+ ensure
48
+ UI.exit_screen
49
+ end
@@ -0,0 +1,47 @@
1
+ require "slop"
2
+ require "yaml"
3
+ require "fileutils"
4
+ require "highline/import"
5
+ require 'active_support/core_ext/string'
6
+ require "active_record"
7
+ require "twitter"
8
+ require_relative "twitter_backup/version"
9
+ require_relative "twitter_backup/error"
10
+ require_relative "twitter_backup/config"
11
+ require_relative "twitter_backup/tweet"
12
+
13
+ module TwitterBackup
14
+ def self.prepare_file file
15
+ file = File.expand_path file
16
+ dir = File.dirname file
17
+ FileUtils.mkdir_p dir unless File.exist? dir
18
+ FileUtils.touch file unless File.exist? file
19
+ raise( Error::InvalidPath, "Can't create file: #{file}" ) unless File.exist?( file )
20
+ end
21
+ end
22
+
23
+ unless Kernel.respond_to?(:require_relative)
24
+ module Kernel
25
+ def require_relative(path)
26
+ require File.join(File.dirname(caller[0]), path.to_str)
27
+ end
28
+ end
29
+ end
30
+ class File
31
+ def tail(n)
32
+ buffer = 1024
33
+ idx = (size - buffer).abs
34
+ chunks = []
35
+ lines = 0
36
+ begin
37
+ seek(idx)
38
+ chunk = read(buffer)
39
+ lines += chunk ? chunk.count("\n") : 0
40
+ chunks.unshift chunk
41
+ idx -= buffer
42
+ end while lines < ( n + 1 ) && pos != 0
43
+ tail_of_file = chunks.join('')
44
+ ary = tail_of_file.split(/\n/)
45
+ lines_to_return = ary[ ary.size - n, ary.size - 1 ] || []
46
+ end
47
+ end
@@ -0,0 +1,138 @@
1
+
2
+ CONFIG_DIR = File.expand_path(File.join("~/", ".config", "twitter_backup"))
3
+ CONFIG_FILE = File.join(CONFIG_DIR, "config.yml")
4
+
5
+ CONFIG_DEFAULTS = {
6
+ :credentials => {
7
+ :consumer_key => "",
8
+ :consumer_secret => "",
9
+ :oauth_token => "",
10
+ :oauth_token_secret => "",
11
+ },
12
+ :db => {
13
+ :adapter => "sqlite3",
14
+ :database => File.join(CONFIG_DIR, "tweets.sqlite3"),
15
+ :pool => 5,
16
+ :timeout => 5000
17
+ },
18
+ :backup_file => ""
19
+ }
20
+
21
+ module TwitterBackup
22
+ class TBConfig
23
+ class << self
24
+ def load
25
+ TwitterBackup.prepare_file config_file
26
+ yaml = YAML::load_file( config_file )
27
+ @@options = if yaml.is_a? Hash
28
+ CONFIG_DEFAULTS.merge(yaml)
29
+ else
30
+ CONFIG_DEFAULTS
31
+ end
32
+ end
33
+
34
+ def save options
35
+ @@options = options
36
+ TwitterBackup.prepare_file config_file
37
+ File.open( config_file, "w" ){|f| YAML::dump( options, f )}
38
+ end
39
+
40
+ def options
41
+ @@options ||= load
42
+ end
43
+
44
+ def credentials_missing?
45
+ options[:credentials].values.map(&:strip).include? ""
46
+ end
47
+
48
+ def path_to_backup_defined?
49
+ options[:backup_file].present?
50
+ end
51
+
52
+ def save_backup_file file
53
+ options[:backup_file] = file
54
+ save options
55
+ end
56
+
57
+ def save_credentials new_credentials
58
+ options[:credentials] = options[:credentials].merge(new_credentials)
59
+ save options
60
+ end
61
+
62
+ def empty_credentials
63
+ new_credentials = { :consumer_key => "",
64
+ :consumer_secret => "",
65
+ :oauth_token => "",
66
+ :oauth_token_secret => ""}
67
+ save_credentials new_credentials
68
+ end
69
+
70
+ def configure_database
71
+ ActiveRecord::Base.establish_connection(options[:db])
72
+ unless ActiveRecord::Base.connection.tables.include?("tweets")
73
+ ActiveRecord::Migration.create_table :tweets do |t|
74
+ t.text :status
75
+ t.integer :status_id
76
+ t.timestamp :created_at
77
+ t.text :raw
78
+ end
79
+ end
80
+ end
81
+
82
+ def configure_twitter_gem
83
+ raise MissingCredentials if credentials_missing?
84
+ Twitter.configure do |config|
85
+ config.consumer_key = options[:credentials][:consumer_key]
86
+ config.consumer_secret = options[:credentials][:consumer_secret]
87
+ config.oauth_token = options[:credentials][:oauth_token]
88
+ config.oauth_token_secret = options[:credentials][:oauth_token_secret]
89
+ end
90
+ @@twitter_gem_configured = true
91
+ end
92
+
93
+ def check_backup_file
94
+ begin
95
+ backup_file = File.expand_path(options[:backup_file])
96
+ TwitterBackup.prepare_file( backup_file ) unless File.exist?( backup_file )
97
+ rescue Exception => e
98
+ raise Error::InvalidBackupFile, e.message
99
+ end
100
+ end
101
+
102
+ def configure
103
+ say ".... configuring database" if passed_opts.verbose?
104
+ configure_database
105
+
106
+ say ".... configuring twitter gem" if passed_opts.verbose?
107
+ configure_twitter_gem
108
+
109
+ say ".... checking backup file: #{options[:backup_file]}" if passed_opts.verbose?
110
+ check_backup_file
111
+ end
112
+
113
+ def verify_credentials
114
+ say ".... checking validity of credentials" if passed_opts.verbose?
115
+ configure_twitter_gem unless @@twitter_gem_configured
116
+ @@user = Twitter.verify_credentials(:skip_status => true, :include_entities => false)
117
+ end
118
+
119
+ def user
120
+ @@user ||= verify_credentials
121
+ end
122
+
123
+ def config_file
124
+ passed_opts[:config] || CONFIG_FILE
125
+ end
126
+
127
+ def passed_opts
128
+ @@opts ||= Slop.parse(:help => true) do
129
+ on :v, :verbose, 'Enable verbose mode'
130
+ on :f, :force, "Try to download tweets, even it seems useless"
131
+ on :s, :seed, "Try to download tweets older then the oldest one you have"
132
+ on :c, :config, "Config file. Default: #{CONFIG_FILE}", :argument => true
133
+ end
134
+ end
135
+
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,7 @@
1
+ module TwitterBackup
2
+ module Error
3
+ class InvalidPath < StandardError; end
4
+ class InvalidBackupFile < StandardError; end
5
+ class MissingCredentials < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,116 @@
1
+ module TwitterBackup
2
+ class Tweet < ActiveRecord::Base
3
+
4
+ serialize :raw
5
+
6
+ scope :slim, select([:id, :created_at, :status, :status_id])
7
+
8
+ class << self
9
+
10
+ def update_tweets(args={})
11
+ args = {
12
+ :from => nil,
13
+ :to => latest.try(:status_id)
14
+ }.merge(args)
15
+ if args[:from] == :earliest
16
+ args[:from] = earliest.try(:status_id)
17
+ args[:to] = nil if args[:to] == latest.try(:status_id)
18
+ end
19
+ if !synced? || TBConfig.passed_opts.force?
20
+ say ".... Updating tweets from #{args[:from] || 'the youngest one'} to #{args[:to] || 'the oldest one'}" if TBConfig.passed_opts.verbose?
21
+ download(:from => args[:from], :to => args[:to]).each do |tweet|
22
+ if absent_tweet = find_by_status_id(tweet.id)
23
+ next
24
+ else
25
+ say %[<%= color(".", YELLOW) %> ] if TBConfig.passed_opts.verbose?
26
+ tweet_text = if tweet.retweet?
27
+ "RT @#{tweet.retweeted_status.user.screen_name}: #{tweet.retweeted_status.text}"
28
+ else
29
+ tweet.text
30
+ end
31
+ create(
32
+ :status_id => tweet.id,
33
+ :status => tweet_text,
34
+ :created_at => tweet.created_at,
35
+ :raw => tweet
36
+ )
37
+ end
38
+ end
39
+ say "!"
40
+ say ".... Updating succeeded" if TBConfig.passed_opts.verbose?
41
+ else
42
+ say ".... Seems like there is nothing we can update" if TBConfig.passed_opts.verbose?
43
+ end
44
+ end
45
+
46
+ def latest
47
+ self.slim.order("created_at DESC").first
48
+ end
49
+ def earliest
50
+ self.slim.order("created_at DESC").last
51
+ end
52
+
53
+ def download(args={})
54
+ args = {
55
+ :from => nil,
56
+ :to => nil
57
+ }.merge(args)
58
+ args[:from], args[:to] = [args[:from], args[:to]].sort.reverse unless [args[:from], args[:to]].include? nil
59
+
60
+ needed_tweets = []
61
+
62
+ if args[:from].blank?
63
+ say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
64
+ received_tweets = Twitter.user_timeline(:count => 200)
65
+ else
66
+ say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
67
+ received_tweets = Twitter.user_timeline(:count => 200, :max_id => args[:from])
68
+ end
69
+
70
+ if args[:to].blank?
71
+ id_of_the_earliest_received_tweet = nil
72
+ until id_of_the_earliest_received_tweet == received_tweets.last.id
73
+ id_of_the_earliest_received_tweet = received_tweets.last.id
74
+ say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
75
+ received_tweets.concat(
76
+ Twitter.user_timeline(:count => 200, :max_id => received_tweets.last.id)
77
+ )
78
+ end
79
+ received_tweets
80
+ else args[:to]
81
+ id_of_the_earliest_needed_tweet = args[:to]
82
+ until received_tweets.map(&:id).include? id_of_the_earliest_needed_tweet
83
+ say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
84
+ received_tweets.concat(
85
+ Twitter.user_timeline(:count => 200, :max_id => received_tweets.last.id)
86
+ )
87
+ end
88
+ index_of_earliest_needed_tweet = received_tweets.rindex{|tweet| tweet.id == id_of_the_earliest_needed_tweet}
89
+ received_tweets[0..index_of_earliest_needed_tweet]
90
+ end
91
+
92
+ end
93
+
94
+ def synced?
95
+ self.count == TBConfig.user.statuses_count
96
+ end
97
+
98
+ def dump_to_backup_file
99
+ if TBConfig.passed_opts.verbose?
100
+ say ".... Saving tweets to: #{TBConfig.options[:backup_file]}"
101
+ end
102
+ tweets = slim.order("created_at DESC").map{ |tweet| {
103
+ :id => tweet.status_id,
104
+ :text => tweet.status,
105
+ :created_at => tweet.created_at,
106
+ :link => tweet.public_link } }
107
+ File.open( TBConfig.options[:backup_file], "w" ) { |f| YAML::dump( tweets, f ) }
108
+ end
109
+ end
110
+
111
+ def public_link
112
+ "https://twitter.com/#{TBConfig.user.screen_name}/status/#{self.status_id}"
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,89 @@
1
+ module TwitterBackup
2
+ module UI
3
+ class << self
4
+ def ask_credentials
5
+ say "You need to give us necessary credentials"
6
+ say "Get them at https://dev.twitter.com/apps"
7
+
8
+ existing_credentials = TBConfig.options[:credentials]
9
+ new_credentials = {}
10
+
11
+ new_credentials[:consumer_key] = ask("Consumer key? ").to_s if existing_credentials[:consumer_key].blank?
12
+ new_credentials[:consumer_secret] = ask("Consumer secret? ").to_s if existing_credentials[:consumer_secret].blank?
13
+ new_credentials[:oauth_token] = ask("Access token? ").to_s if existing_credentials[:oauth_token].blank?
14
+ new_credentials[:oauth_token_secret] = ask("Access token secret? ").to_s if existing_credentials[:oauth_token_secret].blank?
15
+
16
+ TBConfig.save_credentials new_credentials
17
+ raise MissingCredentials if TBConfig.credentials_missing?
18
+ end
19
+
20
+ def define_path_to_backup
21
+ default_path = File.join(CONFIG_DIR, "tweets.yml")
22
+ say "We are going to save your tweets to #{TBConfig.options[:db][:database]}"
23
+ say "And also as plain text to #{default_path}"
24
+ if agree ("Dou you want to define another directory for text copy? ")
25
+ backup_dir = ask("Enter a path to your direcory: ") do |q|
26
+ q.validate = lambda { |file| File.exist?(File.dirname(File.expand_path(file))) }
27
+ q.responses[:not_valid] = "You can't save files there"
28
+ q.confirm = true
29
+ end
30
+ backup_file = File.expand_path(File.join(backup_dir, "tweets.yml"))
31
+ else
32
+ backup_file = default_path
33
+ end
34
+
35
+ TBConfig.save_backup_file backup_file
36
+
37
+ end
38
+
39
+ def greet_user
40
+ say %[.... <%= color("Tweets for: #{TBConfig.user.name}", GREEN) %>!]
41
+ say %[.... Your tweets at twitter.com: #{TBConfig.user.statuses_count}]
42
+ end
43
+
44
+ def exit_screen
45
+ if ActiveRecord::Base.connected?
46
+ say %[.... Your tweets in this backup: #{Tweet.count}]
47
+ if Tweet.earliest.present?
48
+ say %[.... Your earliest tweet:]
49
+ say %[.... <%= color("#{Tweet.earliest.try(:status)}", YELLOW) %>]
50
+ end
51
+ if Tweet.latest.present?
52
+ say %[.... Your latest tweet:]
53
+ say %[.... <%= color("#{Tweet.latest.try(:status)}", GREEN) %>]
54
+ end
55
+ end
56
+ end
57
+
58
+ def missing_credentials_exit
59
+ say %[.... <%= color("We can't work without credentials. Sorry.", RED) %>]
60
+ say ".... Go get them at https://dev.twitter.com/apps"
61
+ say ".... And run this script again"
62
+ exit 1
63
+ end
64
+
65
+ def wrong_credentials_exit
66
+ say %[.... <%= color("Your credentials are somehow wrong.", RED) %>]
67
+ say ".... Go get the right ones at https://dev.twitter.com/apps"
68
+ say ".... And run this script again"
69
+ exit 1
70
+ end
71
+
72
+ def failed_backup_path_exit message
73
+ say %[.... <%= color("We've failed to create #{TBConfig.options[:backup_file]} for you.", RED) %>]
74
+ say %[.... #{message}]
75
+ TBConfig.save_backup_file("")
76
+ say ".... Run this script again and chose another place to store your backup"
77
+ say ".... Or edit it manually at #{TBConfig.config_file}"
78
+ exit 1
79
+ end
80
+
81
+ def too_many_requests_exit
82
+ say %[.... <%= color("You've exceeded your request limit", RED) %>]
83
+ say ".... Try again tomorrow =)"
84
+ exit 1
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module TwitterBackup
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/twitter_backup/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["il.zoff"]
6
+ gem.email = ["il.zoff@gmail.com"]
7
+ gem.description = %q{This gem will download your tweets from Twitter and save them to sqlite3 database and plaintext (yaml) archive file.}
8
+ gem.summary = %q{Twitter archiver}
9
+ gem.homepage = "http://github.com/ilzoff/twitter_backup"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "twitter_backup"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = TwitterBackup::VERSION
17
+
18
+ gem.add_dependency("highline")
19
+ gem.add_dependency("activesupport")
20
+ gem.add_dependency("activerecord")
21
+ gem.add_dependency("sqlite3")
22
+ gem.add_dependency("twitter")
23
+ gem.add_dependency("slop")
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twitter_backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - il.zoff
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: highline
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activerecord
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sqlite3
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: twitter
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: slop
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: This gem will download your tweets from Twitter and save them to sqlite3
111
+ database and plaintext (yaml) archive file.
112
+ email:
113
+ - il.zoff@gmail.com
114
+ executables:
115
+ - twitter_backup
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - .gitignore
120
+ - Gemfile
121
+ - LICENSE
122
+ - README.md
123
+ - Rakefile
124
+ - bin/twitter_backup
125
+ - lib/twitter_backup.rb
126
+ - lib/twitter_backup/config.rb
127
+ - lib/twitter_backup/error.rb
128
+ - lib/twitter_backup/tweet.rb
129
+ - lib/twitter_backup/ui.rb
130
+ - lib/twitter_backup/version.rb
131
+ - twitter_backup.gemspec
132
+ homepage: http://github.com/ilzoff/twitter_backup
133
+ licenses: []
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ! '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 1.8.24
153
+ signing_key:
154
+ specification_version: 3
155
+ summary: Twitter archiver
156
+ test_files: []