Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SpreeConf Europe: Backbone.js & Spree

SpreeConf Europe: Backbone.js & Spree

A look a hypothetical Backbone.js front-end replacement for Spree Commerce, with sever-side EJS rendering.

Brian Quinn

August 23, 2012
Tweet

More Decks by Brian Quinn

Other Decks in Technology

Transcript

  1. Overhauled Product & Order API’s Spree-API: improvements Included RABL for

    response templates Swapped to Ransack for searching
  2. Overhauled Product & Order API’s Spree-API: improvements Included RABL for

    response templates Swapped to Ransack for searching Improved Authentication
  3. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 class Spree::Api::BaseController < Spree::BaseController ... respond_to :json def index respond_with(@collection) do |format| format.json { render :json => @collection.to_json(serialization_options) } end end def show respond_with(@object) do |format| format.json { render :json => @object.to_json(serialization_options) } end end ... 01. api/app/controllers/spree/api/base_controller.rb Spree-API: PRe-RABL
  4. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 class Spree::Api::BaseController < Spree::BaseController ... respond_to :json def index respond_with(@collection) do |format| format.json { render :json => @collection.to_json(serialization_options) } end end def show respond_with(@object) do |format| format.json { render :json => @object.to_json(serialization_options) } end end ... 01. api/app/controllers/spree/api/base_controller.rb Spree-API: PRe-RABL
  5. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 object @product attributes *product_attributes child :variants_including_master => :variants do attributes *variant_attributes child :option_values => :option_values do attributes *option_value_attributes end end child :images => :images do extends "spree/api/v1/images/show" end child :option_types => :option_types do child :option_values => :option_values do attributes *option_value_attributes end end 01. api/app/views/spree/api/v1/products/show.rabl Spree-API: With RABL
  6. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 object @product attributes *product_attributes child :variants_including_master => :variants do attributes *variant_attributes child :option_values => :option_values do attributes *option_value_attributes end end child :images => :images do extends "spree/api/v1/images/show" end child :option_types => :option_types do child :option_values => :option_values do attributes *option_value_attributes end end 01. api/app/views/spree/api/v1/products/show.rabl Spree-API: With RABL
  7. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 object @product attributes *product_attributes child :variants_including_master => :variants do attributes *variant_attributes child :option_values => :option_values do attributes *option_value_attributes end end child :images => :images do extends "spree/api/v1/images/show" end child :option_types => :option_types do child :option_values => :option_values do attributes *option_value_attributes end end 01. api/app/views/spree/api/v1/products/show.rabl Spree-API: With RABL
  8. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 object @product attributes *product_attributes child :variants_including_master => :variants do attributes *variant_attributes child :option_values => :option_values do attributes *option_value_attributes end end child :images => :images do extends "spree/api/v1/images/show" end child :option_types => :option_types do child :option_values => :option_values do attributes *option_value_attributes end end 01. api/app/views/spree/api/v1/products/show.rabl Spree-API: With RABL
  9. Better control over JSON output Spree-API: Rabl Benefits Easily extendable

    by extensions / applications Supports lots of formats
  10. Better control over JSON output Spree-API: Rabl Benefits Easily extendable

    by extensions / applications Supports lots of formats Great caching support
  11. GET api/products/search?q[name_cont]=ruby Spree-API: RANSACK GET api/products/search?q[master_price_lteq]=10.00 GET api/orders/search?q=[email_matches]=brian%25 GET api/orders/search?q=[completed_at_present]=1

    GET api/products/search?q[name_start]=Ruby GET api/products/search?q[taxons_id_eq]=12345 GET api/products/search?q[variants_count_on_hand_gt]=5
  12. The BASICS: MODELS Product Variant Image Option Type Option Value

    Taxonomy Taxon Order LineItem Adjustment Status
  13. 01 02 03 04 05 06 07 08 09 10

    11 12 Spree.Models.Product = Backbone.Model.extend({ initialize: function(){ this.build_associations(); }, build_associations: function(){ this.images = new Spree.Collections.Images( this.get('images' ) ); ... }, ... }); 01. app/assets/javascripts/store/models/product.js The Basics: Models
  14. 01 02 03 04 05 06 07 08 09 10

    11 12 Spree.Models.Product = Backbone.Model.extend({ initialize: function(){ this.build_associations(); }, build_associations: function(){ this.images = new Spree.Collections.Images( this.get('images' ) ); ... }, ... }); 01. app/assets/javascripts/store/models/product.js The Basics: Models
  15. 01 02 03 04 05 06 07 08 09 10

    11 12 Spree.Models.Product = Backbone.Model.extend({ initialize: function(){ this.build_associations(); }, build_associations: function(){ this.images = new Spree.Collections.Images( this.get('images' ) ); ... }, ... }); 01. app/assets/javascripts/store/models/product.js The Basics: Models
  16. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 { id: 723959550, name: "Ruby on Rails Bag", description: "Lorem ipsum tincidunt egestas. Mauris nibh...", price: "22.99", available_on: "2012-06-05T13:02:05Z", permalink: "ruby-on-rails-bag", count_on_hand: 45, images: [{ id: 3, position: 1, attachment_content_type: "image/jpeg", attachment_file_name: "ror_bag.jpeg", alt: null, viewable_type: "Spree::Variant", viewable_id: 504220342 }] } 01. Sample Products JSON Snippet The Basics: Models
  17. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 { id: 723959550, name: "Ruby on Rails Bag", description: "Lorem ipsum tincidunt egestas. Mauris nibh...", price: "22.99", available_on: "2012-06-05T13:02:05Z", permalink: "ruby-on-rails-bag", count_on_hand: 45, images: [{ id: 3, position: 1, attachment_content_type: "image/jpeg", attachment_file_name: "ror_bag.jpeg", alt: null, viewable_type: "Spree::Variant", viewable_id: 504220342 }] } 01. Sample Products JSON Snippet The Basics: Models
  18. Product Variant Image Option Type Option Value Taxonomy Taxon Order

    LineItem Adjustment The BASICS: Collections
  19. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 //= require jquery //= require jquery_ujs //= require store/spree_core //= require store/spree_auth //= require store/spree_promo //= require jquery //= require underscore //= require backbone //= require store/spree //= require store/plumbing //= require store/paginated_collection //= require store/underscore_mixins //= require_tree ./models //= require_tree ./collections //= require_tree ./routers //= require_tree ./views //= require_tree ./templates 01. app/assets/javascripts/store/all.js The Basics: all.js
  20. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 //= require jquery //= require jquery_ujs //= require store/spree_core //= require store/spree_auth //= require store/spree_promo //= require jquery //= require underscore //= require backbone //= require store/spree //= require store/plumbing //= require store/paginated_collection //= require store/underscore_mixins //= require_tree ./models //= require_tree ./collections //= require_tree ./routers //= require_tree ./views //= require_tree ./templates 01. app/assets/javascripts/store/all.js The Basics: all.js
  21. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 //= require jquery //= require jquery_ujs //= require store/spree_core //= require store/spree_auth //= require store/spree_promo //= require jquery //= require underscore //= require backbone //= require store/spree //= require store/plumbing //= require store/paginated_collection //= require store/underscore_mixins //= require_tree ./models //= require_tree ./collections //= require_tree ./routers //= require_tree ./views //= require_tree ./templates 01. app/assets/javascripts/store/all.js The Basics: all.js
  22. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 //= require jquery //= require jquery_ujs //= require store/spree_core //= require store/spree_auth //= require store/spree_promo //= require jquery //= require underscore //= require backbone //= require store/spree //= require store/plumbing //= require store/paginated_collection //= require store/underscore_mixins //= require_tree ./models //= require_tree ./collections //= require_tree ./routers //= require_tree ./views //= require_tree ./templates 01. app/assets/javascripts/store/all.js The Basics: all.js
  23. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 //= require jquery //= require jquery_ujs //= require store/spree_core //= require store/spree_auth //= require store/spree_promo //= require jquery //= require underscore //= require backbone //= require store/spree //= require store/plumbing //= require store/paginated_collection //= require store/underscore_mixins //= require_tree ./models //= require_tree ./collections //= require_tree ./routers //= require_tree ./views //= require_tree ./templates 01. app/assets/javascripts/store/all.js The Basics: all.js
  24. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  25. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  26. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  27. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  28. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  29. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 var Spree = { Views: { Shared: {}, Products: {}, Taxons: {} ,Orders: {} }, Routers: { _active: {} }, Models: {}, Collections: {}, Data: { _preload: {}, products: null, taxonomies: null }, current_order: null, current_user: null, init: function(){ ... } }); 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  30. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 init: function(){ if(this.Data._preload.products!=undefined){ //preload products this.Data.products = new this.Collections.Products(); this.Data.products.resetWithPagination(this.Data._preload.products); } this.Routers._active.products = new this.Routers.Products(); this.Routers._active.taxons = new this.Routers.Taxons(); this.Routers._active.orders = new this.Routers.Orders(); Backbone.history.start({pushState: true}) $('a[data-push-state]').click(this._navigate); this.status = new this.Models.Status() this.status.fetch({success: this.set_state}); } 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  31. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 init: function(){ if(this.Data._preload.products!=undefined){ //preload products this.Data.products = new this.Collections.Products(); this.Data.products.resetWithPagination(this.Data._preload.products); } this.Routers._active.products = new this.Routers.Products(); this.Routers._active.taxons = new this.Routers.Taxons(); this.Routers._active.orders = new this.Routers.Orders(); Backbone.history.start({pushState: true}) $('a[data-push-state]').click(this._navigate); this.status = new this.Models.Status() this.status.fetch({success: this.set_state}); } 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  32. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 init: function(){ if(this.Data._preload.products!=undefined){ //preload products this.Data.products = new this.Collections.Products(); this.Data.products.resetWithPagination(this.Data._preload.products); } this.Routers._active.products = new this.Routers.Products(); this.Routers._active.taxons = new this.Routers.Taxons(); this.Routers._active.orders = new this.Routers.Orders(); Backbone.history.start({pushState: true}) $('a[data-push-state]').click(this._navigate); this.status = new this.Models.Status() this.status.fetch({success: this.set_state}); } 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  33. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 init: function(){ if(this.Data._preload.products!=undefined){ //preload products this.Data.products = new this.Collections.Products(); this.Data.products.resetWithPagination(this.Data._preload.products); } this.Routers._active.products = new this.Routers.Products(); this.Routers._active.taxons = new this.Routers.Taxons(); this.Routers._active.orders = new this.Routers.Orders(); Backbone.history.start({pushState: true}) $('a[data-push-state]').click(this._navigate); this.status = new this.Models.Status() this.status.fetch({success: this.set_state}); } 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  34. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 init: function(){ if(this.Data._preload.products!=undefined){ //preload products this.Data.products = new this.Collections.Products(); this.Data.products.resetWithPagination(this.Data._preload.products); } this.Routers._active.products = new this.Routers.Products(); this.Routers._active.taxons = new this.Routers.Taxons(); this.Routers._active.orders = new this.Routers.Orders(); Backbone.history.start({pushState: true}) $('a[data-push-state]').click(this._navigate); this.status = new this.Models.Status() this.status.fetch({success: this.set_state}); } 01. app/assets/javascripts/store/spree.js The Basics: Spree Object
  35. 01 02 03 04 _navigate: function(evt){ evt.preventDefault(); Spree.Routers._active.products.navigate($(evt.currentTarget).attr('href'), true); }

    02. spree.js _navigate() function 01 02 03 04 05 init: function(){ ... $('a[data-push-state]').click(this._navigate); ... } 01. spree.js init() function The BASICS: push state
  36. 01 02 03 04 _navigate: function(evt){ evt.preventDefault(); Spree.Routers._active.products.navigate($(evt.currentTarget).attr('href'), true); }

    02. spree.js _navigate() function 01 02 03 04 05 init: function(){ ... $('a[data-push-state]').click(this._navigate); ... } 01. spree.js init() function The BASICS: push state
  37. 01 02 03 04 _navigate: function(evt){ evt.preventDefault(); Spree.Routers._active.products.navigate($(evt.currentTarget).attr('href'), true); }

    02. spree.js _navigate() function 01 02 03 04 05 init: function(){ ... $('a[data-push-state]').click(this._navigate); ... } 01. spree.js init() function The BASICS: push state
  38. Index Show Edit Products => Orders => The BASICS: Templates

    Pagination Cart Link Account Links Show Taxons =>
  39. EJS

  40. 01 02 03 04 05 06 07 <td class="cart-item-image" data-hook="cart_item_image">

    <% if variant.images.length == 0 %> <%= link_to small_image(variant.product), variant.product %> <% else %> <%= link_to image_tag(variant.images.first.attachment.url(:small)) ... <% end %> </td> 01. Original HTML.ERB Template 01 02 03 04 05 06 07 <td class="cart-item-image" data-hook="cart_item_image"> <% if(line_item.product.images.size()>0){ %> <a href="/products/<%= line_item.product.get('permalink') %>" <img src="/spree/products/<%= line_item.product.images.first().id %>/... </a> <% } %> </td> 02. Replacement JST.EJS Template Template: Conversion
  41. 01 02 03 04 05 06 07 <% var products

    = new Spree.Collections.Products() %> <% products.resetWithPagination(JSON.parse(get('products_json'))) %> <%= JST['store/templates/products/index']({ products: products }) %> <script type="text/javascript"> Spree.Data._preload.products = <%= get('products_json') %>; </script> 01. app/views/spree/products/index.jst.ejs Scribble: Example Server Side JST.EJS
  42. 01 02 03 04 05 06 07 <% var products

    = new Spree.Collections.Products() %> <% products.resetWithPagination(JSON.parse(get('products_json'))) %> <%= JST['store/templates/products/index']({ products: products }) %> <script type="text/javascript"> Spree.Data._preload.products = <%= get('products_json') %>; </script> 01. app/views/spree/products/index.jst.ejs Scribble: Example Server Side JST.EJS
  43. 01 02 03 04 05 06 07 <% var products

    = new Spree.Collections.Products() %> <% products.resetWithPagination(JSON.parse(get('products_json'))) %> <%= JST['store/templates/products/index']({ products: products }) %> <script type="text/javascript"> Spree.Data._preload.products = <%= get('products_json') %>; </script> 01. app/views/spree/products/index.jst.ejs Scribble: Example Server Side JST.EJS
  44. 01 02 03 04 05 06 07 <% var products

    = new Spree.Collections.Products() %> <% products.resetWithPagination(JSON.parse(get('products_json'))) %> <%= JST['store/templates/products/index']({ products: products }) %> <script type="text/javascript"> Spree.Data._preload.products = <%= get('products_json') %>; </script> 01. app/views/spree/products/index.jst.ejs Scribble: Example Server Side JST.EJS
  45. 01 02 03 04 05 06 07 08 09 10

    11 12 module Spree ProductsController.class_eval do helper Spree::Api::ApiHelpers def index @searcher = Config.searcher_class.new(params) @products = @searcher.retrieve_products @products_json = render_to_string(:file => 'spree/api/v1/products/index') respond_with(@products) end 01. app/controllers/spree/products_contoller_decorator.rb Scribble: Getting JSON from Actions
  46. 01 02 03 04 05 06 07 08 09 10

    11 12 module Spree ProductsController.class_eval do helper Spree::Api::ApiHelpers def index @searcher = Config.searcher_class.new(params) @products = @searcher.retrieve_products @products_json = render_to_string(:file => 'spree/api/v1/products/index') respond_with(@products) end 01. app/controllers/spree/products_contoller_decorator.rb Scribble: Getting JSON from Actions
  47. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 source: - vendor/assets/javascripts/underscore.js - vendor/assets/javascripts/backbone.js - app/assets/javascripts/store/spree.js - app/assets/javascripts/store/paginated_collection.js - app/assets/javascripts/store/underscore_mixins.js - app/assets/javascripts/store/models/product.js - app/assets/javascripts/store/models/variant.js - ... - app/assets/javascripts/store/models/adjustment.js - app/assets/javascripts/store/collections/products.js - app/assets/javascripts/store/collections/variants.js - ... - app/assets/javascripts/store/collections/images.js layout: store/templates/layouts/spree_application 01. config/scribble.yaml Scribble: Configuration
  48. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 source: - vendor/assets/javascripts/underscore.js - vendor/assets/javascripts/backbone.js - app/assets/javascripts/store/spree.js - app/assets/javascripts/store/paginated_collection.js - app/assets/javascripts/store/underscore_mixins.js - app/assets/javascripts/store/models/product.js - app/assets/javascripts/store/models/variant.js - ... - app/assets/javascripts/store/models/adjustment.js - app/assets/javascripts/store/collections/products.js - app/assets/javascripts/store/collections/variants.js - ... - app/assets/javascripts/store/collections/images.js layout: store/templates/layouts/spree_application 01. config/scribble.yaml Scribble: Configuration
  49. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 source: - vendor/assets/javascripts/underscore.js - vendor/assets/javascripts/backbone.js - app/assets/javascripts/store/spree.js - app/assets/javascripts/store/paginated_collection.js - app/assets/javascripts/store/underscore_mixins.js - app/assets/javascripts/store/models/product.js - app/assets/javascripts/store/models/variant.js - ... - app/assets/javascripts/store/models/adjustment.js - app/assets/javascripts/store/collections/products.js - app/assets/javascripts/store/collections/variants.js - ... - app/assets/javascripts/store/collections/images.js layout: store/templates/layouts/spree_application 01. config/scribble.yaml Scribble: Configuration
  50. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  51. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  52. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  53. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  54. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  55. 01 02 03 04 05 06 07 08 09 10

    11 12 if template.exist? ctx = Scribble::Context.new(@controller) compile = ctx.load_templates compile << %Q{var _view = #{EJS.compile(Pathname.new(template).read)}; var layout = JST['#{Scribble::Config.layout}']; layout()} @controller.response_body = ctx.js_context.eval(compile) else super #fallback to built rails method end 01. lib/scribble/responder.rb Scribble: ActionController::responder
  56. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 <head data-hook="inside_head"> <title><%= get('title') %></title> <%= get('csrf_meta_tags') %> <%= get('javascript_include_tag', 'store/all') %> <script type="text/javascript"> Spree.api_key = '<%= get('api_key') %>'; $(function() { Spree.init(); }); </script> </head> <body data-hook="body"> <div class="container"> <%= JST['store/templates/shared/header']() %> <div id="wrapper" class="row" data-hook> <%= _view() %> </div> <%= JST['store/templates/shared/footer']() %> </div> </body> 01. app/assets/javascripts/store/templates/layouts/spree_application.jst.ejs Scribble: LAyout
  57. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 <head data-hook="inside_head"> <title><%= get('title') %></title> <%= get('csrf_meta_tags') %> <%= get('javascript_include_tag', 'store/all') %> <script type="text/javascript"> Spree.api_key = '<%= get('api_key') %>'; $(function() { Spree.init(); }); </script> </head> <body data-hook="body"> <div class="container"> <%= JST['store/templates/shared/header']() %> <div id="wrapper" class="row" data-hook> <%= _view() %> </div> <%= JST['store/templates/shared/footer']() %> </div> </body> 01. app/assets/javascripts/store/templates/layouts/spree_application.jst.ejs Scribble: LAyout
  58. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 <head data-hook="inside_head"> <title><%= get('title') %></title> <%= get('csrf_meta_tags') %> <%= get('javascript_include_tag', 'store/all') %> <script type="text/javascript"> Spree.api_key = '<%= get('api_key') %>'; $(function() { Spree.init(); }); </script> </head> <body data-hook="body"> <div class="container"> <%= JST['store/templates/shared/header']() %> <div id="wrapper" class="row" data-hook> <%= _view() %> </div> <%= JST['store/templates/shared/footer']() %> </div> </body> 01. app/assets/javascripts/store/templates/layouts/spree_application.jst.ejs Scribble: LAyout
  59. 01 02 03 04 05 06 07 08 09 10

    11 12 13 14 15 16 17 18 19 20 <head data-hook="inside_head"> <title><%= get('title') %></title> <%= get('csrf_meta_tags') %> <%= get('javascript_include_tag', 'store/all') %> <script type="text/javascript"> Spree.api_key = '<%= get('api_key') %>'; $(function() { Spree.init(); }); </script> </head> <body data-hook="body"> <div class="container"> <%= JST['store/templates/shared/header']() %> <div id="wrapper" class="row" data-hook> <%= _view() %> </div> <%= JST['store/templates/shared/footer']() %> </div> </body> 01. app/assets/javascripts/store/templates/layouts/spree_application.jst.ejs Scribble: LAyout
  60. The BASICS: MODELS Product Variant Image Option Type Option Value

    Taxonomy Taxon Order LineItem Adjustment Status
  61. 01 02 03 04 05 06 07 08 09 10

    { user: { id: 1 }, order: { id: 1234, number: 'R716022188', item_total: '16.99', state: 'cart', ... line_items: [{...}] } } 01. /status.json snippet Status: JSON
  62. 01 02 03 04 05 06 07 08 09 10

    { user: { id: 1 }, order: { id: 1234, number: 'R716022188', item_total: '16.99', state: 'cart', ... line_items: [{...}] } } 01. /status.json snippet Status: JSON
  63. 01 02 03 04 05 06 07 08 09 10

    { user: { id: 1 }, order: { id: 1234, number: 'R716022188', item_total: '16.99', state: 'cart', ... line_items: [{...}] } } 01. /status.json snippet Status: JSON
  64. 01 02 03 04 05 06 07 08 09 10

    { user: { id: 1 }, order: { id: 1234, number: 'R716022188', item_total: '16.99', state: 'cart', ... line_items: [{...}] } } 01. /status.json snippet Status: JSON
  65. 01 02 03 04 05 06 07 08 09 10

    object @taxon cache @taxon attributes *taxon_attributes node do |t| child t.children => :taxons do attributes *taxon_attributes end end 01. /api/app/views/spree/v1/taxons/show.rabl API caching: Rabl
  66. 01 02 03 04 05 06 07 08 09 10

    object @taxon cache @taxon attributes *taxon_attributes node do |t| child t.children => :taxons do attributes *taxon_attributes end end 01. /api/app/views/spree/v1/taxons/show.rabl API caching: Rabl
  67. 01 02 03 Started GET "/api/products/search?q%5Btaxons_id_eq%5D=558398358&page=1" for 127.0.0.1 at 2012-08-18

    16:56:33 +0100 Rendered spree/api/v1/products/index.rabl (3.2ms) 02. With Cache 01 02 03 Started GET "/api/products/search?q%5Btaxons_id_eq%5D=558398358&page=1" for 127.0.0.1 at 2012-08-18 16:56:05 +0100 Rendered spree/api/v1/products/index.rabl (87.2ms) 01. Without Cache Api Caching: performance
  68. 01 02 03 Started GET "/api/products/search?q%5Btaxons_id_eq%5D=558398358&page=1" for 127.0.0.1 at 2012-08-18

    16:56:33 +0100 Rendered spree/api/v1/products/index.rabl (3.2ms) 02. With Cache 01 02 03 Started GET "/api/products/search?q%5Btaxons_id_eq%5D=558398358&page=1" for 127.0.0.1 at 2012-08-18 16:56:05 +0100 Rendered spree/api/v1/products/index.rabl (87.2ms) 01. Without Cache Api Caching: performance
  69. ECO

  70. JS

  71. JS

  72. JS

  73. JS

  74. JS

  75. JS

  76. ?