scrapbooker 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.
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +79 -0
- data/Rakefile +6 -0
- data/init.rb +1 -0
- data/lib/generators/USAGE +10 -0
- data/lib/generators/scrapbooker.rb +55 -0
- data/lib/generators/templates/migration.rb +17 -0
- data/lib/generators/templates/model.rb +14 -0
- data/lib/scrapbooker.rb +11 -0
- data/lib/scrapbooker/base.rb +20 -0
- data/lib/scrapbooker/configuration.rb +29 -0
- data/lib/scrapbooker/helpers/helpers.rb +7 -0
- data/lib/scrapbooker/helpers/tweet_helpers.rb +32 -0
- data/lib/scrapbooker/railtie.rb +10 -0
- data/lib/scrapbooker/services/services.rb +11 -0
- data/lib/scrapbooker/services/twitter_feed.rb +117 -0
- data/lib/scrapbooker/utilities.rb +44 -0
- data/lib/scrapbooker/version.rb +3 -0
- data/scrapbooker.gemspec +34 -0
- data/spec/models.rb +4 -0
- data/spec/schema.rb +11 -0
- data/spec/scrapbooker/helpers/tweet_helpers_spec.rb +38 -0
- data/spec/scrapbooker/scrapbooker_spec.rb +20 -0
- data/spec/scrapbooker/services/twitter_feed_spec.rb +48 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/cassettes/twitter_feed.yml +183 -0
- metadata +162 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Scrapbooker Changelog
|
2
|
+
|
3
|
+
## 1.0 (April 6th, 2012)
|
4
|
+
|
5
|
+
Changed gem name and releasing version 1.0
|
6
|
+
|
7
|
+
## 0.1.2.1 (March 6th, 2012)
|
8
|
+
|
9
|
+
Fix for live gem.
|
10
|
+
|
11
|
+
## 0.1.2 (March 6th, 2012)
|
12
|
+
|
13
|
+
Basic generator functionality added.
|
14
|
+
|
15
|
+
## 0.1.1 (March 5th, 2012)
|
16
|
+
|
17
|
+
ContrarySheep added to authors.
|
18
|
+
|
19
|
+
## 0.1 (March 3rd, 2012)
|
20
|
+
|
21
|
+
Initial project creation.
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Christopher Griffith Studio
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Scrapbooker
|
2
|
+
|
3
|
+
Scrapbooker is a gem for rails that allows you to pull information from social media feeds then store the information locally. This is handy for keeping an archive of activity, but also a good way to make that content available to your Rails app.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
The scrapbooker gem makes use of railties, and as such requires Rails version >= 3.0.
|
8
|
+
|
9
|
+
Installing the gem is as simple as adding it your gemfile.
|
10
|
+
|
11
|
+
gem "scrapbooker"
|
12
|
+
|
13
|
+
This will also install the latest version of the gem, plus any dependencies that you may need to connect to the various services including:
|
14
|
+
|
15
|
+
+ twitter
|
16
|
+
+ simple_oauth
|
17
|
+
+ addressable
|
18
|
+
+ multipart-post
|
19
|
+
+ faraday
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The gem provides a generator for getting started quickly. Running the generator will create a model and migration in you app. As an example, I can to create a new class called `Scrapbook` by running the following command.
|
24
|
+
|
25
|
+
rails g scrapbooker Scrapbook
|
26
|
+
|
27
|
+
Once the new scrapbook migration has been created, run the migration.
|
28
|
+
|
29
|
+
rake db:migrate
|
30
|
+
|
31
|
+
The new `scrapbook.rb` model contains `extend_with_scrapbooker`. This extends the class with the scrapbooker functionality. The scrapbooker configuration can be set by passing the settings in a `configure` block.
|
32
|
+
|
33
|
+
In the example, the Scrapbook class is going to store tweets from [@GriffithStudio](http://twitter.com/GriffithStudio) and check for new tweets every hour. In the `scrapbook.rb` model, the following should be added.
|
34
|
+
|
35
|
+
self.configure do |c|
|
36
|
+
c.update_interval = 60
|
37
|
+
c.twitter_feed = { :username => 'GriffithStudio' }
|
38
|
+
end
|
39
|
+
|
40
|
+
Now, the method `update_scrapbook` can be called on the Scrapbook class to pull any new tweets into the application. A before_filter can also be setup in the controller to perform the update at the specified interval. For this, the following can be added to the application controller.
|
41
|
+
|
42
|
+
before_filter :update_scrapbook
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def update_scrapbook
|
47
|
+
Scrapbook.update_scrapbook?
|
48
|
+
end
|
49
|
+
|
50
|
+
The Scrapbook class can be used anywhere in the application, just as any other class. for instance the following calls the first Scrapbook entry.
|
51
|
+
|
52
|
+
Scrapbook.first
|
53
|
+
|
54
|
+
Scrapbooker also extends the Scrapbook class with the a few unique attributes.
|
55
|
+
|
56
|
+
Scrapbook.first.original_tweet
|
57
|
+
Scrapbook.first.is_retweet
|
58
|
+
Scrapbook.first.links
|
59
|
+
Scrapbook.first.media
|
60
|
+
Scrapbook.first.mentions
|
61
|
+
Scrapbook.first.hashtags
|
62
|
+
|
63
|
+
In the application views, scrapbooker adds a few helper methods.
|
64
|
+
|
65
|
+
render_tweet Scrapbook.first
|
66
|
+
|
67
|
+
The helper methods can all be overridden in the application helpers.
|
68
|
+
|
69
|
+
## Testing
|
70
|
+
|
71
|
+
Testing the gem makes use of the [`vcr`](https://github.com/myronmarston/vcr) gem from Myron Marston in order to playback specific HTTP responses. This is fantastic for testing, but be warned that the tests are very brittle.
|
72
|
+
|
73
|
+
## Credits
|
74
|
+
|
75
|
+
The gem is being developed as a collaboration between [@GriffithStudio](http://twitter.com/GriffithStudio) and [@ContrarySheep](http://twitter.com/ContrarySheep).
|
76
|
+
|
77
|
+
##License
|
78
|
+
|
79
|
+
Scrapbooker is (c) 2012 Christopher Griffith Studio. It is free software, and may be redistributed under the terms specified in the LICENSE file.
|
data/Rakefile
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "scrapbooker"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Description:
|
2
|
+
The scrapbooker generator creates a model and migration in your Rails app. Where the NAME argument is the name of your model.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
|
6
|
+
>> rails generate scrapbooker Scrapbook
|
7
|
+
|
8
|
+
Creates a new model with the class "Scrapbook" and a migration for a table called "scrapbooks".
|
9
|
+
|
10
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class ScrapbookerGenerator < Rails::Generators::NamedBase
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
source_root File.expand_path("../templates",__FILE__)
|
7
|
+
|
8
|
+
attr_accessor :class_name
|
9
|
+
|
10
|
+
def initialize(args,*)
|
11
|
+
super
|
12
|
+
given_name = args.first
|
13
|
+
@class_name = test_singularity(given_name) ? given_name.underscore : given_name.singularize.underscore
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_model
|
17
|
+
template "model.rb", "app/models/#{@class_name}.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_migration
|
21
|
+
migration_template "migration.rb", "db/migrate/#{migration_file_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
@@migrations = false
|
25
|
+
|
26
|
+
def self.next_migration_number(dirname)
|
27
|
+
if ActiveRecord::Base.timestamped_migrations
|
28
|
+
if @@migrations
|
29
|
+
(current_migration_number(dirname) + 1)
|
30
|
+
else
|
31
|
+
@@migrations = true
|
32
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
33
|
+
end
|
34
|
+
else
|
35
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def migration_name
|
43
|
+
"create_#{@class_name.pluralize}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def migration_file_name
|
47
|
+
"#{migration_name}.rb"
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_singularity(str)
|
51
|
+
str.pluralize != str and str.singularize == str
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Create<%= @class_name.pluralize.camelize -%> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%= @class_name.pluralize -%> do |t|
|
4
|
+
t.boolean :display, :default => true
|
5
|
+
t.text :entry
|
6
|
+
t.datetime :posted_at
|
7
|
+
t.text :raw_feed
|
8
|
+
t.string :source
|
9
|
+
t.integer :source_id, :limit => 8
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.down
|
15
|
+
drop_table :<%= @class_name.pluralize -%>
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class <%= @class_name.camelize -%> < ActiveRecord::Base
|
2
|
+
|
3
|
+
extend_with_scrapbooker
|
4
|
+
|
5
|
+
#
|
6
|
+
# Example Scrapbooker configuration settings. Refer to documentation for other configuration options.
|
7
|
+
#
|
8
|
+
# self.configure do |c|
|
9
|
+
# c.update_interval = 60 # In minutes
|
10
|
+
# c.twitter_feed = { :username => 'username', :include_retweets => true }
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
|
14
|
+
end
|
data/lib/scrapbooker.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "generators/scrapbooker"
|
2
|
+
|
3
|
+
require "scrapbooker/railtie" if defined? Rails
|
4
|
+
require "scrapbooker/base"
|
5
|
+
require "scrapbooker/configuration"
|
6
|
+
require "scrapbooker/utilities"
|
7
|
+
require "scrapbooker/helpers/helpers"
|
8
|
+
require "scrapbooker/services/services"
|
9
|
+
|
10
|
+
module Scrapbooker
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Scrapbooker
|
2
|
+
module Base
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def extend_with_scrapbooker
|
9
|
+
|
10
|
+
include Scrapbooker::Services
|
11
|
+
|
12
|
+
extend Scrapbooker::Utilities
|
13
|
+
extend Scrapbooker::Services
|
14
|
+
|
15
|
+
validates_uniqueness_of :source_id, :scope => :source
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Scrapbooker
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
def update_interval=(minutes)
|
5
|
+
@interval = minutes
|
6
|
+
end
|
7
|
+
|
8
|
+
def update_interval
|
9
|
+
@interval = @interval ? @interval : 1440 #The default interval in minutes (24 hours)
|
10
|
+
end
|
11
|
+
|
12
|
+
def timestamp_storage=(path)
|
13
|
+
@timestamp_storage = path
|
14
|
+
end
|
15
|
+
|
16
|
+
def timestamp_storage
|
17
|
+
@timestamp_storage = @timestamp_storage ? @timestamp_storage : "#{Rails.root}/tmp"
|
18
|
+
end
|
19
|
+
|
20
|
+
def twitter_feed=(options)
|
21
|
+
@twitter_feed = options
|
22
|
+
end
|
23
|
+
|
24
|
+
def twitter_feed
|
25
|
+
@twitter_feed
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Scrapbooker
|
2
|
+
module Helpers
|
3
|
+
module TweetHelpers
|
4
|
+
|
5
|
+
def render_tweet(tweet)
|
6
|
+
rendered_tweet = tweet.entry
|
7
|
+
tweet.hashtags.each { |hashtag| expand_hashtag(hashtag,rendered_tweet) }
|
8
|
+
tweet.mentions.each { |mention| expand_mention(mention,rendered_tweet) }
|
9
|
+
tweet.links.each { |link| expand_link(link,rendered_tweet) }
|
10
|
+
tweet.media.each { |media| expand_media(media,rendered_tweet) }
|
11
|
+
return raw rendered_tweet
|
12
|
+
end
|
13
|
+
|
14
|
+
def expand_hashtag(hashtag,tweet)
|
15
|
+
tweet.sub!("#{hashtag}", link_to(hashtag,"http://twitter.com/search/#{URI.escape(hashtag)}",:target => :blank))
|
16
|
+
end
|
17
|
+
|
18
|
+
def expand_link(link,tweet)
|
19
|
+
tweet.sub!(link['display'], link_to(link['source'],link['source'],:target => :blank))
|
20
|
+
end
|
21
|
+
|
22
|
+
def expand_media(media,tweet)
|
23
|
+
tweet.sub!(media['display'], link_to(media['source'],media['source'],:target => :blank))
|
24
|
+
end
|
25
|
+
|
26
|
+
def expand_mention(mention,tweet)
|
27
|
+
tweet.sub!(/@#{mention}/i, link_to("@#{mention}","http://twitter.com/#{mention}",:target => :blank))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Scrapbooker
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer 'scrapbooker.initialize' do
|
4
|
+
ActiveSupport.on_load(:active_record) do
|
5
|
+
ActiveRecord::Base.send :include, Scrapbooker::Base
|
6
|
+
ActionView::Base.send :include, Scrapbooker::Helpers
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'twitter'
|
2
|
+
|
3
|
+
module Scrapbooker
|
4
|
+
module Services
|
5
|
+
|
6
|
+
class TwitterFeed
|
7
|
+
def self.default_options
|
8
|
+
@default_options ||= {
|
9
|
+
:username => nil,
|
10
|
+
:include_retweets => true,
|
11
|
+
:exclude_replies => true,
|
12
|
+
:include_entities => true,
|
13
|
+
:count => 20,
|
14
|
+
:trim_user => true
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :username, :include_retweets, :exclude_replies, :include_entities, :count, :trim_user, :active
|
19
|
+
|
20
|
+
def initialize(options)
|
21
|
+
@active = options ? true : false
|
22
|
+
options ||= {}
|
23
|
+
options = self.class.default_options.merge(options)
|
24
|
+
@username = options[:username]
|
25
|
+
@include_retweets = options[:include_retweets]
|
26
|
+
@exclude_replies = options[:exclude_replies]
|
27
|
+
@include_entities = options[:include_entities]
|
28
|
+
@count = options[:count]
|
29
|
+
@trim_user = options[:trim_user]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def poll_twitter?
|
34
|
+
poll_twitter if self.twitter_feed.active
|
35
|
+
end
|
36
|
+
|
37
|
+
def poll_twitter
|
38
|
+
results = Twitter.user_timeline(self.twitter_feed.username, {
|
39
|
+
:include_rts => self.twitter_feed.include_retweets,
|
40
|
+
:include_replies => self.twitter_feed.exclude_replies,
|
41
|
+
:include_entities => self.twitter_feed.include_entities,
|
42
|
+
:count => self.twitter_feed.count,
|
43
|
+
:trim_user => self.twitter_feed.trim_user
|
44
|
+
})
|
45
|
+
for tweet in results do
|
46
|
+
store_tweet(tweet)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def store_tweet(tweet)
|
51
|
+
self.create(
|
52
|
+
:source => 'twitter',
|
53
|
+
:source_id => tweet.id,
|
54
|
+
:raw_feed => tweet.instance_variable_get('@attrs').to_yaml,
|
55
|
+
:posted_at => tweet.created_at,
|
56
|
+
:entry => tweet.text
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
module TweetMethods
|
61
|
+
|
62
|
+
def original_tweet
|
63
|
+
YAML::load(self.raw_feed)
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_retweet
|
67
|
+
self.original_tweet['retweeted_status'] ? true : false
|
68
|
+
end
|
69
|
+
|
70
|
+
def links
|
71
|
+
links = Array.new
|
72
|
+
if self.original_tweet['entities'] && self.original_tweet['entities']['urls']
|
73
|
+
for link in self.original_tweet['entities']['urls'] do
|
74
|
+
link_hash = Hash['source', link['expanded_url'], 'display', link['url']]
|
75
|
+
links << link_hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
return links
|
79
|
+
end
|
80
|
+
|
81
|
+
def media
|
82
|
+
media = Array.new
|
83
|
+
if self.original_tweet['entities'] && self.original_tweet['entities']['media']
|
84
|
+
for entity in self.original_tweet['entities']['media'] do
|
85
|
+
media_hash = Hash['source', entity['media_url'], 'display', entity['url'], 'type', entity['type']]
|
86
|
+
media << media_hash
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return media
|
90
|
+
end
|
91
|
+
|
92
|
+
def mentions
|
93
|
+
mentions = Array.new
|
94
|
+
if self.original_tweet['entities'] && self.original_tweet['entities']['user_mentions']
|
95
|
+
for mention in self.original_tweet['entities']['user_mentions'] do
|
96
|
+
mentions << "#{mention['screen_name']}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return mentions
|
100
|
+
end
|
101
|
+
|
102
|
+
def hashtags
|
103
|
+
hashtags = Array.new
|
104
|
+
if self.original_tweet['entities'] && self.original_tweet['entities']['hashtags']
|
105
|
+
for hashtag in self.original_tweet['entities']['hashtags'] do
|
106
|
+
hashtags << "##{hashtag['text']}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return hashtags
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
include TweetMethods
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Scrapbooker
|
2
|
+
module Utilities
|
3
|
+
|
4
|
+
def configure
|
5
|
+
yield configuration
|
6
|
+
end
|
7
|
+
|
8
|
+
def configuration
|
9
|
+
@configuration ||= Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_scrapbook?
|
13
|
+
update_scrapbook if !timestamp_exists?
|
14
|
+
update_scrapbook if !timestamp_is_current?
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_scrapbook
|
18
|
+
#Get results from active services
|
19
|
+
poll_twitter?
|
20
|
+
update_timestamp
|
21
|
+
end
|
22
|
+
|
23
|
+
def timestamp_is_current?
|
24
|
+
last_update > self.configuration.update_interval.minutes.ago
|
25
|
+
end
|
26
|
+
|
27
|
+
def timestamp_exists?
|
28
|
+
File.exists?(timestamp_file) ? true : false
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_timestamp
|
32
|
+
File.open(timestamp_file, "w+") {|f| f.write(Time.now)}
|
33
|
+
end
|
34
|
+
|
35
|
+
def timestamp_file
|
36
|
+
"#{self.configuration.timestamp_storage}/#{self.name.underscore}_last_update"
|
37
|
+
end
|
38
|
+
|
39
|
+
def last_update
|
40
|
+
Time.parse(File.read(timestamp_file))
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/scrapbooker.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "scrapbooker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "scrapbooker"
|
7
|
+
s.version = Scrapbooker::VERSION
|
8
|
+
s.authors = ["Christopher Griffith Studio","ContrarySheep"]
|
9
|
+
s.email = ["studio@christophergriffith.com","contrarysheep@gmail.com"]
|
10
|
+
s.homepage = "http://griffithstudio.github.com/scrapbooker"
|
11
|
+
s.summary = %q{Scrapbooker saves social media feeds locally.}
|
12
|
+
s.description = %q{Scrapbooker is a gem for rails that allows you to pull information from social media feeds then store the information locally.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "scrapbooker"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here;
|
22
|
+
s.add_dependency 'rails', '>= 3.0'
|
23
|
+
s.add_dependency 'json'
|
24
|
+
s.add_dependency 'twitter'
|
25
|
+
|
26
|
+
# specify development depencies here;
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
s.add_development_dependency 'vcr', '~> 2.0.0'
|
29
|
+
s.add_development_dependency 'fakeweb'
|
30
|
+
s.add_development_dependency 'sqlite3'
|
31
|
+
|
32
|
+
# specify any runtime dependencies here;
|
33
|
+
|
34
|
+
end
|
data/spec/models.rb
ADDED
data/spec/schema.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
ActiveRecord::Schema.define :version => 0 do
|
2
|
+
create_table "scrapbooks", :force => true do |t|
|
3
|
+
t.boolean :display, :default => true
|
4
|
+
t.text :entry
|
5
|
+
t.datetime :posted_at
|
6
|
+
t.text :raw_feed
|
7
|
+
t.string :source
|
8
|
+
t.integer :source_id, :limit => 8
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Scrapbooker::Helpers::TweetHelpers do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@helper = class Helper
|
7
|
+
include ActionView::Helpers
|
8
|
+
include Scrapbooker::Helpers
|
9
|
+
end.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return the tweet with hashtags linked" do
|
13
|
+
test = @helper.expand_hashtag('#testing','This is a test #testing')
|
14
|
+
result = "This is a test <a href=\"http://twitter.com/search/%23testing\" target=\"blank\">#testing</a>"
|
15
|
+
test.should eq result
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return the tweet with mentions linked" do
|
19
|
+
test = @helper.expand_mention('test','This is a test for @test')
|
20
|
+
result = "This is a test for <a href=\"http://twitter.com/test\" target=\"blank\">@test</a>"
|
21
|
+
test.should eq result
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return the tweet with links expanded and linked" do
|
25
|
+
link = { 'source' => 'http://test.com', 'display' => 'http://test.com'}
|
26
|
+
test = @helper.expand_link(link,'This is a test for http://test.com')
|
27
|
+
result = "This is a test for <a href=\"http://test.com\" target=\"blank\">http://test.com</a>"
|
28
|
+
test.should eq result
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return the tweet with media expanded and linked" do
|
32
|
+
media = { 'source' => 'http://test.com/test.jpg', 'display' => 'http://test.com/test.jpg'}
|
33
|
+
test = @helper.expand_media(media,'This is a test for http://test.com/test.jpg')
|
34
|
+
result = "This is a test for <a href=\"http://test.com/test.jpg\" target=\"blank\">http://test.com/test.jpg</a>"
|
35
|
+
test.should eq result
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Scrapbooker do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
clean_database!
|
7
|
+
reset_scrapbook
|
8
|
+
end
|
9
|
+
|
10
|
+
it "tests the timestamp file" do
|
11
|
+
Scrapbook.update_timestamp
|
12
|
+
Scrapbook.last_update.strftime("%H:%M:%S").should eq Time.now.strftime("%H:%M:%S")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "tests the ability to add a database entry" do
|
16
|
+
scrapbook = Scrapbook.create(:source => "test")
|
17
|
+
scrapbook.source.should eq "test"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Scrapbooker::Services::TwitterFeed do
|
4
|
+
|
5
|
+
use_vcr_cassette "twitter_feed"
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
clean_database!
|
9
|
+
reset_scrapbook
|
10
|
+
Scrapbook.configuration.twitter_feed = { :username => 'GriffithStudio',
|
11
|
+
:include_entities => true,
|
12
|
+
:exclude_replies => false,
|
13
|
+
:count => 50,
|
14
|
+
:trim_user => false }
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
it "polls twitter and stores tweets in the database" do
|
19
|
+
Scrapbook.poll_twitter?
|
20
|
+
Scrapbook.count.should > 0
|
21
|
+
end
|
22
|
+
|
23
|
+
it "checks for the retweet status" do
|
24
|
+
tweet = Scrapbook.find_by_source_id(169118526404165632)
|
25
|
+
tweet.is_retweet.should eq true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns the links from a tweet" do
|
29
|
+
tweet = Scrapbook.find_by_source_id(187542378033332224)
|
30
|
+
tweet.links.first['display'].should eq "http://t.co/UAWQ5u27"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns the media from a tweet" do
|
34
|
+
tweet = Scrapbook.find_by_source_id(185131786605563904)
|
35
|
+
tweet.media.first['display'].should eq "http://t.co/XiIWPrJn"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "returns the mentions from a tweet" do
|
39
|
+
tweet = Scrapbook.find_by_source_id(181838740447182848)
|
40
|
+
tweet.mentions.first.should eq "mrwhite_"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns the hashtags from a tweet" do
|
44
|
+
tweet = Scrapbook.find_by_source_id(172399167614881792)
|
45
|
+
tweet.hashtags.last.should eq "#desert"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
$LOAD_PATH << "." unless $LOAD_PATH.include?(".")
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "rails/all"
|
5
|
+
require "scrapbooker"
|
6
|
+
require "twitter"
|
7
|
+
require "vcr"
|
8
|
+
|
9
|
+
VCR.configure do |config|
|
10
|
+
config.cassette_library_dir = 'spec/support/cassettes'
|
11
|
+
config.hook_into :fakeweb
|
12
|
+
config.default_cassette_options = { :record => :new_episodes }
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.mock_with :rspec
|
17
|
+
config.extend VCR::RSpec::Macros
|
18
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
19
|
+
end
|
20
|
+
|
21
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
22
|
+
|
23
|
+
ActiveRecord::Base.silence do
|
24
|
+
ActiveRecord::Migration.verbose = false
|
25
|
+
load(File.dirname(__FILE__) + '/schema.rb')
|
26
|
+
load(File.dirname(__FILE__) + '/models.rb')
|
27
|
+
end
|
28
|
+
|
29
|
+
def clean_database!
|
30
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{Scrapbook.table_name}"
|
31
|
+
end
|
32
|
+
|
33
|
+
clean_database!
|
34
|
+
|
35
|
+
def reset_scrapbook
|
36
|
+
Scrapbook.configuration.update_interval = nil
|
37
|
+
Scrapbook.configuration.twitter_feed = nil
|
38
|
+
# Set the timestamp_storage to the tmp directory in support.
|
39
|
+
Scrapbook.configuration.timestamp_storage = File.dirname(__FILE__) + '/support/tmp'
|
40
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.twitter.com/1/statuses/user_timeline.json?include_rts=true&include_replies=false&include_entities=true&count=50&trim_user=false&screen_name=GriffithStudio
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
accept:
|
11
|
+
- application/json
|
12
|
+
user-agent:
|
13
|
+
- Twitter Ruby Gem 2.2.0
|
14
|
+
accept-encoding:
|
15
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
|
+
connection:
|
17
|
+
- close
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
date:
|
24
|
+
- Thu, 05 Apr 2012 21:53:30 GMT
|
25
|
+
status:
|
26
|
+
- 200 OK
|
27
|
+
x-ratelimit-class:
|
28
|
+
- api
|
29
|
+
etag:
|
30
|
+
- ! '"6f115a89394add2aedb4bb17c85c6d13"'
|
31
|
+
x-ratelimit-reset:
|
32
|
+
- '1333665736'
|
33
|
+
x-transaction-mask:
|
34
|
+
- a6183ffa5f8ca943ff1b53b5644ef1140428d3cb
|
35
|
+
pragma:
|
36
|
+
- no-cache
|
37
|
+
cache-control:
|
38
|
+
- no-cache, no-store, must-revalidate, pre-check=0, post-check=0
|
39
|
+
x-mid:
|
40
|
+
- 02e9a379d6b522f6a97a71043493c8deb57784d0
|
41
|
+
x-ratelimit-limit:
|
42
|
+
- '150'
|
43
|
+
expires:
|
44
|
+
- Tue, 31 Mar 1981 05:00:00 GMT
|
45
|
+
last-modified:
|
46
|
+
- Thu, 05 Apr 2012 21:53:30 GMT
|
47
|
+
content-type:
|
48
|
+
- application/json; charset=utf-8
|
49
|
+
x-ratelimit-remaining:
|
50
|
+
- '142'
|
51
|
+
x-runtime:
|
52
|
+
- '0.14167'
|
53
|
+
x-frame-options:
|
54
|
+
- SAMEORIGIN
|
55
|
+
x-transaction:
|
56
|
+
- 6b11aaef91832139
|
57
|
+
set-cookie:
|
58
|
+
- k=10.35.55.138.1333662810682999; path=/; expires=Thu, 12-Apr-12 21:53:30 GMT;
|
59
|
+
domain=.twitter.com
|
60
|
+
- guest_id=v1%3A133366281068694086; domain=.twitter.com; path=/; expires=Sun,
|
61
|
+
06-Apr-2014 09:53:30 GMT
|
62
|
+
- _twitter_sess=BAh7CCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7ADoPY3JlYXRlZF9hdGwrCD%252BCf4Q2AToHaWQiJTc3%250AMTY3NGI5YjlhZTEwYTZlMTUwZTQ1OWI1NTk0Yjdi--a1704c6d223e9bfd66a3ae316c2fc45712beb2bd;
|
63
|
+
domain=.twitter.com; path=/; HttpOnly
|
64
|
+
vary:
|
65
|
+
- Accept-Encoding
|
66
|
+
content-encoding:
|
67
|
+
- gzip
|
68
|
+
content-length:
|
69
|
+
- '4743'
|
70
|
+
server:
|
71
|
+
- tfe
|
72
|
+
body:
|
73
|
+
encoding: ASCII-8BIT
|
74
|
+
string: !binary |-
|
75
|
+
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
|
180
|
+
J2gY05B3/8f/ARsDraD46AAA
|
181
|
+
http_version: '1.1'
|
182
|
+
recorded_at: Thu, 05 Apr 2012 21:53:29 GMT
|
183
|
+
recorded_with: VCR 2.0.1
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: scrapbooker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Christopher Griffith Studio
|
9
|
+
- ContrarySheep
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-04-06 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: &70168508446000 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70168508446000
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
requirement: &70168508445580 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70168508445580
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: twitter
|
39
|
+
requirement: &70168508445120 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70168508445120
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
requirement: &70168508444700 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70168508444700
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: vcr
|
61
|
+
requirement: &70168500181280 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ~>
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 2.0.0
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *70168500181280
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: fakeweb
|
72
|
+
requirement: &70168500180860 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *70168500180860
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: sqlite3
|
83
|
+
requirement: &70168500180400 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: *70168500180400
|
92
|
+
description: Scrapbooker is a gem for rails that allows you to pull information from
|
93
|
+
social media feeds then store the information locally.
|
94
|
+
email:
|
95
|
+
- studio@christophergriffith.com
|
96
|
+
- contrarysheep@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .rspec
|
103
|
+
- CHANGELOG.md
|
104
|
+
- Gemfile
|
105
|
+
- LICENSE.txt
|
106
|
+
- README.md
|
107
|
+
- Rakefile
|
108
|
+
- init.rb
|
109
|
+
- lib/generators/USAGE
|
110
|
+
- lib/generators/scrapbooker.rb
|
111
|
+
- lib/generators/templates/migration.rb
|
112
|
+
- lib/generators/templates/model.rb
|
113
|
+
- lib/scrapbooker.rb
|
114
|
+
- lib/scrapbooker/base.rb
|
115
|
+
- lib/scrapbooker/configuration.rb
|
116
|
+
- lib/scrapbooker/helpers/helpers.rb
|
117
|
+
- lib/scrapbooker/helpers/tweet_helpers.rb
|
118
|
+
- lib/scrapbooker/railtie.rb
|
119
|
+
- lib/scrapbooker/services/services.rb
|
120
|
+
- lib/scrapbooker/services/twitter_feed.rb
|
121
|
+
- lib/scrapbooker/utilities.rb
|
122
|
+
- lib/scrapbooker/version.rb
|
123
|
+
- scrapbooker.gemspec
|
124
|
+
- spec/models.rb
|
125
|
+
- spec/schema.rb
|
126
|
+
- spec/scrapbooker/helpers/tweet_helpers_spec.rb
|
127
|
+
- spec/scrapbooker/scrapbooker_spec.rb
|
128
|
+
- spec/scrapbooker/services/twitter_feed_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/support/cassettes/twitter_feed.yml
|
131
|
+
homepage: http://griffithstudio.github.com/scrapbooker
|
132
|
+
licenses: []
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project: scrapbooker
|
151
|
+
rubygems_version: 1.8.15
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: Scrapbooker saves social media feeds locally.
|
155
|
+
test_files:
|
156
|
+
- spec/models.rb
|
157
|
+
- spec/schema.rb
|
158
|
+
- spec/scrapbooker/helpers/tweet_helpers_spec.rb
|
159
|
+
- spec/scrapbooker/scrapbooker_spec.rb
|
160
|
+
- spec/scrapbooker/services/twitter_feed_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
- spec/support/cassettes/twitter_feed.yml
|