Skip to content

Commit

Permalink
Merge pull request #68 from zendesk/likun/commonjs_support
Browse files Browse the repository at this point in the history
APPS-1524 As an App developer I expect CommonJS Support in Apps
  • Loading branch information
tmcinerney committed May 9, 2014
2 parents 8499f0d + 36bbdb7 commit fd4b3ce
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 56 deletions.
46 changes: 28 additions & 18 deletions lib/zendesk_apps_support/assets/src.js.erb
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
(function() {
with( require('apps/framework/app_scope') ) {
with( ZendeskApps.AppScope.create() ) {
<% unless modules.nil? %>
require.modules = {
<% modules.each do |path, content| %>
'<%= path %>': function(exports, require, module) {
<%= content %>
},
<% end %>
eom: undefined
};
<% end %>

var source = <%= source %>;
var source = <%= source %>;

ZendeskApps[<%= name.to_json %>] = ZendeskApps.defineApp(source)
.reopenClass({ location: <%= location.to_json %> })
.reopen({
assetUrlPrefix: <%= asset_url_prefix.to_json %>,
appClassName: <%= app_class_name.to_json %>,
author: {
name: <%= author[:name].to_json %>,
email: <%= author[:email].to_json %>
},
translations: <%= translations.to_json %>,
templates: <%= templates.to_json %>,
frameworkVersion: <%= framework_version.to_json %>
});
var app = ZendeskApps.defineApp(source)
.reopenClass({ location: <%= location.to_json %> })
.reopen({
assetUrlPrefix: <%= asset_url_prefix.to_json %>,
appClassName: <%= app_class_name.to_json %>,
author: {
name: <%= author[:name].to_json %>,
email: <%= author[:email].to_json %>
},
translations: <%= translations.to_json %>,
templates: <%= templates.to_json %>,
frameworkVersion: <%= framework_version.to_json %>,
});

}

ZendeskApps[<%= name.to_json %>].install({"id": <%= app_id %>, "app_id": <%= app_id %>, "settings": <%= settings.to_json %>});
ZendeskApps[<%= name.to_json %>] = app;
}

ZendeskApps[<%= name.to_json %>].install({"id": <%= app_id %>, "app_id": <%= app_id %>, "settings": <%= settings.to_json %>});
}());

ZendeskApps.trigger && ZendeskApps.trigger('ready');
30 changes: 27 additions & 3 deletions lib/zendesk_apps_support/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ class Package
DEFAULT_SCSS = File.read(File.expand_path('../assets/default_styles.scss', __FILE__))
SRC_TEMPLATE = Erubis::Eruby.new( File.read(File.expand_path('../assets/src.js.erb', __FILE__)) )

attr_reader :root, :warnings
attr_reader :lib_root, :root, :warnings
attr_accessor :requirements_only

def initialize(dir)
@root = Pathname.new(File.expand_path(dir))
@lib_root = Pathname.new(File.join(@root, 'lib'))
@warnings = []
@requirements_only = false
end
Expand Down Expand Up @@ -46,10 +47,28 @@ def validate
end
end

def app_js
read_file("app.js")
end

def commonjs_modules
return unless has_lib_js?

lib_files.each_with_object({}) do |file, modules|
name = file.relative_path.gsub!(/^lib\//, '')
content = file.read
modules[name] = content
end
end

def files
non_tmp_files
end

def lib_files
@lib_files ||= files.select { |f| f =~ /^lib\/.*\.js$/ }
end

def template_files
files.select { |f| f =~ /^templates\/.*\.hdbs$/ }
end
Expand All @@ -76,7 +95,7 @@ def app_translations

def readified_js(app_name, app_id, asset_url_prefix, settings={})
manifest = manifest_json
source = read_file("app.js")
source = app_js
name = app_name || manifest[:name] || 'Local App'
location = manifest[:location]
app_class_name = "app-#{app_id}"
Expand All @@ -97,7 +116,8 @@ def readified_js(app_name, app_id, asset_url_prefix, settings={})
:framework_version => framework_version,
:templates => templates,
:settings => settings,
:app_id => app_id
:app_id => app_id,
:modules => commonjs_modules
)
end

