diff --git a/backbone-rails.gemspec b/backbone-rails.gemspec index ef9ceb5..4305f11 100644 --- a/backbone-rails.gemspec +++ b/backbone-rails.gemspec @@ -12,11 +12,11 @@ Gem::Specification.new do |s| s.files = Dir["lib/**/*"] + Dir["vendor/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"] s.add_dependency('railties', '>= 3.1.0') - s.add_dependency('coffee-script', '~> 2.2.0') s.add_dependency('jquery-rails', '~> 2.1.3') s.add_dependency('ejs', '~> 1.1.1') s.add_development_dependency('rails', '~> 3.2.0') + s.add_development_dependency('coffee-script', '~> 2.2.0') s.add_development_dependency('sqlite3') s.add_development_dependency('sass') s.add_development_dependency('uglifier') diff --git a/lib/backbone-rails.rb b/lib/backbone-rails.rb index a2b3121..24216fa 100644 --- a/lib/backbone-rails.rb +++ b/lib/backbone-rails.rb @@ -1,6 +1,42 @@ require 'rails' module BackboneRails + class << self + # Returns true if `BackboneRails` is set to produce coffeescript, + # false if it is set to produce javascript. + def coffeescript? + if @coffeescript.nil? + detect_script! + else + @coffeescript + end + end + + # Forces generators to produce coffeescript instead of javascript. + def coffeescript! + @coffeescript = true + end + + # Forces generators to produce javascript instead of coffeescript. + def javascript! + @coffeescript = false + end + + # Sets `BackboneRails` to produce CoffeeScript if the `coffee-script` + # gem is available, JavaScript otherwise. + def detect_script! + @coffeescript = coffeescript_available? + end + + # Returns true if the `coffee-script` gem is available. + def coffeescript_available? + defined?(CoffeeScript) || require('coffee-script') + true + rescue LoadError + false + end + end + class Engine < Rails::Engine end end diff --git a/lib/generators/backbone/install/install_generator.rb b/lib/generators/backbone/install/install_generator.rb index 96eef77..9952d65 100644 --- a/lib/generators/backbone/install/install_generator.rb +++ b/lib/generators/backbone/install/install_generator.rb @@ -26,7 +26,7 @@ def create_dir_layout end def create_app_file - template "app.coffee", "app/assets/javascripts/backbone/#{application_name.underscore}.js.coffee" + template "app.#{script_extension}", "app/assets/javascripts/backbone/#{application_name.underscore}.#{script_extension}" end end diff --git a/lib/generators/backbone/install/templates/app.js b/lib/generators/backbone/install/templates/app.js new file mode 100644 index 0000000..1f304fd --- /dev/null +++ b/lib/generators/backbone/install/templates/app.js @@ -0,0 +1,12 @@ +//= require_self +//= require_tree ./templates +//= require_tree ./models +//= require_tree ./views +//= require_tree ./routers + +window.<%= js_app_name %> = { + Models: {}, + Collections: {}, + Routers: {}, + Views: {} +}; diff --git a/lib/generators/backbone/install/templates/app.coffee b/lib/generators/backbone/install/templates/app.js.coffee similarity index 100% rename from lib/generators/backbone/install/templates/app.coffee rename to lib/generators/backbone/install/templates/app.js.coffee diff --git a/lib/generators/backbone/model/model_generator.rb b/lib/generators/backbone/model/model_generator.rb index ee1d487..0c707b7 100644 --- a/lib/generators/backbone/model/model_generator.rb +++ b/lib/generators/backbone/model/model_generator.rb @@ -11,7 +11,7 @@ class ModelGenerator < Rails::Generators::NamedBase argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" def create_backbone_model - template "model.coffee", "#{backbone_path}/models/#{file_name}.js.coffee" + template "model.#{script_extension}", "#{backbone_path}/models/#{file_name}.#{script_extension}" end end diff --git a/lib/generators/backbone/model/templates/model.js b/lib/generators/backbone/model/templates/model.js new file mode 100644 index 0000000..7a41514 --- /dev/null +++ b/lib/generators/backbone/model/templates/model.js @@ -0,0 +1,12 @@ +<%= model_namespace %> = Backbone.Model.extend({ + paramRoot: '<%= singular_table_name %>', + + defaults: { + <%= attributes.collect { |a| "#{a.name}: null" }.join(",\n ") %> + } +}); + +<%= collection_namespace %>Collection = Backbone.Collection.extend({ + model: <%= model_namespace %>, + url: '<%= route_url %>' +}); diff --git a/lib/generators/backbone/model/templates/model.coffee b/lib/generators/backbone/model/templates/model.js.coffee similarity index 100% rename from lib/generators/backbone/model/templates/model.coffee rename to lib/generators/backbone/model/templates/model.js.coffee diff --git a/lib/generators/backbone/resource_helpers.rb b/lib/generators/backbone/resource_helpers.rb index afddab7..95db957 100644 --- a/lib/generators/backbone/resource_helpers.rb +++ b/lib/generators/backbone/resource_helpers.rb @@ -1,6 +1,22 @@ module Backbone module Generators module ResourceHelpers + def self.included base + base.module_eval do + class_option :javascript, :aliases => '-J', + :type => :boolean, + :default => false, + :desc => "Generate JavaScript instead of CoffeeScript" + end + end + + def script_extension + if options['javascript'] || !BackboneRails.coffeescript? + "js" + else + "js.coffee" + end + end def backbone_path "app/assets/javascripts/backbone" diff --git a/lib/generators/backbone/router/router_generator.rb b/lib/generators/backbone/router/router_generator.rb index 2f5c045..3295096 100644 --- a/lib/generators/backbone/router/router_generator.rb +++ b/lib/generators/backbone/router/router_generator.rb @@ -25,16 +25,16 @@ def validate_no_reserved_words end def create_router_files - template 'router.coffee', File.join(backbone_path, "routers", class_path, "#{file_name}_router.js.coffee") + template "router.#{script_extension}", File.join(backbone_path, "routers", class_path, "#{file_name}_router.#{script_extension}") end def create_view_files actions.each do |action| @action = action - @view_path = File.join(backbone_path, "views", plural_name, "#{action}_view.js.coffee") + @view_path = File.join(backbone_path, "views", plural_name, "#{action}_view.#{script_extension}") @jst_path = File.join(backbone_path,"templates", plural_name, "#{action}.jst.ejs") - template "view.coffee", @view_path + template "view.#{script_extension}", @view_path template "template.jst", @jst_path end end diff --git a/lib/generators/backbone/router/templates/router.js b/lib/generators/backbone/router/templates/router.js new file mode 100644 index 0000000..668c810 --- /dev/null +++ b/lib/generators/backbone/router/templates/router.js @@ -0,0 +1,17 @@ +<%= router_namespace %>Router = Backbone.Router.extend({ + initialize: function(options) { + + }, + + routes: { + <%= actions.collect { |a| "\"#{a}\": \"#{a}\"" }.join(",\n ") %> + }, + +<% actions.each do |action| -%> + <%= action %>: function() { + this.view = new <%= "#{view_namespace}.#{action.camelize}View()" %>; + $("#<%= plural_name %>").html(this.view.render().el); + }, + +<% end -%> +}); diff --git a/lib/generators/backbone/router/templates/router.coffee b/lib/generators/backbone/router/templates/router.js.coffee similarity index 100% rename from lib/generators/backbone/router/templates/router.coffee rename to lib/generators/backbone/router/templates/router.js.coffee diff --git a/lib/generators/backbone/router/templates/view.js b/lib/generators/backbone/router/templates/view.js new file mode 100644 index 0000000..6db4468 --- /dev/null +++ b/lib/generators/backbone/router/templates/view.js @@ -0,0 +1,10 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.<%= @action.camelize %>View = Backbone.View.extend({ + template: JST["<%= jst @action %>"], + + render: function() { + this.$el.html(this.template()); + return this; + } +}); diff --git a/lib/generators/backbone/router/templates/view.coffee b/lib/generators/backbone/router/templates/view.js.coffee similarity index 100% rename from lib/generators/backbone/router/templates/view.coffee rename to lib/generators/backbone/router/templates/view.js.coffee diff --git a/lib/generators/backbone/scaffold/scaffold_generator.rb b/lib/generators/backbone/scaffold/scaffold_generator.rb index 4ea2adc..4220bdf 100644 --- a/lib/generators/backbone/scaffold/scaffold_generator.rb +++ b/lib/generators/backbone/scaffold/scaffold_generator.rb @@ -8,16 +8,16 @@ class ScaffoldGenerator < ModelGenerator desc "This generator creates the client side crud scaffolding" def create_router_files - template 'router.coffee', File.join(backbone_path, "routers", class_path, "#{plural_name}_router.js.coffee") + template "router.#{script_extension}", File.join(backbone_path, "routers", class_path, "#{plural_name}_router.#{script_extension}") end def create_view_files available_views.each do |view| - template "views/#{view}_view.coffee", File.join(backbone_path, "views", plural_name, "#{view}_view.js.coffee") + template "views/#{view}_view.#{script_extension}", File.join(backbone_path, "views", plural_name, "#{view}_view.#{script_extension}") template "templates/#{view}.jst", File.join(backbone_path, "templates", plural_name, "#{view}.jst.ejs") end - template "views/model_view.coffee", File.join(backbone_path, "views", plural_name, "#{singular_name}_view.js.coffee") + template "views/model_view.#{script_extension}", File.join(backbone_path, "views", plural_name, "#{singular_name}_view.#{script_extension}") template "templates/model.jst", File.join(backbone_path, "templates", plural_name, "#{singular_name}.jst.ejs") end diff --git a/lib/generators/backbone/scaffold/templates/model.js b/lib/generators/backbone/scaffold/templates/model.js new file mode 100644 index 0000000..7a41514 --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/model.js @@ -0,0 +1,12 @@ +<%= model_namespace %> = Backbone.Model.extend({ + paramRoot: '<%= singular_table_name %>', + + defaults: { + <%= attributes.collect { |a| "#{a.name}: null" }.join(",\n ") %> + } +}); + +<%= collection_namespace %>Collection = Backbone.Collection.extend({ + model: <%= model_namespace %>, + url: '<%= route_url %>' +}); diff --git a/lib/generators/backbone/scaffold/templates/model.coffee b/lib/generators/backbone/scaffold/templates/model.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/model.coffee rename to lib/generators/backbone/scaffold/templates/model.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/router.js b/lib/generators/backbone/scaffold/templates/router.js new file mode 100644 index 0000000..1586b07 --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/router.js @@ -0,0 +1,38 @@ +<%= router_namespace %>Router = Backbone.Router.extend({ + initialize: function(options) { + this.<%= plural_model_name %> = new <%= collection_namespace %>Collection(); + this.<%= plural_model_name %>.reset(options.<%= plural_model_name %>); + }, + + routes: { + "new" : "new<%= class_name %>", + "index" : "index", + ":id/edit" : "edit", + ":id" : "show", + ".*" : "index" + }, + + new<%= class_name %>: function() { + this.view = new <%= view_namespace %>.NewView({collection: this.<%= plural_name %>}); + $("#<%= plural_name %>").html(this.view.render().el); + }, + + index: function() { + this.view = new <%= view_namespace %>.IndexView({<%= plural_name %>: this.<%= plural_name %>}); + $("#<%= plural_name %>").html(this.view.render().el); + }, + + show: function(id) { + var <%= singular_name %> = this.<%= plural_name %>.get(id); + + this.view = new <%= view_namespace %>.ShowView({model: <%= singular_name %>}); + $("#<%= plural_name %>").html(this.view.render().el); + }, + + edit: function(id) { + var <%= singular_name %> = this.<%= plural_name %>.get(id); + + this.view = new <%= view_namespace %>.EditView({model: <%= singular_name %>}); + $("#<%= plural_name %>").html(this.view.render().el); + } +}); diff --git a/lib/generators/backbone/scaffold/templates/router.coffee b/lib/generators/backbone/scaffold/templates/router.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/router.coffee rename to lib/generators/backbone/scaffold/templates/router.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/views/edit_view.js b/lib/generators/backbone/scaffold/templates/views/edit_view.js new file mode 100644 index 0000000..ab395fe --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/views/edit_view.js @@ -0,0 +1,30 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.EditView = Backbone.View.extend({ + template: JST["<%= jst 'edit' %>"], + + events: { + "submit #edit-<%= singular_name %>": "update" + }, + + update: function(e) { + var self = this; + e.preventDefault(); + e.stopPropagation(); + + this.model.save(null, { + success: function(<%= singular_name %>) { + self.model = <%= singular_name %>; + window.location.hash = "/" + self.model.id; + } + }); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + + this.$("form").backboneLink(this.model); + + return this; + } +}); diff --git a/lib/generators/backbone/scaffold/templates/views/edit_view.coffee b/lib/generators/backbone/scaffold/templates/views/edit_view.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/views/edit_view.coffee rename to lib/generators/backbone/scaffold/templates/views/edit_view.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/views/index_view.js b/lib/generators/backbone/scaffold/templates/views/index_view.js new file mode 100644 index 0000000..858360c --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/views/index_view.js @@ -0,0 +1,28 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.IndexView = Backbone.View.extend({ + template: JST["<%= jst 'index' %>"], + + initialize: function() { + this.addAll = _.bind(this.addAll, this); + this.addOne = _.bind(this.addOne, this); + this.render = _.bind(this.render, this); + this.options.<%= plural_model_name %>.bind('reset', this.addAll); + }, + + addAll: function() { + this.options.<%= plural_model_name %>.each(this.addOne); + }, + + addOne: function(<%= singular_model_name %>) { + view = new <%= view_namespace %>.<%= singular_name.camelize %>View({model : <%= singular_model_name %>}); + this.$("tbody").append(view.render().el); + }, + + render: function() { + this.$el.html(this.template({<%= plural_model_name %>: this.options.<%= plural_model_name %>.toJSON() })); + this.addAll(); + + return this; + } +}); diff --git a/lib/generators/backbone/scaffold/templates/views/index_view.coffee b/lib/generators/backbone/scaffold/templates/views/index_view.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/views/index_view.coffee rename to lib/generators/backbone/scaffold/templates/views/index_view.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/views/model_view.js b/lib/generators/backbone/scaffold/templates/views/model_view.js new file mode 100644 index 0000000..7a4ea59 --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/views/model_view.js @@ -0,0 +1,23 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.<%= singular_name.camelize %>View = Backbone.View.extend({ + template: JST["<%= jst singular_name %>"], + + events: { + "click .destroy" : "destroy" + }, + + tagName: "tr", + + destroy: function() { + this.model.destroy(); + this.remove(); + + return false; + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + return this; + } +}); diff --git a/lib/generators/backbone/scaffold/templates/views/model_view.coffee b/lib/generators/backbone/scaffold/templates/views/model_view.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/views/model_view.coffee rename to lib/generators/backbone/scaffold/templates/views/model_view.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/views/new_view.js b/lib/generators/backbone/scaffold/templates/views/new_view.js new file mode 100644 index 0000000..d227510 --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/views/new_view.js @@ -0,0 +1,45 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.NewView = Backbone.View.extend({ + template: JST["<%= jst 'new' %>"], + + events: { + "submit #new-<%= singular_name %>": "save" + }, + + initialize: function() { + var self = this; + this.model = new this.collection.model(); + + this.model.bind("change:errors", function() { + self.render(); + }); + }, + + save: function(e) { + e.preventDefault(); + e.stopPropagation(); + + this.model.unset("errors"); + + var self = this; + this.collection.create(this.model.toJSON(), { + success: function(<%= singular_name %>) { + self.model = <%= singular_name %>; + window.location.hash = "/" + self.model.id; + }, + + error: function(<%= singular_name %>, jqXHR) { + self.model.set({errors: $.parseJSON(jqXHR.responseText)}); + } + }); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + + this.$("form").backboneLink(this.model); + + return this; + } +}); diff --git a/lib/generators/backbone/scaffold/templates/views/new_view.coffee b/lib/generators/backbone/scaffold/templates/views/new_view.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/views/new_view.coffee rename to lib/generators/backbone/scaffold/templates/views/new_view.js.coffee diff --git a/lib/generators/backbone/scaffold/templates/views/show_view.js b/lib/generators/backbone/scaffold/templates/views/show_view.js new file mode 100644 index 0000000..35c7c58 --- /dev/null +++ b/lib/generators/backbone/scaffold/templates/views/show_view.js @@ -0,0 +1,10 @@ +<%= view_namespace %> || (<%= view_namespace %> = {}); + +<%= view_namespace %>.ShowView = Backbone.View.extend({ + template: JST["<%= jst 'show' %>"], + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + return this; + } +}); diff --git a/lib/generators/backbone/scaffold/templates/views/show_view.coffee b/lib/generators/backbone/scaffold/templates/views/show_view.js.coffee similarity index 100% rename from lib/generators/backbone/scaffold/templates/views/show_view.coffee rename to lib/generators/backbone/scaffold/templates/views/show_view.js.coffee diff --git a/test/dummy/app/assets/javascripts/albums.js b/test/dummy/app/assets/javascripts/albums.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/test/dummy/app/assets/javascripts/albums.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/test/dummy/app/assets/javascripts/backbone/models/album.js b/test/dummy/app/assets/javascripts/backbone/models/album.js new file mode 100644 index 0000000..0675405 --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/models/album.js @@ -0,0 +1,13 @@ +Dummy.Models.Album = Backbone.Model.extend({ + paramRoot: 'album', + + defaults: { + title: null, + artist: null + } +}); + +Dummy.Collections.AlbumsCollection = Backbone.Collection.extend({ + model: Dummy.Models.Album, + url: '/albums' +}); diff --git a/test/dummy/app/assets/javascripts/backbone/routers/albums_router.js b/test/dummy/app/assets/javascripts/backbone/routers/albums_router.js new file mode 100644 index 0000000..03c5667 --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/routers/albums_router.js @@ -0,0 +1,38 @@ +Dummy.Routers.AlbumsRouter = Backbone.Router.extend({ + initialize: function(options) { + this.albums = new Dummy.Collections.AlbumsCollection(); + this.albums.reset(options.albums); + }, + + routes: { + "new" : "newAlbum", + "index" : "index", + ":id/edit" : "edit", + ":id" : "show", + ".*" : "index" + }, + + newAlbum: function() { + this.view = new Dummy.Views.Albums.NewView({collection: this.albums}); + $("#albums").html(this.view.render().el); + }, + + index: function() { + this.view = new Dummy.Views.Albums.IndexView({albums: this.albums}); + $("#albums").html(this.view.render().el); + }, + + show: function(id) { + var album = this.albums.get(id); + + this.view = new Dummy.Views.Albums.ShowView({model: album}); + $("#albums").html(this.view.render().el); + }, + + edit: function(id) { + var album = this.albums.get(id); + + this.view = new Dummy.Views.Albums.EditView({model: album}); + $("#albums").html(this.view.render().el); + } +}); diff --git a/test/dummy/app/assets/javascripts/backbone/templates/albums/album.jst.ejs b/test/dummy/app/assets/javascripts/backbone/templates/albums/album.jst.ejs new file mode 100644 index 0000000..9883a47 --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/templates/albums/album.jst.ejs @@ -0,0 +1,6 @@ +
Title | +Artist | ++ | + | + |
---|
+ Title: + <%= title %> +
+ ++ Artist: + <%= artist %> +
+ + +Back \ No newline at end of file diff --git a/test/dummy/app/assets/javascripts/backbone/views/albums/album_view.js b/test/dummy/app/assets/javascripts/backbone/views/albums/album_view.js new file mode 100644 index 0000000..27ef929 --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/views/albums/album_view.js @@ -0,0 +1,23 @@ +Dummy.Views.Albums || (Dummy.Views.Albums = {}); + +Dummy.Views.Albums.AlbumView = Backbone.View.extend({ + template: JST["backbone/templates/albums/album"], + + events: { + "click .destroy" : "destroy" + }, + + tagName: "tr", + + destroy: function() { + this.model.destroy(); + this.remove(); + + return false; + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + return this; + } +}); diff --git a/test/dummy/app/assets/javascripts/backbone/views/albums/edit_view.js b/test/dummy/app/assets/javascripts/backbone/views/albums/edit_view.js new file mode 100644 index 0000000..fe2923c --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/views/albums/edit_view.js @@ -0,0 +1,30 @@ +Dummy.Views.Albums || (Dummy.Views.Albums = {}); + +Dummy.Views.Albums.EditView = Backbone.View.extend({ + template: JST["backbone/templates/albums/edit"], + + events: { + "submit #edit-album": "update" + }, + + update: function(e) { + var self = this; + e.preventDefault(); + e.stopPropagation(); + + this.model.save(null, { + success: function(album) { + self.model = album; + window.location.hash = "/" + self.model.id; + } + }); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + + this.$("form").backboneLink(this.model); + + return this; + } +}); diff --git a/test/dummy/app/assets/javascripts/backbone/views/albums/index_view.js b/test/dummy/app/assets/javascripts/backbone/views/albums/index_view.js new file mode 100644 index 0000000..c83ac3e --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/views/albums/index_view.js @@ -0,0 +1,28 @@ +Dummy.Views.Albums || (Dummy.Views.Albums = {}); + +Dummy.Views.Albums.IndexView = Backbone.View.extend({ + template: JST["backbone/templates/albums/index"], + + initialize: function() { + this.addAll = _.bind(this.addAll, this); + this.addOne = _.bind(this.addOne, this); + this.render = _.bind(this.render, this); + this.options.albums.bind('reset', this.addAll); + }, + + addAll: function() { + this.options.albums.each(this.addOne); + }, + + addOne: function(album) { + view = new Dummy.Views.Albums.AlbumView({model : album}); + this.$("tbody").append(view.render().el); + }, + + render: function() { + this.$el.html(this.template({albums: this.options.albums.toJSON() })); + this.addAll(); + + return this; + } +}); diff --git a/test/dummy/app/assets/javascripts/backbone/views/albums/new_view.js b/test/dummy/app/assets/javascripts/backbone/views/albums/new_view.js new file mode 100644 index 0000000..ff6c0f0 --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/views/albums/new_view.js @@ -0,0 +1,45 @@ +Dummy.Views.Albums || (Dummy.Views.Albums = {}); + +Dummy.Views.Albums.NewView = Backbone.View.extend({ + template: JST["backbone/templates/albums/new"], + + events: { + "submit #new-album": "save" + }, + + initialize: function() { + var self = this; + this.model = new this.collection.model(); + + this.model.bind("change:errors", function() { + self.render(); + }); + }, + + save: function(e) { + e.preventDefault(); + e.stopPropagation(); + + this.model.unset("errors"); + + var self = this; + this.collection.create(this.model.toJSON(), { + success: function(album) { + self.model = album; + window.location.hash = "/" + self.model.id; + }, + + error: function(album, jqXHR) { + self.model.set({errors: $.parseJSON(jqXHR.responseText)}); + } + }); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + + this.$("form").backboneLink(this.model); + + return this; + } +}); diff --git a/test/dummy/app/assets/javascripts/backbone/views/albums/show_view.js b/test/dummy/app/assets/javascripts/backbone/views/albums/show_view.js new file mode 100644 index 0000000..117f61d --- /dev/null +++ b/test/dummy/app/assets/javascripts/backbone/views/albums/show_view.js @@ -0,0 +1,10 @@ +Dummy.Views.Albums || (Dummy.Views.Albums = {}); + +Dummy.Views.Albums.ShowView = Backbone.View.extend({ + template: JST["backbone/templates/albums/show"], + + render: function() { + this.$el.html(this.template(this.model.toJSON() )); + return this; + } +}); diff --git a/test/dummy/app/assets/stylesheets/albums.css b/test/dummy/app/assets/stylesheets/albums.css new file mode 100644 index 0000000..afad32d --- /dev/null +++ b/test/dummy/app/assets/stylesheets/albums.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/test/dummy/app/controllers/albums_controller.rb b/test/dummy/app/controllers/albums_controller.rb new file mode 100644 index 0000000..91af672 --- /dev/null +++ b/test/dummy/app/controllers/albums_controller.rb @@ -0,0 +1,83 @@ +class AlbumsController < ApplicationController + # GET /albums + # GET /albums.json + def index + @albums = Album.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @albums } + end + end + + # GET /albums/1 + # GET /albums/1.json + def show + @album = Album.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @album } + end + end + + # GET /albums/new + # GET /albums/new.json + def new + @album = Album.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @album } + end + end + + # GET /albums/1/edit + def edit + @album = Album.find(params[:id]) + end + + # POST /albums + # POST /albums.json + def create + @album = Album.new(params[:album]) + + respond_to do |format| + if @album.save + format.html { redirect_to @album, notice: 'Album was successfully created.' } + format.json { render json: @album, status: :created, location: @album } + else + format.html { render action: "new" } + format.json { render json: @album.errors, status: :unprocessable_entity } + end + end + end + + # PUT /albums/1 + # PUT /albums/1.json + def update + @album = Album.find(params[:id]) + + respond_to do |format| + if @album.update_attributes(params[:album]) + format.html { redirect_to @album, notice: 'Album was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: "edit" } + format.json { render json: @album.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /albums/1 + # DELETE /albums/1.json + def destroy + @album = Album.find(params[:id]) + @album.destroy + + respond_to do |format| + format.html { redirect_to albums_url } + format.json { head :no_content } + end + end +end diff --git a/test/dummy/app/helpers/albums_helper.rb b/test/dummy/app/helpers/albums_helper.rb new file mode 100644 index 0000000..d976b7c --- /dev/null +++ b/test/dummy/app/helpers/albums_helper.rb @@ -0,0 +1,2 @@ +module AlbumsHelper +end diff --git a/test/dummy/app/models/album.rb b/test/dummy/app/models/album.rb new file mode 100644 index 0000000..53a361c --- /dev/null +++ b/test/dummy/app/models/album.rb @@ -0,0 +1,3 @@ +class Album < ActiveRecord::Base + attr_accessible :artist, :title +end diff --git a/test/dummy/app/views/albums/_form.html.erb b/test/dummy/app/views/albums/_form.html.erb new file mode 100644 index 0000000..8c35ae8 --- /dev/null +++ b/test/dummy/app/views/albums/_form.html.erb @@ -0,0 +1,25 @@ +<%= form_for(@album) do |f| %> + <% if @album.errors.any? %> +<%= notice %>
+ ++ Title: + <%= @album.title %> +
+ ++ Artist: + <%= @album.artist %> +
+ + +<%= link_to 'Edit', edit_album_path(@album) %> | +<%= link_to 'Back', albums_path %> diff --git a/test/dummy/config/routes.rb b/test/dummy/config/routes.rb index fbb2909..82480cb 100644 --- a/test/dummy/config/routes.rb +++ b/test/dummy/config/routes.rb @@ -1,4 +1,7 @@ Dummy::Application.routes.draw do + resources :albums + + resources :posts get "home/index" diff --git a/test/dummy/db/migrate/20130226223136_create_albums.rb b/test/dummy/db/migrate/20130226223136_create_albums.rb new file mode 100644 index 0000000..2aa24d9 --- /dev/null +++ b/test/dummy/db/migrate/20130226223136_create_albums.rb @@ -0,0 +1,10 @@ +class CreateAlbums < ActiveRecord::Migration + def change + create_table :albums do |t| + t.string :title + t.string :artist + + t.timestamps + end + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index e588070..6dbf0f9 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -11,7 +11,14 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110615014116) do +ActiveRecord::Schema.define(:version => 20130226223136) do + + create_table "albums", :force => true do |t| + t.string "title" + t.string "artist" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end create_table "posts", :force => true do |t| t.string "title" diff --git a/test/dummy/test/fixtures/albums.yml b/test/dummy/test/fixtures/albums.yml new file mode 100644 index 0000000..dc1e8b5 --- /dev/null +++ b/test/dummy/test/fixtures/albums.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html + +one: + title: MyString + artist: MyString + +two: + title: MyString + artist: MyString diff --git a/test/dummy/test/unit/album_test.rb b/test/dummy/test/unit/album_test.rb new file mode 100644 index 0000000..1efcda0 --- /dev/null +++ b/test/dummy/test/unit/album_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AlbumTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/dummy/test/unit/helpers/albums_helper_test.rb b/test/dummy/test/unit/helpers/albums_helper_test.rb new file mode 100644 index 0000000..7fd1689 --- /dev/null +++ b/test/dummy/test/unit/helpers/albums_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class AlbumsHelperTest < ActionView::TestCase +end diff --git a/test/generators/install_generator_test.rb b/test/generators/install_generator_test.rb index 1541882..835bfc1 100644 --- a/test/generators/install_generator_test.rb +++ b/test/generators/install_generator_test.rb @@ -14,13 +14,32 @@ def setup super end + + def teardown + BackboneRails.detect_script! + end + + test "Assert javascript file is created when coffeescript is not detected" do + BackboneRails.javascript! + run_generator + + assert_file "#{backbone_path}/dummy.js", /window\.Dummy/ + assert_no_file "#{backbone_path}/dummy.js.coffee" + end + test "Assert javascript file is created when flagged" do + run_generator [destination_root, "--javascript"] + + assert_file "#{backbone_path}/dummy.js", /window\.Dummy/ + assert_no_file "#{backbone_path}/dummy.js.coffee" + end + test "Assert application coffeescript file is created" do run_generator assert_file "#{backbone_path}/dummy.js.coffee", /window\.Dummy/ end - + test "Assert application coffeescript file is created for two word application name" do Rails.application.class.stubs(:name).returns("FooBar::Application") run_generator diff --git a/test/generators/model_generator_test.rb b/test/generators/model_generator_test.rb index 585c4ff..2f062b1 100644 --- a/test/generators/model_generator_test.rb +++ b/test/generators/model_generator_test.rb @@ -5,7 +5,41 @@ class ModelGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper tests Backbone::Generators::ModelGenerator - + + teardown do + BackboneRails.detect_script! + end + + test "Assert javascript file is created when coffeescript is not detected" do + BackboneRails.javascript! + run_generator %w(Post title:string content:string) + + assert_no_file "#{backbone_path}/models/post.js.coffee" + + assert_file "#{backbone_path}/models/post.js" do |model| + model_class = Regexp.escape("Dummy.Models.Post = Backbone.Model.extend({") + collection_class = Regexp.escape("Dummy.Collections.PostsCollection = Backbone.Collection.extend({") + + assert_match /#{model_class}/, model + assert_match /#{collection_class}/, model + + assert_match /paramRoot: 'post'/, model + assert_match /url: '\/posts'/, model + + assert_match /defaults:/, model + assert_match /title: null/, model + assert_match /content: null/, model + end + end + + test "Assert javascript file is created when flagged" do + run_generator %w(Post title:string content:string --javascript) + + assert_file "#{backbone_path}/models/post.js" + assert_no_file "#{backbone_path}/models/post.js.coffee" + end + + test "simple model" do run_generator %w(Post title:string content:string) diff --git a/test/generators/router_generator_test.rb b/test/generators/router_generator_test.rb index ab31e40..2f80078 100644 --- a/test/generators/router_generator_test.rb +++ b/test/generators/router_generator_test.rb @@ -6,6 +6,37 @@ class RouterGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper tests Backbone::Generators::RouterGenerator + teardown do + BackboneRails.detect_script! + end + + test "javascript router with two actions" do + BackboneRails.javascript! + run_generator ["Posts", "index", "edit"] + + assert_file "#{backbone_path}/routers/posts_router.js" do |router| + assert_match /Dummy.Routers.PostsRouter = Backbone.Router.extend/, router + end + + assert_file "#{backbone_path}/views/posts/index_view.js" + assert_file "#{backbone_path}/templates/posts/index.jst.ejs" + assert_file "#{backbone_path}/views/posts/edit_view.js" + assert_file "#{backbone_path}/templates/posts/edit.jst.ejs" + end + + test "javascript router with two actions via flag" do + run_generator ["Posts", "index", "edit", "--javascript"] + + assert_file "#{backbone_path}/routers/posts_router.js" do |router| + assert_match /Dummy.Routers.PostsRouter = Backbone.Router.extend/, router + end + + assert_file "#{backbone_path}/views/posts/index_view.js" + assert_file "#{backbone_path}/templates/posts/index.jst.ejs" + assert_file "#{backbone_path}/views/posts/edit_view.js" + assert_file "#{backbone_path}/templates/posts/edit.jst.ejs" + end + test "simple router with two actions" do run_generator ["Posts", "index", "edit"] diff --git a/test/generators/scaffold_generator_test.rb b/test/generators/scaffold_generator_test.rb index dcd12b8..d667fb5 100644 --- a/test/generators/scaffold_generator_test.rb +++ b/test/generators/scaffold_generator_test.rb @@ -6,6 +6,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper tests Backbone::Generators::ScaffoldGenerator arguments %w(Post title:string content:string) + + teardown do + BackboneRails.detect_script! + end test "generate router scaffolding" do run_generator @@ -21,6 +25,37 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end end + test "generate javascript router scaffolding" do + BackboneRails.javascript! + run_generator + + assert_file "#{backbone_path}/routers/posts_router.js" do |router| + assert_match /Dummy.Routers.PostsRouter = Backbone.Router.extend/, router + assert_match /newPost: function\(\)/, router + assert_match /this.posts.reset\(options.posts\)/, router + + assert_match /new Dummy.Views.Posts.NewView/, router + assert_match /new Dummy.Views.Posts.IndexView/, router + assert_match /new Dummy.Views.Posts.ShowView/, router + assert_match /new Dummy.Views.Posts.EditView/, router + end + end + + test "generate javascript router scaffolding via flag" do + run_generator %w(Post title:string content:string --javascript) + + assert_file "#{backbone_path}/routers/posts_router.js" do |router| + assert_match /Dummy.Routers.PostsRouter = Backbone.Router.extend/, router + assert_match /newPost: function\(\)/, router + assert_match /this.posts.reset\(options.posts\)/, router + + assert_match /new Dummy.Views.Posts.NewView/, router + assert_match /new Dummy.Views.Posts.IndexView/, router + assert_match /new Dummy.Views.Posts.ShowView/, router + assert_match /new Dummy.Views.Posts.EditView/, router + end + end + test "generate router scaffolding for model with two words" do run_generator %w(BlogPost title:string content:string) @@ -73,6 +108,83 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end end + test "generate javascript view files" do + BackboneRails.javascript! + run_generator + + assert_file "#{backbone_path}/views/posts/index_view.js" do |view| + assert_match /#{Regexp.escape('JST["backbone/templates/posts/index"]')}/, view + assert_match /#{Regexp.escape('this.template({posts: this.options.posts.toJSON() }))')}/, view + assert_match /#{Regexp.escape("new Dummy.Views.Posts.PostView({model : post})")}/, view + end + + assert_file "#{backbone_path}/views/posts/show_view.js" do |view| + assert_match /Dummy.Views.Posts.ShowView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('template: JST["backbone/templates/posts/show"]')}/, view + end + + assert_file "#{backbone_path}/views/posts/new_view.js" do |view| + assert_match /Dummy.Views.Posts.NewView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/new"]')}/, view + assert_match /#{Regexp.escape('"submit #new-post": "save"')}/, view + assert_match /#{Regexp.escape('success: function(post) {')}/, view + assert_match /#{Regexp.escape('self.model = post')}/, view + end + + assert_file "#{backbone_path}/views/posts/edit_view.js" do |view| + assert_match /Dummy.Views.Posts.EditView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/edit"]')}/, view + assert_match /#{Regexp.escape('"submit #edit-post": "update"')}/, view + assert_match /#{Regexp.escape('success: function(post) {')}/, view + end + + assert_file "#{backbone_path}/views/posts/post_view.js" do |view| + assert_match /Dummy.Views.Posts.PostView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/post"]')}/, view + end + end + + test "generate javascript view files via flag" do + run_generator %w(Post title:string content:string --javascript) + + assert_file "#{backbone_path}/views/posts/index_view.js" do |view| + assert_match /#{Regexp.escape('JST["backbone/templates/posts/index"]')}/, view + assert_match /#{Regexp.escape('this.template({posts: this.options.posts.toJSON() }))')}/, view + assert_match /#{Regexp.escape("new Dummy.Views.Posts.PostView({model : post})")}/, view + end + + assert_file "#{backbone_path}/views/posts/show_view.js" do |view| + assert_match /Dummy.Views.Posts.ShowView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('template: JST["backbone/templates/posts/show"]')}/, view + end + + assert_file "#{backbone_path}/views/posts/new_view.js" do |view| + assert_match /Dummy.Views.Posts.NewView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/new"]')}/, view + assert_match /#{Regexp.escape('"submit #new-post": "save"')}/, view + assert_match /#{Regexp.escape('success: function(post) {')}/, view + assert_match /#{Regexp.escape('self.model = post')}/, view + end + + assert_file "#{backbone_path}/views/posts/edit_view.js" do |view| + assert_match /Dummy.Views.Posts.EditView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/edit"]')}/, view + assert_match /#{Regexp.escape('"submit #edit-post": "update"')}/, view + assert_match /#{Regexp.escape('success: function(post) {')}/, view + end + + assert_file "#{backbone_path}/views/posts/post_view.js" do |view| + assert_match /Dummy.Views.Posts.PostView = Backbone.View.extend/, view + assert_match /#{Regexp.escape('this.template(this.model.toJSON() )')}/, view + assert_match /#{Regexp.escape('JST["backbone/templates/posts/post"]')}/, view + end + end + test "generate view files for model with two words" do run_generator %w(BlogPost title:string content:string)