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