Expand All @@ -110,6 +130,10 @@ def has_js?
file_exists?("app.js")
end

def has_lib_js?
lib_files.any?
end

def has_manifest?
file_exists?("manifest.json")
end
Expand Down
29 changes: 19 additions & 10 deletions lib/zendesk_apps_support/validations/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,38 @@ module Source

# predefined globals:
:predef => %w(_ console services helpers alert window document self
JSON Base64 clearInterval clearTimeout setInterval setTimeout)
JSON Base64 clearInterval clearTimeout setInterval setTimeout
require module exports top frames parent)
}.freeze

class <<self
def call(package)
source = package.files.find { |f| f.relative_path == 'app.js' }
app = package.files.find { |file| file.relative_path == 'app.js' }
files = package.lib_files << app

if package.requirements_only
return source ? [ ValidationError.new(:no_app_js_required) ] : []
return app ? [ ValidationError.new(:no_app_js_required) ] : []
end

return [ ValidationError.new(:missing_source) ] unless source
return [ ValidationError.new(:missing_source) ] unless app

jshint_errors = linter.lint(source.read)
if jshint_errors.any?
[ JSHintValidationError.new(source.relative_path, jshint_errors) ]
else
[]
end
jshint_errors(files).flatten!
end

private

def jshint_error(file)
errors = linter.lint(file.read)
[ JSHintValidationError.new(file.relative_path, errors) ] if errors.any?
end

def jshint_errors(files)
files.each_with_object([]) do |file, errors|
error = jshint_error(file)
errors << error unless error.nil?
end
end

def linter
Jshintrb::Lint.new(LINTER_OPTIONS)
end
Expand Down
3 changes: 3 additions & 0 deletions spec/app/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
(function() {

return {
a: require('a.js'),

events: {
'app.activated':'doSomething'
},

doSomething: function() {
console.log(a.name);
}
};

Expand Down
5 changes: 5 additions & 0 deletions spec/app/lib/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var a = {
name: 'This is A'
};

module.exports = a;
Empty file added spec/app/lib/a.txt
Empty file.
5 changes: 5 additions & 0 deletions spec/app/lib/nested/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var b = {
name: 'This is B'
};

module.exports = b;
6 changes: 6 additions & 0 deletions spec/invalid_app/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
h1 {
color: red;
span {
color: green;
}
}
13 changes: 13 additions & 0 deletions spec/invalid_app/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(function() {

return {
events: {
'app.activated':'doSomething'
},

doSomething: function() {
console.log("Invalid app");
}
};

}());
Binary file added spec/invalid_app/assets/logo-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added spec/invalid_app/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions spec/invalid_app/lib/invalid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var b = {}
module.exports = b;
11 changes: 11 additions & 0 deletions spec/invalid_app/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "ABC",
"author": {
"name": "John Smith",
"email": "[email protected]"
},
"defaultLocale": "en",
"private": true,
"location": "ticket_sidebar",
"frameworkVersion": "0.5"
}
11 changes: 11 additions & 0 deletions spec/invalid_app/templates/layout.hdbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<header>
<span class="logo"/>
<h3>{{setting "name"}}</h3>
</header>
<section data-main/>
<footer>
<a href="mailto:{{author.email}}">
{{author.name}}
</a>
</footer>
</div>
5 changes: 5 additions & 0 deletions spec/invalid_app/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"app": {
"name": "Buddha Machine"
}
}
80 changes: 59 additions & 21 deletions spec/package_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

describe 'files' do
it 'should return all the files within the app folder excluding files in tmp folder' do
@package.files.map(&:relative_path).should =~ %w(app.css app.js assets/logo-small.png assets/logo.png manifest.json templates/layout.hdbs translations/en.json)
@package.files.map(&:relative_path).should =~ %w(app.css app.js assets/logo-small.png assets/logo.png lib/a.js lib/a.txt lib/nested/b.js manifest.json templates/layout.hdbs translations/en.json)
end

