lib/xmvc/vendor.rb in xmvc-0.1.8 vs lib/xmvc/vendor.rb in xmvc-0.1.9

- old
+ new

@@ -1,21 +1,184 @@ -$LOAD_PATH.unshift(File.dirname(__FILE__)) - module Xmvc - + + ## + # A base thor-extension for which to build vendors/plugins for the Xmvc framework + # Extensions must impmlement several methods including :install and :secretary. + # Vendors are run in a root-dir named after themselves through the Xmvc::Vendor class-method + # #vendor_name. A Vendor must at least provide a file named 'vendor.yml' in the root of its + # template namespace + # this file is used to define asset and meta-data + # + # name: some-vendor + # javascripts: + # - App.js + # - app/view/**/*.js + # - app/model/*.js + # - app/controller/*.js + # + class Vendor < Thor - autoload :Plugin, 'vendor/plugin' + include Thor::Actions - #CONFIG_PATH = File.join(ROOT, "vendor.yml") - #ROOT = File.dirname(__FILE__) - #class << self - # def config(root) - # @config ||= YAML.load(File.read(File.join(root, "vendor.yml", ))).to_mash - # end - #end + class Error < StandardError + end + + class SpecNotFound < Error + end - def initialize(args, options, config) - puts "options: #{options}, config: #{config}" - say_status("Xmvc::Vendor initialized, load teh config file") + ## + # all vendors must implement vendor.yml in the same directory their extension lives + # + CONFIG_FILENAME = "vendor.yml" + + attr_accessor :config + attr_accessor :js + attr_accessor :css + + class << self + + ## + # get / set the vendor name + # + def vendor_name(name=nil) + @name ||= name + end + + ## + # Install a vendor + # @return {Xmvc::Vendor} + # + def install(options) + unless vendor_name + raise Vendor::Error.new("Vendors must specify a name using the class-method vendor_name") + end + + prepare_vendor(options) do |vendor| + vendor.say_status("install", vendor.class.to_s) + vendor.invoke(:install) + begin + vendor.copy_file(CONFIG_FILENAME, CONFIG_FILENAME) unless File.exists? CONFIG_FILENAME + rescue StandardError => e + raise SpecNotFound.new("Vendors must have available in their source-directory, a file named 'vendor.yml' or the class-method #source_root defined, which provides the base-path to the location of vendor.yml. Xmvc::Vendor attempted to automatically copy this file into the installation directory.") + end + end + end + + ## + # Loads and configures an Xmvc::Vendor instance + # @return {Xmvc::Vendor} + # + def load(options) + prepare_vendor(options) + end + + ## + # @param {Xmvc::Vendor} vendor + # @param {Symbol} environment + # @param {Symbol} ext File-extension of requested resource, :js, :css, etc + # @param {String} host + # + def asset_urls(vendor, environment = :development, ext = :js, host=nil) + config = vendor.config + host = config['host'] unless host + type = Xmvc.asset_dir(ext) + rs = [] + # Note, we check config['host'] here, not host as defined above, since cachefly urls + # override host requested as function param. Host as in http://extjs.cachefly.net/... + if config['host'].include?("http://") + config[type].each do |file| + rs << cachefly_url(environment, config['host'], config['name'], file, config['version'], ext) + end + elsif host != Xmvc::PUBLIC_PATH + rs << File.join("/#{host}", "#{config['name']}.#{ext.to_s}") + else + rs << Xmvc.build_path(environment, host, config['name'], config['version'], ext) + end + rs + end + + def bundle_js(vendor, sec, root=Xmvc::PUBLIC_PATH) + config = vendor.config + vendor.destination_root = root + path = "" + vendor.inside "javascripts" do + vendor.empty_directory(config['name']) unless File.exists? config['name'] + vendor.inside config['name'] do |dir| + path = File.expand_path(File.join(dir, Xmvc.build_name(vendor.options[:environment]||:development, config['name'], config['version'], :js))) + sec.concatenation.save_to(path) + end + end + path + end + + def bundle_css(vendor) + vendor.destination_root = Xmvc::PUBLIC_PATH + vendor.inside "stylesheets" do + Xmvc.ui.warn(' - bundle css -- no implementation') + end + end + + protected + + def cachefly_url(environment, host, name, file, version, ext) + version = (version.nil? or version.empty?) ? "" : "-#{version}" + File.join(host, "#{name}#{version}", "#{file}.#{ext.to_s}") + end + + def prepare_vendor(options) + vendor = new([], options) + vendor.inside(File.expand_path(options[:root])) do + vendor.inside vendor_name do |root| + vendor.destination_root = root + yield vendor if block_given? + configure(vendor) + end + end + vendor + end + + def defaults + @defaults ||= { + "name" => "", + "host" => "public", # <-- http://foo.cachefly.net, /sprockets, /public, (defaults to /public) + "version" => "", + "javascripts" => [], + "stylesheets" => [] + } + end + + def configure(vendor) + begin + vendor.config = defaults.merge(YAML.load_file(File.join(vendor.destination_root, CONFIG_FILENAME))) + + # Enforce config name to that defined in the class, ignoring that + # defined in YAML, which can't be trusted. #vendor_name in the class-extension is the only + # way to ID a vendor. + vendor.config.update("name" => vendor_name) + rescue StandardError => e + raise SpecNotFound.new("Tried to load vendor #{vendor_name} but found no #{CONFIG_FILENAME} in the directory #{vendor.destination_root}") + end + vendor + end + end # end Vendor class + + desc "secretary", "Return this vendor's Sprockets::Secretary" + def secretary(type = :js) + say_status "secretary", self.class.to_s + case type + when :js then @secretary ||= Sprockets::Secretary.new({ + :root => destination_root, + :source_files => config["javascripts"] + }) + end end + + desc "bundle", "Bundle a vendor's assets" + def bundle(sec = secretary(:js)) + # ugly hack for root in order to satisfy both Sprockts helper and CLI. + root = (self.class.vendor_name == :app) ? Xmvc::PUBLIC_PATH : "../#{Xmvc::PUBLIC_PATH}" + + self.class.bundle_js(self, sec, root) unless config['host'].include?('http://') + self.class.bundle_css(self) unless config['host'].include?('http://') + end end -end +end \ No newline at end of file