radiant-twitter-extension 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/HELP_admin.md ADDED
@@ -0,0 +1,19 @@
1
+ You can have updates to your site automatically send details to your
2
+ Twitter account. All you'll need to do is provide your account information
3
+ in the Radiant::Config settings.
4
+
5
+ The required keys are `twitter.password`, `twitter.username` and `twitter.url_host`.
6
+
7
+ The `username` and `password` are the same for your login details of your Twitter
8
+ account. The `url_host` will be used when generating links back to your site, this
9
+ should be your domain name.
10
+
11
+ On the edit screen of each page, you can select the option to notify Twitter
12
+ of all of the updates to the children of that page. You can create a blog page,
13
+ for example, and have each new post linked in your Twitter account.
14
+
15
+ _Built with version `0.6.6` of the `twitter` gem._
16
+
17
+ To get latest tweet(s):
18
+
19
+ <r:twitter:message [ max="10" ] />
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Twitter
2
+
3
+ An extension to Radiant that will automatically tweet the publication of new pages in selected parts of your site, and which provides a set of radius tags that allow you to feature twitter feeds of different kinds on your site.
4
+
5
+ ## Installation
6
+
7
+ sudo gem install radiant-twitter-extension
8
+
9
+ add this to your environment.rb
10
+
11
+ config.gem 'radiant-twitter-extension', :version => '~> 2.0.0.rc1'
12
+
13
+ and then:
14
+
15
+ rake radiant:extensions:update_all
16
+ rake radiant:extensions:twitter:migrate
17
+
18
+ You can also vendor the extension in the old-fashioned way:
19
+
20
+ git submodule add git://github.com/radiant/radiant-twitter-extension.git vendor/extensions/twitter
21
+ rake radiant:extensions:twitter:update
22
+ rake radiant:extensions:twitter:migrate
23
+
24
+ ## Status
25
+
26
+ Nearly stable. I've just made some quite sweeping changes to bring this up to date, so small bugs are likely. Please file issues.
27
+
28
+ ## Configuration
29
+
30
+ You can present twitter searches and feeds without authenticating, but if you want to post automatically to twitter you need to provide login information. The extension adds a 'twitter' block to the main radiant configuration interface: enter your screen name and password. Future versions may integrate with Twitter as an application but for now all we need is the ability to tweet.
31
+
32
+ ## Tweet on publication
33
+
34
+ To post a tweet every time you publish a blog entry, check the 'Notify Twitter of newly published child pages?' box on the parent blog page. The tweet will contain the title of the page and its url.
35
+
36
+ ## Display a twitter feed
37
+
38
+ If radiant is configured to tweet for you, all you need is this radius tag:
39
+
40
+ <r:twitter:tweets [max="10"] />
41
+
42
+ If it's not configured, or you want to display another user:
43
+
44
+ <r:twitter:tweets user="screen_name" />
45
+
46
+ To display a hashtag, or any other search:
47
+
48
+ <r:twitter:tweets search="#radiant" />
49
+
50
+ To display tweets from someone's list:
51
+
52
+ <r:twitter:tweets user="screen_name" list="list_name" />
53
+
54
+ The default presentation of tweets is exactly as [suggested by Twitter](https://dev.twitter.com/terms/display-guidelines) and if you include their widget script and the provided css it should all just work. If you want to present tweets differently, a range of more detailed radius tags is available. This is a slightly more compact format:
55
+
56
+ <r:twitter:tweets user="screen_name" />
57
+ <li class="tweet">
58
+ <r:tweet:avatar class="avatar" />
59
+ <r:tweet:user:screen_name />
60
+ <span class="block">
61
+ <r:tweet:text />
62
+ </span>
63
+ <span class="hidden">
64
+ <r:tweet:reply_link />
65
+ <r:tweet:retweet_link />
66
+ </span>
67
+ </li>
68
+ </r:twitter:tweets>
69
+
70
+ ## Scripts and styles
71
+
72
+ The quick way to format tweets nicely is to include the supplied sass in your site stylesheet. If you're using radiant's built-in stylesheet manager and working in Sass, you can keep everything in one file (and selectively override it) by including this line near the top:
73
+
74
+ @import 'twitter.sass'
75
+
76
+ You can also link to `/stylesheets/twitter.css` in the usual way.
77
+
78
+ The links created by radius tags here are all compatible with twitter's widgeting. To enable basic intent-based popups, just include this line in the head or at the foot of your layout:
79
+
80
+ <script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
81
+
82
+ ## Todo
83
+
84
+ * More gratifying twitter integration of admin
85
+ * Page field to edit tweet text before publication
86
+ * URL-shortener
87
+
88
+ ## Copyright and license
89
+
90
+ Originally created by Sean Cribbs and now the work of many hands, including:
91
+
92
+ * Jim Gay
93
+ * Edmund Haselwanter
94
+ * Anna Billstrom
95
+ * William Ross
96
+
97
+ Currently maintained by Will at spanner.org. Issues and comments on github, please:
98
+
99
+ https://github.com/radiant/radiant-twitter-extension/issues
100
+
101
+ Released under the same terms as Rails and/or Radiant.
102
+
103
+
104
+
data/Rakefile ADDED
@@ -0,0 +1,120 @@
1
+ # I think this is the one that should be moved to the extension Rakefile template
2
+
3
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
4
+ # Check to see if the rspec plugin is installed first and require
5
+ # it if it is. If not, use the gem version.
6
+
7
+ # Determine where the RSpec plugin is by loading the boot
8
+ unless defined? RADIANT_ROOT
9
+ ENV["RAILS_ENV"] = "test"
10
+ case
11
+ when ENV["RADIANT_ENV_FILE"]
12
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
13
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
14
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
15
+ else
16
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
17
+ end
18
+ end
19
+
20
+ require 'rake'
21
+ require 'rake/rdoctask'
22
+ require 'rake/testtask'
23
+
24
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
25
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
26
+ require 'spec/rake/spectask'
27
+ # require 'spec/translator'
28
+
29
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
30
+ Object.send(:remove_const, :RADIANT_ROOT)
31
+
32
+ extension_root = File.expand_path(File.dirname(__FILE__))
33
+
34
+ task :default => :spec
35
+ task :stats => "spec:statsetup"
36
+
37
+ desc "Run all specs in spec directory"
38
+ Spec::Rake::SpecTask.new(:spec) do |t|
39
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
40
+ t.spec_files = FileList['spec/**/*_spec.rb']
41
+ end
42
+
43
+ namespace :spec do
44
+ desc "Run all specs in spec directory with RCov"
45
+ Spec::Rake::SpecTask.new(:rcov) do |t|
46
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
47
+ t.spec_files = FileList['spec/**/*_spec.rb']
48
+ t.rcov = true
49
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
50
+ end
51
+
52
+ desc "Print Specdoc for all specs"
53
+ Spec::Rake::SpecTask.new(:doc) do |t|
54
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
55
+ t.spec_files = FileList['spec/**/*_spec.rb']
56
+ end
57
+
58
+ [:models, :controllers, :views, :helpers].each do |sub|
59
+ desc "Run the specs under spec/#{sub}"
60
+ Spec::Rake::SpecTask.new(sub) do |t|
61
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
62
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
63
+ end
64
+ end
65
+
66
+ # Hopefully no one has written their extensions in pre-0.9 style
67
+ # desc "Translate specs from pre-0.9 to 0.9 style"
68
+ # task :translate do
69
+ # translator = ::Spec::Translator.new
70
+ # dir = RAILS_ROOT + '/spec'
71
+ # translator.translate(dir, dir)
72
+ # end
73
+
74
+ # Setup specs for stats
75
+ task :statsetup do
76
+ require 'code_statistics'
77
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
78
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
79
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
80
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
81
+ ::CodeStatistics::TEST_TYPES << "Model specs"
82
+ ::CodeStatistics::TEST_TYPES << "View specs"
83
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
84
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
85
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
86
+ end
87
+
88
+ namespace :db do
89
+ namespace :fixtures do
90
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
91
+ task :load => :environment do
92
+ require 'active_record/fixtures'
93
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
94
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
95
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ desc 'Generate documentation for the twitter extension.'
103
+ Rake::RDocTask.new(:rdoc) do |rdoc|
104
+ rdoc.rdoc_dir = 'rdoc'
105
+ rdoc.title = 'TwitterExtension'
106
+ rdoc.options << '--line-numbers' << '--inline-source'
107
+ rdoc.rdoc_files.include('README')
108
+ rdoc.rdoc_files.include('lib/**/*.rb')
109
+ end
110
+
111
+ # For extensions that are in transition
112
+ desc 'Test the twitter extension.'
113
+ Rake::TestTask.new(:test) do |t|
114
+ t.libs << 'lib'
115
+ t.pattern = 'test/**/*_test.rb'
116
+ t.verbose = true
117
+ end
118
+
119
+ # Load any custom rakefiles for extension
120
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
@@ -0,0 +1,6 @@
1
+ %fieldset
2
+ %h4= t('twitter_extension.twitter')
3
+ %p= edit_config 'twitter.username'
4
+ %p= edit_config 'twitter.password'
5
+ %p= edit_config 'twitter.token'
6
+ %p= edit_config 'twitter.secret'
@@ -0,0 +1,5 @@
1
+ %h4= t('twitter_extension.twitter')
2
+ %p.ruled= show_config 'twitter.username'
3
+ %p.ruled= show_config 'twitter.password'
4
+ %p.ruled= show_config 'twitter.token'
5
+ %p.ruled= show_config 'twitter.secret'
@@ -0,0 +1,9 @@
1
+ %tr
2
+ %th.label
3
+ Twitter
4
+ %td
5
+ - fields_for @page do |pf|
6
+ = pf.check_box :notify_twitter_of_children
7
+ = pf.label :notify_twitter_of_children
8
+ - if @page.twitter_id
9
+ = link_to "[#{@page.twitter_id}]", "http://twitter.com/#{Radiant::Config['twitter.username']}/status/#{@page.twitter_id}"
@@ -0,0 +1,8 @@
1
+ Radiant.config do |config|
2
+ config.namespace 'twitter' do |twit|
3
+ twit.define 'username', :default => '', :allow_blank => true
4
+ twit.define 'password', :default => '', :allow_blank => true
5
+ twit.define 'token', :default => '', :allow_blank => true
6
+ twit.define 'secret', :default => '', :allow_blank => true
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ page:
5
+ notify_twitter_of_children: "Notify Twitter of newly published child pages?"
6
+ config:
7
+ twitter:
8
+ username: "Username (for tweets)"
9
+ password: "Password (for tweets)"
10
+ token: "Application token"
11
+ secret: "Application secret"
12
+ date:
13
+ formats:
14
+ twitter: "%m %B"
15
+ twitter_extension:
16
+ favorite: "Favorite"
17
+ reply: "Reply"
18
+ retweet: "Retweet"
19
+ twitter: "Twitter"
20
+
@@ -0,0 +1,11 @@
1
+ class AddTwitterNotificationFields < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :pages, :notify_twitter_of_children, :boolean, :default => false
4
+ add_column :pages, :twitter_id, :string
5
+ end
6
+
7
+ def self.down
8
+ remove_column :pages, :notify_twitter_of_children
9
+ remove_column :pages, :twitter_id
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module RadiantTwitterExtension
2
+ VERSION = '2.0.0.rc1'
3
+ SUMMARY = %q{Twitter posting and radius tags for twitter feeds.}
4
+ DESCRIPTION = %q{Posts notification of pages to Twitter and provides radiius tags to display the results of twitter searches.}
5
+ URL = "http://github.com/ehaselwanter/radiant-twitter-extension"
6
+ AUTHORS = ["Sean Cribbs", "Edmund Haselwanter", "Jim Gay", "William Ross"]
7
+ EMAIL = ["radiant@radiantcms.org"]
8
+ end
@@ -0,0 +1,28 @@
1
+ namespace :radiant do
2
+ namespace :extensions do
3
+ namespace :twitter do
4
+
5
+ desc "Runs the migration of the Twitter extension"
6
+ task :migrate => :environment do
7
+ require 'radiant/extension_migrator'
8
+ if ENV["VERSION"]
9
+ TwitterExtension.migrator.migrate(ENV["VERSION"].to_i)
10
+ else
11
+ TwitterExtension.migrator.migrate
12
+ end
13
+ end
14
+
15
+ desc "Copies public assets of the Twitter to the instance public/ directory."
16
+ task :update => :environment do
17
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
18
+ puts "Copying assets from TwitterExtension"
19
+ Dir[TwitterExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
20
+ path = file.sub(TwitterExtension.root, '')
21
+ directory = File.dirname(path)
22
+ mkdir_p RAILS_ROOT + directory
23
+ cp file, RAILS_ROOT + path
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ require 'twitter'
2
+ module TwitterNotification
3
+ def self.included(base)
4
+ base.class_eval {
5
+ after_save :notify_twitter
6
+ }
7
+ end
8
+
9
+ def notify_twitter
10
+ if parent
11
+ if published? && Radiant.configured? && parent.notify_twitter_of_children? && !self.twitter_id
12
+ title_length = 138 - absolute_url.length
13
+ message_title = title.length > title_length ? (title[0..title_length-4] + "...") : title
14
+ message = "#{message_title}: #{absolute_url}"
15
+ begin
16
+ httpauth = Twitter::HTTPAuth.new(Radiant.config['twitter.username'], Radiant.config['twitter.password'])
17
+ client = Twitter::Base.new(httpauth)
18
+ status = client.update(message, :source => "radianttwitternotifier")
19
+ # Don't trigger save callbacks
20
+ self.class.update_all({:twitter_id => status.id}, :id => self.id)
21
+ rescue Twitter::Error => e
22
+ logger.error "Twitter Notification failure: #{e.inspect}"
23
+ end
24
+ end
25
+ end
26
+ true
27
+ end
28
+
29
+ def absolute_url
30
+ if host = Radiant.config['site.host']
31
+ if host =~ /^http/
32
+ "#{host}#{self.url}"
33
+ else
34
+ "http://#{host}#{self.url}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def Radiant.configured?
40
+ !%w(twitter.username twitter.password site.host).any? {|k| Radiant.config[k].blank? }
41
+ end
42
+
43
+ end
@@ -0,0 +1,406 @@
1
+ require 'twitter'
2
+
3
+ module TwitterTags
4
+ include ActionView::Helpers::DateHelper
5
+ include Radiant::Taggable
6
+ class TagError < StandardError; end
7
+
8
+ tag 'twitter' do |tag|
9
+ tag.expand
10
+ end
11
+
12
+ desc %{
13
+ Retrieve a list of tweets. The minimal default is to return the ten most recent tweets of the
14
+ Radiant.configured twitter user (that is, the person as whom radiant is set up to tweet).
15
+ For that, you can use just a single tag:
16
+
17
+ <pre><code><r:twitter:tweets /></code></pre>
18
+
19
+ or control the presentation of tweets in a more detailed way:
20
+
21
+ <r:twitter:tweets:each><r:tweet:user:screen_name /> : <r:tweet:text /></r:twitter:tweets:each>
22
+
23
+ You can also specify a search in various ways.
24
+
25
+ * Supply a `max` attribute to change the number of tweets displayed. Default is 10.
26
+ * Supply a `user` attribute to display tweets from a different username
27
+ * Supply a `list` attribute to display tweets from the named list (see also r:twitter:list for a shortcut)
28
+ * Supply a `search` attribute to show tweets containing that text (see also r:twitter:search for a shortcut)
29
+ You don't need to %escape the search string.
30
+ In a search query the user and list parameters will be ignored.
31
+
32
+ <pre><code>
33
+ <r:twitter:tweets user="spanner_org" max="2" />
34
+ <r:twitter:tweets search="#radiant" />
35
+ </code></pre>
36
+
37
+ }
38
+ tag 'twitter:tweets' do |tag|
39
+ tag.locals.tweets = fetch_and_cache_tweets(tag.attr.slice('user', 'max', 'search', 'list').symbolize_keys)
40
+ tag.double? ? tag.expand : tag.render('twitter:messages')
41
+ end
42
+
43
+ tag 'twitter:tweets:each' do |tag|
44
+ tag.locals.tweets ||= fetch_and_cache_tweets(tag.attr.slice('user', 'max', 'search', 'list').symbolize_keys)
45
+ tag.render('_tweets_list', tag.attr.dup, &tag.block)
46
+ end
47
+
48
+ desc %{
49
+ Fetches and loops through tweets matching the supplied search string. You don't need to %escape the search string.
50
+
51
+ <pre><code>
52
+ <r:twitter:search for="#radiant"><r:tweets:each>...</r:tweets:each></r:twitter:search>
53
+ <r:twitter:search for="rails cms" max="1">...</r:twitter:search>
54
+ <r:twitter:search for="somethinbg" max="20">...</r:twitter:search>
55
+ </code></pre>
56
+
57
+ Short form also works:
58
+
59
+ <pre><code><r:twitter:search for="#radiant" /></code></pre>
60
+
61
+ and you can go stright into a loop:
62
+
63
+ <pre><code><r:twitter:search:each for="#radiant">...</r:twitter:search:each></code></pre>
64
+ }
65
+ tag 'twitter:search' do |tag|
66
+ tag.locals.tweets = fetch_and_cache_tweets(:search => tag.attr['for']) if tag.attr.any?
67
+ tag.double? ? tag.expand : tag.render('twitter:messages')
68
+ end
69
+
70
+ tag 'twitter:search:each' do |tag|
71
+ tag.locals.tweets ||= fetch_and_cache_tweets(:search => tag.attr['for'])
72
+ tag.render('_tweets_list', tag.attr.dup, &tag.block)
73
+ end
74
+
75
+ desc %{
76
+ Fetches tweets from the specified list belonging to the specified (or default) user.
77
+
78
+ <pre><code><r:twitter:list list="listname" [user="username"] [max="10"] /></code></pre>
79
+
80
+ Short form also works:
81
+
82
+ <pre><code><r:twitter:list list="radiant" /></code></pre>
83
+
84
+ and you can go stright into a loop:
85
+
86
+ <pre><code><r:twitter:list:each list="radiant">...</r:twitter:list:each></code></pre>
87
+ }
88
+ tag 'twitter:list' do |tag|
89
+ tag.locals.tweets = fetch_and_cache_tweets(:user => tag.attr['user'], :max => tag.attr['max'], :list => tag.attr['list']) if tag.attr.any?
90
+ tag.double? ? tag.expand : tag.render('twitter:messages')
91
+ end
92
+
93
+ tag 'twitter:list:each' do |tag|
94
+ tag.locals.tweets ||= fetch_and_cache_tweets(:user => tag.attr['user'], :max => tag.attr['max'], :list => tag.attr['list'])
95
+ tag.render('_tweets_list', tag.attr.dup, &tag.block)
96
+ end
97
+
98
+ desc %{
99
+ Returns the number of tweets.
100
+ }
101
+ tag 'tweets:length' do |tag|
102
+ tag.render('_tweets_length', tag.attr.dup, &tag.block)
103
+ end
104
+ tag 'twitter:list:length' do |tag|
105
+ tag.render('_tweets_length', tag.attr.dup, &tag.block)
106
+ end
107
+ tag 'twitter:search:length' do |tag|
108
+ tag.render('_tweets_length', tag.attr.dup, &tag.block)
109
+ end
110
+
111
+ desc %{
112
+ Loops through the current list of tweets.
113
+ }
114
+ tag 'tweets:each' do |tag|
115
+ tag.render('_tweets_list', tag.attr.dup, &tag.block)
116
+ end
117
+
118
+ # these are just for drying out: they can't be called directly.
119
+
120
+ tag '_tweets_list' do |tag|
121
+ raise TagError, "tweet_list utility tag called without a list of tweets to list" unless tag.locals.tweets
122
+ out = ""
123
+ tag.locals.tweets.each do |tweet|
124
+ tag.locals.tweet = tweet
125
+ if tag.double?
126
+ out << tag.expand
127
+ else
128
+ out << tag.render('tweet:message')
129
+ end
130
+ end
131
+ out
132
+ end
133
+
134
+ tag '_tweets_length' do |tag|
135
+ raise TagError, "_tweets_length utility tag called without a list of tweets to length" unless tag.locals.tweets
136
+ tag.locals.tweets.length
137
+ end
138
+
139
+ desc %{
140
+ Usage:
141
+
142
+ This is a shortcut that displays messages from a twitter user's timeline.
143
+ The username can be specified with a 'user' parameter, or we will default to the Radiant.configured twitter user.
144
+ The number of messages is determined by the 'max' parameter, which must be 10 or less. Default is 5.
145
+
146
+ <pre><code><r:twitter:messages max="10" /></code></pre>
147
+ }
148
+ tag 'twitter:messages' do |tag|
149
+ out = ""
150
+ tag.locals.tweets ||= fetch_and_cache_tweets(:user => tag.attr['user'], :max => tag.attr['max'])
151
+ tag.locals.tweets.each do |tweet|
152
+ tag.locals.tweet = tweet
153
+ out << tag.render('tweet:message')
154
+ end
155
+ out
156
+ end
157
+
158
+ deprecated_tag 'twitter:message', :substitute => 'twitter:messages'
159
+
160
+ tag 'tweet' do |tag|
161
+ tag.expand if tag.locals.tweet
162
+ end
163
+
164
+ desc %{
165
+ Shortcut to display a single tweet in the standard way suggested by https://dev.twitter.com/terms/display-guidelines.
166
+
167
+ Note that for this to work you will probably want to include the twitter intents javascript in your page, and you may
168
+ also want to include the supplied `twitter.sass` in your site stylesheets.
169
+ }
170
+ tag 'tweet:message' do |tag|
171
+ if tweet = tag.locals.tweet
172
+ text = replace_links(tweet.text)
173
+ screen_name = tweet.from_user || tweet.user.screen_name # search returns a different data structure
174
+ date = tag.render('tweet:date', tag.attr.dup.merge('format' => "%d %B"))
175
+ %{
176
+ <p class="twitter">
177
+ <a class="twitter_avatar" href="http://twitter.com/#{screen_name}">#{tag.render("tweet:avatar")}</a>
178
+ <span class="tweet">
179
+ <a class="twitter_user" href="http://twitter.com/#{screen_name}">#{screen_name}</a>
180
+ <span class="twitter_name">#{tag.render('tweet:user:name')}</span>
181
+ <span class="twitter_text">#{text}</span>
182
+ <span class="twitter_links">
183
+ #{tag.render('tweet:permalink')}
184
+ #{tag.render('tweet:reply_link')}
185
+ #{tag.render('tweet:retweet_link')}
186
+ #{tag.render('tweet:favorite_link')}
187
+ </span>
188
+ </span>
189
+ </p>
190
+ }
191
+ end
192
+ end
193
+
194
+ [:coordinates, :in_reply_to_screen_name, :truncated, :in_reply_to_user_id, :in_reply_to_status_id,
195
+ :source, :place, :geo, :favorited, :contributors, :id].each do |method|
196
+ desc %{
197
+ Renders the @#{method.to_s}@ attribute of the tweet
198
+ <pre><code><r:tweet:#{method.to_s}/></code></pre>
199
+ }
200
+ tag "tweet:#{method.to_s}" do |tag|
201
+ tag.locals.tweet.send(method) if tag.locals.tweet.respond_to? method
202
+ end
203
+
204
+ desc %{
205
+ expands if the property has a value
206
+ <pre><code><r:tweet:if_#{method.to_s}/></code></pre>
207
+ }
208
+ tag "tweet:if_#{method.to_s}" do |tag|
209
+ value = tag.locals.tweet.send(method) if tag.locals.tweet.respond_to? method
210
+ tag.expand if !value.nil? && !value.empty?
211
+ end
212
+
213
+ desc %{
214
+ expands if the property has no value
215
+ <pre><code><r:tweet:unless_#{method.to_s}/></code></pre>
216
+ }
217
+ tag "tweet:unless_#{method.to_s}" do |tag|
218
+ value = tag.locals.tweet.send(method) if tag.locals.tweet.respond_to? method
219
+ tag.expand if value.nil? || value.empty?
220
+ end
221
+ end
222
+
223
+ [:date, :created_at].each do |method|
224
+ desc %{
225
+ renders the created_at timestamp of the tweet
226
+ <pre><code><r:tweet:#{method.to_s} [format="%c"]/></code></pre>
227
+ }
228
+ tag "tweet:#{method.to_s}" do |tag|
229
+ format = tag.attr['format'] || "%c"
230
+ date = DateTime.parse(tag.locals.tweet.created_at)
231
+ I18n.l date, :format => format
232
+ end
233
+ end
234
+
235
+ tag 'tweet:user' do |tag|
236
+ unless tag.locals.twitterer = tag.locals.tweet.user
237
+ tag.locals.twitterer = fetch_twitter_user(tag.locals.tweet.from_user)
238
+ end
239
+ raise TagError, "twitter user could not be found" unless tag.locals.twitterer
240
+ tag.expand
241
+ end
242
+
243
+ [:time_zone, :description, :lang, :profile_link_color, :profile_background_image_url, :profile_sidebar_fill_color, :following,
244
+ :profile_background_tile, :created_at, :statuses_count,:profile_sidebar_border_color,:profile_use_background_image,:followers_count,
245
+ :contributors_enabled,:notifications,:friends_count,:protected,:url,:profile_image_url,:geo_enabled,:profile_background_color,
246
+ :name,:favourites_count,:location,:screen_name, :id,:verified,:utc_offset,:profile_text_color].each do |method|
247
+ desc %{
248
+ Renders the @#{method.to_s}@ attribute of the tweeting user
249
+ <pre><code><r:tweet:user:#{method.to_s}/></code></pre>
250
+ }
251
+ tag "tweet:user:#{method.to_s}" do |tag|
252
+ tag.locals.twitterer.send(method)
253
+ end
254
+
255
+ desc %{
256
+ expands if @#{method.to_s}@ attribute of the tweeting user has a value
257
+ <pre><code><r:tweet:user:if_#{method.to_s}/></code></pre>
258
+ }
259
+ tag "tweet:user:if_#{method.to_s}" do |tag|
260
+ value = tag.locals.twitterer.send(method) rescue nil
261
+ tag.expand unless value.nil? || value.empty?
262
+ end
263
+
264
+ desc %{
265
+ expands if @#{method.to_s}@ attribute of the tweeting user has no value
266
+ <pre><code><r:tweet:user:unless_#{method.to_s}/></code></pre>
267
+ }
268
+ tag "tweet:user:unless_#{method.to_s}" do |tag|
269
+ value = tag.locals.twitterer.send(method) rescue nil
270
+ tag.expand if value.nil? || value.empty?
271
+ end
272
+ end
273
+
274
+ desc %{
275
+ Renders an avatar image for the tweeter of the current tweet.
276
+ }
277
+ tag 'tweet:avatar' do |tag|
278
+ url = tag.locals.tweet.profile_image_url || tag.render('tweet:user:profile_image_url')
279
+ %{<img src="#{url}" class="twitter_avatar" />}
280
+ end
281
+
282
+ desc %{
283
+ Renders the text for the current tweet.
284
+ }
285
+ tag 'tweet:text' do |tag|
286
+ tweet = tag.locals.tweet
287
+ replace_links(tweet.text)
288
+ end
289
+
290
+ desc %{
291
+ Renders the created ago string for the tweet e.g. Created 7 days...
292
+ }
293
+ tag 'tweet:created_ago' do |tag|
294
+ tweet = tag.locals.tweet
295
+ time_ago_in_words tweet.created_at
296
+ end
297
+
298
+ desc %{
299
+ Renders a permalink to this tweet with its date as the default link text.
300
+ }
301
+ tag 'tweet:permalink' do |tag|
302
+ cssclass = tag.attr['class'] || 'twitter_permalink'
303
+ text = tag.double? ? tag.expand : I18n.l(tag.locals.tweet.created_at, :twitter)
304
+ %{<a class="#{cssclass}" href="http://twitter.com/#!/#{screen_name}/status/#{tweet.id_str}">#{text}</a>}
305
+ end
306
+
307
+ desc %{
308
+ Renders a 'Reply' link that can be left as it is or hooked up by the twitter javascript.
309
+ }
310
+ tag 'tweet:reply_link' do |tag|
311
+ cssclass = tag.attr['class'] || 'twitter_reply'
312
+ text = tag.double? ? tag.expand : I18n.t('twitter_extension.reply')
313
+ %{<a class="#{cssclass}" href="http://twitter.com/intent/tweet?in_reply_to=#{tag.locals.tweet.id_str}">#{text}</a>}
314
+ end
315
+
316
+ desc %{
317
+ Renders a 'Retweet' link that can be left as it is or hooked up by the twitter javascript.
318
+ }
319
+ tag 'tweet:retweet_link' do |tag|
320
+ cssclass = tag.attr['class'] || 'twitter_retweet'
321
+ text = tag.double? ? tag.expand : I18n.t('twitter_extension.retweet')
322
+ %{<a class="#{cssclass}" href="http://twitter.com/intent/retweet?tweet_id=#{tag.locals.tweet.id_str}">#{text}</a>}
323
+ end
324
+
325
+ desc %{
326
+ Renders a 'Favorite' link that can be left as it is or hooked up by the twitter javascript.
327
+ }
328
+ tag 'tweet:favorite_link' do |tag|
329
+ cssclass = tag.attr['class'] || 'twitter_favorite'
330
+ text = tag.double? ? tag.expand : I18n.t('twitter_extension.favorite')
331
+ %{<a class="#{cssclass}" href="http://twitter.com/intent/favorite?tweet_id=#{tag.locals.tweet.id_str}">#{text}</a>}
332
+ end
333
+
334
+ private
335
+
336
+ # Retained for compatibility
337
+ #
338
+ def twitter_status(max = 1)
339
+ max = 1 if (max > 10) or (max < 1)
340
+ fetch_and_cache_tweets(:max => max)
341
+ end
342
+
343
+ # General-purpose tweet-fetcher using Rails::Cache to provide a calm-enhancing gap between similar
344
+ # requests. Set Radiant.config['twitter.expires_in'] to change the gap from 5 minutes.
345
+ # Always returns an array of tweet hashes (mashes, really).
346
+ # :max, :user, :list and :search options are used to determine the call we make (and the cache key).
347
+ # :page and :per_page options are passed through to the search call but not the user or list calls.
348
+ # other options are passed through (to non-search calls) unchanged.
349
+ #
350
+ def fetch_and_cache_tweets(options = {})
351
+ max = options.delete(:max) || 10
352
+ user = options.delete(:username) || Radiant.config['twitter.username']
353
+ list = options.delete(:list) || Radiant.config['twitter.listname']
354
+ search = options.delete(:search)
355
+ options[:count] ||= max
356
+ cache_key = ['twitter', list, user, max, search].compact.join('_')
357
+ begin
358
+ tweets = Rails.cache.fetch(cache_key,:expires_in => twitter_cache_duration) do
359
+ if search
360
+ Twitter::Search.new.containing(search).page(options[:page] || 1).per_page(options[:per_page] || 10).fetch
361
+ elsif list
362
+ twitter_client.list_timeline(user, list, options)
363
+ else
364
+ twitter_client.user_timeline(user, options)
365
+ end
366
+ end
367
+
368
+ rescue Twitter::Error => e
369
+ logger.error "Unable to fetch timeline: #{e.inspect}"
370
+ end
371
+
372
+ tweets || []
373
+ end
374
+
375
+ def fetch_twitter_user(screen_name)
376
+ cache_key = "twitter_user_#{screen_name}"
377
+ begin
378
+ twitter_user = Rails.cache.fetch(cache_key,:expires_in => twitter_cache_duration) do
379
+ twitter_client.user(screen_name)
380
+ end
381
+ rescue Twitter::Error => e
382
+ logger.error "Unable to fetch user '#{screen_name}': #{e.inspect}"
383
+ end
384
+ end
385
+
386
+ # these operations don't require authentication
387
+ #
388
+ def twitter_client
389
+ @twitter_client ||= Twitter::Client.new
390
+ end
391
+
392
+ # Turns http
393
+ #
394
+ def replace_links(text)
395
+ text = text.gsub(/(https?:\/\/\S*)/, '<a class="twitter_link" href="\1">\1</a>')
396
+ text = text.gsub(/@(\w*)/, '@<a class="twitter_link" href="http://twitter.com/\1">\1</a>')
397
+ text = text.gsub(/#(\w*)/, '<a class="twitter_link" href="http://twitter.com/search/#\1">#\1</a>')
398
+ end
399
+
400
+ # The interval between twitter api calls with the same parameters is set by the
401
+ # `twitter.expires_in` config entry and defaults to 5 minutes.
402
+ #
403
+ def twitter_cache_duration
404
+ @twitter_expires_in ||= (Radiant::Config["twitter.expires_in"] || 5).to_i.minutes
405
+ end
406
+ end
Binary file
Binary file
@@ -0,0 +1,46 @@
1
+ p.twitter
2
+ a.twitter_avatar
3
+ float: left
4
+ margin-right: 0.8em
5
+ img
6
+ margin-top: 0.8em
7
+ span.tweet
8
+ display: block
9
+ overflow: hidden
10
+ a.twitter_user
11
+ font-size: 150%
12
+ font-weight: normal
13
+ span.twitter_name
14
+ color: #b2b2b2
15
+ span.twitter_text
16
+ display: block
17
+ span.twitter_links
18
+ display: block
19
+ font-size: 80%
20
+ a
21
+ padding-left: 18px
22
+ height: 16px
23
+ color: #b2b2b2
24
+ background:
25
+ position: top left
26
+ repeat: no-repeat
27
+ image: url(/images/twitter/sprite.png)
28
+ &:hover
29
+ color: #666666
30
+ a.twitter_permalink
31
+ a.twitter_reply
32
+ background-position: 0 -16px
33
+ &:hover
34
+ background-position: 0 -32px
35
+ a.twitter_favourite
36
+ background-position: 0 -48px
37
+ &:hover
38
+ background-position: 0 -64px
39
+ a.twitter_retweet
40
+ background-position: 0 -96px
41
+ &:hover
42
+ background-position: 0 -112px
43
+ a.twitter_favorite
44
+ background-position: 0 -80px
45
+ &:hover
46
+ background-position: 0 -112px
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "radiant-twitter-extension"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "radiant-twitter-extension"
7
+ s.version = RadiantTwitterExtension::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = RadiantTwitterExtension::AUTHORS
10
+ s.email = RadiantTwitterExtension::EMAIL
11
+ s.homepage = RadiantTwitterExtension::URL
12
+ s.summary = RadiantTwitterExtension::SUMMARY
13
+ s.description = RadiantTwitterExtension::DESCRIPTION
14
+
15
+ s.add_dependency 'twitter', "~> 1.6.0"
16
+
17
+ ignores = if File.exist?('.gitignore')
18
+ File.read('.gitignore').split("\n").inject([]) {|a,p| a + Dir[p] }
19
+ else
20
+ []
21
+ end
22
+ s.files = Dir['**/*'] - ignores
23
+ s.test_files = Dir['test/**/*','spec/**/*','features/**/*'] - ignores
24
+ # s.executables = Dir['bin/*'] - ignores
25
+ s.require_paths = ["lib"]
26
+
27
+ s.post_install_message = %{
28
+ Add this to your radiant project with:
29
+ config.gem 'radiant-twitter-extension', :version => '~>#{RadiantTwitterExtension::VERSION}'
30
+ }
31
+ end
@@ -0,0 +1,114 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe 'TwitterTags' do
4
+ dataset :pages
5
+
6
+ before do
7
+ Radiant.config['twitter.username'] = 'testy'
8
+ Radiant.config['twitter.password'] = 'secret'
9
+
10
+ @client = mock("HTTPAuth (client)").as_null_object
11
+
12
+ # I've been getting odd failures with mock tweets
13
+ # when they're passed into a radius context
14
+
15
+ @tweets = (1..10).collect do |i|
16
+ OpenStruct.new.tap do |tweet|
17
+ tweet.text = "tweet #{i}"
18
+ tweet.created_at = DateTime.new(2010, 2, i+1).to_s
19
+ tweet.source = "<a href=\"http://www.atebits.com/\" rel=\"nofollow\">Tweetie</a>"
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '<r:twitter> The main context' do
25
+ it 'should give no output' do
26
+ tag = %{<r:twitter />}
27
+ pages(:home).should render(tag).as('')
28
+ end
29
+ end
30
+
31
+ describe '<r:twitter:tweets> Collect the users recent tweets' do
32
+ before(:each) do
33
+ Twitter::Client.stub!(:new).and_return(@client)
34
+ @client.stub!(:user_timeline).and_return(@tweets)
35
+ end
36
+
37
+ it 'should give no output' do
38
+ tag = %{<r:twitter><r:tweets></r:tweets></r:twitter>}
39
+ pages(:home).should render(tag).as('')
40
+ end
41
+
42
+ it 'should report the correct number of tweets' do
43
+ tag = %{<r:twitter><r:tweets:length /></r:twitter>}
44
+ pages(:home).should render(tag).as("10")
45
+ end
46
+
47
+ it 'should return the tweet text and replace links' do
48
+ tag = %{<r:twitter><r:tweets:each><r:tweet:text /></r:tweets:each></r:twitter>}
49
+ expected = @tweets.map(&:text).join('')
50
+ pages(:home).should render(tag).as(expected)
51
+ end
52
+
53
+ it 'should return the created at timestamp' do
54
+ tag = %{<r:twitter><r:tweets:each><r:tweet:created_at format="%d %B" /></r:tweets:each></r:twitter>}
55
+ expected = (1..10).map{|i| "#{'%02d' % (i+1)} February"}.join('')
56
+ pages(:home).should render(tag).as(expected)
57
+ end
58
+
59
+ it 'should return the created ago string' do
60
+ tag = %{<r:twitter><r:tweets:each><r:tweet:created_ago /></r:tweets:each></r:twitter>}
61
+ Time.stub!(:now).and_return(Time.parse("Feb 12 2010"))
62
+ expected = '10 days9 days8 days7 days6 days5 days4 days3 days2 days1 day'
63
+ pages(:home).should render(tag).as(expected)
64
+ end
65
+
66
+ it 'should return the source' do
67
+ tag = %{<r:twitter><r:tweets:each><r:tweet:source /></r:tweets:each></r:twitter>}
68
+ expected = @tweets.map(&:source).join('')
69
+ pages(:home).should render(tag).as(expected)
70
+ end
71
+ end
72
+
73
+ describe '<r:twitter:list list="list"> Collect the users list tweets' do
74
+ before(:each) do
75
+ Twitter::Client.stub!(:new).and_return(@client)
76
+ @client.stub!(:list_timeline).and_return(@tweets)
77
+ end
78
+
79
+ it 'should give no output' do
80
+ tag = %{<r:twitter><r:list list="list"></r:list></r:twitter>}
81
+ pages(:home).should render(tag).as('')
82
+ end
83
+
84
+ it 'should report the correct number of tweets (default 10)' do
85
+ tag = %{<r:twitter><r:list list="list"><r:length /></r:list></r:twitter>}
86
+ pages(:home).should render(tag).as("10")
87
+ end
88
+
89
+ it 'should return the tweet text and replace links' do
90
+ tag = %{<r:twitter><r:list list="list"><r:each><r:tweet:text /></r:each></r:list></r:twitter>}
91
+ expected = @tweets.map(&:text).join('')
92
+ pages(:home).should render(tag).as(expected)
93
+ end
94
+
95
+ it 'should return the created at timestamp' do
96
+ tag = %{<r:twitter><r:list list="list"><r:each><r:tweet:created_at format="%d %B" /></r:each></r:list></r:twitter>}
97
+ expected = (1..10).map{|i| "#{'%02d' % (i+1)} February"}.join('')
98
+ pages(:home).should render(tag).as(expected)
99
+ end
100
+
101
+ it 'should return the created ago string' do
102
+ tag = %{<r:twitter><r:list list="list"><r:each><r:tweet:created_ago /></r:each></r:list></r:twitter>}
103
+ Time.stub!(:now).and_return(Time.parse("Feb 12 2010"))
104
+ expected = '10 days9 days8 days7 days6 days5 days4 days3 days2 days1 day'
105
+ pages(:home).should render(tag).as(expected)
106
+ end
107
+
108
+ it 'should return the source' do
109
+ tag = %{<r:twitter><r:list list="list"><r:each><r:tweet:source /></r:each></r:list></r:twitter>}
110
+ expected = @tweets.map(&:source).join('')
111
+ pages(:home).should render(tag).as(expected)
112
+ end
113
+ end
114
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -0,0 +1,37 @@
1
+ unless defined? RADIANT_ROOT
2
+ ENV["RAILS_ENV"] = "test"
3
+ case
4
+ when ENV["RADIANT_ENV_FILE"]
5
+ require ENV["RADIANT_ENV_FILE"]
6
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
7
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
8
+ else
9
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
10
+ end
11
+ end
12
+ require "#{RADIANT_ROOT}/spec/spec_helper"
13
+
14
+ if File.directory?(File.dirname(__FILE__) + "/scenarios")
15
+ Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
16
+ end
17
+ if File.directory?(File.dirname(__FILE__) + "/matchers")
18
+ Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
19
+ end
20
+
21
+ Spec::Runner.configure do |config|
22
+ # config.use_transactional_fixtures = true
23
+ # config.use_instantiated_fixtures = false
24
+ config.fixture_path = File.dirname(__FILE__) + '/spec/fixtures'
25
+
26
+ # You can declare fixtures for each behaviour like this:
27
+ # describe "...." do
28
+ # fixtures :table_a, :table_b
29
+ #
30
+ # Alternatively, if you prefer to declare them only once, you can
31
+ # do so here, like so ...
32
+ #
33
+ # config.global_fixtures = :table_a, :table_b
34
+ #
35
+ # If you declare global fixtures, be aware that they will be declared
36
+ # for all of your examples, even those that don't use them.
37
+ end
@@ -0,0 +1,18 @@
1
+ class TwitterExtension < Radiant::Extension
2
+ version RadiantTwitterExtension::VERSION
3
+ description RadiantTwitterExtension::DESCRIPTION
4
+ url RadiantTwitterExtension::URL
5
+
6
+ extension_config do |config|
7
+ config.gem "twitter", :version => "~> 1.6.0"
8
+ end
9
+
10
+ def activate
11
+ Page.send :include, TwitterNotification # tweet page title upon publication
12
+ Page.send :include, TwitterTags # radius tags to display twitter search results
13
+
14
+ admin.pages.edit.add :extended_metadata, "twitter" # toggle twitter-posting at parent level
15
+ admin.configuration.show.add :config, 'admin/configuration/twitter_show', :after => 'defaults'
16
+ admin.configuration.edit.add :form, 'admin/configuration/twitter_edit', :after => 'edit_defaults'
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
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.
@@ -0,0 +1,56 @@
1
+ Filestore Cache Expires_in Plugin
2
+ ============================
3
+
4
+ Adds a :expires_in option to Rails filestore caching, in line with memcached store. Providing a very easy way to add high capacity caching to a site.
5
+
6
+ Model observers and sweepers are still useable as well, of course.
7
+
8
+ Filestore caching is a much easier method of caching to implement quickly than memcached, since it just involves writing the cache data to files in the tmp/ directory (also making it good for shared hosting environments).
9
+
10
+ Installation
11
+ ------
12
+
13
+ 1) Install plugin
14
+
15
+ ./script/plugin install git://github.com/adamsalter/filestore_expires_in-plugin.git
16
+
17
+ 2) Enable :file_store caching
18
+
19
+ config/environments/production.rb:
20
+
21
+ config.action_controller.perform_caching = true
22
+ config.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
23
+
24
+ Useage
25
+ ------
26
+
27
+ On controller classes:
28
+
29
+ class MyController < ApplicationController
30
+ caches_action :show, {:if => etc.}, {:expires_in => 1.day}
31
+ end
32
+
33
+ In controller methods:
34
+
35
+ unless read_fragment({:controller => 'mycontroller', etc.}, {:expires_in => 1.day})
36
+ #code here
37
+ end
38
+
39
+ In views:
40
+
41
+ cache({:controller => 'mycontroller', etc.}, {:expires_in => 1.day}) do
42
+ #fragment here
43
+ end
44
+
45
+ In models:
46
+
47
+ cache_key = "models/my_model/%s?%s-%s-%s" % ["model_method_name", self.value, page, limit]
48
+
49
+ result = Rails.cache.fetch(cache_key, :expires_in => 1.day) do
50
+ # result data to be cached
51
+ end
52
+
53
+
54
+ Copyright (c) 2009 Adam @ [Codebright.net][cb], released under the MIT license
55
+
56
+ [cb]:http://codebright.net
@@ -0,0 +1,3 @@
1
+ # Include hook code here
2
+
3
+ require 'active_support/cache/file_store_extras'
@@ -0,0 +1,26 @@
1
+
2
+ module ActiveSupport
3
+ module Cache
4
+ class FileStore < Store
5
+ def read(name, options = nil)
6
+ super
7
+ file_name = real_file_path(name)
8
+ expires = expires_in(options)
9
+
10
+ if exist_without_instrument?(file_name, expires)
11
+ File.open(file_name, 'rb') { |f| Marshal.load(f) }
12
+ end
13
+ end
14
+
15
+ def exist?(name, options = nil)
16
+ super
17
+ File.exist?(real_file_path(name))
18
+ exist_without_instrument?(real_file_path(name), expires_in(options))
19
+ end
20
+
21
+ def exist_without_instrument?(file_name, expires)
22
+ File.exist?(file_name) && (expires <= 0 || Time.now - File.mtime(file_name) < expires)
23
+ end
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radiant-twitter-extension
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15424087
5
+ prerelease: 6
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ - rc
11
+ - 1
12
+ version: 2.0.0.rc1
13
+ platform: ruby
14
+ authors:
15
+ - Sean Cribbs
16
+ - Edmund Haselwanter
17
+ - Jim Gay
18
+ - William Ross
19
+ autorequire:
20
+ bindir: bin
21
+ cert_chain: []
22
+
23
+ date: 2011-07-28 00:00:00 +01:00
24
+ default_executable:
25
+ dependencies:
26
+ - !ruby/object:Gem::Dependency
27
+ name: twitter
28
+ prerelease: false
29
+ requirement: &id001 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ hash: 15
35
+ segments:
36
+ - 1
37
+ - 6
38
+ - 0
39
+ version: 1.6.0
40
+ type: :runtime
41
+ version_requirements: *id001
42
+ description: Posts notification of pages to Twitter and provides radiius tags to display the results of twitter searches.
43
+ email:
44
+ - radiant@radiantcms.org
45
+ executables: []
46
+
47
+ extensions: []
48
+
49
+ extra_rdoc_files: []
50
+
51
+ files:
52
+ - app/views/admin/configuration/_twitter_edit.html.haml
53
+ - app/views/admin/configuration/_twitter_show.html.haml
54
+ - app/views/admin/pages/_twitter.html.haml
55
+ - config/initializers/radiant_config.rb
56
+ - config/locales/en.yml
57
+ - db/migrate/001_add_twitter_notification_fields.rb
58
+ - HELP_admin.md
59
+ - lib/radiant-twitter-extension.rb
60
+ - lib/tasks/twitter_extension_tasks.rake
61
+ - lib/twitter_notification.rb
62
+ - lib/twitter_tags.rb
63
+ - public/images/twitter/bird.png
64
+ - public/images/twitter/sprite.png
65
+ - public/stylesheets/sass/twitter.sass
66
+ - radiant-twitter-extension.gemspec
67
+ - Rakefile
68
+ - README.md
69
+ - spec/lib/twitter_tags_spec.rb
70
+ - spec/spec.opts
71
+ - spec/spec_helper.rb
72
+ - twitter_extension.rb
73
+ - vendor/plugins/filestore_expires_in-plugin/init.rb
74
+ - vendor/plugins/filestore_expires_in-plugin/lib/active_support/cache/file_store_extras.rb
75
+ - vendor/plugins/filestore_expires_in-plugin/MIT-LICENSE
76
+ - vendor/plugins/filestore_expires_in-plugin/README.md
77
+ has_rdoc: true
78
+ homepage: http://github.com/ehaselwanter/radiant-twitter-extension
79
+ licenses: []
80
+
81
+ post_install_message: "\n Add this to your radiant project with:\n config.gem 'radiant-twitter-extension', :version => '~>2.0.0.rc1'\n "
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">"
99
+ - !ruby/object:Gem::Version
100
+ hash: 25
101
+ segments:
102
+ - 1
103
+ - 3
104
+ - 1
105
+ version: 1.3.1
106
+ requirements: []
107
+
108
+ rubyforge_project:
109
+ rubygems_version: 1.5.3
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Twitter posting and radius tags for twitter feeds.
113
+ test_files:
114
+ - spec/lib/twitter_tags_spec.rb
115
+ - spec/spec.opts
116
+ - spec/spec_helper.rb