it 'should error out when manifest is missing' do
Expand All @@ -30,6 +30,21 @@
end
end

describe 'lib_files' do
it 'should return all the javascript files in the lib folder within the app folder' do
@package.lib_files.map(&:relative_path).should == %w(lib/a.js lib/nested/b.js)
end
end

describe 'commonjs_modules' do
it 'should return an object with name value pairs containing the path and code' do
@package.commonjs_modules.should == {
"a.js"=>"var a = {\n name: 'This is A'\n};\n\nmodule.exports = a;\n",
"nested/b.js"=>"var b = {\n name: 'This is B'\n};\n\nmodule.exports = b;\n"
}
end
end

describe 'manifest_json' do
it 'should return manifest json' do
manifest = @package.manifest_json
Expand All @@ -46,42 +61,65 @@
describe 'readified_js' do
it 'should generate js ready for installation' do
js = @package.readified_js(nil, 0, 'http://localhost:4567/')

expected =<<HERE
(function() {
with( require('apps/framework/app_scope') ) {
with( ZendeskApps.AppScope.create() ) {
require.modules = {
'a.js': function(exports, require, module) {
var a = {
name: 'This is A'
};
module.exports = a;
var source = (function() {
},
'nested/b.js': function(exports, require, module) {
var b = {
name: 'This is B'
};
module.exports = b;
},
eom: undefined
};
var source = (function() {
return {
a: require('a.js'),
events: {
'app.activated':'doSomething'
},
doSomething: function() {
console.log(a.name);
}
};
}());
;
ZendeskApps["ABC"] = ZendeskApps.defineApp(source)
.reopenClass({ location: "ticket_sidebar" })
.reopen({
assetUrlPrefix: "http://localhost:4567/",
appClassName: "app-0",
author: {
name: "John Smith",
email: "[email protected]"
},
translations: {"app":{\"name\":\"Buddha Machine\"}},
templates: {"layout":"<style>\\n.app-0 header .logo {\\n background-image: url(\\"http://localhost:4567/logo-small.png\\"); }\\n.app-0 h1 {\\n color: red; }\\n .app-0 h1 span {\\n color: green; }\\n</style>\\n<header>\\n <span class=\\"logo\\"/>\\n <h3>{{setting \\"name\\"}}</h3>\\n</header>\\n<section data-main/>\\n<footer>\\n <a href=\\"mailto:{{author.email}}\\">\\n {{author.name}}\\n </a>\\n</footer>\\n</div>"},
frameworkVersion: "0.5"
});
}
ZendeskApps["ABC"].install({"id": 0, "app_id": 0, "settings": {\"title\":\"ABC\"}});
var app = ZendeskApps.defineApp(source)
.reopenClass({ location: "ticket_sidebar" })
.reopen({
assetUrlPrefix: "http://localhost:4567/",
appClassName: "app-0",
author: {
name: "John Smith",
email: "[email protected]"
},
translations: {"app":{"name":"Buddha Machine"}},
templates: {"layout":"<style>\\n.app-0 header .logo {\\n background-image: url(\\"http://localhost:4567/logo-small.png\\"); }\\n.app-0 h1 {\\n color: red; }\\n .app-0 h1 span {\\n color: green; }\\n</style>\\n<header>\\n <span class=\\"logo\\"/>\\n <h3>{{setting \\"name\\"}}</h3>\\n</header>\\n<section data-main/>\\n<footer>\\n <a href=\\"mailto:{{author.email}}\\">\\n {{author.name}}\\n </a>\\n</footer>\\n</div>"},
frameworkVersion: "0.5",
});
ZendeskApps["ABC"] = app;
}
ZendeskApps["ABC"].install({"id": 0, "app_id": 0, "settings": {"title":"ABC"}});
}());
ZendeskApps.trigger && ZendeskApps.trigger('ready');
Expand Down
Loading

0 comments on commit fd4b3ce

Please sign in to comment.