js extension mechanism for plugins, refs CNVS-5434
given app/coffeescripts/foo.coffee in canvas-lms, if you want to monkey patch it from your plugin, create app/coffeescripts/extensions/foo.coffee (in your plugin) like so: define -> (Foo) -> Foo::zomg = -> "i added this method" Foo and that's it, no changes required in canvas-lms, no plugin bundles, etc. note that Foo is not an explicit dependency, it magically figures it out. also note that your module should return a function that returns Foo. this function will magically wrap around Foo so you can do stuff to it anytime somebody requires "foo" as per usual. test plan: 1. use it as explained above 2. it should work Change-Id: If3b21782c0e79bb0ce55b4f16804047a2c2e2143 Reviewed-on: https://gerrit.instructure.com/20004 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com> Product-Review: Jon Jensen <jon@instructure.com> QA-Review: Jon Jensen <jon@instructure.com>
This commit is contained in:
parent
c8cd11388a
commit
e6b36d6817
|
@ -4,7 +4,9 @@ guard 'coffeescript', :input => 'app/coffeescripts', :output => 'public/javascr
|
|||
guard 'coffeescript', :input => 'spec/coffeescripts', :output => 'spec/javascripts'
|
||||
guard 'jst', :input => 'app/views/jst', :output => 'public/javascripts/jst'
|
||||
guard :styleguide
|
||||
guard :js_extensions
|
||||
|
||||
Dir[File.join(File.dirname(__FILE__),'vendor/plugins/*/Guardfile')].each do |g|
|
||||
eval(File.read(g))
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
require 'guard'
|
||||
require 'guard/guard'
|
||||
require 'lib/canvas/require_js/plugin_extension'
|
||||
|
||||
module Guard
|
||||
class JsExtensions < Guard
|
||||
def initialize(watchers = [], options = {})
|
||||
pattern = %r{vendor/plugins/[^/]+/app/coffeescripts/extensions/(.+\.coffee)$}
|
||||
super [::Guard::Watcher.new(pattern)], options
|
||||
end
|
||||
|
||||
def run_on_additions(paths)
|
||||
UI.info "Generating plugin extensions for #{paths.join(", ")}"
|
||||
paths.each do |path|
|
||||
path = path.gsub(%r{.*?/extensions/}, '')
|
||||
Canvas::RequireJs::PluginExtension.generate(path)
|
||||
end
|
||||
UI.info "Successfully generated plugin extensions for #{paths.join(", ")}"
|
||||
end
|
||||
|
||||
def run_all
|
||||
UI.info "Generating all plugin extensions"
|
||||
Canvas::RequireJs::PluginExtension.generate_all
|
||||
UI.info "Successfully generated all plugin extensions"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -56,7 +56,7 @@ module Canvas
|
|||
:jqueryui => 'vendor/jqueryui',
|
||||
:use => 'vendor/use',
|
||||
:uploadify => '../flash/uploadify/jquery.uploadify-3.1.min'
|
||||
}.update(plugin_paths).to_json.gsub(/([,{])/, "\\1\n ")
|
||||
}.update(plugin_paths).update(Canvas::RequireJs::PluginExtension.paths).to_json.gsub(/([,{])/, "\\1\n ")
|
||||
end
|
||||
|
||||
def plugin_paths
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
module Canvas
|
||||
module RequireJs
|
||||
module PluginExtension
|
||||
class << self
|
||||
# magical plugin js extension stuff
|
||||
#
|
||||
# given app/coffeescripts/foo.coffee in canvas-lms, if you want to
|
||||
# monkey patch it from your plugin, create
|
||||
# app/coffeescripts/extensions/foo.coffee (in your plugin) like so:
|
||||
#
|
||||
# define ->
|
||||
# (Foo) ->
|
||||
# Foo::zomg = -> "i added this method"
|
||||
# Foo
|
||||
#
|
||||
# and that's it, no changes required in canvas-lms, no plugin
|
||||
# bundles, etc.
|
||||
#
|
||||
# note that Foo is not an explicit dependency, it magically figures
|
||||
# it out. also note that your module should return a function that
|
||||
# accepts and returns Foo. this function will magically wrap around
|
||||
# Foo so you can do stuff to it anytime somebody requires "foo" as
|
||||
# per usual.
|
||||
|
||||
GLOB = "vendor/plugins/*/app/coffeescripts/extensions"
|
||||
REGEXP = Regexp.new GLOB.gsub('*', '([^/]*)')
|
||||
|
||||
# added to require config paths so that when you require an
|
||||
# extended module, you instead get the glue module which gives you
|
||||
# the original plus the extensions
|
||||
def paths
|
||||
map.keys.inject({}) { |hash, file|
|
||||
hash["compiled/#{file}_without_extensions"] = "compiled/#{file}"
|
||||
hash["compiled/#{file}"] = "compiled/#{file}_with_extensions"
|
||||
hash
|
||||
}
|
||||
end
|
||||
|
||||
def map
|
||||
@map ||= Dir["#{GLOB}/**/*.coffee"].inject({}) { |hash, ext|
|
||||
_, plugin, file = ext.match(%r{#{REGEXP}/(.*)\.coffee}).to_a
|
||||
hash[file] ||= []
|
||||
hash[file] << plugin
|
||||
hash
|
||||
}
|
||||
end
|
||||
|
||||
# given a file, which plugins extend it?
|
||||
def infer_extension_plugins(file)
|
||||
Dir[GLOB].map { |match|
|
||||
match.sub(REGEXP, '\1')
|
||||
}
|
||||
end
|
||||
|
||||
# create the glue module that requires the original and its
|
||||
# extensions. when your bundle (or whatever) requires the original
|
||||
# path, you'll get this module instead
|
||||
def generate(file, plugins = infer_extension_plugins(file))
|
||||
plugin_paths = plugins.map { |p| "#{p}/compiled/extensions/#{file}" }
|
||||
plugin_paths.unshift "compiled/#{file}_without_extensions"
|
||||
plugin_args = plugins.each_index.map { |i| "p#{i}" }
|
||||
plugin_calls = plugin_args.reverse.inject("orig"){ |s, a| "#{a}(#{s})" }
|
||||
FileUtils.mkdir_p(File.dirname("public/javascripts/compiled/#{file}"))
|
||||
File.open("public/javascripts/compiled/#{file}_with_extensions.js", "w") { |f|
|
||||
f.write <<-JS.gsub(/^ /, '')
|
||||
define(#{plugin_paths.inspect}, function(orig, #{plugin_args.join(', ')}) {
|
||||
return #{plugin_calls};
|
||||
});
|
||||
JS
|
||||
}
|
||||
end
|
||||
|
||||
def generate_all
|
||||
map.each { |file, plugin| generate file, plugin }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -46,7 +46,13 @@ namespace :js do
|
|||
end
|
||||
end
|
||||
|
||||
desc "generates compiled coffeescript and handlebars templates"
|
||||
desc "generates plugin extension modules"
|
||||
task :generate_extensions do
|
||||
require 'canvas/require_js/plugin_extension'
|
||||
Canvas::RequireJs::PluginExtension.generate_all
|
||||
end
|
||||
|
||||
desc "generates compiled coffeescript, handlebars templates and plugin extensions"
|
||||
task :generate do
|
||||
require 'config/initializers/plugin_symlinks'
|
||||
require 'fileutils'
|
||||
|
@ -63,6 +69,12 @@ namespace :js do
|
|||
FileUtils.rm_rf(paths_to_remove)
|
||||
|
||||
threads = []
|
||||
threads << Thread.new do
|
||||
puts "--> Generating plugin extensions"
|
||||
extensions_time = Benchmark.realtime { Rake::Task['js:generate_extensions'].invoke }
|
||||
puts "--> Generating plugin extensions finished in #{extensions_time}"
|
||||
end
|
||||
|
||||
threads << Thread.new do
|
||||
puts "--> Pre-compiling handlebars templates"
|
||||
handlebars_time = Benchmark.realtime { Rake::Task['jst:compile'].invoke }
|
||||
|
|
Loading…
Reference in New Issue