add coffeescript

This commit is contained in:
John Bintz 2011-05-08 22:44:21 -04:00
parent 3b1f15f9ab
commit a37de3c20c
15 changed files with 323 additions and 19 deletions

View File

@ -7,11 +7,28 @@ require 'thor/group'
class BackboneGenerator < Thor
include Thor::Actions
class_option :coffee, :type => :boolean, :desc => 'Generate CoffeeScript instead of JavaScript'
def self.source_root
File.expand_path('../../templates', __FILE__)
end
no_tasks do
def extension
options[:coffee] ? 'coffee' : 'js'
end
def script_target
options[:coffee] ? 'app/coffeescripts' : 'public/javascripts'
end
def view_target
'app/views'
end
def test_target
'spec/javascripts'
end
def underscore_name
singularize(Thor::Util.snake_case(@name.gsub("::", "/")))
end
@ -29,25 +46,25 @@ class BackboneGenerator < Thor
end
def generate_model
template('model.js.erb', "public/javascripts/models/#{underscore_name}.js")
template('model_spec.js.erb', "spec/javascripts/models/#{underscore_name}_spec.js")
template("model.#{extension}.erb", File.join(script_target, "models", "#{underscore_name}.#{extension}"))
template("model_spec.#{extension}.erb", File.join(test_target, "models", "#{underscore_name}_spec.#{extension}"))
end
def generate_view
template('view.js.erb', "public/javascripts/views/#{underscore_name}_view.js")
template('view.jst.erb', "app/views/#{underscore_name}s/view.jst")
template('view_spec.js.erb', "spec/javascripts/views/#{underscore_name}_view_spec.js")
template("view.#{extension}.erb", File.join(script_target, "views", "#{underscore_name}_view.#{extension}"))
template("view.jst.erb", File.join(view_target, "#{underscore_name}s/view.jst"))
template("view_spec.#{extension}.erb", File.join(test_target, "views", "#{underscore_name}_view_spec.#{extension}"))
end
def generate_collection
template('collection.js.erb', "public/javascripts/collections/#{plural_underscore_name}.js")
template('collection_spec.js.erb', "spec/javascripts/collections/#{plural_underscore_name}_spec.js")
template("collection.#{extension}.erb", File.join(script_target, "collections", "#{plural_underscore_name}.#{extension}"))
template("collection_spec.#{extension}.erb", File.join(test_target, "collections", "#{plural_underscore_name}_spec.#{extension}"))
end
def generate_collection_view
template('collection_view.js.erb', "public/javascripts/views/#{plural_underscore_name}_view.js")
template('collection_view.jst.erb', "app/views/#{plural_underscore_name}/list.jst")
template('collection_view_spec.js.erb', "spec/javascripts/views/#{plural_underscore_name}_view_spec.js")
template("collection_view.#{extension}.erb", File.join(script_target, 'views', "#{plural_underscore_name}_view.#{extension}"))
template("collection_view.jst.erb", File.join(view_target, "#{plural_underscore_name}/list.jst"))
template("collection_view_spec.#{extension}.erb", File.join(test_target, "views", "#{plural_underscore_name}_view_spec.#{extension}"))
end
end
@ -83,20 +100,20 @@ class BackboneGenerator < Thor
desc 'spec-helper', "Generate a spec helper for Backbone things"
def spec_helper
template('spec_helper.js.erb', 'spec/javascripts/helpers/backbone_spec_helper.js')
template("spec_helper.#{extension}.erb", File.join(test_target, "helpers", "backbone_spec_helper.#{extension}"))
end
desc 'app-helper', "Generate an application helper for useful Backbone things"
desc "app-helper", "Generate an application helper for useful Backbone things"
def app_helper
template('app_helper.js.erb', 'public/javascripts/applications/backbone_helper.js')
template("app_helper.#{extension}.erb", File.join(script_target, "applications", "backbone_helper.#{extension}"))
end
desc 'app-scaffold', "Generate an application scaffold"
desc "app-scaffold", "Generate an application scaffold"
def app_scaffold
template('app_view.js.erb', 'public/javascripts/application/app_view.js')
template('app_view.jst.erb', 'app/views/application/app_view.jst')
template('controller.js.erb', 'public/javascripts/application/controller.js')
template('app_view_spec.js.erb', 'spec/javascripts/application/app_view_spec.js')
template("app_view.#{extension}.erb", File.join(script_target, "application", "app_view.#{extension}"))
template("app_view.jst.erb", File.join(view_target, "application", "app_view.jst"))
template("controller.#{extension}.erb", File.join(script_target, "application", "controller.#{extension}"))
template("app_view_spec.#{extension}.erb", File.join(test_target, "application", "app_view_spec.#{extension}"))
end
private

