diff --git a/README b/README new file mode 100644 index 0000000..9008ef5 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ += Twitter + +Description goes here \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..eef2ed4 --- /dev/null +++ b/Rakefile @@ -0,0 +1,120 @@ +# I think this is the one that should be moved to the extension Rakefile template + +# In rails 1.2, plugins aren't available in the path until they're loaded. +# Check to see if the rspec plugin is installed first and require +# it if it is. If not, use the gem version. + +# Determine where the RSpec plugin is by loading the boot +unless defined? RADIANT_ROOT + ENV["RAILS_ENV"] = "test" + case + when ENV["RADIANT_ENV_FILE"] + require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot" + when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions} + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot" + else + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot" + end +end + +require 'rake' +require 'rake/rdoctask' +require 'rake/testtask' + +rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib') +$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base) +require 'spec/rake/spectask' +# require 'spec/translator' + +# Cleanup the RADIANT_ROOT constant so specs will load the environment +Object.send(:remove_const, :RADIANT_ROOT) + +extension_root = File.expand_path(File.dirname(__FILE__)) + +task :default => :spec +task :stats => "spec:statsetup" + +desc "Run all specs in spec directory" +Spec::Rake::SpecTask.new(:spec) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] +end + +namespace :spec do + desc "Run all specs in spec directory with RCov" + Spec::Rake::SpecTask.new(:rcov) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList['spec/**/*_spec.rb'] + t.rcov = true + t.rcov_opts = ['--exclude', 'spec', '--rails'] + end + + desc "Print Specdoc for all specs" + Spec::Rake::SpecTask.new(:doc) do |t| + t.spec_opts = ["--format", "specdoc", "--dry-run"] + t.spec_files = FileList['spec/**/*_spec.rb'] + end + + [:models, :controllers, :views, :helpers].each do |sub| + desc "Run the specs under spec/#{sub}" + Spec::Rake::SpecTask.new(sub) do |t| + t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""] + t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] + end + end + + # Hopefully no one has written their extensions in pre-0.9 style + # desc "Translate specs from pre-0.9 to 0.9 style" + # task :translate do + # translator = ::Spec::Translator.new + # dir = RAILS_ROOT + '/spec' + # translator.translate(dir, dir) + # end + + # Setup specs for stats + task :statsetup do + require 'code_statistics' + ::STATS_DIRECTORIES << %w(Model\ specs spec/models) + ::STATS_DIRECTORIES << %w(View\ specs spec/views) + ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) + ::STATS_DIRECTORIES << %w(Helper\ specs spec/views) + ::CodeStatistics::TEST_TYPES << "Model specs" + ::CodeStatistics::TEST_TYPES << "View specs" + ::CodeStatistics::TEST_TYPES << "Controller specs" + ::CodeStatistics::TEST_TYPES << "Helper specs" + ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/} + end + + namespace :db do + namespace :fixtures do + desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y" + task :load => :environment do + require 'active_record/fixtures' + ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym) + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*')) + end + end + end + end +end + +desc 'Generate documentation for the twitter extension.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'TwitterExtension' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +# For extensions that are in transition +desc 'Test the twitter extension.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +# Load any custom rakefiles for extension +Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f } \ No newline at end of file diff --git a/app/views/admin/page/_twitter.html.haml b/app/views/admin/page/_twitter.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/db/migrate/001_add_twitter_notification_fields.rb b/db/migrate/001_add_twitter_notification_fields.rb new file mode 100644 index 0000000..e488614 --- /dev/null +++ b/db/migrate/001_add_twitter_notification_fields.rb @@ -0,0 +1,11 @@ +class AddTwitterNotificationFields < ActiveRecord::Migration + def self.up + add_column :pages, :notify_twitter_of_children, :boolean, :default => false + add_column :pages, :twitter_id, :string + end + + def self.down + remove_column :pages, :notify_twitter_of_children + remove_column :pages, :twitter_id + end +end \ No newline at end of file diff --git a/lib/tasks/twitter_extension_tasks.rake b/lib/tasks/twitter_extension_tasks.rake new file mode 100644 index 0000000..68f9a36 --- /dev/null +++ b/lib/tasks/twitter_extension_tasks.rake @@ -0,0 +1,28 @@ +namespace :radiant do + namespace :extensions do + namespace :twitter do + + desc "Runs the migration of the Twitter extension" + task :migrate => :environment do + require 'radiant/extension_migrator' + if ENV["VERSION"] + TwitterExtension.migrator.migrate(ENV["VERSION"].to_i) + else + TwitterExtension.migrator.migrate + end + end + + desc "Copies public assets of the Twitter to the instance public/ directory." + task :update => :environment do + is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) } + puts "Copying assets from TwitterExtension" + Dir[TwitterExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file| + path = file.sub(TwitterExtension.root, '') + directory = File.dirname(path) + mkdir_p RAILS_ROOT + directory + cp file, RAILS_ROOT + path + end + end + end + end +end diff --git a/lib/twitter_notification.rb b/lib/twitter_notification.rb new file mode 100644 index 0000000..5e5fe44 --- /dev/null +++ b/lib/twitter_notification.rb @@ -0,0 +1,40 @@ +require 'twitter' +module TwitterNotification + def self.included(base) + base.class_eval { + after_save :notify_twitter + } + end + + def notify_twitter + if published? && twitter_configured? && parent.notify_twitter_of_children? && !self.twitter_id + title_length = 160 - absolute_url.length - 2 + message_title = title.length > title_length ? (title[0..title_length-4] + "...") : title + message = "#{message_title}: #{absolute_url}" + begin + status = Twitter::Base.new(config['twitter.username'], config['twitter.password']).update(message, :source => "Radiant Twitter Notifier") + # Don't trigger save callbacks + self.class.update_all(:twitter_id => status.id, :id => self.id) + rescue Exception => e + # Twitter failed... just log for now + logger.error "Twitter Notification failure: #{e.inspect}" + end + end + end + + def absolute_url + if config['twitter.url_host'] =~ /^http/ + "#{config['twitter.url_host']}#{self.url}" + else + "http://#{config['twitter.url_host']}#{self.url}" + end + end + + def twitter_configured? + !%w(twitter.username twitter.password twitter.url_host).any? {|k| config[k].blank? } + end + + def config + Radiant::Config + end +end \ No newline at end of file diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 0000000..d8c8db5 --- /dev/null +++ b/spec/spec.opts @@ -0,0 +1,6 @@ +--colour +--format +progress +--loadby +mtime +--reverse diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..67b3694 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,37 @@ +unless defined? RADIANT_ROOT + ENV["RAILS_ENV"] = "test" + case + when ENV["RADIANT_ENV_FILE"] + require ENV["RADIANT_ENV_FILE"] + when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions} + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment" + else + require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment" + end +end +require "#{RADIANT_ROOT}/spec/spec_helper" + +if File.directory?(File.dirname(__FILE__) + "/scenarios") + Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios" +end +if File.directory?(File.dirname(__FILE__) + "/matchers") + Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file } +end + +Spec::Runner.configure do |config| + # config.use_transactional_fixtures = true + # config.use_instantiated_fixtures = false + # config.fixture_path = RAILS_ROOT + '/spec/fixtures' + + # You can declare fixtures for each behaviour like this: + # describe "...." do + # fixtures :table_a, :table_b + # + # Alternatively, if you prefer to declare them only once, you can + # do so here, like so ... + # + # config.global_fixtures = :table_a, :table_b + # + # If you declare global fixtures, be aware that they will be declared + # for all of your examples, even those that don't use them. +end \ No newline at end of file diff --git a/twitter_extension.rb b/twitter_extension.rb new file mode 100644 index 0000000..058fc0d --- /dev/null +++ b/twitter_extension.rb @@ -0,0 +1,18 @@ +# Uncomment this if you reference any of your controllers in activate +# require_dependency 'application' +class TwitterExtension < Radiant::Extension + version "1.0" + description "Posts notification of pages to Twitter." + url "http://github.com/seancribbs/radiant-twitter-extension" + + define_routes do |map| + map.connect '/admin/twitter', :controller => "twitter", :action => "edit", :method => :get + map.connect '/admin/twitter', :controller => "twitter", :action => "update", :method => :put + end + + def activate + admin.tabs.add "Twitter", "/admin/twitter", :after => "Layouts", :visibility => [:all] + admin.page.edit.add :extended_metadata, "twitter" + Page.class_eval { include TwitterNotification } + end +end \ No newline at end of file