require 'active_resource' require 'tmpdir' require 'fileutils' require 'rake' module Registry class Extension < ActiveResource::Base self.site = ENV['REGISTRY_URL'] || "http://ext.radiantcms.org/" def install Registry.const_get(install_type).new(self).install end def uninstall Uninstaller.new(self).uninstall end def inspect %{ Name: #{name} Description: #{description} Author: #{author.name} <#{author.email}> Source code: #{repository_url} Download: #{download_url} Install type: #{install_type} Supports: Radiant #{supports_radiant_version} }.strip end end class Action def rake(command) puts "rake #{command}" puts `rake #{command} RAILS_ENV=#{RAILS_ENV}` if tasks_include? command end def tasks_include?(command) command = command.split(':') if command.length > 1 && command[0..1] == ['radiant','extensions'] extension = command[2] task = "radiant:extensions:#{extension}:#{command[3].split[0]}" else extension = task = command[0] end rake_file = File.join(RAILS_ROOT, 'vendor', 'extensions', extension) + '/lib/tasks/' + extension + '_extension_tasks.rake' load rake_file if File.exist? rake_file tasks = Rake.application.tasks.map(&:name) tasks.include? task end def file_utils FileUtils end delegate :cd, :cp_r, :rm_r, :to => :file_utils end class Installer < Action attr_accessor :url, :path, :name def initialize(url, name) self.url, self.name = url, name end def install copy_to_vendor_extensions migrate update end def copy_to_vendor_extensions cp_r(self.path, File.expand_path(File.join(RAILS_ROOT, 'vendor', 'extensions', name))) rm_r(self.path) end def migrate rake "radiant:extensions:#{name}:migrate" end def update rake "radiant:extensions:#{name}:update" end end class Uninstaller < Action attr_accessor :name def initialize(extension) self.name = extension.name end def uninstall migrate_down remove_extension_directory end def migrate_down rake "radiant:extensions:#{name}:migrate VERSION=0" end def remove_extension_directory rm_r(File.join(RAILS_ROOT, 'vendor', 'extensions', name)) end end class Checkout < Installer def initialize(extension) super(extension.repository_url, extension.name) end def checkout_command raise "Not Implemented!" end def install checkout super end def checkout self.path = File.join(Dir.tmpdir, name) cd(Dir.tmpdir) { system "#{checkout_command}" } end end class Download < Installer def initialize(extension) super(extension.download_url, extension.name) end def install download unpack super end def unpack raise "Not Implemented!" end def filename File.basename(self.url) end def download require 'open-uri' File.open(File.join(Dir.tmpdir, self.filename), 'w') {|f| f.write open(self.url).read } end end class Git < Checkout def project_in_git? @in_git ||= File.directory?(".git") end def checkout_command "git clone #{url} #{name}" end def checkout if project_in_git? system "git submodule add #{url} vendor/extensions/#{name}" cd(File.join('vendor', 'extensions', name)) do system "git submodule init && git submodule update" end else super cd(path) do system "git submodule init && git submodule update" end end end def copy_to_vendor_extensions super unless project_in_git? end end class Subversion < Checkout def checkout_command "svn checkout #{url} #{name}" end end class Gem < Download def gem_name(name) name.gsub(/-\d+\.\d+\.\d+(.+)?\.gem/, '') end def download # Don't download the gem if it's already installed extension = gem_name(filename) begin gem extension rescue ::Gem::LoadError super `gem install #{extension}` end end def unpack output = nil cd(Dir.tmpdir) do output = `gem unpack #{gem_name(filename)}` end self.path = output.match(/'(.*)'/)[1] end end class Tarball < Download def filename "#{self.name}.tar" end def unpack output = nil cd(Dir.tmpdir) { output = `tar xvf #{filename}` } self.path = File.join(Dir.tmpdir, output.split(/\n/).first.split('/').first) end end class Gzip < Tarball def filename @unpacked ? super : "#{self.name}.tar.gz" end def unpack cd(Dir.tmpdir) { system "gunzip #{self.filename}" } @unpacked = true super end end class Bzip2 < Tarball def filename @unpacked ? super : "#{self.name}.tar.bz2" end def unpack cd(Dir.tmpdir) { system "bunzip2 #{self.filename}" } @unpacked = true super end end class Zip < Download def unpack output = nil cd(Dir.tmpdir) { output = `unzip #{filename} -d #{name}` } self.path = File.join(Dir.tmpdir, name) end end end module Radiant class Extension module Script class << self def execute(args) command = args.shift || 'help' begin const_get(command.camelize).new(args) rescue ArgumentError => e puts e.message Help.new [command] end end end module Util attr_accessor :extension_name, :extension def to_extension_name(string) string.to_s.underscore end def installed? path_match = Regexp.compile("(^|/|\\\\)#{extension_name}$") extension_paths.any? {|p| p =~ path_match } end def registered? self.extension end def extension_paths paths = [RAILS_ROOT, RADIANT_ROOT].uniq.map { |p| Dir["#{p}/vendor/extensions/*"] } paths.unshift Dir["#{RADIANT_ROOT}/test/fixtures/extensions/*"] if RAILS_ENV == 'test' #nasty paths.flatten end def load_extensions Registry::Extension.find(:all) end def find_extension self.extension = load_extensions.find{|e| e.name == self.extension_name } end end class Install include Util def initialize(args=[]) raise ArgumentError, "You must specify an extension to install." if args.blank? self.extension_name = to_extension_name(args.shift) if installed? puts "#{extension_name} is already installed." else find_extension if registered? extension.install else raise ArgumentError, "#{extension_name} is not available in the registry." end end end end class Uninstall include Util def initialize(args=[]) raise ArgumentError, "You must specify an extension to uninstall." if args.blank? self.extension_name = to_extension_name(args.shift) if installed? find_extension && extension.uninstall else puts "#{extension_name} is not installed." end end end class Info include Util def initialize(args=[]) raise ArgumentError, "You must specify an extension to get info on" if args.blank? self.extension_name = to_extension_name(args.shift) find_extension and puts extension.inspect end end class Help def initialize(args=[]) command = args.shift command = 'help' unless self.class.instance_methods(false).collect {|im| im.to_s}.include?(command.to_s) send(command) end def help $stdout.puts %{Usage: script/extension command [arguments] Available commands: #{command_names} For help on an individual command: script/extension help command You may install extensions from another registry by setting the REGISTRY_URL By default the REGISTRY_URL is set to http://ext.radiantcms.org Code for the registry application may be found at: http://github.com/radiant/radiant-extension-registry/ } end def install $stdout.puts %{Usage: script/extension install extension_name Installs an extension from information in the global registry. } end def uninstall $stdout.puts %{Usage: script/extension uninstall extension_name Removes a previously installed extension from the current project. } end def info $stdout.puts %{Usage: script/extension info extension_name Displays registry information about the extension. } end private def command_names (Radiant::Extension::Script.constants - ['Util']).sort.map {|n| n.to_s.underscore }.join(", ") end end end end end