View File

@ -16,8 +16,116 @@ describe 'backbone-generator' do
after { clean! }
describe 'coffeescript' do
describe 'model' do
def should_generate_model
File.file?(model = 'app/coffeescripts/models/section/model.coffee').should be_true
File.file?(spec = 'spec/javascripts/models/section/model_spec.coffee').should be_true
File.read(model).should match(/SectionModel/)
File.read(spec).should match(/SectionModel/)
end
def should_generate_view
File.file?(view = 'app/coffeescripts/views/section/model_view.coffee').should be_true
File.file?(spec = 'spec/javascripts/views/section/model_view_spec.coffee').should be_true
File.file?(template = 'app/views/section/models/view.jst').should be_true
File.read(view).should match(/SectionModel/)
File.read(view).should match(%r{template: JST\['section/models/view'\]})
File.read(spec).should match(/SectionModel/)
end
def should_generate_collection
File.file?(collection = 'app/coffeescripts/collections/section/models.coffee').should be_true
File.file?(spec = 'spec/javascripts/collections/section/models_spec.coffee').should be_true
File.read(collection).should match(/SectionModels/)
File.read(collection).should_not match(/SectionModelss/)
File.read(collection).should match(%r{section/model})
File.read(spec).should match(/SectionModels/)
File.read(spec).should_not match(/SectionModelss/)
end
def should_generate_collection_view
File.file?(view = 'app/coffeescripts/views/section/models_view.coffee').should be_true
File.file?(spec = 'spec/javascripts/views/section/models_view_spec.coffee').should be_true
File.file?(template = 'app/views/section/models/list.jst').should be_true
File.read(view).should match(/SectionModelsView/)
File.read(view).should_not match(/SectionModelssView/)
File.read(view).should match(/SectionModelView/)
File.read(view).should match(%r{template: JST\['section/models/list'\]})
File.read(spec).should match(/SectionModelsView/)
File.read(spec).should_not match(/SectionModelssView/)
end
describe 'model' do
it "should generate the model files" do
run "model", "Section::Model", '--coffee'
should_generate_model
end
end
describe 'view' do
it "should generate the model files" do
run "view", "Section::Model", '--coffee'
should_generate_view
end
end
describe 'collection view' do
it "should generate the collection view files" do
run "collection-view", "Section::Model", '--coffee'
should_generate_collection_view
end
end
describe 'collection' do
it "should generate the collection files" do
run "collection", "Section::Model", '--coffee'
should_generate_collection
end
end
describe 'scaffold' do
it "should generate everything!" do
run "scaffold", "Section::Model", '--coffee'
should_generate_model
should_generate_view
should_generate_collection
should_generate_collection_view
end
end
describe 'spec helper' do
it "should generate a spec helper" do
run "spec-helper", '--coffee'
File.file?(collection = 'spec/javascripts/helpers/backbone_spec_helper.coffee').should be_true
end
end
describe 'app helper' do
it "should generate an app helper" do
run "app-helper", '--coffee'
File.file?(collection = 'app/coffeescripts/applications/backbone_helper.coffee').should be_true
end
end
describe 'application scaffold' do
it "should generate an application scaffold" do
run "app-scaffold", '--coffee'
File.file?(app = 'app/coffeescripts/application/app_view.coffee').should be_true
File.file?(app_view = 'app/views/application/app_view.jst').should be_true
File.file?(controller = 'app/coffeescripts/application/controller.coffee').should be_true
File.file?(spec = 'spec/javascripts/application/app_view_spec.coffee').should be_true
end
end
end

View File

@ -0,0 +1,18 @@
Backbone.Collection.prototype.ensureFetched = (callback) ->
if (@_alreadyEnsureFetched)
_refresher =>
this.unbind('refresh', _refresher)
callback.apply(this)
@_alreadyEnsureFetched = true
@bind('refresh', _refresher)
@fetch()
else
callback.apply(this)
Backbone.View.prototype.attributes = ->
attrs = {}
for field in @attributeFields
do (field) =>
attrs[field] = this.$("input[name='#{field}']").val()
attrs

View File

