cartoonist-twitter 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/app/controllers/admin/tweets_controller.rb +38 -0
- data/app/models/tweet.rb +214 -0
- data/app/views/admin/tweets/_entity_tweet.html.erb +22 -0
- data/app/views/admin/tweets/edit.html.erb +5 -0
- data/app/views/admin/tweets/index.html.erb +21 -0
- data/app/views/layouts/admin/tweets.html.erb +3 -0
- data/cartoonist-twitter.gemspec +16 -0
- data/config/locales/en.yml +43 -0
- data/db/migrate/20120501083235_create_tweets.rb +14 -0
- data/lib/cartoonist-twitter/engine.rb +67 -0
- data/lib/cartoonist-twitter.rb +5 -0
- metadata +78 -0
data/.gitignore
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class Admin::TweetsController < CartoonistController
|
2
|
+
before_filter :ensure_ssl!
|
3
|
+
before_filter :check_admin!
|
4
|
+
|
5
|
+
def index
|
6
|
+
@untweeted = Tweet.untweeted.created_chronological.not_disabled
|
7
|
+
@tweeted = Tweet.tweeted.reverse_chronological
|
8
|
+
end
|
9
|
+
|
10
|
+
def edit
|
11
|
+
@tweet = Tweet.find params[:id].to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def update
|
15
|
+
@tweet = Tweet.update_tweet params
|
16
|
+
send_tweet_if_requested
|
17
|
+
tweet_or_entity_redirect
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@tweet = Tweet.create_tweet params
|
22
|
+
send_tweet_if_requested
|
23
|
+
tweet_or_entity_redirect
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def send_tweet_if_requested
|
28
|
+
@tweet.manual_tweet! if params[:tweet_now].present?
|
29
|
+
end
|
30
|
+
|
31
|
+
def tweet_or_entity_redirect
|
32
|
+
if params[:destination] == "entity_edit" && @tweet.entity.entity_edit_url
|
33
|
+
redirect_to @tweet.entity.entity_edit_url
|
34
|
+
else
|
35
|
+
redirect_to "/admin/tweets/#{@tweet.id}/edit"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/app/models/tweet.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
class Tweet < ActiveRecord::Base
|
2
|
+
validate :doesnt_update_tweet_after_tweeted
|
3
|
+
validate :entity_doesnt_change, :on => :update
|
4
|
+
attr_accessible :entity_id, :entity_type, :tweet, :tweeted_at
|
5
|
+
|
6
|
+
def allow_tweet_now?
|
7
|
+
if entity.kind_of? Postable
|
8
|
+
posted = entity.posted?
|
9
|
+
else
|
10
|
+
posted = true
|
11
|
+
end
|
12
|
+
|
13
|
+
allow_save? && !disabled? && posted
|
14
|
+
end
|
15
|
+
|
16
|
+
def allow_save?
|
17
|
+
!tweeted?
|
18
|
+
end
|
19
|
+
|
20
|
+
def disabled?
|
21
|
+
tweet_style == :disabled
|
22
|
+
end
|
23
|
+
|
24
|
+
def entity
|
25
|
+
@entity ||= Cartoonist::Entity[entity_type.to_sym].find(entity_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def description
|
29
|
+
"#{entity.entity_localized_label}: #{entity.entity_description}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def formatted_tweeted_at(default_msg = "not yet tweeted")
|
33
|
+
return default_msg unless tweeted_at
|
34
|
+
tweeted_at.localtime.strftime "%-m/%-d/%Y at %-l:%M %P"
|
35
|
+
end
|
36
|
+
|
37
|
+
def manual_tweet!
|
38
|
+
raise "Tweeting is not allowed!" unless allow_tweet_now?
|
39
|
+
return if tweeted?
|
40
|
+
return if disabled?
|
41
|
+
send_tweet!
|
42
|
+
end
|
43
|
+
|
44
|
+
def auto_tweet!
|
45
|
+
return if tweeted?
|
46
|
+
return unless expected_tweet_time
|
47
|
+
return unless Time.now >= expected_tweet_time
|
48
|
+
send_tweet!
|
49
|
+
end
|
50
|
+
|
51
|
+
def tweet_style
|
52
|
+
Setting[:"#{entity.entity_type}_tweet_style"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def tweet_time
|
56
|
+
Setting[:"#{entity.entity_type}_tweet_time"]
|
57
|
+
end
|
58
|
+
|
59
|
+
def form_path
|
60
|
+
if new_record?
|
61
|
+
"/admin/tweets"
|
62
|
+
else
|
63
|
+
"/admin/tweets/#{id}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def form_method
|
68
|
+
if new_record?
|
69
|
+
:post
|
70
|
+
else
|
71
|
+
:put
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def expected_tweet_time
|
76
|
+
case tweet_style
|
77
|
+
when :disabled
|
78
|
+
nil
|
79
|
+
when :manual
|
80
|
+
nil
|
81
|
+
when :automatic
|
82
|
+
entity.posted_at
|
83
|
+
when :automatic_timed
|
84
|
+
if tweet_time.blank?
|
85
|
+
entity.posted_at
|
86
|
+
elsif entity.posted_at
|
87
|
+
parsed = DateTime.strptime("#{entity.posted_at.year}-#{entity.posted_at.month}-#{entity.posted_at.day} #{tweet_time.downcase}", "%Y-%m-%d %I:%M %p").to_time
|
88
|
+
result = Time.local entity.posted_at.year, entity.posted_at.month, entity.posted_at.day, parsed.hour, parsed.min
|
89
|
+
result = result + 1.day if result < entity.posted_at.to_time
|
90
|
+
result
|
91
|
+
end
|
92
|
+
else
|
93
|
+
raise "Invalid tweet style #{tweet_style}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def tweeted?
|
98
|
+
tweeted_at.present?
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def send_tweet!
|
103
|
+
if Rails.env.production?
|
104
|
+
Twitter.update tweet
|
105
|
+
else
|
106
|
+
logger.info "Fake Tweet: #{tweet}"
|
107
|
+
end
|
108
|
+
|
109
|
+
self.tweeted_at = Time.now
|
110
|
+
save!
|
111
|
+
end
|
112
|
+
|
113
|
+
def doesnt_update_tweet_after_tweeted
|
114
|
+
if !tweeted_at_was.nil? && tweet_changed?
|
115
|
+
errors.add :tweet, "can't change after the tweet has been sent"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def entity_doesnt_change
|
120
|
+
if entity_id_changed?
|
121
|
+
errors.add :entity_id, "can't change"
|
122
|
+
end
|
123
|
+
|
124
|
+
if entity_type_changed?
|
125
|
+
errors.add :entity_id, "can't change"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class << self
|
130
|
+
def update_tweet(params)
|
131
|
+
tweet = find params[:id].to_i
|
132
|
+
raise "Saving is not allowed!" unless tweet.allow_save?
|
133
|
+
tweet.tweet = params[:tweet]
|
134
|
+
tweet.save!
|
135
|
+
tweet
|
136
|
+
end
|
137
|
+
|
138
|
+
def create_tweet(params)
|
139
|
+
raise "The entity_id is required!" if params[:entity_id].blank?
|
140
|
+
raise "The entity_type is required!" if params[:entity_type].blank?
|
141
|
+
create :entity_id => params[:entity_id].to_i, :entity_type => params[:entity_type], :tweet => params[:tweet]
|
142
|
+
end
|
143
|
+
|
144
|
+
def tweet_for(entity)
|
145
|
+
result = find_for entity
|
146
|
+
|
147
|
+
if result
|
148
|
+
result
|
149
|
+
else
|
150
|
+
new options_for(entity)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_for(entity)
|
155
|
+
create options_for(entity)
|
156
|
+
end
|
157
|
+
|
158
|
+
def options_for(entity)
|
159
|
+
{ :entity_id => entity.id, :entity_type => entity.entity_type, :tweet => SimpleTemplate[Setting[:"#{entity.entity_type}_default_tweet"], :self => entity] }
|
160
|
+
end
|
161
|
+
|
162
|
+
def find_for(entity)
|
163
|
+
raise "Can only find tweets for entities!" unless entity.kind_of? Entity
|
164
|
+
where({ :entity_id => entity.id, :entity_type => entity.entity_type }).first
|
165
|
+
end
|
166
|
+
|
167
|
+
def tweeted
|
168
|
+
where "tweeted_at IS NOT NULL"
|
169
|
+
end
|
170
|
+
|
171
|
+
def untweeted
|
172
|
+
where "tweeted_at IS NULL"
|
173
|
+
end
|
174
|
+
|
175
|
+
def chronological
|
176
|
+
order "tweeted_at ASC"
|
177
|
+
end
|
178
|
+
|
179
|
+
def reverse_chronological
|
180
|
+
order "tweeted_at DESC"
|
181
|
+
end
|
182
|
+
|
183
|
+
def created_chronological
|
184
|
+
order "created_at ASC"
|
185
|
+
end
|
186
|
+
|
187
|
+
def created_reverse_chronological
|
188
|
+
order "created_at DESC"
|
189
|
+
end
|
190
|
+
|
191
|
+
def not_disabled
|
192
|
+
result = all
|
193
|
+
|
194
|
+
types = result.map(&:entity_type).uniq.select do |t|
|
195
|
+
Setting[:"#{t}_tweet_style"] != :disabled
|
196
|
+
end
|
197
|
+
|
198
|
+
result.select { |x| types.include? x.entity_type }
|
199
|
+
end
|
200
|
+
|
201
|
+
def styles(entity)
|
202
|
+
results = []
|
203
|
+
results << { :value => :disabled, :label => "tweet.style.disabled" }
|
204
|
+
results << { :value => :manual, :label => "tweet.style.manual" }
|
205
|
+
|
206
|
+
if Cartoonist::Entity[entity].include? Postable
|
207
|
+
results << { :value => :automatic, :label => "tweet.style.automatic" }
|
208
|
+
results << { :value => :automatic_timed, :label => "tweet.style.automatic_timed" }
|
209
|
+
end
|
210
|
+
|
211
|
+
results
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% tweet = Tweet.tweet_for entity %>
|
2
|
+
<% if tweet.tweet_style != :disabled %>
|
3
|
+
<% destination = :entity_edit if destination.nil? %>
|
4
|
+
<%= form_tag tweet.form_path, :method => tweet.form_method do %>
|
5
|
+
<input type="hidden" name="entity_id" value="<%= tweet.entity_id %>" />
|
6
|
+
<input type="hidden" name="entity_type" value="<%= tweet.entity_type %>" />
|
7
|
+
<input type="hidden" name="destination" value="<%= destination %>" />
|
8
|
+
|
9
|
+
<p>
|
10
|
+
<%= t "admin.tweets.entity_tweet.tweet" %><br />
|
11
|
+
<textarea name="tweet" rows="2" cols="100" <%= 'disabled="disabled"'.html_safe unless tweet.allow_save? %>><%= tweet.tweet %></textarea>
|
12
|
+
</p>
|
13
|
+
|
14
|
+
<p>
|
15
|
+
<input type="submit" name="save" value="<%= t "admin.tweets.entity_tweet.save" %>" <%= 'disabled="disabled"'.html_safe unless tweet.allow_save? %> />
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<input type="submit" name="tweet_now" value="<%= t "admin.tweets.entity_tweet.tweet_now" %>" <%= 'disabled="disabled"'.html_safe unless tweet.allow_tweet_now? %> />
|
20
|
+
</p>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h2><%= t "admin.tweets.index.not_tweeted" %></h2>
|
2
|
+
|
3
|
+
<ul>
|
4
|
+
<% @untweeted.each do |tweet| %>
|
5
|
+
<li>
|
6
|
+
<%= tweet.formatted_tweeted_at "" %>
|
7
|
+
<a href="/admin/tweets/<%= tweet.id %>/edit"><%= tweet.description %></a>
|
8
|
+
</li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
|
12
|
+
<h2><%= t "admin.tweets.index.tweeted" %></h2>
|
13
|
+
|
14
|
+
<ul>
|
15
|
+
<% @tweeted.each do |tweet| %>
|
16
|
+
<li>
|
17
|
+
<%= tweet.formatted_tweeted_at "" %>
|
18
|
+
<a href="/admin/tweets/<%= tweet.id %>/edit"><%= tweet.description %></a>
|
19
|
+
</li>
|
20
|
+
<% end %>
|
21
|
+
</ul>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
raise "Cannot find version file!" unless File.exists?(File.join(File.dirname(__FILE__), "../CARTOONIST_VERSION"))
|
3
|
+
cartoonist_version = File.read(File.join(File.dirname(__FILE__), "../CARTOONIST_VERSION")).strip
|
4
|
+
s.name = "cartoonist-twitter"
|
5
|
+
s.version = cartoonist_version
|
6
|
+
s.date = Time.now.strftime "%Y-%m-%d"
|
7
|
+
s.summary = "Cartoonist Twitter"
|
8
|
+
s.description = "Plugin to allow tweeting of cartoonist entities."
|
9
|
+
s.authors = ["Mike Virata-Stone"]
|
10
|
+
s.email = "reasonnumber@gmail.com"
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.homepage = "http://reasonnumber.com/cartoonist"
|
14
|
+
s.add_dependency "cartoonist", cartoonist_version
|
15
|
+
s.add_dependency "twitter", "~> 2.2.0"
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
en:
|
2
|
+
admin:
|
3
|
+
layout:
|
4
|
+
tab:
|
5
|
+
tweets: Tweets
|
6
|
+
tweets:
|
7
|
+
entity_tweet:
|
8
|
+
save: Save
|
9
|
+
tweet: "Tweet:"
|
10
|
+
tweet_now: Tweet Now
|
11
|
+
index:
|
12
|
+
not_tweeted: Not yet tweeted
|
13
|
+
tweeted: Tweeted
|
14
|
+
layout:
|
15
|
+
section: Tweets
|
16
|
+
cartoonist:
|
17
|
+
layout:
|
18
|
+
navigation:
|
19
|
+
follow_on_twitter: Follow us on Twitter
|
20
|
+
follow_on_twitter_title: Follow us on Twitter
|
21
|
+
settings:
|
22
|
+
show:
|
23
|
+
errors:
|
24
|
+
invalid_tweet_time: "Invalid tweet time '%{value}' should be formatted like: '1:22 pm' or '11:30 am'"
|
25
|
+
sections:
|
26
|
+
social_and_analytics:
|
27
|
+
twitter: Twitter
|
28
|
+
settings:
|
29
|
+
default_tweet: "%{entity} Default Tweet: "
|
30
|
+
tweet_style: "%{entity} Tweet Style: "
|
31
|
+
tweet_time: "%{entity} Tweet Time: "
|
32
|
+
tweet_time_info: "Time to tweet when style is \"Automatic at a specific time\" (format example: 3:30 pm)"
|
33
|
+
twitter_consumer_key: "Twitter Consumer Key: "
|
34
|
+
twitter_consumer_secret: "Twitter Consumer Secret: "
|
35
|
+
twitter_handle: "Twitter Handle: "
|
36
|
+
twitter_oauth_token: "Twitter Oauth Token: "
|
37
|
+
twitter_oauth_token_secret: "Twitter Oauth Token Secret: "
|
38
|
+
tweet:
|
39
|
+
style:
|
40
|
+
automatic: Automatic after posted
|
41
|
+
automatic_timed: Automatic at a specific time
|
42
|
+
disabled: Disabled
|
43
|
+
manual: Manual only
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateTweets < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :tweets do |t|
|
4
|
+
t.integer :entity_id, :null => false
|
5
|
+
t.string :entity_type, :null => false
|
6
|
+
t.string :tweet, :null => false, :length => 140
|
7
|
+
t.datetime :tweeted_at
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :tweets, :tweeted_at
|
12
|
+
add_index :tweets, [:entity_id, :entity_type], :unique => true
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module CartoonistTwitter
|
2
|
+
class EntityHooks
|
3
|
+
class << self
|
4
|
+
def after_entity_save(entity)
|
5
|
+
return if Setting[:"#{entity.entity_type}_tweet_style"] == :disabled
|
6
|
+
result = Tweet.find_for entity
|
7
|
+
Tweet.create_for entity unless result
|
8
|
+
end
|
9
|
+
|
10
|
+
def edit_entity_before_partial
|
11
|
+
"admin/tweets/entity_tweet"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Engine < ::Rails::Engine
|
17
|
+
config.to_prepare do
|
18
|
+
twitter_auth_changed = lambda do
|
19
|
+
Twitter.configure do |twitter_config|
|
20
|
+
twitter_config.consumer_key = Setting[:twitter_consumer_key]
|
21
|
+
twitter_config.consumer_secret = Setting[:twitter_consumer_secret]
|
22
|
+
twitter_config.oauth_token = Setting[:twitter_oauth_token]
|
23
|
+
twitter_config.oauth_token_secret = Setting[:twitter_oauth_token_secret]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Setting::Section.define :twitter, :order => 2, :tab => :social_and_analytics do
|
28
|
+
order = 0
|
29
|
+
|
30
|
+
Cartoonist::Entity.all.each do |entity|
|
31
|
+
Setting.define :"#{entity.entity_type}_tweet_style", :type => :symbol, :order => (order += 1), :label => lambda { I18n.t("settings.show.settings.tweet_style", :entity => entity.entity_localized_label) }, :select_from => lambda { Tweet.styles(entity.entity_type) }, :default => :disabled
|
32
|
+
Setting.define :"#{entity.entity_type}_tweet_time", :order => (order += 1), :label => lambda { I18n.t("settings.show.settings.tweet_time", :entity => entity.entity_localized_label) }, :info_label => "settings.show.settings.tweet_time_info", :validation => lambda { |value| raise Setting::InvalidError.new I18n.t("settings.show.errors.invalid_tweet_time", :value => value) unless value =~ /^((1[0-2]|[1-9])\:[0-5]\d (am|pm|AM|PM))?$/ }
|
33
|
+
Setting.define :"#{entity.entity_type}_default_tweet", :order => (order += 1), :label => lambda { I18n.t("settings.show.settings.default_tweet", :entity => entity.entity_localized_label) }
|
34
|
+
end
|
35
|
+
|
36
|
+
Setting.define :twitter_handle, :order => (order += 1)
|
37
|
+
Setting.define :twitter_consumer_key, :onchange => twitter_auth_changed, :order => (order += 1)
|
38
|
+
Setting.define :twitter_consumer_secret, :onchange => twitter_auth_changed, :order => (order += 1)
|
39
|
+
Setting.define :twitter_oauth_token, :onchange => twitter_auth_changed, :order => (order += 1)
|
40
|
+
Setting.define :twitter_oauth_token_secret, :onchange => twitter_auth_changed, :order => (order += 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
twitter_auth_changed.call
|
44
|
+
end
|
45
|
+
|
46
|
+
Cartoonist::Admin::Tab.add :tweets, :url => "/admin/tweets"
|
47
|
+
Cartoonist::Navigation::Link.add :url => (lambda { "https://twitter.com/#{Setting[:twitter_handle]}" }), :class => "follow-us", :label => "cartoonist.layout.navigation.follow_on_twitter", :title => "cartoonist.layout.navigation.follow_on_twitter_title", :order => 2
|
48
|
+
Cartoonist::Migration.add_for self
|
49
|
+
Cartoonist::Entity.register_hooks CartoonistTwitter::EntityHooks
|
50
|
+
|
51
|
+
Cartoonist::Backup.for :tweets do
|
52
|
+
Tweet.order(:id).all
|
53
|
+
end
|
54
|
+
|
55
|
+
Cartoonist::Cron.add do
|
56
|
+
Tweet.untweeted.each do |tweet|
|
57
|
+
tweet.auto_tweet!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Cartoonist::Routes.add do
|
62
|
+
namespace :admin do
|
63
|
+
resources :tweets
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cartoonist-twitter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Virata-Stone
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: cartoonist
|
16
|
+
requirement: &17879120 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.0.9
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *17879120
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: twitter
|
27
|
+
requirement: &17878040 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.2.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *17878040
|
36
|
+
description: Plugin to allow tweeting of cartoonist entities.
|
37
|
+
email: reasonnumber@gmail.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- .gitignore
|
43
|
+
- app/controllers/admin/tweets_controller.rb
|
44
|
+
- app/models/tweet.rb
|
45
|
+
- app/views/admin/tweets/_entity_tweet.html.erb
|
46
|
+
- app/views/admin/tweets/edit.html.erb
|
47
|
+
- app/views/admin/tweets/index.html.erb
|
48
|
+
- app/views/layouts/admin/tweets.html.erb
|
49
|
+
- cartoonist-twitter.gemspec
|
50
|
+
- config/locales/en.yml
|
51
|
+
- db/migrate/20120501083235_create_tweets.rb
|
52
|
+
- lib/cartoonist-twitter.rb
|
53
|
+
- lib/cartoonist-twitter/engine.rb
|
54
|
+
homepage: http://reasonnumber.com/cartoonist
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.17
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Cartoonist Twitter
|
78
|
+
test_files: []
|