@ -0,0 +1,10 @@
class window.AppView extends Backbone.View
el: '#application'
template: JST['application/app_view']
initialize: ->
controller = new Controller({app: this})
Backbone.history.start()
render: =>
$(@el).html(@template())
this

View File

@ -0,0 +1,9 @@
describe 'AppView', ->
appView = null
beforeEach, ->
appView = new AppView()
it 'should render', ->
expect($(appView.render().el)).toContain('.something')

View File

@ -0,0 +1,4 @@
class window.<%= plural_object_name %> extends Backbone.Collection
url: '/<%= plural_underscore_name %>'
model: <%= object_name %>

View File

@ -0,0 +1,7 @@
describe '<%= plural_object_name %>', ->
collection = null
withServer()
it 'should fetch records from the API', ->

View File

@ -0,0 +1,23 @@
class window.<%= plural_object_name %>View extends Backbone.View
events: {
'click button.new': 'addNew'
}
template: JST['<%= plural_underscore_name %>/list']
initialize: ->
@collection.bind('refresh', @addAll)
@render
@collection.fetch
render: =>
$(this.el).html(@template())
this
addOne: (model) =>
view = new <%= object_name %>View({model: model})
this.$('.list').append(view.render().el)
addAll: =>
@collection.each(@addOne)
addNew: =>
facility = new <%= object_name %>()
@collection.add(facility)
@addOne(facility)

View File

@ -0,0 +1,19 @@
describe '<%= plural_object_name %>View', ->
view = collection = null
beforeEach ->
collection = new <%= plural_object_name %>()
it 'should render', ->
view = new <%= plural_object_name %>View({collection: collectioon})
view.render
expect($(view.el)).toContain('.list')
expect($(view.el)).toContail('button.new')
it 'should add a new model when new is clicked', ->
view.$('button.new').trigger('click')
expect(view.$('.list').toContain('.<%= underscore_name %>')
expect(collection.length).toEqual(1)

View File

@ -0,0 +1,2 @@
class window.Controller extends Backbone.Controller
routes: {}

View File

@ -0,0 +1 @@
class window.<%= object_name %> extends Backbone.Model

View File

@ -0,0 +1,8 @@
describe '<%= object_name %>', ->
model = null
it 'should have some tests', ->
model = new <%= object_name %>()
expect(true).toEqual(false)

View File

@ -0,0 +1,14 @@
window.withServer ->
jasmine.getEnv().withServer()
jasmine.Env.prototype.withServer ->
@currentSuite.beforeEach ->
@server = sinon.fakeServer.create()
@currentSuite.afterEach ->
@server.restore()
beforeEach ->
@validJSONResponse = (data) ->
[ 200, { 'Content-type': 'application/json' }, JSON.stringify(data) ]

22
templates/view.coffee.erb Normal file
View File

@ -0,0 +1,22 @@
class window.<%= object_name %>View extends Backbone.View
events: {
'click button.save': 'save',
'click button.delete': 'destroy'
}
attributeFields: []
template: JST['<%= underscore_name %>s/view']
className: '<%= underscore_name %>'
initialize: ->
@model.bind('change', @render)
@model.bind('remove', @remove)
@model.view = this
render: =>
$(@el).html(@template(@model.toJSON))
this.$('button.save').text(@model.isNew() ? 'Create' : 'Update')
this
save: =>
@model.save(@attributes)
destroy: =>
if confirm("Are you sure?")
@model.destroy

View File

@ -0,0 +1,42 @@
describe '<%= object_name %>View', ->
view = model = null
describe 'new record', ->
beforeEach ->
model = new <%= object_name %>
it 'should render with a create button', ->
view = new <%= object_name %>View({model: model})
view.render
expect(view.$('button.save')).toHaveText('Create')
describe 'existing record', ->
beforeEach ->
model = new <%= object_name %>({id: 1})
setFixtures('<div id="container" />')
view = new <%= object_name %>View({model: model})
it 'should render with an update button', ->
view.render
expect(view.$('button.save')).toHaveText('Update')
it 'should destroy the model', ->
spyOn(window, 'confirm').andReturn(true)
spyOn(model, 'destory')
$('#container').append(view.el)
view.$('button.delete').trigger('click')
expect(model.destroy).toHaveBeenCalled
expect(window.confirm).toHaveBeenCalled
it 'should remove the view when the model is destroyed', ->
$('#container').append(view.render().el)
expect($('button.save')).toExist
model.trigger('remove')
expect($('button.save')).not.toExist